Skip to content

Commit 2ad912b

Browse files
committed
Backends: Win32, SDL, GLFW: only honor io.WantSetMousePos when focused + fix GLFW uninstalling handler + tweaks to reduce branch drift with docking. (#787, #2445, #2696, #3751, #4377)
1 parent db686ad commit 2ad912b

File tree

3 files changed

+99
-85
lines changed

3 files changed

+99
-85
lines changed

backends/imgui_impl_glfw.cpp

+19-18
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
5656
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
5757
#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
58-
#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?
58+
#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?
5959
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
6060
#else
6161
#define GLFW_HAS_NEW_CURSORS (0)
@@ -116,7 +116,7 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
116116
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
117117
{
118118
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
119-
if (bd->PrevUserCallbackMousebutton != NULL)
119+
if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window)
120120
bd->PrevUserCallbackMousebutton(window, button, action, mods);
121121

122122
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed))
@@ -126,7 +126,7 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti
126126
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
127127
{
128128
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
129-
if (bd->PrevUserCallbackScroll != NULL)
129+
if (bd->PrevUserCallbackScroll != NULL && window == bd->Window)
130130
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
131131

132132
ImGuiIO& io = ImGui::GetIO();
@@ -137,7 +137,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
137137
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
138138
{
139139
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
140-
if (bd->PrevUserCallbackKey != NULL)
140+
if (bd->PrevUserCallbackKey != NULL && window == bd->Window)
141141
bd->PrevUserCallbackKey(window, key, scancode, action, mods);
142142

143143
ImGuiIO& io = ImGui::GetIO();
@@ -174,7 +174,7 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
174174
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
175175
{
176176
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
177-
if (bd->PrevUserCallbackChar != NULL)
177+
if (bd->PrevUserCallbackChar != NULL && window == bd->Window)
178178
bd->PrevUserCallbackChar(window, c);
179179

180180
ImGuiIO& io = ImGui::GetIO();
@@ -298,6 +298,7 @@ void ImGui_ImplGlfw_Shutdown()
298298

299299
if (bd->InstalledCallbacks)
300300
{
301+
glfwSetCursorEnterCallback(bd->Window, bd->PrevUserCallbackCursorEnter);
301302
glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton);
302303
glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll);
303304
glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey);
@@ -316,32 +317,32 @@ void ImGui_ImplGlfw_Shutdown()
316317
static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
317318
{
318319
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
319-
320-
// Update buttons
321320
ImGuiIO& io = ImGui::GetIO();
321+
322+
const ImVec2 mouse_pos_prev = io.MousePos;
323+
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
324+
325+
// Update mouse buttons
326+
// (if a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame)
322327
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
323328
{
324-
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
325329
io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0;
326330
bd->MouseJustPressed[i] = false;
327331
}
328332

329333
#ifdef __EMSCRIPTEN__
330-
const bool focused = true; // Emscripten
334+
const bool focused = true;
331335
#else
332336
const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
333337
#endif
334338
GLFWwindow* mouse_window = (bd->MouseWindow == bd->Window || focused) ? bd->Window : NULL;
335339

336-
// Update mouse position
337-
const ImVec2 mouse_pos_backup = io.MousePos;
338-
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
339-
if (io.WantSetMousePos)
340-
{
341-
if (focused)
342-
glfwSetCursorPos(bd->Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y);
343-
}
344-
else if (mouse_window != NULL)
340+
// Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
341+
if (io.WantSetMousePos && focused)
342+
glfwSetCursorPos(bd->Window, (double)mouse_pos_prev.x, (double)mouse_pos_prev.y);
343+
344+
// Set Dear ImGui mouse position from OS position
345+
if (mouse_window != NULL)
345346
{
346347
double mouse_x, mouse_y;
347348
glfwGetCursorPos(mouse_window, &mouse_x, &mouse_y);

backends/imgui_impl_sdl.cpp

+60-52
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@
5959
#include "TargetConditionals.h"
6060
#endif
6161

62-
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
62+
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE (SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS))
6363
#define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5)
6464
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
6565

66+
// SDL Data
6667
struct ImGui_ImplSDL2_Data
6768
{
6869
SDL_Window* Window;
@@ -156,6 +157,17 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window)
156157
ImGuiIO& io = ImGui::GetIO();
157158
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
158159

160+
// Check and store if we are on a SDL backend that supports global mouse position
161+
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
162+
const char* sdl_backend = SDL_GetCurrentVideoDriver();
163+
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
164+
bool mouse_can_use_global_state = false;
165+
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
166+
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
167+
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
168+
mouse_can_use_global_state = true;
169+
#endif
170+
159171
// Setup backend capabilities flags
160172
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
161173
io.BackendPlatformUserData = (void*)bd;
@@ -164,6 +176,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window)
164176
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
165177

166178
bd->Window = window;
179+
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
167180

168181
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
169182
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
@@ -204,20 +217,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window)
204217
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
205218
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
206219

