]> git.saurik.com Git - wxWidgets.git/blob - src/x11/app.cpp
Added some XSyncs to help size calculations, but positioning
[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.\n");
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 // We didn't process wxEVT_KEY_DOWN, so send
373 // wxEVT_CHAR
374 if (!win->GetEventHandler()->ProcessEvent( keyEvent ))
375 {
376 keyEvent.SetEventType(wxEVT_CHAR);
377 win->GetEventHandler()->ProcessEvent( keyEvent );
378 }
379
380 // We intercepted and processed the key down event
381 return;
382 }
383 }
384 return;
385 }
386 case KeyRelease:
387 {
388 if (win && !win->IsEnabled())
389 return;
390
391 if (win)
392 {
393 wxKeyEvent keyEvent(wxEVT_KEY_UP);
394 wxTranslateKeyEvent(keyEvent, win, window, event);
395
396 win->GetEventHandler()->ProcessEvent( keyEvent );
397 }
398 return;
399 }
400 case PropertyNotify:
401 {
402 HandlePropertyChange(_event);
403 return;
404 }
405 case ClientMessage:
406 {
407 if (win && !win->IsEnabled())
408 return;
409
410 Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);;
411 Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);;
412
413 if (event->xclient.message_type == wm_protocols)
414 {
415 if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
416 {
417 if (win)
418 {
419 win->Close(FALSE);
420 }
421 }
422 }
423 return;
424 }
425 case ConfigureNotify:
426 {
427 if (win)
428 {
429 wxSizeEvent sizeEvent( wxSize(event->xconfigure.width,event->xconfigure.height), win->GetId() );
430 sizeEvent.SetEventObject( win );
431
432 win->GetEventHandler()->ProcessEvent( sizeEvent );
433 }
434 }
435 case ResizeRequest:
436 {
437 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
438 * If resize event, don't resize until the last resize event for this
439 * window is recieved. Prevents flicker as windows are resized.
440 */
441
442 Display *disp = (Display*) wxGetDisplay();
443 XEvent report;
444
445 // to avoid flicker
446 report = * event;
447 while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
448
449 // TODO: when implementing refresh optimization, we can use
450 // XtAddExposureToRegion to expand the window's paint region.
451
452 if (win)
453 {
454 wxSize sz = win->GetSize();
455 wxSizeEvent sizeEvent(sz, win->GetId());
456 sizeEvent.SetEventObject(win);
457
458 win->GetEventHandler()->ProcessEvent( sizeEvent );
459 }
460
461 return;
462 }
463 case Expose:
464 {
465 if (win)
466 {
467 // Schedule update for later
468 win->GetUpdateRegion().Union( event->xexpose.x, event->xexpose.y,
469 event->xexpose.width, event->xexpose.height);
470 }
471
472 return;
473 }
474 case EnterNotify:
475 case LeaveNotify:
476 case ButtonPress:
477 case ButtonRelease:
478 case MotionNotify:
479 {
480 if (win && !win->IsEnabled())
481 return;
482
483 if (win)
484 {
485 wxMouseEvent wxevent;
486 wxTranslateMouseEvent(wxevent, win, window, event);
487 win->GetEventHandler()->ProcessEvent( wxevent );
488 }
489 return;
490 }
491 case FocusIn:
492 {
493 if (win && event->xfocus.detail != NotifyPointer)
494 {
495 wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
496 focusEvent.SetEventObject(win);
497 win->GetEventHandler()->ProcessEvent(focusEvent);
498 }
499 break;
500 }
501 case FocusOut:
502 {
503 if (win && event->xfocus.detail != NotifyPointer)
504 {
505 wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
506 focusEvent.SetEventObject(win);
507 win->GetEventHandler()->ProcessEvent(focusEvent);
508 }
509 break;
510 }
511 case DestroyNotify:
512 {
513 // Do we want to process this (for top-level windows)?
514 // But we want to be able to veto closes, anyway
515 break;
516 }
517 default:
518 {
519 #ifdef __WXDEBUG__
520 //wxString eventName = wxGetXEventName(XEvent& event);
521 //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
522 #endif
523 break;
524 }
525 }
526 }
527
528 // Returns TRUE if more time is needed.
529 // Note that this duplicates wxEventLoopImpl::SendIdleEvent
530 // but ProcessIdle may be needed by apps, so is kept.
531 bool wxApp::ProcessIdle()
532 {
533 wxIdleEvent event;
534 event.SetEventObject(this);
535 ProcessEvent(event);
536
537 return event.MoreRequested();
538 }
539
540 void wxApp::ExitMainLoop()
541 {
542 if (m_mainLoop)
543 m_mainLoop->Exit(0);
544 }
545
546 // Is a message/event pending?
547 bool wxApp::Pending()
548 {
549 return wxEventLoop::GetActive()->Pending();
550 }
551
552 // Dispatch a message.
553 void wxApp::Dispatch()
554 {
555 wxEventLoop::GetActive()->Dispatch();
556 }
557
558 // This should be redefined in a derived class for
559 // handling property change events for XAtom IPC.
560 void wxApp::HandlePropertyChange(WXEvent *event)
561 {
562 // by default do nothing special
563 // TODO: what to do for X11
564 // XtDispatchEvent((XEvent*) event); /* let Motif do the work */
565 }
566
567 void wxApp::OnIdle(wxIdleEvent& event)
568 {
569 static bool s_inOnIdle = FALSE;
570
571 // Avoid recursion (via ProcessEvent default case)
572 if (s_inOnIdle)
573 return;
574
575 s_inOnIdle = TRUE;
576
577 // Resend in the main thread events which have been prepared in other
578 // threads
579 ProcessPendingEvents();
580
581 // 'Garbage' collection of windows deleted with Close()
582 DeletePendingObjects();
583
584 // Send OnIdle events to all windows
585 bool needMore = SendIdleEvents();
586
587 if (needMore)
588 event.RequestMore(TRUE);
589
590 s_inOnIdle = FALSE;
591 }
592
593 void wxWakeUpIdle()
594 {
595 // **** please implement me! ****
596 // Wake up the idle handler processor, even if it is in another thread...
597 }
598
599
600 // Send idle event to all top-level windows
601 bool wxApp::SendIdleEvents()
602 {
603 bool needMore = FALSE;
604
605 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
606 while (node)
607 {
608 wxWindow* win = node->GetData();
609 if (SendIdleEvents(win))
610 needMore = TRUE;
611 node = node->GetNext();
612 }
613
614 return needMore;
615 }
616
617 // Send idle event to window and all subwindows
618 bool wxApp::SendIdleEvents(wxWindow* win)
619 {
620 bool needMore = FALSE;
621
622 wxIdleEvent event;
623 event.SetEventObject(win);
624
625 win->GetEventHandler()->ProcessEvent(event);
626
627 win->OnInternalIdle();
628
629 if (event.MoreRequested())
630 needMore = TRUE;
631
632 wxNode* node = win->GetChildren().First();
633 while (node)
634 {
635 wxWindow* win = (wxWindow*) node->Data();
636 if (SendIdleEvents(win))
637 needMore = TRUE;
638
639 node = node->Next();
640 }
641
642 return needMore;
643 }
644
645 void wxApp::DeletePendingObjects()
646 {
647 wxNode *node = wxPendingDelete.First();
648 while (node)
649 {
650 wxObject *obj = (wxObject *)node->Data();
651
652 delete obj;
653
654 if (wxPendingDelete.Member(obj))
655 delete node;
656
657 // Deleting one object may have deleted other pending
658 // objects, so start from beginning of list again.
659 node = wxPendingDelete.First();
660 }
661 }
662
663 // Create an application context
664 bool wxApp::OnInitGui()
665 {
666 // Eventually this line will be removed, but for
667 // now we don't want to try popping up a dialog
668 // for error messages.
669 delete wxLog::SetActiveTarget(new wxLogStderr);
670
671 if (!wxAppBase::OnInitGui())
672 return FALSE;
673
674
675 GetMainColormap( wxApp::GetDisplay() );
676 m_maxRequestSize = XMaxRequestSize( (Display*) wxApp::GetDisplay() );
677
678 return TRUE;
679 }
680
681 WXColormap wxApp::GetMainColormap(WXDisplay* display)
682 {
683 if (!display) /* Must be called first with non-NULL display */
684 return m_mainColormap;
685
686 int defaultScreen = DefaultScreen((Display*) display);
687 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
688
689 Colormap c = DefaultColormapOfScreen(screen);
690
691 if (!m_mainColormap)
692 m_mainColormap = (WXColormap) c;
693
694 return (WXColormap) c;
695 }
696
697 Window wxGetWindowParent(Window window)
698 {
699 wxASSERT_MSG( window, "invalid window" );
700
701 return (Window) 0;
702
703 Window parent, root = 0;
704 unsigned int noChildren = 0;
705 Window* children = NULL;
706 int res = XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
707 & children, & noChildren);
708 if (children)
709 XFree(children);
710 if (res)
711 return parent;
712 else
713 return (Window) 0;
714 }
715
716 void wxExit()
717 {
718 int retValue = 0;
719 if (wxTheApp)
720 retValue = wxTheApp->OnExit();
721
722 wxApp::CleanUp();
723 /*
724 * Exit in some platform-specific way. Not recommended that the app calls this:
725 * only for emergencies.
726 */
727 exit(retValue);
728 }
729
730 // Yield to other processes
731
732 bool wxApp::Yield(bool onlyIfNeeded)
733 {
734 bool s_inYield = FALSE;
735
736 if ( s_inYield )
737 {
738 if ( !onlyIfNeeded )
739 {
740 wxFAIL_MSG( wxT("wxYield called recursively" ) );
741 }
742
743 return FALSE;
744 }
745
746 s_inYield = TRUE;
747
748 while (wxTheApp && wxTheApp->Pending())
749 wxTheApp->Dispatch();
750
751 s_inYield = FALSE;
752
753 return TRUE;
754 }
755
756 // TODO use XmGetPixmap (?) to get the really standard icons!
757
758 // XPM hack: make the arrays const
759 #define static static const
760
761 #include "wx/generic/info.xpm"
762 #include "wx/generic/error.xpm"
763 #include "wx/generic/question.xpm"
764 #include "wx/generic/warning.xpm"
765
766 #undef static
767
768 wxIcon
769 wxApp::GetStdIcon(int which) const
770 {
771 switch(which)
772 {
773 case wxICON_INFORMATION:
774 return wxIcon(info_xpm);
775
776 case wxICON_QUESTION:
777 return wxIcon(question_xpm);
778
779 case wxICON_EXCLAMATION:
780 return wxIcon(warning_xpm);
781
782 default:
783 wxFAIL_MSG("requested non existent standard icon");
784 // still fall through
785
786 case wxICON_HAND:
787 return wxIcon(error_xpm);
788 }
789 }
790
791 void wxApp::OnAssert(const wxChar *file, int line, const wxChar *msg)
792 {
793 // While the GUI isn't working that well, just print out the
794 // message.
795 #if 0
796 wxAppBase::OnAssert(file, line, msg);
797 #else
798 wxString msg2;
799 msg2.Printf("At file %s:%d: %s", file, line, msg);
800 wxLogDebug(msg2);
801 #endif
802 }
803