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