1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/x11/app.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
24 #include "wx/dialog.h"
25 #include "wx/memory.h"
26 #include "wx/gdicmn.h"
27 #include "wx/module.h"
30 #include "wx/evtloop.h"
31 #include "wx/filename.h"
33 #include "wx/univ/theme.h"
34 #include "wx/univ/renderer.h"
35 #include "wx/generic/private/timer.h"
38 #include "wx/thread.h"
41 #include "wx/x11/private.h"
45 //------------------------------------------------------------------------
47 //------------------------------------------------------------------------
49 wxWindowHash
*wxWidgetHashTable
= NULL
;
50 wxWindowHash
*wxClientWidgetHashTable
= NULL
;
52 static bool g_showIconic
= false;
53 static wxSize g_initialSize
= wxDefaultSize
;
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
59 static wxWindow
*g_nextFocus
= NULL
;
60 static wxWindow
*g_prevFocus
= NULL
;
62 //------------------------------------------------------------------------
64 //------------------------------------------------------------------------
67 typedef int (*XErrorHandlerFunc
)(Display
*, XErrorEvent
*);
69 XErrorHandlerFunc gs_pfnXErrorHandler
= 0;
71 static int wxXErrorHandler(Display
*dpy
, XErrorEvent
*xevent
)
73 // just forward to the default handler for now
74 if (gs_pfnXErrorHandler
)
75 return gs_pfnXErrorHandler(dpy
, xevent
);
81 //------------------------------------------------------------------------
83 //------------------------------------------------------------------------
85 long wxApp::sm_lastMessageTime
= 0;
87 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
89 bool wxApp::Initialize(int& argC
, wxChar
**argV
)
91 #if defined(__WXDEBUG__) && !wxUSE_NANOX
92 // install the X error handler
93 gs_pfnXErrorHandler
= XSetErrorHandler( wxXErrorHandler
);
97 bool syncDisplay
= false;
100 for ( int i
= 0; i
< argCOrig
; i
++ )
102 if (wxStrcmp( argV
[i
], _T("-display") ) == 0)
108 displayName
= argV
[i
];
114 else if (wxStrcmp( argV
[i
], _T("-geometry") ) == 0)
121 if (wxSscanf(argV
[i
], _T("%dx%d"), &w
, &h
) != 2)
123 wxLogError( _("Invalid geometry specification '%s'"),
124 wxString(argV
[i
]).c_str() );
128 g_initialSize
= wxSize(w
, h
);
135 else if (wxStrcmp( argV
[i
], _T("-sync") ) == 0)
142 else if (wxStrcmp( argV
[i
], _T("-iconic") ) == 0)
151 if ( argC
!= argCOrig
)
153 // remove the argumens we consumed
154 for ( int i
= 0; i
< argC
; i
++ )
158 memmove(argV
+ i
, argV
+ i
+ 1, argCOrig
- i
);
163 // open and set up the X11 display
164 if ( !wxSetDisplay(displayName
) )
166 wxLogError(_("wxWidgets could not open display. Exiting."));
170 Display
*dpy
= wxGlobalDisplay();
172 XSynchronize(dpy
, True
);
174 XSelectInput(dpy
, XDefaultRootWindow(dpy
), PropertyChangeMask
);
176 wxSetDetectableAutoRepeat( true );
179 if ( !wxAppBase::Initialize(argC
, argV
) )
183 // Glib's type system required by Pango
188 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
191 wxWidgetHashTable
= new wxWindowHash
;
192 wxClientWidgetHashTable
= new wxWindowHash
;
197 void wxApp::CleanUp()
199 delete wxWidgetHashTable
;
200 wxWidgetHashTable
= NULL
;
201 delete wxClientWidgetHashTable
;
202 wxClientWidgetHashTable
= NULL
;
204 wxAppBase::CleanUp();
209 m_mainColormap
= NULL
;
210 m_topLevelWidget
= NULL
;
211 m_maxRequestSize
= 0;
212 m_showIconic
= false;
213 m_initialSize
= wxDefaultSize
;
229 //-----------------------------------------------------------------------
230 // X11 predicate function for exposure compression
231 //-----------------------------------------------------------------------
236 Bool found_non_matching
;
240 Bool
wxX11ExposePredicate (Display
*WXUNUSED(display
), XEvent
*xevent
, XPointer arg
)
242 wxExposeInfo
*info
= (wxExposeInfo
*) arg
;
244 if (info
->found_non_matching
)
247 if (xevent
->xany
.type
!= Expose
)
249 info
->found_non_matching
= true;
253 if (xevent
->xexpose
.window
!= info
->window
)
255 info
->found_non_matching
= true;
262 #endif // wxUSE_NANOX
264 //-----------------------------------------------------------------------
265 // Processes an X event, returning true if the event was processed.
266 //-----------------------------------------------------------------------
268 bool wxApp::ProcessXEvent(WXEvent
* _event
)
270 XEvent
* event
= (XEvent
*) _event
;
272 wxWindow
* win
= NULL
;
273 Window window
= XEventGetWindow(event
);
275 Window actualWindow
= window
;
278 // Find the first wxWindow that corresponds to this event window
279 // Because we're receiving events after a window
280 // has been destroyed, assume a 1:1 match between
281 // Window and wxWindow, so if it's not in the table,
282 // it must have been destroyed.
284 win
= wxGetWindowFromTable(window
);
287 #if wxUSE_TWO_WINDOWS
288 win
= wxGetClientWindowFromTable(window
);
295 wxString windowClass
= win
->GetClassInfo()->GetClassName();
302 #if wxUSE_TWO_WINDOWS && !wxUSE_NANOX
303 if (event
->xexpose
.window
!= (Window
)win
->GetClientAreaWindow())
307 info
.window
= event
->xexpose
.window
;
308 info
.found_non_matching
= false;
309 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event
, wxX11ExposePredicate
, (XPointer
) &info
))
311 // Don't worry about optimizing redrawing the border etc.
313 win
->NeedUpdateNcAreaInIdle();
318 win
->GetUpdateRegion().Union( XExposeEventGetX(event
), XExposeEventGetY(event
),
319 XExposeEventGetWidth(event
), XExposeEventGetHeight(event
));
320 win
->GetClearRegion().Union( XExposeEventGetX(event
), XExposeEventGetY(event
),
321 XExposeEventGetWidth(event
), XExposeEventGetHeight(event
));
326 info
.window
= event
->xexpose
.window
;
327 info
.found_non_matching
= false;
328 while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event
, wxX11ExposePredicate
, (XPointer
) &info
))
330 win
->GetUpdateRegion().Union( tmp_event
.xexpose
.x
, tmp_event
.xexpose
.y
,
331 tmp_event
.xexpose
.width
, tmp_event
.xexpose
.height
);
333 win
->GetClearRegion().Union( tmp_event
.xexpose
.x
, tmp_event
.xexpose
.y
,
334 tmp_event
.xexpose
.width
, tmp_event
.xexpose
.height
);
338 // This simplifies the expose and clear areas to simple
340 win
->GetUpdateRegion() = win
->GetUpdateRegion().GetBox();
341 win
->GetClearRegion() = win
->GetClearRegion().GetBox();
343 // If we only have one X11 window, always indicate
344 // that borders might have to be redrawn.
345 if (win
->GetMainWindow() == win
->GetClientAreaWindow())
346 win
->NeedUpdateNcAreaInIdle();
348 // Only erase background, paint in idle time.
349 win
->SendEraseEvents();
361 wxLogTrace( _T("expose"), _T("GraphicsExpose from %s"), win
->GetName().c_str());
363 win
->GetUpdateRegion().Union( event
->xgraphicsexpose
.x
, event
->xgraphicsexpose
.y
,
364 event
->xgraphicsexpose
.width
, event
->xgraphicsexpose
.height
);
366 win
->GetClearRegion().Union( event
->xgraphicsexpose
.x
, event
->xgraphicsexpose
.y
,
367 event
->xgraphicsexpose
.width
, event
->xgraphicsexpose
.height
);
369 if (event
->xgraphicsexpose
.count
== 0)
371 // Only erase background, paint in idle time.
372 win
->SendEraseEvents();
382 if (!win
->IsEnabled())
385 wxKeyEvent
keyEvent(wxEVT_KEY_DOWN
);
386 wxTranslateKeyEvent(keyEvent
, win
, window
, event
);
388 // wxLogDebug( "OnKey from %s", win->GetName().c_str() );
390 // We didn't process wxEVT_KEY_DOWN, so send wxEVT_CHAR
391 if (win
->GetEventHandler()->ProcessEvent( keyEvent
))
394 keyEvent
.SetEventType(wxEVT_CHAR
);
395 // Do the translation again, retaining the ASCII
397 if (wxTranslateKeyEvent(keyEvent
, win
, window
, event
, true) &&
398 win
->GetEventHandler()->ProcessEvent( keyEvent
))
401 if ( (keyEvent
.m_keyCode
== WXK_TAB
) &&
402 win
->GetParent() && (win
->GetParent()->HasFlag( wxTAB_TRAVERSAL
)) )
404 wxNavigationKeyEvent new_event
;
405 new_event
.SetEventObject( win
->GetParent() );
406 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
407 new_event
.SetDirection( (keyEvent
.m_keyCode
== WXK_TAB
) );
408 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
409 new_event
.SetWindowChange( keyEvent
.ControlDown() );
410 new_event
.SetCurrentFocus( win
);
411 return win
->GetParent()->GetEventHandler()->ProcessEvent( new_event
);
418 if (!win
->IsEnabled())
421 wxKeyEvent
keyEvent(wxEVT_KEY_UP
);
422 wxTranslateKeyEvent(keyEvent
, win
, window
, event
);
424 return win
->GetEventHandler()->ProcessEvent( keyEvent
);
426 case ConfigureNotify
:
429 if (event
->update
.utype
== GR_UPDATE_SIZE
)
432 wxTopLevelWindow
*tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
435 tlw
->SetConfigureGeometry( XConfigureEventGetX(event
), XConfigureEventGetY(event
),
436 XConfigureEventGetWidth(event
), XConfigureEventGetHeight(event
) );
439 if ( tlw
&& tlw
->IsShown() )
441 tlw
->SetNeedResizeInIdle();
445 wxSizeEvent
sizeEvent( wxSize(XConfigureEventGetWidth(event
), XConfigureEventGetHeight(event
)), win
->GetId() );
446 sizeEvent
.SetEventObject( win
);
448 return win
->GetEventHandler()->ProcessEvent( sizeEvent
);
456 //wxLogDebug("PropertyNotify: %s", windowClass.c_str());
457 return HandlePropertyChange(_event
);
461 if (!win
->IsEnabled())
464 Atom wm_delete_window
= XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True
);
465 Atom wm_protocols
= XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True
);
467 if (event
->xclient
.message_type
== wm_protocols
)
469 if ((Atom
) (event
->xclient
.data
.l
[0]) == wm_delete_window
)
480 printf( "destroy from %s\n", win
->GetName().c_str() );
485 printf( "create from %s\n", win
->GetName().c_str() );
490 printf( "map request from %s\n", win
->GetName().c_str() );
495 printf( "resize request from %s\n", win
->GetName().c_str() );
497 Display
*disp
= (Display
*) wxGetDisplay();
502 while( XCheckTypedWindowEvent (disp
, actualWindow
, ResizeRequest
, &report
));
504 wxSize sz
= win
->GetSize();
505 wxSizeEvent
sizeEvent(sz
, win
->GetId());
506 sizeEvent
.SetEventObject(win
);
508 return win
->GetEventHandler()->ProcessEvent( sizeEvent
);
513 case GR_EVENT_TYPE_CLOSE_REQ
:
530 if (!win
->IsEnabled())
533 // Here we check if the top level window is
534 // disabled, which is one aspect of modality.
536 while (tlw
&& !tlw
->IsTopLevel())
537 tlw
= tlw
->GetParent();
538 if (tlw
&& !tlw
->IsEnabled())
541 if (event
->type
== ButtonPress
)
543 if ((win
!= wxWindow::FindFocus()) && win
->CanAcceptFocus())
545 // This might actually be done in wxWindow::SetFocus()
546 // and not here. TODO.
547 g_prevFocus
= wxWindow::FindFocus();
550 wxLogTrace( _T("focus"), _T("About to call SetFocus on %s of type %s due to button press"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
552 // Record the fact that this window is
553 // getting the focus, because we'll need to
554 // check if its parent is getting a bogus
555 // focus and duly ignore it.
556 // TODO: may need to have this code in SetFocus, too.
557 extern wxWindow
* g_GettingFocus
;
558 g_GettingFocus
= win
;
564 if (event
->type
== LeaveNotify
|| event
->type
== EnterNotify
)
566 // Throw out NotifyGrab and NotifyUngrab
567 if (event
->xcrossing
.mode
!= NotifyNormal
)
571 wxMouseEvent wxevent
;
572 wxTranslateMouseEvent(wxevent
, win
, window
, event
);
573 return win
->GetEventHandler()->ProcessEvent( wxevent
);
577 if ((event
->xfocus
.detail
!= NotifyPointer
) &&
578 (event
->xfocus
.mode
== NotifyNormal
))
581 wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
583 extern wxWindow
* g_GettingFocus
;
584 if (g_GettingFocus
&& g_GettingFocus
->GetParent() == win
)
586 // Ignore this, this can be a spurious FocusIn
587 // caused by a child having its focus set.
588 g_GettingFocus
= NULL
;
589 wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s being deliberately ignored"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
594 wxFocusEvent
focusEvent(wxEVT_SET_FOCUS
, win
->GetId());
595 focusEvent
.SetEventObject(win
);
596 focusEvent
.SetWindow( g_prevFocus
);
599 return win
->GetEventHandler()->ProcessEvent(focusEvent
);
606 if ((event
->xfocus
.detail
!= NotifyPointer
) &&
607 (event
->xfocus
.mode
== NotifyNormal
))
610 wxLogTrace( _T("focus"), _T("FocusOut from %s of type %s"), win
->GetName().c_str(), win
->GetClassInfo()->GetClassName() );
612 wxFocusEvent
focusEvent(wxEVT_KILL_FOCUS
, win
->GetId());
613 focusEvent
.SetEventObject(win
);
614 focusEvent
.SetWindow( g_nextFocus
);
616 return win
->GetEventHandler()->ProcessEvent(focusEvent
);
622 //wxString eventName = wxGetXEventName(XEvent& event);
623 //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
625 #endif // __WXDEBUG__
631 // This should be redefined in a derived class for
632 // handling property change events for XAtom IPC.
633 bool wxApp::HandlePropertyChange(WXEvent
*WXUNUSED(event
))
635 // by default do nothing special
636 // TODO: what to do for X11
637 // XtDispatchEvent((XEvent*) event);
641 void wxApp::WakeUpIdle()
643 // TODO: use wxMotif implementation?
645 // Wake up the idle handler processor, even if it is in another thread...
649 // Create display, and other initialization
650 bool wxApp::OnInitGui()
653 // Eventually this line will be removed, but for
654 // now we don't want to try popping up a dialog
655 // for error messages.
656 delete wxLog::SetActiveTarget(new wxLogStderr
);
659 if (!wxAppBase::OnInitGui())
662 Display
*dpy
= wxGlobalDisplay();
663 GetMainColormap(dpy
);
665 m_maxRequestSize
= XMaxRequestSize(dpy
);
668 m_visualInfo
= new wxXVisualInfo
;
669 wxFillXVisualInfo(m_visualInfo
, dpy
);
677 #include <pango/pango.h>
678 #include <pango/pangox.h>
679 #ifdef HAVE_PANGO_XFT
680 #include <pango/pangoxft.h>
683 PangoContext
* wxApp::GetPangoContext()
685 static PangoContext
*s_pangoContext
= NULL
;
686 if ( !s_pangoContext
)
688 Display
*dpy
= wxGlobalDisplay();
690 #ifdef HAVE_PANGO_XFT
691 int xscreen
= DefaultScreen(dpy
);
692 static int use_xft
= -1;
695 wxString val
= wxGetenv( L
"GDK_USE_XFT" );
696 use_xft
= val
== L
"1";
700 s_pangoContext
= pango_xft_get_context(dpy
, xscreen
);
702 #endif // HAVE_PANGO_XFT
703 s_pangoContext
= pango_x_get_context(dpy
);
705 if (!PANGO_IS_CONTEXT(s_pangoContext
))
706 wxLogError( wxT("No pango context.") );
709 return s_pangoContext
;
712 #endif // wxUSE_UNICODE
714 WXColormap
wxApp::GetMainColormap(WXDisplay
* display
)
716 if (!display
) /* Must be called first with non-NULL display */
717 return m_mainColormap
;
719 int defaultScreen
= DefaultScreen((Display
*) display
);
720 Screen
* screen
= XScreenOfDisplay((Display
*) display
, defaultScreen
);
722 Colormap c
= DefaultColormapOfScreen(screen
);
725 m_mainColormap
= (WXColormap
) c
;
727 return (WXColormap
) c
;
730 Window
wxGetWindowParent(Window window
)
732 wxASSERT_MSG( window
, _T("invalid window") );
737 // VMS chokes on unreacheable code
738 Window parent
, root
= 0;
742 unsigned int noChildren
= 0;
744 Window
* children
= NULL
;
746 // #define XQueryTree(d,w,r,p,c,nc) GrQueryTree(w,p,c,nc)
751 XQueryTree((Display
*) wxGetDisplay(), window
, & root
, & parent
,
752 & children
, & noChildren
);
766 wxAppConsole::Exit();
769 // Yield to other processes
771 bool wxApp::Yield(bool onlyIfNeeded
)
773 // Sometimes only 2 yields seem
774 // to do the trick, e.g. in the
777 for (i
= 0; i
< 2; i
++)
779 static bool s_inYield
= false;
785 wxFAIL_MSG( wxT("wxYield called recursively" ) );
793 // Make sure we have an event loop object,
794 // or Pending/Dispatch will fail
795 wxEventLoopBase
* const eventLoop
= wxEventLoop::GetActive();
796 wxEventLoop
* newEventLoop
= NULL
;
799 newEventLoop
= new wxEventLoop
;
800 wxEventLoop::SetActive(newEventLoop
);
803 // Call dispatch at least once so that sockets
805 wxTheApp
->Dispatch();
807 while (wxTheApp
&& wxTheApp
->Pending())
808 wxTheApp
->Dispatch();
811 wxGenericTimerImpl::NotifyTimers();
817 wxEventLoop::SetActive(NULL
);
829 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
* cond
, const wxChar
*msg
)
831 // While the GUI isn't working that well, just print out the
834 wxAppBase::OnAssert(file
, line
, cond
, msg
);
837 msg2
.Printf("At file %s:%d: %s", file
, line
, msg
);
842 #endif // __WXDEBUG__