nss: upgrade to release 3.73
[LibreOffice.git] / vcl / unx / generic / app / saldata.cxx
blobc5a97ee4d5cdc69c9ff68c5d0bc646c274965148
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <unistd.h>
21 #include <fcntl.h>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <errno.h>
26 #ifdef SUN
27 #include <sys/systeminfo.h>
28 #endif
29 #ifdef AIX
30 #include <strings.h>
31 #endif
32 #ifdef FREEBSD
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #endif
37 #include <osl/process.h>
39 #include <unx/saldisp.hxx>
40 #include <unx/saldata.hxx>
41 #include <unx/salunxtime.h>
42 #include <unx/sm.hxx>
43 #include <unx/i18n_im.hxx>
45 #include <X11/Xlib.h>
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);
64 return p2;
67 extern "C" {
69 static int XErrorHdl( Display *pDisplay, XErrorEvent *pEvent )
71 GetX11SalData()->XError( pDisplay, pEvent );
72 return 0;
75 static int XIOErrorHdl( Display * )
77 if ( Application::IsMainThread() )
79 /* #106197# hack: until a real shutdown procedure exists
80 * _exit ASAP
82 if( ImplGetSVData()->maAppData.mbAppQuit )
83 _exit(1);
85 // really bad hack
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.
98 _exit(1);
99 return 0;
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 ...
109 nullptr,
110 "X_CreateWindow",
111 "X_ChangeWindowAttributes",
112 "X_GetWindowAttributes",
113 "X_DestroyWindow",
114 "X_DestroySubwindows",
115 "X_ChangeSaveSet",
116 "X_ReparentWindow",
117 "X_MapWindow",
118 "X_MapSubwindows",
119 "X_UnmapWindow",
120 "X_UnmapSubwindows",
121 "X_ConfigureWindow",
122 "X_CirculateWindow",
123 "X_GetGeometry",
124 "X_QueryTree",
125 "X_InternAtom",
126 "X_GetAtomName",
127 "X_ChangeProperty",
128 "X_DeleteProperty",
129 "X_GetProperty",
130 "X_ListProperties",
131 "X_SetSelectionOwner",
132 "X_GetSelectionOwner",
133 "X_ConvertSelection",
134 "X_SendEvent",
135 "X_GrabPointer",
136 "X_UngrabPointer",
137 "X_GrabButton",
138 "X_UngrabButton",
139 "X_ChangeActivePointerGrab",
140 "X_GrabKeyboard",
141 "X_UngrabKeyboard",
142 "X_GrabKey",
143 "X_UngrabKey",
144 "X_AllowEvents",
145 "X_GrabServer",
146 "X_UngrabServer",
147 "X_QueryPointer",
148 "X_GetMotionEvents",
149 "X_TranslateCoords",
150 "X_WarpPointer",
151 "X_SetInputFocus",
152 "X_GetInputFocus",
153 "X_QueryKeymap",
154 "X_OpenFont",
155 "X_CloseFont",
156 "X_QueryFont",
157 "X_QueryTextExtents",
158 "X_ListFonts",
159 "X_ListFontsWithInfo",
160 "X_SetFontPath",
161 "X_GetFontPath",
162 "X_CreatePixmap",
163 "X_FreePixmap",
164 "X_CreateGC",
165 "X_ChangeGC",
166 "X_CopyGC",
167 "X_SetDashes",
168 "X_SetClipRectangles",
169 "X_FreeGC",
170 "X_ClearArea",
171 "X_CopyArea",
172 "X_CopyPlane",
173 "X_PolyPoint",
174 "X_PolyLine",
175 "X_PolySegment",
176 "X_PolyRectangle",
177 "X_PolyArc",
178 "X_FillPoly",
179 "X_PolyFillRectangle",
180 "X_PolyFillArc",
181 "X_PutImage",
182 "X_GetImage",
183 "X_PolyText8",
184 "X_PolyText16",
185 "X_ImageText8",
186 "X_ImageText16",
187 "X_CreateColormap",
188 "X_FreeColormap",
189 "X_CopyColormapAndFree",
190 "X_InstallColormap",
191 "X_UninstallColormap",
192 "X_ListInstalledColormaps",
193 "X_AllocColor",
194 "X_AllocNamedColor",
195 "X_AllocColorCells",
196 "X_AllocColorPlanes",
197 "X_FreeColors",
198 "X_StoreColors",
199 "X_StoreNamedColor",
200 "X_QueryColors",
201 "X_LookupColor",
202 "X_CreateCursor",
203 "X_CreateGlyphCursor",
204 "X_FreeCursor",
205 "X_RecolorCursor",
206 "X_QueryBestSize",
207 "X_QueryExtension",
208 "X_ListExtensions",
209 "X_ChangeKeyboardMapping",
210 "X_GetKeyboardMapping",
211 "X_ChangeKeyboardControl",
212 "X_GetKeyboardControl",
213 "X_Bell",
214 "X_ChangePointerControl",
215 "X_GetPointerControl",
216 "X_SetScreenSaver",
217 "X_GetScreenSaver",
218 "X_ChangeHosts",
219 "X_ListHosts",
220 "X_SetAccessControl",
221 "X_SetCloseDownMode",
222 "X_KillClient",
223 "X_RotateProperties",
224 "X_ForceScreenSaver",
225 "X_SetPointerMapping",
226 "X_GetPointerMapping",
227 "X_SetModifierMapping",
228 "X_GetModifierMapping",
229 nullptr,
230 nullptr,
231 nullptr,
232 nullptr,
233 nullptr,
234 nullptr,
235 nullptr,
236 "X_NoOperation"
239 X11SalData::X11SalData( GenericUnixSalDataType t, SalInstance *pInstance )
240 : GenericUnixSalData( t, pInstance )
242 pXLib_ = nullptr;
244 m_aOrigXIOErrorHandler = XSetIOErrorHandler ( XIOErrorHdl );
245 PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) );
248 X11SalData::~X11SalData()
250 DeleteDisplay();
251 PopXErrorLevel();
252 XSetIOErrorHandler (m_aOrigXIOErrorHandler);
255 void X11SalData::Dispose()
257 delete GetDisplay();
258 SetSalData( nullptr );
261 void X11SalData::DeleteDisplay()
263 delete GetDisplay();
264 SetDisplay( nullptr );
265 pXLib_.reset();
268 void X11SalData::Init()
270 pXLib_.reset(new SalXLib());
271 pXLib_->Init();
274 void X11SalData::ErrorTrapPush()
276 PushXErrorLevel( true );
279 bool X11SalData::ErrorTrapPop( bool bIgnoreError )
281 bool err = false;
282 if( !bIgnoreError )
283 err = HasXErrorOccurred();
284 ResetXErrorOccurred();
285 PopXErrorLevel();
286 return err;
289 void X11SalData::PushXErrorLevel( bool bIgnore )
291 m_aXErrorHandlerStack.emplace_back( );
292 XErrorStackEntry& rEnt = m_aXErrorHandlerStack.back();
293 rEnt.m_bWas = false;
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();
307 SalXLib::SalXLib()
309 m_aTimeout.tv_sec = 0;
310 m_aTimeout.tv_usec = 0;
311 m_nTimeoutMS = 0;
313 nFDs_ = 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)
322 return;
324 // initialize 'wakeup' pipe.
325 int flags;
327 // set close-on-exec descriptor flag.
328 if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFD)) != -1)
330 flags |= FD_CLOEXEC;
331 (void)fcntl(m_pTimeoutFDS[0], F_SETFD, flags);
333 if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFD)) != -1)
335 flags |= FD_CLOEXEC;
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)
342 flags |= O_NONBLOCK;
343 (void)fcntl(m_pTimeoutFDS[0], F_SETFL, flags);
345 if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFL)) != -1)
347 flags |= O_NONBLOCK;
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;
356 SalXLib::~SalXLib()
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
369 * try in this order:
370 * o -display command line parameter,
371 * o $DISPLAY environment variable
372 * o default display
375 Display *pDisp = nullptr;
377 // is there a -display command line parameter?
379 sal_uInt32 nParams = osl_getCommandArgCount();
380 OUString aParam;
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);
401 break;
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);
414 return pDisp;
417 void SalXLib::Init()
419 m_pInputMethod.reset( new SalI18N_InputMethod );
420 m_pInputMethod->SetLocale();
421 XrmInitialize();
423 OString aDisplay;
424 m_pDisplay = OpenX11Display(aDisplay);
426 if ( m_pDisplay )
427 return;
429 OUString aProgramFileURL;
430 osl_getExecutableFile( &aProgramFileURL.pData );
431 OUString aProgramSystemPath;
432 osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData);
433 OString aProgramName = OUStringToOString(
434 aProgramSystemPath,
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 );
442 exit(0);
446 extern "C" {
447 static void EmitFontpathWarning()
449 static Bool bOnce = False;
450 if ( !bOnce )
452 bOnce = True;
453 std::fprintf( stderr, "Please verify your fontpath settings\n"
454 "\t(See \"man xset\" for details"
455 " or ask your system administrator)\n" );
459 } /* extern "C" */
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];
469 if( !pName )
470 pName = "BadRequest?";
471 std::fprintf( stderr, "\tMajor opcode: %d (%s)\n", pEvent->request_code, pName );
473 else
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;
503 if ( !bOnce )
505 std::fprintf(stderr, "X-Error occurred in a request for X_OpenFont\n");
506 EmitFontpathWarning();
508 bOnce = True ;
510 return;
512 /* ignore
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
520 return;
522 if( pDisplay != vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() )
523 return;
525 PrintXError( pDisplay, pEvent );
527 oslSignalAction eToDo = osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, nullptr);
528 switch (eToDo)
530 case osl_Signal_ActIgnore :
531 return;
532 case osl_Signal_ActAbortApp :
533 abort();
534 case osl_Signal_ActKillApp :
535 exit(0);
536 case osl_Signal_ActCallNextHdl :
537 break;
538 default :
539 break;
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();
554 namespace {
556 struct YieldEntry
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,
576 YieldFunc pending,
577 YieldFunc queued,
578 YieldFunc handle )
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_ );
592 if( nFD >= nFDs_ )
593 nFDs_ = nFD + 1;
596 void SalXLib::Remove( int nFD )
598 FD_CLR( nFD, &aReadFDS_ );
599 FD_CLR( nFD, &aExceptionFDS_ );
601 yieldTable[nFD].fd = 0;
603 if ( nFD == nFDs_ )
605 for ( nFD = nFDs_ - 1;
606 nFD >= 0 && !yieldTable[nFD].fd;
607 nFD-- ) ;
609 nFDs_ = nFD + 1;
613 bool SalXLib::CheckTimeout( bool bExecuteTimers )
615 bool bRet = false;
616 if( m_aTimeout.tv_sec ) // timer is started
618 timeval aTimeOfDay;
619 gettimeofday( &aTimeOfDay, nullptr );
620 if( aTimeOfDay >= m_aTimeout )
622 bRet = true;
623 if( bExecuteTimers )
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;
634 // notify
635 X11SalData::Timeout();
639 return bRet;
642 bool
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]);
657 if ( pEntry->fd )
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 )
665 return true;
671 // next, select with or without timeout according to bWait.
672 int nFDs = nFDs_;
673 fd_set ReadFDS = aReadFDS_;
674 fd_set ExceptionFDS = aExceptionFDS_;
675 int nFound = 0;
677 timeval Timeout = noyield_;
678 timeval *pTimeout = &Timeout;
681 if (bWait)
683 pTimeout = nullptr;
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.
692 Timeout = yield_;
694 pTimeout = &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
705 #ifdef DBG_UTIL
706 SAL_INFO("vcl.app", "SalXLib::Yield e=" << errno << " f=" << nFound);
707 #endif
708 if( EINTR == errno )
710 errno = 0;
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))
721 int buffer;
722 while (read (m_pTimeoutFDS[0], &buffer, sizeof(buffer)) > 0)
723 continue;
724 nFound -= 1;
727 // handle other events.
728 if( nFound > 0 )
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
738 if (nFound == 0)
740 return false;
743 for ( int nFD = 0; nFD < nFDs_; nFD++ )
745 YieldEntry* pEntry = &(yieldTable[nFD]);
746 if ( pEntry->fd )
748 if ( FD_ISSET( nFD, &ExceptionFDS ) ) {
749 #if OSL_DEBUG_LEVEL > 1
750 SAL_WARN("vcl.app", "SalXLib::Yield exception.");
751 #endif
752 nFound--;
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
761 // so abort here
763 nFound--;
769 return bHandledEvent;
772 void SalXLib::Wakeup()
774 OSL_VERIFY(write (m_pTimeoutFDS[1], "", 1) == 1);
777 void SalXLib::TriggerUserEventProcessing()
779 Wakeup();
782 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */