1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "app.h"
19 #include "wx/gdicmn.h"
21 #include "wx/dialog.h"
23 #include "wx/module.h"
24 #include "wx/memory.h"
27 #include "wx/evtloop.h"
29 #include "wx/filename.h"
32 #include "wx/univ/theme.h"
33 #include "wx/univ/renderer.h"
36 #include "wx/thread.h"
39 #include "wx/x11/private.h"
43 //------------------------------------------------------------------------
45 //------------------------------------------------------------------------
47 extern wxList wxPendingDelete
;
49 wxHashTable
*wxWidgetHashTable
= NULL
;
50 wxHashTable
*wxClientWidgetHashTable
= NULL
;
52 static bool g_showIconic
= FALSE
;
53 static wxSize g_initialSize
= wxDefaultSize
;
55 // This is required for wxFocusEvent::SetWindow(). It will only
56 // work for focus events which we provoke ourselves (by calling
57 // SetFocus()). It will not work for those events, which X11
59 static wxWindow
*g_nextFocus
= NULL
;
60 static wxWindow
*g_prevFocus
= NULL
;
62 //------------------------------------------------------------------------
64 //------------------------------------------------------------------------
67 typedef int (*XErrorHandlerFunc
)(Display
*, XErrorEvent
*);
69 XErrorHandlerFunc gs_pfnXErrorHandler
= 0;
71 static int wxXErrorHandler(Display
*dpy
, XErrorEvent
*xevent
)
73 // just forward to the default handler for now
74 if (gs_pfnXErrorHandler
)
75 return gs_pfnXErrorHandler(dpy
, xevent
);
81 //------------------------------------------------------------------------
83 //------------------------------------------------------------------------
85 long wxApp::sm_lastMessageTime
= 0;
86 WXDisplay
*wxApp::ms_display
= NULL
;
88 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
90 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
91 EVT_IDLE(wxApp::OnIdle
)
94 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
96 #if defined(__WXDEBUG__) && !wxUSE_NANOX
97 // install the X error handler
98 gs_pfnXErrorHandler
= XSetErrorHandler( wxXErrorHandler
);
101 char *displayName
= NULL
;
102 bool syncDisplay
= FALSE
;
105 for ( int i
= 0; i
< argcOrig
; i
++ )
107 if (wxStrcmp( argv
[i
], _T("-display") ) == 0)
113 displayName
= argv
[i
];
119 else if (wxStrcmp( argv
[i
], _T("-geometry") ) == 0)
126 if (wxSscanf(argv
[i
], _T("%dx%d"), &w
, &h
) != 2)
128 wxLogError( _("Invalid geometry specification '%s'"),
129 wxString::FromAscii(argv
[i
]).c_str() );
133 g_initialSize
= wxSize(w
, h
);
140 else if (wxStrcmp( argv
[i
], _T("-sync") ) == 0)
147 else if (wxStrcmp( argv
[i
], _T("-iconic") ) == 0)
156 if ( argc
!= argcOrig
)
158 // remove the argumens we consumed
159 for ( int i
= 0; i
< argc
; i
++ )
163 memmove(argv
+ i
, argv
+ i
+ 1, argcOrig
- i
);
169 Display
*xdisplay
= XOpenDisplay( displayName
);
172 wxLogError( _("wxWindows could not open display. Exiting.") );
176 if ( !wxAppBase::Initialize(argc
, argv
) )
178 XCloseDisplay(xdisplay
);
184 XSynchronize(xdisplay
, True
);
186 ms_display
= (WXDisplay
*) xdisplay
;
188 XSelectInput( xdisplay
, XDefaultRootWindow(xdisplay
), PropertyChangeMask
);
191 wxSetDetectableAutoRepeat( TRUE
);
194 // Glib's type system required by Pango
199 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
202 wxWidgetHashTable
= new wxHashTable(wxKEY_INTEGER
);
203 wxClientWidgetHashTable
= new wxHashTable(wxKEY_INTEGER
);
208 void wxApp::CleanUp()
210 delete wxWidgetHashTable
;
211 wxWidgetHashTable
= NULL
;
212 delete wxClientWidgetHashTable
;
213 wxClientWidgetHashTable
= NULL
;
215 wxAppBase::CleanUp();
220 // TODO: parse the command line
224 m_mainColormap
= (WXColormap
) NULL
;
225 m_topLevelWidget
= (WXWindow
) NULL
;
226 m_maxRequestSize
= 0;
228 m_showIconic
= FALSE
;
229 m_initialSize
= wxDefaultSize
;
243 bool wxApp::Initialized()
251 int wxApp::MainLoop()
254 m_mainLoop
= new wxEventLoop
;
256 rt
= m_mainLoop
->Run();
264 //-----------------------------------------------------------------------
265 // X11 predicate function for exposure compression
266 //-----------------------------------------------------------------------
271 Bool found_non_matching
;
274 static Bool
expose_predicate (Display
*display
, XEvent
*xevent
, XPointer arg
)
276 wxExposeInfo
*info
= (wxExposeInfo
*) arg
;
278 if (info
->found_non_matching
)
281 if (xevent
->xany
.type
!= Expose
)
283 info
->found_non_matching
= TRUE
;
287 if (xevent
->xexpose
.window
!= info
->window
)
289 info
->found_non_matching
= TRUE
;
298 //-----------------------------------------------------------------------
299 // Processes an X event, returning TRUE if the event was processed.
300 //-----------------------------------------------------------------------
302 bool wxApp::ProcessXEvent(WXEvent
* _event
)
304 XEvent
* event
= (XEvent
*) _event
;
306 wxWindow
* win
= NULL
;
307 Window window
= XEventGetWindow(event
);
309 Window actualWindow
= window
;
312 // Find the first wxWindow that corresponds to this event window
313 // Because we're receiving events after a window
314 // has been destroyed, assume a 1:1 match between
315 // Window and wxWindow, so if it's not in the table,
316 // it must have been destroyed.
318 win
= wxGetWindowFromTable(window
);
321 #if wxUSE_TWO_WINDOWS
322 win
= wxGetClientWindowFromTable(window
);
329 wxString windowClass
= win
->GetClassInfo()->GetClassName();
336 #if wxUSE_TWO_WINDOWS && !wxUSE_NANOX
337 if (event
->xexpose
.window
!= (Window
)win
->GetClientAreaWindow())
341 info
.window
= event
->xexpose
.window
;
342 info
.found_non_matching
= FALSE
;
343 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event
, expose_predicate
, (XPointer
) &info
))
345 // Don't worry about optimizing redrawing the border etc.
347 win
->NeedUpdateNcAreaInIdle();
352 win
->GetUpdateRegion().Union( XExposeEventGetX(event
), XExposeEventGetY(event
),
353 XExposeEventGetWidth(event
), XExposeEventGetHeight(event
));
354 win
->GetClearRegion().Union( XExposeEventGetX(event
), XExposeEventGetY(event
),
355 XExposeEventGetWidth(event
), XExposeEventGetHeight(event
));
360 info
.window
= event
->xexpose
.window
;
361 info
.found_non_matching
= FALSE
;
362 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event
, expose_predicate
, (XPointer
) &info
))
364 win
->GetUpdateRegion().Union( tmp_event
.xexpose
.x
, tmp_event
.xexpose
.y
,
365 tmp_event
.xexpose
.width
, tmp_event
.xexpose
.height
);
367 win
->GetClearRegion().Union( tmp_event
.xexpose
.x
, tmp_event
.xexpose
.y
,
368 tmp_event
.xexpose
.width
, tmp_event
.xexpose
.height
);
372 // This simplifies the expose and clear areas to simple
374 win
->GetUpdateRegion() = win
->GetUpdateRegion().GetBox();
375 win
->GetClearRegion() = win
->GetClearRegion().GetBox();
377 // If we only have one X11 window, always indicate
378 // that borders might have to be redrawn.
379 if (win
->GetMainWindow() == win
->GetClientAreaWindow())
380 win
->NeedUpdateNcAreaInIdle();
382 // Only erase background, paint in idle time.
383 win
->SendEraseEvents();
395 printf( "GraphicExpose event\n" );
397 wxLogTrace( _T("expose"), _T("GraphicsExpose from %s"), win
->GetName().c_str());
399 win
->GetUpdateRegion().Union( event
->xgraphicsexpose
.x
, event
->xgraphicsexpose
.y
,
400 event
->xgraphicsexpose
.width
, event
->xgraphicsexpose
.height
);
402 win
->GetClearRegion().Union( event
->xgraphicsexpose
.x
, event
->xgraphicsexpose
.y
,
403 event
->xgraphicsexpose
.width
, event
->xgraphicsexpose
.height
);
405 if (event
->xgraphicsexpose
.count
== 0)
407 // Only erase background, paint in idle time.
408 win
->SendEraseEvents();
418 if (!win
->IsEnabled())
421 wxKeyEvent
keyEvent(wxEVT_KEY_DOWN
);
422 wxTranslateKeyEvent(keyEvent
, win
, window
, event
);
424 // wxLogDebug( "OnKey from %s", win->GetName().c_str() );
426 // We didn't process wxEVT_KEY_DOWN, so send wxEVT_CHAR
427 if (win
->GetEventHandler()->ProcessEvent( keyEvent
))
430 keyEvent
.SetEventType(wxEVT_CHAR
);
431 // Do the translation again, retaining the ASCII
433 wxTranslateKeyEvent(keyEvent
, win
, window
, event
, TRUE
);
434 if (win
->GetEventHandler()->ProcessEvent( keyEvent
))
437 if ( (keyEvent
.m_keyCode
== WXK_TAB
) &&
438 win
->GetParent() && (win
->GetParent()->HasFlag( wxTAB_TRAVERSAL
)) )
440 wxNavigationKeyEvent new_event
;
441 new_event
.SetEventObject( win
->GetParent() );
442 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
443 new_event
.SetDirection( (keyEvent
.m_keyCode
== WXK_TAB
) );
444 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
445 new_event
.SetWindowChange( keyEvent
.ControlDown() );
446 new_event
.SetCurrentFocus( win
);
447 return win
->GetParent()->GetEventHandler()->ProcessEvent( new_event
);
454 if (!win
->IsEnabled())
457 wxKeyEvent
keyEvent(wxEVT_KEY_UP
);
458 wxTranslateKeyEvent(keyEvent
, win
, window
, event
);
460 return win
->GetEventHandler()->ProcessEvent( keyEvent
);
462 case ConfigureNotify
:
465 if (event
->update
.utype
== GR_UPDATE_SIZE
)
468 if (win
->IsTopLevel())
470 wxTopLevelWindow
*tlw
= (wxTopLevelWindow
*) win
;
471 tlw
->SetConfigureGeometry( XConfigureEventGetX(event
), XConfigureEventGetY(event
),
472 XConfigureEventGetWidth(event
), XConfigureEventGetHeight(event
) );
475 if (win
->IsTopLevel() && win
->IsShown())
477 wxTopLevelWindowX11
*tlw
= (wxTopLevelWindowX11
*) win
;
478 tlw
->SetNeedResizeInIdle();
482 wxSizeEvent
sizeEvent( wxSize(XConfigureEventGetWidth(event
), XConfigureEventGetHeight(event
)), win
->GetId() );
483 sizeEvent
.SetEventObject( win
);
485 return win
->GetEventHandler()->ProcessEvent( sizeEvent
);
494 //wxLogDebug("PropertyNotify: %s", windowClass.c_str());
495 return HandlePropertyChange(_event
);
499 if (!win
->IsEnabled())
502 Atom wm_delete_window
= XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True
);
503 Atom wm_protocols
= XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True
);
505 if (event
->xclient
.message_type
== wm_protocols
)
507 if ((Atom
) (event
->xclient
.data
.l
[0]) == wm_delete_window
)
518 printf( "destroy from %s\n", win
->GetName().c_str() );
523 printf( "create from %s\n", win
->GetName().c_str() );
528 printf( "map request from %s\n", win
->GetName().c_str() );
533 printf( "resize request from %s\n", win
->GetName().c_str() );
535 Display
*disp
= (Display
*) wxGetDisplay();
540 while( XCheckTypedWindowEvent (disp
, actualWindow
, ResizeRequest
, &report
));
542 wxSize sz
= win
->GetSize();
543 wxSizeEvent
sizeEvent(sz
, win
->GetId());
544 sizeEvent
.SetEventObject(win
);
546 return win
->GetEventHandler()->ProcessEvent( sizeEvent
);
551 case GR_EVENT_TYPE_CLOSE_REQ
:
568 if (!win
->IsEnabled())
571 // Here we check if the top level window is
572 // disabled, which is one aspect of modality.
574 while (tlw
&& !tlw
->IsTopLevel())
575 tlw
= tlw
->GetParent();
576 if (tlw
&& !tlw
->IsEnabled())
579 if (event
->type
== ButtonPress
)
581 if ((win
!= wxWindow::FindFocus()) && win
->AcceptsFocus())
583 // This might actually be done in wxWindow::SetFocus()
584 // and not here. TODO.
585 g_prevFocus
= wxWindow::FindFocus();
588 wxLogTrace( _T("focus"), _T("About to call SetFocus on %s of type %s due to button press"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
590 // Record the fact that this window is
591 // getting the focus, because we'll need to
592 // check if its parent is getting a bogus
593 // focus and duly ignore it.
594 // TODO: may need to have this code in SetFocus, too.
595 extern wxWindow
* g_GettingFocus
;
596 g_GettingFocus
= win
;
602 if (event
->type
== LeaveNotify
|| event
->type
== EnterNotify
)
604 // Throw out NotifyGrab and NotifyUngrab
605 if (event
->xcrossing
.mode
!= NotifyNormal
)
609 wxMouseEvent wxevent
;
610 wxTranslateMouseEvent(wxevent
, win
, window
, event
);
611 return win
->GetEventHandler()->ProcessEvent( wxevent
);
616 if ((event
->xfocus
.detail
!= NotifyPointer
) &&
617 (event
->xfocus
.mode
== NotifyNormal
))
620 wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
622 extern wxWindow
* g_GettingFocus
;
623 if (g_GettingFocus
&& g_GettingFocus
->GetParent() == win
)
625 // Ignore this, this can be a spurious FocusIn
626 // caused by a child having its focus set.
627 g_GettingFocus
= NULL
;
628 wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s being deliberately ignored"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
633 wxFocusEvent
focusEvent(wxEVT_SET_FOCUS
, win
->GetId());
634 focusEvent
.SetEventObject(win
);
635 focusEvent
.SetWindow( g_prevFocus
);
638 return win
->GetEventHandler()->ProcessEvent(focusEvent
);
647 if ((event
->xfocus
.detail
!= NotifyPointer
) &&
648 (event
->xfocus
.mode
== NotifyNormal
))
651 wxLogTrace( _T("focus"), _T("FocusOut from %s of type %s"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
653 wxFocusEvent
focusEvent(wxEVT_KILL_FOCUS
, win
->GetId());
654 focusEvent
.SetEventObject(win
);
655 focusEvent
.SetWindow( g_nextFocus
);
657 return win
->GetEventHandler()->ProcessEvent(focusEvent
);
665 //wxString eventName = wxGetXEventName(XEvent& event);
666 //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
675 // Returns TRUE if more time is needed.
676 // Note that this duplicates wxEventLoopImpl::SendIdleEvent
677 // but ProcessIdle may be needed by apps, so is kept.
678 bool wxApp::ProcessIdle()
681 event
.SetEventObject(this);
684 return event
.MoreRequested();
687 void wxApp::ExitMainLoop()
693 // Is a message/event pending?
694 bool wxApp::Pending()
696 return wxEventLoop::GetActive()->Pending();
699 // Dispatch a message.
700 void wxApp::Dispatch()
702 wxEventLoop::GetActive()->Dispatch();
705 // This should be redefined in a derived class for
706 // handling property change events for XAtom IPC.
707 bool wxApp::HandlePropertyChange(WXEvent
*event
)
709 // by default do nothing special
710 // TODO: what to do for X11
711 // XtDispatchEvent((XEvent*) event);
715 void wxApp::OnIdle(wxIdleEvent
& event
)
717 static bool s_inOnIdle
= FALSE
;
719 // Avoid recursion (via ProcessEvent default case)
725 // Resend in the main thread events which have been prepared in other
727 ProcessPendingEvents();
729 // 'Garbage' collection of windows deleted with Close()
730 DeletePendingObjects();
732 // Send OnIdle events to all windows
733 bool needMore
= SendIdleEvents();
736 event
.RequestMore(TRUE
);
741 void wxApp::WakeUpIdle()
743 // TODO: use wxMotif implementation?
745 // Wake up the idle handler processor, even if it is in another thread...
749 // Send idle event to all top-level windows
750 bool wxApp::SendIdleEvents()
752 bool needMore
= FALSE
;
754 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
757 wxWindow
* win
= node
->GetData();
758 if (SendIdleEvents(win
))
760 node
= node
->GetNext();
766 // Send idle event to window and all subwindows
767 bool wxApp::SendIdleEvents(wxWindow
* win
)
769 bool needMore
= FALSE
;
772 event
.SetEventObject(win
);
774 win
->GetEventHandler()->ProcessEvent(event
);
776 if (event
.MoreRequested())
779 wxWindowListNode
* node
= win
->GetChildren().GetFirst();
782 wxWindow
* win
= (wxWindow
*) node
->GetData();
783 if (SendIdleEvents(win
))
786 node
= node
->GetNext();
789 win
->OnInternalIdle();
794 // Create display, and other initialization
795 bool wxApp::OnInitGui()
797 // Eventually this line will be removed, but for
798 // now we don't want to try popping up a dialog
799 // for error messages.
800 delete wxLog::SetActiveTarget(new wxLogStderr
);
802 if (!wxAppBase::OnInitGui())
805 GetMainColormap( wxApp::GetDisplay() );
807 m_maxRequestSize
= XMaxRequestSize( (Display
*) wxApp::GetDisplay() );
810 m_visualInfo
= new wxXVisualInfo
;
811 wxFillXVisualInfo( m_visualInfo
, (Display
*) wxApp::GetDisplay() );
819 #include <pango/pango.h>
820 #include <pango/pangox.h>
821 #include <pango/pangoxft.h>
823 PangoContext
* wxApp::GetPangoContext()
825 static PangoContext
*ret
= NULL
;
829 Display
*xdisplay
= (Display
*) wxApp::GetDisplay();
832 int xscreen
= DefaultScreen(xdisplay
);
833 static int use_xft
= -1;
836 wxString val
= wxGetenv( L
"GDK_USE_XFT" );
837 use_xft
= (val
== L
"1");
841 ret
= pango_xft_get_context( xdisplay
, xscreen
);
844 ret
= pango_x_get_context( xdisplay
);
846 if (!PANGO_IS_CONTEXT(ret
))
847 wxLogError( wxT("No pango context.") );
853 WXColormap
wxApp::GetMainColormap(WXDisplay
* display
)
855 if (!display
) /* Must be called first with non-NULL display */
856 return m_mainColormap
;
858 int defaultScreen
= DefaultScreen((Display
*) display
);
859 Screen
* screen
= XScreenOfDisplay((Display
*) display
, defaultScreen
);
861 Colormap c
= DefaultColormapOfScreen(screen
);
864 m_mainColormap
= (WXColormap
) c
;
866 return (WXColormap
) c
;
869 Window
wxGetWindowParent(Window window
)
871 wxASSERT_MSG( window
, "invalid window" );
875 Window parent
, root
= 0;
879 unsigned int noChildren
= 0;
881 Window
* children
= NULL
;
883 // #define XQueryTree(d,w,r,p,c,nc) GrQueryTree(w,p,c,nc)
888 XQueryTree((Display
*) wxGetDisplay(), window
, & root
, & parent
,
889 & children
, & noChildren
);
902 wxAppConsole::Exit();
905 // Yield to other processes
907 bool wxApp::Yield(bool onlyIfNeeded
)
909 // Sometimes only 2 yields seem
910 // to do the trick, e.g. in the
913 for (i
= 0; i
< 2; i
++)
915 bool s_inYield
= FALSE
;
921 wxFAIL_MSG( wxT("wxYield called recursively" ) );
929 // Make sure we have an event loop object,
930 // or Pending/Dispatch will fail
931 wxEventLoop
* eventLoop
= wxEventLoop::GetActive();
932 wxEventLoop
* newEventLoop
= NULL
;
935 newEventLoop
= new wxEventLoop
;
936 wxEventLoop::SetActive(newEventLoop
);
939 // Call dispatch at least once so that sockets
941 wxTheApp
->Dispatch();
943 while (wxTheApp
&& wxTheApp
->Pending())
944 wxTheApp
->Dispatch();
947 wxTimer::NotifyTimers();
953 wxEventLoop::SetActive(NULL
);
965 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
* cond
, const wxChar
*msg
)
967 // While the GUI isn't working that well, just print out the
970 wxAppBase::OnAssert(file
, line
, cond
, msg
);
973 msg2
.Printf("At file %s:%d: %s", file
, line
, msg
);
978 #endif // __WXDEBUG__