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