]> git.saurik.com Git - wxWidgets.git/blob - src/x11/app.cpp
1133927b8d4ca57fa1cd805905565ecbd4322f27
[wxWidgets.git] / src / x11 / 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 #include "wx/frame.h"
17 #include "wx/app.h"
18 #include "wx/utils.h"
19 #include "wx/gdicmn.h"
20 #include "wx/pen.h"
21 #include "wx/brush.h"
22 #include "wx/cursor.h"
23 #include "wx/icon.h"
24 #include "wx/dialog.h"
25 #include "wx/msgdlg.h"
26 #include "wx/log.h"
27 #include "wx/module.h"
28 #include "wx/memory.h"
29 #include "wx/log.h"
30 #include "wx/intl.h"
31 #include "wx/evtloop.h"
32 #include "wx/cmdline.h"
33
34 #if wxUSE_THREADS
35 #include "wx/thread.h"
36 #endif
37
38 #if wxUSE_WX_RESOURCES
39 #include "wx/resource.h"
40 #endif
41
42 #ifdef __VMS__
43 #pragma message disable nosimpint
44 #endif
45 #include <X11/Xlib.h>
46 #include <X11/Xutil.h>
47 #include <X11/Xresource.h>
48 #include <X11/Xatom.h>
49 #ifdef __VMS__
50 #pragma message enable nosimpint
51 #endif
52
53 #include "wx/x11/private.h"
54
55 #include <string.h>
56
57 extern wxList wxPendingDelete;
58
59 wxApp *wxTheApp = NULL;
60
61 wxHashTable *wxWidgetHashTable = NULL;
62
63 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
64
65 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
66 EVT_IDLE(wxApp::OnIdle)
67 END_EVENT_TABLE()
68
69 #ifdef __WXDEBUG__
70 typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
71
72 XErrorHandlerFunc gs_pfnXErrorHandler = 0;
73
74 static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
75 {
76 // just forward to the default handler for now
77 return gs_pfnXErrorHandler(dpy, xevent);
78 }
79 #endif // __WXDEBUG__
80
81 long wxApp::sm_lastMessageTime = 0;
82 WXDisplay *wxApp::ms_display = NULL;
83
84 bool wxApp::Initialize()
85 {
86 wxClassInfo::InitializeClasses();
87
88 // GL: I'm annoyed ... I don't know where to put this and I don't want to
89 // create a module for that as it's part of the core.
90 #if wxUSE_THREADS
91 wxPendingEventsLocker = new wxCriticalSection();
92 #endif
93
94 wxTheColourDatabase = new wxColourDatabase(wxKEY_STRING);
95 wxTheColourDatabase->Initialize();
96
97 wxInitializeStockLists();
98 wxInitializeStockObjects();
99
100 #if wxUSE_WX_RESOURCES
101 wxInitializeResourceSystem();
102 #endif
103
104 wxWidgetHashTable = new wxHashTable(wxKEY_INTEGER);
105
106 wxModule::RegisterModules();
107 if (!wxModule::InitializeModules()) return FALSE;
108
109 return TRUE;
110 }
111
112 void wxApp::CleanUp()
113 {
114 delete wxWidgetHashTable;
115 wxWidgetHashTable = NULL;
116
117 wxModule::CleanUpModules();
118
119 #if wxUSE_WX_RESOURCES
120 wxCleanUpResourceSystem();
121 #endif
122
123 delete wxTheColourDatabase;
124 wxTheColourDatabase = NULL;
125
126 wxDeleteStockObjects();
127
128 wxDeleteStockLists();
129
130 delete wxTheApp;
131 wxTheApp = NULL;
132
133 wxClassInfo::CleanUpClasses();
134
135 #if wxUSE_THREADS
136 delete wxPendingEvents;
137 delete wxPendingEventsLocker;
138 #endif
139
140 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
141 // At this point we want to check if there are any memory
142 // blocks that aren't part of the wxDebugContext itself,
143 // as a special case. Then when dumping we need to ignore
144 // wxDebugContext, too.
145 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
146 {
147 wxLogDebug("There were memory leaks.");
148 wxDebugContext::Dump();
149 wxDebugContext::PrintStatistics();
150 }
151 #endif
152
153 // do it as the very last thing because everything else can log messages
154 wxLog::DontCreateOnDemand();
155 // do it as the very last thing because everything else can log messages
156 delete wxLog::SetActiveTarget(NULL);
157 }
158
159 // This is set within wxEntryStart -- too early on
160 // to put these in wxTheApp
161 static int g_newArgc = 0;
162 static wxChar** g_newArgv = NULL;
163
164 // NB: argc and argv may be changed here, pass by reference!
165 int wxEntryStart( int& argc, char *argv[] )
166 {
167 #ifdef __WXDEBUG__
168 // install the X error handler
169 gs_pfnXErrorHandler = XSetErrorHandler( wxXErrorHandler );
170 #endif // __WXDEBUG__
171
172 /// TODO
173 #if 0
174 // Parse the arguments.
175 #endif
176
177 Display* xdisplay = XOpenDisplay(NULL);
178
179 if (!xdisplay)
180 {
181 wxLogError( _("wxWindows could not open display. Exiting.") );
182 return -1;
183 }
184
185 wxApp::ms_display = (WXDisplay*) xdisplay;
186
187 XSelectInput( xdisplay, XDefaultRootWindow(xdisplay), PropertyChangeMask);
188
189 // wxSetDetectableAutoRepeat( TRUE );
190
191 if (!wxApp::Initialize())
192 return -1;
193
194 return 0;
195 }
196
197
198 int wxEntryInitGui()
199 {
200 int retValue = 0;
201
202 if ( !wxTheApp->OnInitGui() )
203 retValue = -1;
204
205 return retValue;
206 }
207
208
209 int wxEntry( int argc, char *argv[] )
210 {
211 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
212 // This seems to be necessary since there are 'rogue'
213 // objects present at this point (perhaps global objects?)
214 // Setting a checkpoint will ignore them as far as the
215 // memory checking facility is concerned.
216 // Of course you may argue that memory allocated in globals should be
217 // checked, but this is a reasonable compromise.
218 wxDebugContext::SetCheckpoint();
219 #endif
220 int err = wxEntryStart(argc, argv);
221 if (err)
222 return err;
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 // The command line may have been changed
245 // by stripping out -display etc.
246 if (g_newArgc > 0)
247 {
248 wxTheApp->argc = g_newArgc;
249 wxTheApp->argv = g_newArgv;
250 }
251 else
252 {
253 wxTheApp->argc = argc;
254 wxTheApp->argv = argv;
255 }
256
257 int retValue;
258 retValue = wxEntryInitGui();
259
260 // Here frames insert themselves automatically into wxTopLevelWindows by
261 // getting created in OnInit().
262 if ( retValue == 0 )
263 {
264 if ( !wxTheApp->OnInit() )
265 retValue = -1;
266 }
267
268 if ( retValue == 0 )
269 {
270 if (wxTheApp->Initialized()) retValue = wxTheApp->OnRun();
271 }
272
273 // flush the logged messages if any
274 wxLog *pLog = wxLog::GetActiveTarget();
275 if ( pLog != NULL && pLog->HasPendingMessages() )
276 pLog->Flush();
277
278 delete wxLog::SetActiveTarget(new wxLogStderr); // So dialog boxes aren't used
279 // for further messages
280
281 if (wxTheApp->GetTopWindow())
282 {
283 delete wxTheApp->GetTopWindow();
284 wxTheApp->SetTopWindow(NULL);
285 }
286
287 wxTheApp->DeletePendingObjects();
288
289 wxTheApp->OnExit();
290
291 wxApp::CleanUp();
292
293 return retValue;
294 };
295
296 // Static member initialization
297 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
298
299 wxApp::wxApp()
300 {
301 m_topWindow = NULL;
302 wxTheApp = this;
303 m_className = "";
304 m_wantDebugOutput = TRUE ;
305 m_appName = "";
306 argc = 0;
307 argv = NULL;
308 m_exitOnFrameDelete = TRUE;
309 m_mainColormap = (WXColormap) NULL;
310 m_topLevelWidget = (WXWindow) NULL;
311 m_maxRequestSize = 0;
312 m_mainLoop = NULL;
313 }
314
315 bool wxApp::Initialized()
316 {
317 if (GetTopWindow())
318 return TRUE;
319 else
320 return FALSE;
321 }
322
323 int wxApp::MainLoop()
324 {
325 int rt;
326 m_mainLoop = new wxEventLoop;
327
328 rt = m_mainLoop->Run();
329
330 delete m_mainLoop;
331 m_mainLoop = NULL;
332 return rt;
333 }
334
335 // Processes an X event.
336 void wxApp::ProcessXEvent(WXEvent* _event)
337 {
338 XEvent* event = (XEvent*) _event;
339
340 wxWindow* win = NULL;
341 Window window = event->xany.window;
342 Window actualWindow = window;
343
344 // Find the first wxWindow that corresponds to this event window
345 // TODO: may need to translate coordinates from actualWindow
346 // to window, if the receiving window != wxWindow window
347 // while (window && !(win = wxGetWindowFromTable(window)))
348 // window = wxGetWindowParent(window);
349
350 // Because we're receiving events after a window
351 // has been destroyed, assume a 1:1 match between
352 // Window and wxWindow, so if it's not in the table,
353 // it must have been destroyed.
354
355 win = wxGetWindowFromTable(window);
356 if (!win)
357 return;
358
359 switch (event->type)
360 {
361 case KeyPress:
362 {
363 if (win && !win->IsEnabled())
364 return;
365
366 {
367 if (win)
368 {
369 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
370 wxTranslateKeyEvent(keyEvent, win, window, event);
371
372 wxLogDebug( "OnKey from %s", win->GetName().c_str() );
373
374 // We didn't process wxEVT_KEY_DOWN, so send
375 // wxEVT_CHAR
376 if (!win->GetEventHandler()->ProcessEvent( keyEvent ))
377 {
378 keyEvent.SetEventType(wxEVT_CHAR);
379 win->GetEventHandler()->ProcessEvent( keyEvent );
380 }
381
382 // We intercepted and processed the key down event
383 return;
384 }
385 }
386 return;
387 }
388 case KeyRelease:
389 {
390 if (win && !win->IsEnabled())
391 return;
392
393 if (win)
394 {
395 wxKeyEvent keyEvent(wxEVT_KEY_UP);
396 wxTranslateKeyEvent(keyEvent, win, window, event);
397
398 win->GetEventHandler()->ProcessEvent( keyEvent );
399 }
400 return;
401 }
402 case PropertyNotify:
403 {
404 HandlePropertyChange(_event);
405 return;
406 }
407 case ClientMessage:
408 {
409 if (win && !win->IsEnabled())
410 return;
411
412 Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);;
413 Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);;
414
415 if (event->xclient.message_type == wm_protocols)
416 {
417 if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
418 {
419 if (win)
420 {
421 win->Close(FALSE);
422 }
423 }
424 }
425 return;
426 }
427 case ConfigureNotify:
428 {
429 if (win)
430 {
431 wxSizeEvent sizeEvent( wxSize(event->xconfigure.width,event->xconfigure.height), win->GetId() );
432 sizeEvent.SetEventObject( win );
433
434 win->GetEventHandler()->ProcessEvent( sizeEvent );
435 }
436 }
437 case ResizeRequest:
438 {
439 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
440 * If resize event, don't resize until the last resize event for this
441 * window is recieved. Prevents flicker as windows are resized.
442 */
443
444 Display *disp = (Display*) wxGetDisplay();
445 XEvent report;
446
447 // to avoid flicker
448 report = * event;
449 while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
450
451 // TODO: when implementing refresh optimization, we can use
452 // XtAddExposureToRegion to expand the window's paint region.
453
454 if (win)
455 {
456 wxSize sz = win->GetSize();
457 wxSizeEvent sizeEvent(sz, win->GetId());
458 sizeEvent.SetEventObject(win);
459
460 win->GetEventHandler()->ProcessEvent( sizeEvent );
461 }
462
463 return;
464 }
465 case Expose:
466 {
467 if (win)
468 {
469 win->GetUpdateRegion().Union( event->xexpose.x, event->xexpose.y,
470 event->xexpose.width, event->xexpose.height);
471
472 win->GetClearRegion().Union( event->xexpose.x, event->xexpose.y,
473 event->xexpose.width, event->xexpose.height);
474
475 // if (event->xexpose.count == 0)
476 // win->Update();
477 }
478
479 return;
480 }
481 case EnterNotify:
482 case LeaveNotify:
483 case ButtonPress:
484 case ButtonRelease:
485 case MotionNotify:
486 {
487 if (win && !win->IsEnabled())
488 return;
489
490 if (win)
491 {
492 wxMouseEvent wxevent;
493 wxTranslateMouseEvent(wxevent, win, window, event);
494 win->GetEventHandler()->ProcessEvent( wxevent );
495 }
496 return;
497 }
498 case FocusIn:
499 {
500 if (win && event->xfocus.detail != NotifyPointer)
501 {
502 wxLogDebug( "FocusIn from %s", win->GetName().c_str() );
503
504 wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
505 focusEvent.SetEventObject(win);
506 win->GetEventHandler()->ProcessEvent(focusEvent);
507 }
508 break;
509 }
510 case FocusOut:
511 {
512 if (win && event->xfocus.detail != NotifyPointer)
513 {
514 wxLogDebug( "FocusOut from %s\n", win->GetName().c_str() );
515
516 wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
517 focusEvent.SetEventObject(win);
518 win->GetEventHandler()->ProcessEvent(focusEvent);
519 }
520 break;
521 }
522 case DestroyNotify:
523 {
524 // Do we want to process this (for top-level windows)?
525 // But we want to be able to veto closes, anyway
526 break;
527 }
528 default:
529 {
530 #ifdef __WXDEBUG__
531 //wxString eventName = wxGetXEventName(XEvent& event);
532 //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
533 #endif
534 break;
535 }
536 }
537 }
538
539 // Returns TRUE if more time is needed.
540 // Note that this duplicates wxEventLoopImpl::SendIdleEvent
541 // but ProcessIdle may be needed by apps, so is kept.
542 bool wxApp::ProcessIdle()
543 {
544 wxIdleEvent event;
545 event.SetEventObject(this);
546 ProcessEvent(event);
547
548 return event.MoreRequested();
549 }
550
551 void wxApp::ExitMainLoop()
552 {
553 if (m_mainLoop)
554 m_mainLoop->Exit(0);
555 }
556
557 // Is a message/event pending?
558 bool wxApp::Pending()
559 {
560 return wxEventLoop::GetActive()->Pending();
561 }
562
563 // Dispatch a message.
564 void wxApp::Dispatch()
565 {
566 wxEventLoop::GetActive()->Dispatch();
567 }
568
569 // This should be redefined in a derived class for
570 // handling property change events for XAtom IPC.
571 void wxApp::HandlePropertyChange(WXEvent *event)
572 {
573 // by default do nothing special
574 // TODO: what to do for X11
575 // XtDispatchEvent((XEvent*) event); /* let Motif do the work */
576 }
577
578 void wxApp::OnIdle(wxIdleEvent& event)
579 {
580 static bool s_inOnIdle = FALSE;
581
582 // Avoid recursion (via ProcessEvent default case)
583 if (s_inOnIdle)
584 return;
585
586 s_inOnIdle = TRUE;
587
588 // Resend in the main thread events which have been prepared in other
589 // threads
590 ProcessPendingEvents();
591
592 // 'Garbage' collection of windows deleted with Close()
593 DeletePendingObjects();
594
595 // Send OnIdle events to all windows
596 bool needMore = SendIdleEvents();
597
598 if (needMore)
599 event.RequestMore(TRUE);
600
601 s_inOnIdle = FALSE;
602 }
603
604 void wxWakeUpIdle()
605 {
606 // **** please implement me! ****
607 // Wake up the idle handler processor, even if it is in another thread...
608 }
609
610
611 // Send idle event to all top-level windows
612 bool wxApp::SendIdleEvents()
613 {
614 bool needMore = FALSE;
615
616 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
617 while (node)
618 {
619 wxWindow* win = node->GetData();
620 if (SendIdleEvents(win))
621 needMore = TRUE;
622 node = node->GetNext();
623 }
624
625 return needMore;
626 }
627
628 // Send idle event to window and all subwindows
629 bool wxApp::SendIdleEvents(wxWindow* win)
630 {
631 bool needMore = FALSE;
632
633 wxIdleEvent event;
634 event.SetEventObject(win);
635
636 win->GetEventHandler()->ProcessEvent(event);
637
638 win->OnInternalIdle();
639
640 if (event.MoreRequested())
641 needMore = TRUE;
642
643 wxNode* node = win->GetChildren().First();
644 while (node)
645 {
646 wxWindow* win = (wxWindow*) node->Data();
647 if (SendIdleEvents(win))
648 needMore = TRUE;
649
650 node = node->Next();
651 }
652
653 return needMore;
654 }
655
656 void wxApp::DeletePendingObjects()
657 {
658 wxNode *node = wxPendingDelete.First();
659 while (node)
660 {
661 wxObject *obj = (wxObject *)node->Data();
662
663 delete obj;
664
665 if (wxPendingDelete.Member(obj))
666 delete node;
667
668 // Deleting one object may have deleted other pending
669 // objects, so start from beginning of list again.
670 node = wxPendingDelete.First();
671 }
672 }
673
674 // Create an application context
675 bool wxApp::OnInitGui()
676 {
677 // Eventually this line will be removed, but for
678 // now we don't want to try popping up a dialog
679 // for error messages.
680 delete wxLog::SetActiveTarget(new wxLogStderr);
681
682 if (!wxAppBase::OnInitGui())
683 return FALSE;
684
685
686 GetMainColormap( wxApp::GetDisplay() );
687 m_maxRequestSize = XMaxRequestSize( (Display*) wxApp::GetDisplay() );
688
689 return TRUE;
690 }
691
692 WXColormap wxApp::GetMainColormap(WXDisplay* display)
693 {
694 if (!display) /* Must be called first with non-NULL display */
695 return m_mainColormap;
696
697 int defaultScreen = DefaultScreen((Display*) display);
698 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
699
700 Colormap c = DefaultColormapOfScreen(screen);
701
702 if (!m_mainColormap)
703 m_mainColormap = (WXColormap) c;
704
705 return (WXColormap) c;
706 }
707
708 Window wxGetWindowParent(Window window)
709 {
710 wxASSERT_MSG( window, "invalid window" );
711
712 return (Window) 0;
713
714 Window parent, root = 0;
715 unsigned int noChildren = 0;
716 Window* children = NULL;
717 int res = XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
718 & children, & noChildren);
719 if (children)
720 XFree(children);
721 if (res)
722 return parent;
723 else
724 return (Window) 0;
725 }
726
727 void wxExit()
728 {
729 int retValue = 0;
730 if (wxTheApp)
731 retValue = wxTheApp->OnExit();
732
733 wxApp::CleanUp();
734 /*
735 * Exit in some platform-specific way. Not recommended that the app calls this:
736 * only for emergencies.
737 */
738 exit(retValue);
739 }
740
741 // Yield to other processes
742
743 bool wxApp::Yield(bool onlyIfNeeded)
744 {
745 bool s_inYield = FALSE;
746
747 if ( s_inYield )
748 {
749 if ( !onlyIfNeeded )
750 {
751 wxFAIL_MSG( wxT("wxYield called recursively" ) );
752 }
753
754 return FALSE;
755 }
756
757 s_inYield = TRUE;
758
759 while (wxTheApp && wxTheApp->Pending())
760 wxTheApp->Dispatch();
761
762 s_inYield = FALSE;
763
764 return TRUE;
765 }
766
767 // TODO use XmGetPixmap (?) to get the really standard icons!
768
769 // XPM hack: make the arrays const
770 #define static static const
771
772 #include "wx/generic/info.xpm"
773 #include "wx/generic/error.xpm"
774 #include "wx/generic/question.xpm"
775 #include "wx/generic/warning.xpm"
776
777 #undef static
778
779 wxIcon
780 wxApp::GetStdIcon(int which) const
781 {
782 switch(which)
783 {
784 case wxICON_INFORMATION:
785 return wxIcon(info_xpm);
786
787 case wxICON_QUESTION:
788 return wxIcon(question_xpm);
789
790 case wxICON_EXCLAMATION:
791 return wxIcon(warning_xpm);
792
793 default:
794 wxFAIL_MSG("requested non existent standard icon");
795 // still fall through
796
797 case wxICON_HAND:
798 return wxIcon(error_xpm);
799 }
800 }
801
802 void wxApp::OnAssert(const wxChar *file, int line, const wxChar *msg)
803 {
804 // While the GUI isn't working that well, just print out the
805 // message.
806 #if 0
807 wxAppBase::OnAssert(file, line, msg);
808 #else
809 wxString msg2;
810 msg2.Printf("At file %s:%d: %s", file, line, msg);
811 wxLogDebug(msg2);
812 #endif
813 }
814