207-
// Check and store if we are on a SDL backend that supports global mouse position
208-
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
209-
const char* sdl_backend = SDL_GetCurrentVideoDriver();
210-
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
211-
bd->MouseCanUseGlobalState = false;
212-
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
213-
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
214-
bd->MouseCanUseGlobalState = true;
215-
216220
#ifdef _WIN32
217-
SDL_SysWMinfo wmInfo;
218-
SDL_VERSION(&wmInfo.version);
219-
SDL_GetWindowWMInfo(window, &wmInfo);
220-
io.ImeWindowHandle = wmInfo.info.win.window;
221+
SDL_SysWMinfo info;
222+
SDL_VERSION(&info.version);
223+
if (SDL_GetWindowWMInfo(window, &info))
224+
io.ImeWindowHandle = info.info.win.window;
221225
#else
222226
(void)window;
223227
#endif
@@ -278,55 +282,59 @@ void ImGui_ImplSDL2_Shutdown()
278282

279283
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
280284
{
281-
ImGuiIO& io = ImGui::GetIO();
282285
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
286+
ImGuiIO& io = ImGui::GetIO();
283287

284-
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
285-
if (io.WantSetMousePos)
286-
SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
287-
else
288-
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
288+
ImVec2 mouse_pos_prev = io.MousePos;
289+
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
289290

290-
int mx, my;
291-
Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my);
291+
// Update mouse buttons
292+
int mouse_x_local, mouse_y_local;
293+
Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local);
292294
io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
293295
io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
294296
io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
295297
bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false;
296298

297-
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
298-
SDL_Window* focused_window = SDL_GetKeyboardFocus(); // Mouse position won't be reported unless window is focused.
299-
#if SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH
300-
SDL_Window* hovered_window = SDL_GetMouseFocus(); // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH enabled.
301-
SDL_Window* mouse_window = (bd->Window == focused_window || bd->Window == hovered_window) ? bd->Window : NULL;
299+
// Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing)
300+
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
301+
SDL_Window* focused_window = SDL_GetKeyboardFocus();
302+
SDL_Window* hovered_window = SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH ? SDL_GetMouseFocus() : NULL; // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH.
303+
SDL_Window* mouse_window = NULL;
304+
if (hovered_window && bd->Window == hovered_window)
305+
mouse_window = hovered_window;
306+
else if (focused_window && bd->Window == focused_window)
307+
mouse_window = focused_window;
308+
309+
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
310+
SDL_CaptureMouse(ImGui::IsAnyMouseDown() ? SDL_TRUE : SDL_FALSE);
302311
#else
303-
SDL_Window* mouse_window = (bd->Window == focused_window) ? bd->Window : NULL;
312+
// SDL 2.0.3 and non-windowed systems: single-viewport only
313+
SDL_Window* mouse_window = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) ? bd->Window : NULL;
304314
#endif
305-
if (mouse_window != NULL)
315+
316+
if (mouse_window == NULL)
317+
return;
318+
319+
// Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
320+
if (io.WantSetMousePos)
321+
SDL_WarpMouseInWindow(bd->Window, (int)mouse_pos_prev.x, (int)mouse_pos_prev.y);
322+
323+
// Set Dear ImGui mouse position from OS position + get buttons. (this is the common behavior)
324+
if (bd->MouseCanUseGlobalState)
306325
{
307-
if (bd->MouseCanUseGlobalState)
308-
{
309-
// SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?)
310-
// The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally.
311-
// Won't use this workaround on SDL backends that have no global mouse position, like Wayland or RPI
312-
int wx, wy;
313-
SDL_GetWindowPosition(mouse_window, &wx, &wy);
314-
SDL_GetGlobalMouseState(&mx, &my);
315-
mx -= wx;
316-
my -= wy;
317-
}
318-
io.MousePos = ImVec2((float)mx, (float)my);
326+
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
327+
// Unlike local position obtained earlier this will be valid when straying out of bounds.
328+
int mouse_x_global, mouse_y_global;
329+
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
330+
int window_x, window_y;
331+
SDL_GetWindowPosition(mouse_window, &window_x, &window_y);
332+
io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y));
333+
}
334+
else
335+
{
336+
io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local);
319337
}
320-
321-
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
322-
// The function is only supported from SDL 2.0.4 (released Jan 2016)
323-
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
324-
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
325-
#else
326-
// SDL 2.0.3 and non-windowed systems
327-
if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS)
328-
io.MousePos = ImVec2((float)mx, (float)my);
329-
#endif
330338
}
331339

