Avoid using gdk_window_freeze_updates() to implement Freeze()

Implement Freeze() by blocking the GtkWindow "expose-event"/"draw" signal
instead. Since the introduction of client-side windows in GTK+ 2.18,
gdk_window_freeze_updates() is unuseable because the impl_window (and thus the
update_freeze_count) for a given GdkWindow can change unpredictably. See #16795
This commit is contained in:
Paul Cornett 2015-06-24 08:43:30 -07:00
parent c15ae5e4a3
commit b7cf54d24a
3 changed files with 41 additions and 49 deletions

View File

@ -383,6 +383,7 @@ protected:
virtual void DoFreeze() wxOVERRIDE; virtual void DoFreeze() wxOVERRIDE;
virtual void DoThaw() wxOVERRIDE; virtual void DoThaw() wxOVERRIDE;
void GTKConnectFreezeWidget(GtkWidget* widget);
void GTKFreezeWidget(GtkWidget *w); void GTKFreezeWidget(GtkWidget *w);
void GTKThawWidget(GtkWidget *w); void GTKThawWidget(GtkWidget *w);
void GTKDisconnect(void* instance); void GTKDisconnect(void* instance);

View File

@ -695,6 +695,7 @@ bool wxTextCtrl::Create( wxWindow *parent,
gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList); gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList);
// Create view // Create view
m_text = gtk_text_view_new_with_buffer(m_buffer); m_text = gtk_text_view_new_with_buffer(m_buffer);
GTKConnectFreezeWidget(m_text);
// gtk_text_view_set_buffer adds its own reference // gtk_text_view_set_buffer adds its own reference
g_object_unref(m_buffer); g_object_unref(m_buffer);
g_signal_handler_disconnect(m_buffer, sig_id); g_signal_handler_disconnect(m_buffer, sig_id);
@ -1975,6 +1976,8 @@ void wxTextCtrl::DoFreeze()
wxCHECK_RET(m_text != NULL, wxT("invalid text ctrl")); wxCHECK_RET(m_text != NULL, wxT("invalid text ctrl"));
GTKFreezeWidget(m_text); GTKFreezeWidget(m_text);
if (m_widget != m_text)
GTKFreezeWidget(m_widget);
if ( HasFlag(wxTE_MULTILINE) ) if ( HasFlag(wxTE_MULTILINE) )
{ {
@ -2021,6 +2024,8 @@ void wxTextCtrl::DoThaw()
} }
GTKThawWidget(m_text); GTKThawWidget(m_text);
if (m_widget != m_text)
GTKThawWidget(m_widget);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -2138,9 +2138,6 @@ static void frame_clock_layout(GdkFrameClock*, wxWindow* win)
void wxWindowGTK::GTKHandleRealized() void wxWindowGTK::GTKHandleRealized()
{ {
if (IsFrozen())
DoFreeze();
GdkWindow* const window = GTKGetDrawingWindow(); GdkWindow* const window = GTKGetDrawingWindow();
if (m_wxwindow) if (m_wxwindow)
@ -2224,11 +2221,6 @@ void wxWindowGTK::GTKHandleUnrealize()
{ {
m_isGtkPositionValid = false; m_isGtkPositionValid = false;
// unrealizing a frozen window seems to have some lingering effect
// preventing updates to the affected area
if (IsFrozen())
DoThaw();
if (m_wxwindow) if (m_wxwindow)
{ {
if (m_imContext) if (m_imContext)
@ -2578,12 +2570,6 @@ wxWindowGTK::~wxWindowGTK()
m_imContext = NULL; m_imContext = NULL;
} }
// avoid problem with GTK+ 2.18 where a frozen window causes the whole
// TLW to be frozen, and if the window is then destroyed, nothing ever
// gets painted again
while (IsFrozen())
Thaw();
#ifdef __WXGTK3__ #ifdef __WXGTK3__
if (m_styleProvider) if (m_styleProvider)
g_object_unref(m_styleProvider); g_object_unref(m_styleProvider);
@ -2629,6 +2615,10 @@ void wxWindowGTK::PostCreation()
{ {
wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
GTKConnectFreezeWidget(m_widget);
if (m_wxwindow && m_wxwindow != m_widget)
GTKConnectFreezeWidget(m_wxwindow);
#if wxGTK_HAS_COMPOSITING_SUPPORT #if wxGTK_HAS_COMPOSITING_SUPPORT
// Set RGBA visual as soon as possible to minimize the possibility that // Set RGBA visual as soon as possible to minimize the possibility that
// somebody uses the wrong one. // somebody uses the wrong one.
@ -5120,53 +5110,49 @@ GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
// freeze/thaw // freeze/thaw
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
extern "C" {
static gboolean draw_freeze(GtkWidget*, void*, wxWindow*)
{
// stop other handlers from being invoked
return true;
}
}
void wxWindowGTK::GTKConnectFreezeWidget(GtkWidget* widget)
{
#ifdef __WXGTK3__
gulong id = g_signal_connect(widget, "draw", G_CALLBACK(draw_freeze), this);
#else
gulong id = g_signal_connect(widget, "expose-event", G_CALLBACK(draw_freeze), this);
#endif
g_signal_handler_block(widget, id);
}
void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget) void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
{ {
if (widget && gtk_widget_get_has_window(widget)) g_signal_handlers_unblock_by_func(widget, (void*)draw_freeze, this);
{
GdkWindow* window = gtk_widget_get_window(widget);
if (window)
{
#if GTK_CHECK_VERSION(2,18,0)
#ifndef __WXGTK3__
if (gtk_check_version(2,18,0) == NULL)
#endif
{
// impl_window for a non-native GdkWindow can change if
// gdk_window_ensure_native() is called on it or some other
// GdkWindow in the same TLW. Since the freeze count is on the
// impl_window, we have to make sure impl_window does not change
// after we call gdk_window_freeze_updates().
gdk_window_ensure_native(window);
}
#endif
gdk_window_freeze_updates(window);
}
}
} }
void wxWindowGTK::GTKThawWidget(GtkWidget* widget) void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
{ {
if (widget && gtk_widget_get_has_window(widget)) g_signal_handlers_block_by_func(widget, (void*)draw_freeze, this);
{ gtk_widget_queue_draw(widget);
GdkWindow* window = gtk_widget_get_window(widget);
if (window)
gdk_window_thaw_updates(window);
}
} }
void wxWindowGTK::DoFreeze() void wxWindowGTK::DoFreeze()
{ {
GtkWidget* widget = m_wxwindow; wxCHECK_RET(m_widget, "invalid window");
if (widget == NULL)
widget = m_widget; GTKFreezeWidget(m_widget);
GTKFreezeWidget(widget); if (m_wxwindow && m_wxwindow != m_widget)
GTKFreezeWidget(m_wxwindow);
} }
void wxWindowGTK::DoThaw() void wxWindowGTK::DoThaw()
{ {
GtkWidget* widget = m_wxwindow; wxCHECK_RET(m_widget, "invalid window");
if (widget == NULL)
widget = m_widget; GTKThawWidget(m_widget);
GTKThawWidget(widget); if (m_wxwindow && m_wxwindow != m_widget)
GTKThawWidget(m_wxwindow);
} }