Check if we successfully restored input focus
In rare cases, Workspace::restoreFocus() may fail, for example when the most recently activated client is about to be destroyed or unmapped. If it happens that we cannot restore the focus, then mark the window in FocusIn event as active. CCBUG: 424223
This commit is contained in:
parent
4921acf45a
commit
555885072d
11 changed files with 56 additions and 32 deletions
|
@ -550,7 +550,7 @@ public:
|
||||||
void setupWindowRules(bool ignore_temporary);
|
void setupWindowRules(bool ignore_temporary);
|
||||||
void evaluateWindowRules();
|
void evaluateWindowRules();
|
||||||
virtual void applyWindowRules();
|
virtual void applyWindowRules();
|
||||||
virtual void takeFocus() = 0;
|
virtual bool takeFocus() = 0;
|
||||||
virtual bool wantsInput() const = 0;
|
virtual bool wantsInput() const = 0;
|
||||||
/**
|
/**
|
||||||
* Whether a dock window wants input.
|
* Whether a dock window wants input.
|
||||||
|
|
|
@ -340,12 +340,12 @@ void Workspace::activateClient(AbstractClient* c, bool force)
|
||||||
*
|
*
|
||||||
* @see activateClient
|
* @see activateClient
|
||||||
*/
|
*/
|
||||||
void Workspace::requestFocus(AbstractClient* c, bool force)
|
bool Workspace::requestFocus(AbstractClient* c, bool force)
|
||||||
{
|
{
|
||||||
takeActivity(c, force ? ActivityFocusForce : ActivityFocus);
|
return takeActivity(c, force ? ActivityFocusForce : ActivityFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::takeActivity(AbstractClient* c, ActivityFlags flags)
|
bool Workspace::takeActivity(AbstractClient* c, ActivityFlags flags)
|
||||||
{
|
{
|
||||||
// the 'if ( c == active_client ) return;' optimization mustn't be done here
|
// the 'if ( c == active_client ) return;' optimization mustn't be done here
|
||||||
if (!focusChangeEnabled() && (c != active_client))
|
if (!focusChangeEnabled() && (c != active_client))
|
||||||
|
@ -353,7 +353,7 @@ void Workspace::takeActivity(AbstractClient* c, ActivityFlags flags)
|
||||||
|
|
||||||
if (!c) {
|
if (!c) {
|
||||||
focusToNull();
|
focusToNull();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ActivityFocus) {
|
if (flags & ActivityFocus) {
|
||||||
|
@ -390,16 +390,20 @@ void Workspace::takeActivity(AbstractClient* c, ActivityFlags flags)
|
||||||
}
|
}
|
||||||
if (!c->isShown(true)) { // shouldn't happen, call activateClient() if needed
|
if (!c->isShown(true)) { // shouldn't happen, call activateClient() if needed
|
||||||
qCWarning(KWIN_CORE) << "takeActivity: not shown" ;
|
qCWarning(KWIN_CORE) << "takeActivity: not shown" ;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
if (flags & ActivityFocus)
|
if (flags & ActivityFocus)
|
||||||
c->takeFocus();
|
ret &= c->takeFocus();
|
||||||
if (flags & ActivityRaise)
|
if (flags & ActivityRaise)
|
||||||
workspace()->raiseClient(c);
|
workspace()->raiseClient(c);
|
||||||
|
|
||||||
if (!c->isOnActiveScreen())
|
if (!c->isOnActiveScreen())
|
||||||
screens()->setCurrent(c->screen());
|
screens()->setCurrent(c->screen());
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -660,9 +664,13 @@ bool Workspace::allowFullClientRaising(const KWin::AbstractClient *c, xcb_timest
|
||||||
return NET::timestampCompare(time, user_time) >= 0; // time >= user_time
|
return NET::timestampCompare(time, user_time) >= 0; // time >= user_time
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from Client after FocusIn that wasn't initiated by KWin and the client
|
/**
|
||||||
// wasn't allowed to activate
|
* Called from X11Client after FocusIn that wasn't initiated by KWin and the client wasn't
|
||||||
void Workspace::restoreFocus()
|
* allowed to activate.
|
||||||
|
*
|
||||||
|
* Returns @c true if the focus has been restored successfully; otherwise returns @c false.
|
||||||
|
*/
|
||||||
|
bool Workspace::restoreFocus()
|
||||||
{
|
{
|
||||||
// this updateXTime() is necessary - as FocusIn events don't have
|
// this updateXTime() is necessary - as FocusIn events don't have
|
||||||
// a timestamp *sigh*, kwin's timestamp would be older than the timestamp
|
// a timestamp *sigh*, kwin's timestamp would be older than the timestamp
|
||||||
|
@ -670,9 +678,10 @@ void Workspace::restoreFocus()
|
||||||
// the attempt to restore the focus would fail due to old timestamp
|
// the attempt to restore the focus would fail due to old timestamp
|
||||||
updateXTime();
|
updateXTime();
|
||||||
if (should_get_focus.count() > 0)
|
if (should_get_focus.count() > 0)
|
||||||
requestFocus(should_get_focus.last());
|
return requestFocus(should_get_focus.last());
|
||||||
else if (last_active_client)
|
else if (last_active_client)
|
||||||
requestFocus(last_active_client);
|
return requestFocus(last_active_client);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::clientAttentionChanged(AbstractClient* c, bool set)
|
void Workspace::clientAttentionChanged(AbstractClient* c, bool set)
|
||||||
|
|
|
@ -1008,8 +1008,6 @@ void X11ClientTest::testActivateFocusedWindow()
|
||||||
// case no FocusIn event will be generated and the window won't be marked as active. This test
|
// case no FocusIn event will be generated and the window won't be marked as active. This test
|
||||||
// verifies that we handle that subtle case properly.
|
// verifies that we handle that subtle case properly.
|
||||||
|
|
||||||
QSKIP("Focus is not restored properly when the active client is about to be unmapped");
|
|
||||||
|
|
||||||
QScopedPointer<xcb_connection_t, XcbConnectionDeleter> connection(xcb_connect(nullptr, nullptr));
|
QScopedPointer<xcb_connection_t, XcbConnectionDeleter> connection(xcb_connect(nullptr, nullptr));
|
||||||
QVERIFY(!xcb_connection_has_error(connection.data()));
|
QVERIFY(!xcb_connection_has_error(connection.data()));
|
||||||
|
|
||||||
|
|
12
events.cpp
12
events.cpp
|
@ -1146,11 +1146,15 @@ void X11Client::focusInEvent(xcb_focus_in_event_t *e)
|
||||||
// check if this client is in should_get_focus list or if activation is allowed
|
// check if this client is in should_get_focus list or if activation is allowed
|
||||||
bool activate = workspace()->allowClientActivation(this, -1U, true);
|
bool activate = workspace()->allowClientActivation(this, -1U, true);
|
||||||
workspace()->gotFocusIn(this); // remove from should_get_focus list
|
workspace()->gotFocusIn(this); // remove from should_get_focus list
|
||||||
if (activate)
|
if (activate) {
|
||||||
setActive(true);
|
setActive(true);
|
||||||
else {
|
} else {
|
||||||
workspace()->restoreFocus();
|
if (workspace()->restoreFocus()) {
|
||||||
demandAttention();
|
demandAttention();
|
||||||
|
} else {
|
||||||
|
qCWarning(KWIN_CORE, "Failed to restore focus. Activating 0x%x", windowId());
|
||||||
|
setActive(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -353,8 +353,9 @@ void InternalClient::setOnAllActivities(bool set)
|
||||||
// Internal clients do not support activities.
|
// Internal clients do not support activities.
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalClient::takeFocus()
|
bool InternalClient::takeFocus()
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalClient::setNoBorder(bool set)
|
void InternalClient::setNoBorder(bool set)
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
bool supportsWindowRules() const override;
|
bool supportsWindowRules() const override;
|
||||||
AbstractClient *findModal(bool allow_itself = false) override;
|
AbstractClient *findModal(bool allow_itself = false) override;
|
||||||
void setOnAllActivities(bool set) override;
|
void setOnAllActivities(bool set) override;
|
||||||
void takeFocus() override;
|
bool takeFocus() override;
|
||||||
void setNoBorder(bool set) override;
|
void setNoBorder(bool set) override;
|
||||||
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
||||||
void updateColorScheme() override;
|
void updateColorScheme() override;
|
||||||
|
|
|
@ -167,17 +167,17 @@ public:
|
||||||
AbstractClient* clientUnderMouse(int screen) const;
|
AbstractClient* clientUnderMouse(int screen) const;
|
||||||
|
|
||||||
void activateClient(AbstractClient*, bool force = false);
|
void activateClient(AbstractClient*, bool force = false);
|
||||||
void requestFocus(AbstractClient* c, bool force = false);
|
bool requestFocus(AbstractClient* c, bool force = false);
|
||||||
enum ActivityFlag {
|
enum ActivityFlag {
|
||||||
ActivityFocus = 1 << 0, // focus the window
|
ActivityFocus = 1 << 0, // focus the window
|
||||||
ActivityFocusForce = 1 << 1 | ActivityFocus, // focus even if Dock etc.
|
ActivityFocusForce = 1 << 1 | ActivityFocus, // focus even if Dock etc.
|
||||||
ActivityRaise = 1 << 2 // raise the window
|
ActivityRaise = 1 << 2 // raise the window
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(ActivityFlags, ActivityFlag)
|
Q_DECLARE_FLAGS(ActivityFlags, ActivityFlag)
|
||||||
void takeActivity(AbstractClient* c, ActivityFlags flags);
|
bool takeActivity(AbstractClient* c, ActivityFlags flags);
|
||||||
bool allowClientActivation(const AbstractClient* c, xcb_timestamp_t time = -1U, bool focus_in = false,
|
bool allowClientActivation(const AbstractClient* c, xcb_timestamp_t time = -1U, bool focus_in = false,
|
||||||
bool ignore_desktop = false);
|
bool ignore_desktop = false);
|
||||||
void restoreFocus();
|
bool restoreFocus();
|
||||||
void gotFocusIn(const AbstractClient*);
|
void gotFocusIn(const AbstractClient*);
|
||||||
void setShouldGetFocus(AbstractClient*);
|
void setShouldGetFocus(AbstractClient*);
|
||||||
bool activateNextClient(AbstractClient* c);
|
bool activateNextClient(AbstractClient* c);
|
||||||
|
|
|
@ -2021,12 +2021,20 @@ void X11Client::setOnAllActivities(bool on)
|
||||||
/**
|
/**
|
||||||
* Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
|
* Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
|
||||||
*/
|
*/
|
||||||
void X11Client::takeFocus()
|
bool X11Client::takeFocus()
|
||||||
{
|
{
|
||||||
if (rules()->checkAcceptFocus(info->input()))
|
if (rules()->checkAcceptFocus(info->input())) {
|
||||||
m_client.focus();
|
xcb_void_cookie_t cookie = xcb_set_input_focus_checked(connection(),
|
||||||
else
|
XCB_INPUT_FOCUS_POINTER_ROOT,
|
||||||
|
windowId(), XCB_TIME_CURRENT_TIME);
|
||||||
|
ScopedCPointer<xcb_generic_error_t> error(xcb_request_check(connection(), cookie));
|
||||||
|
if (error) {
|
||||||
|
qCWarning(KWIN_CORE, "Failed to focus 0x%x (error %d)", windowId(), error->error_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
demandAttention(false); // window cannot take input, at least withdraw urgency
|
demandAttention(false); // window cannot take input, at least withdraw urgency
|
||||||
|
}
|
||||||
if (info->supportsProtocol(NET::TakeFocusProtocol)) {
|
if (info->supportsProtocol(NET::TakeFocusProtocol)) {
|
||||||
updateXTime();
|
updateXTime();
|
||||||
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
|
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
|
||||||
|
@ -2044,6 +2052,8 @@ void X11Client::takeFocus()
|
||||||
}
|
}
|
||||||
if (breakShowingDesktop)
|
if (breakShowingDesktop)
|
||||||
workspace()->setShowingDesktop(false);
|
workspace()->setShowingDesktop(false);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -177,7 +177,7 @@ public:
|
||||||
bool isMovableAcrossScreens() const override;
|
bool isMovableAcrossScreens() const override;
|
||||||
bool isCloseable() const override; ///< May be closed by the user (May have a close button)
|
bool isCloseable() const override; ///< May be closed by the user (May have a close button)
|
||||||
|
|
||||||
void takeFocus() override;
|
bool takeFocus() override;
|
||||||
|
|
||||||
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
||||||
|
|
||||||
|
|
|
@ -1033,7 +1033,7 @@ void XdgToplevelClient::doFinishMoveResize()
|
||||||
scheduleConfigure();
|
scheduleConfigure();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgToplevelClient::takeFocus()
|
bool XdgToplevelClient::takeFocus()
|
||||||
{
|
{
|
||||||
if (wantsInput()) {
|
if (wantsInput()) {
|
||||||
sendPing(PingReason::FocusWindow);
|
sendPing(PingReason::FocusWindow);
|
||||||
|
@ -1042,6 +1042,7 @@ void XdgToplevelClient::takeFocus()
|
||||||
if (!keepAbove() && !isOnScreenDisplay() && !belongsToDesktop()) {
|
if (!keepAbove() && !isOnScreenDisplay() && !belongsToDesktop()) {
|
||||||
workspace()->setShowingDesktop(false);
|
workspace()->setShowingDesktop(false);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XdgToplevelClient::wantsInput() const
|
bool XdgToplevelClient::wantsInput() const
|
||||||
|
@ -2130,8 +2131,9 @@ bool XdgPopupClient::wantsInput() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgPopupClient::takeFocus()
|
bool XdgPopupClient::takeFocus()
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XdgPopupClient::acceptsFocus() const
|
bool XdgPopupClient::acceptsFocus() const
|
||||||
|
|
|
@ -164,7 +164,7 @@ public:
|
||||||
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
||||||
void updateColorScheme() override;
|
void updateColorScheme() override;
|
||||||
bool supportsWindowRules() const override;
|
bool supportsWindowRules() const override;
|
||||||
void takeFocus() override;
|
bool takeFocus() override;
|
||||||
bool wantsInput() const override;
|
bool wantsInput() const override;
|
||||||
bool dockWantsInput() const override;
|
bool dockWantsInput() const override;
|
||||||
bool hasStrut() const override;
|
bool hasStrut() const override;
|
||||||
|
@ -270,7 +270,7 @@ public:
|
||||||
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
void updateDecoration(bool check_workspace_pos, bool force = false) override;
|
||||||
void showOnScreenEdge() override;
|
void showOnScreenEdge() override;
|
||||||
bool wantsInput() const override;
|
bool wantsInput() const override;
|
||||||
void takeFocus() override;
|
bool takeFocus() override;
|
||||||
bool supportsWindowRules() const override;
|
bool supportsWindowRules() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Reference in a new issue