Files
wxWidgets/src/x11/app.cpp
T
Vadim Zeitlin 2be5f429c1 Don't create wrong wxAppTraits during wxApp construction
Creating wxAppTraits before the object of the user-defined class
deriving from wxApp is fully constructed may result in creating a wrong
traits object if the user class overrides CreateTraits() because the
correct, i.e. user-defined, version isn't used if it's called from wx
code.

This had already happened before and was worked around back in
bc334f39ec (Fixes Bug [ 930200 ] Async does not work, 2004-04-27), but
recently happened again after the changes of 94e4818caa (Warn on startup
when not using comctl32.dll v6 in wxMSW, 2022-11-28). To prevent it from
happening in the future, add WXAppConstructed() that must be called
after fully constructing wxApp (and forgetting to do it would result in
an assert, so doesn't risk to go unnoticed) and which resets m_traits if
it has been wrongly created.

It would, perhaps, be even better if we avoided creating the wrong
traits in the first place, but for now this at least fixes the latest
problem and ensures it doesn't reoccur.

Closes #23315.
2023-03-06 21:28:58 +00:00

802 lines
24 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/x11/app.cpp
// Purpose: wxApp
// Author: Julian Smart
// Modified by:
// Created: 17/09/98
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/app.h"
#ifndef WX_PRECOMP
#include "wx/hash.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/utils.h"
#include "wx/frame.h"
#include "wx/icon.h"
#include "wx/dialog.h"
#include "wx/gdicmn.h"
#include "wx/module.h"
#include "wx/crt.h"
#endif
#include "wx/evtloop.h"
#include "wx/filename.h"
#include "wx/univ/theme.h"
#include "wx/univ/renderer.h"
#include "wx/generic/private/timer.h"
#if wxUSE_THREADS
#include "wx/thread.h"
#endif
#include "wx/clipbrd.h"
#include "wx/x11/private.h"
#include <string.h>
//------------------------------------------------------------------------
// global data
//------------------------------------------------------------------------
wxWindowHash *wxWidgetHashTable = nullptr;
wxWindowHash *wxClientWidgetHashTable = nullptr;
static bool g_showIconic = false;
static wxSize g_initialSize = wxDefaultSize;
// This is required for wxFocusEvent::SetWindow(). It will only
// work for focus events which we provoke ourselves (by calling
// SetFocus()). It will not work for those events, which X11
// generates itself.
static wxWindow *g_nextFocus = nullptr;
static wxWindow *g_prevFocus = nullptr;
//------------------------------------------------------------------------
// X11 clipboard event handling
//------------------------------------------------------------------------
extern "C" void wxClipboardHandleSelectionRequest(XEvent event);
//------------------------------------------------------------------------
// X11 error handling
//------------------------------------------------------------------------
typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
XErrorHandlerFunc gs_pfnXErrorHandler = nullptr;
static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
{
// just forward to the default handler for now
if (gs_pfnXErrorHandler)
return gs_pfnXErrorHandler(dpy, xevent);
else
return 0;
}
//------------------------------------------------------------------------
// wxApp
//------------------------------------------------------------------------
long wxApp::sm_lastMessageTime = 0;
wxIMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler);
bool wxApp::Initialize(int& argC, wxChar **argV)
{
#if !wxUSE_NANOX
// install the X error handler
gs_pfnXErrorHandler = XSetErrorHandler( wxXErrorHandler );
#endif
wxString displayName;
bool syncDisplay = false;
int argCOrig = argC;
for ( int i = 0; i < argCOrig; i++ )
{
if (wxStrcmp( argV[i], wxT("-display") ) == 0)
{
if (i < (argCOrig - 1))
{
argV[i++] = nullptr;
displayName = argV[i];
argV[i] = nullptr;
argC -= 2;
}
}
else if (wxStrcmp( argV[i], wxT("-geometry") ) == 0)
{
if (i < (argCOrig - 1))
{
argV[i++] = nullptr;
int w, h;
if (wxSscanf(argV[i], wxT("%dx%d"), &w, &h) != 2)
{
wxLogError( _("Invalid geometry specification '%s'"),
wxString(argV[i]) );
}
else
{
g_initialSize = wxSize(w, h);
}
argV[i] = nullptr;
argC -= 2;
}
}
else if (wxStrcmp( argV[i], wxT("-sync") ) == 0)
{
syncDisplay = true;
argV[i] = nullptr;
argC--;
}
else if (wxStrcmp( argV[i], wxT("-iconic") ) == 0)
{
g_showIconic = true;
argV[i] = nullptr;
argC--;
}
}
if ( argC != argCOrig )
{
// remove the arguments we consumed
for ( int i = 0; i < argC; i++ )
{
while ( !argV[i] )
{
memmove(argV + i, argV + i + 1, (argCOrig - i)*sizeof(wxChar *));
}
}
}
// open and set up the X11 display
if ( !wxSetDisplay(displayName) )
{
wxLogError(_("wxWidgets could not open display. Exiting."));
return false;
}
Display *dpy = wxGlobalDisplay();
if (syncDisplay)
XSynchronize(dpy, True);
XSelectInput(dpy, XDefaultRootWindow(dpy), PropertyChangeMask);
wxSetDetectableAutoRepeat( true );
if ( !wxAppBase::Initialize(argC, argV) )
return false;
// Glib's type system required by Pango (deprecated since glib 2.36 but
// used to be required, so still call it, it's harmless).
wxGCC_WARNING_SUPPRESS(deprecated-declarations)
g_type_init();
wxGCC_WARNING_RESTORE()
#if wxUSE_INTL
wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
#endif
wxWidgetHashTable = new wxWindowHash;
wxClientWidgetHashTable = new wxWindowHash;
return true;
}
void wxApp::CleanUp()
{
wxDELETE(wxWidgetHashTable);
wxDELETE(wxClientWidgetHashTable);
wxAppBase::CleanUp();
}
wxApp::wxApp()
{
m_mainColormap = nullptr;
m_topLevelWidget = nullptr;
m_maxRequestSize = 0;
m_showIconic = false;
m_initialSize = wxDefaultSize;
#if !wxUSE_NANOX
m_visualInfo = nullptr;
#endif
WXAppConstructed();
}
wxApp::~wxApp()
{
#if !wxUSE_NANOX
delete m_visualInfo;
#endif
}
#if !wxUSE_NANOX
//-----------------------------------------------------------------------
// X11 predicate function for exposure compression
//-----------------------------------------------------------------------
struct wxExposeInfo
{
Window window;
Bool found_non_matching;
};
extern "C"
Bool wxX11ExposePredicate (Display *WXUNUSED(display), XEvent *xevent, XPointer arg)
{
wxExposeInfo *info = (wxExposeInfo*) arg;
if (info->found_non_matching)
return FALSE;
if (xevent->xany.type != Expose)
{
info->found_non_matching = true;
return FALSE;
}
if (xevent->xexpose.window != info->window)
{
info->found_non_matching = true;
return FALSE;
}
return TRUE;
}
#endif // wxUSE_NANOX
//-----------------------------------------------------------------------
// Processes an X event, returning true if the event was processed.
//-----------------------------------------------------------------------
bool wxApp::ProcessXEvent(WXEvent* _event)
{
XEvent* event = (XEvent*) _event;
wxWindow* win = nullptr;
Window window = XEventGetWindow(event);
#if 0
Window actualWindow = window;
#endif
// Find the first wxWindow that corresponds to this event window
// Because we're receiving events after a window
// has been destroyed, assume a 1:1 match between
// Window and wxWindow, so if it's not in the table,
// it must have been destroyed.
win = wxGetWindowFromTable(window);
if (!win)
{
#if wxUSE_TWO_WINDOWS
win = wxGetClientWindowFromTable(window);
if (!win)
#endif
return false;
}
switch (event->type)
{
case Expose:
{
#if wxUSE_TWO_WINDOWS && !wxUSE_NANOX
if (event->xexpose.window != (Window)win->GetClientAreaWindow())
{
XEvent tmp_event;
wxExposeInfo info;
info.window = event->xexpose.window;
info.found_non_matching = false;
while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
{
// Don't worry about optimizing redrawing the border etc.
}
win->NeedUpdateNcAreaInIdle();
}
else
#endif
{
win->GetUpdateRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
XExposeEventGetWidth(event), XExposeEventGetHeight(event));
win->GetClearRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
XExposeEventGetWidth(event), XExposeEventGetHeight(event));
#if !wxUSE_NANOX
XEvent tmp_event;
wxExposeInfo info;
info.window = event->xexpose.window;
info.found_non_matching = false;
while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
{
win->GetUpdateRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
tmp_event.xexpose.width, tmp_event.xexpose.height );
win->GetClearRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
tmp_event.xexpose.width, tmp_event.xexpose.height );
}
#endif
// This simplifies the expose and clear areas to simple
// rectangles.
win->GetUpdateRegion() = win->GetUpdateRegion().GetBox();
win->GetClearRegion() = win->GetClearRegion().GetBox();
// If we only have one X11 window, always indicate
// that borders might have to be redrawn.
if (win->X11GetMainWindow() == win->GetClientAreaWindow())
win->NeedUpdateNcAreaInIdle();
// Only erase background, paint in idle time.
win->SendEraseEvents();
// EXPERIMENT
//win->Update();
}
return true;
}
#if !wxUSE_NANOX
case GraphicsExpose:
{
wxLogTrace( wxT("expose"), wxT("GraphicsExpose from %s"), win->GetName());
win->GetUpdateRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
event->xgraphicsexpose.width, event->xgraphicsexpose.height);
win->GetClearRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
event->xgraphicsexpose.width, event->xgraphicsexpose.height);
if (event->xgraphicsexpose.count == 0)
{
// Only erase background, paint in idle time.
win->SendEraseEvents();
// win->Update();
}
return true;
}
#endif
case KeyPress:
{
if (!win->IsEnabled())
return false;
wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
wxTranslateKeyEvent(keyEvent, win, window, event);
// wxLogDebug( "OnKey from %s", win->GetName() );
// We didn't process wxEVT_KEY_DOWN, so send wxEVT_CHAR
if (win->HandleWindowEvent( keyEvent ))
return true;
// Do the translation again, retaining the ASCII
// code.
if ( wxTranslateKeyEvent(keyEvent, win, window, event, true) )
{
switch ( keyEvent.m_keyCode )
{
// for modifiers, don't send wxEVT_CHAR event.
// the definition of Modifiers, please see the doc of
// wxKeyModifier. we only take care of wxMOD_ALT, wxMOD_CONTROL
// wxMOD_SHIFT under X11 platform. Other modifiers is handled
// by window manager.
case WXK_CONTROL:
case WXK_SHIFT:
case WXK_ALT:
break;
default:
{
// process wxEVT_CHAR here
keyEvent.SetEventType(wxEVT_CHAR);
if ( win->HandleWindowEvent( keyEvent ) )
return true;
}
}
}
if ( (keyEvent.m_keyCode == WXK_TAB) &&
win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
{
wxNavigationKeyEvent new_event;
new_event.SetEventObject( win->GetParent() );
/* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
new_event.SetDirection( (keyEvent.m_keyCode == WXK_TAB) );
/* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
new_event.SetWindowChange( keyEvent.ControlDown() );
new_event.SetCurrentFocus( win );
return win->GetParent()->HandleWindowEvent( new_event );
}
return false;
}
case KeyRelease:
{
if (!win->IsEnabled())
return false;
wxKeyEvent keyEvent(wxEVT_KEY_UP);
wxTranslateKeyEvent(keyEvent, win, window, event);
// if receive the modifiers key up. set the corresponding
// keyboardState to false.
switch ( keyEvent.m_keyCode )
{
case WXK_CONTROL:
keyEvent.SetControlDown(false);
break;
case WXK_SHIFT:
keyEvent.SetShiftDown(false);
break;
case WXK_ALT:
keyEvent.SetAltDown(false);
break;
default:
break;
}
return win->HandleWindowEvent( keyEvent );
}
case ConfigureNotify:
{
#if wxUSE_NANOX
if (event->update.utype == GR_UPDATE_SIZE)
#endif
{
wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
if ( tlw )
{
tlw->SetConfigureGeometry( XConfigureEventGetX(event), XConfigureEventGetY(event),
XConfigureEventGetWidth(event), XConfigureEventGetHeight(event) );
}
if ( tlw && tlw->IsShown() )
{
tlw->SetNeedResizeInIdle();
}
else
{
wxSizeEvent sizeEvent( wxSize(XConfigureEventGetWidth(event), XConfigureEventGetHeight(event)), win->GetId() );
sizeEvent.SetEventObject( win );
return win->HandleWindowEvent( sizeEvent );
}
}
return false;
}
#if !wxUSE_NANOX
case PropertyNotify:
return HandlePropertyChange(_event);
case ClientMessage:
{
if (!win->IsEnabled())
return false;
Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);
Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);
if (event->xclient.message_type == wm_protocols)
{
if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
{
win->Close(false);
return true;
}
}
return false;
}
case SelectionRequest:
{
// A request to paste has occurred.
wxClipboardHandleSelectionRequest(*event);
// The event handle doesn't care the clipboard
// how to response requestor, so just return true.
return true;
}
#if 0
case DestroyNotify:
{
printf( "destroy from %s\n", win->GetName().c_str() );
break;
}
case CreateNotify:
{
printf( "create from %s\n", win->GetName().c_str() );
break;
}
case MapRequest:
{
printf( "map request from %s\n", win->GetName().c_str() );
break;
}
case ResizeRequest:
{
printf( "resize request from %s\n", win->GetName().c_str() );
Display *disp = (Display*) wxGetDisplay();
XEvent report;
// to avoid flicker
report = * event;
while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
wxSize sz = win->GetSize();
wxSizeEvent sizeEvent(sz, win->GetId());
sizeEvent.SetEventObject(win);
return win->HandleWindowEvent( sizeEvent );
}
#endif
#endif
#if wxUSE_NANOX
case GR_EVENT_TYPE_CLOSE_REQ:
{
if (win)
{
win->Close(false);
return true;
}
return false;
break;
}
#endif
case EnterNotify:
case LeaveNotify:
case ButtonPress:
case ButtonRelease:
case MotionNotify:
{
if (!win->IsEnabled())
return false;
// Here we check if the top level window is
// disabled, which is one aspect of modality.
wxWindow *tlw = win;
while (tlw && !tlw->IsTopLevel())
tlw = tlw->GetParent();
if (tlw && !tlw->IsEnabled())
return false;
if (event->type == ButtonPress)
{
if ((win != wxWindow::FindFocus()) && win->CanAcceptFocus())
{
// This might actually be done in wxWindow::SetFocus()
// and not here. TODO.
g_prevFocus = wxWindow::FindFocus();
g_nextFocus = win;
wxLogTrace( wxT("focus"), wxT("About to call SetFocus on %s of type %s due to button press"), win->GetName(), win->GetClassInfo()->GetClassName() );
// Record the fact that this window is
// getting the focus, because we'll need to
// check if its parent is getting a bogus
// focus and duly ignore it.
// TODO: may need to have this code in SetFocus, too.
extern wxWindow* g_GettingFocus;
g_GettingFocus = win;
win->SetFocus();
}
}
#if !wxUSE_NANOX
if (event->type == LeaveNotify || event->type == EnterNotify)
{
// Throw out NotifyGrab and NotifyUngrab
if (event->xcrossing.mode != NotifyNormal)
return false;
}
#endif
wxMouseEvent wxevent;
wxTranslateMouseEvent(wxevent, win, window, event);
return win->HandleWindowEvent( wxevent );
}
case FocusIn:
#if !wxUSE_NANOX
if ((event->xfocus.detail != NotifyPointer) &&
(event->xfocus.mode == NotifyNormal))
#endif
{
wxLogTrace( wxT("focus"), wxT("FocusIn from %s of type %s"), win->GetName(), win->GetClassInfo()->GetClassName() );
extern wxWindow* g_GettingFocus;
if (g_GettingFocus && g_GettingFocus->GetParent() == win)
{
// Ignore this, this can be a spurious FocusIn
// caused by a child having its focus set.
g_GettingFocus = nullptr;
wxLogTrace( wxT("focus"), wxT("FocusIn from %s of type %s being deliberately ignored"), win->GetName(), win->GetClassInfo()->GetClassName() );
return true;
}
else
{
wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
focusEvent.SetEventObject(win);
focusEvent.SetWindow( g_prevFocus );
g_prevFocus = nullptr;
return win->HandleWindowEvent(focusEvent);
}
}
return false;
case FocusOut:
#if !wxUSE_NANOX
if ((event->xfocus.detail != NotifyPointer) &&
(event->xfocus.mode == NotifyNormal))
#endif
{
wxLogTrace( wxT("focus"), wxT("FocusOut from %s of type %s"), win->GetName(), win->GetClassInfo()->GetClassName() );
wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
focusEvent.SetEventObject(win);
focusEvent.SetWindow( g_nextFocus );
g_nextFocus = nullptr;
return win->HandleWindowEvent(focusEvent);
}
return false;
}
return false;
}
// This should be redefined in a derived class for
// handling property change events for XAtom IPC.
bool wxApp::HandlePropertyChange(WXEvent *WXUNUSED(event))
{
// by default do nothing special
// TODO: what to do for X11
// XtDispatchEvent((XEvent*) event);
return false;
}
void wxApp::WakeUpIdle()
{
// TODO:
// Wake up the idle handler processor, even if it is in another thread...
}
// Create display, and other initialization
bool wxApp::OnInitGui()
{
#if wxUSE_LOG
// Eventually this line will be removed, but for
// now we don't want to try popping up a dialog
// for error messages.
delete wxLog::SetActiveTarget(new wxLogStderr);
#endif
if (!wxAppBase::OnInitGui())
return false;
Display *dpy = wxGlobalDisplay();
GetMainColormap(dpy);
m_maxRequestSize = XMaxRequestSize(dpy);
#if !wxUSE_NANOX
m_visualInfo = new wxXVisualInfo;
wxFillXVisualInfo(m_visualInfo, dpy);
#endif
return true;
}
#include <pango/pango.h>
#include <pango/pangoxft.h>
PangoContext* wxApp::GetPangoContext()
{
static PangoContext *s_pangoContext = nullptr;
if ( !s_pangoContext )
{
Display *dpy = wxGlobalDisplay();
int xscreen = DefaultScreen(dpy);
// Calling pango_xft_get_context() is exactly the same as doing
// pango_font_map_create_context(pango_xft_get_font_map(dpy, xscreen))
// so continue to use it even if it's deprecated to not bother with
// checking for Pango 1.2 in configure and just disable the warning.
wxGCC_WARNING_SUPPRESS(deprecated-declarations)
s_pangoContext = pango_xft_get_context(dpy, xscreen);
wxGCC_WARNING_RESTORE(deprecated-declarations)
if (!PANGO_IS_CONTEXT(s_pangoContext))
{
wxLogError( wxT("No pango context.") );
}
}
return s_pangoContext;
}
PangoContext* wxGetPangoContext()
{
PangoContext* context = wxTheApp->GetPangoContext();
g_object_ref(context);
return context;
}
WXColormap wxApp::GetMainColormap(WXDisplay* display)
{
if (!display) /* Must be called first with non-null display */
return m_mainColormap;
int defaultScreen = DefaultScreen((Display*) display);
Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
Colormap c = DefaultColormapOfScreen(screen);
if (!m_mainColormap)
m_mainColormap = (WXColormap) c;
return (WXColormap) c;
}
Window wxGetWindowParent(Window window)
{
wxASSERT_MSG( window, wxT("invalid window") );
return (Window) 0;
#ifndef __VMS
// VMS chokes on unreachable code
Window parent, root = 0;
#if wxUSE_NANOX
int noChildren = 0;
#else
unsigned int noChildren = 0;
#endif
Window* children = nullptr;
// #define XQueryTree(d,w,r,p,c,nc) GrQueryTree(w,p,c,nc)
int res = 1;
#if !wxUSE_NANOX
res =
#endif
XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
& children, & noChildren);
if (children)
XFree(children);
if (res)
return parent;
else
return (Window) 0;
#endif
}
void wxApp::Exit()
{
wxApp::CleanUp();
wxAppConsole::Exit();
}