From 60c4f957956fe4f77ada109efd55a1e205fcaa12 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 11 Aug 2014 22:47:02 +0200 Subject: [PATCH] Implemented case differentiation for window focus/notification on Windows and X11 --- src/SFML/Window/Unix/WindowImplX11.cpp | 69 +++++++++++++++++++---- src/SFML/Window/Win32/WindowImplWin32.cpp | 22 +++++++- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index e01d4920..e4dfe775 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef SFML_OPENGL_ES #include @@ -54,10 +55,11 @@ //////////////////////////////////////////////////////////// namespace { - sf::priv::WindowImplX11* fullscreenWindow = NULL; - unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | - PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | - EnterWindowMask | LeaveWindowMask; + sf::priv::WindowImplX11* fullscreenWindow = NULL; + std::vector allWindows; + unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | + PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | + EnterWindowMask | LeaveWindowMask; // Filter the events received by windows (only allow those matching a specific window) Bool checkEvent(::Display*, XEvent* event, XPointer userData) @@ -303,6 +305,9 @@ WindowImplX11::~WindowImplX11() // Close the connection with the X server CloseDisplay(m_display); + + // Remove this window from the global list of windows (required for focus request) + allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this)); } @@ -486,19 +491,50 @@ void WindowImplX11::setKeyRepeatEnabled(bool enabled) //////////////////////////////////////////////////////////// void WindowImplX11::requestFocus() { - // Ensure WM hints exist - XWMHints* hints = XGetWMHints(m_display, m_window); - if (hints == NULL) + // Focus is only stolen among SFML windows, not between applications + // Check the global list of windows to find out whether an SFML window has the focus + // Note: can't handle console and other non-SFML windows belonging to the application. + bool sfmlWindowFocused = false; + for (std::vector::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) { - hints = XAllocWMHints(); + if ((*itr)->hasFocus()) + { + sfmlWindowFocused = true; + break; + } + } + + // Check if window is viewable (not on other desktop, ...) + // TODO: Check also if minimized + XWindowAttributes attributes; + if (XGetWindowAttributes(m_display, m_window, &attributes) == 0) + { + sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl; + return; // error getting attribute + } + + bool windowViewable = (attributes.map_state == IsViewable); + + if (sfmlWindowFocused && windowViewable) + { + // Another SFML window of this application has the focus and the current window is viewable: + // steal focus (i.e. bring window to the front and give it input focus) + XRaiseWindow(m_display, m_window); + XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime); } else { - // Add Urgency Hint flag (visual notification) + // Otherwise: display urgency hint (flashing application logo) + // Ensure WM hints exist, allocate if necessary + XWMHints* hints = XGetWMHints(m_display, m_window); + if (hints == NULL) + hints = XAllocWMHints(); + + // Add urgency (notification) flag to hints hints->flags |= XUrgencyHint; XSetWMHints(m_display, m_window, hints); XFree(hints); - } + } } @@ -598,6 +634,9 @@ void WindowImplX11::initialize() // Flush the commands queue XFlush(m_display); + + // Add this window to the global list of windows (required for focus request) + allWindows.push_back(this); } @@ -710,6 +749,16 @@ bool WindowImplX11::processEvent(XEvent windowEvent) Event event; event.type = Event::GainedFocus; pushEvent(event); + + // If the window has been previously marked urgent (notification) as a result of a focus request, undo that + XWMHints* hints = XGetWMHints(m_display, m_window); + if (hints != NULL) + { + // Remove urgency (notification) flag from hints + hints->flags &= ~XUrgencyHint; + XSetWMHints(m_display, m_window, hints); + XFree(hints); + } break; } diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index 5dcfc3e0..5ebc8a80 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -369,7 +369,27 @@ void WindowImplWin32::setKeyRepeatEnabled(bool enabled) //////////////////////////////////////////////////////////// void WindowImplWin32::requestFocus() { - SetForegroundWindow(m_handle); + // Allow focus stealing only within the same process; compare PIDs of current and foreground window + DWORD thisPid = GetWindowThreadProcessId(m_handle, NULL); + DWORD foregroundPid = GetWindowThreadProcessId(GetForegroundWindow(), NULL); + + if (thisPid == foregroundPid) + { + // The window requesting focus belongs to the same process as the current window: steal focus + SetForegroundWindow(m_handle); + } + else + { + // Different process: don't steal focus, but create a taskbar notification ("flash") + FLASHWINFO info; + info.cbSize = sizeof(info); + info.hwnd = m_handle; + info.dwFlags = FLASHW_TRAY; + info.dwTimeout = 0; + info.uCount = 3; + + FlashWindowEx(&info); + } }