wxBase/GUI separation: 1st step, wxMSW should build, all the rest is broken
[wxWidgets.git] / src / motif / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose: wxApp
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "app.h"
14 #endif
15
16 #ifdef __VMS
17 #define XtParent XTPARENT
18 #define XtDisplay XTDISPLAY
19 #endif
20
21 #include "wx/app.h"
22 #include "wx/utils.h"
23 #include "wx/module.h"
24 #include "wx/memory.h"
25 #include "wx/log.h"
26 #include "wx/intl.h"
27 #include "wx/evtloop.h"
28 #include "wx/hash.h"
29 #include "wx/hashmap.h"
30
31 #if wxUSE_THREADS
32 #include "wx/thread.h"
33 #endif
34
35 #ifdef __VMS__
36 #pragma message disable nosimpint
37 #endif
38 #include <Xm/Xm.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xresource.h>
42 #include <X11/Xatom.h>
43 #ifdef __VMS__
44 #pragma message enable nosimpint
45 #endif
46
47 #include "wx/motif/private.h"
48
49 #include <string.h>
50
51 struct wxPerDisplayData
52 {
53 wxPerDisplayData()
54 { m_visualInfo = NULL; m_topLevelWidget = NULL; }
55
56 wxXVisualInfo* m_visualInfo;
57 Widget m_topLevelWidget;
58 };
59
60 WX_DECLARE_VOIDPTR_HASH_MAP( wxPerDisplayData, wxPerDisplayDataMap );
61
62 static void wxTLWidgetDestroyCallback(Widget w, XtPointer clientData,
63 XtPointer ptr);
64 static WXWidget wxCreateTopLevelWidget( WXDisplay* display );
65
66 extern wxList wxPendingDelete;
67 extern bool wxAddIdleCallback();
68
69 wxApp *wxTheApp = NULL;
70
71 wxHashTable *wxWidgetHashTable = NULL;
72
73 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
74
75 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
76 EVT_IDLE(wxApp::OnIdle)
77 END_EVENT_TABLE()
78
79 #ifdef __WXDEBUG__
80 typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
81
82 XErrorHandlerFunc gs_pfnXErrorHandler = 0;
83
84 static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
85 {
86 // just forward to the default handler for now
87 return gs_pfnXErrorHandler(dpy, xevent);
88 }
89 #endif // __WXDEBUG__
90
91 bool wxApp::Initialize()
92 {
93 wxClassInfo::InitializeClasses();
94
95 // GL: I'm annoyed ... I don't know where to put this and I don't want to
96 // create a module for that as it's part of the core.
97 #if wxUSE_THREADS
98 wxPendingEventsLocker = new wxCriticalSection();
99 #endif
100
101 wxTheColourDatabase = new wxColourDatabase(wxKEY_STRING);
102 wxTheColourDatabase->Initialize();
103
104 wxInitializeStockLists();
105 wxInitializeStockObjects();
106
107 wxWidgetHashTable = new wxHashTable(wxKEY_INTEGER);
108
109 wxModule::RegisterModules();
110 if (!wxModule::InitializeModules()) return FALSE;
111
112 return TRUE;
113 }
114
115 void wxApp::CleanUp()
116 {
117 wxModule::CleanUpModules();
118
119 wxDeleteStockObjects() ;
120
121 // Destroy all GDI lists, etc.
122
123 wxDeleteStockLists();
124
125 delete wxTheColourDatabase;
126 wxTheColourDatabase = NULL;
127
128 wxClassInfo::CleanUpClasses();
129
130 delete wxTheApp;
131 wxTheApp = NULL;
132
133 delete wxWidgetHashTable;
134 wxWidgetHashTable = NULL;
135
136 // GL: I'm annoyed ... I don't know where to put this and I don't want to
137 // create a module for that as it's part of the core.
138 #if wxUSE_THREADS
139 delete wxPendingEvents;
140 wxPendingEvents = NULL;
141 delete wxPendingEventsLocker;
142 wxPendingEventsLocker = NULL;
143 #endif
144
145 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
146 // At this point we want to check if there are any memory
147 // blocks that aren't part of the wxDebugContext itself,
148 // as a special case. Then when dumping we need to ignore
149 // wxDebugContext, too.
150 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
151 {
152 wxLogDebug("There were memory leaks.\n");
153 wxDebugContext::Dump();
154 wxDebugContext::PrintStatistics();
155 }
156 #endif
157
158 // do it as the very last thing because everything else can log messages
159 wxLog::DontCreateOnDemand();
160 // do it as the very last thing because everything else can log messages
161 delete wxLog::SetActiveTarget(NULL);
162 }
163
164 void wxApp::Exit()
165 {
166 wxApp::CleanUp();
167
168 wxAppConsole::Exit();
169 }
170
171 // ============================================================================
172 // wxEntry*
173 // ============================================================================
174
175 int wxEntryStart( int argc, char* argv[] )
176 {
177 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
178 // This seems to be necessary since there are 'rogue'
179 // objects present at this point (perhaps global objects?)
180 // Setting a checkpoint will ignore them as far as the
181 // memory checking facility is concerned.
182 // Of course you may argue that memory allocated in globals should be
183 // checked, but this is a reasonable compromise.
184 wxDebugContext::SetCheckpoint();
185 #endif
186
187 if (!wxApp::Initialize())
188 return -1;
189
190 return 0;
191 }
192
193 int wxEntryInitGui()
194 {
195 int retValue = 0;
196
197 // GUI-specific initialization, such as creating an app context.
198 if (!wxTheApp->OnInitGui())
199 retValue = -1;
200
201 return retValue;
202 }
203
204 void wxEntryCleanup()
205 {
206 // So dialog boxes aren't used for further messages
207 delete wxLog::SetActiveTarget(new wxLogStderr);
208
209 // flush the logged messages if any
210 wxLog *pLog = wxLog::GetActiveTarget();
211 if ( pLog != NULL && pLog->HasPendingMessages() )
212 pLog->Flush();
213
214 wxApp::CleanUp();
215 }
216
217 int wxEntry( int argc, char *argv[] )
218 {
219 int retValue = 0;
220
221 retValue = wxEntryStart( argc, argv );
222 if (retValue) return retValue;
223
224 if (!wxTheApp)
225 {
226 if (!wxApp::GetInitializerFunction())
227 {
228 printf( "wxWindows error: No initializer - use IMPLEMENT_APP macro.\n" );
229 return 0;
230 };
231
232 wxTheApp = (wxApp*) (* wxApp::GetInitializerFunction()) ();
233 };
234
235 if (!wxTheApp)
236 {
237 printf( "wxWindows error: wxTheApp == NULL\n" );
238 return 0;
239 };
240
241 wxTheApp->SetClassName(wxFileNameFromPath(argv[0]));
242 wxTheApp->SetAppName(wxFileNameFromPath(argv[0]));
243
244 wxTheApp->argc = argc;
245 wxTheApp->argv = argv;
246
247 // GUI-specific initialization, such as creating an app context.
248 retValue = wxEntryInitGui();
249 if (retValue) return retValue;
250
251 // Here frames insert themselves automatically into wxTopLevelWindows by
252 // getting created in OnInit().
253
254 if (wxTheApp->OnInit())
255 {
256 if (wxTheApp->Initialized())
257 wxTheApp->OnRun();
258 }
259
260 if (wxTheApp->GetTopWindow())
261 {
262 delete wxTheApp->GetTopWindow();
263 wxTheApp->SetTopWindow(NULL);
264 }
265
266 wxTheApp->DeletePendingObjects();
267
268 retValue = wxTheApp->OnExit();
269
270 wxEntryCleanup();
271
272 return retValue;
273 }
274
275 // Static member initialization
276 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
277
278 wxApp::wxApp()
279 {
280 argc = 0;
281 argv = NULL;
282
283 m_eventLoop = new wxEventLoop;
284 m_mainColormap = (WXColormap) NULL;
285 m_appContext = (WXAppContext) NULL;
286 m_initialDisplay = (WXDisplay*) 0;
287 m_perDisplayData = new wxPerDisplayDataMap;
288 }
289
290 wxApp::~wxApp()
291 {
292 delete m_eventLoop;
293
294 for( wxPerDisplayDataMap::iterator it = m_perDisplayData->begin(),
295 end = m_perDisplayData->end();
296 it != end; ++it )
297 {
298 delete it->second.m_visualInfo;
299 XtDestroyWidget( it->second.m_topLevelWidget );
300 }
301
302 delete m_perDisplayData;
303
304 wxTheApp = NULL;
305 }
306
307 bool wxApp::Initialized()
308 {
309 if (GetTopWindow())
310 return TRUE;
311 else
312 return FALSE;
313 }
314
315 int wxApp::MainLoop()
316 {
317 /*
318 * Sit around forever waiting to process X-events. Property Change
319 * event are handled special, because they have to refer to
320 * the root window rather than to a widget. therefore we can't
321 * use an Xt-eventhandler.
322 */
323
324 XSelectInput(XtDisplay((Widget) wxTheApp->GetTopLevelWidget()),
325 XDefaultRootWindow(XtDisplay((Widget) wxTheApp->GetTopLevelWidget())),
326 PropertyChangeMask);
327
328 m_eventLoop->Run();
329
330 return 0;
331 }
332
333 // Processes an idle event.
334 // Returns TRUE if more time is needed.
335 bool wxApp::ProcessIdle()
336 {
337 wxIdleEvent event;
338
339 return ProcessEvent(event) && event.MoreRequested();
340 }
341
342 void wxApp::ExitMainLoop()
343 {
344 if( m_eventLoop->IsRunning() )
345 m_eventLoop->Exit();
346 }
347
348 // Is a message/event pending?
349 bool wxApp::Pending()
350 {
351 return m_eventLoop->Pending();
352 #if 0
353 XFlush(XtDisplay( (Widget) wxTheApp->GetTopLevelWidget() ));
354
355 // Fix by Doug from STI, to prevent a stall if non-X event
356 // is found.
357 return ((XtAppPending( (XtAppContext) GetAppContext() ) & XtIMXEvent) != 0) ;
358 #endif
359 }
360
361 // Dispatch a message.
362 void wxApp::Dispatch()
363 {
364 m_eventLoop->Dispatch();
365 }
366
367 // This should be redefined in a derived class for
368 // handling property change events for XAtom IPC.
369 void wxApp::HandlePropertyChange(WXEvent *event)
370 {
371 // by default do nothing special
372 XtDispatchEvent((XEvent*) event); /* let Motif do the work */
373 }
374
375 void wxApp::OnIdle(wxIdleEvent& event)
376 {
377 static bool inOnIdle = FALSE;
378
379 // Avoid recursion (via ProcessEvent default case)
380 if (inOnIdle)
381 return;
382
383 inOnIdle = TRUE;
384
385 // If there are pending events, we must process them: pending events
386 // are either events to the threads other than main or events posted
387 // with wxPostEvent() functions
388 // GRG: I have moved this here so that all pending events are processed
389 // before starting to delete any objects. This behaves better (in
390 // particular, wrt wxPostEvent) and is coherent with wxGTK's current
391 // behaviour. Also removed the '#if wxUSE_THREADS' around it.
392 // Changed Mar/2000 before 2.1.14
393
394 // Flush pending events.
395 ProcessPendingEvents();
396
397 // 'Garbage' collection of windows deleted with Close().
398 DeletePendingObjects();
399
400 // flush the logged messages if any
401 wxLog *pLog = wxLog::GetActiveTarget();
402 if ( pLog != NULL && pLog->HasPendingMessages() )
403 pLog->Flush();
404
405 // Send OnIdle events to all windows
406 bool needMore = SendIdleEvents();
407
408 if (needMore)
409 event.RequestMore(TRUE);
410
411 inOnIdle = FALSE;
412 }
413
414 // Send idle event to all top-level windows
415 bool wxApp::SendIdleEvents()
416 {
417 bool needMore = FALSE;
418
419 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
420 while (node)
421 {
422 wxWindow* win = node->GetData();
423 if (SendIdleEvents(win))
424 needMore = TRUE;
425 node = node->GetNext();
426 }
427
428 return needMore;
429 }
430
431 // Send idle event to window and all subwindows
432 bool wxApp::SendIdleEvents(wxWindow* win)
433 {
434 bool needMore = FALSE;
435
436 wxIdleEvent event;
437 event.SetEventObject(win);
438 win->GetEventHandler()->ProcessEvent(event);
439
440 if (event.MoreRequested())
441 needMore = TRUE;
442
443 wxWindowList::Node* node = win->GetChildren().GetFirst();
444 while (node)
445 {
446 wxWindow* win = node->GetData();
447 if (SendIdleEvents(win))
448 needMore = TRUE;
449
450 node = node->GetNext();
451 }
452 return needMore ;
453 }
454
455 void wxApp::DeletePendingObjects()
456 {
457 wxList::Node *node = wxPendingDelete.GetFirst();
458 while (node)
459 {
460 wxObject *obj = node->GetData();
461
462 delete obj;
463
464 if (wxPendingDelete.Member(obj))
465 delete node;
466
467 // Deleting one object may have deleted other pending
468 // objects, so start from beginning of list again.
469 node = wxPendingDelete.GetFirst();
470 }
471 }
472
473 static char *fallbackResources[] = {
474 "*menuBar.marginHeight: 0",
475 "*menuBar.shadowThickness: 1",
476 "*background: #c0c0c0",
477 "*foreground: black",
478 NULL
479 };
480
481 // Create an application context
482 bool wxApp::OnInitGui()
483 {
484 if( !wxAppBase::OnInitGui() )
485 return FALSE;
486
487 XtToolkitInitialize() ;
488 wxTheApp->m_appContext = (WXAppContext) XtCreateApplicationContext();
489 XtAppSetFallbackResources((XtAppContext) wxTheApp->m_appContext, fallbackResources);
490
491 Display *dpy = XtOpenDisplay((XtAppContext) wxTheApp->m_appContext,(String)NULL,NULL,
492 wxTheApp->GetClassName().c_str(), NULL, 0,
493 # if XtSpecificationRelease < 5
494 (Cardinal*) &argc,
495 # else
496 &argc,
497 # endif
498 argv);
499
500 if (!dpy) {
501 // if you don't log to stderr, nothing will be shown...
502 delete wxLog::SetActiveTarget(new wxLogStderr);
503 wxString className(wxTheApp->GetClassName());
504 wxLogError(_("wxWindows could not open display for '%s': exiting."),
505 className.c_str());
506 exit(-1);
507 }
508 m_initialDisplay = (WXDisplay*) dpy;
509
510 #ifdef __WXDEBUG__
511 // install the X error handler
512 gs_pfnXErrorHandler = XSetErrorHandler(wxXErrorHandler);
513 #endif // __WXDEBUG__
514
515 // Add general resize proc
516 XtActionsRec rec;
517 rec.string = "resize";
518 rec.proc = (XtActionProc)wxWidgetResizeProc;
519 XtAppAddActions((XtAppContext) wxTheApp->m_appContext, &rec, 1);
520
521 GetMainColormap(dpy);
522
523 wxAddIdleCallback();
524
525 return TRUE;
526 }
527
528 WXColormap wxApp::GetMainColormap(WXDisplay* display)
529 {
530 if (!display) /* Must be called first with non-NULL display */
531 return m_mainColormap;
532
533 int defaultScreen = DefaultScreen((Display*) display);
534 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
535
536 Colormap c = DefaultColormapOfScreen(screen);
537
538 if (!m_mainColormap)
539 m_mainColormap = (WXColormap) c;
540
541 return (WXColormap) c;
542 }
543
544 wxXVisualInfo* wxApp::GetVisualInfo( WXDisplay* display )
545 {
546 wxPerDisplayDataMap::iterator it = m_perDisplayData->find( display );
547
548 if( it != m_perDisplayData->end() && it->second.m_visualInfo )
549 return it->second.m_visualInfo;
550
551 wxXVisualInfo* vi = new wxXVisualInfo;
552 wxFillXVisualInfo( vi, (Display*)display );
553
554 (*m_perDisplayData)[display].m_visualInfo = vi;
555
556 return vi;
557 }
558
559 static void wxTLWidgetDestroyCallback(Widget w, XtPointer clientData,
560 XtPointer ptr)
561 {
562 if( wxTheApp )
563 wxTheApp->SetTopLevelWidget( (WXDisplay*)XtDisplay(w),
564 (WXWidget)NULL );
565 }
566
567 WXWidget wxCreateTopLevelWidget( WXDisplay* display )
568 {
569 Widget tlw = XtAppCreateShell( (String)NULL,
570 wxTheApp->GetClassName().c_str(),
571 applicationShellWidgetClass,
572 (Display*)display,
573 NULL, 0 );
574 XtSetMappedWhenManaged( tlw, False );
575 XtRealizeWidget( tlw );
576
577 XtAddCallback( tlw, XmNdestroyCallback,
578 (XtCallbackProc)wxTLWidgetDestroyCallback,
579 (XtPointer)NULL );
580
581 return (WXWidget)tlw;
582 }
583
584 WXWidget wxApp::GetTopLevelWidget()
585 {
586 WXDisplay* display = wxGetDisplay();
587 wxPerDisplayDataMap::iterator it = m_perDisplayData->find( display );
588
589 if( it != m_perDisplayData->end() && it->second.m_topLevelWidget )
590 return (WXWidget)it->second.m_topLevelWidget;
591
592 WXWidget tlw = wxCreateTopLevelWidget( display );
593 SetTopLevelWidget( display, tlw );
594
595 return tlw;
596 }
597
598 void wxApp::SetTopLevelWidget(WXDisplay* display, WXWidget widget)
599 {
600 (*m_perDisplayData)[display].m_topLevelWidget = (Widget)widget;
601 }
602
603 // Yield to other processes
604
605 bool wxApp::Yield(bool onlyIfNeeded)
606 {
607 bool s_inYield = FALSE;
608
609 if ( s_inYield )
610 {
611 if ( !onlyIfNeeded )
612 {
613 wxFAIL_MSG( wxT("wxYield called recursively" ) );
614 }
615
616 return FALSE;
617 }
618
619 s_inYield = TRUE;
620
621 while (wxTheApp && wxTheApp->Pending())
622 wxTheApp->Dispatch();
623
624 s_inYield = FALSE;
625
626 return TRUE;
627 }
628
629 // ----------------------------------------------------------------------------
630 // accessors for C modules
631 // ----------------------------------------------------------------------------
632
633 extern "C" XtAppContext wxGetAppContext()
634 {
635 return (XtAppContext)wxTheApp->GetAppContext();
636 }