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