1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/appbase.cpp
3 // Purpose: implements wxAppConsoleBase class
4 // Author: Vadim Zeitlin
6 // Created: 19.06.2003 (extracted from common/appcmn.cpp)
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/msw/wrapwin.h" // includes windows.h for MessageBox()
36 #include "wx/wxcrtvararg.h"
39 #include "wx/apptrait.h"
40 #include "wx/cmdline.h"
41 #include "wx/confbase.h"
42 #include "wx/evtloop.h"
43 #include "wx/filename.h"
44 #include "wx/msgout.h"
45 #include "wx/ptr_scpd.h"
46 #include "wx/tokenzr.h"
47 #include "wx/thread.h"
49 #if wxUSE_EXCEPTIONS && wxUSE_STL
55 #if !defined(__WXMSW__) || defined(__WXMICROWIN__)
56 #include <signal.h> // for SIGTRAP used by wxTrap()
60 #endif // ! __WXPALMOS5__
63 #include "wx/fontmap.h"
64 #endif // wxUSE_FONTMAP
66 #if defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
67 // For MacTypes.h for Debugger function
68 #include <CoreFoundation/CFBase.h>
71 #if defined(__WXMAC__)
73 #include <CoreServices/CoreServices.h>
75 #include "wx/mac/private.h" // includes mac headers
81 #include "wx/stackwalk.h"
83 #include "wx/msw/debughlp.h"
85 #endif // wxUSE_STACKWALKER
87 #include "wx/recguard.h"
90 // wxABI_VERSION can be defined when compiling applications but it should be
91 // left undefined when compiling the library itself, it is then set to its
92 // default value in version.h
93 #if wxABI_VERSION != wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + 99
94 #error "wxABI_VERSION should not be defined when compiling the library"
97 // ----------------------------------------------------------------------------
98 // private functions prototypes
99 // ----------------------------------------------------------------------------
102 // really just show the assert dialog
103 static bool DoShowAssertDialog(const wxString
& msg
);
105 // prepare for showing the assert dialog, use the given traits or
106 // DoShowAssertDialog() as last fallback to really show it
108 void ShowAssertDialog(const wxString
& szFile
,
110 const wxString
& szFunc
,
111 const wxString
& szCond
,
112 const wxString
& szMsg
,
113 wxAppTraits
*traits
= NULL
);
115 // turn on the trace masks specified in the env variable WXTRACE
116 static void LINKAGEMODE
SetTraceMasks();
117 #endif // __WXDEBUG__
119 // ----------------------------------------------------------------------------
121 // ----------------------------------------------------------------------------
123 wxAppConsole
*wxAppConsoleBase::ms_appInstance
= NULL
;
125 wxAppInitializerFunction
wxAppConsoleBase::ms_appInitFn
= NULL
;
127 // ----------------------------------------------------------------------------
129 // ----------------------------------------------------------------------------
131 // this defines wxEventLoopPtr
132 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopBase
)
134 // ============================================================================
135 // wxAppConsoleBase implementation
136 // ============================================================================
138 // ----------------------------------------------------------------------------
140 // ----------------------------------------------------------------------------
142 wxAppConsoleBase::wxAppConsoleBase()
147 ms_appInstance
= wx_static_cast(wxAppConsole
*, this);
152 // In unicode mode the SetTraceMasks call can cause an apptraits to be
153 // created, but since we are still in the constructor the wrong kind will
154 // be created for GUI apps. Destroy it so it can be created again later.
161 wxAppConsoleBase::~wxAppConsoleBase()
166 // ----------------------------------------------------------------------------
167 // initilization/cleanup
168 // ----------------------------------------------------------------------------
170 bool wxAppConsoleBase::Initialize(int& argcOrig
, wxChar
**argvOrig
)
173 GetTraits()->SetLocale();
176 // remember the command line arguments
181 wxPendingEventsLocker
= new wxCriticalSection
;
185 if ( m_appName
.empty() && argv
)
187 // the application name is, by default, the name of its executable file
188 wxFileName::SplitPath(argv
[0], NULL
, &m_appName
, NULL
);
190 #endif // !__WXPALMOS__
195 wxEventLoopBase
*wxAppConsoleBase::CreateMainLoop()
197 return GetTraits()->CreateEventLoop();
200 void wxAppConsoleBase::CleanUp()
208 delete wxPendingEvents
;
209 wxPendingEvents
= NULL
;
212 delete wxPendingEventsLocker
;
213 wxPendingEventsLocker
= NULL
;
214 #endif // wxUSE_THREADS
217 // ----------------------------------------------------------------------------
219 // ----------------------------------------------------------------------------
221 bool wxAppConsoleBase::OnInit()
223 #if wxUSE_CMDLINE_PARSER
224 wxCmdLineParser
parser(argc
, argv
);
226 OnInitCmdLine(parser
);
229 switch ( parser
.Parse(false /* don't show usage */) )
232 cont
= OnCmdLineHelp(parser
);
236 cont
= OnCmdLineParsed(parser
);
240 cont
= OnCmdLineError(parser
);
246 #endif // wxUSE_CMDLINE_PARSER
251 int wxAppConsoleBase::OnRun()
256 int wxAppConsoleBase::OnExit()
259 // delete the config object if any (don't use Get() here, but Set()
260 // because Get() could create a new config object)
261 delete wxConfigBase::Set((wxConfigBase
*) NULL
);
262 #endif // wxUSE_CONFIG
267 void wxAppConsoleBase::Exit()
269 if (m_mainLoop
!= NULL
)
275 // ----------------------------------------------------------------------------
277 // ----------------------------------------------------------------------------
279 wxAppTraits
*wxAppConsoleBase::CreateTraits()
281 return new wxConsoleAppTraits
;
284 wxAppTraits
*wxAppConsoleBase::GetTraits()
286 // FIXME-MT: protect this with a CS?
289 m_traits
= CreateTraits();
291 wxASSERT_MSG( m_traits
, _T("wxApp::CreateTraits() failed?") );
297 // ----------------------------------------------------------------------------
299 // ----------------------------------------------------------------------------
301 int wxAppConsoleBase::MainLoop()
303 wxEventLoopBaseTiedPtr
mainLoop(&m_mainLoop
, CreateMainLoop());
305 return m_mainLoop
? m_mainLoop
->Run() : -1;
308 void wxAppConsoleBase::ExitMainLoop()
310 // we should exit from the main event loop, not just any currently active
311 // (e.g. modal dialog) event loop
312 if ( m_mainLoop
&& m_mainLoop
->IsRunning() )
318 bool wxAppConsoleBase::Pending()
320 // use the currently active message loop here, not m_mainLoop, because if
321 // we're showing a modal dialog (with its own event loop) currently the
322 // main event loop is not running anyhow
323 wxEventLoopBase
* const loop
= wxEventLoopBase::GetActive();
325 return loop
&& loop
->Pending();
328 bool wxAppConsoleBase::Dispatch()
330 // see comment in Pending()
331 wxEventLoopBase
* const loop
= wxEventLoopBase::GetActive();
333 return loop
&& loop
->Dispatch();
336 bool wxAppConsoleBase::HasPendingEvents() const
338 wxENTER_CRIT_SECT( *wxPendingEventsLocker
);
340 bool has
= wxPendingEvents
&& !wxPendingEvents
->IsEmpty();
342 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker
);
348 bool wxAppConsoleBase::IsMainLoopRunning()
350 const wxAppConsole
* const app
= GetInstance();
352 return app
&& app
->m_mainLoop
!= NULL
;
355 void wxAppConsoleBase::ProcessPendingEvents()
358 if ( !wxPendingEventsLocker
)
362 wxENTER_CRIT_SECT( *wxPendingEventsLocker
);
366 // iterate until the list becomes empty: the handlers remove themselves
367 // from it when they don't have any more pending events
368 wxList::compatibility_iterator node
= wxPendingEvents
->GetFirst();
371 // In ProcessPendingEvents(), new handlers might be add
372 // and we can safely leave the critical section here.
373 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker
);
375 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->GetData();
376 handler
->ProcessPendingEvents();
378 wxENTER_CRIT_SECT( *wxPendingEventsLocker
);
380 // restart as the iterators could have been invalidated
381 node
= wxPendingEvents
->GetFirst();
385 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker
);
388 void wxAppConsoleBase::WakeUpIdle()
391 m_mainLoop
->WakeUp();
394 bool wxAppConsoleBase::ProcessIdle()
398 event
.SetEventObject(this);
400 return event
.MoreRequested();
403 int wxAppConsoleBase::FilterEvent(wxEvent
& WXUNUSED(event
))
405 // process the events normally by default
409 // ----------------------------------------------------------------------------
410 // exception handling
411 // ----------------------------------------------------------------------------
416 wxAppConsoleBase::HandleEvent(wxEvtHandler
*handler
,
417 wxEventFunction func
,
418 wxEvent
& event
) const
420 // by default, simply call the handler
421 (handler
->*func
)(event
);
424 void wxAppConsoleBase::OnUnhandledException()
427 // we're called from an exception handler so we can re-throw the exception
428 // to recover its type
435 catch ( std::exception
& e
)
437 what
.Printf("std::exception of type \"%s\", what() = \"%s\"",
438 typeid(e
).name(), e
.what());
443 what
= "unknown exception";
446 wxMessageOutputBest().Printf(
447 "*** Caught unhandled %s; terminating\n", what
449 #endif // __WXDEBUG__
452 // ----------------------------------------------------------------------------
453 // exceptions support
454 // ----------------------------------------------------------------------------
456 bool wxAppConsoleBase::OnExceptionInMainLoop()
460 // some compilers are too stupid to know that we never return after throw
461 #if defined(__DMC__) || (defined(_MSC_VER) && _MSC_VER < 1200)
466 #endif // wxUSE_EXCEPTIONS
468 // ----------------------------------------------------------------------------
470 // ----------------------------------------------------------------------------
472 #if wxUSE_CMDLINE_PARSER
474 #define OPTION_VERBOSE "verbose"
476 void wxAppConsoleBase::OnInitCmdLine(wxCmdLineParser
& parser
)
478 // the standard command line options
479 static const wxCmdLineEntryDesc cmdLineDesc
[] =
485 gettext_noop("show this help message"),
487 wxCMD_LINE_OPTION_HELP
495 gettext_noop("generate verbose log messages"),
505 parser
.SetDesc(cmdLineDesc
);
508 bool wxAppConsoleBase::OnCmdLineParsed(wxCmdLineParser
& parser
)
511 if ( parser
.Found(OPTION_VERBOSE
) )
513 wxLog::SetVerbose(true);
522 bool wxAppConsoleBase::OnCmdLineHelp(wxCmdLineParser
& parser
)
529 bool wxAppConsoleBase::OnCmdLineError(wxCmdLineParser
& parser
)
536 #endif // wxUSE_CMDLINE_PARSER
538 // ----------------------------------------------------------------------------
540 // ----------------------------------------------------------------------------
543 bool wxAppConsoleBase::CheckBuildOptions(const char *optionsSignature
,
544 const char *componentName
)
546 #if 0 // can't use wxLogTrace, not up and running yet
547 printf("checking build options object '%s' (ptr %p) in '%s'\n",
548 optionsSignature
, optionsSignature
, componentName
);
551 if ( strcmp(optionsSignature
, WX_BUILD_OPTIONS_SIGNATURE
) != 0 )
553 wxString lib
= wxString::FromAscii(WX_BUILD_OPTIONS_SIGNATURE
);
554 wxString prog
= wxString::FromAscii(optionsSignature
);
555 wxString progName
= wxString::FromAscii(componentName
);
558 msg
.Printf(_T("Mismatch between the program and library build versions detected.\nThe library used %s,\nand %s used %s."),
559 lib
.c_str(), progName
.c_str(), prog
.c_str());
561 wxLogFatalError(msg
.c_str());
563 // normally wxLogFatalError doesn't return
573 void wxAppConsoleBase::OnAssertFailure(const wxChar
*file
,
579 ShowAssertDialog(file
, line
, func
, cond
, msg
, GetTraits());
582 void wxAppConsoleBase::OnAssert(const wxChar
*file
,
587 OnAssertFailure(file
, line
, NULL
, cond
, msg
);
590 #endif // __WXDEBUG__
592 // ============================================================================
593 // other classes implementations
594 // ============================================================================
596 // ----------------------------------------------------------------------------
597 // wxConsoleAppTraitsBase
598 // ----------------------------------------------------------------------------
602 wxLog
*wxConsoleAppTraitsBase::CreateLogTarget()
604 return new wxLogStderr
;
609 wxMessageOutput
*wxConsoleAppTraitsBase::CreateMessageOutput()
611 return new wxMessageOutputStderr
;
616 wxFontMapper
*wxConsoleAppTraitsBase::CreateFontMapper()
618 return (wxFontMapper
*)new wxFontMapperBase
;
621 #endif // wxUSE_FONTMAP
623 wxRendererNative
*wxConsoleAppTraitsBase::CreateRenderer()
625 // console applications don't use renderers
630 bool wxConsoleAppTraitsBase::ShowAssertDialog(const wxString
& msg
)
632 return wxAppTraitsBase::ShowAssertDialog(msg
);
636 bool wxConsoleAppTraitsBase::HasStderr()
638 // console applications always have stderr, even under Mac/Windows
642 void wxConsoleAppTraitsBase::ScheduleForDestroy(wxObject
*object
)
647 void wxConsoleAppTraitsBase::RemoveFromPendingDelete(wxObject
* WXUNUSED(object
))
652 // ----------------------------------------------------------------------------
654 // ----------------------------------------------------------------------------
657 void wxAppTraitsBase::SetLocale()
659 wxSetlocale(LC_ALL
, "");
660 wxUpdateLocaleIsUtf8();
665 void wxMutexGuiEnterImpl();
666 void wxMutexGuiLeaveImpl();
668 void wxAppTraitsBase::MutexGuiEnter()
670 wxMutexGuiEnterImpl();
673 void wxAppTraitsBase::MutexGuiLeave()
675 wxMutexGuiLeaveImpl();
678 void WXDLLIMPEXP_BASE
wxMutexGuiEnter()
680 wxAppConsoleBase::GetInstance()->GetTraits()->MutexGuiEnter();
683 void WXDLLIMPEXP_BASE
wxMutexGuiLeave()
685 wxAppConsoleBase::GetInstance()->GetTraits()->MutexGuiLeave();
687 #endif // wxUSE_THREADS
691 bool wxAppTraitsBase::ShowAssertDialog(const wxString
& msgOriginal
)
693 wxString msg
= msgOriginal
;
695 #if wxUSE_STACKWALKER
696 #if !defined(__WXMSW__)
697 // on Unix stack frame generation may take some time, depending on the
698 // size of the executable mainly... warn the user that we are working
699 wxFprintf(stderr
, wxT("[Debug] Generating a stack trace... please wait"));
703 const wxString stackTrace
= GetAssertStackTrace();
704 if ( !stackTrace
.empty() )
705 msg
<< _T("\n\nCall stack:\n") << stackTrace
;
706 #endif // wxUSE_STACKWALKER
708 return DoShowAssertDialog(msg
);
711 #if wxUSE_STACKWALKER
712 wxString
wxAppTraitsBase::GetAssertStackTrace()
716 class StackDump
: public wxStackWalker
721 const wxString
& GetStackTrace() const { return m_stackTrace
; }
724 virtual void OnStackFrame(const wxStackFrame
& frame
)
726 m_stackTrace
<< wxString::Format
729 wx_truncate_cast(int, frame
.GetLevel())
732 wxString name
= frame
.GetName();
735 m_stackTrace
<< wxString::Format(_T("%-40s"), name
.c_str());
739 m_stackTrace
<< wxString::Format(_T("%p"), frame
.GetAddress());
742 if ( frame
.HasSourceLocation() )
744 m_stackTrace
<< _T('\t')
745 << frame
.GetFileName()
750 m_stackTrace
<< _T('\n');
754 wxString m_stackTrace
;
757 // don't show more than maxLines or we could get a dialog too tall to be
758 // shown on screen: 20 should be ok everywhere as even with 15 pixel high
759 // characters it is still only 300 pixels...
760 static const int maxLines
= 20;
763 dump
.Walk(2, maxLines
); // don't show OnAssert() call itself
764 stackTrace
= dump
.GetStackTrace();
766 const int count
= stackTrace
.Freq(wxT('\n'));
767 for ( int i
= 0; i
< count
- maxLines
; i
++ )
768 stackTrace
= stackTrace
.BeforeLast(wxT('\n'));
772 #endif // wxUSE_STACKWALKER
775 #endif // __WXDEBUG__
777 // ============================================================================
778 // global functions implementation
779 // ============================================================================
789 // what else can we do?
798 wxTheApp
->WakeUpIdle();
800 //else: do nothing, what can we do?
806 bool wxAssertIsEqual(int x
, int y
)
811 // break into the debugger
814 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
816 #elif defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
818 #elif defined(__UNIX__)
825 // this function is called when an assert fails
826 static void wxDoOnAssert(const wxString
& szFile
,
828 const wxString
& szFunc
,
829 const wxString
& szCond
,
830 const wxString
& szMsg
= wxEmptyString
)
833 static int s_bInAssert
= 0;
835 wxRecursionGuard
guard(s_bInAssert
);
836 if ( guard
.IsInside() )
838 // can't use assert here to avoid infinite loops, so just trap
846 // by default, show the assert dialog box -- we can't customize this
848 ShowAssertDialog(szFile
, nLine
, szFunc
, szCond
, szMsg
);
852 // let the app process it as it wants
853 // FIXME-UTF8: use wc_str(), not c_str(), when ANSI build is removed
854 wxTheApp
->OnAssertFailure(szFile
.c_str(), nLine
, szFunc
.c_str(),
855 szCond
.c_str(), szMsg
.c_str());
859 void wxOnAssert(const wxString
& szFile
,
861 const wxString
& szFunc
,
862 const wxString
& szCond
,
863 const wxString
& szMsg
)
865 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, szMsg
);
868 void wxOnAssert(const wxString
& szFile
,
870 const wxString
& szFunc
,
871 const wxString
& szCond
)
873 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
);
876 void wxOnAssert(const wxChar
*szFile
,
879 const wxChar
*szCond
,
882 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, szMsg
);
885 void wxOnAssert(const char *szFile
,
889 const wxString
& szMsg
)
891 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, szMsg
);
894 void wxOnAssert(const char *szFile
,
898 const wxCStrData
& msg
)
900 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, msg
);
904 void wxOnAssert(const char *szFile
,
909 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
);
912 void wxOnAssert(const char *szFile
,
918 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, szMsg
);
921 void wxOnAssert(const char *szFile
,
927 wxDoOnAssert(szFile
, nLine
, szFunc
, szCond
, szMsg
);
929 #endif // wxUSE_UNICODE
931 #endif // __WXDEBUG__
933 // ============================================================================
934 // private functions implementation
935 // ============================================================================
939 static void LINKAGEMODE
SetTraceMasks()
943 if ( wxGetEnv(wxT("WXTRACE"), &mask
) )
945 wxStringTokenizer
tkn(mask
, wxT(",;:"));
946 while ( tkn
.HasMoreTokens() )
947 wxLog::AddTraceMask(tkn
.GetNextToken());
953 bool DoShowAssertDialog(const wxString
& msg
)
955 // under MSW we can show the dialog even in the console mode
956 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
957 wxString
msgDlg(msg
);
959 // this message is intentionally not translated -- it is for
961 msgDlg
+= wxT("\nDo you want to stop the program?\n")
962 wxT("You can also choose [Cancel] to suppress ")
963 wxT("further warnings.");
965 switch ( ::MessageBox(NULL
, msgDlg
.wx_str(), _T("wxWidgets Debug Alert"),
966 MB_YESNOCANCEL
| MB_ICONSTOP
) )
976 //case IDNO: nothing to do
979 wxFprintf(stderr
, wxT("%s\n"), msg
.c_str());
982 // TODO: ask the user to enter "Y" or "N" on the console?
984 #endif // __WXMSW__/!__WXMSW__
986 // continue with the asserts
990 // show the assert modal dialog
992 void ShowAssertDialog(const wxString
& szFile
,
994 const wxString
& szFunc
,
995 const wxString
& szCond
,
996 const wxString
& szMsg
,
999 // this variable can be set to true to suppress "assert failure" messages
1000 static bool s_bNoAsserts
= false;
1005 // make life easier for people using VC++ IDE by using this format: like
1006 // this, clicking on the message will take us immediately to the place of
1007 // the failed assert
1008 msg
.Printf(wxT("%s(%d): assert \"%s\" failed"), szFile
, nLine
, szCond
);
1010 // add the function name, if any
1011 if ( !szFunc
.empty() )
1012 msg
<< _T(" in ") << szFunc
<< _T("()");
1014 // and the message itself
1015 if ( !szMsg
.empty() )
1017 msg
<< _T(": ") << szMsg
;
1019 else // no message given
1025 // if we are not in the main thread, output the assert directly and trap
1026 // since dialogs cannot be displayed
1027 if ( !wxThread::IsMain() )
1029 msg
+= wxT(" [in child thread]");
1031 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
1033 OutputDebugString(msg
.wx_str());
1036 wxFprintf(stderr
, wxT("%s\n"), msg
.c_str());
1039 // He-e-e-e-elp!! we're asserting in a child thread
1043 #endif // wxUSE_THREADS
1045 if ( !s_bNoAsserts
)
1047 // send it to the normal log destination
1048 wxLogDebug(_T("%s"), msg
.c_str());
1052 // delegate showing assert dialog (if possible) to that class
1053 s_bNoAsserts
= traits
->ShowAssertDialog(msg
);
1055 else // no traits object
1057 // fall back to the function of last resort
1058 s_bNoAsserts
= DoShowAssertDialog(msg
);
1063 #endif // __WXDEBUG__