Fix extraction of standard command line arguments in wxX11.
[wxWidgets.git] / src / x11 / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/x11/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 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #include "wx/app.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/hash.h"
19 #include "wx/intl.h"
20 #include "wx/log.h"
21 #include "wx/utils.h"
22 #include "wx/frame.h"
23 #include "wx/icon.h"
24 #include "wx/dialog.h"
25 #include "wx/memory.h"
26 #include "wx/gdicmn.h"
27 #include "wx/module.h"
28 #include "wx/crt.h"
29 #endif
30
31 #include "wx/evtloop.h"
32 #include "wx/filename.h"
33
34 #include "wx/univ/theme.h"
35 #include "wx/univ/renderer.h"
36 #include "wx/generic/private/timer.h"
37
38 #if wxUSE_THREADS
39 #include "wx/thread.h"
40 #endif
41
42 #include "wx/x11/private.h"
43
44 #include <string.h>
45
46 //------------------------------------------------------------------------
47 // global data
48 //------------------------------------------------------------------------
49
50 wxWindowHash *wxWidgetHashTable = NULL;
51 wxWindowHash *wxClientWidgetHashTable = NULL;
52
53 static bool g_showIconic = false;
54 static wxSize g_initialSize = wxDefaultSize;
55
56 // This is required for wxFocusEvent::SetWindow(). It will only
57 // work for focus events which we provoke ourselves (by calling
58 // SetFocus()). It will not work for those events, which X11
59 // generates itself.
60 static wxWindow *g_nextFocus = NULL;
61 static wxWindow *g_prevFocus = NULL;
62
63 //------------------------------------------------------------------------
64 // X11 error handling
65 //------------------------------------------------------------------------
66
67 typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
68
69 XErrorHandlerFunc gs_pfnXErrorHandler = 0;
70
71 static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
72 {
73 // just forward to the default handler for now
74 if (gs_pfnXErrorHandler)
75 return gs_pfnXErrorHandler(dpy, xevent);
76 else
77 return 0;
78 }
79
80 //------------------------------------------------------------------------
81 // wxApp
82 //------------------------------------------------------------------------
83
84 long wxApp::sm_lastMessageTime = 0;
85
86 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
87
88 bool wxApp::Initialize(int& argC, wxChar **argV)
89 {
90 #if !wxUSE_NANOX
91 // install the X error handler
92 gs_pfnXErrorHandler = XSetErrorHandler( wxXErrorHandler );
93 #endif
94
95 wxString displayName;
96 bool syncDisplay = false;
97
98 int argCOrig = argC;
99 for ( int i = 0; i < argCOrig; i++ )
100 {
101 if (wxStrcmp( argV[i], wxT("-display") ) == 0)
102 {
103 if (i < (argCOrig - 1))
104 {
105 argV[i++] = NULL;
106
107 displayName = argV[i];
108
109 argV[i] = NULL;
110 argC -= 2;
111 }
112 }
113 else if (wxStrcmp( argV[i], wxT("-geometry") ) == 0)
114 {
115 if (i < (argCOrig - 1))
116 {
117 argV[i++] = NULL;
118
119 int w, h;
120 if (wxSscanf(argV[i], wxT("%dx%d"), &w, &h) != 2)
121 {
122 wxLogError( _("Invalid geometry specification '%s'"),
123 wxString(argV[i]).c_str() );
124 }
125 else
126 {
127 g_initialSize = wxSize(w, h);
128 }
129
130 argV[i] = NULL;
131 argC -= 2;
132 }
133 }
134 else if (wxStrcmp( argV[i], wxT("-sync") ) == 0)
135 {
136 syncDisplay = true;
137
138 argV[i] = NULL;
139 argC--;
140 }
141 else if (wxStrcmp( argV[i], wxT("-iconic") ) == 0)
142 {
143 g_showIconic = true;
144
145 argV[i] = NULL;
146 argC--;
147 }
148 }
149
150 if ( argC != argCOrig )
151 {
152 // remove the arguments we consumed
153 for ( int i = 0; i < argC; i++ )
154 {
155 while ( !argV[i] )
156 {
157 memmove(argV + i, argV + i + 1, (argCOrig - i)*sizeof(wxChar *));
158 }
159 }
160 }
161
162 // open and set up the X11 display
163 if ( !wxSetDisplay(displayName) )
164 {
165 wxLogError(_("wxWidgets could not open display. Exiting."));
166 return false;
167 }
168
169 Display *dpy = wxGlobalDisplay();
170 if (syncDisplay)
171 XSynchronize(dpy, True);
172
173 XSelectInput(dpy, XDefaultRootWindow(dpy), PropertyChangeMask);
174
175 wxSetDetectableAutoRepeat( true );
176
177
178 if ( !wxAppBase::Initialize(argC, argV) )
179 return false;
180
181 #if wxUSE_UNICODE
182 // Glib's type system required by Pango
183 g_type_init();
184 #endif
185
186 #if wxUSE_INTL
187 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
188 #endif
189
190 wxWidgetHashTable = new wxWindowHash;
191 wxClientWidgetHashTable = new wxWindowHash;
192
193 return true;
194 }
195
196 void wxApp::CleanUp()
197 {
198 delete wxWidgetHashTable;
199 wxWidgetHashTable = NULL;
200 delete wxClientWidgetHashTable;
201 wxClientWidgetHashTable = NULL;
202
203 wxAppBase::CleanUp();
204 }
205
206 wxApp::wxApp()
207 {
208 m_mainColormap = NULL;
209 m_topLevelWidget = NULL;
210 m_maxRequestSize = 0;
211 m_showIconic = false;
212 m_initialSize = wxDefaultSize;
213
214 #if !wxUSE_NANOX
215 m_visualInfo = NULL;
216 #endif
217 }
218
219 wxApp::~wxApp()
220 {
221 #if !wxUSE_NANOX
222 delete m_visualInfo;
223 #endif
224 }
225
226 #if !wxUSE_NANOX
227
228 //-----------------------------------------------------------------------
229 // X11 predicate function for exposure compression
230 //-----------------------------------------------------------------------
231
232 struct wxExposeInfo
233 {
234 Window window;
235 Bool found_non_matching;
236 };
237
238 extern "C"
239 Bool wxX11ExposePredicate (Display *WXUNUSED(display), XEvent *xevent, XPointer arg)
240 {
241 wxExposeInfo *info = (wxExposeInfo*) arg;
242
243 if (info->found_non_matching)
244 return FALSE;
245
246 if (xevent->xany.type != Expose)
247 {
248 info->found_non_matching = true;
249 return FALSE;
250 }
251
252 if (xevent->xexpose.window != info->window)
253 {
254 info->found_non_matching = true;
255 return FALSE;
256 }
257
258 return TRUE;
259 }
260
261 #endif // wxUSE_NANOX
262
263 //-----------------------------------------------------------------------
264 // Processes an X event, returning true if the event was processed.
265 //-----------------------------------------------------------------------
266
267 bool wxApp::ProcessXEvent(WXEvent* _event)
268 {
269 XEvent* event = (XEvent*) _event;
270
271 wxWindow* win = NULL;
272 Window window = XEventGetWindow(event);
273 #if 0
274 Window actualWindow = window;
275 #endif
276
277 // Find the first wxWindow that corresponds to this event window
278 // Because we're receiving events after a window
279 // has been destroyed, assume a 1:1 match between
280 // Window and wxWindow, so if it's not in the table,
281 // it must have been destroyed.
282
283 win = wxGetWindowFromTable(window);
284 if (!win)
285 {
286 #if wxUSE_TWO_WINDOWS
287 win = wxGetClientWindowFromTable(window);
288 if (!win)
289 #endif
290 return false;
291 }
292
293
294 switch (event->type)
295 {
296 case Expose:
297 {
298 #if wxUSE_TWO_WINDOWS && !wxUSE_NANOX
299 if (event->xexpose.window != (Window)win->GetClientAreaWindow())
300 {
301 XEvent tmp_event;
302 wxExposeInfo info;
303 info.window = event->xexpose.window;
304 info.found_non_matching = false;
305 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
306 {
307 // Don't worry about optimizing redrawing the border etc.
308 }
309 win->NeedUpdateNcAreaInIdle();
310 }
311 else
312 #endif
313 {
314 win->GetUpdateRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
315 XExposeEventGetWidth(event), XExposeEventGetHeight(event));
316 win->GetClearRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
317 XExposeEventGetWidth(event), XExposeEventGetHeight(event));
318
319 #if !wxUSE_NANOX
320 XEvent tmp_event;
321 wxExposeInfo info;
322 info.window = event->xexpose.window;
323 info.found_non_matching = false;
324 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
325 {
326 win->GetUpdateRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
327 tmp_event.xexpose.width, tmp_event.xexpose.height );
328
329 win->GetClearRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
330 tmp_event.xexpose.width, tmp_event.xexpose.height );
331 }
332 #endif
333
334 // This simplifies the expose and clear areas to simple
335 // rectangles.
336 win->GetUpdateRegion() = win->GetUpdateRegion().GetBox();
337 win->GetClearRegion() = win->GetClearRegion().GetBox();
338
339 // If we only have one X11 window, always indicate
340 // that borders might have to be redrawn.
341 if (win->GetMainWindow() == win->GetClientAreaWindow())
342 win->NeedUpdateNcAreaInIdle();
343
344 // Only erase background, paint in idle time.
345 win->SendEraseEvents();
346
347 // EXPERIMENT
348 //win->Update();
349 }
350
351 return true;
352 }
353
354 #if !wxUSE_NANOX
355 case GraphicsExpose:
356 {
357 wxLogTrace( wxT("expose"), wxT("GraphicsExpose from %s"), win->GetName().c_str());
358
359 win->GetUpdateRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
360 event->xgraphicsexpose.width, event->xgraphicsexpose.height);
361
362 win->GetClearRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
363 event->xgraphicsexpose.width, event->xgraphicsexpose.height);
364
365 if (event->xgraphicsexpose.count == 0)
366 {
367 // Only erase background, paint in idle time.
368 win->SendEraseEvents();
369 // win->Update();
370 }
371
372 return true;
373 }
374 #endif
375
376 case KeyPress:
377 {
378 if (!win->IsEnabled())
379 return false;
380
381 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
382 wxTranslateKeyEvent(keyEvent, win, window, event);
383
384 // wxLogDebug( "OnKey from %s", win->GetName().c_str() );
385
386 // We didn't process wxEVT_KEY_DOWN, so send wxEVT_CHAR
387 if (win->HandleWindowEvent( keyEvent ))
388 return true;
389
390 keyEvent.SetEventType(wxEVT_CHAR);
391 // Do the translation again, retaining the ASCII
392 // code.
393 if (wxTranslateKeyEvent(keyEvent, win, window, event, true) &&
394 win->HandleWindowEvent( keyEvent ))
395 return true;
396
397 if ( (keyEvent.m_keyCode == WXK_TAB) &&
398 win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
399 {
400 wxNavigationKeyEvent new_event;
401 new_event.SetEventObject( win->GetParent() );
402 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
403 new_event.SetDirection( (keyEvent.m_keyCode == WXK_TAB) );
404 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
405 new_event.SetWindowChange( keyEvent.ControlDown() );
406 new_event.SetCurrentFocus( win );
407 return win->GetParent()->HandleWindowEvent( new_event );
408 }
409
410 return false;
411 }
412 case KeyRelease:
413 {
414 if (!win->IsEnabled())
415 return false;
416
417 wxKeyEvent keyEvent(wxEVT_KEY_UP);
418 wxTranslateKeyEvent(keyEvent, win, window, event);
419
420 return win->HandleWindowEvent( keyEvent );
421 }
422 case ConfigureNotify:
423 {
424 #if wxUSE_NANOX
425 if (event->update.utype == GR_UPDATE_SIZE)
426 #endif
427 {
428 wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
429 if ( tlw )
430 {
431 tlw->SetConfigureGeometry( XConfigureEventGetX(event), XConfigureEventGetY(event),
432 XConfigureEventGetWidth(event), XConfigureEventGetHeight(event) );
433 }
434
435 if ( tlw && tlw->IsShown() )
436 {
437 tlw->SetNeedResizeInIdle();
438 }
439 else
440 {
441 wxSizeEvent sizeEvent( wxSize(XConfigureEventGetWidth(event), XConfigureEventGetHeight(event)), win->GetId() );
442 sizeEvent.SetEventObject( win );
443
444 return win->HandleWindowEvent( sizeEvent );
445 }
446 }
447 return false;
448 }
449 #if !wxUSE_NANOX
450 case PropertyNotify:
451 return HandlePropertyChange(_event);
452
453 case ClientMessage:
454 {
455 if (!win->IsEnabled())
456 return false;
457
458 Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);
459 Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);
460
461 if (event->xclient.message_type == wm_protocols)
462 {
463 if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
464 {
465 win->Close(false);
466 return true;
467 }
468 }
469 return false;
470 }
471 #if 0
472 case DestroyNotify:
473 {
474 printf( "destroy from %s\n", win->GetName().c_str() );
475 break;
476 }
477 case CreateNotify:
478 {
479 printf( "create from %s\n", win->GetName().c_str() );
480 break;
481 }
482 case MapRequest:
483 {
484 printf( "map request from %s\n", win->GetName().c_str() );
485 break;
486 }
487 case ResizeRequest:
488 {
489 printf( "resize request from %s\n", win->GetName().c_str() );
490
491 Display *disp = (Display*) wxGetDisplay();
492 XEvent report;
493
494 // to avoid flicker
495 report = * event;
496 while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
497
498 wxSize sz = win->GetSize();
499 wxSizeEvent sizeEvent(sz, win->GetId());
500 sizeEvent.SetEventObject(win);
501
502 return win->HandleWindowEvent( sizeEvent );
503 }
504 #endif
505 #endif
506 #if wxUSE_NANOX
507 case GR_EVENT_TYPE_CLOSE_REQ:
508 {
509 if (win)
510 {
511 win->Close(false);
512 return true;
513 }
514 return false;
515 break;
516 }
517 #endif
518 case EnterNotify:
519 case LeaveNotify:
520 case ButtonPress:
521 case ButtonRelease:
522 case MotionNotify:
523 {
524 if (!win->IsEnabled())
525 return false;
526
527 // Here we check if the top level window is
528 // disabled, which is one aspect of modality.
529 wxWindow *tlw = win;
530 while (tlw && !tlw->IsTopLevel())
531 tlw = tlw->GetParent();
532 if (tlw && !tlw->IsEnabled())
533 return false;
534
535 if (event->type == ButtonPress)
536 {
537 if ((win != wxWindow::FindFocus()) && win->CanAcceptFocus())
538 {
539 // This might actually be done in wxWindow::SetFocus()
540 // and not here. TODO.
541 g_prevFocus = wxWindow::FindFocus();
542 g_nextFocus = win;
543
544 wxLogTrace( wxT("focus"), wxT("About to call SetFocus on %s of type %s due to button press"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
545
546 // Record the fact that this window is
547 // getting the focus, because we'll need to
548 // check if its parent is getting a bogus
549 // focus and duly ignore it.
550 // TODO: may need to have this code in SetFocus, too.
551 extern wxWindow* g_GettingFocus;
552 g_GettingFocus = win;
553 win->SetFocus();
554 }
555 }
556
557 #if !wxUSE_NANOX
558 if (event->type == LeaveNotify || event->type == EnterNotify)
559 {
560 // Throw out NotifyGrab and NotifyUngrab
561 if (event->xcrossing.mode != NotifyNormal)
562 return false;
563 }
564 #endif
565 wxMouseEvent wxevent;
566 wxTranslateMouseEvent(wxevent, win, window, event);
567 return win->HandleWindowEvent( wxevent );
568 }
569 case FocusIn:
570 #if !wxUSE_NANOX
571 if ((event->xfocus.detail != NotifyPointer) &&
572 (event->xfocus.mode == NotifyNormal))
573 #endif
574 {
575 wxLogTrace( wxT("focus"), wxT("FocusIn from %s of type %s"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
576
577 extern wxWindow* g_GettingFocus;
578 if (g_GettingFocus && g_GettingFocus->GetParent() == win)
579 {
580 // Ignore this, this can be a spurious FocusIn
581 // caused by a child having its focus set.
582 g_GettingFocus = NULL;
583 wxLogTrace( wxT("focus"), wxT("FocusIn from %s of type %s being deliberately ignored"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
584 return true;
585 }
586 else
587 {
588 wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
589 focusEvent.SetEventObject(win);
590 focusEvent.SetWindow( g_prevFocus );
591 g_prevFocus = NULL;
592
593 return win->HandleWindowEvent(focusEvent);
594 }
595 }
596 return false;
597
598 case FocusOut:
599 #if !wxUSE_NANOX
600 if ((event->xfocus.detail != NotifyPointer) &&
601 (event->xfocus.mode == NotifyNormal))
602 #endif
603 {
604 wxLogTrace( wxT("focus"), wxT("FocusOut from %s of type %s"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
605
606 wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
607 focusEvent.SetEventObject(win);
608 focusEvent.SetWindow( g_nextFocus );
609 g_nextFocus = NULL;
610 return win->HandleWindowEvent(focusEvent);
611 }
612 return false;
613 }
614
615 return false;
616 }
617
618 // This should be redefined in a derived class for
619 // handling property change events for XAtom IPC.
620 bool wxApp::HandlePropertyChange(WXEvent *WXUNUSED(event))
621 {
622 // by default do nothing special
623 // TODO: what to do for X11
624 // XtDispatchEvent((XEvent*) event);
625 return false;
626 }
627
628 void wxApp::WakeUpIdle()
629 {
630 // TODO: use wxMotif implementation?
631
632 // Wake up the idle handler processor, even if it is in another thread...
633 }
634
635
636 // Create display, and other initialization
637 bool wxApp::OnInitGui()
638 {
639 #if wxUSE_LOG
640 // Eventually this line will be removed, but for
641 // now we don't want to try popping up a dialog
642 // for error messages.
643 delete wxLog::SetActiveTarget(new wxLogStderr);
644 #endif
645
646 if (!wxAppBase::OnInitGui())
647 return false;
648
649 Display *dpy = wxGlobalDisplay();
650 GetMainColormap(dpy);
651
652 m_maxRequestSize = XMaxRequestSize(dpy);
653
654 #if !wxUSE_NANOX
655 m_visualInfo = new wxXVisualInfo;
656 wxFillXVisualInfo(m_visualInfo, dpy);
657 #endif
658
659 return true;
660 }
661
662 #if wxUSE_UNICODE
663
664 #include <pango/pango.h>
665 #include <pango/pangox.h>
666 #ifdef HAVE_PANGO_XFT
667 #include <pango/pangoxft.h>
668 #endif
669
670 PangoContext* wxApp::GetPangoContext()
671 {
672 static PangoContext *s_pangoContext = NULL;
673 if ( !s_pangoContext )
674 {
675 Display *dpy = wxGlobalDisplay();
676
677 #ifdef HAVE_PANGO_XFT
678 int xscreen = DefaultScreen(dpy);
679 static int use_xft = -1;
680 if (use_xft == -1)
681 {
682 wxString val = wxGetenv( L"GDK_USE_XFT" );
683 use_xft = val == L"1";
684 }
685
686 if (use_xft)
687 s_pangoContext = pango_xft_get_context(dpy, xscreen);
688 else
689 #endif // HAVE_PANGO_XFT
690 s_pangoContext = pango_x_get_context(dpy);
691
692 if (!PANGO_IS_CONTEXT(s_pangoContext))
693 {
694 wxLogError( wxT("No pango context.") );
695 }
696 }
697
698 return s_pangoContext;
699 }
700
701 #endif // wxUSE_UNICODE
702
703 WXColormap wxApp::GetMainColormap(WXDisplay* display)
704 {
705 if (!display) /* Must be called first with non-NULL display */
706 return m_mainColormap;
707
708 int defaultScreen = DefaultScreen((Display*) display);
709 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
710
711 Colormap c = DefaultColormapOfScreen(screen);
712
713 if (!m_mainColormap)
714 m_mainColormap = (WXColormap) c;
715
716 return (WXColormap) c;
717 }
718
719 Window wxGetWindowParent(Window window)
720 {
721 wxASSERT_MSG( window, wxT("invalid window") );
722
723 return (Window) 0;
724
725 #ifndef __VMS
726 // VMS chokes on unreacheable code
727 Window parent, root = 0;
728 #if wxUSE_NANOX
729 int noChildren = 0;
730 #else
731 unsigned int noChildren = 0;
732 #endif
733 Window* children = NULL;
734
735 // #define XQueryTree(d,w,r,p,c,nc) GrQueryTree(w,p,c,nc)
736 int res = 1;
737 #if !wxUSE_NANOX
738 res =
739 #endif
740 XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
741 & children, & noChildren);
742 if (children)
743 XFree(children);
744 if (res)
745 return parent;
746 else
747 return (Window) 0;
748 #endif
749 }
750
751 void wxApp::Exit()
752 {
753 wxApp::CleanUp();
754
755 wxAppConsole::Exit();
756 }
757