forked from mirrors/gecko-dev
Bug 1904424 [Linux/X11] Don't steal focus on X11 r=emilio a=RyanVM
The patch comes from https://gitlab.com/redhat/centos-stream/rpms/firefox/-/merge_requests/94 Firefox isn't currently handling window focusing right on X11... There's a few problems: If no startup id is provided, it tries to get user time from g_get_monotonic_time when there's no guarantee that timestamp is synchronized or even in a compatible unit. This merge request addressed that by dropping the code that uses g_get_monotonic_time entirely, and instead adding a heuristic to synthesize a startup id based on the time of the last user interaction on the display. This makes an assumption that firefox was started as a result of user interaction, which might not always hold true, but it's more likely than not, and is basically the same assumption firefox is already making by trying to use a timestamp of "now" (g_get_monotonic_time) when focusing the window. If a startup id is provided, it fails to tell gtk to use it when focusing the window because it calls gtk_window_present_with_time with some other random timestamp instead of passing GDK_CURRENT_TIME (which is the magic value that means "use startup id for timestamp"). This commit tries to detect when a startup id is available, and the display is X11, and if so use a timestamp of GDK_CURRENT_TIME. Differential Revision: https://phabricator.services.mozilla.com/D217174
This commit is contained in:
parent
1a694701d5
commit
075294349b
5 changed files with 96 additions and 7 deletions
|
|
@ -47,11 +47,7 @@ bool nsDBusRemoteServer::HandleOpenURL(const gchar* aInterfaceName,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
guint32 timestamp = gtk_get_current_event_time();
|
HandleCommandLine(aParam, gtk_get_current_event_time());
|
||||||
if (timestamp == GDK_CURRENT_TIME) {
|
|
||||||
timestamp = guint32(g_get_monotonic_time() / 1000);
|
|
||||||
}
|
|
||||||
HandleCommandLine(aParam, timestamp);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4943,8 +4943,12 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
|
||||||
// Try to remote the entire command line. If this fails, start up
|
// Try to remote the entire command line. If this fails, start up
|
||||||
// normally.
|
// normally.
|
||||||
# ifdef MOZ_WIDGET_GTK
|
# ifdef MOZ_WIDGET_GTK
|
||||||
const auto& startupToken =
|
auto& startupToken =
|
||||||
GdkIsWaylandDisplay() ? mXDGActivationToken : mDesktopStartupID;
|
GdkIsWaylandDisplay() ? mXDGActivationToken : mDesktopStartupID;
|
||||||
|
# ifdef MOZ_X11
|
||||||
|
if (GdkIsX11Display() && startupToken.IsEmpty())
|
||||||
|
startupToken = SynthesizeStartupToken();
|
||||||
|
# endif /* MOZ_X11 */
|
||||||
# else
|
# else
|
||||||
const nsCString startupToken;
|
const nsCString startupToken;
|
||||||
# endif
|
# endif
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
#ifdef MOZ_X11
|
||||||
|
# include <X11/Xlib.h>
|
||||||
|
# include <X11/Xatom.h>
|
||||||
|
#endif /* MOZ_X11 */
|
||||||
|
|
||||||
#undef LOGW
|
#undef LOGW
|
||||||
#ifdef MOZ_LOGGING
|
#ifdef MOZ_LOGGING
|
||||||
# include "mozilla/Logging.h"
|
# include "mozilla/Logging.h"
|
||||||
|
|
@ -498,4 +503,70 @@ bool IsCancelledGError(GError* aGError) {
|
||||||
return g_error_matches(aGError, G_IO_ERROR, G_IO_ERROR_CANCELLED);
|
return g_error_matches(aGError, G_IO_ERROR, G_IO_ERROR_CANCELLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MOZ_X11)
|
||||||
|
static unsigned long GetWindowUserTime(GdkDisplay* aDisplay,
|
||||||
|
uintptr_t aWindow) {
|
||||||
|
Atom actualType;
|
||||||
|
int actualFormat;
|
||||||
|
unsigned long numberOfItems;
|
||||||
|
unsigned long bytesAfter;
|
||||||
|
unsigned char* property = nullptr;
|
||||||
|
unsigned long userTime = 0;
|
||||||
|
|
||||||
|
Display* xDisplay = GDK_DISPLAY_XDISPLAY(aDisplay);
|
||||||
|
Atom atom =
|
||||||
|
gdk_x11_get_xatom_by_name_for_display(aDisplay, "_NET_WM_USER_TIME");
|
||||||
|
|
||||||
|
if (XGetWindowProperty(xDisplay, aWindow, atom, 0, 1, false, XA_CARDINAL,
|
||||||
|
&actualType, &actualFormat, &numberOfItems,
|
||||||
|
&bytesAfter, &property) == Success &&
|
||||||
|
property) {
|
||||||
|
if (numberOfItems == 1) {
|
||||||
|
userTime = *((unsigned long*)property);
|
||||||
|
}
|
||||||
|
XFree(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindLatestUserTime(GdkDisplay* aDisplay, uintptr_t aWindow,
|
||||||
|
unsigned long* aLatestTime) {
|
||||||
|
Window rootReturn;
|
||||||
|
Window parentReturn;
|
||||||
|
Window* children;
|
||||||
|
unsigned int numberOfChildren;
|
||||||
|
unsigned long userTime;
|
||||||
|
|
||||||
|
Display* xDisplay = GDK_DISPLAY_XDISPLAY(aDisplay);
|
||||||
|
|
||||||
|
if (XQueryTree(xDisplay, aWindow, &rootReturn, &parentReturn, &children,
|
||||||
|
&numberOfChildren)) {
|
||||||
|
for (unsigned int i = 0; i < numberOfChildren; i++) {
|
||||||
|
userTime = GetWindowUserTime(aDisplay, children[i]);
|
||||||
|
if (userTime > *aLatestTime) {
|
||||||
|
*aLatestTime = userTime;
|
||||||
|
}
|
||||||
|
FindLatestUserTime(aDisplay, children[i], aLatestTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume we're started from user interaction and infer user time if its missing
|
||||||
|
nsCString SynthesizeStartupToken() {
|
||||||
|
unsigned long latestUserTime = 0;
|
||||||
|
FindLatestUserTime(gdk_display_get_default(),
|
||||||
|
GDK_WINDOW_XID(gdk_get_default_root_window()),
|
||||||
|
&latestUserTime);
|
||||||
|
|
||||||
|
if (latestUserTime == 0) {
|
||||||
|
return nsCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsPrintfCString("%s_TIME%lu", g_get_host_name(), latestUserTime);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace mozilla::widget
|
} // namespace mozilla::widget
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "mozilla/MozPromise.h"
|
#include "mozilla/MozPromise.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct _GdkDisplay GdkDisplay;
|
typedef struct _GdkDisplay GdkDisplay;
|
||||||
|
|
@ -78,6 +77,13 @@ RefPtr<FocusRequestPromise> RequestWaylandFocusPromise();
|
||||||
|
|
||||||
bool IsCancelledGError(GError* aGError);
|
bool IsCancelledGError(GError* aGError);
|
||||||
|
|
||||||
|
#if defined(MOZ_X11)
|
||||||
|
// Used by startup notifications
|
||||||
|
nsCString SynthesizeStartupToken();
|
||||||
|
void FindLatestUserTime(GdkDisplay* aDisplay, uintptr_t aWindow,
|
||||||
|
unsigned long* aLatestTime);
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace mozilla::widget
|
} // namespace mozilla::widget
|
||||||
|
|
||||||
#endif // WidgetUtilsGtk_h__
|
#endif // WidgetUtilsGtk_h__
|
||||||
|
|
|
||||||
|
|
@ -3102,6 +3102,18 @@ void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(MOZ_X11)
|
||||||
|
// If it's X11 and there's a startup token, use GDK_CURRENT_TIME, so
|
||||||
|
// gtk_window_present_with_time will pull the timestamp from the startup
|
||||||
|
// token.
|
||||||
|
if (GdkIsX11Display()) {
|
||||||
|
nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
|
||||||
|
const auto& startupToken = toolkit->GetStartupToken();
|
||||||
|
if (!startupToken.IsEmpty()) {
|
||||||
|
return static_cast<uint32_t>(GDK_CURRENT_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return GetLastUserInputTime();
|
return GetLastUserInputTime();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue