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 .
27 #include <sys/systeminfo.h>
33 #include <sys/types.h>
37 #include <osl/process.h>
39 #include <unx/saldisp.hxx>
40 #include <unx/saldata.hxx>
41 #include <unx/salunxtime.h>
43 #include <unx/i18n_im.hxx>
46 #include <X11/Xproto.h>
48 #include <salinst.hxx>
49 #include <saltimer.hxx>
51 #include <osl/diagnose.h>
52 #include <osl/signal.h>
53 #include <osl/thread.h>
54 #include <sal/log.hxx>
56 #include <vcl/svapp.hxx>
58 X11SalData
* GetX11SalData()
60 SalData
* p1
= ImplGetSVData()->mpSalData
;
61 OSL_ASSERT(p1
!= nullptr);
62 X11SalData
* p2
= dynamic_cast< X11SalData
* >(p1
);
63 OSL_ASSERT(p2
!= nullptr);
69 static int XErrorHdl( Display
*pDisplay
, XErrorEvent
*pEvent
)
71 GetX11SalData()->XError( pDisplay
, pEvent
);
75 static int XIOErrorHdl( Display
* )
77 if ( Application::IsMainThread() )
79 /* #106197# hack: until a real shutdown procedure exists
82 if( ImplGetSVData()->maAppData
.mbAppQuit
)
86 if( ! SessionManagerClient::checkDocumentsSaved() )
87 /* oslSignalAction eToDo = */ osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR
, nullptr);
90 std::fprintf( stderr
, "X IO Error\n" );
91 std::fflush( stdout
);
92 std::fflush( stderr
);
94 /* #106197# the same reasons to use _exit instead of exit in salmain
95 * do apply here. Since there is nothing to be done after an XIO
96 * error we have to _exit immediately.
104 const struct timeval noyield_
= { 0, 0 };
105 const struct timeval yield_
= { 0, 10000 };
107 static const char* XRequest
[] = {
108 // see /usr/lib/X11/XErrorDB, /usr/openwin/lib/XErrorDB ...
111 "X_ChangeWindowAttributes",
112 "X_GetWindowAttributes",
114 "X_DestroySubwindows",
131 "X_SetSelectionOwner",
132 "X_GetSelectionOwner",
133 "X_ConvertSelection",
139 "X_ChangeActivePointerGrab",
157 "X_QueryTextExtents",
159 "X_ListFontsWithInfo",
168 "X_SetClipRectangles",
179 "X_PolyFillRectangle",
189 "X_CopyColormapAndFree",
191 "X_UninstallColormap",
192 "X_ListInstalledColormaps",
196 "X_AllocColorPlanes",
203 "X_CreateGlyphCursor",
209 "X_ChangeKeyboardMapping",
210 "X_GetKeyboardMapping",
211 "X_ChangeKeyboardControl",
212 "X_GetKeyboardControl",
214 "X_ChangePointerControl",
215 "X_GetPointerControl",
220 "X_SetAccessControl",
221 "X_SetCloseDownMode",
223 "X_RotateProperties",
224 "X_ForceScreenSaver",
225 "X_SetPointerMapping",
226 "X_GetPointerMapping",
227 "X_SetModifierMapping",
228 "X_GetModifierMapping",
239 X11SalData::X11SalData( GenericUnixSalDataType t
, SalInstance
*pInstance
)
240 : GenericUnixSalData( t
, pInstance
)
244 m_aOrigXIOErrorHandler
= XSetIOErrorHandler ( XIOErrorHdl
);
245 PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) );
248 X11SalData::~X11SalData()
252 XSetIOErrorHandler (m_aOrigXIOErrorHandler
);
255 void X11SalData::Dispose()
258 SetSalData( nullptr );
261 void X11SalData::DeleteDisplay()
264 SetDisplay( nullptr );
268 void X11SalData::Init()
270 pXLib_
.reset(new SalXLib());
274 void X11SalData::ErrorTrapPush()
276 PushXErrorLevel( true );
279 bool X11SalData::ErrorTrapPop( bool bIgnoreError
)
283 err
= HasXErrorOccurred();
284 ResetXErrorOccurred();
289 void X11SalData::PushXErrorLevel( bool bIgnore
)
291 m_aXErrorHandlerStack
.emplace_back( );
292 XErrorStackEntry
& rEnt
= m_aXErrorHandlerStack
.back();
294 rEnt
.m_bIgnore
= bIgnore
;
295 rEnt
.m_aHandler
= XSetErrorHandler( XErrorHdl
);
298 void X11SalData::PopXErrorLevel()
300 if( !m_aXErrorHandlerStack
.empty() )
302 XSetErrorHandler( m_aXErrorHandlerStack
.back().m_aHandler
);
303 m_aXErrorHandlerStack
.pop_back();
309 m_aTimeout
.tv_sec
= 0;
310 m_aTimeout
.tv_usec
= 0;
314 FD_ZERO( &aReadFDS_
);
315 FD_ZERO( &aExceptionFDS_
);
317 m_pInputMethod
= nullptr;
318 m_pDisplay
= nullptr;
320 m_pTimeoutFDS
[0] = m_pTimeoutFDS
[1] = -1;
321 if (pipe (m_pTimeoutFDS
) == -1)
324 // initialize 'wakeup' pipe.
327 // set close-on-exec descriptor flag.
328 if ((flags
= fcntl (m_pTimeoutFDS
[0], F_GETFD
)) != -1)
331 (void)fcntl(m_pTimeoutFDS
[0], F_SETFD
, flags
);
333 if ((flags
= fcntl (m_pTimeoutFDS
[1], F_GETFD
)) != -1)
336 (void)fcntl(m_pTimeoutFDS
[1], F_SETFD
, flags
);
339 // set non-blocking I/O flag.
340 if ((flags
= fcntl (m_pTimeoutFDS
[0], F_GETFL
)) != -1)
343 (void)fcntl(m_pTimeoutFDS
[0], F_SETFL
, flags
);
345 if ((flags
= fcntl (m_pTimeoutFDS
[1], F_GETFL
)) != -1)
348 (void)fcntl(m_pTimeoutFDS
[1], F_SETFL
, flags
);
351 // insert [0] into read descriptor set.
352 FD_SET( m_pTimeoutFDS
[0], &aReadFDS_
);
353 nFDs_
= m_pTimeoutFDS
[0] + 1;
358 // close 'wakeup' pipe.
359 close (m_pTimeoutFDS
[0]);
360 close (m_pTimeoutFDS
[1]);
362 m_pInputMethod
.reset();
365 static Display
*OpenX11Display(OString
& rDisplay
)
368 * open connection to X11 Display
370 * o -display command line parameter,
371 * o $DISPLAY environment variable
375 Display
*pDisp
= nullptr;
377 // is there a -display command line parameter?
379 sal_uInt32 nParams
= osl_getCommandArgCount();
381 for (sal_uInt32 i
=0; i
<nParams
; i
++)
383 osl_getCommandArg(i
, &aParam
.pData
);
384 if ( aParam
== "-display" )
386 osl_getCommandArg(i
+1, &aParam
.pData
);
387 rDisplay
= OUStringToOString(
388 aParam
, osl_getThreadTextEncoding());
390 if ((pDisp
= XOpenDisplay(rDisplay
.getStr()))!=nullptr)
393 * if a -display switch was used, we need
394 * to set the environment accordingly since
395 * the clipboard build another connection
396 * to the xserver using $DISPLAY
398 OUString
envVar("DISPLAY");
399 osl_setEnvironment(envVar
.pData
, aParam
.pData
);
405 if (!pDisp
&& rDisplay
.isEmpty())
407 // Open $DISPLAY or default...
408 char *pDisplay
= getenv("DISPLAY");
409 if (pDisplay
!= nullptr)
410 rDisplay
= OString(pDisplay
);
411 pDisp
= XOpenDisplay(pDisplay
);
419 m_pInputMethod
.reset( new SalI18N_InputMethod
);
420 m_pInputMethod
->SetLocale();
424 m_pDisplay
= OpenX11Display(aDisplay
);
429 OUString aProgramFileURL
;
430 osl_getExecutableFile( &aProgramFileURL
.pData
);
431 OUString aProgramSystemPath
;
432 osl_getSystemPathFromFileURL (aProgramFileURL
.pData
, &aProgramSystemPath
.pData
);
433 OString aProgramName
= OUStringToOString(
435 osl_getThreadTextEncoding() );
436 std::fprintf( stderr
, "%s X11 error: Can't open display: %s\n",
437 aProgramName
.getStr(), aDisplay
.getStr());
438 std::fprintf( stderr
, " Set DISPLAY environment variable, use -display option\n");
439 std::fprintf( stderr
, " or check permissions of your X-Server\n");
440 std::fprintf( stderr
, " (See \"man X\" resp. \"man xhost\" for details)\n");
441 std::fflush( stderr
);
447 static void EmitFontpathWarning()
449 static Bool bOnce
= False
;
453 std::fprintf( stderr
, "Please verify your fontpath settings\n"
454 "\t(See \"man xset\" for details"
455 " or ask your system administrator)\n" );
461 static void PrintXError( Display
*pDisplay
, XErrorEvent
*pEvent
)
463 char msg
[ 120 ] = "";
464 XGetErrorText( pDisplay
, pEvent
->error_code
, msg
, sizeof( msg
) );
465 std::fprintf( stderr
, "X-Error: %s\n", msg
);
466 if( pEvent
->request_code
< SAL_N_ELEMENTS( XRequest
) )
468 const char* pName
= XRequest
[pEvent
->request_code
];
470 pName
= "BadRequest?";
471 std::fprintf( stderr
, "\tMajor opcode: %d (%s)\n", pEvent
->request_code
, pName
);
475 std::fprintf( stderr
, "\tMajor opcode: %d\n", pEvent
->request_code
);
476 // TODO: also display extension name?
477 std::fprintf( stderr
, "\tMinor opcode: %d\n", pEvent
->minor_code
);
480 std::fprintf( stderr
, "\tResource ID: 0x%lx\n",
481 pEvent
->resourceid
);
482 std::fprintf( stderr
, "\tSerial No: %ld (%ld)\n",
483 pEvent
->serial
, LastKnownRequestProcessed(pDisplay
) );
485 if( !getenv( "SAL_SYNCHRONIZE" ) )
487 std::fprintf( stderr
, "These errors are reported asynchronously,\n");
488 std::fprintf( stderr
, "set environment variable SAL_SYNCHRONIZE to 1 to help debugging\n");
491 std::fflush( stdout
);
492 std::fflush( stderr
);
495 void X11SalData::XError( Display
*pDisplay
, XErrorEvent
*pEvent
)
497 if( ! m_aXErrorHandlerStack
.back().m_bIgnore
)
499 if ( (pEvent
->error_code
== BadAlloc
)
500 && (pEvent
->request_code
== X_OpenFont
) )
502 static Bool bOnce
= False
;
505 std::fprintf(stderr
, "X-Error occurred in a request for X_OpenFont\n");
506 EmitFontpathWarning();
513 * X_SetInputFocus: it's a hint only anyway
514 * X_GetProperty: this is part of the XGetWindowProperty call and will
515 * be handled by the return value of that function
517 else if( pEvent
->request_code
== X_SetInputFocus
||
518 pEvent
->request_code
== X_GetProperty
522 if( pDisplay
!= vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() )
525 PrintXError( pDisplay
, pEvent
);
527 oslSignalAction eToDo
= osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR
, nullptr);
530 case osl_Signal_ActIgnore
:
532 case osl_Signal_ActAbortApp
:
534 case osl_Signal_ActKillApp
:
536 case osl_Signal_ActCallNextHdl
:
544 m_aXErrorHandlerStack
.back().m_bWas
= true;
547 void X11SalData::Timeout()
549 ImplSVData
* pSVData
= ImplGetSVData();
550 if( pSVData
->maSchedCtx
.mpSalTimer
)
551 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
558 int fd
; // file descriptor for reading
559 void* data
; // data for predicate and callback
560 YieldFunc pending
; // predicate (determines pending events)
561 YieldFunc queued
; // read and queue up events
562 YieldFunc handle
; // handle pending events
564 int HasPendingEvent() const { return pending( fd
, data
); }
565 int IsEventQueued() const { return queued( fd
, data
); }
566 void HandleNextEvent() const { handle( fd
, data
); }
571 #define MAX_NUM_DESCRIPTORS 128
573 static YieldEntry yieldTable
[ MAX_NUM_DESCRIPTORS
];
575 void SalXLib::Insert( int nFD
, void* data
,
580 SAL_WARN_IF( !nFD
, "vcl", "can not insert stdin descriptor" );
581 SAL_WARN_IF( yieldTable
[nFD
].fd
, "vcl", "SalXLib::Insert fd twice" );
583 yieldTable
[nFD
].fd
= nFD
;
584 yieldTable
[nFD
].data
= data
;
585 yieldTable
[nFD
].pending
= pending
;
586 yieldTable
[nFD
].queued
= queued
;
587 yieldTable
[nFD
].handle
= handle
;
589 FD_SET( nFD
, &aReadFDS_
);
590 FD_SET( nFD
, &aExceptionFDS_
);
596 void SalXLib::Remove( int nFD
)
598 FD_CLR( nFD
, &aReadFDS_
);
599 FD_CLR( nFD
, &aExceptionFDS_
);
601 yieldTable
[nFD
].fd
= 0;
605 for ( nFD
= nFDs_
- 1;
606 nFD
>= 0 && !yieldTable
[nFD
].fd
;
613 bool SalXLib::CheckTimeout( bool bExecuteTimers
)
616 if( m_aTimeout
.tv_sec
) // timer is started
619 gettimeofday( &aTimeOfDay
, nullptr );
620 if( aTimeOfDay
>= m_aTimeout
)
625 // timed out, update timeout
626 m_aTimeout
= aTimeOfDay
;
628 * #107827# autorestart immediately, will be stopped (or set
629 * to different value in notify hdl if necessary;
630 * CheckTimeout should return false while
631 * timers are being dispatched.
633 m_aTimeout
+= m_nTimeoutMS
;
635 X11SalData::Timeout();
643 SalXLib::Yield( bool bWait
, bool bHandleAllCurrentEvents
)
645 // check for timeouts here if you want to make screenshots
646 static char* p_prioritize_timer
= getenv ("SAL_HIGHPRIORITY_REPAINT");
647 bool bHandledEvent
= false;
648 if (p_prioritize_timer
!= nullptr)
649 bHandledEvent
= CheckTimeout();
651 const int nMaxEvents
= bHandleAllCurrentEvents
? 100 : 1;
653 // first, check for already queued events.
654 for ( int nFD
= 0; nFD
< nFDs_
; nFD
++ )
656 YieldEntry
* pEntry
= &(yieldTable
[nFD
]);
659 SAL_WARN_IF( nFD
!= pEntry
->fd
, "vcl", "wrong fd in Yield()" );
660 for( int i
= 0; i
< nMaxEvents
&& pEntry
->HasPendingEvent(); i
++ )
662 pEntry
->HandleNextEvent();
663 if( ! bHandleAllCurrentEvents
)
671 // next, select with or without timeout according to bWait.
673 fd_set ReadFDS
= aReadFDS_
;
674 fd_set ExceptionFDS
= aExceptionFDS_
;
677 timeval Timeout
= noyield_
;
678 timeval
*pTimeout
= &Timeout
;
684 if (m_aTimeout
.tv_sec
) // Timer is started.
686 // determine remaining timeout.
687 gettimeofday (&Timeout
, nullptr);
688 Timeout
= m_aTimeout
- Timeout
;
689 if (yield_
>= Timeout
)
691 // guard against micro timeout.
699 // release YieldMutex (and re-acquire at block end)
700 SolarMutexReleaser aReleaser
;
701 nFound
= select( nFDs
, &ReadFDS
, nullptr, &ExceptionFDS
, pTimeout
);
703 if( nFound
< 0 ) // error
706 SAL_INFO("vcl.app", "SalXLib::Yield e=" << errno
<< " f=" << nFound
);
714 // usually handle timeouts here (as in 5.2)
715 if (p_prioritize_timer
== nullptr)
716 bHandledEvent
= CheckTimeout() || bHandledEvent
;
718 // handle wakeup events.
719 if ((nFound
> 0) && FD_ISSET(m_pTimeoutFDS
[0], &ReadFDS
))
722 while (read (m_pTimeoutFDS
[0], &buffer
, sizeof(buffer
)) > 0)
727 // handle other events.
730 // now we are in the protected section !
731 // recall select if we have acquired fd's, ready for reading,
733 struct timeval noTimeout
= { 0, 0 };
734 nFound
= select( nFDs_
, &ReadFDS
, nullptr,
735 &ExceptionFDS
, &noTimeout
);
737 // someone-else has done the job for us
743 for ( int nFD
= 0; nFD
< nFDs_
; nFD
++ )
745 YieldEntry
* pEntry
= &(yieldTable
[nFD
]);
748 if ( FD_ISSET( nFD
, &ExceptionFDS
) ) {
749 #if OSL_DEBUG_LEVEL > 1
750 SAL_WARN("vcl.app", "SalXLib::Yield exception.");
754 if ( FD_ISSET( nFD
, &ReadFDS
) )
756 for( int i
= 0; pEntry
->IsEventQueued() && i
< nMaxEvents
; i
++ )
758 pEntry
->HandleNextEvent();
759 bHandledEvent
= true;
760 // if a recursive call has done the job
769 return bHandledEvent
;
772 void SalXLib::Wakeup()
774 OSL_VERIFY(write (m_pTimeoutFDS
[1], "", 1) == 1);
777 void SalXLib::TriggerUserEventProcessing()
782 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */