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 .
30 #include <sys/resource.h>
32 #include <sys/systeminfo.h>
38 #include <sys/types.h>
42 #include <osl/process.h>
43 #include <osl/mutex.hxx>
45 #include "unx/saldisp.hxx"
46 #include "unx/saldata.hxx"
47 #include "unx/salframe.h"
49 #include "unx/i18n_im.hxx"
50 #include "unx/i18n_xkb.hxx"
51 #include "unx/x11/x11display.hxx"
52 #include "salinst.hxx"
54 #include <osl/signal.h>
55 #include <osl/thread.h>
56 #include <rtl/strbuf.hxx>
57 #include <rtl/bootstrap.hxx>
59 #include <tools/debug.hxx>
60 #include <vcl/svapp.hxx>
70 #define SIGIOT SIGABRT
74 X11SalData
* GetX11SalData()
76 SalData
* p1
= ImplGetSVData()->mpSalData
;
78 X11SalData
* p2
= dynamic_cast< X11SalData
* >(p1
);
85 static int XErrorHdl( Display
*pDisplay
, XErrorEvent
*pEvent
)
87 GetX11SalData()->XError( pDisplay
, pEvent
);
91 static int XIOErrorHdl( Display
* )
93 if (::osl::Thread::getCurrentIdentifier() == Application::GetMainThreadIdentifier())
95 /* #106197# hack: until a real shutdown procedure exists
98 if( ImplGetSVData()->maAppData
.mbAppQuit
)
102 if( ! SessionManagerClient::checkDocumentsSaved() )
103 /* oslSignalAction eToDo = */ osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR
, NULL
);
106 std::fprintf( stderr
, "X IO Error\n" );
107 std::fflush( stdout
);
108 std::fflush( stderr
);
110 /* #106197# the same reasons to use _exit instead of exit in salmain
111 * do apply here. Since there is nothing to be done after an XIO
112 * error we have to _exit immediately.
120 static const struct timeval noyield__
= { 0, 0 };
121 static const struct timeval yield__
= { 0, 10000 };
123 static const char* XRequest
[] = {
124 // see /usr/lib/X11/XErrorDB, /usr/openwin/lib/XErrorDB ...
127 "X_ChangeWindowAttributes",
128 "X_GetWindowAttributes",
130 "X_DestroySubwindows",
147 "X_SetSelectionOwner",
148 "X_GetSelectionOwner",
149 "X_ConvertSelection",
155 "X_ChangeActivePointerGrab",
173 "X_QueryTextExtents",
175 "X_ListFontsWithInfo",
184 "X_SetClipRectangles",
195 "X_PolyFillRectangle",
205 "X_CopyColormapAndFree",
207 "X_UninstallColormap",
208 "X_ListInstalledColormaps",
212 "X_AllocColorPlanes",
219 "X_CreateGlyphCursor",
225 "X_ChangeKeyboardMapping",
226 "X_GetKeyboardMapping",
227 "X_ChangeKeyboardControl",
228 "X_GetKeyboardControl",
230 "X_ChangePointerControl",
231 "X_GetPointerControl",
236 "X_SetAccessControl",
237 "X_SetCloseDownMode",
239 "X_RotateProperties",
240 "X_ForceScreenSaver",
241 "X_SetPointerMapping",
242 "X_GetPointerMapping",
243 "X_SetModifierMapping",
244 "X_GetModifierMapping",
255 X11SalData::X11SalData( SalGenericDataType t
, SalInstance
*pInstance
)
256 : SalGenericData( t
, pInstance
)
261 m_aOrigXIOErrorHandler
= XSetIOErrorHandler ( XIOErrorHdl
);
262 PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) );
265 X11SalData::~X11SalData()
269 XSetIOErrorHandler (m_aOrigXIOErrorHandler
);
272 void X11SalData::Dispose()
279 void X11SalData::DeleteDisplay()
287 void X11SalData::Init()
289 pXLib_
= new SalXLib();
293 void X11SalData::initNWF()
297 void X11SalData::deInitNWF()
301 void X11SalData::ErrorTrapPush()
303 PushXErrorLevel( true );
306 bool X11SalData::ErrorTrapPop( bool bIgnoreError
)
310 err
= HasXErrorOccurred();
311 ResetXErrorOccurred();
316 void X11SalData::PushXErrorLevel( bool bIgnore
)
318 m_aXErrorHandlerStack
.push_back( XErrorStackEntry() );
319 XErrorStackEntry
& rEnt
= m_aXErrorHandlerStack
.back();
321 rEnt
.m_bIgnore
= bIgnore
;
322 rEnt
.m_nLastErrorRequest
= 0;
323 rEnt
.m_aHandler
= XSetErrorHandler( XErrorHdl
);
326 void X11SalData::PopXErrorLevel()
328 if( m_aXErrorHandlerStack
.size() )
330 XSetErrorHandler( m_aXErrorHandlerStack
.back().m_aHandler
);
331 m_aXErrorHandlerStack
.pop_back();
336 : blockIdleTimeout( false )
338 m_aTimeout
.tv_sec
= 0;
339 m_aTimeout
.tv_usec
= 0;
343 FD_ZERO( &aReadFDS_
);
344 FD_ZERO( &aExceptionFDS_
);
346 m_pTimeoutFDS
[0] = m_pTimeoutFDS
[1] = -1;
347 if (pipe (m_pTimeoutFDS
) != -1)
349 // initialize 'wakeup' pipe.
352 // set close-on-exec descriptor flag.
353 if ((flags
= fcntl (m_pTimeoutFDS
[0], F_GETFD
)) != -1)
356 (void)fcntl(m_pTimeoutFDS
[0], F_SETFD
, flags
);
358 if ((flags
= fcntl (m_pTimeoutFDS
[1], F_GETFD
)) != -1)
361 (void)fcntl(m_pTimeoutFDS
[1], F_SETFD
, flags
);
364 // set non-blocking I/O flag.
365 if ((flags
= fcntl (m_pTimeoutFDS
[0], F_GETFL
)) != -1)
368 (void)fcntl(m_pTimeoutFDS
[0], F_SETFL
, flags
);
370 if ((flags
= fcntl (m_pTimeoutFDS
[1], F_GETFL
)) != -1)
373 (void)fcntl(m_pTimeoutFDS
[1], F_SETFL
, flags
);
376 // insert [0] into read descriptor set.
377 FD_SET( m_pTimeoutFDS
[0], &aReadFDS_
);
378 nFDs_
= m_pTimeoutFDS
[0] + 1;
384 // close 'wakeup' pipe.
385 close (m_pTimeoutFDS
[0]);
386 close (m_pTimeoutFDS
[1]);
391 SalI18N_InputMethod
* pInputMethod
= new SalI18N_InputMethod
;
392 pInputMethod
->SetLocale();
396 Display
*pDisp
= OpenX11Display(aDisplay
);
400 OUString aProgramFileURL
;
401 osl_getExecutableFile( &aProgramFileURL
.pData
);
402 OUString aProgramSystemPath
;
403 osl_getSystemPathFromFileURL (aProgramFileURL
.pData
, &aProgramSystemPath
.pData
);
404 OString aProgramName
= OUStringToOString(
406 osl_getThreadTextEncoding() );
407 std::fprintf( stderr
, "%s X11 error: Can't open display: %s\n",
408 aProgramName
.getStr(), aDisplay
.getStr());
409 std::fprintf( stderr
, " Set DISPLAY environment variable, use -display option\n");
410 std::fprintf( stderr
, " or check permissions of your X-Server\n");
411 std::fprintf( stderr
, " (See \"man X\" resp. \"man xhost\" for details)\n");
412 std::fflush( stderr
);
416 SalX11Display
*pSalDisplay
= new SalX11Display( pDisp
);
418 pInputMethod
->CreateMethod( pDisp
);
419 pSalDisplay
->SetupInput( pInputMethod
);
423 void EmitFontpathWarning()
425 static Bool bOnce
= False
;
429 std::fprintf( stderr
, "Please verify your fontpath settings\n"
430 "\t(See \"man xset\" for details"
431 " or ask your system administrator)\n" );
437 static void PrintXError( Display
*pDisplay
, XErrorEvent
*pEvent
)
439 char msg
[ 120 ] = "";
440 #if ! ( defined LINUX && defined PPC )
441 XGetErrorText( pDisplay
, pEvent
->error_code
, msg
, sizeof( msg
) );
443 std::fprintf( stderr
, "X-Error: %s\n", msg
);
444 if( pEvent
->request_code
< SAL_N_ELEMENTS( XRequest
) )
446 const char* pName
= XRequest
[pEvent
->request_code
];
448 pName
= "BadRequest?";
449 std::fprintf( stderr
, "\tMajor opcode: %d (%s)\n", pEvent
->request_code
, pName
);
453 std::fprintf( stderr
, "\tMajor opcode: %d\n", pEvent
->request_code
);
454 // TODO: also display extension name?
455 std::fprintf( stderr
, "\tMinor opcode: %d\n", pEvent
->minor_code
);
458 std::fprintf( stderr
, "\tResource ID: 0x%lx\n",
459 pEvent
->resourceid
);
460 std::fprintf( stderr
, "\tSerial No: %ld (%ld)\n",
461 pEvent
->serial
, LastKnownRequestProcessed(pDisplay
) );
463 if( !getenv( "SAL_SYNCHRONIZE" ) )
465 std::fprintf( stderr
, "These errors are reported asynchronously,\n");
466 std::fprintf( stderr
, "set environment variable SAL_SYNCHRONIZE to 1 to help debugging\n");
469 std::fflush( stdout
);
470 std::fflush( stderr
);
473 void X11SalData::XError( Display
*pDisplay
, XErrorEvent
*pEvent
)
475 if( ! m_aXErrorHandlerStack
.back().m_bIgnore
)
477 if ( (pEvent
->error_code
== BadAlloc
)
478 && (pEvent
->request_code
== X_OpenFont
) )
480 static Bool bOnce
= False
;
483 std::fprintf(stderr
, "X-Error occurred in a request for X_OpenFont\n");
484 EmitFontpathWarning();
491 * X_SetInputFocus: it's a hint only anyway
492 * X_GetProperty: this is part of the XGetWindowProperty call and will
493 * be handled by the return value of that function
495 else if( pEvent
->request_code
== X_SetInputFocus
||
496 pEvent
->request_code
== X_GetProperty
500 if( pDisplay
!= vcl_sal::getSalDisplay(GetGenericData())->GetDisplay() )
503 PrintXError( pDisplay
, pEvent
);
505 oslSignalAction eToDo
= osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR
, NULL
);
508 case osl_Signal_ActIgnore
:
510 case osl_Signal_ActAbortApp
:
512 case osl_Signal_ActKillApp
:
514 case osl_Signal_ActCallNextHdl
:
522 m_aXErrorHandlerStack
.back().m_bWas
= true;
527 YieldEntry
* next
; // pointer to next entry
528 int fd
; // file descriptor for reading
529 void* data
; // data for predicate and callback
530 YieldFunc pending
; // predicate (determins pending events)
531 YieldFunc queued
; // read and queue up events
532 YieldFunc handle
; // handle pending events
534 inline int HasPendingEvent() const { return pending( fd
, data
); }
535 inline int IsEventQueued() const { return queued( fd
, data
); }
536 inline void HandleNextEvent() const { handle( fd
, data
); }
539 #define MAX_NUM_DESCRIPTORS 128
541 static YieldEntry yieldTable
[ MAX_NUM_DESCRIPTORS
];
543 void SalXLib::Insert( int nFD
, void* data
,
548 DBG_ASSERT( nFD
, "can not insert stdin descriptor" );
549 DBG_ASSERT( !yieldTable
[nFD
].fd
, "SalXLib::Insert fd twice" );
551 yieldTable
[nFD
].fd
= nFD
;
552 yieldTable
[nFD
].data
= data
;
553 yieldTable
[nFD
].pending
= pending
;
554 yieldTable
[nFD
].queued
= queued
;
555 yieldTable
[nFD
].handle
= handle
;
557 FD_SET( nFD
, &aReadFDS_
);
558 FD_SET( nFD
, &aExceptionFDS_
);
564 void SalXLib::Remove( int nFD
)
566 FD_CLR( nFD
, &aReadFDS_
);
567 FD_CLR( nFD
, &aExceptionFDS_
);
569 yieldTable
[nFD
].fd
= 0;
573 for ( nFD
= nFDs_
- 1;
574 nFD
>= 0 && !yieldTable
[nFD
].fd
;
581 bool SalXLib::CheckTimeout( bool bExecuteTimers
)
584 if( m_aTimeout
.tv_sec
) // timer is started
587 gettimeofday( &aTimeOfDay
, 0 );
588 if( aTimeOfDay
>= m_aTimeout
)
593 // timed out, update timeout
594 m_aTimeout
= aTimeOfDay
;
596 * #107827# autorestart immediately, will be stopped (or set
597 * to different value in notify hdl if necessary;
598 * CheckTimeout should return false while
599 * timers are being dispatched.
601 m_aTimeout
+= m_nTimeoutMS
;
602 // Determine if the app is idle (for idle timers). If there's user input pending,
603 // if there's IO pending or if we're called inside a temporary yield (=blockIdleTimeout),
604 // then the app is not idle.
606 if( blockIdleTimeout
|| XPending( vcl_sal::getSalDisplay(GetGenericData())->GetDisplay()))
608 for ( int nFD
= 0; idle
&& nFD
< nFDs_
; nFD
++ )
610 YieldEntry
* pEntry
= &(yieldTable
[nFD
]);
611 if ( pEntry
->fd
&& pEntry
->HasPendingEvent())
615 X11SalData::Timeout( idle
);
622 void SalXLib::Yield( bool bWait
, bool bHandleAllCurrentEvents
)
624 blockIdleTimeout
= !bWait
;
625 // check for timeouts here if you want to make screenshots
626 static char* p_prioritize_timer
= getenv ("SAL_HIGHPRIORITY_REPAINT");
627 if (p_prioritize_timer
!= NULL
)
630 const int nMaxEvents
= bHandleAllCurrentEvents
? 100 : 1;
632 // first, check for already queued events.
633 for ( int nFD
= 0; nFD
< nFDs_
; nFD
++ )
635 YieldEntry
* pEntry
= &(yieldTable
[nFD
]);
638 DBG_ASSERT( nFD
== pEntry
->fd
, "wrong fd in Yield()" );
639 for( int i
= 0; i
< nMaxEvents
&& pEntry
->HasPendingEvent(); i
++ )
641 pEntry
->HandleNextEvent();
642 if( ! bHandleAllCurrentEvents
)
644 blockIdleTimeout
= false;
651 // next, select with or without timeout according to bWait.
653 fd_set ReadFDS
= aReadFDS_
;
654 fd_set ExceptionFDS
= aExceptionFDS_
;
657 timeval Timeout
= noyield__
;
658 timeval
*pTimeout
= &Timeout
;
663 if (m_aTimeout
.tv_sec
) // Timer is started.
665 // determine remaining timeout.
666 gettimeofday (&Timeout
, 0);
667 Timeout
= m_aTimeout
- Timeout
;
668 if (yield__
>= Timeout
)
670 // guard against micro timeout.
678 // release YieldMutex (and re-acquire at block end)
679 SalYieldMutexReleaser aReleaser
;
680 nFound
= select( nFDs
, &ReadFDS
, NULL
, &ExceptionFDS
, pTimeout
);
682 if( nFound
< 0 ) // error
685 std::fprintf( stderr
, "SalXLib::Yield e=%d f=%d\n", errno
, nFound
);
693 // usually handle timeouts here (as in 5.2)
694 if (p_prioritize_timer
== NULL
)
697 // handle wakeup events.
698 if ((nFound
> 0) && (FD_ISSET(m_pTimeoutFDS
[0], &ReadFDS
)))
701 while (read (m_pTimeoutFDS
[0], &buffer
, sizeof(buffer
)) > 0)
706 // handle other events.
709 // now we are in the protected section !
710 // recall select if we have acquired fd's, ready for reading,
712 struct timeval noTimeout
= { 0, 0 };
713 nFound
= select( nFDs_
, &ReadFDS
, NULL
,
714 &ExceptionFDS
, &noTimeout
);
716 // someone-else has done the job for us
719 blockIdleTimeout
= false;
723 for ( int nFD
= 0; nFD
< nFDs_
; nFD
++ )
725 YieldEntry
* pEntry
= &(yieldTable
[nFD
]);
728 if ( FD_ISSET( nFD
, &ExceptionFDS
) ) {
729 #if OSL_DEBUG_LEVEL > 1
730 std::fprintf( stderr
, "SalXLib::Yield exception\n" );
734 if ( FD_ISSET( nFD
, &ReadFDS
) )
736 for( int i
= 0; pEntry
->IsEventQueued() && i
< nMaxEvents
; i
++ )
738 pEntry
->HandleNextEvent();
739 // if a recursive call has done the job
747 blockIdleTimeout
= false;
750 void SalXLib::Wakeup()
752 OSL_VERIFY(write (m_pTimeoutFDS
[1], "", 1) == 1);
755 void SalXLib::PostUserEvent()
760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */