Implemented case differentiation for window focus/notification on Windows and X11

This commit is contained in:
Jan Haller 2014-08-11 22:47:02 +02:00
parent c4435b8a31
commit 60c4f95795
2 changed files with 80 additions and 11 deletions

View File

@ -40,6 +40,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <iterator> #include <iterator>
#include <algorithm>
#ifdef SFML_OPENGL_ES #ifdef SFML_OPENGL_ES
#include <SFML/Window/EglContext.hpp> #include <SFML/Window/EglContext.hpp>
@ -54,10 +55,11 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
namespace namespace
{ {
sf::priv::WindowImplX11* fullscreenWindow = NULL; sf::priv::WindowImplX11* fullscreenWindow = NULL;
unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | std::vector<sf::priv::WindowImplX11*> allWindows;
PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
EnterWindowMask | LeaveWindowMask; PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask |
EnterWindowMask | LeaveWindowMask;
// Filter the events received by windows (only allow those matching a specific window) // Filter the events received by windows (only allow those matching a specific window)
Bool checkEvent(::Display*, XEvent* event, XPointer userData) Bool checkEvent(::Display*, XEvent* event, XPointer userData)
@ -303,6 +305,9 @@ WindowImplX11::~WindowImplX11()
// Close the connection with the X server // Close the connection with the X server
CloseDisplay(m_display); 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() void WindowImplX11::requestFocus()
{ {
// Ensure WM hints exist // Focus is only stolen among SFML windows, not between applications
XWMHints* hints = XGetWMHints(m_display, m_window); // Check the global list of windows to find out whether an SFML window has the focus
if (hints == NULL) // Note: can't handle console and other non-SFML windows belonging to the application.
bool sfmlWindowFocused = false;
for (std::vector<WindowImplX11*>::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 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; hints->flags |= XUrgencyHint;
XSetWMHints(m_display, m_window, hints); XSetWMHints(m_display, m_window, hints);
XFree(hints); XFree(hints);
} }
} }
@ -598,6 +634,9 @@ void WindowImplX11::initialize()
// Flush the commands queue // Flush the commands queue
XFlush(m_display); 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 event;
event.type = Event::GainedFocus; event.type = Event::GainedFocus;
pushEvent(event); 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; break;
} }

View File

@ -369,7 +369,27 @@ void WindowImplWin32::setKeyRepeatEnabled(bool enabled)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplWin32::requestFocus() 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);
}
} }