Merge branch 'feature/window_focus'

This commit is contained in:
Lukas Dürrenberger 2014-10-09 14:56:16 +02:00
commit e2b3cdacc2
16 changed files with 364 additions and 19 deletions

View File

@ -403,6 +403,7 @@ public:
/// on the previous thread first if it was active. /// on the previous thread first if it was active.
/// Only one window can be active on a thread at a time, thus /// Only one window can be active on a thread at a time, thus
/// the window previously active (if any) automatically gets deactivated. /// the window previously active (if any) automatically gets deactivated.
/// This is not to be confused with requestFocus().
/// ///
/// \param active True to activate, false to deactivate /// \param active True to activate, false to deactivate
/// ///
@ -411,6 +412,35 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool setActive(bool active = true) const; bool setActive(bool active = true) const;
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
/// At any given time, only one window may have the input focus
/// to receive input events such as keystrokes or mouse events.
/// If a window requests focus, it only hints to the operating
/// system, that it would like to be focused. The operating system
/// is free to deny the request.
/// This is not to be confused with setActive().
///
/// \see hasFocus
///
////////////////////////////////////////////////////////////
void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// At any given time, only one window may have the input focus
/// to receive input events such as keystrokes or most mouse
/// events.
///
/// \return True if window has focus, false otherwise
/// \see requestFocus
///
////////////////////////////////////////////////////////////
bool hasFocus() const;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Display on screen what has been rendered to the window so far /// \brief Display on screen what has been rendered to the window so far
/// ///

View File

@ -181,6 +181,21 @@ void WindowImplAndroid::setKeyRepeatEnabled(bool enabled)
} }
////////////////////////////////////////////////////////////
void WindowImplAndroid::requestFocus()
{
// Not applicable
}
////////////////////////////////////////////////////////////
bool WindowImplAndroid::hasFocus() const
{
// Not applicable
return false;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplAndroid::forwardEvent(const Event& event) void WindowImplAndroid::forwardEvent(const Event& event)
{ {

View File

@ -154,6 +154,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled); virtual void setKeyRepeatEnabled(bool enabled);
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const;
static void forwardEvent(const Event& event); static void forwardEvent(const Event& event);
static WindowImplAndroid* singleInstance; static WindowImplAndroid* singleInstance;

View File

@ -187,6 +187,26 @@
} }
////////////////////////////////////////////////////////
-(void)requestFocus
{
// Note: this doesn't imply that the view will get any event.
// The user has to make sure events are forwarded to the view
// with the usual responder chain.
[[m_view window] makeKeyAndOrderFront:nil];
// In case the app is not active, make its dock icon bounce for one sec
[NSApp requestUserAttention:NSInformationalRequest];
}
////////////////////////////////////////////////////////////
-(BOOL)hasFocus
{
return [NSApp keyWindow] == [m_view window];
}
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
-(void)enableKeyRepeat -(void)enableKeyRepeat
{ {

View File

@ -451,6 +451,23 @@
} }
////////////////////////////////////////////////////////
-(void)requestFocus
{
[m_window makeKeyAndOrderFront:nil];
// In case the app is not active, make its dock icon bounce for one sec
[NSApp requestUserAttention:NSInformationalRequest];
}
////////////////////////////////////////////////////////////
-(BOOL)hasFocus
{
return [NSApp keyWindow] == m_window;
}
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
-(void)enableKeyRepeat -(void)enableKeyRepeat
{ {

View File

@ -320,6 +320,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled); virtual void setKeyRepeatEnabled(bool enabled);
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const;
protected: protected:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -552,6 +552,20 @@ void WindowImplCocoa::setKeyRepeatEnabled(bool enabled)
} }
////////////////////////////////////////////////////////////
void WindowImplCocoa::requestFocus()
{
[m_delegate requestFocus];
}
////////////////////////////////////////////////////////////
bool WindowImplCocoa::hasFocus() const
{
return [m_delegate hasFocus];
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -166,6 +166,21 @@ namespace sf {
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
-(void)closeWindow; -(void)closeWindow;
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
-(void)requestFocus;
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
-(BOOL)hasFocus;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Enable key repeat /// \brief Enable key repeat
/// ///

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>
@ -55,6 +56,7 @@
namespace namespace
{ {
sf::priv::WindowImplX11* fullscreenWindow = NULL; sf::priv::WindowImplX11* fullscreenWindow = NULL;
std::vector<sf::priv::WindowImplX11*> allWindows;
unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask |
EnterWindowMask | LeaveWindowMask; EnterWindowMask | LeaveWindowMask;
@ -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));
} }
@ -483,6 +488,67 @@ void WindowImplX11::setKeyRepeatEnabled(bool enabled)
} }
////////////////////////////////////////////////////////////
void WindowImplX11::requestFocus()
{
// 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<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
{
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
{
// 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);
}
}
////////////////////////////////////////////////////////////
bool WindowImplX11::hasFocus() const
{
::Window focusedWindow = 0;
int revertToReturn = 0;
XGetInputFocus(m_display, &focusedWindow, &revertToReturn);
return m_window == focusedWindow;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplX11::switchToFullscreen(const VideoMode& mode) void WindowImplX11::switchToFullscreen(const VideoMode& mode)
{ {
@ -568,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);
} }
@ -680,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

@ -154,6 +154,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled); virtual void setKeyRepeatEnabled(bool enabled);
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const;
protected: protected:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -366,6 +366,40 @@ void WindowImplWin32::setKeyRepeatEnabled(bool enabled)
} }
////////////////////////////////////////////////////////////
void WindowImplWin32::requestFocus()
{
// 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);
}
}
////////////////////////////////////////////////////////////
bool WindowImplWin32::hasFocus() const
{
return m_handle == GetForegroundWindow();
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplWin32::registerWindowClass() void WindowImplWin32::registerWindowClass()
{ {

View File

@ -153,6 +153,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled); virtual void setKeyRepeatEnabled(bool enabled);
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const;
protected: protected:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -337,6 +337,22 @@ bool Window::setActive(bool active) const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Window::requestFocus()
{
if (m_impl)
m_impl->requestFocus();
}
////////////////////////////////////////////////////////////
bool Window::hasFocus() const
{
return m_impl && m_impl->hasFocus();
}
////////////////////////////////////////////////////////////
void Window::display() void Window::display()
{ {
// Display the backbuffer on screen // Display the backbuffer on screen

View File

@ -194,6 +194,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled) = 0; virtual void setKeyRepeatEnabled(bool enabled) = 0;
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus() = 0;
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const = 0;
protected: protected:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -157,6 +157,21 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled); virtual void setKeyRepeatEnabled(bool enabled);
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
virtual void requestFocus();
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return True if window has focus, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool hasFocus() const;
public: public:
using WindowImpl::pushEvent; using WindowImpl::pushEvent;

View File

@ -170,6 +170,21 @@ void WindowImplUIKit::setKeyRepeatEnabled(bool enabled)
} }
////////////////////////////////////////////////////////////
void WindowImplUIKit::requestFocus()
{
// Not applicable
}
////////////////////////////////////////////////////////////
bool WindowImplUIKit::hasFocus() const
{
// Not applicable
return false;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SFView* WindowImplUIKit::getGlView() const SFView* WindowImplUIKit::getGlView() const
{ {