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