blob: 1efd4432fab0a2eec30dbe20f082b5afc2f850ba [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#define _SV_SALDATA_CXX
// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <poll.h>
#ifdef FREEBSD
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include <unx/gtk/gtkdata.hxx>
#include <unx/gtk/gtkinst.hxx>
#include <unx/gtk/gtkframe.hxx>
#include <unx/salobj.h>
#include <osl/thread.h>
#include <osl/process.h>
#include <tools/debug.hxx>
#include "unx/i18n_im.hxx"
#include "unx/i18n_xkb.hxx"
#include <unx/wmadaptor.hxx>
#include "unx/x11_cursors/salcursors.h"
#include <vcl/svapp.hxx>
using namespace rtl;
using namespace vcl_sal;
/***************************************************************************
* class GtkDisplay *
***************************************************************************/
GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay )
: SalDisplay( gdk_x11_display_get_xdisplay( pDisplay ) ),
m_pGdkDisplay( pDisplay ),
m_bStartupCompleted( false )
{
m_bUseRandRWrapper = false; // use gdk signal instead
for(int i = 0; i < POINTER_COUNT; i++)
m_aCursors[ i ] = NULL;
Init ();
}
GtkSalDisplay::~GtkSalDisplay()
{
if( !m_bStartupCompleted )
gdk_notify_startup_complete();
doDestruct();
for(int i = 0; i < POINTER_COUNT; i++)
if( m_aCursors[ i ] )
gdk_cursor_unref( m_aCursors[ i ] );
pDisp_ = NULL;
}
void GtkSalDisplay::deregisterFrame( SalFrame* pFrame )
{
if( m_pCapture == pFrame )
{
static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
m_pCapture = NULL;
}
SalDisplay::deregisterFrame( pFrame );
}
extern "C" {
GdkFilterReturn call_filterGdkEvent( GdkXEvent* sys_event,
GdkEvent* event,
gpointer data )
{
return GtkSalDisplay::filterGdkEvent( sys_event, event, data );
}
void signalKeysChanged( GdkKeymap*, gpointer data )
{
GtkSalDisplay* pDisp = (GtkSalDisplay*)data;
pDisp->GetKeyboardName(TRUE);
}
void signalScreenSizeChanged( GdkScreen* pScreen, gpointer data )
{
GtkSalDisplay* pDisp = (GtkSalDisplay*)data;
pDisp->screenSizeChanged( pScreen );
}
void signalMonitorsChanged( GdkScreen* pScreen, gpointer data )
{
GtkSalDisplay* pDisp = (GtkSalDisplay*)data;
pDisp->monitorsChanged( pScreen );
}
}
GdkFilterReturn GtkSalDisplay::filterGdkEvent( GdkXEvent* sys_event,
GdkEvent*,
gpointer data )
{
GdkFilterReturn aFilterReturn = GDK_FILTER_CONTINUE;
XEvent *pEvent = (XEvent *)sys_event;
GtkSalDisplay *pDisplay = (GtkSalDisplay *)data;
// dispatch all XEvents to event callback
if( GetSalData()->m_pInstance->
CallEventCallback( pEvent, sizeof( XEvent ) ) )
aFilterReturn = GDK_FILTER_REMOVE;
GTK_YIELD_GRAB();
if (pDisplay->GetDisplay() == pEvent->xany.display )
{
// #i53471# gtk has no callback mechanism that lets us be notified
// when settings (as in XSETTING and opposed to styles) are changed.
// so we need to listen for corresponding property notifications here
// these should be rare enough so that we can assume that the settings
// actually change when a corresponding PropertyNotify occurs
if( pEvent->type == PropertyNotify &&
pEvent->xproperty.atom == pDisplay->getWMAdaptor()->getAtom( WMAdaptor::XSETTINGS ) &&
! pDisplay->m_aFrames.empty()
)
{
pDisplay->SendInternalEvent( pDisplay->m_aFrames.front(), NULL, SALEVENT_SETTINGSCHANGED );
}
// let's see if one of our frames wants to swallow these events
// get the frame
for( std::list< SalFrame* >::const_iterator it = pDisplay->m_aFrames.begin();
it != pDisplay->m_aFrames.end(); ++it )
{
GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(*it);
if( (GdkNativeWindow)pFrame->GetSystemData()->aWindow == pEvent->xany.window ||
( pFrame->getForeignParent() && pFrame->getForeignParentWindow() == pEvent->xany.window ) ||
( pFrame->getForeignTopLevel() && pFrame->getForeignTopLevelWindow() == pEvent->xany.window )
)
{
if( ! pFrame->Dispatch( pEvent ) )
aFilterReturn = GDK_FILTER_REMOVE;
break;
}
}
X11SalObject::Dispatch( pEvent );
}
return aFilterReturn;
}
void GtkSalDisplay::screenSizeChanged( GdkScreen* pScreen )
{
if( pScreen )
{
int nScreen = gdk_screen_get_number( pScreen );
if( nScreen < static_cast<int>(m_aScreens.size()) )
{
ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]);
if( rSD.m_bInit )
{
rSD.m_aSize = Size( gdk_screen_get_width( pScreen ),
gdk_screen_get_height( pScreen ) );
if( ! m_aFrames.empty() )
m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 );
}
}
else
{
DBG_ERROR( "unknown screen changed size" );
}
}
}
void GtkSalDisplay::monitorsChanged( GdkScreen* pScreen )
{
/* Caution: since we support the _NET_WM_FULLSCREEN_MONITORS property now and
the EWMH spec says, the index used for that needs to be that of the
Xinerama extension, we need to ensure that the order of m_aXineramaScreens is actually intact.
gdk_screen_get_monitor_geometry however has a different sort order that has a default monitor number
Xinerama returns the default monitor as 0.
That means if we fill in the multiple montors vector from gdk, we'll get the wrong order unless
the default monitor is incidentally the same (number 0).
Given that XRandR (which is what gdk_screen_get_monitor_geometry is based on) is
supposed to replace Xinerama, this is bound to get a problem at some time again,
unfortunately there does not currently seem to be a way to map the returns of xinerama to
that of randr. Currently getting Xinerama values again works with updated values, given
a new enough Xserver.
*/
InitXinerama();
(void)pScreen;
#if 0
if( pScreen )
{
if( gdk_display_get_n_screens(m_pGdkDisplay) == 1 )
{
int nScreen = gdk_screen_get_number( pScreen );
if( nScreen == m_nDefaultScreen ) //To-Do, make m_aXineramaScreens a per-screen thing ?
{
gint nMonitors = gdk_screen_get_n_monitors(pScreen);
m_aXineramaScreens = std::vector<Rectangle>();
m_aXineramaScreenIndexMap = std::vector<int>(nMonitors);
for (gint i = 0; i < nMonitors; ++i)
{
GdkRectangle dest;
gdk_screen_get_monitor_geometry(pScreen, i, &dest);
m_aXineramaScreenIndexMap[i] = addXineramaScreenUnique( dest.x, dest.y, dest.width, dest.height );
}
m_bXinerama = m_aXineramaScreens.size() > 1;
if( ! m_aFrames.empty() )
m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 );
}
else
{
DBG_ERROR( "monitors for non-default screen changed, extend-me" );
}
}
}
#endif
}
extern "C"
{
typedef gint(* screen_get_primary_monitor)(GdkScreen *screen);
}
int GtkSalDisplay::GetDefaultMonitorNumber() const
{
int n = 0;
// currently disabled, see remarks in monitorsChanged
#if 0
GdkScreen* pScreen = gdk_display_get_screen( m_pGdkDisplay, m_nDefaultScreen );
#if GTK_CHECK_VERSION(2,20,0)
n = gdk_screen_get_primary_monitor(pScreen);
#else
static screen_get_primary_monitor sym_gdk_screen_get_primary_monitor =
(screen_get_primary_monitor)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_screen_get_primary_monitor" );
if (sym_gdk_screen_get_primary_monitor)
n = sym_gdk_screen_get_primary_monitor( pScreen );
#endif
if( n >= 0 && size_t(n) < m_aXineramaScreenIndexMap.size() )
n = m_aXineramaScreenIndexMap[n];
#endif
return n;
}
void GtkSalDisplay::initScreen( int nScreen ) const
{
if( nScreen < 0 || nScreen >= static_cast<int>(m_aScreens.size()) )
nScreen = m_nDefaultScreen;
ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]);
if( rSD.m_bInit )
return;
// choose visual for screen
SalDisplay::initScreen( nScreen );
// now set a gdk default colormap matching the chosen visual to the screen
GdkVisual* pVis = gdkx_visual_get( rSD.m_aVisual.visualid );
GdkScreen* pScreen = gdk_display_get_screen( m_pGdkDisplay, nScreen );
if( pVis )
{
GdkColormap* pDefCol = gdk_screen_get_default_colormap( pScreen );
GdkVisual* pDefVis = gdk_colormap_get_visual( pDefCol );
if( pDefVis != pVis )
{
pDefCol = gdk_x11_colormap_foreign_new( pVis, rSD.m_aColormap.GetXColormap() );
gdk_screen_set_default_colormap( pScreen, pDefCol );
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "set new gdk color map for screen %d\n", nScreen );
#endif
}
}
#if OSL_DEBUG_LEVEL > 1
else
fprintf( stderr, "not GdkVisual for visual id %d\n", (int)rSD.m_aVisual.visualid );
#endif
}
long GtkSalDisplay::Dispatch( XEvent* pEvent )
{
if( GetDisplay() == pEvent->xany.display )
{
// let's see if one of our frames wants to swallow these events
// get the child frame
for( std::list< SalFrame* >::const_iterator it = m_aFrames.begin();
it != m_aFrames.end(); ++it )
{
if( (GdkNativeWindow)(*it)->GetSystemData()->aWindow == pEvent->xany.window )
return static_cast<GtkSalFrame*>(*it)->Dispatch( pEvent );
}
}
return GDK_FILTER_CONTINUE;
}
GdkCursor* GtkSalDisplay::getFromXPM( const char *pBitmap,
const char *pMask,
int nWidth, int nHeight,
int nXHot, int nYHot )
{
GdkScreen *pScreen = gdk_display_get_default_screen( m_pGdkDisplay );
GdkDrawable *pDrawable = GDK_DRAWABLE( gdk_screen_get_root_window (pScreen) );
GdkBitmap *pBitmapPix = gdk_bitmap_create_from_data
( pDrawable, pBitmap, nWidth, nHeight );
GdkBitmap *pMaskPix = gdk_bitmap_create_from_data
( pDrawable, pMask, nWidth, nHeight );
GdkColormap *pColormap = gdk_drawable_get_colormap( pDrawable );
GdkColor aWhite = { 0, 0xffff, 0xffff, 0xffff };
GdkColor aBlack = { 0, 0, 0, 0 };
gdk_colormap_alloc_color( pColormap, &aBlack, FALSE, TRUE);
gdk_colormap_alloc_color( pColormap, &aWhite, FALSE, TRUE);
return gdk_cursor_new_from_pixmap
( pBitmapPix, pMaskPix,
&aBlack, &aWhite, nXHot, nYHot);
}
#define MAKE_CURSOR( vcl_name, name ) \
case vcl_name: \
pCursor = getFromXPM( (const char*)name##curs##_bits, (const char*)name##mask##_bits, \
name##curs_width, name##curs_height, \
name##curs_x_hot, name##curs_y_hot ); \
break
#define MAP_BUILTIN( vcl_name, gdk_name ) \
case vcl_name: \
pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk_name ); \
break
GdkCursor *GtkSalDisplay::getCursor( PointerStyle ePointerStyle )
{
if (ePointerStyle >= POINTER_COUNT)
return NULL;
if ( !m_aCursors[ ePointerStyle ] )
{
GdkCursor *pCursor = NULL;
switch( ePointerStyle )
{
MAP_BUILTIN( POINTER_ARROW, GDK_LEFT_PTR );
MAP_BUILTIN( POINTER_TEXT, GDK_XTERM );
MAP_BUILTIN( POINTER_HELP, GDK_QUESTION_ARROW );
MAP_BUILTIN( POINTER_CROSS, GDK_CROSSHAIR );
MAP_BUILTIN( POINTER_WAIT, GDK_WATCH );
MAP_BUILTIN( POINTER_NSIZE, GDK_SB_V_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_SSIZE, GDK_SB_V_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_WSIZE, GDK_SB_H_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_ESIZE, GDK_SB_H_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_NWSIZE, GDK_TOP_LEFT_CORNER );
MAP_BUILTIN( POINTER_NESIZE, GDK_TOP_RIGHT_CORNER );
MAP_BUILTIN( POINTER_SWSIZE, GDK_BOTTOM_LEFT_CORNER );
MAP_BUILTIN( POINTER_SESIZE, GDK_BOTTOM_RIGHT_CORNER );
MAP_BUILTIN( POINTER_WINDOW_NSIZE, GDK_TOP_SIDE );
MAP_BUILTIN( POINTER_WINDOW_SSIZE, GDK_BOTTOM_SIDE );
MAP_BUILTIN( POINTER_WINDOW_WSIZE, GDK_LEFT_SIDE );
MAP_BUILTIN( POINTER_WINDOW_ESIZE, GDK_RIGHT_SIDE );
MAP_BUILTIN( POINTER_WINDOW_NWSIZE, GDK_TOP_LEFT_CORNER );
MAP_BUILTIN( POINTER_WINDOW_NESIZE, GDK_TOP_RIGHT_CORNER );
MAP_BUILTIN( POINTER_WINDOW_SWSIZE, GDK_BOTTOM_LEFT_CORNER );
MAP_BUILTIN( POINTER_WINDOW_SESIZE, GDK_BOTTOM_RIGHT_CORNER );
MAP_BUILTIN( POINTER_HSIZEBAR, GDK_SB_H_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_VSIZEBAR, GDK_SB_V_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_REFHAND, GDK_HAND1 );
MAP_BUILTIN( POINTER_HAND, GDK_HAND2 );
MAP_BUILTIN( POINTER_PEN, GDK_PENCIL );
MAP_BUILTIN( POINTER_HSPLIT, GDK_SB_H_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_VSPLIT, GDK_SB_V_DOUBLE_ARROW );
MAP_BUILTIN( POINTER_MOVE, GDK_FLEUR );
MAKE_CURSOR( POINTER_NULL, null );
MAKE_CURSOR( POINTER_MAGNIFY, magnify_ );
MAKE_CURSOR( POINTER_FILL, fill_ );
MAKE_CURSOR( POINTER_MOVEDATA, movedata_ );
MAKE_CURSOR( POINTER_COPYDATA, copydata_ );
MAKE_CURSOR( POINTER_MOVEFILE, movefile_ );
MAKE_CURSOR( POINTER_COPYFILE, copyfile_ );
MAKE_CURSOR( POINTER_MOVEFILES, movefiles_ );
MAKE_CURSOR( POINTER_COPYFILES, copyfiles_ );
MAKE_CURSOR( POINTER_NOTALLOWED, nodrop_ );
MAKE_CURSOR( POINTER_ROTATE, rotate_ );
MAKE_CURSOR( POINTER_HSHEAR, hshear_ );
MAKE_CURSOR( POINTER_VSHEAR, vshear_ );
MAKE_CURSOR( POINTER_DRAW_LINE, drawline_ );
MAKE_CURSOR( POINTER_DRAW_RECT, drawrect_ );
MAKE_CURSOR( POINTER_DRAW_POLYGON, drawpolygon_ );
MAKE_CURSOR( POINTER_DRAW_BEZIER, drawbezier_ );
MAKE_CURSOR( POINTER_DRAW_ARC, drawarc_ );
MAKE_CURSOR( POINTER_DRAW_PIE, drawpie_ );
MAKE_CURSOR( POINTER_DRAW_CIRCLECUT, drawcirclecut_ );
MAKE_CURSOR( POINTER_DRAW_ELLIPSE, drawellipse_ );
MAKE_CURSOR( POINTER_DRAW_CONNECT, drawconnect_ );
MAKE_CURSOR( POINTER_DRAW_TEXT, drawtext_ );
MAKE_CURSOR( POINTER_MIRROR, mirror_ );
MAKE_CURSOR( POINTER_CROOK, crook_ );
MAKE_CURSOR( POINTER_CROP, crop_ );
MAKE_CURSOR( POINTER_MOVEPOINT, movepoint_ );
MAKE_CURSOR( POINTER_MOVEBEZIERWEIGHT, movebezierweight_ );
MAKE_CURSOR( POINTER_DRAW_FREEHAND, drawfreehand_ );
MAKE_CURSOR( POINTER_DRAW_CAPTION, drawcaption_ );
MAKE_CURSOR( POINTER_LINKDATA, linkdata_ );
MAKE_CURSOR( POINTER_MOVEDATALINK, movedlnk_ );
MAKE_CURSOR( POINTER_COPYDATALINK, copydlnk_ );
MAKE_CURSOR( POINTER_LINKFILE, linkfile_ );
MAKE_CURSOR( POINTER_MOVEFILELINK, moveflnk_ );
MAKE_CURSOR( POINTER_COPYFILELINK, copyflnk_ );
MAKE_CURSOR( POINTER_CHART, chart_ );
MAKE_CURSOR( POINTER_DETECTIVE, detective_ );
MAKE_CURSOR( POINTER_PIVOT_COL, pivotcol_ );
MAKE_CURSOR( POINTER_PIVOT_ROW, pivotrow_ );
MAKE_CURSOR( POINTER_PIVOT_FIELD, pivotfld_ );
MAKE_CURSOR( POINTER_PIVOT_DELETE, pivotdel_ );
MAKE_CURSOR( POINTER_CHAIN, chain_ );
MAKE_CURSOR( POINTER_CHAIN_NOTALLOWED, chainnot_ );
MAKE_CURSOR( POINTER_TIMEEVENT_MOVE, timemove_ );
MAKE_CURSOR( POINTER_TIMEEVENT_SIZE, timesize_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_N, asn_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_S, ass_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_W, asw_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_E, ase_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_NW, asnw_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_NE, asne_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_SW, assw_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_SE, asse_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_NS, asns_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_WE, aswe_ );
MAKE_CURSOR( POINTER_AUTOSCROLL_NSWE, asnswe_ );
MAKE_CURSOR( POINTER_AIRBRUSH, airbrush_ );
MAKE_CURSOR( POINTER_TEXT_VERTICAL, vertcurs_ );
// --> FME 2004-07-30 #i32329# Enhanced table selection
MAKE_CURSOR( POINTER_TAB_SELECT_S, tblsels_ );
MAKE_CURSOR( POINTER_TAB_SELECT_E, tblsele_ );
MAKE_CURSOR( POINTER_TAB_SELECT_SE, tblselse_ );
MAKE_CURSOR( POINTER_TAB_SELECT_W, tblselw_ );
MAKE_CURSOR( POINTER_TAB_SELECT_SW, tblselsw_ );
// <--
// --> FME 2004-08-16 #i20119# Paintbrush tool
MAKE_CURSOR( POINTER_PAINTBRUSH, paintbrush_ );
// <--
default:
fprintf( stderr, "pointer %d not implemented", ePointerStyle );
break;
}
if( !pCursor )
pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, GDK_LEFT_PTR );
m_aCursors[ ePointerStyle ] = pCursor;
}
return m_aCursors[ ePointerStyle ];
}
int GtkSalDisplay::CaptureMouse( SalFrame* pSFrame )
{
GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(pSFrame);
if( !pFrame )
{
if( m_pCapture )
static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
m_pCapture = NULL;
return 0;
}
if( m_pCapture )
{
if( pFrame == m_pCapture )
return 1;
static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
}
m_pCapture = pFrame;
static_cast<GtkSalFrame*>(pFrame)->grabPointer( TRUE );
return 1;
}
/***************************************************************************
* class GtkXLib *
***************************************************************************/
class GtkXLib : public SalXLib
{
GtkSalDisplay *m_pGtkSalDisplay;
std::list<GSource *> m_aSources;
GSource *m_pTimeout;
GSource *m_pUserEvent;
oslMutex m_aDispatchMutex;
oslCondition m_aDispatchCondition;
XIOErrorHandler m_aOrigGTKXIOErrorHandler;
public:
static gboolean timeoutFn(gpointer data);
static gboolean userEventFn(gpointer data);
GtkXLib();
virtual ~GtkXLib();
virtual void Init();
virtual void Yield( bool bWait, bool bHandleAllCurrentEvents );
virtual void Insert( int fd, void* data,
YieldFunc pending,
YieldFunc queued,
YieldFunc handle );
virtual void Remove( int fd );
virtual void StartTimer( sal_uLong nMS );
virtual void StopTimer();
virtual void Wakeup();
virtual void PostUserEvent();
};
GtkXLib::GtkXLib()
{
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "GtkXLib::GtkXLib()\n" );
#endif
m_pGtkSalDisplay = NULL;
m_pTimeout = NULL;
m_nTimeoutMS = 0;
m_pUserEvent = NULL;
m_aDispatchCondition = osl_createCondition();
m_aDispatchMutex = osl_createMutex();
m_aOrigGTKXIOErrorHandler = NULL;
}
GtkXLib::~GtkXLib()
{
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "GtkXLib::~GtkXLib()\n" );
#endif
StopTimer();
// sanity check: at this point nobody should be yielding, but wake them
// up anyway before the condition they're waiting on gets destroyed.
osl_setCondition( m_aDispatchCondition );
osl_destroyCondition( m_aDispatchCondition );
osl_destroyMutex( m_aDispatchMutex );
PopXErrorLevel();
XSetIOErrorHandler (m_aOrigGTKXIOErrorHandler);
}
void GtkXLib::Init()
{
int i;
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "GtkXLib::Init()\n" );
#endif
XrmInitialize();
gtk_set_locale();
/*
* open connection to X11 Display
* try in this order:
* o -display command line parameter,
* o $DISPLAY environment variable
* o default display
*/
GdkDisplay *pGdkDisp = NULL;
// is there a -display command line parameter?
rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
int nParams = osl_getCommandArgCount();
rtl::OString aDisplay;
rtl::OUString aParam, aBin;
char** pCmdLineAry = new char*[ nParams+1 ];
osl_getExecutableFile( &aParam.pData );
osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData );
pCmdLineAry[0] = g_strdup( OUStringToOString( aBin, aEnc ).getStr() );
for (i=0; i<nParams; i++)
{
osl_getCommandArg(i, &aParam.pData );
OString aBParam( OUStringToOString( aParam, aEnc ) );
if( aParam.equalsAscii( "-display" ) || aParam.equalsAscii( "--display" ) )
{
pCmdLineAry[i+1] = g_strdup( "--display" );
osl_getCommandArg(i+1, &aParam.pData );
aDisplay = rtl::OUStringToOString( aParam, aEnc );
}
else
pCmdLineAry[i+1] = g_strdup( aBParam.getStr() );
}
// add executable
nParams++;
g_set_application_name(X11SalData::getFrameClassName());
// Set consistant name of the root accessible
rtl::OUString aAppName = Application::GetAppName();
if( aAppName.getLength() > 0 )
{
rtl::OString aPrgName = rtl::OUStringToOString(aAppName, aEnc);
g_set_prgname( aPrgName.getStr());
}
// init gtk/gdk
gtk_init_check( &nParams, &pCmdLineAry );
//gtk_init_check sets XError/XIOError handlers, we want our own one
m_aOrigGTKXIOErrorHandler = XSetIOErrorHandler ( (XIOErrorHandler)X11SalData::XIOErrorHdl );
PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) );
for (i = 0; i < nParams; i++ )
g_free( pCmdLineAry[i] );
delete [] pCmdLineAry;
#if OSL_DEBUG_LEVEL > 1
if (g_getenv ("SAL_DEBUG_UPDATES"))
gdk_window_set_debug_updates (TRUE);
#endif
pGdkDisp = gdk_display_get_default();
if ( !pGdkDisp )
{
rtl::OUString aProgramFileURL;
osl_getExecutableFile( &aProgramFileURL.pData );
rtl::OUString aProgramSystemPath;
osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData);
rtl::OString aProgramName = rtl::OUStringToOString(
aProgramSystemPath,
osl_getThreadTextEncoding() );
fprintf( stderr, "%s X11 error: Can't open display: %s\n",
aProgramName.getStr(), aDisplay.getStr());
fprintf( stderr, " Set DISPLAY environment variable, use -display option\n");
fprintf( stderr, " or check permissions of your X-Server\n");
fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n");
fflush( stderr );
exit(0);
}
/*
* if a -display switch was used, we need
* to set the environment accoringly since
* the clipboard build another connection
* to the xserver using $DISPLAY
*/
rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("DISPLAY"));
const gchar *name = gdk_display_get_name( pGdkDisp );
rtl::OUString envValue(name, strlen(name), aEnc);
osl_setEnvironment(envVar.pData, envValue.pData);
Display *pDisp = gdk_x11_display_get_xdisplay( pGdkDisp );
m_pGtkSalDisplay = new GtkSalDisplay( pGdkDisp );
gdk_window_add_filter( NULL, call_filterGdkEvent, m_pGtkSalDisplay );
PushXErrorLevel( true );
SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp );
XSync( pDisp, False );
pKbdExtension->UseExtension( ! HasXErrorOccured() );
PopXErrorLevel();
m_pGtkSalDisplay->SetKbdExtension( pKbdExtension );
g_signal_connect( G_OBJECT(gdk_keymap_get_default()), "keys_changed", G_CALLBACK(signalKeysChanged), m_pGtkSalDisplay );
// add signal handler to notify screen size changes
int nScreens = gdk_display_get_n_screens( pGdkDisp );
for( int n = 0; n < nScreens; n++ )
{
GdkScreen *pScreen = gdk_display_get_screen( pGdkDisp, n );
if( pScreen )
{
g_signal_connect( G_OBJECT(pScreen), "size-changed", G_CALLBACK(signalScreenSizeChanged), m_pGtkSalDisplay );
if( ! gtk_check_version( 2, 14, 0 ) ) // monitors-changed came in with 2.14, avoid an assertion
g_signal_connect( G_OBJECT(pScreen), "monitors-changed", G_CALLBACK(signalMonitorsChanged), m_pGtkSalDisplay );
}
}
}
extern "C"
{
gboolean call_timeoutFn(gpointer data)
{
return GtkXLib::timeoutFn(data);
}
}
gboolean GtkXLib::timeoutFn(gpointer data)
{
SalData *pSalData = GetSalData();
GtkXLib *pThis = (GtkXLib *) data;
pSalData->m_pInstance->GetYieldMutex()->acquire();
if( pThis->m_pTimeout )
{
g_source_unref (pThis->m_pTimeout);
pThis->m_pTimeout = NULL;
}
// Auto-restart immediately
pThis->StartTimer( pThis->m_nTimeoutMS );
GetX11SalData()->Timeout();
pSalData->m_pInstance->GetYieldMutex()->release();
return FALSE;
}
void GtkXLib::StartTimer( sal_uLong nMS )
{
m_nTimeoutMS = nMS; // for restarting
if (m_pTimeout)
{
g_source_destroy (m_pTimeout);
g_source_unref (m_pTimeout);
}
m_pTimeout = g_timeout_source_new (m_nTimeoutMS);
// #i36226# timers should be executed with lower priority
// than XEvents like in generic plugin
g_source_set_priority( m_pTimeout, G_PRIORITY_LOW );
g_source_set_can_recurse (m_pTimeout, TRUE);
g_source_set_callback (m_pTimeout, call_timeoutFn,
(gpointer) this, NULL);
g_source_attach (m_pTimeout, g_main_context_default ());
SalXLib::StartTimer( nMS );
}
void GtkXLib::StopTimer()
{
SalXLib::StopTimer();
if (m_pTimeout)
{
g_source_destroy (m_pTimeout);
g_source_unref (m_pTimeout);
m_pTimeout = NULL;
}
}
extern "C"
{
gboolean call_userEventFn( gpointer data )
{
return GtkXLib::userEventFn( data );
}
}
gboolean GtkXLib::userEventFn(gpointer data)
{
gboolean bContinue;
GtkXLib *pThis = (GtkXLib *) data;
SalData *pSalData = GetSalData();
pSalData->m_pInstance->GetYieldMutex()->acquire();
pThis->m_pGtkSalDisplay->EventGuardAcquire();
if( !pThis->m_pGtkSalDisplay->HasMoreEvents() )
{
if( pThis->m_pUserEvent )
{
g_source_unref (pThis->m_pUserEvent);
pThis->m_pUserEvent = NULL;
}
bContinue = FALSE;
}
else
bContinue = TRUE;
pThis->m_pGtkSalDisplay->EventGuardRelease();
pThis->m_pGtkSalDisplay->DispatchInternalEvent();
pSalData->m_pInstance->GetYieldMutex()->release();
return bContinue;
}
// hEventGuard_ held during this invocation
void GtkXLib::PostUserEvent()
{
if( !m_pUserEvent ) // not pending anyway
{
m_pUserEvent = g_idle_source_new();
g_source_set_priority( m_pUserEvent, G_PRIORITY_HIGH );
g_source_set_can_recurse (m_pUserEvent, TRUE);
g_source_set_callback (m_pUserEvent, call_userEventFn,
(gpointer) this, NULL);
g_source_attach (m_pUserEvent, g_main_context_default ());
}
Wakeup();
}
void GtkXLib::Wakeup()
{
g_main_context_wakeup( g_main_context_default () );
}
void GtkXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
{
/* #i33212# only enter g_main_context_iteration in one thread at any one
* time, else one of them potentially will never end as long as there is
* another thread in in there. Having only one yieldin thread actually dispatch
* fits the vcl event model (see e.g. the generic plugin).
*/
bool bDispatchThread = false;
gboolean wasEvent = FALSE;
{
// release YieldMutex (and re-acquire at block end)
YieldMutexReleaser aReleaser;
if( osl_tryToAcquireMutex( m_aDispatchMutex ) )
bDispatchThread = true;
else if( ! bWait )
return; // someone else is waiting already, return
if( bDispatchThread )
{
int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
gboolean wasOneEvent = TRUE;
while( nMaxEvents-- && wasOneEvent )
{
wasOneEvent = g_main_context_iteration( NULL, FALSE );
if( wasOneEvent )
wasEvent = TRUE;
}
if( bWait && ! wasEvent )
wasEvent = g_main_context_iteration( NULL, TRUE );
}
else if( bWait )
{
/* #i41693# in case the dispatch thread hangs in join
* for this thread the condition will never be set
* workaround: timeout of 1 second a emergency exit
*/
// we are the dispatch thread
osl_resetCondition( m_aDispatchCondition );
TimeValue aValue = { 1, 0 };
osl_waitCondition( m_aDispatchCondition, &aValue );
}
}
if( bDispatchThread )
{
osl_releaseMutex( m_aDispatchMutex );
if( wasEvent )
osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields
}
}
extern "C" {
typedef struct {
GSource source;
GPollFD pollfd;
GIOCondition condition;
YieldFunc pending;
YieldFunc handle;
gpointer user_data;
} SalWatch;
static gboolean
sal_source_prepare (GSource *source,
gint *timeout)
{
SalWatch *watch = (SalWatch *)source;
*timeout = -1;
if (watch->pending &&
watch->pending (watch->pollfd.fd, watch->user_data)) {
watch->pollfd.revents |= watch->condition;
return TRUE;
}
return FALSE;
}
static gboolean
sal_source_check (GSource *source)
{
SalWatch *watch = (SalWatch *)source;
return watch->pollfd.revents & watch->condition;
}
static gboolean
sal_source_dispatch (GSource *source,
GSourceFunc,
gpointer)
{
SalData *pSalData = GetSalData();
SalWatch *watch = (SalWatch *) source;
pSalData->m_pInstance->GetYieldMutex()->acquire();
watch->handle (watch->pollfd.fd, watch->user_data);
pSalData->m_pInstance->GetYieldMutex()->release();
return TRUE;
}
static void
sal_source_finalize (GSource*)
{
}
static GSourceFuncs sal_source_watch_funcs = {
sal_source_prepare,
sal_source_check,
sal_source_dispatch,
sal_source_finalize,
NULL,
NULL
};
static GSource *
sal_source_create_watch (int fd,
GIOCondition condition,
YieldFunc pending,
YieldFunc handle,
gpointer user_data)
{
GSource *source;
SalWatch *watch;
GMainContext *context = g_main_context_default ();
source = g_source_new (&sal_source_watch_funcs,
sizeof (SalWatch));
watch = (SalWatch *) source;
watch->pollfd.fd = fd;
watch->pollfd.events = condition;
watch->condition = condition;
watch->pending = pending;
watch->handle = handle;
watch->user_data = user_data;
g_source_set_can_recurse (source, TRUE);
g_source_add_poll (source, &watch->pollfd);
g_source_attach (source, context);
return source;
}
} // extern "C"
void GtkXLib::Insert( int nFD,
void *data,
YieldFunc pending,
YieldFunc,
YieldFunc handle )
{
GSource *source = sal_source_create_watch
( nFD, (GIOCondition) ((G_IO_IN|G_IO_PRI) |
(G_IO_ERR|G_IO_HUP|G_IO_NVAL)),
pending, handle, data );
m_aSources.push_back( source );
}
void GtkXLib::Remove( int nFD )
{
::std::list< GSource * >::iterator it;
for (it = m_aSources.begin(); it != m_aSources.end(); ++it)
{
SalWatch *watch = (SalWatch *) *it;
if (watch->pollfd.fd == nFD)
{
m_aSources.erase( it );
g_source_destroy ((GSource *)watch);
g_source_unref ((GSource *)watch);
return;
}
}
}
/**********************************************************************
* class GtkData *
**********************************************************************/
GtkData::~GtkData()
{
}
void GtkData::Init()
{
pXLib_ = new GtkXLib();
pXLib_->Init();
}