1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #if defined(FREEBSD) || defined(NETBSD)
25 #include <sys/types.h>
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 <o3tl/safeint.hxx>
35 #include <o3tl/string_view.hxx>
36 #include <osl/thread.h>
37 #include <osl/process.h>
39 #include <vcl/svapp.hxx>
40 #include <sal/log.hxx>
44 using namespace vcl_sal
;
46 /***************************************************************
47 * class GtkSalDisplay *
48 ***************************************************************/
50 GtkSalDisplay::GtkSalDisplay( GdkDisplay
* pDisplay
) :
51 m_pSys( GtkSalSystem::GetSingleton() ),
52 m_pGdkDisplay( pDisplay
),
53 m_bStartupCompleted( false )
55 for(GdkCursor
* & rpCsr
: m_aCursors
)
58 if ( getenv( "SAL_IGNOREXERRORS" ) )
59 GetGenericUnixSalData()->ErrorTrapPush(); // and leak the trap
61 gtk_widget_set_default_direction(AllSettings::GetLayoutRTL() ? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
);
64 GtkSalDisplay::~GtkSalDisplay()
66 #if !GTK_CHECK_VERSION(4, 0, 0)
67 if( !m_bStartupCompleted
)
68 gdk_notify_startup_complete();
70 for(GdkCursor
* & rpCsr
: m_aCursors
)
72 gdk_cursor_unref( rpCsr
);
76 #if GTK_CHECK_VERSION(4, 0, 0)
78 static void signalMonitorsChanged(GListModel
*, gpointer data
)
80 GtkSalDisplay
* pDisp
= static_cast<GtkSalDisplay
*>(data
);
81 pDisp
->emitDisplayChanged();
86 static void signalScreenSizeChanged( GdkScreen
* pScreen
, gpointer data
)
88 GtkSalDisplay
* pDisp
= static_cast<GtkSalDisplay
*>(data
);
89 pDisp
->screenSizeChanged( pScreen
);
92 static void signalMonitorsChanged( GdkScreen
* pScreen
, gpointer data
)
94 GtkSalDisplay
* pDisp
= static_cast<GtkSalDisplay
*>(data
);
95 pDisp
->monitorsChanged( pScreen
);
98 void GtkSalDisplay::screenSizeChanged( GdkScreen
const * pScreen
)
100 m_pSys
->countScreenMonitors();
102 emitDisplayChanged();
105 void GtkSalDisplay::monitorsChanged( GdkScreen
const * pScreen
)
107 m_pSys
->countScreenMonitors();
109 emitDisplayChanged();
113 GdkCursor
* GtkSalDisplay::getFromSvg(OUString
const & name
, int nXHot
, int nYHot
)
115 GdkPixbuf
* pPixBuf
= load_icon_by_name(name
);
116 assert(pPixBuf
&& "missing image?");
120 #if !GTK_CHECK_VERSION(4, 0, 0)
121 guint nDefaultCursorSize
= gdk_display_get_default_cursor_size( m_pGdkDisplay
);
122 int nPixWidth
= gdk_pixbuf_get_width(pPixBuf
);
123 int nPixHeight
= gdk_pixbuf_get_height(pPixBuf
);
124 double fScalefactor
= static_cast<double>(nDefaultCursorSize
) / std::max(nPixWidth
, nPixHeight
);
125 GdkPixbuf
* pScaledPixBuf
= gdk_pixbuf_scale_simple(pPixBuf
,
126 nPixWidth
* fScalefactor
,
127 nPixHeight
* fScalefactor
,
129 g_object_unref(pPixBuf
);
130 return gdk_cursor_new_from_pixbuf(m_pGdkDisplay
, pScaledPixBuf
,
131 nXHot
* fScalefactor
, nYHot
* fScalefactor
);
133 GdkTexture
* pTexture
= gdk_texture_new_for_pixbuf(pPixBuf
);
134 g_object_unref(pPixBuf
);
135 return gdk_cursor_new_from_texture(pTexture
, nXHot
, nYHot
, nullptr);
139 #define MAKE_CURSOR( vcl_name, name, name2 ) \
141 pCursor = getFromSvg(name2, name##curs_x_hot, name##curs_y_hot); \
144 #if !GTK_CHECK_VERSION(4, 0, 0)
145 #define MAP_BUILTIN( vcl_name, gdk3_name, css_name ) \
147 pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk3_name ); \
150 #define MAP_BUILTIN( vcl_name, gdk3_name, css_name ) \
152 pCursor = gdk_cursor_new_from_name(css_name, nullptr); \
156 GdkCursor
*GtkSalDisplay::getCursor( PointerStyle ePointerStyle
)
158 if ( !m_aCursors
[ ePointerStyle
] )
160 GdkCursor
*pCursor
= nullptr;
162 switch( ePointerStyle
)
164 MAP_BUILTIN( PointerStyle::Arrow
, GDK_LEFT_PTR
, "default" );
165 MAP_BUILTIN( PointerStyle::Text
, GDK_XTERM
, "text" );
166 MAP_BUILTIN( PointerStyle::Help
, GDK_QUESTION_ARROW
, "help" );
167 MAP_BUILTIN( PointerStyle::Cross
, GDK_CROSSHAIR
, "crosshair" );
168 MAP_BUILTIN( PointerStyle::Wait
, GDK_WATCH
, "wait" );
170 MAP_BUILTIN( PointerStyle::NSize
, GDK_SB_V_DOUBLE_ARROW
, "n-resize" );
171 MAP_BUILTIN( PointerStyle::SSize
, GDK_SB_V_DOUBLE_ARROW
, "s-resize" );
172 MAP_BUILTIN( PointerStyle::WSize
, GDK_SB_H_DOUBLE_ARROW
, "w-resize" );
173 MAP_BUILTIN( PointerStyle::ESize
, GDK_SB_H_DOUBLE_ARROW
, "e-resize" );
175 MAP_BUILTIN( PointerStyle::NWSize
, GDK_TOP_LEFT_CORNER
, "nw-resize" );
176 MAP_BUILTIN( PointerStyle::NESize
, GDK_TOP_RIGHT_CORNER
, "ne-resize" );
177 MAP_BUILTIN( PointerStyle::SWSize
, GDK_BOTTOM_LEFT_CORNER
, "sw-resize" );
178 MAP_BUILTIN( PointerStyle::SESize
, GDK_BOTTOM_RIGHT_CORNER
, "se-resize" );
180 MAP_BUILTIN( PointerStyle::WindowNSize
, GDK_TOP_SIDE
, "n-resize" );
181 MAP_BUILTIN( PointerStyle::WindowSSize
, GDK_BOTTOM_SIDE
, "s-resize" );
182 MAP_BUILTIN( PointerStyle::WindowWSize
, GDK_LEFT_SIDE
, "w-resize" );
183 MAP_BUILTIN( PointerStyle::WindowESize
, GDK_RIGHT_SIDE
, "e-resize" );
185 MAP_BUILTIN( PointerStyle::WindowNWSize
, GDK_TOP_LEFT_CORNER
, "nw-resize" );
186 MAP_BUILTIN( PointerStyle::WindowNESize
, GDK_TOP_RIGHT_CORNER
, "ne-resize" );
187 MAP_BUILTIN( PointerStyle::WindowSWSize
, GDK_BOTTOM_LEFT_CORNER
, "sw-resize" );
188 MAP_BUILTIN( PointerStyle::WindowSESize
, GDK_BOTTOM_RIGHT_CORNER
, "se-resize" );
190 MAP_BUILTIN( PointerStyle::HSizeBar
, GDK_SB_H_DOUBLE_ARROW
, "col-resize" );
191 MAP_BUILTIN( PointerStyle::VSizeBar
, GDK_SB_V_DOUBLE_ARROW
, "row-resize" );
193 MAP_BUILTIN( PointerStyle::RefHand
, GDK_HAND2
, "grab" );
194 MAP_BUILTIN( PointerStyle::Hand
, GDK_HAND2
, "grab" );
196 #if !GTK_CHECK_VERSION(4, 0, 0)
197 MAP_BUILTIN( PointerStyle::Pen
, GDK_PENCIL
, "" );
199 MAKE_CURSOR( PointerStyle::Pen
, pen_
, RID_CURSOR_PEN
);
202 MAP_BUILTIN( PointerStyle::HSplit
, GDK_SB_H_DOUBLE_ARROW
, "col-resize" );
203 MAP_BUILTIN( PointerStyle::VSplit
, GDK_SB_V_DOUBLE_ARROW
, "row-resize" );
205 MAP_BUILTIN( PointerStyle::Move
, GDK_FLEUR
, "move" );
207 #if !GTK_CHECK_VERSION(4, 0, 0)
208 MAKE_CURSOR( PointerStyle::Null
, null
, RID_CURSOR_NULL
);
210 MAP_BUILTIN( PointerStyle::Null
, 0, "none" );
213 MAKE_CURSOR( PointerStyle::Magnify
, magnify_
, RID_CURSOR_MAGNIFY
);
214 MAKE_CURSOR( PointerStyle::Fill
, fill_
, RID_CURSOR_FILL
);
215 MAKE_CURSOR( PointerStyle::MoveData
, movedata_
, RID_CURSOR_MOVE_DATA
);
216 MAKE_CURSOR( PointerStyle::CopyData
, copydata_
, RID_CURSOR_COPY_DATA
);
217 MAKE_CURSOR( PointerStyle::MoveFile
, movefile_
, RID_CURSOR_MOVE_FILE
);
218 MAKE_CURSOR( PointerStyle::CopyFile
, copyfile_
, RID_CURSOR_COPY_FILE
);
219 MAKE_CURSOR( PointerStyle::MoveFiles
, movefiles_
, RID_CURSOR_MOVE_FILES
);
220 MAKE_CURSOR( PointerStyle::CopyFiles
, copyfiles_
, RID_CURSOR_COPY_FILES
);
222 #if !GTK_CHECK_VERSION(4, 0, 0)
223 MAKE_CURSOR( PointerStyle::NotAllowed
, nodrop_
, RID_CURSOR_NOT_ALLOWED
);
225 MAP_BUILTIN( PointerStyle::NotAllowed
, 0, "not-allowed" );
228 MAKE_CURSOR( PointerStyle::Rotate
, rotate_
, RID_CURSOR_ROTATE
);
229 MAKE_CURSOR( PointerStyle::HShear
, hshear_
, RID_CURSOR_H_SHEAR
);
230 MAKE_CURSOR( PointerStyle::VShear
, vshear_
, RID_CURSOR_V_SHEAR
);
231 MAKE_CURSOR( PointerStyle::DrawLine
, drawline_
, RID_CURSOR_DRAW_LINE
);
232 MAKE_CURSOR( PointerStyle::DrawRect
, drawrect_
, RID_CURSOR_DRAW_RECT
);
233 MAKE_CURSOR( PointerStyle::DrawPolygon
, drawpolygon_
, RID_CURSOR_DRAW_POLYGON
);
234 MAKE_CURSOR( PointerStyle::DrawBezier
, drawbezier_
, RID_CURSOR_DRAW_BEZIER
);
235 MAKE_CURSOR( PointerStyle::DrawArc
, drawarc_
, RID_CURSOR_DRAW_ARC
);
236 MAKE_CURSOR( PointerStyle::DrawPie
, drawpie_
, RID_CURSOR_DRAW_PIE
);
237 MAKE_CURSOR( PointerStyle::DrawCircleCut
, drawcirclecut_
, RID_CURSOR_DRAW_CIRCLE_CUT
);
238 MAKE_CURSOR( PointerStyle::DrawEllipse
, drawellipse_
, RID_CURSOR_DRAW_ELLIPSE
);
239 MAKE_CURSOR( PointerStyle::DrawConnect
, drawconnect_
, RID_CURSOR_DRAW_CONNECT
);
240 MAKE_CURSOR( PointerStyle::DrawText
, drawtext_
, RID_CURSOR_DRAW_TEXT
);
241 MAKE_CURSOR( PointerStyle::Mirror
, mirror_
, RID_CURSOR_MIRROR
);
242 MAKE_CURSOR( PointerStyle::Crook
, crook_
, RID_CURSOR_CROOK
);
243 MAKE_CURSOR( PointerStyle::Crop
, crop_
, RID_CURSOR_CROP
);
244 MAKE_CURSOR( PointerStyle::MovePoint
, movepoint_
, RID_CURSOR_MOVE_POINT
);
245 MAKE_CURSOR( PointerStyle::MoveBezierWeight
, movebezierweight_
, RID_CURSOR_MOVE_BEZIER_WEIGHT
);
246 MAKE_CURSOR( PointerStyle::DrawFreehand
, drawfreehand_
, RID_CURSOR_DRAW_FREEHAND
);
247 MAKE_CURSOR( PointerStyle::DrawCaption
, drawcaption_
, RID_CURSOR_DRAW_CAPTION
);
248 MAKE_CURSOR( PointerStyle::LinkData
, linkdata_
, RID_CURSOR_LINK_DATA
);
249 MAKE_CURSOR( PointerStyle::MoveDataLink
, movedlnk_
, RID_CURSOR_MOVE_DATA_LINK
);
250 MAKE_CURSOR( PointerStyle::CopyDataLink
, copydlnk_
, RID_CURSOR_COPY_DATA_LINK
);
251 MAKE_CURSOR( PointerStyle::LinkFile
, linkfile_
, RID_CURSOR_LINK_FILE
);
252 MAKE_CURSOR( PointerStyle::MoveFileLink
, moveflnk_
, RID_CURSOR_MOVE_FILE_LINK
);
253 MAKE_CURSOR( PointerStyle::CopyFileLink
, copyflnk_
, RID_CURSOR_COPY_FILE_LINK
);
254 MAKE_CURSOR( PointerStyle::Chart
, chart_
, RID_CURSOR_CHART
);
255 MAKE_CURSOR( PointerStyle::Detective
, detective_
, RID_CURSOR_DETECTIVE
);
256 MAKE_CURSOR( PointerStyle::PivotCol
, pivotcol_
, RID_CURSOR_PIVOT_COLUMN
);
257 MAKE_CURSOR( PointerStyle::PivotRow
, pivotrow_
, RID_CURSOR_PIVOT_ROW
);
258 MAKE_CURSOR( PointerStyle::PivotField
, pivotfld_
, RID_CURSOR_PIVOT_FIELD
);
259 MAKE_CURSOR( PointerStyle::PivotDelete
, pivotdel_
, RID_CURSOR_PIVOT_DELETE
);
260 MAKE_CURSOR( PointerStyle::Chain
, chain_
, RID_CURSOR_CHAIN
);
261 MAKE_CURSOR( PointerStyle::ChainNotAllowed
, chainnot_
, RID_CURSOR_CHAIN_NOT_ALLOWED
);
262 MAKE_CURSOR( PointerStyle::AutoScrollN
, asn_
, RID_CURSOR_AUTOSCROLL_N
);
263 MAKE_CURSOR( PointerStyle::AutoScrollS
, ass_
, RID_CURSOR_AUTOSCROLL_S
);
264 MAKE_CURSOR( PointerStyle::AutoScrollW
, asw_
, RID_CURSOR_AUTOSCROLL_W
);
265 MAKE_CURSOR( PointerStyle::AutoScrollE
, ase_
, RID_CURSOR_AUTOSCROLL_E
);
266 MAKE_CURSOR( PointerStyle::AutoScrollNW
, asnw_
, RID_CURSOR_AUTOSCROLL_NW
);
267 MAKE_CURSOR( PointerStyle::AutoScrollNE
, asne_
, RID_CURSOR_AUTOSCROLL_NE
);
268 MAKE_CURSOR( PointerStyle::AutoScrollSW
, assw_
, RID_CURSOR_AUTOSCROLL_SW
);
269 MAKE_CURSOR( PointerStyle::AutoScrollSE
, asse_
, RID_CURSOR_AUTOSCROLL_SE
);
270 MAKE_CURSOR( PointerStyle::AutoScrollNS
, asns_
, RID_CURSOR_AUTOSCROLL_NS
);
271 MAKE_CURSOR( PointerStyle::AutoScrollWE
, aswe_
, RID_CURSOR_AUTOSCROLL_WE
);
272 MAKE_CURSOR( PointerStyle::AutoScrollNSWE
, asnswe_
, RID_CURSOR_AUTOSCROLL_NSWE
);
273 MAKE_CURSOR( PointerStyle::TextVertical
, vertcurs_
, RID_CURSOR_TEXT_VERTICAL
);
276 MAKE_CURSOR( PointerStyle::TabSelectS
, tblsels_
, RID_CURSOR_TAB_SELECT_S
);
277 MAKE_CURSOR( PointerStyle::TabSelectE
, tblsele_
, RID_CURSOR_TAB_SELECT_E
);
278 MAKE_CURSOR( PointerStyle::TabSelectSE
, tblselse_
, RID_CURSOR_TAB_SELECT_SE
);
279 MAKE_CURSOR( PointerStyle::TabSelectW
, tblselw_
, RID_CURSOR_TAB_SELECT_W
);
280 MAKE_CURSOR( PointerStyle::TabSelectSW
, tblselsw_
, RID_CURSOR_TAB_SELECT_SW
);
282 MAKE_CURSOR( PointerStyle::HideWhitespace
, hidewhitespace_
, RID_CURSOR_HIDE_WHITESPACE
);
283 MAKE_CURSOR( PointerStyle::ShowWhitespace
, showwhitespace_
, RID_CURSOR_SHOW_WHITESPACE
);
284 MAKE_CURSOR( PointerStyle::FatCross
, fatcross_
, RID_CURSOR_FATCROSS
);
287 SAL_WARN( "vcl.gtk", "pointer " << static_cast<int>(ePointerStyle
) << "not implemented" );
292 #if !GTK_CHECK_VERSION(4, 0, 0)
293 pCursor
= gdk_cursor_new_for_display( m_pGdkDisplay
, GDK_LEFT_PTR
);
295 pCursor
= gdk_cursor_new_from_name("normal", nullptr);
299 m_aCursors
[ ePointerStyle
] = pCursor
;
302 return m_aCursors
[ ePointerStyle
];
305 int GtkSalDisplay::CaptureMouse( SalFrame
* pSFrame
)
307 GtkSalFrame
* pFrame
= static_cast<GtkSalFrame
*>(pSFrame
);
312 static_cast<GtkSalFrame
*>(m_pCapture
)->grabPointer( false, false, false );
313 m_pCapture
= nullptr;
319 if( pFrame
== m_pCapture
)
321 static_cast<GtkSalFrame
*>(m_pCapture
)->grabPointer( false, false, false );
325 pFrame
->grabPointer( true, false, false );
329 /**********************************************************************
331 **********************************************************************/
333 GtkSalData::GtkSalData()
334 : GenericUnixSalData()
336 m_pUserEvent
= nullptr;
339 #if defined(GDK_WINDOWING_X11)
340 static XIOErrorHandler aOrigXIOErrorHandler
= nullptr;
344 static int XIOErrorHdl(Display
*)
346 fprintf(stderr
, "X IO Error\n");
348 // avoid crashes in unrelated threads that still run while atexit
349 // handlers are in progress
355 GtkSalData::~GtkSalData()
357 // sanity check: at this point nobody should be yielding, but wake them
358 // up anyway before the condition they're waiting on gets destroyed.
359 m_aDispatchCondition
.set();
361 osl::MutexGuard
g( m_aDispatchMutex
);
364 g_source_destroy (m_pUserEvent
);
365 g_source_unref (m_pUserEvent
);
366 m_pUserEvent
= nullptr;
368 #if defined(GDK_WINDOWING_X11)
369 if (DLSYM_GDK_IS_X11_DISPLAY(gdk_display_get_default()))
370 XSetIOErrorHandler(aOrigXIOErrorHandler
);
374 void GtkSalData::Dispose()
379 /// Allows events to be processed, returns true if we processed an event.
380 bool GtkSalData::Yield( bool bWait
, bool bHandleAllCurrentEvents
)
382 /* #i33212# only enter g_main_context_iteration in one thread at any one
383 * time, else one of them potentially will never end as long as there is
384 * another thread in there. Having only one yielding thread actually dispatch
385 * fits the vcl event model (see e.g. the generic plugin).
387 bool bDispatchThread
= false;
388 bool bWasEvent
= false;
390 // release YieldMutex (and re-acquire at block end)
391 SolarMutexReleaser aReleaser
;
392 if( m_aDispatchMutex
.tryToAcquire() )
393 bDispatchThread
= true;
396 return false; // someone else is waiting already, return
399 if( bDispatchThread
)
401 int nMaxEvents
= bHandleAllCurrentEvents
? 100 : 1;
402 bool wasOneEvent
= true;
403 while( nMaxEvents
-- && wasOneEvent
)
405 wasOneEvent
= g_main_context_iteration( nullptr, bWait
&& !bWasEvent
);
410 std::rethrow_exception(m_aException
);
414 /* #i41693# in case the dispatch thread hangs in join
415 * for this thread the condition will never be set
416 * workaround: timeout of 1 second an emergency exit
418 // we are the dispatch thread
419 m_aDispatchCondition
.reset();
420 m_aDispatchCondition
.wait(std::chrono::seconds(1));
424 if( bDispatchThread
)
426 m_aDispatchMutex
.release();
428 m_aDispatchCondition
.set(); // trigger non dispatch thread yields
434 static GtkStyleProvider
* CreateStyleProvider()
439 1) allow certain widgets to have no padding
441 1.a) little close button in menubar to close back to start-center
442 1.b) and small buttons in view->data sources (button.small-button)
443 1.c.1) gtk3 small toolbar button in infobars (toolbar.small-button button)
444 1.c.2) gtk4 small toolbar button in infobars (box.small-button button)
445 1.d) comboboxes in the data browser for tdf#137695 (box#combobox button.small-button,
446 which would instead be combobox button.small-button if we didn't replace GtkComboBox,
447 see GtkInstanceComboBox for an explanation for why we do that)
448 1.e) entry in the data browser for tdf#137695 (entry.small-button)
449 1.f) spinbutton in the data browser tdf#141633 (spinbutton.small-button)
451 2) hide the unwanted active tab in an 'overflow' notebook of double-decker notebooks.
452 (tdf#122623) it's nigh impossible to have a GtkNotebook without an active (checked) tab,
453 so theme the unwanted tab into invisibility
455 GtkCssProvider
* pStyleProvider
= gtk_css_provider_new();
456 static const gchar data
[] =
457 "button.small-button, toolbar.small-button button, box.small-button button, "
458 "combobox.small-button *.combo, box#combobox.small-button *.combo, entry.small-button, "
459 "spinbutton.small-button, spinbutton.small-button entry, spinbutton.small-button button { "
460 "padding: 0; margin-left: 0; margin-right: 0; margin-top: 0; margin-bottom: 0;"
461 "border-width: 0; min-height: 0; min-width: 0; }"
462 #if GTK_CHECK_VERSION(4, 0, 0)
463 // we basically assumed during dialog design that the frame's were invisible, because
464 // they used to be in the default theme during gtk3
465 "frame { border-style: none; }"
467 "notebook.overflow > header.top > tabs > tab:checked { "
468 "box-shadow: none; padding: 0 0 0 0; margin: 0 0 0 0;"
469 "border-image: none; border-image-width: 0 0 0 0;"
470 "background-image: none; background-color: transparent;"
471 "border-radius: 0 0 0 0; border-width: 0 0 0 0;"
472 "border-style: none; border-color: transparent;"
473 "opacity: 0; min-height: 0; min-width: 0; }"
474 // https://css-tricks.com/restart-css-animation/
475 // This animation appears twice with two different names so we can change
476 // the class from "call_attention_1" to "call_attention_2" to restart the
478 "@keyframes shinkandrestore1 { 50% { margin-left: 15px; margin-right: 15px; opacity: 0.5; } }"
479 "@keyframes shinkandrestore2 { 50% { margin-left: 15px; margin-right: 15px; opacity: 0.5; } }"
480 " *.call_attention_1 {"
481 "animation-name: shinkandrestore1; animation-duration: 1s; "
482 "animation-timing-function: linear; animation-iteration-count: 2; }"
483 " *.call_attention_2 {"
484 "animation-name: shinkandrestore2; animation-duration: 1s; "
485 "animation-timing-function: linear; animation-iteration-count: 2; }";
486 css_provider_load_from_data(pStyleProvider
, data
, -1);
487 return GTK_STYLE_PROVIDER(pStyleProvider
);
490 void GtkSalData::Init()
492 SAL_INFO( "vcl.gtk", "GtkMainloop::Init()" );
495 * open connection to X11 Display
497 * o -display command line parameter,
498 * o $DISPLAY environment variable
502 GdkDisplay
*pGdkDisp
= nullptr;
504 // is there a -display command line parameter?
505 rtl_TextEncoding aEnc
= osl_getThreadTextEncoding();
506 int nParams
= osl_getCommandArgCount();
508 OUString aParam
, aBin
;
509 char** pCmdLineAry
= new char*[ nParams
+1 ];
510 osl_getExecutableFile( &aParam
.pData
);
511 osl_getSystemPathFromFileURL( aParam
.pData
, &aBin
.pData
);
512 pCmdLineAry
[0] = g_strdup( OUStringToOString( aBin
, aEnc
).getStr() );
513 for (int i
= 0; i
< nParams
; ++i
)
515 osl_getCommandArg(i
, &aParam
.pData
);
516 OString
aBParam( OUStringToOString( aParam
, aEnc
) );
518 if( aParam
== "-display" || aParam
== "--display" )
520 pCmdLineAry
[i
+1] = g_strdup( "--display" );
521 osl_getCommandArg(i
+1, &aParam
.pData
);
522 aDisplay
= OUStringToOString( aParam
, aEnc
);
525 pCmdLineAry
[i
+1] = g_strdup( aBParam
.getStr() );
530 g_set_application_name(SalGenericSystem::getFrameClassName());
532 // Set consistent name of the root accessible
533 OUString aAppName
= Application::GetAppName();
534 if( !aAppName
.isEmpty() )
536 OString aPrgName
= OUStringToOString(aAppName
, aEnc
);
537 g_set_prgname(aPrgName
.getStr());
541 #if GTK_CHECK_VERSION(4, 0, 0)
544 gtk_init_check( &nParams
, &pCmdLineAry
);
547 for (int i
= 0; i
< nParams
; ++i
)
548 g_free( pCmdLineAry
[i
] );
549 delete [] pCmdLineAry
;
551 #if OSL_DEBUG_LEVEL > 1
552 if (g_getenv("SAL_DEBUG_UPDATES"))
553 gdk_window_set_debug_updates (TRUE
);
556 pGdkDisp
= gdk_display_get_default();
559 OUString aProgramFileURL
;
560 osl_getExecutableFile( &aProgramFileURL
.pData
);
561 OUString aProgramSystemPath
;
562 osl_getSystemPathFromFileURL (aProgramFileURL
.pData
, &aProgramSystemPath
.pData
);
563 OString aProgramName
= OUStringToOString(
565 osl_getThreadTextEncoding() );
566 fprintf( stderr
, "%s X11 error: Can't open display: %s\n",
567 aProgramName
.getStr(), aDisplay
.getStr());
568 fprintf( stderr
, " Set DISPLAY environment variable, use -display option\n");
569 fprintf( stderr
, " or check permissions of your X-Server\n");
570 fprintf( stderr
, " (See \"man X\" resp. \"man xhost\" for details)\n");
577 #if defined(GDK_WINDOWING_X11)
578 if (DLSYM_GDK_IS_X11_DISPLAY(pGdkDisp
))
579 aOrigXIOErrorHandler
= XSetIOErrorHandler(XIOErrorHdl
);
582 GtkSalDisplay
*pDisplay
= new GtkSalDisplay( pGdkDisp
);
583 SetDisplay( pDisplay
);
585 #if GTK_CHECK_VERSION(4, 0, 0)
586 pDisplay
->emitDisplayChanged();
587 GListModel
*pMonitors
= gdk_display_get_monitors(pGdkDisp
);
588 g_signal_connect(pMonitors
, "items-changed", G_CALLBACK(signalMonitorsChanged
), pDisplay
);
590 gtk_style_context_add_provider_for_display(pGdkDisp
, CreateStyleProvider(),
591 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
593 int nScreens
= gdk_display_get_n_screens( pGdkDisp
);
594 for( int n
= 0; n
< nScreens
; n
++ )
596 GdkScreen
*pScreen
= gdk_display_get_screen( pGdkDisp
, n
);
600 pDisplay
->screenSizeChanged( pScreen
);
601 pDisplay
->monitorsChanged( pScreen
);
602 // add signal handler to notify screen size changes
603 g_signal_connect( G_OBJECT(pScreen
), "size-changed",
604 G_CALLBACK(signalScreenSizeChanged
), pDisplay
);
605 g_signal_connect( G_OBJECT(pScreen
), "monitors-changed",
606 G_CALLBACK(signalMonitorsChanged
), pDisplay
);
608 gtk_style_context_add_provider_for_screen(pScreen
, CreateStyleProvider(),
609 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
614 void GtkSalData::ErrorTrapPush()
616 #if GTK_CHECK_VERSION(4, 0, 0)
617 # if defined(GDK_WINDOWING_X11)
618 GdkDisplay
* pGdkDisp
= gdk_display_get_default();
619 if (DLSYM_GDK_IS_X11_DISPLAY(pGdkDisp
))
620 gdk_x11_display_error_trap_push(pGdkDisp
);
623 gdk_error_trap_push();
627 bool GtkSalData::ErrorTrapPop( bool bIgnoreError
)
629 #if GTK_CHECK_VERSION(4, 0, 0)
630 # if defined(GDK_WINDOWING_X11)
631 GdkDisplay
* pGdkDisp
= gdk_display_get_default();
632 if (DLSYM_GDK_IS_X11_DISPLAY(pGdkDisp
))
636 gdk_x11_display_error_trap_pop_ignored(pGdkDisp
); // faster
639 return gdk_x11_display_error_trap_pop(pGdkDisp
) != 0;
646 gdk_error_trap_pop_ignored (); // faster
649 return gdk_error_trap_pop () != 0;
653 #if !GLIB_CHECK_VERSION(2,32,0)
654 #define G_SOURCE_REMOVE FALSE
659 struct SalGtkTimeoutSource
{
662 GtkSalTimer
*pInstance
;
665 static void sal_gtk_timeout_defer( SalGtkTimeoutSource
*pTSource
)
667 g_get_current_time( &pTSource
->aFireTime
);
668 g_time_val_add( &pTSource
->aFireTime
, pTSource
->pInstance
->m_nTimeoutMS
* 1000 );
671 static gboolean
sal_gtk_timeout_expired( SalGtkTimeoutSource
*pTSource
,
672 gint
*nTimeoutMS
, GTimeVal
const *pTimeNow
)
674 glong nDeltaSec
= pTSource
->aFireTime
.tv_sec
- pTimeNow
->tv_sec
;
675 glong nDeltaUSec
= pTSource
->aFireTime
.tv_usec
- pTimeNow
->tv_usec
;
676 if( nDeltaSec
< 0 || ( nDeltaSec
== 0 && nDeltaUSec
< 0) )
683 nDeltaUSec
+= 1000000;
686 // if the clock changes backwards we need to cope ...
687 if( o3tl::make_unsigned(nDeltaSec
) > 1 + ( pTSource
->pInstance
->m_nTimeoutMS
/ 1000 ) )
689 sal_gtk_timeout_defer( pTSource
);
693 *nTimeoutMS
= MIN( G_MAXINT
, ( nDeltaSec
* 1000 + (nDeltaUSec
+ 999) / 1000 ) );
695 return *nTimeoutMS
== 0;
698 static gboolean
sal_gtk_timeout_prepare( GSource
*pSource
, gint
*nTimeoutMS
)
700 SalGtkTimeoutSource
*pTSource
= reinterpret_cast<SalGtkTimeoutSource
*>(pSource
);
703 g_get_current_time( &aTimeNow
);
705 return sal_gtk_timeout_expired( pTSource
, nTimeoutMS
, &aTimeNow
);
708 static gboolean
sal_gtk_timeout_check( GSource
*pSource
)
710 SalGtkTimeoutSource
*pTSource
= reinterpret_cast<SalGtkTimeoutSource
*>(pSource
);
713 g_get_current_time( &aTimeNow
);
715 return ( pTSource
->aFireTime
.tv_sec
< aTimeNow
.tv_sec
||
716 ( pTSource
->aFireTime
.tv_sec
== aTimeNow
.tv_sec
&&
717 pTSource
->aFireTime
.tv_usec
< aTimeNow
.tv_usec
) );
720 static gboolean
sal_gtk_timeout_dispatch( GSource
*pSource
, GSourceFunc
, gpointer
)
722 SalGtkTimeoutSource
*pTSource
= reinterpret_cast<SalGtkTimeoutSource
*>(pSource
);
724 if( !pTSource
->pInstance
)
727 SolarMutexGuard aGuard
;
729 sal_gtk_timeout_defer( pTSource
);
731 ImplSVData
* pSVData
= ImplGetSVData();
732 if( pSVData
->maSchedCtx
.mpSalTimer
)
733 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
735 return G_SOURCE_REMOVE
;
738 static GSourceFuncs sal_gtk_timeout_funcs
=
740 sal_gtk_timeout_prepare
,
741 sal_gtk_timeout_check
,
742 sal_gtk_timeout_dispatch
,
743 nullptr, nullptr, nullptr
747 static SalGtkTimeoutSource
*
748 create_sal_gtk_timeout( GtkSalTimer
*pTimer
)
750 GSource
*pSource
= g_source_new( &sal_gtk_timeout_funcs
, sizeof( SalGtkTimeoutSource
) );
751 SalGtkTimeoutSource
*pTSource
= reinterpret_cast<SalGtkTimeoutSource
*>(pSource
);
752 pTSource
->pInstance
= pTimer
;
754 // #i36226# timers should be executed with lower priority
755 // than XEvents like in generic plugin
756 g_source_set_priority( pSource
, G_PRIORITY_LOW
);
757 g_source_set_can_recurse( pSource
, true );
758 g_source_set_callback( pSource
,
759 /* unused dummy */ g_idle_remove_by_data
,
761 g_source_attach( pSource
, g_main_context_default() );
763 g_source_set_name( pSource
, "VCL timeout source" );
766 sal_gtk_timeout_defer( pTSource
);
771 GtkSalTimer::GtkSalTimer()
772 : m_pTimeout(nullptr)
777 GtkSalTimer::~GtkSalTimer()
779 GetGtkInstance()->RemoveTimer();
783 bool GtkSalTimer::Expired()
785 if( !m_pTimeout
|| g_source_is_destroyed( &m_pTimeout
->aParent
) )
790 g_get_current_time( &aTimeNow
);
791 return !!sal_gtk_timeout_expired( m_pTimeout
, &nDummy
, &aTimeNow
);
794 void GtkSalTimer::Start( sal_uInt64 nMS
)
796 // glib is not 64bit safe in this regard.
797 assert( nMS
<= G_MAXINT
);
798 if ( nMS
> G_MAXINT
)
800 m_nTimeoutMS
= nMS
; // for restarting
801 Stop(); // FIXME: ideally re-use an existing m_pTimeout
802 m_pTimeout
= create_sal_gtk_timeout( this );
805 void GtkSalTimer::Stop()
809 g_source_destroy( &m_pTimeout
->aParent
);
810 g_source_unref( &m_pTimeout
->aParent
);
811 m_pTimeout
= nullptr;
816 static gboolean
call_userEventFn( void *data
)
818 SolarMutexGuard aGuard
;
819 const SalGenericDisplay
*pDisplay
= GetGenericUnixSalData()->GetDisplay();
822 GtkSalDisplay
*pThisDisplay
= static_cast<GtkSalData
*>(data
)->GetGtkDisplay();
823 assert(static_cast<const SalGenericDisplay
*>(pThisDisplay
) == pDisplay
);
824 pThisDisplay
->DispatchInternalEvent();
830 void GtkSalData::TriggerUserEventProcessing()
833 g_main_context_wakeup (nullptr); // really needed ?
834 else // nothing pending anyway
836 m_pUserEvent
= g_idle_source_new();
837 // tdf#110737 set user-events to a lower priority than system redraw
838 // events, which is G_PRIORITY_HIGH_IDLE + 20, so presentations
839 // queue-redraw has a chance to be fulfilled
840 g_source_set_priority (m_pUserEvent
, G_PRIORITY_HIGH_IDLE
+ 30);
841 g_source_set_can_recurse (m_pUserEvent
, true);
842 g_source_set_callback (m_pUserEvent
, call_userEventFn
,
843 static_cast<gpointer
>(this), nullptr);
844 g_source_attach (m_pUserEvent
, g_main_context_default ());
848 void GtkSalData::TriggerAllUserEventsProcessed()
850 assert( m_pUserEvent
);
851 g_source_destroy( m_pUserEvent
);
852 g_source_unref( m_pUserEvent
);
853 m_pUserEvent
= nullptr;
856 void GtkSalDisplay::TriggerUserEventProcessing()
858 GetGtkSalData()->TriggerUserEventProcessing();
861 void GtkSalDisplay::TriggerAllUserEventsProcessed()
863 GetGtkSalData()->TriggerAllUserEventsProcessed();
866 GtkWidget
* GtkSalDisplay::findGtkWidgetForNativeHandle(sal_uIntPtr hWindow
) const
868 for (auto pSalFrame
: m_aFrames
)
870 const SystemEnvData
* pEnvData
= pSalFrame
->GetSystemData();
871 if (pEnvData
->GetWindowHandle(pSalFrame
) == hWindow
)
872 return GTK_WIDGET(pEnvData
->pWidget
);
877 void GtkSalDisplay::deregisterFrame( SalFrame
* pFrame
)
879 if( m_pCapture
== pFrame
)
881 static_cast<GtkSalFrame
*>(m_pCapture
)->grabPointer( false, false, false );
882 m_pCapture
= nullptr;
884 SalGenericDisplay::deregisterFrame( pFrame
);
891 std::u16string_view m_aType
;
897 int getButtonPriority(std::u16string_view rType
)
899 static const size_t N_TYPES
= 8;
900 static const ButtonOrder aDiscardCancelSave
[N_TYPES
] =
912 static const ButtonOrder aSaveDiscardCancel
[N_TYPES
] =
924 const ButtonOrder
* pOrder
= &aDiscardCancelSave
[0];
926 const OUString
&rEnv
= Application::GetDesktopEnvironment();
928 if (rEnv
.equalsIgnoreAsciiCase("windows") ||
929 rEnv
.equalsIgnoreAsciiCase("tde") ||
930 rEnv
.startsWithIgnoreAsciiCase("kde"))
932 pOrder
= &aSaveDiscardCancel
[0];
935 for (size_t i
= 0; i
< N_TYPES
; ++i
, ++pOrder
)
937 if (rType
== pOrder
->m_aType
)
938 return pOrder
->m_nPriority
;
944 void container_remove(GtkWidget
* pContainer
, GtkWidget
* pChild
)
946 #if !GTK_CHECK_VERSION(4, 0, 0)
947 gtk_container_remove(GTK_CONTAINER(pContainer
), pChild
);
949 assert(GTK_IS_BOX(pContainer
) || GTK_IS_GRID(pContainer
) || GTK_IS_POPOVER(pContainer
) ||
950 GTK_IS_FIXED(pContainer
) || GTK_IS_WINDOW(pContainer
));
951 if (GTK_IS_BOX(pContainer
))
952 gtk_box_remove(GTK_BOX(pContainer
), pChild
);
953 else if (GTK_IS_GRID(pContainer
))
954 gtk_grid_remove(GTK_GRID(pContainer
), pChild
);
955 else if (GTK_IS_POPOVER(pContainer
))
956 gtk_popover_set_child(GTK_POPOVER(pContainer
), nullptr);
957 else if (GTK_IS_WINDOW(pContainer
))
958 gtk_window_set_child(GTK_WINDOW(pContainer
), nullptr);
959 else if (GTK_IS_FIXED(pContainer
))
960 gtk_fixed_remove(GTK_FIXED(pContainer
), pChild
);
964 void container_add(GtkWidget
* pContainer
, GtkWidget
* pChild
)
966 #if !GTK_CHECK_VERSION(4, 0, 0)
967 gtk_container_add(GTK_CONTAINER(pContainer
), pChild
);
969 assert(GTK_IS_BOX(pContainer
) || GTK_IS_GRID(pContainer
) || GTK_IS_POPOVER(pContainer
) ||
970 GTK_IS_FIXED(pContainer
) || GTK_IS_WINDOW(pContainer
));
971 if (GTK_IS_BOX(pContainer
))
972 gtk_box_append(GTK_BOX(pContainer
), pChild
);
973 else if (GTK_IS_GRID(pContainer
))
974 gtk_grid_attach(GTK_GRID(pContainer
), pChild
, 0, 0, 1, 1);
975 else if (GTK_IS_POPOVER(pContainer
))
976 gtk_popover_set_child(GTK_POPOVER(pContainer
), pChild
);
977 else if (GTK_IS_WINDOW(pContainer
))
978 gtk_window_set_child(GTK_WINDOW(pContainer
), pChild
);
979 else if (GTK_IS_FIXED(pContainer
))
980 gtk_fixed_put(GTK_FIXED(pContainer
), pChild
, 0, 0);
984 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */