From f137f19bb75c22bfe3ff61972a669d12b1c1c3f7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 May 2022 00:01:15 +0200 Subject: [PATCH 1/4] Factor out wxGTKImpl::LogFilter from GTKSuppressDiagnostics() Refactor the code to separate setting of the log callback from the filtering decision. Right now the only existing filter is the one just checking the log level, which is used by GTKSuppressDiagnostics(), but this will allow adding other filters in the upcoming commits. --- include/wx/gtk/private/log.h | 99 ++++++++++++++++++++++++++++++++++++ src/gtk/app.cpp | 70 +++++++++++++++---------- 2 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 include/wx/gtk/private/log.h diff --git a/include/wx/gtk/private/log.h b/include/wx/gtk/private/log.h new file mode 100644 index 0000000000..155bb2b578 --- /dev/null +++ b/include/wx/gtk/private/log.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/gtk/private/log.h +// Purpose: Support for filtering GTK log messages. +// Author: Vadim Zeitlin +// Created: 2022-05-11 +// Copyright: (c) 2022 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_GTK_PRIVATE_LOG_H_ +#define _WX_GTK_PRIVATE_LOG_H_ + +#include + +// Support for custom log writers is only available in glib 2.50 or later. +#if GLIB_CHECK_VERSION(2, 50, 0) + #define wxHAS_GLIB_LOG_WRITER +#endif + +namespace wxGTKImpl +{ + +#ifdef wxHAS_GLIB_LOG_WRITER + +// LogFilter is the base class for filtering GTK log messages +// +// Note that all members of this class are defined in src/gtk/app.cpp. +class LogFilter +{ +public: + LogFilter() + { + m_next = NULL; + } + + // Function to call to install this filter as the active one. + // Does nothing if run-time glib version is too old. + void Install(); + +protected: + // Function to override in the derived class to actually filter: return + // true if the message should be suppressed or false if it should be passed + // through to the default writer (which may, or not, show it). + virtual bool Filter(GLogLevelFlags log_level, + const GLogField* fields, + gsize n_fields) const = 0; + +private: + // The function used as glib log writer. + static GLogWriterOutput + wx_log_writer(GLogLevelFlags log_level, + const GLogField *fields, + gsize n_fields, + gpointer user_data); + + // False initially, set to true when we install wx_log_writer() as the log + // writer. Once we do it, we never change it any more. + static bool ms_installed; + + // We maintain a simple linked list of log filters and this is the head of + // this list. + static LogFilter* ms_first; + + // Next entry in the linked list, may be null. + LogFilter* m_next; + + wxDECLARE_NO_COPY_CLASS(LogFilter); +}; + +// LogFilterByLevel filters out all the messages at the specified level. +class LogFilterByLevel : public LogFilter +{ +public: + LogFilterByLevel() { } + + void SetLevelToIgnore(int flags) + { + m_logLevelToIgnore = flags; + } + +protected: + bool Filter(GLogLevelFlags log_level, + const GLogField* WXUNUSED(fields), + gsize WXUNUSED(n_fields)) const wxOVERRIDE + { + return log_level & m_logLevelToIgnore; + } + +private: + int m_logLevelToIgnore; + + wxDECLARE_NO_COPY_CLASS(LogFilterByLevel); +}; + +#endif // wxHAS_GLIB_LOG_WRITER + +} // namespace wxGTKImpl + +#endif // _WX_GTK_PRIVATE_LOG_H_ diff --git a/src/gtk/app.cpp b/src/gtk/app.cpp index ab392b150e..619d02ebcf 100644 --- a/src/gtk/app.cpp +++ b/src/gtk/app.cpp @@ -30,6 +30,7 @@ #include "wx/msgout.h" #include "wx/gtk/private.h" +#include "wx/gtk/private/log.h" #include "wx/gtk/mimetype.h" //----------------------------------------------------------------------------- @@ -178,48 +179,65 @@ bool wxApp::DoIdle() return keepSource; } -// Custom Glib log writer: setting it is only possible with glib 2.50 or later. -#if GLIB_CHECK_VERSION(2, 50, 0) -extern "C" { -static GLogWriterOutput -wx_log_writer(GLogLevelFlags log_level, - const GLogField *fields, - gsize n_fields, - gpointer user_data) -{ - const wxUIntPtr log_mask = reinterpret_cast(user_data); +#ifdef wxHAS_GLIB_LOG_WRITER - GLogWriterOutput result; - if (log_level & log_mask) +namespace wxGTKImpl +{ + +bool LogFilter::ms_installed = false; +LogFilter* LogFilter::ms_first = NULL; + +/* static */ +GLogWriterOutput +LogFilter::wx_log_writer(GLogLevelFlags log_level, + const GLogField *fields, + gsize n_fields, + gpointer WXUNUSED(user_data)) +{ + for ( const LogFilter* lf = LogFilter::ms_first; lf; lf = lf->m_next ) { - result = G_LOG_WRITER_HANDLED; + if ( lf->Filter(log_level, fields, n_fields) ) + return G_LOG_WRITER_HANDLED; } - else + + return g_log_writer_default(log_level, fields, n_fields, NULL); +} + +void LogFilter::Install() +{ + if ( !ms_installed ) { - result = g_log_writer_default(log_level, fields, n_fields, NULL); + if ( glib_check_version(2, 50, 0) != 0 ) + { + // No runtime support for log callback, we can't do anything. + return; + } + + g_log_set_writer_func(LogFilter::wx_log_writer, NULL, NULL); + ms_installed = true; } - return result; -} + + // Put this object in front of the linked list. + m_next = ms_first; + ms_first = this; } +} // namespace wxGTKImpl + /* static */ void wxApp::GTKSuppressDiagnostics(int flags) { - if (glib_check_version(2, 50, 0) == 0) - { - g_log_set_writer_func( - wx_log_writer, - (wxUIntToPtr)(flags == -1 ? G_LOG_LEVEL_MASK : flags), - NULL); - } + static wxGTKImpl::LogFilterByLevel s_logFilter; + s_logFilter.SetLevelToIgnore(flags); + s_logFilter.Install(); } -#else // glib < 2.50 +#else // !wxHAS_GLIB_LOG_WRITER /* static */ void wxApp::GTKSuppressDiagnostics(int WXUNUSED(flags)) { // We can't do anything here. } -#endif // glib >=/< 2.50 +#endif // wxHAS_GLIB_LOG_WRITER/!wxHAS_GLIB_LOG_WRITER //----------------------------------------------------------------------------- // wxApp From 4602caf49d683516379420d72977b1d8828e4afa Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 May 2022 00:27:39 +0200 Subject: [PATCH 2/4] Add LogFilterByMessage GTK log filter This will allow suppressing GTK log messages with the specified fixed contents. --- include/wx/gtk/private/log.h | 29 +++++++++++++++++++++++++++ src/gtk/app.cpp | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/include/wx/gtk/private/log.h b/include/wx/gtk/private/log.h index 155bb2b578..c8c745a76c 100644 --- a/include/wx/gtk/private/log.h +++ b/include/wx/gtk/private/log.h @@ -45,6 +45,9 @@ protected: const GLogField* fields, gsize n_fields) const = 0; + // Typically called from the derived class dtor to stop using this filter. + void Uninstall(); + private: // The function used as glib log writer. static GLogWriterOutput @@ -92,6 +95,32 @@ private: wxDECLARE_NO_COPY_CLASS(LogFilterByLevel); }; +// LogFilterByMessage filters out all the messages with the specified content. +class LogFilterByMessage : public LogFilter +{ +public: + // Objects of this class are supposed to be created with literal strings as + // argument, so don't bother copying the string but just use the pointer. + explicit LogFilterByMessage(const char* message) + : m_message(message) + { + Install(); + } + + // Remove this filter when the object goes out of scope. + ~LogFilterByMessage(); + +protected: + bool Filter(GLogLevelFlags WXUNUSED(log_level), + const GLogField* fields, + gsize n_fields) const wxOVERRIDE; + +private: + const char* const m_message; + + wxDECLARE_NO_COPY_CLASS(LogFilterByMessage); +}; + #endif // wxHAS_GLIB_LOG_WRITER } // namespace wxGTKImpl diff --git a/src/gtk/app.cpp b/src/gtk/app.cpp index 619d02ebcf..288ae1373b 100644 --- a/src/gtk/app.cpp +++ b/src/gtk/app.cpp @@ -222,6 +222,45 @@ void LogFilter::Install() ms_first = this; } +void LogFilter::Uninstall() +{ + if ( !ms_installed ) + { + // We don't do anything at all in this case. + return; + } + + // We should be uninstalling only the currently installed filter. + wxASSERT( ms_first == this ); + + ms_first = m_next; +} + +bool LogFilterByMessage::Filter(GLogLevelFlags WXUNUSED(log_level), + const GLogField* fields, + gsize n_fields) const +{ + for ( gsize n = 0; n < n_fields; ++n ) + { + const GLogField& f = fields[n]; + if ( strcmp(f.key, "MESSAGE") == 0 ) + { + if ( strcmp(static_cast(f.value), m_message) == 0 ) + { + // This is the message we want to filter. + return true; + } + } + } + + return false; +} + +LogFilterByMessage::~LogFilterByMessage() +{ + Uninstall(); +} + } // namespace wxGTKImpl /* static */ From 8f49ecc3d0c73b79eb2ae2980225bd6e57392feb Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 May 2022 00:28:27 +0200 Subject: [PATCH 3/4] Suppress bogus GTK messages when removing wxNotebook pages These messages are due to an assertion failure deep inside ATK which doesn't indicate any real problem (as failure to get the label of an already destroyed tab is normal and is already handled correctly in the GTK code) and can't be avoided, so suppress them to avoid showing them to the users who can't do anything at all about them anyhow, but can be scared by the "CRITICAL" GTK messages. Closes #22176. --- src/gtk/notebook.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gtk/notebook.cpp b/src/gtk/notebook.cpp index 3b7eb94cd1..1791b4b729 100644 --- a/src/gtk/notebook.cpp +++ b/src/gtk/notebook.cpp @@ -26,6 +26,7 @@ #include "wx/gtk/private.h" #include "wx/gtk/private/image.h" +#include "wx/gtk/private/log.h" #include "wx/gtk/private/stylecontext.h" //----------------------------------------------------------------------------- @@ -426,6 +427,12 @@ wxNotebookPage *wxNotebook::DoRemovePage( size_t page ) if ( !client ) return NULL; + // Suppress bogus assertion failures happening deep inside ATK (used by + // GTK) that can't be avoided in any other way, see #22176. + wxGTKImpl::LogFilterByMessage filterLog( + "gtk_notebook_get_tab_label: assertion 'list != NULL' failed" + ); + // we don't need to unparent the client->m_widget; GTK+ will do // that for us (and will throw a warning if we do it!) gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page ); From bffcb88266f11b02d5772f8667058ac141f3291c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 May 2022 00:34:54 +0200 Subject: [PATCH 4/4] Optionally detect not filtered GTK log messages In the future it might be useful to run the code with WXTRACE=gtklog to see if any GTK log messages we're filtering don't need to be filtered any longer. --- include/wx/gtk/private/log.h | 13 ++++++++++--- src/gtk/app.cpp | 12 ++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/wx/gtk/private/log.h b/include/wx/gtk/private/log.h index c8c745a76c..662342e373 100644 --- a/include/wx/gtk/private/log.h +++ b/include/wx/gtk/private/log.h @@ -34,8 +34,8 @@ public: } // Function to call to install this filter as the active one. - // Does nothing if run-time glib version is too old. - void Install(); + // Does nothing and just returns false if run-time glib version is too old. + bool Install(); protected: // Function to override in the derived class to actually filter: return @@ -104,10 +104,15 @@ public: explicit LogFilterByMessage(const char* message) : m_message(message) { - Install(); + // We shouldn't warn about anything if Install() failed. + m_warnNotFiltered = Install(); } // Remove this filter when the object goes out of scope. + // + // The dtor also checks if we actually filtered the message and logs a + // trace message with the "gtklog" mask if we didn't: this allows checking + // if the filter is actually being used. ~LogFilterByMessage(); protected: @@ -118,6 +123,8 @@ protected: private: const char* const m_message; + mutable bool m_warnNotFiltered; + wxDECLARE_NO_COPY_CLASS(LogFilterByMessage); }; diff --git a/src/gtk/app.cpp b/src/gtk/app.cpp index 288ae1373b..4e8bd2926c 100644 --- a/src/gtk/app.cpp +++ b/src/gtk/app.cpp @@ -203,14 +203,14 @@ LogFilter::wx_log_writer(GLogLevelFlags log_level, return g_log_writer_default(log_level, fields, n_fields, NULL); } -void LogFilter::Install() +bool LogFilter::Install() { if ( !ms_installed ) { if ( glib_check_version(2, 50, 0) != 0 ) { // No runtime support for log callback, we can't do anything. - return; + return false; } g_log_set_writer_func(LogFilter::wx_log_writer, NULL, NULL); @@ -220,6 +220,8 @@ void LogFilter::Install() // Put this object in front of the linked list. m_next = ms_first; ms_first = this; + + return true; } void LogFilter::Uninstall() @@ -248,6 +250,7 @@ bool LogFilterByMessage::Filter(GLogLevelFlags WXUNUSED(log_level), if ( strcmp(static_cast(f.value), m_message) == 0 ) { // This is the message we want to filter. + m_warnNotFiltered = false; return true; } } @@ -259,6 +262,11 @@ bool LogFilterByMessage::Filter(GLogLevelFlags WXUNUSED(log_level), LogFilterByMessage::~LogFilterByMessage() { Uninstall(); + + if ( m_warnNotFiltered ) + { + wxLogTrace("gtklog", "Message \"%s\" wasn't logged.", m_message); + } } } // namespace wxGTKImpl