зеркало из https://github.com/mozilla/gecko-dev.git
b=509278 suppress painting while the window is invisible and detect viewability changes through signals r=roc
This commit is contained in:
Родитель
d7bf04ddc4
Коммит
46584e05b2
|
@ -134,8 +134,6 @@ D_DEBUG_DOMAIN( ns_Window, "nsWindow", "nsWindow" );
|
|||
}
|
||||
#include "gfxDirectFBSurface.h"
|
||||
#define GDK_WINDOW_XWINDOW(_win) _win
|
||||
#else
|
||||
#define D_DEBUG_AT(x,y...) do {} while (0)
|
||||
#endif
|
||||
|
||||
// Don't put more than this many rects in the dirty region, just fluff
|
||||
|
@ -200,6 +198,8 @@ static gboolean scroll_event_cb (GtkWidget *widget,
|
|||
GdkEventScroll *event);
|
||||
static gboolean visibility_notify_event_cb(GtkWidget *widget,
|
||||
GdkEventVisibility *event);
|
||||
static void hierarchy_changed_cb (GtkWidget *widget,
|
||||
GtkWidget *previous_toplevel);
|
||||
static gboolean window_state_event_cb (GtkWidget *widget,
|
||||
GdkEventWindowState *event);
|
||||
static void theme_changed_cb (GtkSettings *settings,
|
||||
|
@ -364,6 +364,17 @@ static PRBool gForce24bpp = PR_FALSE;
|
|||
|
||||
static GtkWidget *gInvisibleContainer = NULL;
|
||||
|
||||
// Some gobject functions expect functions for gpointer arguments.
|
||||
// gpointer is void* but C++ doesn't like casting functions to void*.
|
||||
template<class T> gpointer
|
||||
FuncToGpointer(T aFunction)
|
||||
{
|
||||
return reinterpret_cast<gpointer>
|
||||
(reinterpret_cast<uintptr_t>
|
||||
// This cast just provides a warning if T is not a function.
|
||||
(reinterpret_cast<void (*)()>(aFunction)));
|
||||
}
|
||||
|
||||
nsWindow::nsWindow()
|
||||
{
|
||||
mIsTopLevel = PR_FALSE;
|
||||
|
@ -384,7 +395,8 @@ nsWindow::nsWindow()
|
|||
mContainerGotFocus = PR_FALSE;
|
||||
mContainerLostFocus = PR_FALSE;
|
||||
mContainerBlockFocus = PR_FALSE;
|
||||
mIsVisible = PR_FALSE;
|
||||
mHasMappedToplevel = PR_FALSE;
|
||||
mIsFullyObscured = PR_FALSE;
|
||||
mRetryPointerGrab = PR_FALSE;
|
||||
mRetryKeyboardGrab = PR_FALSE;
|
||||
mTransientParent = nsnull;
|
||||
|
@ -710,7 +722,7 @@ nsWindow::Destroy(void)
|
|||
}
|
||||
|
||||
g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
|
||||
(gpointer)G_CALLBACK(theme_changed_cb),
|
||||
FuncToGpointer(theme_changed_cb),
|
||||
this);
|
||||
|
||||
// ungrab if required
|
||||
|
@ -846,11 +858,11 @@ nsWindow::SetParent(nsIWidget *aNewParent)
|
|||
NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(mGdkWindow)->destroyed,
|
||||
"destroyed GdkWindow with widget");
|
||||
|
||||
nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
|
||||
GdkWindow* newParentWindow = NULL;
|
||||
GtkWidget* newContainer = NULL;
|
||||
if (aNewParent) {
|
||||
newParentWindow = static_cast<GdkWindow*>
|
||||
(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
|
||||
newParentWindow = newParent->mGdkWindow;
|
||||
if (newParentWindow) {
|
||||
newContainer = get_gtk_widget_for_gdk_window(newParentWindow);
|
||||
}
|
||||
|
@ -879,6 +891,12 @@ nsWindow::SetParent(nsIWidget *aNewParent)
|
|||
gdk_window_reparent(mGdkWindow, newParentWindow, 0, 0);
|
||||
}
|
||||
|
||||
PRBool parentHasMappedToplevel =
|
||||
newParent && newParent->mHasMappedToplevel;
|
||||
if (mHasMappedToplevel != parentHasMappedToplevel) {
|
||||
SetHasMappedToplevel(parentHasMappedToplevel);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -921,33 +939,6 @@ nsWindow::IsVisible(PRBool& aState)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Internal method, which returns false when no part of the window can be seen.
|
||||
PRBool
|
||||
nsWindow::CanBeSeen()
|
||||
{
|
||||
// mIsVisible keeps track of whether any part of the window is unobscured,
|
||||
// but does not get updated when the window changes state from viewable to
|
||||
// not viewable (when any ancestor window is unmapped) because
|
||||
// VisibilityNotifty events do not get sent on such a change in state.
|
||||
if (!mIsShown || !mIsVisible)
|
||||
return PR_FALSE;
|
||||
|
||||
GtkWidget *topWidget = nsnull;
|
||||
GetToplevelWidget(&topWidget);
|
||||
|
||||
// If the toplevel widget has been unmapped, then record this in
|
||||
// mIsVisible. mIsVisible will be updated in OnVisibilityNotifyEvent()
|
||||
// when its window becomes viewable again.
|
||||
// (gdk_window_is_viewable() is not suitable here as it does not
|
||||
// check GDK_WINDOW_STATE_ICONIFIED.)
|
||||
mIsVisible =
|
||||
topWidget &&
|
||||
!(gdk_window_get_state(topWidget->window) &
|
||||
(GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
|
||||
|
||||
return mIsVisible;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY)
|
||||
{
|
||||
|
@ -980,10 +971,19 @@ nsWindow::ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY)
|
|||
NS_IMETHODIMP
|
||||
nsWindow::Show(PRBool aState)
|
||||
{
|
||||
if (aState == mIsShown)
|
||||
return NS_OK;
|
||||
|
||||
mIsShown = aState;
|
||||
|
||||
LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
|
||||
|
||||
if (aState) {
|
||||
// Now that this window is shown, mHasMappedToplevel needs to be
|
||||
// tracked on viewable descendants.
|
||||
SetHasMappedToplevel(mHasMappedToplevel);
|
||||
}
|
||||
|
||||
// Ok, someone called show on a window that isn't sized to a sane
|
||||
// value. Mark this window as needing to have Show() called on it
|
||||
// and return.
|
||||
|
@ -1661,7 +1661,7 @@ NS_IMETHODIMP
|
|||
nsWindow::Invalidate(const nsIntRect &aRect,
|
||||
PRBool aIsSynchronous)
|
||||
{
|
||||
if (!mGdkWindow || !CanBeSeen())
|
||||
if (!mGdkWindow)
|
||||
return NS_OK;
|
||||
|
||||
GdkRectangle rect;
|
||||
|
@ -1686,6 +1686,8 @@ nsWindow::Update()
|
|||
if (!mGdkWindow)
|
||||
return NS_OK;
|
||||
|
||||
LOGDRAW(("Update [%p] %p\n", this, mGdkWindow));
|
||||
|
||||
gdk_window_process_updates(mGdkWindow, FALSE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2062,7 +2064,8 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!mGdkWindow)
|
||||
// Windows that are not visible will be painted after they become visible.
|
||||
if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
|
||||
return FALSE;
|
||||
|
||||
static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
|
||||
|
@ -3215,10 +3218,23 @@ void
|
|||
nsWindow::OnVisibilityNotifyEvent(GtkWidget *aWidget,
|
||||
GdkEventVisibility *aEvent)
|
||||
{
|
||||
LOGDRAW(("Visibility event %i on [%p] %p\n",
|
||||
aEvent->state, this, aEvent->window));
|
||||
|
||||
if (!mGdkWindow)
|
||||
return;
|
||||
|
||||
switch (aEvent->state) {
|
||||
case GDK_VISIBILITY_UNOBSCURED:
|
||||
case GDK_VISIBILITY_PARTIAL:
|
||||
mIsVisible = PR_TRUE;
|
||||
if (mIsFullyObscured && mHasMappedToplevel) {
|
||||
// GDK_EXPOSE events have been ignored, so make sure GDK
|
||||
// doesn't think that the window has already been painted.
|
||||
gdk_window_invalidate_rect(mGdkWindow, NULL, FALSE);
|
||||
}
|
||||
|
||||
mIsFullyObscured = PR_FALSE;
|
||||
|
||||
#ifdef MOZ_PLATFORM_HILDON
|
||||
#ifdef USE_XIM
|
||||
// In Hildon/Maemo, a browser window will get into 'patially visible' state wheneven an
|
||||
|
@ -3234,7 +3250,7 @@ nsWindow::OnVisibilityNotifyEvent(GtkWidget *aWidget,
|
|||
EnsureGrabs();
|
||||
break;
|
||||
default: // includes GDK_VISIBILITY_FULLY_OBSCURED
|
||||
mIsVisible = PR_FALSE;
|
||||
mIsFullyObscured = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3245,6 +3261,27 @@ nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
|
|||
LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
|
||||
(void *)this, aEvent->changed_mask, aEvent->new_window_state));
|
||||
|
||||
if (IS_MOZ_CONTAINER(aWidget)) {
|
||||
// This event is notifying the container widget of changes to the
|
||||
// toplevel window. Just detect changes affecting whether windows are
|
||||
// viewable.
|
||||
//
|
||||
// (A visibility notify event is sent to each window that becomes
|
||||
// viewable when the toplevel is mapped, but we can't rely on that for
|
||||
// setting mHasMappedToplevel because these toplevel window state
|
||||
// events are asynchronous. The windows in the hierarchy now may not
|
||||
// be the same windows as when the toplevel was mapped, so they may
|
||||
// not get VisibilityNotify events.)
|
||||
PRBool mapped =
|
||||
!(aEvent->new_window_state &
|
||||
(GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
|
||||
if (mHasMappedToplevel != mapped) {
|
||||
SetHasMappedToplevel(mapped);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// else the widget is a shell widget.
|
||||
|
||||
nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
|
||||
|
||||
// We don't care about anything but changes in the maximized/icon
|
||||
|
@ -3872,6 +3909,10 @@ nsWindow::Create(nsIWidget *aParent,
|
|||
case eWindowType_child: {
|
||||
if (parentMozContainer) {
|
||||
mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
|
||||
nsWindow *parentnsWindow =
|
||||
get_window_for_gdk_window(parentGdkWindow);
|
||||
if (parentnsWindow)
|
||||
mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
|
||||
}
|
||||
else if (parentGtkContainer) {
|
||||
GtkWidget *container = moz_container_new();
|
||||
|
@ -3959,6 +4000,10 @@ nsWindow::Create(nsIWidget *aParent,
|
|||
G_CALLBACK(scroll_event_cb), NULL);
|
||||
g_signal_connect(G_OBJECT(mContainer), "visibility_notify_event",
|
||||
G_CALLBACK(visibility_notify_event_cb), NULL);
|
||||
g_signal_connect(G_OBJECT(mContainer), "hierarchy_changed",
|
||||
G_CALLBACK(hierarchy_changed_cb), NULL);
|
||||
// Initialize mHasMappedToplevel.
|
||||
hierarchy_changed_cb(GTK_WIDGET(mContainer), NULL);
|
||||
|
||||
gtk_drag_dest_set((GtkWidget *)mContainer,
|
||||
(GtkDestDefaults)0,
|
||||
|
@ -4253,6 +4298,48 @@ nsWindow::NativeShow (PRBool aAction)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::SetHasMappedToplevel(PRBool aState)
|
||||
{
|
||||
// Even when aState == mHasMappedToplevel (as when this method is called
|
||||
// from Show()), child windows need to have their state checked, so don't
|
||||
// return early.
|
||||
PRBool oldState = mHasMappedToplevel;
|
||||
mHasMappedToplevel = aState;
|
||||
|
||||
// mHasMappedToplevel is not updated for children of windows that are
|
||||
// hidden; GDK knows not to send expose events for these windows. The
|
||||
// state is recorded on the hidden window itself, but, for child trees of
|
||||
// hidden windows, their state essentially becomes disconnected from their
|
||||
// hidden parent. When the hidden parent gets shown, the child trees are
|
||||
// reconnected, and the state of the window being shown can be easily
|
||||
// propagated.
|
||||
if (!mIsShown || !mGdkWindow)
|
||||
return;
|
||||
|
||||
if (aState && !oldState && !mIsFullyObscured) {
|
||||
// GDK_EXPOSE events have been ignored but the window is now visible,
|
||||
// so make sure GDK doesn't think that the window has already been
|
||||
// painted.
|
||||
gdk_window_invalidate_rect(mGdkWindow, NULL, FALSE);
|
||||
|
||||
// Check that a grab didn't fail due to the window not being
|
||||
// viewable.
|
||||
EnsureGrabs();
|
||||
}
|
||||
|
||||
for (GList *children = gdk_window_peek_children(mGdkWindow);
|
||||
children;
|
||||
children = children->next) {
|
||||
GdkWindow *gdkWin = GDK_WINDOW(children->data);
|
||||
nsWindow *child = get_window_for_gdk_window(gdkWin);
|
||||
|
||||
if (child && child->mHasMappedToplevel != aState) {
|
||||
child->SetHasMappedToplevel(aState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
nsWindow::GetSafeWindowSize(nsIntSize aSize)
|
||||
{
|
||||
|
@ -4546,7 +4633,7 @@ nsWindow::GrabPointer(void)
|
|||
// If the window isn't visible, just set the flag to retry the
|
||||
// grab. When this window becomes visible, the grab will be
|
||||
// retried.
|
||||
if (!CanBeSeen()) {
|
||||
if (!mHasMappedToplevel || mIsFullyObscured) {
|
||||
LOG(("GrabPointer: window not visible\n"));
|
||||
mRetryPointerGrab = PR_TRUE;
|
||||
return;
|
||||
|
@ -4583,7 +4670,7 @@ nsWindow::GrabKeyboard(void)
|
|||
// If the window isn't visible, just set the flag to retry the
|
||||
// grab. When this window becomes visible, the grab will be
|
||||
// retried.
|
||||
if (!CanBeSeen()) {
|
||||
if (!mHasMappedToplevel || mIsFullyObscured) {
|
||||
LOG(("GrabKeyboard: window not visible\n"));
|
||||
mRetryKeyboardGrab = PR_TRUE;
|
||||
return;
|
||||
|
@ -5654,6 +5741,44 @@ visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
hierarchy_changed_cb (GtkWidget *widget,
|
||||
GtkWidget *previous_toplevel)
|
||||
{
|
||||
GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
|
||||
GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
|
||||
GdkEventWindowState event;
|
||||
|
||||
event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
|
||||
|
||||
if (GTK_IS_WINDOW(previous_toplevel)) {
|
||||
g_signal_handlers_disconnect_by_func(previous_toplevel,
|
||||
FuncToGpointer(window_state_event_cb),
|
||||
widget);
|
||||
if (widget->window) {
|
||||
old_window_state = gdk_window_get_state(widget->window);
|
||||
}
|
||||
}
|
||||
|
||||
if (GTK_IS_WINDOW(toplevel)) {
|
||||
g_signal_connect_swapped(toplevel, "window-state-event",
|
||||
G_CALLBACK(window_state_event_cb), widget);
|
||||
if (widget->window) {
|
||||
event.new_window_state = gdk_window_get_state(widget->window);
|
||||
}
|
||||
}
|
||||
|
||||
event.changed_mask = static_cast<GdkWindowState>
|
||||
(old_window_state ^ event.new_window_state);
|
||||
|
||||
if (event.changed_mask) {
|
||||
event.type = GDK_WINDOW_STATE;
|
||||
event.window = NULL;
|
||||
event.send_event = TRUE;
|
||||
window_state_event_cb(widget, &event);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
gboolean
|
||||
window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
|
||||
|
|
|
@ -273,7 +273,8 @@ public:
|
|||
PRBool aRepaint);
|
||||
|
||||
virtual void NativeShow (PRBool aAction);
|
||||
virtual nsIntSize GetSafeWindowSize(nsIntSize aSize);
|
||||
void SetHasMappedToplevel(PRBool aState);
|
||||
nsIntSize GetSafeWindowSize(nsIntSize aSize);
|
||||
|
||||
void EnsureGrabs (void);
|
||||
void GrabPointer (void);
|
||||
|
@ -431,7 +432,6 @@ protected:
|
|||
PRPackedBool mPlaced;
|
||||
|
||||
private:
|
||||
PRBool CanBeSeen();
|
||||
void GetToplevelWidget(GtkWidget **aWidget);
|
||||
GtkWidget *GetMozContainerWidget();
|
||||
nsWindow *GetContainerWindow();
|
||||
|
@ -452,7 +452,8 @@ private:
|
|||
PRUint32 mContainerGotFocus : 1,
|
||||
mContainerLostFocus : 1,
|
||||
mContainerBlockFocus : 1,
|
||||
mIsVisible : 1,
|
||||
mHasMappedToplevel : 1,
|
||||
mIsFullyObscured : 1,
|
||||
mRetryPointerGrab : 1,
|
||||
mRetryKeyboardGrab : 1;
|
||||
GtkWindow *mTransientParent;
|
||||
|
|
Загрузка…
Ссылка в новой задаче