]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/x11/app.cpp
wxX11:
[wxWidgets.git] / src / x11 / app.cpp
... / ...
CommitLineData
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
56extern char *wxBuffer;
57extern wxList wxPendingDelete;
58
59wxApp *wxTheApp = NULL;
60
61wxHashTable *wxWidgetHashTable = NULL;
62
63static Window wxGetParentWindow(Window window);
64
65IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
66
67BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
68 EVT_IDLE(wxApp::OnIdle)
69END_EVENT_TABLE()
70
71#ifdef __WXDEBUG__
72typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
73
74XErrorHandlerFunc gs_pfnXErrorHandler = 0;
75
76static 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
83long wxApp::sm_lastMessageTime = 0;
84
85bool 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
117void 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
173int 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
247wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
248
249wxApp::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
266bool wxApp::Initialized()
267{
268 if (GetTopWindow())
269 return TRUE;
270 else
271 return FALSE;
272}
273
274int 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.
287void 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 {
311 if (win)
312 {
313 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
314 wxTranslateKeyEvent(keyEvent, win, window, event);
315
316 // We didn't process wxEVT_KEY_DOWN, so send
317 // wxEVT_CHAR
318 if (!win->GetEventHandler()->ProcessEvent( keyEvent ))
319 {
320 keyEvent.SetEventType(wxEVT_CHAR);
321 win->GetEventHandler()->ProcessEvent( keyEvent );
322 }
323
324 // We intercepted and processed the key down event
325 return;
326 }
327 }
328 return;
329 }
330 case KeyRelease:
331 {
332 if (win && !win->IsEnabled())
333 return;
334
335 if (win)
336 {
337 wxKeyEvent keyEvent(wxEVT_KEY_UP);
338 wxTranslateKeyEvent(keyEvent, win, window, event);
339
340 win->GetEventHandler()->ProcessEvent( keyEvent );
341 }
342 return;
343 }
344 case PropertyNotify:
345 {
346 HandlePropertyChange(_event);
347 return;
348 }
349 case ClientMessage:
350 {
351 Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);;
352 Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);;
353
354 if (event->xclient.message_type == wm_protocols)
355 {
356 if (event->xclient.data.l[0] == wm_delete_window)
357 {
358 if (win)
359 {
360 win->Close(FALSE);
361 }
362 }
363 }
364 return;
365 }
366 case ResizeRequest:
367 {
368 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
369 * If resize event, don't resize until the last resize event for this
370 * window is recieved. Prevents flicker as windows are resized.
371 */
372
373 Display *disp = (Display*) wxGetDisplay();
374 XEvent report;
375
376 // to avoid flicker
377 report = * event;
378 while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
379
380 // TODO: when implementing refresh optimization, we can use
381 // XtAddExposureToRegion to expand the window's paint region.
382
383 if (win)
384 {
385 wxSize sz = win->GetSize();
386 wxSizeEvent sizeEvent(sz, win->GetId());
387 sizeEvent.SetEventObject(win);
388
389 win->GetEventHandler()->ProcessEvent( sizeEvent );
390 }
391
392 return;
393 }
394 case Expose:
395 {
396 if (win)
397 {
398 win->GetUpdateRegion().Union( event->xexpose.x, event->xexpose.y,
399 event->xexpose.width, event->xexpose.height);
400 if (event->xexpose.count == 0)
401 {
402 win->X11SendPaintEvents(); // TODO let an idle handler do that
403 }
404 }
405
406 return;
407 }
408 case EnterNotify:
409 case LeaveNotify:
410 case ButtonPress:
411 case ButtonRelease:
412 case MotionNotify:
413 {
414 if (win && !win->IsEnabled())
415 return;
416
417 if (win)
418 {
419 wxMouseEvent wxevent;
420 wxTranslateMouseEvent(wxevent, win, window, event);
421 win->GetEventHandler()->ProcessEvent( wxevent );
422 }
423 return;
424 }
425 case FocusIn:
426 {
427 if (win && event->xfocus.detail != NotifyPointer)
428 {
429 wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
430 focusEvent.SetEventObject(win);
431 win->GetEventHandler()->ProcessEvent(focusEvent);
432 }
433 break;
434 }
435 case FocusOut:
436 {
437 if (win && event->xfocus.detail != NotifyPointer)
438 {
439 wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
440 focusEvent.SetEventObject(win);
441 win->GetEventHandler()->ProcessEvent(focusEvent);
442 }
443 break;
444 }
445 case DestroyNotify:
446 {
447 // Do we want to process this (for top-level windows)?
448 // But we want to be able to veto closes, anyway
449 break;
450 }
451 default:
452 {
453 break;
454 }
455 }
456}
457
458// Returns TRUE if more time is needed.
459// Note that this duplicates wxEventLoopImpl::SendIdleEvent
460// but ProcessIdle may be needed by apps, so is kept.
461bool wxApp::ProcessIdle()
462{
463 wxIdleEvent event;
464 event.SetEventObject(this);
465 ProcessEvent(event);
466
467 return event.MoreRequested();
468}
469
470void wxApp::ExitMainLoop()
471{
472 if (m_mainLoop)
473 m_mainLoop->Exit(0);
474}
475
476// Is a message/event pending?
477bool wxApp::Pending()
478{
479 return wxEventLoop::GetActive()->Pending();
480}
481
482// Dispatch a message.
483void wxApp::Dispatch()
484{
485 wxEventLoop::GetActive()->Dispatch();
486}
487
488// This should be redefined in a derived class for
489// handling property change events for XAtom IPC.
490void wxApp::HandlePropertyChange(WXEvent *event)
491{
492 // by default do nothing special
493 // TODO: what to do for X11
494 // XtDispatchEvent((XEvent*) event); /* let Motif do the work */
495}
496
497void wxApp::OnIdle(wxIdleEvent& event)
498{
499 static bool inOnIdle = FALSE;
500
501 // Avoid recursion (via ProcessEvent default case)
502 if (inOnIdle)
503 return;
504
505 inOnIdle = TRUE;
506
507 // If there are pending events, we must process them: pending events
508 // are either events to the threads other than main or events posted
509 // with wxPostEvent() functions
510 // GRG: I have moved this here so that all pending events are processed
511 // before starting to delete any objects. This behaves better (in
512 // particular, wrt wxPostEvent) and is coherent with wxGTK's current
513 // behaviour. Also removed the '#if wxUSE_THREADS' around it.
514 // Changed Mar/2000 before 2.1.14
515
516 // Flush pending events.
517 ProcessPendingEvents();
518
519 // 'Garbage' collection of windows deleted with Close().
520 DeletePendingObjects();
521
522 // flush the logged messages if any
523 wxLog *pLog = wxLog::GetActiveTarget();
524 if ( pLog != NULL && pLog->HasPendingMessages() )
525 pLog->Flush();
526
527 // Send OnIdle events to all windows
528 bool needMore = SendIdleEvents();
529
530 if (needMore)
531 event.RequestMore(TRUE);
532
533 inOnIdle = FALSE;
534}
535
536void wxWakeUpIdle()
537{
538 // **** please implement me! ****
539 // Wake up the idle handler processor, even if it is in another thread...
540}
541
542
543// Send idle event to all top-level windows
544bool wxApp::SendIdleEvents()
545{
546 bool needMore = FALSE;
547
548 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
549 while (node)
550 {
551 wxWindow* win = node->GetData();
552 if (SendIdleEvents(win))
553 needMore = TRUE;
554 node = node->GetNext();
555 }
556
557 return needMore;
558}
559
560// Send idle event to window and all subwindows
561bool wxApp::SendIdleEvents(wxWindow* win)
562{
563 bool needMore = FALSE;
564
565 wxIdleEvent event;
566 event.SetEventObject(win);
567 win->ProcessEvent(event);
568
569 if (event.MoreRequested())
570 needMore = TRUE;
571
572 wxNode* node = win->GetChildren().First();
573 while (node)
574 {
575 wxWindow* win = (wxWindow*) node->Data();
576 if (SendIdleEvents(win))
577 needMore = TRUE;
578
579 node = node->Next();
580 }
581 return needMore ;
582}
583
584void wxApp::DeletePendingObjects()
585{
586 wxNode *node = wxPendingDelete.First();
587 while (node)
588 {
589 wxObject *obj = (wxObject *)node->Data();
590
591 delete obj;
592
593 if (wxPendingDelete.Member(obj))
594 delete node;
595
596 // Deleting one object may have deleted other pending
597 // objects, so start from beginning of list again.
598 node = wxPendingDelete.First();
599 }
600}
601
602// Create an application context
603bool wxApp::OnInitGui()
604{
605 // TODO: parse argv and get display to pass to XOpenDisplay
606 Display* dpy = XOpenDisplay(NULL);
607 m_initialDisplay = (WXDisplay*) dpy;
608
609 if (!dpy) {
610 wxString className(wxTheApp->GetClassName());
611 wxLogError(_("wxWindows could not open display for '%s': exiting."),
612 (const char*) className);
613 exit(-1);
614 }
615 XSelectInput((Display*) m_initialDisplay,
616 XDefaultRootWindow((Display*) m_initialDisplay),
617 PropertyChangeMask);
618
619#ifdef __WXDEBUG__
620 // install the X error handler
621 gs_pfnXErrorHandler = XSetErrorHandler(wxXErrorHandler);
622#endif // __WXDEBUG__
623
624 GetMainColormap(dpy);
625 m_maxRequestSize = XMaxRequestSize((Display*) dpy);
626
627 return TRUE;
628}
629
630WXColormap wxApp::GetMainColormap(WXDisplay* display)
631{
632 if (!display) /* Must be called first with non-NULL display */
633 return m_mainColormap;
634
635 int defaultScreen = DefaultScreen((Display*) display);
636 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
637
638 Colormap c = DefaultColormapOfScreen(screen);
639
640 if (!m_mainColormap)
641 m_mainColormap = (WXColormap) c;
642
643 return (WXColormap) c;
644}
645
646Window wxGetParentWindow(Window window)
647{
648 Window parent, root = 0;
649 unsigned int noChildren = 0;
650 if (XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
651 NULL, & noChildren))
652 return parent;
653 else
654 return (Window) 0;
655}
656
657void wxExit()
658{
659 int retValue = 0;
660 if (wxTheApp)
661 retValue = wxTheApp->OnExit();
662
663 wxApp::CleanUp();
664 /*
665 * Exit in some platform-specific way. Not recommended that the app calls this:
666 * only for emergencies.
667 */
668 exit(retValue);
669}
670
671// Yield to other processes
672
673bool wxApp::Yield(bool onlyIfNeeded)
674{
675 bool s_inYield = FALSE;
676
677 if ( s_inYield )
678 {
679 if ( !onlyIfNeeded )
680 {
681 wxFAIL_MSG( wxT("wxYield called recursively" ) );
682 }
683
684 return FALSE;
685 }
686
687 s_inYield = TRUE;
688
689 while (wxTheApp && wxTheApp->Pending())
690 wxTheApp->Dispatch();
691
692 s_inYield = FALSE;
693
694 return TRUE;
695}
696
697// TODO use XmGetPixmap (?) to get the really standard icons!
698
699// XPM hack: make the arrays const
700#define static static const
701
702#include "wx/generic/info.xpm"
703#include "wx/generic/error.xpm"
704#include "wx/generic/question.xpm"
705#include "wx/generic/warning.xpm"
706
707#undef static
708
709wxIcon
710wxApp::GetStdIcon(int which) const
711{
712 switch(which)
713 {
714 case wxICON_INFORMATION:
715 return wxIcon(info_xpm);
716
717 case wxICON_QUESTION:
718 return wxIcon(question_xpm);
719
720 case wxICON_EXCLAMATION:
721 return wxIcon(warning_xpm);
722
723 default:
724 wxFAIL_MSG("requested non existent standard icon");
725 // still fall through
726
727 case wxICON_HAND:
728 return wxIcon(error_xpm);
729 }
730}
731
732// ----------------------------------------------------------------------------
733// accessors for C modules
734// ----------------------------------------------------------------------------
735
736#if 0
737extern "C" XtAppContext wxGetAppContext()
738{
739 return (XtAppContext)wxTheApp->GetAppContext();
740}
741#endif