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