332340
static void ImGui_ImplSDL2_UpdateMouseCursor()
@@ -393,9 +401,9 @@ static void ImGui_ImplSDL2_UpdateGamepads()
393401

394402
void ImGui_ImplSDL2_NewFrame()
395403
{
396-
ImGuiIO& io = ImGui::GetIO();
397404
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
398405
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDL2_Init()?");
406+
ImGuiIO& io = ImGui::GetIO();
399407

400408
// Setup display size (every frame to accommodate for window resizing)
401409
int w, h;

backends/imgui_impl_win32.cpp

+20-15
Original file line numberDiff line numberDiff line change
@@ -219,31 +219,36 @@ static bool ImGui_ImplWin32_UpdateMouseCursor()
219219

220220
static void ImGui_ImplWin32_UpdateMousePos()
221221
{
222-
ImGuiIO& io = ImGui::GetIO();
223222
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
223+
ImGuiIO& io = ImGui::GetIO();
224224
IM_ASSERT(bd->hWnd != 0);
225225

226-
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
227-
if (io.WantSetMousePos)
228-
{
229-
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
230-
if (::ClientToScreen(bd->hWnd, &pos))
231-
::SetCursorPos(pos.x, pos.y);
232-
}
233-
234-
// Set mouse position
226+
const ImVec2 mouse_pos_prev = io.MousePos;
235227
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
228+
229+
// Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing)
236230
HWND focused_window = ::GetForegroundWindow();
237231
HWND hovered_window = bd->MouseHwnd;
238232
HWND mouse_window = NULL;
239-
if (hovered_window && (hovered_window == bd->hWnd) || ::IsChild(hovered_window, bd->hWnd))
233+
if (hovered_window && (hovered_window == bd->hWnd || ::IsChild(hovered_window, bd->hWnd)))
240234
mouse_window = hovered_window;
241-
else if (focused_window && (focused_window == bd->hWnd) || ::IsChild(focused_window, bd->hWnd))
235+
else if (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd)))
242236
mouse_window = focused_window;
237+
if (mouse_window == NULL)
238+
return;
239+
240+
// Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
241+
if (io.WantSetMousePos)
242+
{
243+
POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y };
244+
if (::ClientToScreen(bd->hWnd, &pos))
245+
::SetCursorPos(pos.x, pos.y);
246+
}
247+
248+
// Set Dear ImGui mouse position from OS position
243249
POINT pos;
244-
if (mouse_window)
245-
if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos))
246-
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
250+
if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos))
251+
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
247252
}
248253

249254
// Gamepad navigation mapping

0 commit comments

Comments
 (0)