Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <unistd.h>
21 : #include <fcntl.h>
22 :
23 : #include <stdio.h>
24 : #include <string.h>
25 : #include <stdlib.h>
26 : #include <limits.h>
27 : #include <errno.h>
28 : #include <poll.h>
29 : #if defined(FREEBSD) || defined(NETBSD)
30 : #include <sys/types.h>
31 : #include <sys/time.h>
32 : #endif
33 : #include <unx/gtk/gtkdata.hxx>
34 : #include <unx/gtk/gtkinst.hxx>
35 : #include <unx/gtk/gtkframe.hxx>
36 : #include <unx/salobj.h>
37 : #include <generic/geninst.h>
38 : #include <osl/thread.h>
39 : #include <osl/process.h>
40 :
41 : #include "unx/i18n_im.hxx"
42 : #include "unx/i18n_xkb.hxx"
43 : #include <unx/wmadaptor.hxx>
44 :
45 : #include "unx/x11_cursors/salcursors.h"
46 :
47 : #include <vcl/svapp.hxx>
48 :
49 : using namespace vcl_sal;
50 :
51 : /***************************************************************
52 : * class GtkSalDisplay *
53 : ***************************************************************/
54 : extern "C" {
55 0 : GdkFilterReturn call_filterGdkEvent( GdkXEvent* sys_event,
56 : GdkEvent* event,
57 : gpointer data )
58 : {
59 0 : GtkSalDisplay *pDisplay = static_cast<GtkSalDisplay *>(data);
60 0 : return pDisplay->filterGdkEvent( sys_event, event );
61 : }
62 : }
63 :
64 3 : GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay ) :
65 : #if !GTK_CHECK_VERSION(3,0,0)
66 : SalDisplay( gdk_x11_display_get_xdisplay( pDisplay ) ),
67 : #endif
68 3 : m_pSys( GtkSalSystem::GetSingleton() ),
69 : m_pGdkDisplay( pDisplay ),
70 6 : m_bStartupCompleted( false )
71 : {
72 285 : for(GdkCursor* & rpCsr : m_aCursors)
73 282 : rpCsr = NULL;
74 : #if !GTK_CHECK_VERSION(3,0,0)
75 3 : m_bUseRandRWrapper = false; // use gdk signal instead
76 3 : Init ();
77 : #endif
78 :
79 : // FIXME: unify this with SalInst's filter too ?
80 3 : gdk_window_add_filter( NULL, call_filterGdkEvent, this );
81 :
82 3 : if ( getenv( "SAL_IGNOREXERRORS" ) )
83 0 : GetGenericData()->ErrorTrapPush(); // and leak the trap
84 :
85 : #if GTK_CHECK_VERSION(3,0,0)
86 : m_bX11Display = GDK_IS_X11_DISPLAY( m_pGdkDisplay );
87 : #else
88 3 : m_bX11Display = true;
89 : #endif
90 3 : }
91 :
92 0 : GtkSalDisplay::~GtkSalDisplay()
93 : {
94 0 : gdk_window_remove_filter( NULL, call_filterGdkEvent, this );
95 :
96 0 : if( !m_bStartupCompleted )
97 0 : gdk_notify_startup_complete();
98 :
99 : #if !GTK_CHECK_VERSION(3,0,0)
100 0 : doDestruct();
101 0 : pDisp_ = NULL;
102 : #endif
103 :
104 0 : for(GdkCursor* & rpCsr : m_aCursors)
105 0 : if( rpCsr )
106 0 : gdk_cursor_unref( rpCsr );
107 0 : }
108 :
109 : extern "C" {
110 :
111 0 : void signalScreenSizeChanged( GdkScreen* pScreen, gpointer data )
112 : {
113 0 : GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data);
114 0 : pDisp->screenSizeChanged( pScreen );
115 0 : }
116 :
117 0 : void signalMonitorsChanged( GdkScreen* pScreen, gpointer data )
118 : {
119 0 : GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data);
120 0 : pDisp->monitorsChanged( pScreen );
121 0 : }
122 :
123 : }
124 :
125 0 : GdkFilterReturn GtkSalDisplay::filterGdkEvent( GdkXEvent* sys_event,
126 : GdkEvent* )
127 : {
128 : #if !GTK_CHECK_VERSION(3,0,0)
129 0 : GdkFilterReturn aFilterReturn = GDK_FILTER_CONTINUE;
130 0 : XEvent *pEvent = static_cast<XEvent *>(sys_event);
131 :
132 : // dispatch all XEvents to event callback
133 0 : if( GetSalData()->m_pInstance->
134 0 : CallEventCallback( pEvent, sizeof( XEvent ) ) )
135 0 : aFilterReturn = GDK_FILTER_REMOVE;
136 :
137 0 : if (GetDisplay() == pEvent->xany.display )
138 : {
139 : // #i53471# gtk has no callback mechanism that lets us be notified
140 : // when settings (as in XSETTING and opposed to styles) are changed.
141 : // so we need to listen for corresponding property notifications here
142 : // these should be rare enough so that we can assume that the settings
143 : // actually change when a corresponding PropertyNotify occurs
144 0 : if( pEvent->type == PropertyNotify &&
145 0 : pEvent->xproperty.atom == getWMAdaptor()->getAtom( WMAdaptor::XSETTINGS ) &&
146 0 : ! m_aFrames.empty()
147 : )
148 : {
149 0 : SendInternalEvent( m_aFrames.front(), NULL, SALEVENT_SETTINGSCHANGED );
150 : }
151 : // let's see if one of our frames wants to swallow these events
152 : // get the frame
153 0 : for( std::list< SalFrame* >::const_iterator it = m_aFrames.begin();
154 0 : it != m_aFrames.end(); ++it )
155 : {
156 0 : GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(*it);
157 0 : if( (GdkNativeWindow)pFrame->GetSystemData()->aWindow == pEvent->xany.window ||
158 0 : ( pFrame->getForeignParent() && pFrame->getForeignParentWindow() == pEvent->xany.window ) ||
159 0 : ( pFrame->getForeignTopLevel() && pFrame->getForeignTopLevelWindow() == pEvent->xany.window )
160 : )
161 : {
162 0 : if( ! pFrame->Dispatch( pEvent ) )
163 0 : aFilterReturn = GDK_FILTER_REMOVE;
164 0 : break;
165 : }
166 : }
167 0 : X11SalObject::Dispatch( pEvent );
168 : }
169 :
170 0 : return aFilterReturn;
171 : #else
172 : (void) this; // loplugin:staticmethods
173 : (void) sys_event;
174 : //FIXME: implement filterGdkEvent ...
175 : return GDK_FILTER_CONTINUE;
176 : #endif
177 : }
178 :
179 3 : void GtkSalDisplay::screenSizeChanged( GdkScreen* pScreen )
180 : {
181 3 : m_pSys->countScreenMonitors();
182 3 : if (pScreen)
183 3 : emitDisplayChanged();
184 3 : }
185 :
186 3 : void GtkSalDisplay::monitorsChanged( GdkScreen* pScreen )
187 : {
188 3 : m_pSys->countScreenMonitors();
189 3 : if (pScreen)
190 3 : emitDisplayChanged();
191 3 : }
192 :
193 : #if !GTK_CHECK_VERSION(3,0,0)
194 : SalDisplay::ScreenData *
195 3 : GtkSalDisplay::initScreen( SalX11Screen nXScreen ) const
196 : {
197 : // choose visual for screen
198 : ScreenData *pSD;
199 3 : if (!(pSD = SalDisplay::initScreen( nXScreen )))
200 0 : return NULL;
201 :
202 : // now set a gdk default colormap matching the chosen visual to the screen
203 3 : GdkScreen* pScreen = gdk_display_get_screen( m_pGdkDisplay, nXScreen.getXScreen() );
204 : // should really use this:
205 : // GdkVisual* pVis = gdk_x11_screen_lookup_visual_get( screen, pSD->m_aVisual.visualid );
206 : // and not this:
207 3 : GdkVisual* pVis = gdkx_visual_get( pSD->m_aVisual.visualid );
208 3 : if( pVis )
209 : {
210 3 : GdkColormap* pDefCol = gdk_screen_get_default_colormap( pScreen );
211 3 : GdkVisual* pDefVis = gdk_colormap_get_visual( pDefCol );
212 3 : if( pDefVis != pVis )
213 : {
214 0 : pDefCol = gdk_x11_colormap_foreign_new( pVis, pSD->m_aColormap.GetXColormap() );
215 0 : gdk_screen_set_default_colormap( pScreen, pDefCol );
216 : SAL_INFO( "vcl.gtk", "set new gdk color map for screen " << nXScreen.getXScreen() );
217 : }
218 : }
219 : else
220 : SAL_INFO( "vcl.gtk", "not GdkVisual for visual id " << pSD->m_aVisual.visualid );
221 :
222 3 : return pSD;
223 : }
224 :
225 0 : bool GtkSalDisplay::Dispatch( XEvent* pEvent )
226 : {
227 0 : if( GetDisplay() == pEvent->xany.display )
228 : {
229 : // let's see if one of our frames wants to swallow these events
230 : // get the child frame
231 0 : for( std::list< SalFrame* >::const_iterator it = m_aFrames.begin();
232 0 : it != m_aFrames.end(); ++it )
233 : {
234 0 : if( (GdkNativeWindow)(*it)->GetSystemData()->aWindow == pEvent->xany.window )
235 0 : return static_cast<GtkSalFrame*>(*it)->Dispatch( pEvent );
236 : }
237 : }
238 :
239 0 : return false;
240 : }
241 : #endif
242 :
243 : #if GTK_CHECK_VERSION(3,0,0)
244 : namespace
245 : {
246 : //cairo annoyingly won't take raw xbm data unless it fits
247 : //the required cairo stride
248 : unsigned char* ensurePaddedForCairo(const unsigned char *pXBM,
249 : int nWidth, int nHeight, int nStride)
250 : {
251 : unsigned char *pPaddedXBM = const_cast<unsigned char*>(pXBM);
252 :
253 : int bytes_per_row = (nWidth + 7) / 8;
254 :
255 : if (nStride != bytes_per_row)
256 : {
257 : pPaddedXBM = new unsigned char[nStride * nHeight];
258 : for (int row = 0; row < nHeight; ++row)
259 : {
260 : memcpy(pPaddedXBM + (nStride * row),
261 : pXBM + (bytes_per_row * row), bytes_per_row);
262 : memset(pPaddedXBM + (nStride * row) + bytes_per_row,
263 : 0, nStride - bytes_per_row);
264 : }
265 : }
266 :
267 : return pPaddedXBM;
268 : }
269 : }
270 : #endif
271 :
272 0 : GdkCursor* GtkSalDisplay::getFromXBM( const unsigned char *pBitmap,
273 : const unsigned char *pMask,
274 : int nWidth, int nHeight,
275 : int nXHot, int nYHot )
276 : {
277 : #if GTK_CHECK_VERSION(3,0,0)
278 : int cairo_stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, nWidth);
279 :
280 : unsigned char *pPaddedXBM = ensurePaddedForCairo(pBitmap, nWidth, nHeight, cairo_stride);
281 : cairo_surface_t *s = cairo_image_surface_create_for_data(
282 : pPaddedXBM,
283 : CAIRO_FORMAT_A1, nWidth, nHeight,
284 : cairo_stride);
285 :
286 : cairo_t *cr = cairo_create(s);
287 : unsigned char *pPaddedMaskXBM = ensurePaddedForCairo(pMask, nWidth, nHeight, cairo_stride);
288 : cairo_surface_t *mask = cairo_image_surface_create_for_data(
289 : pPaddedMaskXBM,
290 : CAIRO_FORMAT_A1, nWidth, nHeight,
291 : cairo_stride);
292 : cairo_mask_surface(cr, mask, 0, 0);
293 : cairo_destroy(cr);
294 : cairo_surface_destroy(mask);
295 : if (pPaddedMaskXBM != pMask)
296 : delete [] pPaddedMaskXBM;
297 :
298 : GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(s, 0, 0, nWidth, nHeight);
299 : cairo_surface_destroy(s);
300 : if (pPaddedXBM != pBitmap)
301 : delete [] pPaddedXBM;
302 :
303 : GdkCursor *cursor = gdk_cursor_new_from_pixbuf(m_pGdkDisplay, pixbuf, nXHot, nYHot);
304 : g_object_unref(pixbuf);
305 :
306 : return cursor;
307 : #else
308 0 : GdkScreen *pScreen = gdk_display_get_default_screen( m_pGdkDisplay );
309 0 : GdkDrawable *pDrawable = GDK_DRAWABLE( gdk_screen_get_root_window (pScreen) );
310 : GdkBitmap *pBitmapPix = gdk_bitmap_create_from_data
311 0 : ( pDrawable, reinterpret_cast<const char*>(pBitmap), nWidth, nHeight );
312 : GdkBitmap *pMaskPix = gdk_bitmap_create_from_data
313 0 : ( pDrawable, reinterpret_cast<const char*>(pMask), nWidth, nHeight );
314 0 : GdkColormap *pColormap = gdk_drawable_get_colormap( pDrawable );
315 :
316 0 : GdkColor aWhite = { 0, 0xffff, 0xffff, 0xffff };
317 0 : GdkColor aBlack = { 0, 0, 0, 0 };
318 :
319 0 : gdk_colormap_alloc_color( pColormap, &aBlack, FALSE, TRUE);
320 0 : gdk_colormap_alloc_color( pColormap, &aWhite, FALSE, TRUE);
321 :
322 : return gdk_cursor_new_from_pixmap
323 : ( pBitmapPix, pMaskPix,
324 0 : &aBlack, &aWhite, nXHot, nYHot);
325 : #endif
326 : }
327 :
328 : #define MAKE_CURSOR( vcl_name, name ) \
329 : case vcl_name: \
330 : pCursor = getFromXBM( name##curs##_bits, name##mask##_bits, \
331 : name##curs_width, name##curs_height, \
332 : name##curs_x_hot, name##curs_y_hot ); \
333 : break
334 : #define MAP_BUILTIN( vcl_name, gdk_name ) \
335 : case vcl_name: \
336 : pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk_name ); \
337 : break
338 :
339 0 : GdkCursor *GtkSalDisplay::getCursor( PointerStyle ePointerStyle )
340 : {
341 0 : if ( !m_aCursors[ ePointerStyle ] )
342 : {
343 0 : GdkCursor *pCursor = NULL;
344 :
345 0 : switch( ePointerStyle )
346 : {
347 0 : MAP_BUILTIN( PointerStyle::Arrow, GDK_LEFT_PTR );
348 0 : MAP_BUILTIN( PointerStyle::Text, GDK_XTERM );
349 0 : MAP_BUILTIN( PointerStyle::Help, GDK_QUESTION_ARROW );
350 0 : MAP_BUILTIN( PointerStyle::Cross, GDK_CROSSHAIR );
351 0 : MAP_BUILTIN( PointerStyle::Wait, GDK_WATCH );
352 :
353 0 : MAP_BUILTIN( PointerStyle::NSize, GDK_SB_V_DOUBLE_ARROW );
354 0 : MAP_BUILTIN( PointerStyle::SSize, GDK_SB_V_DOUBLE_ARROW );
355 0 : MAP_BUILTIN( PointerStyle::WSize, GDK_SB_H_DOUBLE_ARROW );
356 0 : MAP_BUILTIN( PointerStyle::ESize, GDK_SB_H_DOUBLE_ARROW );
357 :
358 0 : MAP_BUILTIN( PointerStyle::NWSize, GDK_TOP_LEFT_CORNER );
359 0 : MAP_BUILTIN( PointerStyle::NESize, GDK_TOP_RIGHT_CORNER );
360 0 : MAP_BUILTIN( PointerStyle::SWSize, GDK_BOTTOM_LEFT_CORNER );
361 0 : MAP_BUILTIN( PointerStyle::SESize, GDK_BOTTOM_RIGHT_CORNER );
362 :
363 0 : MAP_BUILTIN( PointerStyle::WindowNSize, GDK_TOP_SIDE );
364 0 : MAP_BUILTIN( PointerStyle::WindowSSize, GDK_BOTTOM_SIDE );
365 0 : MAP_BUILTIN( PointerStyle::WindowWSize, GDK_LEFT_SIDE );
366 0 : MAP_BUILTIN( PointerStyle::WindowESize, GDK_RIGHT_SIDE );
367 :
368 0 : MAP_BUILTIN( PointerStyle::WindowNWSize, GDK_TOP_LEFT_CORNER );
369 0 : MAP_BUILTIN( PointerStyle::WindowNESize, GDK_TOP_RIGHT_CORNER );
370 0 : MAP_BUILTIN( PointerStyle::WindowSWSize, GDK_BOTTOM_LEFT_CORNER );
371 0 : MAP_BUILTIN( PointerStyle::WindowSESize, GDK_BOTTOM_RIGHT_CORNER );
372 :
373 0 : MAP_BUILTIN( PointerStyle::HSizeBar, GDK_SB_H_DOUBLE_ARROW );
374 0 : MAP_BUILTIN( PointerStyle::VSizeBar, GDK_SB_V_DOUBLE_ARROW );
375 :
376 0 : MAP_BUILTIN( PointerStyle::RefHand, GDK_HAND2 );
377 0 : MAP_BUILTIN( PointerStyle::Hand, GDK_HAND2 );
378 0 : MAP_BUILTIN( PointerStyle::Pen, GDK_PENCIL );
379 :
380 0 : MAP_BUILTIN( PointerStyle::HSplit, GDK_SB_H_DOUBLE_ARROW );
381 0 : MAP_BUILTIN( PointerStyle::VSplit, GDK_SB_V_DOUBLE_ARROW );
382 :
383 0 : MAP_BUILTIN( PointerStyle::Move, GDK_FLEUR );
384 :
385 0 : MAKE_CURSOR( PointerStyle::Null, null );
386 0 : MAKE_CURSOR( PointerStyle::Magnify, magnify_ );
387 0 : MAKE_CURSOR( PointerStyle::Fill, fill_ );
388 0 : MAKE_CURSOR( PointerStyle::MoveData, movedata_ );
389 0 : MAKE_CURSOR( PointerStyle::CopyData, copydata_ );
390 0 : MAKE_CURSOR( PointerStyle::MoveFile, movefile_ );
391 0 : MAKE_CURSOR( PointerStyle::CopyFile, copyfile_ );
392 0 : MAKE_CURSOR( PointerStyle::MoveFiles, movefiles_ );
393 0 : MAKE_CURSOR( PointerStyle::CopyFiles, copyfiles_ );
394 0 : MAKE_CURSOR( PointerStyle::NotAllowed, nodrop_ );
395 0 : MAKE_CURSOR( PointerStyle::Rotate, rotate_ );
396 0 : MAKE_CURSOR( PointerStyle::HShear, hshear_ );
397 0 : MAKE_CURSOR( PointerStyle::VShear, vshear_ );
398 0 : MAKE_CURSOR( PointerStyle::DrawLine, drawline_ );
399 0 : MAKE_CURSOR( PointerStyle::DrawRect, drawrect_ );
400 0 : MAKE_CURSOR( PointerStyle::DrawPolygon, drawpolygon_ );
401 0 : MAKE_CURSOR( PointerStyle::DrawBezier, drawbezier_ );
402 0 : MAKE_CURSOR( PointerStyle::DrawArc, drawarc_ );
403 0 : MAKE_CURSOR( PointerStyle::DrawPie, drawpie_ );
404 0 : MAKE_CURSOR( PointerStyle::DrawCircleCut, drawcirclecut_ );
405 0 : MAKE_CURSOR( PointerStyle::DrawEllipse, drawellipse_ );
406 0 : MAKE_CURSOR( PointerStyle::DrawConnect, drawconnect_ );
407 0 : MAKE_CURSOR( PointerStyle::DrawText, drawtext_ );
408 0 : MAKE_CURSOR( PointerStyle::Mirror, mirror_ );
409 0 : MAKE_CURSOR( PointerStyle::Crook, crook_ );
410 0 : MAKE_CURSOR( PointerStyle::Crop, crop_ );
411 0 : MAKE_CURSOR( PointerStyle::MovePoint, movepoint_ );
412 0 : MAKE_CURSOR( PointerStyle::MoveBezierWeight, movebezierweight_ );
413 0 : MAKE_CURSOR( PointerStyle::DrawFreehand, drawfreehand_ );
414 0 : MAKE_CURSOR( PointerStyle::DrawCaption, drawcaption_ );
415 0 : MAKE_CURSOR( PointerStyle::LinkData, linkdata_ );
416 0 : MAKE_CURSOR( PointerStyle::MoveDataLink, movedlnk_ );
417 0 : MAKE_CURSOR( PointerStyle::CopyDataLink, copydlnk_ );
418 0 : MAKE_CURSOR( PointerStyle::LinkFile, linkfile_ );
419 0 : MAKE_CURSOR( PointerStyle::MoveFileLink, moveflnk_ );
420 0 : MAKE_CURSOR( PointerStyle::CopyFileLink, copyflnk_ );
421 0 : MAKE_CURSOR( PointerStyle::Chart, chart_ );
422 0 : MAKE_CURSOR( PointerStyle::Detective, detective_ );
423 0 : MAKE_CURSOR( PointerStyle::PivotCol, pivotcol_ );
424 0 : MAKE_CURSOR( PointerStyle::PivotRow, pivotrow_ );
425 0 : MAKE_CURSOR( PointerStyle::PivotField, pivotfld_ );
426 0 : MAKE_CURSOR( PointerStyle::PivotDelete, pivotdel_ );
427 0 : MAKE_CURSOR( PointerStyle::Chain, chain_ );
428 0 : MAKE_CURSOR( PointerStyle::ChainNotAllowed, chainnot_ );
429 0 : MAKE_CURSOR( PointerStyle::TimeEventMove, timemove_ );
430 0 : MAKE_CURSOR( PointerStyle::TimeEventSize, timesize_ );
431 0 : MAKE_CURSOR( PointerStyle::AutoScrollN, asn_ );
432 0 : MAKE_CURSOR( PointerStyle::AutoScrollS, ass_ );
433 0 : MAKE_CURSOR( PointerStyle::AutoScrollW, asw_ );
434 0 : MAKE_CURSOR( PointerStyle::AutoScrollE, ase_ );
435 0 : MAKE_CURSOR( PointerStyle::AutoScrollNW, asnw_ );
436 0 : MAKE_CURSOR( PointerStyle::AutoScrollNE, asne_ );
437 0 : MAKE_CURSOR( PointerStyle::AutoScrollSW, assw_ );
438 0 : MAKE_CURSOR( PointerStyle::AutoScrollSE, asse_ );
439 0 : MAKE_CURSOR( PointerStyle::AutoScrollNS, asns_ );
440 0 : MAKE_CURSOR( PointerStyle::AutoScrollWE, aswe_ );
441 0 : MAKE_CURSOR( PointerStyle::AutoScrollNSWE, asnswe_ );
442 0 : MAKE_CURSOR( PointerStyle::Airbrush, airbrush_ );
443 0 : MAKE_CURSOR( PointerStyle::TextVertical, vertcurs_ );
444 :
445 : // #i32329#
446 0 : MAKE_CURSOR( PointerStyle::TabSelectS, tblsels_ );
447 0 : MAKE_CURSOR( PointerStyle::TabSelectE, tblsele_ );
448 0 : MAKE_CURSOR( PointerStyle::TabSelectSE, tblselse_ );
449 0 : MAKE_CURSOR( PointerStyle::TabSelectW, tblselw_ );
450 0 : MAKE_CURSOR( PointerStyle::TabSelectSW, tblselsw_ );
451 :
452 : // #i20119#
453 0 : MAKE_CURSOR( PointerStyle::Paintbrush, paintbrush_ );
454 :
455 : default:
456 : SAL_WARN( "vcl.gtk", "pointer " << static_cast<int>(ePointerStyle) << "not implemented" );
457 0 : break;
458 : }
459 0 : if( !pCursor )
460 0 : pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, GDK_LEFT_PTR );
461 :
462 0 : m_aCursors[ ePointerStyle ] = pCursor;
463 : }
464 :
465 0 : return m_aCursors[ ePointerStyle ];
466 : }
467 :
468 0 : int GtkSalDisplay::CaptureMouse( SalFrame* pSFrame )
469 : {
470 0 : GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(pSFrame);
471 :
472 0 : if( !pFrame )
473 : {
474 0 : if( m_pCapture )
475 0 : static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
476 0 : m_pCapture = NULL;
477 0 : return 0;
478 : }
479 :
480 0 : if( m_pCapture )
481 : {
482 0 : if( pFrame == m_pCapture )
483 0 : return 1;
484 0 : static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
485 : }
486 :
487 0 : m_pCapture = pFrame;
488 0 : static_cast<GtkSalFrame*>(pFrame)->grabPointer( TRUE );
489 0 : return 1;
490 : }
491 :
492 : /**********************************************************************
493 : * class GtkData *
494 : **********************************************************************/
495 :
496 3 : GtkData::GtkData( SalInstance *pInstance )
497 : #if GTK_CHECK_VERSION(3,0,0)
498 : : SalGenericData( SAL_DATA_GTK3, pInstance )
499 : #else
500 : : SalGenericData( SAL_DATA_GTK, pInstance )
501 : #endif
502 3 : , blockIdleTimeout( false )
503 : {
504 3 : m_pUserEvent = NULL;
505 3 : m_aDispatchMutex = osl_createMutex();
506 3 : m_aDispatchCondition = osl_createCondition();
507 3 : }
508 :
509 : XIOErrorHandler aOrigXIOErrorHandler = NULL;
510 :
511 : extern "C" {
512 :
513 0 : static int XIOErrorHdl(Display *)
514 : {
515 0 : fprintf(stderr, "X IO Error\n");
516 0 : _exit(1);
517 : // avoid crashes in unrelated threads that still run while atexit
518 : // handlers are in progress
519 : }
520 :
521 : }
522 :
523 0 : GtkData::~GtkData()
524 : {
525 0 : Yield( true, true );
526 0 : g_warning ("TESTME: We used to have a stop-timer here, but the central code should do this");
527 :
528 : // sanity check: at this point nobody should be yielding, but wake them
529 : // up anyway before the condition they're waiting on gets destroyed.
530 0 : osl_setCondition( m_aDispatchCondition );
531 :
532 0 : osl_acquireMutex( m_aDispatchMutex );
533 0 : if (m_pUserEvent)
534 : {
535 0 : g_source_destroy (m_pUserEvent);
536 0 : g_source_unref (m_pUserEvent);
537 0 : m_pUserEvent = NULL;
538 : }
539 0 : osl_destroyCondition( m_aDispatchCondition );
540 0 : osl_releaseMutex( m_aDispatchMutex );
541 0 : osl_destroyMutex( m_aDispatchMutex );
542 0 : XSetIOErrorHandler(aOrigXIOErrorHandler);
543 0 : }
544 :
545 1 : void GtkData::Dispose()
546 : {
547 1 : deInitNWF();
548 1 : }
549 :
550 0 : void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
551 : {
552 0 : blockIdleTimeout = !bWait;
553 : /* #i33212# only enter g_main_context_iteration in one thread at any one
554 : * time, else one of them potentially will never end as long as there is
555 : * another thread in there. Having only one yieldin thread actually dispatch
556 : * fits the vcl event model (see e.g. the generic plugin).
557 : */
558 0 : bool bDispatchThread = false;
559 0 : bool bWasEvent = false;
560 : {
561 : // release YieldMutex (and re-acquire at block end)
562 0 : SalYieldMutexReleaser aReleaser;
563 0 : if( osl_tryToAcquireMutex( m_aDispatchMutex ) )
564 0 : bDispatchThread = true;
565 0 : else if( ! bWait )
566 : {
567 0 : blockIdleTimeout = false;
568 0 : return; // someone else is waiting already, return
569 : }
570 :
571 0 : if( bDispatchThread )
572 : {
573 0 : int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
574 0 : gboolean wasOneEvent = TRUE;
575 0 : while( nMaxEvents-- && wasOneEvent )
576 : {
577 0 : wasOneEvent = g_main_context_iteration( NULL, bWait && !bWasEvent );
578 0 : if( wasOneEvent )
579 0 : bWasEvent = true;
580 : }
581 : }
582 0 : else if( bWait )
583 : {
584 : /* #i41693# in case the dispatch thread hangs in join
585 : * for this thread the condition will never be set
586 : * workaround: timeout of 1 second a emergency exit
587 : */
588 : // we are the dispatch thread
589 0 : osl_resetCondition( m_aDispatchCondition );
590 0 : TimeValue aValue = { 1, 0 };
591 0 : osl_waitCondition( m_aDispatchCondition, &aValue );
592 0 : }
593 : }
594 :
595 0 : if( bDispatchThread )
596 : {
597 0 : osl_releaseMutex( m_aDispatchMutex );
598 0 : if( bWasEvent )
599 0 : osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields
600 : }
601 0 : blockIdleTimeout = false;
602 : }
603 :
604 3 : void GtkData::Init()
605 : {
606 : int i;
607 : SAL_INFO( "vcl.gtk", "GtkMainloop::Init()" );
608 3 : XrmInitialize();
609 :
610 : #if !GTK_CHECK_VERSION(3,0,0)
611 3 : gtk_set_locale();
612 : #endif
613 :
614 : /*
615 : * open connection to X11 Display
616 : * try in this order:
617 : * o -display command line parameter,
618 : * o $DISPLAY environment variable
619 : * o default display
620 : */
621 :
622 3 : GdkDisplay *pGdkDisp = NULL;
623 :
624 : // is there a -display command line parameter?
625 3 : rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
626 3 : int nParams = osl_getCommandArgCount();
627 3 : OString aDisplay;
628 6 : OUString aParam, aBin;
629 3 : char** pCmdLineAry = new char*[ nParams+1 ];
630 3 : osl_getExecutableFile( &aParam.pData );
631 3 : osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData );
632 3 : pCmdLineAry[0] = g_strdup( OUStringToOString( aBin, aEnc ).getStr() );
633 22 : for (i=0; i<nParams; i++)
634 : {
635 19 : osl_getCommandArg(i, &aParam.pData );
636 19 : OString aBParam( OUStringToOString( aParam, aEnc ) );
637 :
638 19 : if( aParam == "-display" || aParam == "--display" )
639 : {
640 0 : pCmdLineAry[i+1] = g_strdup( "--display" );
641 0 : osl_getCommandArg(i+1, &aParam.pData );
642 0 : aDisplay = OUStringToOString( aParam, aEnc );
643 : }
644 : else
645 19 : pCmdLineAry[i+1] = g_strdup( aBParam.getStr() );
646 19 : }
647 : // add executable
648 3 : nParams++;
649 :
650 3 : g_set_application_name(SalGenericSystem::getFrameClassName());
651 :
652 : // Set consistent name of the root accessible
653 6 : OUString aAppName = Application::GetAppName();
654 3 : if( !aAppName.isEmpty() )
655 : {
656 0 : OString aPrgName = OUStringToOString(aAppName, aEnc);
657 0 : g_set_prgname(aPrgName.getStr());
658 : }
659 :
660 : // init gtk/gdk
661 3 : gtk_init_check( &nParams, &pCmdLineAry );
662 3 : gdk_error_trap_push();
663 3 : aOrigXIOErrorHandler = XSetIOErrorHandler(XIOErrorHdl);
664 :
665 25 : for (i = 0; i < nParams; i++ )
666 22 : g_free( pCmdLineAry[i] );
667 3 : delete [] pCmdLineAry;
668 :
669 : #if OSL_DEBUG_LEVEL > 1
670 : if (g_getenv ("SAL_DEBUG_UPDATES"))
671 : gdk_window_set_debug_updates (TRUE);
672 : #endif
673 :
674 3 : pGdkDisp = gdk_display_get_default();
675 3 : if ( !pGdkDisp )
676 : {
677 0 : OUString aProgramFileURL;
678 0 : osl_getExecutableFile( &aProgramFileURL.pData );
679 0 : OUString aProgramSystemPath;
680 0 : osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData);
681 : OString aProgramName = OUStringToOString(
682 : aProgramSystemPath,
683 0 : osl_getThreadTextEncoding() );
684 : fprintf( stderr, "%s X11 error: Can't open display: %s\n",
685 0 : aProgramName.getStr(), aDisplay.getStr());
686 0 : fprintf( stderr, " Set DISPLAY environment variable, use -display option\n");
687 0 : fprintf( stderr, " or check permissions of your X-Server\n");
688 0 : fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n");
689 0 : fflush( stderr );
690 0 : exit(0);
691 : }
692 :
693 : /*
694 : * if a -display switch was used, we need
695 : * to set the environment accoringly since
696 : * the clipboard build another connection
697 : * to the xserver using $DISPLAY
698 : */
699 6 : OUString envVar("DISPLAY");
700 3 : const gchar *name = gdk_display_get_name( pGdkDisp );
701 6 : OUString envValue(name, strlen(name), aEnc);
702 3 : osl_setEnvironment(envVar.pData, envValue.pData);
703 :
704 3 : GtkSalDisplay *pDisplay = new GtkSalDisplay( pGdkDisp );
705 3 : SetDisplay( pDisplay );
706 :
707 : #if !GTK_CHECK_VERSION(3,0,0)
708 3 : Display *pDisp = gdk_x11_display_get_xdisplay( pGdkDisp );
709 :
710 3 : gdk_error_trap_push();
711 3 : SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp );
712 3 : bool bErrorOccured = gdk_error_trap_pop() != 0;
713 3 : gdk_error_trap_push();
714 3 : pKbdExtension->UseExtension( bErrorOccured );
715 3 : gdk_error_trap_pop();
716 3 : GetGtkDisplay()->SetKbdExtension( pKbdExtension );
717 : #else
718 : //FIXME: unwind keyboard extension bits
719 : #endif
720 :
721 : // add signal handler to notify screen size changes
722 3 : int nScreens = gdk_display_get_n_screens( pGdkDisp );
723 6 : for( int n = 0; n < nScreens; n++ )
724 : {
725 3 : GdkScreen *pScreen = gdk_display_get_screen( pGdkDisp, n );
726 3 : if( pScreen )
727 : {
728 3 : pDisplay->screenSizeChanged( pScreen );
729 3 : pDisplay->monitorsChanged( pScreen );
730 3 : g_signal_connect( G_OBJECT(pScreen), "size-changed",
731 3 : G_CALLBACK(signalScreenSizeChanged), pDisplay );
732 3 : if( ! gtk_check_version( 2, 14, 0 ) ) // monitors-changed came in with 2.14, avoid an assertion
733 3 : g_signal_connect( G_OBJECT(pScreen), "monitors-changed",
734 3 : G_CALLBACK(signalMonitorsChanged), GetGtkDisplay() );
735 : }
736 3 : }
737 3 : }
738 :
739 17 : void GtkData::ErrorTrapPush()
740 : {
741 17 : gdk_error_trap_push ();
742 17 : }
743 :
744 17 : bool GtkData::ErrorTrapPop( bool bIgnoreError )
745 : {
746 : #if GTK_CHECK_VERSION(3,0,0)
747 : if( bIgnoreError )
748 : {
749 : gdk_error_trap_pop_ignored (); // faster
750 : return false;
751 : }
752 : #else
753 : (void) bIgnoreError;
754 : #endif
755 17 : return gdk_error_trap_pop () != 0;
756 : }
757 :
758 : extern "C" {
759 :
760 : struct SalGtkTimeoutSource {
761 : GSource aParent;
762 : GTimeVal aFireTime;
763 : GtkSalTimer *pInstance;
764 : };
765 :
766 3 : static void sal_gtk_timeout_defer( SalGtkTimeoutSource *pTSource )
767 : {
768 3 : g_get_current_time( &pTSource->aFireTime );
769 3 : g_time_val_add( &pTSource->aFireTime, pTSource->pInstance->m_nTimeoutMS * 1000 );
770 3 : }
771 :
772 0 : static gboolean sal_gtk_timeout_expired( SalGtkTimeoutSource *pTSource,
773 : gint *nTimeoutMS, GTimeVal *pTimeNow )
774 : {
775 0 : glong nDeltaSec = pTSource->aFireTime.tv_sec - pTimeNow->tv_sec;
776 0 : glong nDeltaUSec = pTSource->aFireTime.tv_usec - pTimeNow->tv_usec;
777 0 : if( nDeltaSec < 0 || ( nDeltaSec == 0 && nDeltaUSec < 0) )
778 : {
779 0 : *nTimeoutMS = 0;
780 0 : return TRUE;
781 : }
782 0 : if( nDeltaUSec < 0 )
783 : {
784 0 : nDeltaUSec += 1000000;
785 0 : nDeltaSec -= 1;
786 : }
787 : // if the clock changes backwards we need to cope ...
788 0 : if( (unsigned long) nDeltaSec > 1 + ( pTSource->pInstance->m_nTimeoutMS / 1000 ) )
789 : {
790 0 : sal_gtk_timeout_defer( pTSource );
791 0 : return TRUE;
792 : }
793 :
794 0 : *nTimeoutMS = MIN( G_MAXINT, ( nDeltaSec * 1000 + (nDeltaUSec + 999) / 1000 ) );
795 :
796 0 : return *nTimeoutMS == 0;
797 : }
798 :
799 0 : static gboolean sal_gtk_timeout_prepare( GSource *pSource, gint *nTimeoutMS )
800 : {
801 0 : SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
802 :
803 : GTimeVal aTimeNow;
804 0 : g_get_current_time( &aTimeNow );
805 :
806 0 : return sal_gtk_timeout_expired( pTSource, nTimeoutMS, &aTimeNow );
807 : }
808 :
809 0 : static gboolean sal_gtk_timeout_check( GSource *pSource )
810 : {
811 0 : SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
812 :
813 : GTimeVal aTimeNow;
814 0 : g_get_current_time( &aTimeNow );
815 :
816 0 : return ( pTSource->aFireTime.tv_sec < aTimeNow.tv_sec ||
817 0 : ( pTSource->aFireTime.tv_sec == aTimeNow.tv_sec &&
818 0 : pTSource->aFireTime.tv_usec < aTimeNow.tv_usec ) );
819 : }
820 :
821 0 : static gboolean sal_gtk_timeout_dispatch( GSource *pSource, GSourceFunc, gpointer )
822 : {
823 0 : SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
824 :
825 0 : if( !pTSource->pInstance )
826 0 : return FALSE;
827 :
828 0 : GtkData *pSalData = static_cast< GtkData* >( GetSalData());
829 :
830 0 : osl::Guard< comphelper::SolarMutex > aGuard( pSalData->m_pInstance->GetYieldMutex() );
831 :
832 0 : sal_gtk_timeout_defer( pTSource );
833 :
834 0 : ImplSVData* pSVData = ImplGetSVData();
835 0 : if( pSVData->mpSalTimer )
836 : {
837 : // TODO: context_pending should be probably checked too, but it causes locking assertion failures
838 0 : bool idle = !pSalData->BlockIdleTimeout() && /*!g_main_context_pending( NULL ) &&*/ !gdk_events_pending();
839 0 : pSVData->mpSalTimer->CallCallback( idle );
840 : }
841 :
842 0 : return TRUE;
843 : }
844 :
845 : static GSourceFuncs sal_gtk_timeout_funcs =
846 : {
847 : sal_gtk_timeout_prepare,
848 : sal_gtk_timeout_check,
849 : sal_gtk_timeout_dispatch,
850 : NULL, NULL, NULL
851 : };
852 : }
853 :
854 : static SalGtkTimeoutSource *
855 3 : create_sal_gtk_timeout( GtkSalTimer *pTimer )
856 : {
857 3 : GSource *pSource = g_source_new( &sal_gtk_timeout_funcs, sizeof( SalGtkTimeoutSource ) );
858 3 : SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource);
859 3 : pTSource->pInstance = pTimer;
860 :
861 : // #i36226# timers should be executed with lower priority
862 : // than XEvents like in generic plugin
863 3 : g_source_set_priority( pSource, G_PRIORITY_LOW );
864 3 : g_source_set_can_recurse( pSource, TRUE );
865 : g_source_set_callback( pSource,
866 : /* unused dummy */ g_idle_remove_by_data,
867 3 : NULL, NULL );
868 3 : g_source_attach( pSource, g_main_context_default() );
869 :
870 3 : sal_gtk_timeout_defer( pTSource );
871 :
872 3 : return pTSource;
873 : }
874 :
875 3 : GtkSalTimer::GtkSalTimer()
876 : : m_pTimeout(NULL)
877 3 : , m_nTimeoutMS(0)
878 : {
879 3 : }
880 :
881 3 : GtkSalTimer::~GtkSalTimer()
882 : {
883 1 : GtkInstance *pInstance = static_cast<GtkInstance *>(GetSalData()->m_pInstance);
884 1 : pInstance->RemoveTimer( this );
885 1 : Stop();
886 2 : }
887 :
888 0 : bool GtkSalTimer::Expired()
889 : {
890 0 : if( !m_pTimeout )
891 0 : return false;
892 :
893 0 : gint nDummy = 0;
894 : GTimeVal aTimeNow;
895 0 : g_get_current_time( &aTimeNow );
896 0 : return !!sal_gtk_timeout_expired( m_pTimeout, &nDummy, &aTimeNow);
897 : }
898 :
899 3 : void GtkSalTimer::Start( sal_uLong nMS )
900 : {
901 3 : m_nTimeoutMS = nMS; // for restarting
902 3 : Stop(); // FIXME: ideally re-use an existing m_pTimeout
903 3 : m_pTimeout = create_sal_gtk_timeout( this );
904 3 : }
905 :
906 5 : void GtkSalTimer::Stop()
907 : {
908 5 : if( m_pTimeout )
909 : {
910 1 : g_source_destroy( &m_pTimeout->aParent );
911 1 : g_source_unref( &m_pTimeout->aParent );
912 1 : m_pTimeout = NULL;
913 : }
914 5 : }
915 :
916 0 : gboolean GtkData::userEventFn( gpointer data )
917 : {
918 0 : gboolean bContinue = FALSE;
919 0 : GtkData *pThis = static_cast<GtkData *>(data);
920 0 : SalGenericData *pData = GetGenericData();
921 0 : osl::Guard< comphelper::SolarMutex > aGuard( pData->m_pInstance->GetYieldMutex() );
922 0 : const SalGenericDisplay *pDisplay = pData->GetDisplay();
923 0 : if (pDisplay)
924 : {
925 : OSL_ASSERT(static_cast<const SalGenericDisplay *>(pThis->GetGtkDisplay()) == pDisplay);
926 0 : pThis->GetGtkDisplay()->EventGuardAcquire();
927 :
928 0 : if( !pThis->GetGtkDisplay()->HasUserEvents() )
929 : {
930 0 : if( pThis->m_pUserEvent )
931 : {
932 0 : g_source_unref (pThis->m_pUserEvent);
933 0 : pThis->m_pUserEvent = NULL;
934 : }
935 0 : bContinue = FALSE;
936 : }
937 : else
938 0 : bContinue = TRUE;
939 :
940 0 : pThis->GetGtkDisplay()->EventGuardRelease();
941 :
942 0 : pThis->GetGtkDisplay()->DispatchInternalEvent();
943 : }
944 :
945 0 : return bContinue;
946 : }
947 :
948 : extern "C" {
949 0 : static gboolean call_userEventFn( void *data )
950 : {
951 0 : SolarMutexGuard aGuard;
952 0 : return GtkData::userEventFn( data );
953 : }
954 : }
955 :
956 : // hEventGuard_ held during this invocation
957 58 : void GtkData::PostUserEvent()
958 : {
959 58 : if (m_pUserEvent)
960 55 : g_main_context_wakeup (NULL); // really needed ?
961 : else // nothing pending anyway
962 : {
963 3 : m_pUserEvent = g_idle_source_new();
964 3 : g_source_set_priority (m_pUserEvent, G_PRIORITY_HIGH);
965 3 : g_source_set_can_recurse (m_pUserEvent, TRUE);
966 : g_source_set_callback (m_pUserEvent, call_userEventFn,
967 3 : static_cast<gpointer>(this), NULL);
968 3 : g_source_attach (m_pUserEvent, g_main_context_default ());
969 : }
970 58 : }
971 :
972 58 : void GtkSalDisplay::PostUserEvent()
973 : {
974 58 : GetGtkSalData()->PostUserEvent();
975 58 : }
976 :
977 13 : void GtkSalDisplay::deregisterFrame( SalFrame* pFrame )
978 : {
979 13 : if( m_pCapture == pFrame )
980 : {
981 0 : static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE );
982 0 : m_pCapture = NULL;
983 : }
984 13 : SalGenericDisplay::deregisterFrame( pFrame );
985 22 : }
986 :
987 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|