Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / gtk3 / gtk3gtkdata.cxx
blob66a5dfcce89091dc60ee6081b20b545665d4c6ae
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #if defined(FREEBSD) || defined(NETBSD)
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #endif
28 #include <unx/gtk/gtkbackend.hxx>
29 #include <unx/gtk/gtkdata.hxx>
30 #include <unx/gtk/gtkinst.hxx>
31 #include <unx/gtk/gtkframe.hxx>
32 #include <bitmaps.hlst>
33 #include <cursor_hotspots.hxx>
34 #include <osl/thread.h>
35 #include <osl/process.h>
37 #include <vcl/svapp.hxx>
38 #include <sal/log.hxx>
40 #include <chrono>
44 using namespace vcl_sal;
46 /***************************************************************
47 * class GtkSalDisplay *
48 ***************************************************************/
49 extern "C" {
50 static GdkFilterReturn call_filterGdkEvent( GdkXEvent* sys_event,
51 GdkEvent* /*event*/,
52 gpointer data )
54 GtkSalDisplay *pDisplay = static_cast<GtkSalDisplay *>(data);
55 return pDisplay->filterGdkEvent( sys_event );
59 GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay ) :
60 m_pSys( GtkSalSystem::GetSingleton() ),
61 m_pGdkDisplay( pDisplay ),
62 m_bStartupCompleted( false )
64 for(GdkCursor* & rpCsr : m_aCursors)
65 rpCsr = nullptr;
67 // FIXME: unify this with SalInst's filter too ?
68 gdk_window_add_filter( nullptr, call_filterGdkEvent, this );
70 if ( getenv( "SAL_IGNOREXERRORS" ) )
71 GetGenericUnixSalData()->ErrorTrapPush(); // and leak the trap
73 m_bX11Display = DLSYM_GDK_IS_X11_DISPLAY( m_pGdkDisplay );
75 gtk_widget_set_default_direction(AllSettings::GetLayoutRTL() ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
78 GtkSalDisplay::~GtkSalDisplay()
80 gdk_window_remove_filter( nullptr, call_filterGdkEvent, this );
82 if( !m_bStartupCompleted )
83 gdk_notify_startup_complete();
85 for(GdkCursor* & rpCsr : m_aCursors)
86 if( rpCsr )
87 gdk_cursor_unref( rpCsr );
90 extern "C" {
92 static void signalScreenSizeChanged( GdkScreen* pScreen, gpointer data )
94 GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data);
95 pDisp->screenSizeChanged( pScreen );
98 static void signalMonitorsChanged( GdkScreen* pScreen, gpointer data )
100 GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data);
101 pDisp->monitorsChanged( pScreen );
106 GdkFilterReturn GtkSalDisplay::filterGdkEvent( GdkXEvent* )
108 (void) this; // loplugin:staticmethods
109 //FIXME: implement filterGdkEvent ...
110 return GDK_FILTER_CONTINUE;
113 void GtkSalDisplay::screenSizeChanged( GdkScreen const * pScreen )
115 m_pSys->countScreenMonitors();
116 if (pScreen)
117 emitDisplayChanged();
120 void GtkSalDisplay::monitorsChanged( GdkScreen const * pScreen )
122 m_pSys->countScreenMonitors();
123 if (pScreen)
124 emitDisplayChanged();
127 GdkCursor* GtkSalDisplay::getFromSvg(OUString const & name, int nXHot, int nYHot)
129 GdkPixbuf* pPixBuf = load_icon_by_name(name);
130 assert(pPixBuf && "missing image?");
131 if (!pPixBuf)
132 return nullptr;
133 guint nDefaultCursorSize = gdk_display_get_default_cursor_size( m_pGdkDisplay );
134 GdkPixbuf* pScaledPixBuf = gdk_pixbuf_scale_simple(pPixBuf, nDefaultCursorSize, nDefaultCursorSize, GDK_INTERP_HYPER);
135 g_object_unref(pPixBuf);
136 GdkCursor* pCursor = gdk_cursor_new_from_pixbuf( m_pGdkDisplay, pScaledPixBuf, nXHot, nYHot);
137 return pCursor;
140 #define MAKE_CURSOR( vcl_name, name, name2 ) \
141 case vcl_name: \
142 pCursor = getFromSvg(name2, name##curs_x_hot, name##curs_y_hot); \
143 break
144 #define MAP_BUILTIN( vcl_name, gdk_name ) \
145 case vcl_name: \
146 pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk_name ); \
147 break
149 GdkCursor *GtkSalDisplay::getCursor( PointerStyle ePointerStyle )
151 if ( !m_aCursors[ ePointerStyle ] )
153 GdkCursor *pCursor = nullptr;
155 switch( ePointerStyle )
157 MAP_BUILTIN( PointerStyle::Arrow, GDK_LEFT_PTR );
158 MAP_BUILTIN( PointerStyle::Text, GDK_XTERM );
159 MAP_BUILTIN( PointerStyle::Help, GDK_QUESTION_ARROW );
160 MAP_BUILTIN( PointerStyle::Cross, GDK_CROSSHAIR );
161 MAP_BUILTIN( PointerStyle::Wait, GDK_WATCH );
163 MAP_BUILTIN( PointerStyle::NSize, GDK_SB_V_DOUBLE_ARROW );
164 MAP_BUILTIN( PointerStyle::SSize, GDK_SB_V_DOUBLE_ARROW );
165 MAP_BUILTIN( PointerStyle::WSize, GDK_SB_H_DOUBLE_ARROW );
166 MAP_BUILTIN( PointerStyle::ESize, GDK_SB_H_DOUBLE_ARROW );
168 MAP_BUILTIN( PointerStyle::NWSize, GDK_TOP_LEFT_CORNER );
169 MAP_BUILTIN( PointerStyle::NESize, GDK_TOP_RIGHT_CORNER );
170 MAP_BUILTIN( PointerStyle::SWSize, GDK_BOTTOM_LEFT_CORNER );
171 MAP_BUILTIN( PointerStyle::SESize, GDK_BOTTOM_RIGHT_CORNER );
173 MAP_BUILTIN( PointerStyle::WindowNSize, GDK_TOP_SIDE );
174 MAP_BUILTIN( PointerStyle::WindowSSize, GDK_BOTTOM_SIDE );
175 MAP_BUILTIN( PointerStyle::WindowWSize, GDK_LEFT_SIDE );
176 MAP_BUILTIN( PointerStyle::WindowESize, GDK_RIGHT_SIDE );
178 MAP_BUILTIN( PointerStyle::WindowNWSize, GDK_TOP_LEFT_CORNER );
179 MAP_BUILTIN( PointerStyle::WindowNESize, GDK_TOP_RIGHT_CORNER );
180 MAP_BUILTIN( PointerStyle::WindowSWSize, GDK_BOTTOM_LEFT_CORNER );
181 MAP_BUILTIN( PointerStyle::WindowSESize, GDK_BOTTOM_RIGHT_CORNER );
183 MAP_BUILTIN( PointerStyle::HSizeBar, GDK_SB_H_DOUBLE_ARROW );
184 MAP_BUILTIN( PointerStyle::VSizeBar, GDK_SB_V_DOUBLE_ARROW );
186 MAP_BUILTIN( PointerStyle::RefHand, GDK_HAND2 );
187 MAP_BUILTIN( PointerStyle::Hand, GDK_HAND2 );
188 MAP_BUILTIN( PointerStyle::Pen, GDK_PENCIL );
190 MAP_BUILTIN( PointerStyle::HSplit, GDK_SB_H_DOUBLE_ARROW );
191 MAP_BUILTIN( PointerStyle::VSplit, GDK_SB_V_DOUBLE_ARROW );
193 MAP_BUILTIN( PointerStyle::Move, GDK_FLEUR );
195 MAKE_CURSOR( PointerStyle::Null, null, RID_CURSOR_NULL );
196 MAKE_CURSOR( PointerStyle::Magnify, magnify_, RID_CURSOR_MAGNIFY );
197 MAKE_CURSOR( PointerStyle::Fill, fill_, RID_CURSOR_FILL );
198 MAKE_CURSOR( PointerStyle::MoveData, movedata_, RID_CURSOR_MOVE_DATA );
199 MAKE_CURSOR( PointerStyle::CopyData, copydata_, RID_CURSOR_COPY_DATA );
200 MAKE_CURSOR( PointerStyle::MoveFile, movefile_, RID_CURSOR_MOVE_FILE );
201 MAKE_CURSOR( PointerStyle::CopyFile, copyfile_, RID_CURSOR_COPY_FILE );
202 MAKE_CURSOR( PointerStyle::MoveFiles, movefiles_, RID_CURSOR_MOVE_FILES );
203 MAKE_CURSOR( PointerStyle::CopyFiles, copyfiles_, RID_CURSOR_COPY_FILES );
204 MAKE_CURSOR( PointerStyle::NotAllowed, nodrop_, RID_CURSOR_NOT_ALLOWED );
205 MAKE_CURSOR( PointerStyle::Rotate, rotate_, RID_CURSOR_ROTATE );
206 MAKE_CURSOR( PointerStyle::HShear, hshear_, RID_CURSOR_H_SHEAR );
207 MAKE_CURSOR( PointerStyle::VShear, vshear_, RID_CURSOR_V_SHEAR );
208 MAKE_CURSOR( PointerStyle::DrawLine, drawline_, RID_CURSOR_DRAW_LINE );
209 MAKE_CURSOR( PointerStyle::DrawRect, drawrect_, RID_CURSOR_DRAW_RECT );
210 MAKE_CURSOR( PointerStyle::DrawPolygon, drawpolygon_, RID_CURSOR_DRAW_POLYGON );
211 MAKE_CURSOR( PointerStyle::DrawBezier, drawbezier_, RID_CURSOR_DRAW_BEZIER );
212 MAKE_CURSOR( PointerStyle::DrawArc, drawarc_, RID_CURSOR_DRAW_ARC );
213 MAKE_CURSOR( PointerStyle::DrawPie, drawpie_, RID_CURSOR_DRAW_PIE );
214 MAKE_CURSOR( PointerStyle::DrawCircleCut, drawcirclecut_, RID_CURSOR_DRAW_CIRCLE_CUT );
215 MAKE_CURSOR( PointerStyle::DrawEllipse, drawellipse_, RID_CURSOR_DRAW_ELLIPSE );
216 MAKE_CURSOR( PointerStyle::DrawConnect, drawconnect_, RID_CURSOR_DRAW_CONNECT );
217 MAKE_CURSOR( PointerStyle::DrawText, drawtext_, RID_CURSOR_DRAW_TEXT );
218 MAKE_CURSOR( PointerStyle::Mirror, mirror_, RID_CURSOR_MIRROR );
219 MAKE_CURSOR( PointerStyle::Crook, crook_, RID_CURSOR_CROOK );
220 MAKE_CURSOR( PointerStyle::Crop, crop_, RID_CURSOR_CROP );
221 MAKE_CURSOR( PointerStyle::MovePoint, movepoint_, RID_CURSOR_MOVE_POINT );
222 MAKE_CURSOR( PointerStyle::MoveBezierWeight, movebezierweight_, RID_CURSOR_MOVE_BEZIER_WEIGHT );
223 MAKE_CURSOR( PointerStyle::DrawFreehand, drawfreehand_, RID_CURSOR_DRAW_FREEHAND );
224 MAKE_CURSOR( PointerStyle::DrawCaption, drawcaption_, RID_CURSOR_DRAW_CAPTION );
225 MAKE_CURSOR( PointerStyle::LinkData, linkdata_, RID_CURSOR_LINK_DATA );
226 MAKE_CURSOR( PointerStyle::MoveDataLink, movedlnk_, RID_CURSOR_MOVE_DATA_LINK );
227 MAKE_CURSOR( PointerStyle::CopyDataLink, copydlnk_, RID_CURSOR_COPY_DATA_LINK );
228 MAKE_CURSOR( PointerStyle::LinkFile, linkfile_, RID_CURSOR_LINK_FILE );
229 MAKE_CURSOR( PointerStyle::MoveFileLink, moveflnk_, RID_CURSOR_MOVE_FILE_LINK );
230 MAKE_CURSOR( PointerStyle::CopyFileLink, copyflnk_, RID_CURSOR_COPY_FILE_LINK );
231 MAKE_CURSOR( PointerStyle::Chart, chart_, RID_CURSOR_CHART );
232 MAKE_CURSOR( PointerStyle::Detective, detective_, RID_CURSOR_DETECTIVE );
233 MAKE_CURSOR( PointerStyle::PivotCol, pivotcol_, RID_CURSOR_PIVOT_COLUMN );
234 MAKE_CURSOR( PointerStyle::PivotRow, pivotrow_, RID_CURSOR_PIVOT_ROW );
235 MAKE_CURSOR( PointerStyle::PivotField, pivotfld_, RID_CURSOR_PIVOT_FIELD );
236 MAKE_CURSOR( PointerStyle::PivotDelete, pivotdel_, RID_CURSOR_PIVOT_DELETE );
237 MAKE_CURSOR( PointerStyle::Chain, chain_, RID_CURSOR_CHAIN );
238 MAKE_CURSOR( PointerStyle::ChainNotAllowed, chainnot_, RID_CURSOR_CHAIN_NOT_ALLOWED );
239 MAKE_CURSOR( PointerStyle::AutoScrollN, asn_, RID_CURSOR_AUTOSCROLL_N );
240 MAKE_CURSOR( PointerStyle::AutoScrollS, ass_, RID_CURSOR_AUTOSCROLL_S );
241 MAKE_CURSOR( PointerStyle::AutoScrollW, asw_, RID_CURSOR_AUTOSCROLL_W );
242 MAKE_CURSOR( PointerStyle::AutoScrollE, ase_, RID_CURSOR_AUTOSCROLL_E );
243 MAKE_CURSOR( PointerStyle::AutoScrollNW, asnw_, RID_CURSOR_AUTOSCROLL_NW );
244 MAKE_CURSOR( PointerStyle::AutoScrollNE, asne_, RID_CURSOR_AUTOSCROLL_NE );
245 MAKE_CURSOR( PointerStyle::AutoScrollSW, assw_, RID_CURSOR_AUTOSCROLL_SW );
246 MAKE_CURSOR( PointerStyle::AutoScrollSE, asse_, RID_CURSOR_AUTOSCROLL_SE );
247 MAKE_CURSOR( PointerStyle::AutoScrollNS, asns_, RID_CURSOR_AUTOSCROLL_NS );
248 MAKE_CURSOR( PointerStyle::AutoScrollWE, aswe_, RID_CURSOR_AUTOSCROLL_WE );
249 MAKE_CURSOR( PointerStyle::AutoScrollNSWE, asnswe_, RID_CURSOR_AUTOSCROLL_NSWE );
250 MAKE_CURSOR( PointerStyle::TextVertical, vertcurs_, RID_CURSOR_TEXT_VERTICAL );
252 // #i32329#
253 MAKE_CURSOR( PointerStyle::TabSelectS, tblsels_, RID_CURSOR_TAB_SELECT_S );
254 MAKE_CURSOR( PointerStyle::TabSelectE, tblsele_, RID_CURSOR_TAB_SELECT_E );
255 MAKE_CURSOR( PointerStyle::TabSelectSE, tblselse_, RID_CURSOR_TAB_SELECT_SE );
256 MAKE_CURSOR( PointerStyle::TabSelectW, tblselw_, RID_CURSOR_TAB_SELECT_W );
257 MAKE_CURSOR( PointerStyle::TabSelectSW, tblselsw_, RID_CURSOR_TAB_SELECT_SW );
259 MAKE_CURSOR( PointerStyle::HideWhitespace, hidewhitespace_, RID_CURSOR_HIDE_WHITESPACE );
260 MAKE_CURSOR( PointerStyle::ShowWhitespace, showwhitespace_, RID_CURSOR_SHOW_WHITESPACE );
262 default:
263 SAL_WARN( "vcl.gtk", "pointer " << static_cast<int>(ePointerStyle) << "not implemented" );
264 break;
266 if( !pCursor )
267 pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, GDK_LEFT_PTR );
269 m_aCursors[ ePointerStyle ] = pCursor;
272 return m_aCursors[ ePointerStyle ];
275 int GtkSalDisplay::CaptureMouse( SalFrame* pSFrame )
277 GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(pSFrame);
279 if( !pFrame )
281 if( m_pCapture )
282 static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
283 m_pCapture = nullptr;
284 return 0;
287 if( m_pCapture )
289 if( pFrame == m_pCapture )
290 return 1;
291 static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
294 m_pCapture = pFrame;
295 pFrame->grabPointer( TRUE );
296 return 1;
299 /**********************************************************************
300 * class GtkSalData *
301 **********************************************************************/
303 GtkSalData::GtkSalData( SalInstance *pInstance )
304 : GenericUnixSalData( SAL_DATA_GTK3, pInstance )
305 , m_aDispatchMutex()
306 , m_aDispatchCondition()
307 , m_pDocumentFocusListener(nullptr)
309 m_pUserEvent = nullptr;
312 #if defined(GDK_WINDOWING_X11)
313 static XIOErrorHandler aOrigXIOErrorHandler = nullptr;
315 extern "C" {
317 static int XIOErrorHdl(Display *)
319 fprintf(stderr, "X IO Error\n");
320 _exit(1);
321 // avoid crashes in unrelated threads that still run while atexit
322 // handlers are in progress
326 #endif
328 GtkSalData::~GtkSalData()
330 Yield( true, true );
331 g_warning ("TESTME: We used to have a stop-timer here, but the central code should do this");
333 // sanity check: at this point nobody should be yielding, but wake them
334 // up anyway before the condition they're waiting on gets destroyed.
335 m_aDispatchCondition.set();
337 osl::MutexGuard g( m_aDispatchMutex );
338 if (m_pUserEvent)
340 g_source_destroy (m_pUserEvent);
341 g_source_unref (m_pUserEvent);
342 m_pUserEvent = nullptr;
344 #if defined(GDK_WINDOWING_X11)
345 if (DLSYM_GDK_IS_X11_DISPLAY(gdk_display_get_default()))
346 XSetIOErrorHandler(aOrigXIOErrorHandler);
347 #endif
350 void GtkSalData::Dispose()
352 deInitNWF();
355 /// Allows events to be processed, returns true if we processed an event.
356 bool GtkSalData::Yield( bool bWait, bool bHandleAllCurrentEvents )
358 /* #i33212# only enter g_main_context_iteration in one thread at any one
359 * time, else one of them potentially will never end as long as there is
360 * another thread in there. Having only one yielding thread actually dispatch
361 * fits the vcl event model (see e.g. the generic plugin).
363 bool bDispatchThread = false;
364 bool bWasEvent = false;
366 // release YieldMutex (and re-acquire at block end)
367 SolarMutexReleaser aReleaser;
368 if( m_aDispatchMutex.tryToAcquire() )
369 bDispatchThread = true;
370 else if( ! bWait )
372 return false; // someone else is waiting already, return
375 if( bDispatchThread )
377 int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
378 bool wasOneEvent = TRUE;
379 while( nMaxEvents-- && wasOneEvent )
381 wasOneEvent = g_main_context_iteration( nullptr, bWait && !bWasEvent );
382 if( wasOneEvent )
383 bWasEvent = true;
385 if (m_aException)
386 std::rethrow_exception(m_aException);
388 else if( bWait )
390 /* #i41693# in case the dispatch thread hangs in join
391 * for this thread the condition will never be set
392 * workaround: timeout of 1 second an emergency exit
394 // we are the dispatch thread
395 m_aDispatchCondition.reset();
396 m_aDispatchCondition.wait(std::chrono::seconds(1));
400 if( bDispatchThread )
402 m_aDispatchMutex.release();
403 if( bWasEvent )
404 m_aDispatchCondition.set(); // trigger non dispatch thread yields
407 return bWasEvent;
410 void GtkSalData::Init()
412 SAL_INFO( "vcl.gtk", "GtkMainloop::Init()" );
415 * open connection to X11 Display
416 * try in this order:
417 * o -display command line parameter,
418 * o $DISPLAY environment variable
419 * o default display
422 GdkDisplay *pGdkDisp = nullptr;
424 // is there a -display command line parameter?
425 rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
426 int nParams = osl_getCommandArgCount();
427 OString aDisplay;
428 OUString aParam, aBin;
429 char** pCmdLineAry = new char*[ nParams+1 ];
430 osl_getExecutableFile( &aParam.pData );
431 osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData );
432 pCmdLineAry[0] = g_strdup( OUStringToOString( aBin, aEnc ).getStr() );
433 for (int i = 0; i < nParams; ++i)
435 osl_getCommandArg(i, &aParam.pData );
436 OString aBParam( OUStringToOString( aParam, aEnc ) );
438 if( aParam == "-display" || aParam == "--display" )
440 pCmdLineAry[i+1] = g_strdup( "--display" );
441 osl_getCommandArg(i+1, &aParam.pData );
442 aDisplay = OUStringToOString( aParam, aEnc );
444 else
445 pCmdLineAry[i+1] = g_strdup( aBParam.getStr() );
447 // add executable
448 nParams++;
450 g_set_application_name(SalGenericSystem::getFrameClassName());
452 // Set consistent name of the root accessible
453 OUString aAppName = Application::GetAppName();
454 if( !aAppName.isEmpty() )
456 OString aPrgName = OUStringToOString(aAppName, aEnc);
457 g_set_prgname(aPrgName.getStr());
460 // init gtk/gdk
461 gtk_init_check( &nParams, &pCmdLineAry );
462 gdk_error_trap_push();
464 for (int i = 0; i < nParams; ++i)
465 g_free( pCmdLineAry[i] );
466 delete [] pCmdLineAry;
468 #if OSL_DEBUG_LEVEL > 1
469 if (g_getenv("SAL_DEBUG_UPDATES"))
470 gdk_window_set_debug_updates (TRUE);
471 #endif
473 pGdkDisp = gdk_display_get_default();
474 if ( !pGdkDisp )
476 OUString aProgramFileURL;
477 osl_getExecutableFile( &aProgramFileURL.pData );
478 OUString aProgramSystemPath;
479 osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData);
480 OString aProgramName = OUStringToOString(
481 aProgramSystemPath,
482 osl_getThreadTextEncoding() );
483 fprintf( stderr, "%s X11 error: Can't open display: %s\n",
484 aProgramName.getStr(), aDisplay.getStr());
485 fprintf( stderr, " Set DISPLAY environment variable, use -display option\n");
486 fprintf( stderr, " or check permissions of your X-Server\n");
487 fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n");
488 fflush( stderr );
489 exit(0);
492 #if defined(GDK_WINDOWING_X11)
493 if (DLSYM_GDK_IS_X11_DISPLAY(pGdkDisp))
494 aOrigXIOErrorHandler = XSetIOErrorHandler(XIOErrorHdl);
495 #endif
497 GtkSalDisplay *pDisplay = new GtkSalDisplay( pGdkDisp );
498 SetDisplay( pDisplay );
500 //FIXME: unwind keyboard extension bits
502 // add signal handler to notify screen size changes
503 int nScreens = gdk_display_get_n_screens( pGdkDisp );
504 for( int n = 0; n < nScreens; n++ )
506 GdkScreen *pScreen = gdk_display_get_screen( pGdkDisp, n );
507 if( pScreen )
509 pDisplay->screenSizeChanged( pScreen );
510 pDisplay->monitorsChanged( pScreen );
511 g_signal_connect( G_OBJECT(pScreen), "size-changed",
512 G_CALLBACK(signalScreenSizeChanged), pDisplay );
513 g_signal_connect( G_OBJECT(pScreen), "monitors-changed",
514 G_CALLBACK(signalMonitorsChanged), GetGtkDisplay() );
519 void GtkSalData::ErrorTrapPush()
521 gdk_error_trap_push ();
524 bool GtkSalData::ErrorTrapPop( bool bIgnoreError )
526 if (bIgnoreError)
528 gdk_error_trap_pop_ignored (); // faster
529 return false;
531 return gdk_error_trap_pop () != 0;
534 #if !GLIB_CHECK_VERSION(2,32,0)
535 #define G_SOURCE_REMOVE FALSE
536 #endif
538 extern "C" {
540 struct SalGtkTimeoutSource {
541 GSource aParent;
542 GTimeVal aFireTime;
543 GtkSalTimer *pInstance;
546 static void sal_gtk_timeout_defer( SalGtkTimeoutSource *pTSource )
548 g_get_current_time( &pTSource->aFireTime );
549 g_time_val_add( &pTSource->aFireTime, pTSource->pInstance->m_nTimeoutMS * 1000 );
552 static gboolean sal_gtk_timeout_expired( SalGtkTimeoutSource *pTSource,
553 gint *nTimeoutMS, GTimeVal const *pTimeNow )
555 glong nDeltaSec = pTSource->aFireTime.tv_sec - pTimeNow->tv_sec;
556 glong nDeltaUSec = pTSource->aFireTime.tv_usec - pTimeNow->tv_usec;
557 if( nDeltaSec < 0 || ( nDeltaSec == 0 && nDeltaUSec < 0) )
559 *nTimeoutMS = 0;
560 return TRUE;
562 if( nDeltaUSec < 0 )
564 nDeltaUSec += 1000000;
565 nDeltaSec -= 1;
567 // if the clock changes backwards we need to cope ...
568 if( static_cast<unsigned long>(nDeltaSec) > 1 + ( pTSource->pInstance->m_nTimeoutMS / 1000 ) )
570 sal_gtk_timeout_defer( pTSource );
571 return TRUE;
574 *nTimeoutMS = MIN( G_MAXINT, ( nDeltaSec * 1000 + (nDeltaUSec + 999) / 1000 ) );
576 return *nTimeoutMS == 0;
579 static gboolean sal_gtk_timeout_prepare( GSource *pSource, gint *nTimeoutMS )
581 SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
583 GTimeVal aTimeNow;
584 g_get_current_time( &aTimeNow );
586 return sal_gtk_timeout_expired( pTSource, nTimeoutMS, &aTimeNow );
589 static gboolean sal_gtk_timeout_check( GSource *pSource )
591 SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
593 GTimeVal aTimeNow;
594 g_get_current_time( &aTimeNow );
596 return ( pTSource->aFireTime.tv_sec < aTimeNow.tv_sec ||
597 ( pTSource->aFireTime.tv_sec == aTimeNow.tv_sec &&
598 pTSource->aFireTime.tv_usec < aTimeNow.tv_usec ) );
601 static gboolean sal_gtk_timeout_dispatch( GSource *pSource, GSourceFunc, gpointer )
603 SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
605 if( !pTSource->pInstance )
606 return FALSE;
608 SolarMutexGuard aGuard;
610 sal_gtk_timeout_defer( pTSource );
612 ImplSVData* pSVData = ImplGetSVData();
613 if( pSVData->maSchedCtx.mpSalTimer )
614 pSVData->maSchedCtx.mpSalTimer->CallCallback();
616 return G_SOURCE_REMOVE;
619 static GSourceFuncs sal_gtk_timeout_funcs =
621 sal_gtk_timeout_prepare,
622 sal_gtk_timeout_check,
623 sal_gtk_timeout_dispatch,
624 nullptr, nullptr, nullptr
628 static SalGtkTimeoutSource *
629 create_sal_gtk_timeout( GtkSalTimer *pTimer )
631 GSource *pSource = g_source_new( &sal_gtk_timeout_funcs, sizeof( SalGtkTimeoutSource ) );
632 SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
633 pTSource->pInstance = pTimer;
635 // #i36226# timers should be executed with lower priority
636 // than XEvents like in generic plugin
637 g_source_set_priority( pSource, G_PRIORITY_LOW );
638 g_source_set_can_recurse( pSource, TRUE );
639 g_source_set_callback( pSource,
640 /* unused dummy */ g_idle_remove_by_data,
641 nullptr, nullptr );
642 g_source_attach( pSource, g_main_context_default() );
643 #ifdef DBG_UTIL
644 g_source_set_name( pSource, "VCL timeout source" );
645 #endif
647 sal_gtk_timeout_defer( pTSource );
649 return pTSource;
652 GtkSalTimer::GtkSalTimer()
653 : m_pTimeout(nullptr)
654 , m_nTimeoutMS(0)
658 GtkSalTimer::~GtkSalTimer()
660 GtkInstance *pInstance = static_cast<GtkInstance *>(GetSalData()->m_pInstance);
661 pInstance->RemoveTimer();
662 Stop();
665 bool GtkSalTimer::Expired()
667 if( !m_pTimeout || g_source_is_destroyed( &m_pTimeout->aParent ) )
668 return false;
670 gint nDummy = 0;
671 GTimeVal aTimeNow;
672 g_get_current_time( &aTimeNow );
673 return !!sal_gtk_timeout_expired( m_pTimeout, &nDummy, &aTimeNow);
676 void GtkSalTimer::Start( sal_uInt64 nMS )
678 // glib is not 64bit safe in this regard.
679 assert( nMS <= G_MAXINT );
680 if ( nMS > G_MAXINT )
681 nMS = G_MAXINT;
682 m_nTimeoutMS = nMS; // for restarting
683 Stop(); // FIXME: ideally re-use an existing m_pTimeout
684 m_pTimeout = create_sal_gtk_timeout( this );
687 void GtkSalTimer::Stop()
689 if( m_pTimeout )
691 g_source_destroy( &m_pTimeout->aParent );
692 g_source_unref( &m_pTimeout->aParent );
693 m_pTimeout = nullptr;
697 extern "C" {
698 static gboolean call_userEventFn( void *data )
700 SolarMutexGuard aGuard;
701 const SalGenericDisplay *pDisplay = GetGenericUnixSalData()->GetDisplay();
702 if ( pDisplay )
704 GtkSalDisplay *pThisDisplay = static_cast<GtkSalData *>(data)->GetGtkDisplay();
705 assert(static_cast<const SalGenericDisplay *>(pThisDisplay) == pDisplay);
706 pThisDisplay->DispatchInternalEvent();
708 return TRUE;
712 void GtkSalData::TriggerUserEventProcessing()
714 if (m_pUserEvent)
715 g_main_context_wakeup (nullptr); // really needed ?
716 else // nothing pending anyway
718 m_pUserEvent = g_idle_source_new();
719 // tdf#110737 set user-events to a lower priority than system redraw
720 // events, which is G_PRIORITY_HIGH_IDLE + 20, so presentations
721 // queue-redraw has a chance to be fulfilled
722 g_source_set_priority (m_pUserEvent, G_PRIORITY_HIGH_IDLE + 30);
723 g_source_set_can_recurse (m_pUserEvent, TRUE);
724 g_source_set_callback (m_pUserEvent, call_userEventFn,
725 static_cast<gpointer>(this), nullptr);
726 g_source_attach (m_pUserEvent, g_main_context_default ());
730 void GtkSalData::TriggerAllUserEventsProcessed()
732 assert( m_pUserEvent );
733 g_source_destroy( m_pUserEvent );
734 g_source_unref( m_pUserEvent );
735 m_pUserEvent = nullptr;
738 void GtkSalDisplay::TriggerUserEventProcessing()
740 GetGtkSalData()->TriggerUserEventProcessing();
743 void GtkSalDisplay::TriggerAllUserEventsProcessed()
745 GetGtkSalData()->TriggerAllUserEventsProcessed();
748 GtkWidget* GtkSalDisplay::findGtkWidgetForNativeHandle(sal_uIntPtr hWindow) const
750 for (auto pSalFrame : m_aFrames )
752 const SystemEnvData* pEnvData = pSalFrame->GetSystemData();
753 if (pEnvData->aWindow == hWindow)
754 return GTK_WIDGET(pEnvData->pWidget);
756 return nullptr;
759 void GtkSalDisplay::deregisterFrame( SalFrame* pFrame )
761 if( m_pCapture == pFrame )
763 static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
764 m_pCapture = nullptr;
766 SalGenericDisplay::deregisterFrame( pFrame );
769 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */