Merge branch 'gtk-log-suppress'
Allow suppressing GTK log messages. See #22424.
This commit is contained in:
135
include/wx/gtk/private/log.h
Normal file
135
include/wx/gtk/private/log.h
Normal file
@@ -0,0 +1,135 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_GTK_PRIVATE_LOG_H_
|
||||
#define _WX_GTK_PRIVATE_LOG_H_
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
// 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 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
|
||||
// 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;
|
||||
|
||||
// Typically called from the derived class dtor to stop using this filter.
|
||||
void Uninstall();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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:
|
||||
bool Filter(GLogLevelFlags WXUNUSED(log_level),
|
||||
const GLogField* fields,
|
||||
gsize n_fields) const wxOVERRIDE;
|
||||
|
||||
private:
|
||||
const char* const m_message;
|
||||
|
||||
mutable bool m_warnNotFiltered;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(LogFilterByMessage);
|
||||
};
|
||||
|
||||
#endif // wxHAS_GLIB_LOG_WRITER
|
||||
|
||||
} // namespace wxGTKImpl
|
||||
|
||||
#endif // _WX_GTK_PRIVATE_LOG_H_
|
||||
115
src/gtk/app.cpp
115
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,112 @@ 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<wxUIntPtr>(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);
|
||||
}
|
||||
|
||||
bool 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 false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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<const char*>(f.value), m_message) == 0 )
|
||||
{
|
||||
// This is the message we want to filter.
|
||||
m_warnNotFiltered = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFilterByMessage::~LogFilterByMessage()
|
||||
{
|
||||
Uninstall();
|
||||
|
||||
if ( m_warnNotFiltered )
|
||||
{
|
||||
wxLogTrace("gtklog", "Message \"%s\" wasn't logged.", m_message);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user