summaryrefslogtreecommitdiffstats
path: root/vcl/unx/generic/app
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/unx/generic/app
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/unx/generic/app')
-rw-r--r--vcl/unx/generic/app/gendata.cxx69
-rw-r--r--vcl/unx/generic/app/gendisp.cxx69
-rw-r--r--vcl/unx/generic/app/geninst.cxx99
-rw-r--r--vcl/unx/generic/app/gensys.cxx116
-rw-r--r--vcl/unx/generic/app/i18n_cb.cxx494
-rw-r--r--vcl/unx/generic/app/i18n_ic.cxx604
-rw-r--r--vcl/unx/generic/app/i18n_im.cxx410
-rw-r--r--vcl/unx/generic/app/i18n_keysym.cxx358
-rw-r--r--vcl/unx/generic/app/i18n_xkb.cxx107
-rw-r--r--vcl/unx/generic/app/keysymnames.cxx509
-rw-r--r--vcl/unx/generic/app/randrwrapper.cxx181
-rw-r--r--vcl/unx/generic/app/saldata.cxx775
-rw-r--r--vcl/unx/generic/app/saldisp.cxx2489
-rw-r--r--vcl/unx/generic/app/salinst.cxx253
-rw-r--r--vcl/unx/generic/app/saltimer.cxx68
-rw-r--r--vcl/unx/generic/app/sm.cxx857
-rw-r--r--vcl/unx/generic/app/wmadaptor.cxx2196
17 files changed, 9654 insertions, 0 deletions
diff --git a/vcl/unx/generic/app/gendata.cxx b/vcl/unx/generic/app/gendata.cxx
new file mode 100644
index 0000000000..79175217c8
--- /dev/null
+++ b/vcl/unx/generic/app/gendata.cxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifdef IOS
+#include <ios/iosinst.hxx>
+#endif
+
+#include <unx/gendata.hxx>
+
+#include <unx/fontmanager.hxx>
+
+#ifndef IOS
+
+#include <unx/glyphcache.hxx>
+#include <printerinfomanager.hxx>
+
+SalData::SalData() { SetSalData(this); }
+
+SalData::~SalData() {}
+
+#endif
+
+GenericUnixSalData::GenericUnixSalData()
+ : m_pDisplay(nullptr)
+{
+}
+
+GenericUnixSalData::~GenericUnixSalData()
+{
+#ifndef IOS
+ // at least for InitPrintFontManager the sequence is important
+ m_pPrintFontManager.reset();
+ m_pFreetypeManager.reset();
+ m_pPrinterInfoManager.reset();
+#endif
+}
+
+void GenericUnixSalData::Dispose() {}
+
+#ifndef IOS
+void GenericUnixSalData::InitFreetypeManager() { m_pFreetypeManager.reset(new FreetypeManager); }
+#endif
+
+void GenericUnixSalData::InitPrintFontManager()
+{
+#ifndef IOS
+ GetFreetypeManager();
+ m_pPrintFontManager.reset(new psp::PrintFontManager);
+ m_pPrintFontManager->initialize();
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/gendisp.cxx b/vcl/unx/generic/app/gendisp.cxx
new file mode 100644
index 0000000000..b1dbef3f5f
--- /dev/null
+++ b/vcl/unx/generic/app/gendisp.cxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <salframe.hxx>
+#include <unx/gendisp.hxx>
+
+SalGenericDisplay::SalGenericDisplay()
+{
+ m_pCapture = nullptr;
+}
+
+SalGenericDisplay::~SalGenericDisplay()
+{
+}
+
+void SalGenericDisplay::registerFrame( SalFrame* pFrame )
+{
+ insertFrame( pFrame );
+}
+
+void SalGenericDisplay::deregisterFrame( SalFrame* pFrame )
+{
+ eraseFrame( pFrame );
+}
+
+void SalGenericDisplay::emitDisplayChanged()
+{
+ SalFrame *pAnyFrame = anyFrame();
+ if( pAnyFrame )
+ pAnyFrame->CallCallback( SalEvent::DisplayChanged, nullptr );
+}
+
+bool SalGenericDisplay::DispatchInternalEvent( bool bHandleAllCurrentEvent )
+{
+ return DispatchUserEvents( bHandleAllCurrentEvent );
+}
+
+void SalGenericDisplay::SendInternalEvent( SalFrame* pFrame, void* pData, SalEvent nEvent )
+{
+ PostEvent( pFrame, pData, nEvent );
+}
+
+void SalGenericDisplay::CancelInternalEvent( SalFrame* pFrame, void* pData, SalEvent nEvent )
+{
+ RemoveEvent( pFrame, pData, nEvent );
+}
+
+void SalGenericDisplay::ProcessEvent( SalUserEvent aEvent )
+{
+ aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/geninst.cxx b/vcl/unx/generic/app/geninst.cxx
new file mode 100644
index 0000000000..191d87ca76
--- /dev/null
+++ b/vcl/unx/generic/app/geninst.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#if defined(LINUX)
+# include <stdio.h>
+#endif
+#if defined(__FreeBSD__)
+# include <sys/utsname.h>
+#endif
+
+#include <config_features.h>
+#if HAVE_FEATURE_OPENGL
+#include <vcl/opengl/OpenGLContext.hxx>
+#endif
+#include <unx/geninst.h>
+#include <o3tl/string_view.hxx>
+
+// SalYieldMutex
+
+SalYieldMutex::SalYieldMutex()
+{
+#if HAVE_FEATURE_OPENGL
+ SetBeforeReleaseHandler( &OpenGLContext::prepareForYield );
+#endif
+}
+
+SalYieldMutex::~SalYieldMutex()
+{
+}
+
+SalGenericInstance::~SalGenericInstance()
+{
+}
+
+OUString SalGenericInstance::getOSVersion()
+{
+ OUString aKernelVer = "unknown";
+#if defined(LINUX)
+ FILE* pVersion = fopen( "/proc/version", "r" );
+ if ( pVersion )
+ {
+ char aVerBuffer[512];
+ if ( fgets ( aVerBuffer, 511, pVersion ) )
+ {
+ aKernelVer = OUString::createFromAscii( aVerBuffer );
+ // "Linux version 3.16.7-29-desktop ..."
+ std::u16string_view aVers = o3tl::getToken(aKernelVer, 2, ' ' );
+ // "3.16.7-29-desktop ..."
+ size_t nTooDetailed = aVers.find( '.', 2);
+ if (nTooDetailed < 1 || nTooDetailed > 8)
+ aKernelVer = "Linux (misparsed version)";
+ else // "3.16.7-29-desktop ..."
+ aKernelVer = OUString::Concat("Linux ") + aVers.substr(0, nTooDetailed);
+ }
+ fclose( pVersion );
+ }
+#elif defined(__FreeBSD__)
+ struct utsname stName;
+ if ( uname( &stName ) != 0 )
+ return aKernelVer;
+
+ sal_Int32 nDots = 0;
+ sal_Int32 nIndex = 0;
+ aKernelVer = OUString::createFromAscii( stName.release );
+ while ( nIndex++ < aKernelVer.getLength() )
+ {
+ const char c = stName.release[ nIndex ];
+ if ( c == ' ' || c == '-' || ( c == '.' && nDots++ > 0 ) )
+ break;
+ }
+ aKernelVer = OUString::createFromAscii(stName.sysname) + " " + aKernelVer.copy(0, nIndex);
+#elif defined(EMSCRIPTEN)
+#define str(s) #s
+#define xstr(s) str(s)
+ aKernelVer = "Emscripten " xstr(__EMSCRIPTEN_major__)
+ "." xstr(__EMSCRIPTEN_minor__) "." xstr(__EMSCRIPTEN_tiny__);
+#endif
+ return aKernelVer;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/gensys.cxx b/vcl/unx/generic/app/gensys.cxx
new file mode 100644
index 0000000000..98371c5484
--- /dev/null
+++ b/vcl/unx/generic/app/gensys.cxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <unx/gensys.h>
+
+#include <svdata.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <unotools/configmgr.hxx>
+
+using namespace com::sun::star;
+
+SalGenericSystem::SalGenericSystem()
+{
+}
+
+SalGenericSystem::~SalGenericSystem()
+{
+}
+
+int SalGenericSystem::ShowNativeMessageBox( const OUString& rTitle, const OUString& rMessage )
+{
+ std::vector< OUString > aButtons;
+ int nButtonIds[5] = {0}, nBut = 0;
+
+ ImplHideSplash();
+
+ aButtons.push_back( "OK" );
+ nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK;
+ int nResult = ShowNativeDialog( rTitle, rMessage, aButtons );
+
+ return nResult != -1 ? nButtonIds[ nResult ] : 0;
+}
+
+#if !defined(ANDROID) && !defined(IOS)
+
+// X11-specific
+
+const char* SalGenericSystem::getFrameResName()
+{
+ /* according to ICCCM:
+ * first search command line for -name parameter
+ * then try RESOURCE_NAME environment variable
+ * then use argv[0] stripped by directories
+ */
+ static OStringBuffer aResName;
+ if( aResName.isEmpty() )
+ {
+ int nArgs = osl_getCommandArgCount();
+ for( int n = 0; n < nArgs-1; n++ )
+ {
+ OUString aArg;
+ osl_getCommandArg( n, &aArg.pData );
+ if( aArg.equalsIgnoreAsciiCase("-name") )
+ {
+ osl_getCommandArg( n+1, &aArg.pData );
+ aResName.append( OUStringToOString( aArg, osl_getThreadTextEncoding() ) );
+ break;
+ }
+ }
+ if( aResName.isEmpty() )
+ {
+ const char* pEnv = getenv( "RESOURCE_NAME" );
+ if( pEnv && *pEnv )
+ aResName.append( pEnv );
+ }
+ if( aResName.isEmpty() )
+ aResName.append( OUStringToOString( utl::ConfigManager::getProductName().toAsciiLowerCase(),
+ osl_getThreadTextEncoding()));
+ }
+ return aResName.getStr();
+}
+
+const char* SalGenericSystem::getFrameClassName()
+{
+ static OStringBuffer aClassName;
+ if( aClassName.isEmpty() )
+ {
+ OUString aIni, aProduct;
+ rtl::Bootstrap::get( "BRAND_BASE_DIR", aIni );
+ aIni += "/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap" );
+ rtl::Bootstrap aBootstrap( aIni );
+ aBootstrap.getFrom( "ProductKey", aProduct );
+
+ if( !aProduct.isEmpty() )
+ aClassName.append( OUStringToOString( aProduct, osl_getThreadTextEncoding() ) );
+ else
+ aClassName.append( OUStringToOString( utl::ConfigManager::getProductName(), osl_getThreadTextEncoding()));
+ }
+ return aClassName.getStr();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/i18n_cb.cxx b/vcl/unx/generic/app/i18n_cb.cxx
new file mode 100644
index 0000000000..c17c01a4d2
--- /dev/null
+++ b/vcl/unx/generic/app/i18n_cb.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#include <X11/Xlib.h>
+
+#include <vcl/commandevent.hxx>
+#include <unx/i18n_cb.hxx>
+#include <unx/i18n_ic.hxx>
+#include <unx/i18n_im.hxx>
+#include <salframe.hxx>
+
+// i. preedit start callback
+
+int
+PreeditStartCallback ( XIC, XPointer client_data, XPointer )
+{
+ preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
+ if ( pPreeditData->eState == PreeditStatus::ActivationRequired )
+ {
+ pPreeditData->eState = PreeditStatus::Active;
+ pPreeditData->aText.nLength = 0;
+ }
+
+ return -1;
+}
+
+// ii. preedit done callback
+
+void
+PreeditDoneCallback ( XIC, XPointer client_data, XPointer )
+{
+ preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
+ if (pPreeditData->eState == PreeditStatus::Active )
+ {
+ if( pPreeditData->pFrame )
+ pPreeditData->pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+ }
+ pPreeditData->eState = PreeditStatus::StartPending;
+}
+
+// iii. preedit draw callback
+
+// Handle deletion of text in a preedit_draw_callback
+// from and howmuch are guaranteed to be nonnegative
+
+static void
+Preedit_DeleteText(preedit_text_t *ptext, int from, int howmuch)
+{
+ // If we've been asked to delete no text then just set
+ // nLength correctly and return
+ if (ptext->nLength == 0)
+ {
+ ptext->nLength = from;
+ return;
+ }
+
+ int to = from + howmuch;
+
+ if (to == static_cast<int>(ptext->nLength))
+ {
+ // delete from the end of the text
+ ptext->nLength = from;
+ }
+ else if (to < static_cast<int>(ptext->nLength))
+ {
+ // cut out of the middle of the text
+ memmove( static_cast<void*>(ptext->pUnicodeBuffer + from),
+ static_cast<void*>(ptext->pUnicodeBuffer + to),
+ (ptext->nLength - to) * sizeof(sal_Unicode));
+ memmove( static_cast<void*>(ptext->pCharStyle + from),
+ static_cast<void*>(ptext->pCharStyle + to),
+ (ptext->nLength - to) * sizeof(XIMFeedback));
+ ptext->nLength -= howmuch;
+ }
+ else
+ {
+ // XXX this indicates an error, are we out of sync ?
+ SAL_INFO("vcl.app", "Preedit_DeleteText( from=" << from
+ << " to=" << to
+ << " length=" << ptext->nLength
+ << " ).");
+ fprintf (stderr, "\t XXX internal error, out of sync XXX\n");
+
+ ptext->nLength = from;
+ }
+
+ // NULL-terminate the string
+ ptext->pUnicodeBuffer[ptext->nLength] = u'\0';
+}
+
+// reallocate the textbuffer with sufficiently large size 2^x
+// nnewlimit is presupposed to be larger than ptext->size
+static void
+enlarge_buffer ( preedit_text_t *ptext, int nnewlimit )
+{
+ size_t nnewsize = ptext->nSize;
+
+ while ( nnewsize <= o3tl::make_unsigned(nnewlimit) )
+ nnewsize *= 2;
+
+ ptext->nSize = nnewsize;
+ ptext->pUnicodeBuffer = static_cast<sal_Unicode*>(realloc(static_cast<void*>(ptext->pUnicodeBuffer),
+ nnewsize * sizeof(sal_Unicode)));
+ ptext->pCharStyle = static_cast<XIMFeedback*>(realloc(static_cast<void*>(ptext->pCharStyle),
+ nnewsize * sizeof(XIMFeedback)));
+}
+
+// Handle insertion of text in a preedit_draw_callback
+// string field of XIMText struct is guaranteed to be != NULL
+
+static void
+Preedit_InsertText(preedit_text_t *pText, XIMText *pInsertText, int where)
+{
+ sal_Unicode *pInsertTextString;
+ int nInsertTextLength = 0;
+ XIMFeedback *pInsertTextCharStyle = pInsertText->feedback;
+
+ nInsertTextLength = pInsertText->length;
+
+ // can't handle wchar_t strings, so convert to multibyte chars first
+ char *pMBString;
+ size_t nMBLength;
+ if (pInsertText->encoding_is_wchar)
+ {
+ wchar_t *pWCString = pInsertText->string.wide_char;
+ size_t nBytes = wcstombs ( nullptr, pWCString, 0 /* don't care */);
+ pMBString = static_cast<char*>(alloca( nBytes + 1 ));
+ nMBLength = wcstombs ( pMBString, pWCString, nBytes + 1);
+ }
+ else
+ {
+ pMBString = pInsertText->string.multi_byte;
+ nMBLength = strlen(pMBString); // xxx
+ }
+
+ // convert multibyte chars to unicode
+ rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
+
+ if (nEncoding != RTL_TEXTENCODING_UNICODE)
+ {
+ rtl_TextToUnicodeConverter aConverter =
+ rtl_createTextToUnicodeConverter( nEncoding );
+ rtl_TextToUnicodeContext aContext =
+ rtl_createTextToUnicodeContext(aConverter);
+
+ sal_Size nBufferSize = nInsertTextLength * 2;
+
+ pInsertTextString = static_cast<sal_Unicode*>(alloca(nBufferSize));
+
+ sal_uInt32 nConversionInfo;
+ sal_Size nConvertedChars;
+
+ rtl_convertTextToUnicode( aConverter, aContext,
+ pMBString, nMBLength,
+ pInsertTextString, nBufferSize,
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE,
+ &nConversionInfo, &nConvertedChars );
+
+ rtl_destroyTextToUnicodeContext(aConverter, aContext);
+ rtl_destroyTextToUnicodeConverter(aConverter);
+
+ }
+ else
+ {
+ pInsertTextString = reinterpret_cast<sal_Unicode*>(pMBString);
+ }
+
+ // enlarge target text-buffer if necessary
+ if (pText->nSize <= (pText->nLength + nInsertTextLength))
+ enlarge_buffer(pText, pText->nLength + nInsertTextLength);
+
+ // insert text: displace old mem and put new bytes in
+ int from = where;
+ int to = where + nInsertTextLength;
+ int howmany = pText->nLength - where;
+
+ memmove(static_cast<void*>(pText->pUnicodeBuffer + to),
+ static_cast<void*>(pText->pUnicodeBuffer + from),
+ howmany * sizeof(sal_Unicode));
+ memmove(static_cast<void*>(pText->pCharStyle + to),
+ static_cast<void*>(pText->pCharStyle + from),
+ howmany * sizeof(XIMFeedback));
+
+ to = from;
+ howmany = nInsertTextLength;
+
+ memcpy(static_cast<void*>(pText->pUnicodeBuffer + to), static_cast<void*>(pInsertTextString),
+ howmany * sizeof(sal_Unicode));
+ memcpy(static_cast<void*>(pText->pCharStyle + to), static_cast<void*>(pInsertTextCharStyle),
+ howmany * sizeof(XIMFeedback));
+
+ pText->nLength += howmany;
+
+ // NULL-terminate the string
+ pText->pUnicodeBuffer[pText->nLength] = u'\0';
+}
+
+// Handle the change of attributes in a preedit_draw_callback
+
+static void
+Preedit_UpdateAttributes ( preedit_text_t* ptext, XIMFeedback const * feedback,
+ int from, int amount )
+{
+ if ( (from + amount) > static_cast<int>(ptext->nLength) )
+ {
+ // XXX this indicates an error, are we out of sync ?
+ SAL_INFO("vcl.app", "Preedit_UpdateAttributes( "
+ << from << " + " << amount << " > " << ptext->nLength
+ << " ).");
+ fprintf (stderr, "\t XXX internal error, out of sync XXX\n");
+
+ return;
+ }
+
+ memcpy ( ptext->pCharStyle + from,
+ feedback, amount * sizeof(XIMFeedback) );
+}
+
+// Convert the XIM feedback values into appropriate VCL
+// EXTTEXTINPUT_ATTR values
+// returns an allocate list of attributes, which must be freed by caller
+static ExtTextInputAttr*
+Preedit_FeedbackToSAL ( const XIMFeedback* pfeedback, int nlength, std::vector<ExtTextInputAttr>& rSalAttr )
+{
+ ExtTextInputAttr *psalattr;
+ ExtTextInputAttr nval;
+ ExtTextInputAttr noldval = ExtTextInputAttr::NONE;
+ XIMFeedback nfeedback;
+
+ // only work with reasonable length
+ if (nlength > 0 && nlength > sal::static_int_cast<int>(rSalAttr.size()) )
+ {
+ rSalAttr.reserve( nlength );
+ psalattr = rSalAttr.data();
+ }
+ else
+ return nullptr;
+
+ for (int npos = 0; npos < nlength; npos++)
+ {
+ nval = ExtTextInputAttr::NONE;
+ nfeedback = pfeedback[npos];
+
+ // means to use the feedback of the previous char
+ if (nfeedback == 0)
+ {
+ nval = noldval;
+ }
+ // convert feedback to attributes
+ else
+ {
+ if (nfeedback & XIMReverse)
+ nval |= ExtTextInputAttr::Highlight;
+ if (nfeedback & XIMUnderline)
+ nval |= ExtTextInputAttr::Underline;
+ if (nfeedback & XIMHighlight)
+ nval |= ExtTextInputAttr::Highlight;
+ if (nfeedback & XIMPrimary)
+ nval |= ExtTextInputAttr::DottedUnderline;
+ if (nfeedback & XIMSecondary)
+ nval |= ExtTextInputAttr::DashDotUnderline;
+ if (nfeedback & XIMTertiary) // same as 2ery
+ nval |= ExtTextInputAttr::DashDotUnderline;
+
+ }
+ // copy in list
+ psalattr[npos] = nval;
+ noldval = nval;
+ }
+ // return list of sal attributes
+ return psalattr;
+}
+
+void
+PreeditDrawCallback(XIC ic, XPointer client_data,
+ XIMPreeditDrawCallbackStruct *call_data)
+{
+ preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
+
+ // if there's nothing to change then change nothing
+ if ( ( (call_data->text == nullptr) && (call_data->chg_length == 0) )
+ || pPreeditData->pFrame == nullptr )
+ return;
+
+ // Solaris 7 deletes the preedit buffer after commit
+ // since the next call to preeditstart will have the same effect just skip this.
+ // if (pPreeditData->eState == ePreeditStatusStartPending && call_data->text == NULL)
+ // return;
+
+ if ( pPreeditData->eState == PreeditStatus::StartPending )
+ pPreeditData->eState = PreeditStatus::ActivationRequired;
+ PreeditStartCallback( ic, client_data, nullptr );
+
+ // Edit the internal textbuffer as indicated by the call_data,
+ // chg_first and chg_length are guaranteed to be nonnegative
+
+ // handle text deletion
+ if (call_data->text == nullptr)
+ {
+ Preedit_DeleteText(&(pPreeditData->aText),
+ call_data->chg_first, call_data->chg_length );
+ }
+ else
+ {
+ // handle text insertion
+ if ( (call_data->chg_length == 0)
+ && (call_data->text->string.wide_char != nullptr))
+ {
+ Preedit_InsertText(&(pPreeditData->aText), call_data->text,
+ call_data->chg_first);
+ }
+ else if ( (call_data->chg_length != 0)
+ && (call_data->text->string.wide_char != nullptr))
+ {
+ // handle text replacement by deletion and insertion of text,
+ // not smart, just good enough
+
+ Preedit_DeleteText(&(pPreeditData->aText),
+ call_data->chg_first, call_data->chg_length);
+ Preedit_InsertText(&(pPreeditData->aText), call_data->text,
+ call_data->chg_first);
+ }
+ else if ( (call_data->chg_length != 0)
+ && (call_data->text->string.wide_char == nullptr))
+ {
+ // not really a text update, only attributes are concerned
+ Preedit_UpdateAttributes(&(pPreeditData->aText),
+ call_data->text->feedback,
+ call_data->chg_first, call_data->chg_length);
+ }
+ }
+
+ // build the SalExtTextInputEvent and send it up
+
+ pPreeditData->aInputEv.mpTextAttr = Preedit_FeedbackToSAL(
+ pPreeditData->aText.pCharStyle, pPreeditData->aText.nLength, pPreeditData->aInputFlags);
+ pPreeditData->aInputEv.mnCursorPos = call_data->caret;
+ pPreeditData->aInputEv.maText = OUString(pPreeditData->aText.pUnicodeBuffer,
+ pPreeditData->aText.nLength);
+ pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible
+
+ if ( pPreeditData->eState == PreeditStatus::Active && pPreeditData->pFrame )
+ pPreeditData->pFrame->CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&pPreeditData->aInputEv));
+ if (pPreeditData->aText.nLength == 0 && pPreeditData->pFrame )
+ pPreeditData->pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+
+ if (pPreeditData->aText.nLength == 0)
+ pPreeditData->eState = PreeditStatus::StartPending;
+
+ GetPreeditSpotLocation(ic, reinterpret_cast<XPointer>(pPreeditData));
+}
+
+void
+GetPreeditSpotLocation(XIC ic, XPointer client_data)
+{
+
+ // Send SalEventExtTextInputPos event to get spotlocation
+
+ SalExtTextInputPosEvent aPosEvent;
+ preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
+
+ if( pPreeditData->pFrame )
+ pPreeditData->pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
+
+ XPoint point;
+ point.x = aPosEvent.mnX + aPosEvent.mnWidth;
+ point.y = aPosEvent.mnY + aPosEvent.mnHeight;
+
+ XVaNestedList preedit_attr;
+ preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, nullptr);
+ XSetICValues(ic, XNPreeditAttributes, preedit_attr, nullptr);
+ XFree(preedit_attr);
+}
+
+// iv. preedit caret callback
+
+#if OSL_DEBUG_LEVEL > 1
+void
+PreeditCaretCallback ( XIC ic, XPointer client_data,
+ XIMPreeditCaretCallbackStruct *call_data )
+{
+ // XXX PreeditCaretCallback is pure debug code for now
+ const char *direction = "?";
+ const char *style = "?";
+
+ switch ( call_data->style )
+ {
+ case XIMIsInvisible: style = "Invisible"; break;
+ case XIMIsPrimary: style = "Primary"; break;
+ case XIMIsSecondary: style = "Secondary"; break;
+ }
+ switch ( call_data->direction )
+ {
+ case XIMForwardChar: direction = "Forward char"; break;
+ case XIMBackwardChar: direction = "Backward char"; break;
+ case XIMForwardWord: direction = "Forward word"; break;
+ case XIMBackwardWord: direction = "Backward word"; break;
+ case XIMCaretUp: direction = "Caret up"; break;
+ case XIMCaretDown: direction = "Caret down"; break;
+ case XIMNextLine: direction = "Next line"; break;
+ case XIMPreviousLine: direction = "Previous line"; break;
+ case XIMLineStart: direction = "Line start"; break;
+ case XIMLineEnd: direction = "Line end"; break;
+ case XIMAbsolutePosition: direction = "Absolute"; break;
+ case XIMDontChange: direction = "Don't change"; break;
+ }
+
+ SAL_INFO("vcl.app", "PreeditCaretCallback( ic=" << ic
+ << ", client=" << client_data
+ << ",");
+ SAL_INFO("vcl.app", "\t position=" << call_data->position
+ << ", direction=\"" << direction
+ << "\", style=\"" << style
+ << "\" ).");
+}
+#else
+void
+PreeditCaretCallback ( XIC, XPointer, XIMPreeditCaretCallbackStruct* )
+{
+}
+#endif
+
+// v. commit string callback: convert an extended text input (iiimp ... )
+// into an ordinary key-event
+
+Bool
+IsControlCode(sal_Unicode nChar)
+{
+ if ( nChar <= 0x1F /* C0 controls */ )
+ return True;
+ else
+ return False;
+}
+
+// vi. status callbacks: for now these are empty, they are just needed for turbo linux
+
+void
+StatusStartCallback (XIC, XPointer, XPointer)
+{
+}
+
+void
+StatusDoneCallback (XIC, XPointer, XPointer)
+{
+}
+
+void
+StatusDrawCallback (XIC, XPointer, XIMStatusDrawCallbackStruct *)
+{
+}
+
+// vii. destroy callbacks: internally disable all IC/IM calls
+
+void
+IC_IMDestroyCallback (XIM, XPointer client_data, XPointer)
+{
+ SalI18N_InputContext *pContext = reinterpret_cast<SalI18N_InputContext*>(client_data);
+ if (pContext != nullptr)
+ pContext->HandleDestroyIM();
+}
+
+void
+IM_IMDestroyCallback (XIM, XPointer client_data, XPointer)
+{
+ SalI18N_InputMethod *pMethod = reinterpret_cast<SalI18N_InputMethod*>(client_data);
+ if (pMethod != nullptr)
+ pMethod->HandleDestroyIM();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/i18n_ic.cxx b/vcl/unx/generic/app/i18n_ic.cxx
new file mode 100644
index 0000000000..32390a8888
--- /dev/null
+++ b/vcl/unx/generic/app/i18n_ic.cxx
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <X11/Xlib.h>
+
+#include <unx/i18n_ic.hxx>
+#include <unx/i18n_im.hxx>
+
+#include <unx/salframe.h>
+#include <unx/saldisp.hxx>
+
+#include <sal/log.hxx>
+
+using namespace vcl;
+
+static void sendEmptyCommit( SalFrame* pFrame )
+{
+ vcl::DeletionListener aDel( pFrame );
+
+ SalExtTextInputEvent aEmptyEv;
+ aEmptyEv.mpTextAttr = nullptr;
+ aEmptyEv.maText.clear();
+ aEmptyEv.mnCursorPos = 0;
+ aEmptyEv.mnCursorFlags = 0;
+ pFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void*>(&aEmptyEv) );
+ if( ! aDel.isDeleted() )
+ pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+}
+
+// Constructor / Destructor, the InputContext is bound to the SalFrame, as it
+// needs the shell window as a focus window
+
+SalI18N_InputContext::~SalI18N_InputContext()
+{
+ if ( maContext != nullptr )
+ XDestroyIC( maContext );
+ if ( mpAttributes != nullptr )
+ XFree( mpAttributes );
+ if ( mpStatusAttributes != nullptr )
+ XFree( mpStatusAttributes );
+ if ( mpPreeditAttributes != nullptr )
+ XFree( mpPreeditAttributes );
+
+ if (maClientData.aText.pUnicodeBuffer != nullptr)
+ free(maClientData.aText.pUnicodeBuffer);
+ if (maClientData.aText.pCharStyle != nullptr)
+ free(maClientData.aText.pCharStyle);
+}
+
+// convenience routine to add items to a XVaNestedList
+
+static XVaNestedList
+XVaAddToNestedList( XVaNestedList a_srclist, char* name, XPointer value )
+{
+ XVaNestedList a_dstlist;
+
+ // if ( value == NULL )
+ // return a_srclist;
+
+ if ( a_srclist == nullptr )
+ {
+ a_dstlist = XVaCreateNestedList(
+ 0,
+ name, value,
+ nullptr );
+ }
+ else
+ {
+ a_dstlist = XVaCreateNestedList(
+ 0,
+ XNVaNestedList, a_srclist,
+ name, value,
+ nullptr );
+ }
+
+ return a_dstlist != nullptr ? a_dstlist : a_srclist ;
+}
+
+// convenience routine to create a fontset
+
+static XFontSet
+get_font_set( Display *p_display )
+{
+ static XFontSet p_font_set = nullptr;
+
+ if (p_font_set == nullptr)
+ {
+ char **pp_missing_list;
+ int n_missing_count;
+ char *p_default_string;
+
+ p_font_set = XCreateFontSet(p_display, "-*",
+ &pp_missing_list, &n_missing_count, &p_default_string);
+ }
+
+ return p_font_set;
+}
+
+const XIMStyle g_nSupportedStatusStyle(
+ XIMStatusCallbacks |
+ XIMStatusNothing |
+ XIMStatusNone
+ );
+
+// Constructor for an InputContext (IC)
+
+SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) :
+ mbUseable( True ),
+ maContext( nullptr ),
+ mnSupportedPreeditStyle(
+ XIMPreeditCallbacks |
+ XIMPreeditNothing |
+ XIMPreeditNone
+ ),
+ mnStatusStyle( 0 ),
+ mnPreeditStyle( 0 ),
+ mpAttributes( nullptr ),
+ mpStatusAttributes( nullptr ),
+ mpPreeditAttributes( nullptr )
+{
+#ifdef __sun
+ static const char* pIIIMPEnable = getenv( "SAL_DISABLE_OWN_IM_STATUS" );
+ if( pIIIMPEnable && *pIIIMPEnable )
+ mnSupportedStatusStyle &= ~XIMStatusCallbacks;
+#endif
+
+ memset(&maPreeditStartCallback, 0, sizeof(maPreeditStartCallback));
+ memset(&maPreeditDoneCallback, 0, sizeof(maPreeditDoneCallback));
+ memset(&maPreeditDrawCallback, 0, sizeof(maPreeditDrawCallback));
+ memset(&maPreeditCaretCallback, 0, sizeof(maPreeditCaretCallback));
+ memset(&maCommitStringCallback, 0, sizeof(maCommitStringCallback));
+ memset(&maSwitchIMCallback, 0, sizeof(maSwitchIMCallback));
+ memset(&maDestroyCallback, 0, sizeof(maDestroyCallback));
+
+ maClientData.aText.pUnicodeBuffer = nullptr;
+ maClientData.aText.pCharStyle = nullptr;
+ maClientData.aInputEv.mpTextAttr = nullptr;
+ maClientData.aInputEv.mnCursorPos = 0;
+ maClientData.aInputEv.mnCursorFlags = 0;
+
+ SalI18N_InputMethod *pInputMethod;
+ pInputMethod = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetInputMethod();
+
+ mnSupportedPreeditStyle = XIMPreeditCallbacks | XIMPreeditPosition
+ | XIMPreeditNothing | XIMPreeditNone;
+ if (pInputMethod->UseMethod()
+ && SupportInputMethodStyle( pInputMethod->GetSupportedStyles() ) )
+ {
+ const SystemEnvData* pEnv = pFrame->GetSystemData();
+ ::Window aClientWindow = pEnv->aShellWindow;
+ ::Window aFocusWindow = pEnv->GetWindowHandle(pFrame);
+
+ // for status callbacks and commit string callbacks
+#define PREEDIT_BUFSZ 16
+ maClientData.eState = PreeditStatus::StartPending;
+ maClientData.pFrame = pFrame;
+ maClientData.aText.pUnicodeBuffer =
+ static_cast<sal_Unicode*>(malloc(PREEDIT_BUFSZ * sizeof(sal_Unicode)));
+ maClientData.aText.pCharStyle =
+ static_cast<XIMFeedback*>(malloc(PREEDIT_BUFSZ * sizeof(XIMFeedback)));
+ maClientData.aText.nSize = PREEDIT_BUFSZ;
+ maClientData.aText.nLength = 0;
+
+ // Status attributes
+
+ switch ( mnStatusStyle )
+ {
+ case XIMStatusCallbacks:
+ {
+ static XIMCallback aStatusStartCallback;
+ static XIMCallback aStatusDoneCallback;
+ static XIMCallback aStatusDrawCallback;
+
+ aStatusStartCallback.callback = reinterpret_cast<XIMProc>(StatusStartCallback);
+ aStatusStartCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+ aStatusDoneCallback.callback = reinterpret_cast<XIMProc>(StatusDoneCallback);
+ aStatusDoneCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+ aStatusDrawCallback.callback = reinterpret_cast<XIMProc>(StatusDrawCallback);
+ aStatusDrawCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+
+ mpStatusAttributes = XVaCreateNestedList (
+ 0,
+ XNStatusStartCallback, &aStatusStartCallback,
+ XNStatusDoneCallback, &aStatusDoneCallback,
+ XNStatusDrawCallback, &aStatusDrawCallback,
+ nullptr );
+
+ break;
+ }
+
+ case XIMStatusArea:
+ /* not supported */
+ break;
+
+ case XIMStatusNone:
+ case XIMStatusNothing:
+ default:
+ /* no arguments needed */
+ break;
+ }
+
+ // set preedit attributes
+
+ switch ( mnPreeditStyle )
+ {
+ case XIMPreeditCallbacks:
+
+ maPreeditCaretCallback.callback = reinterpret_cast<XIMProc>(PreeditCaretCallback);
+ maPreeditStartCallback.callback = reinterpret_cast<XIMProc>(PreeditStartCallback);
+ maPreeditDoneCallback.callback = reinterpret_cast<XIMProc>(PreeditDoneCallback);
+ maPreeditDrawCallback.callback = reinterpret_cast<XIMProc>(PreeditDrawCallback);
+ maPreeditCaretCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+ maPreeditStartCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+ maPreeditDoneCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+ maPreeditDrawCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
+
+ mpPreeditAttributes = XVaCreateNestedList (
+ 0,
+ XNPreeditStartCallback, &maPreeditStartCallback,
+ XNPreeditDoneCallback, &maPreeditDoneCallback,
+ XNPreeditDrawCallback, &maPreeditDrawCallback,
+ XNPreeditCaretCallback, &maPreeditCaretCallback,
+ nullptr );
+
+ break;
+
+ case XIMPreeditArea:
+ /* not supported */
+ break;
+
+ case XIMPreeditPosition:
+ {
+ // spot location
+ SalExtTextInputPosEvent aPosEvent;
+ pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
+
+ static XPoint aSpot;
+ aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth;
+ aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight;
+
+ // create attributes for preedit position style
+ mpPreeditAttributes = XVaCreateNestedList (
+ 0,
+ XNSpotLocation, &aSpot,
+ nullptr );
+
+ // XCreateIC() fails on Redflag Linux 2.0 if there is no
+ // fontset though the data itself is not evaluated nor is
+ // it required according to the X specs.
+ Display* pDisplay = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay();
+ XFontSet pFontSet = get_font_set(pDisplay);
+
+ if (pFontSet != nullptr)
+ {
+ mpPreeditAttributes = XVaAddToNestedList( mpPreeditAttributes,
+ const_cast<char*>(XNFontSet), reinterpret_cast<XPointer>(pFontSet));
+ }
+
+ break;
+ }
+
+ case XIMPreeditNone:
+ case XIMPreeditNothing:
+ default:
+ /* no arguments needed */
+ break;
+ }
+
+ // Create the InputContext by giving it exactly the information it
+ // deserves, because inappropriate attributes
+ // let XCreateIC fail on Solaris (eg. for C locale)
+
+ mpAttributes = XVaCreateNestedList(
+ 0,
+ XNFocusWindow, aFocusWindow,
+ XNClientWindow, aClientWindow,
+ XNInputStyle, mnPreeditStyle | mnStatusStyle,
+ nullptr );
+
+ if ( mnPreeditStyle != XIMPreeditNone )
+ {
+#if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
+ if ( mpPreeditAttributes != nullptr )
+#endif
+ mpAttributes = XVaAddToNestedList( mpAttributes,
+ const_cast<char*>(XNPreeditAttributes), static_cast<XPointer>(mpPreeditAttributes) );
+ }
+ if ( mnStatusStyle != XIMStatusNone )
+ {
+#if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
+ if ( mpStatusAttributes != nullptr )
+#endif
+ mpAttributes = XVaAddToNestedList( mpAttributes,
+ const_cast<char*>(XNStatusAttributes), static_cast<XPointer>(mpStatusAttributes) );
+ }
+ maContext = XCreateIC( pInputMethod->GetMethod(),
+ XNVaNestedList, mpAttributes,
+ nullptr );
+ }
+
+ if ( maContext == nullptr )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN("vcl.app", "input context creation failed.");
+#endif
+
+ mbUseable = False;
+
+ if ( mpAttributes != nullptr )
+ XFree( mpAttributes );
+ if ( mpStatusAttributes != nullptr )
+ XFree( mpStatusAttributes );
+ if ( mpPreeditAttributes != nullptr )
+ XFree( mpPreeditAttributes );
+ if ( maClientData.aText.pUnicodeBuffer != nullptr )
+ free ( maClientData.aText.pUnicodeBuffer );
+ if ( maClientData.aText.pCharStyle != nullptr )
+ free ( maClientData.aText.pCharStyle );
+
+ mpAttributes = nullptr;
+ mpStatusAttributes = nullptr;
+ mpPreeditAttributes = nullptr;
+ maClientData.aText.pUnicodeBuffer = nullptr;
+ maClientData.aText.pCharStyle = nullptr;
+ }
+
+ if ( maContext != nullptr)
+ {
+ maDestroyCallback.callback = IC_IMDestroyCallback;
+ maDestroyCallback.client_data = reinterpret_cast<XPointer>(this);
+ XSetICValues( maContext,
+ XNDestroyCallback, &maDestroyCallback,
+ nullptr );
+ }
+}
+
+// In Solaris 8 the status window does not unmap if the frame unmapps, so
+// unmap it the hard way
+
+void
+SalI18N_InputContext::Unmap()
+{
+ UnsetICFocus();
+ maClientData.pFrame = nullptr;
+}
+
+void
+SalI18N_InputContext::Map( SalFrame *pFrame )
+{
+ if( !mbUseable )
+ return;
+
+ if( !pFrame )
+ return;
+
+ if ( maContext == nullptr )
+ {
+ SalI18N_InputMethod *pInputMethod;
+ pInputMethod = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetInputMethod();
+
+ maContext = XCreateIC( pInputMethod->GetMethod(),
+ XNVaNestedList, mpAttributes,
+ nullptr );
+ }
+ if( maClientData.pFrame != pFrame )
+ SetICFocus( pFrame );
+}
+
+// Handle DestroyCallbacks
+// in fact this is a callback called from the XNDestroyCallback
+
+void
+SalI18N_InputContext::HandleDestroyIM()
+{
+ maContext = nullptr; // don't change
+ mbUseable = False;
+}
+
+// make sure, the input method gets all the X-Events it needs, this is only
+// called once on each frame, it relies on a valid maContext
+
+void
+SalI18N_InputContext::ExtendEventMask( ::Window aFocusWindow )
+{
+ unsigned long nIMEventMask;
+ XWindowAttributes aWindowAttributes;
+
+ if ( mbUseable )
+ {
+ Display *pDisplay = XDisplayOfIM( XIMOfIC(maContext) );
+
+ XGetWindowAttributes( pDisplay, aFocusWindow,
+ &aWindowAttributes );
+ XGetICValues ( maContext,
+ XNFilterEvents, &nIMEventMask,
+ nullptr);
+ nIMEventMask |= aWindowAttributes.your_event_mask;
+ XSelectInput ( pDisplay, aFocusWindow, nIMEventMask );
+ }
+}
+
+// tune the styles provided by the input method with the supported one
+
+unsigned int
+SalI18N_InputContext::GetWeightingOfIMStyle( XIMStyle nStyle )
+{
+ struct StyleWeightingT {
+ const XIMStyle nStyle;
+ const unsigned int nWeight;
+ };
+
+ StyleWeightingT const *pWeightPtr;
+ static const StyleWeightingT pWeight[] = {
+ { XIMPreeditCallbacks, 0x10000000 },
+ { XIMPreeditPosition, 0x02000000 },
+ { XIMPreeditArea, 0x01000000 },
+ { XIMPreeditNothing, 0x00100000 },
+ { XIMPreeditNone, 0x00010000 },
+ { XIMStatusCallbacks, 0x1000 },
+ { XIMStatusArea, 0x0100 },
+ { XIMStatusNothing, 0x0010 },
+ { XIMStatusNone, 0x0001 },
+ { 0, 0x0 }
+ };
+
+ int nWeight = 0;
+ for ( pWeightPtr = pWeight; pWeightPtr->nStyle != 0; pWeightPtr++ )
+ {
+ if ( (pWeightPtr->nStyle & nStyle) != 0 )
+ nWeight += pWeightPtr->nWeight;
+ }
+ return nWeight;
+}
+
+bool
+SalI18N_InputContext::IsSupportedIMStyle( XIMStyle nStyle ) const
+{
+ return (nStyle & mnSupportedPreeditStyle)
+ && (nStyle & g_nSupportedStatusStyle);
+}
+
+bool
+SalI18N_InputContext::SupportInputMethodStyle( XIMStyles const *pIMStyles )
+{
+ mnPreeditStyle = 0;
+ mnStatusStyle = 0;
+
+ if ( pIMStyles != nullptr )
+ {
+ int nBestScore = 0;
+ int nActualScore = 0;
+
+ // check whether the XIM supports one of the desired styles
+ // only a single preedit and a single status style must occur
+ // in an input method style. Hideki said so, so i trust him
+ for ( int nStyle = 0; nStyle < pIMStyles->count_styles; nStyle++ )
+ {
+ XIMStyle nProvidedStyle = pIMStyles->supported_styles[ nStyle ];
+ if ( IsSupportedIMStyle(nProvidedStyle) )
+ {
+ nActualScore = GetWeightingOfIMStyle( nProvidedStyle );
+ if ( nActualScore >= nBestScore )
+ {
+ nBestScore = nActualScore;
+ mnPreeditStyle = nProvidedStyle & mnSupportedPreeditStyle;
+ mnStatusStyle = nProvidedStyle & g_nSupportedStatusStyle;
+ }
+ }
+ }
+ }
+
+ return (mnPreeditStyle != 0) && (mnStatusStyle != 0) ;
+}
+
+// handle extended and normal key input
+
+void
+SalI18N_InputContext::CommitKeyEvent(sal_Unicode const * pText, std::size_t nLength)
+{
+ if (nLength == 1 && IsControlCode(pText[0]))
+ return;
+
+ if( maClientData.pFrame )
+ {
+ SalExtTextInputEvent aTextEvent;
+
+ aTextEvent.mpTextAttr = nullptr;
+ aTextEvent.mnCursorPos = nLength;
+ aTextEvent.maText = OUString(pText, nLength);
+ aTextEvent.mnCursorFlags = 0;
+
+ maClientData.pFrame->CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&aTextEvent));
+ maClientData.pFrame->CallCallback(SalEvent::EndExtTextInput, nullptr);
+ }
+#if OSL_DEBUG_LEVEL > 1
+ else
+ SAL_WARN("vcl.app", "CommitKeyEvent without frame.");
+#endif
+}
+
+int
+SalI18N_InputContext::UpdateSpotLocation()
+{
+ if (maContext == nullptr || maClientData.pFrame == nullptr)
+ return -1;
+
+ SalExtTextInputPosEvent aPosEvent;
+ maClientData.pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
+
+ XPoint aSpot;
+ aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth;
+ aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight;
+
+ XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &aSpot, nullptr);
+ XSetICValues(maContext, XNPreeditAttributes, preedit_attr, nullptr);
+ XFree(preedit_attr);
+
+ return 0;
+}
+
+// set and unset the focus for the Input Context
+// the context may be NULL despite it is usable if the framewindow is
+// in unmapped state
+
+void
+SalI18N_InputContext::SetICFocus( SalFrame* pFocusFrame )
+{
+ if ( !(mbUseable && (maContext != nullptr)) )
+ return;
+
+ maClientData.pFrame = pFocusFrame;
+
+ const SystemEnvData* pEnv = pFocusFrame->GetSystemData();
+ ::Window aClientWindow = pEnv->aShellWindow;
+ ::Window aFocusWindow = pEnv->GetWindowHandle(pFocusFrame);
+
+ XSetICValues( maContext,
+ XNFocusWindow, aFocusWindow,
+ XNClientWindow, aClientWindow,
+ nullptr );
+
+ if( maClientData.aInputEv.mpTextAttr )
+ {
+ sendEmptyCommit(pFocusFrame);
+ // begin preedit again
+ vcl_sal::getSalDisplay(GetGenericUnixSalData())->SendInternalEvent( pFocusFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
+ }
+
+ XSetICFocus( maContext );
+}
+
+void
+SalI18N_InputContext::UnsetICFocus()
+{
+
+ if ( mbUseable && (maContext != nullptr) )
+ {
+ // cancel an eventual event posted to begin preedit again
+ vcl_sal::getSalDisplay(GetGenericUnixSalData())->CancelInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
+ maClientData.pFrame = nullptr;
+ XUnsetICFocus( maContext );
+ }
+}
+
+// multi byte input method only
+
+void
+SalI18N_InputContext::EndExtTextInput()
+{
+ if ( !mbUseable || (maContext == nullptr) || !maClientData.pFrame )
+ return;
+
+ vcl::DeletionListener aDel( maClientData.pFrame );
+ // delete preedit in sal (commit an empty string)
+ sendEmptyCommit( maClientData.pFrame );
+ if( ! aDel.isDeleted() )
+ {
+ // mark previous preedit state again (will e.g. be sent at focus gain)
+ maClientData.aInputEv.mpTextAttr = maClientData.aInputFlags.data();
+ if( static_cast<X11SalFrame*>(maClientData.pFrame)->hasFocus() )
+ {
+ // begin preedit again
+ vcl_sal::getSalDisplay(GetGenericUnixSalData())->SendInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/i18n_im.cxx b/vcl/unx/generic/app/i18n_im.cxx
new file mode 100644
index 0000000000..6a655ca39e
--- /dev/null
+++ b/vcl/unx/generic/app/i18n_im.cxx
@@ -0,0 +1,410 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+
+#ifdef LINUX
+# ifndef __USE_XOPEN
+# define __USE_XOPEN
+# endif
+#endif
+
+#include <X11/Xlib.h>
+
+#include <unx/i18n_im.hxx>
+
+#include <osl/thread.h>
+#include <osl/process.h>
+#include <sal/log.hxx>
+
+#include <unx/i18n_cb.hxx>
+
+using namespace vcl;
+
+// kinput2 IME needs special key handling since key release events are filtered in
+// preeditmode and XmbResetIC does not work
+
+namespace {
+
+class XKeyEventOp : public XKeyEvent
+{
+ private:
+ void init();
+
+ public:
+ XKeyEventOp();
+
+ XKeyEventOp& operator= (const XKeyEvent &rEvent);
+ void erase ();
+ bool match (const XKeyEvent &rEvent) const;
+};
+
+}
+
+void
+XKeyEventOp::init()
+{
+ type = 0; /* serial = 0; */
+ send_event = 0; display = nullptr;
+ window = 0; root = 0;
+ subwindow = 0; /* time = 0; */
+ /* x = 0; y = 0; */
+ /* x_root = 0; y_root = 0; */
+ state = 0; keycode = 0;
+ same_screen = 0;
+}
+
+XKeyEventOp::XKeyEventOp()
+{
+ init();
+}
+
+XKeyEventOp&
+XKeyEventOp::operator= (const XKeyEvent &rEvent)
+{
+ type = rEvent.type; /* serial = rEvent.serial; */
+ send_event = rEvent.send_event; display = rEvent.display;
+ window = rEvent.window; root = rEvent.root;
+ subwindow = rEvent.subwindow;/* time = rEvent.time; */
+ /* x = rEvent.x, y = rEvent.y; */
+ /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
+ state = rEvent.state; keycode = rEvent.keycode;
+ same_screen = rEvent.same_screen;
+
+ return *this;
+}
+
+void
+XKeyEventOp::erase ()
+{
+ init();
+}
+
+bool
+XKeyEventOp::match (const XKeyEvent &rEvent) const
+{
+ return ( (type == KeyPress && rEvent.type == KeyRelease)
+ || (type == KeyRelease && rEvent.type == KeyPress ))
+ /* && serial == rEvent.serial */
+ && send_event == rEvent.send_event
+ && display == rEvent.display
+ && window == rEvent.window
+ && root == rEvent.root
+ && subwindow == rEvent.subwindow
+ /* && time == rEvent.time
+ && x == rEvent.x
+ && y == rEvent.y
+ && x_root == rEvent.x_root
+ && y_root == rEvent.y_root */
+ && state == rEvent.state
+ && keycode == rEvent.keycode
+ && same_screen == rEvent.same_screen;
+}
+
+// locale handling
+
+// Locale handling of the operating system layer
+
+static char*
+SetSystemLocale( const char* p_inlocale )
+{
+ char *p_outlocale = setlocale(LC_ALL, p_inlocale);
+
+ SAL_WARN_IF(p_outlocale == nullptr, "vcl.app",
+ "I18N: Operating system doesn't support locale \""
+ << p_inlocale << "\".");
+
+ return p_outlocale;
+}
+
+#ifdef __sun
+static void
+SetSystemEnvironment( const OUString& rLocale )
+{
+ OUString LC_ALL_Var("LC_ALL");
+ osl_setEnvironment(LC_ALL_Var.pData, rLocale.pData);
+
+ OUString LANG_Var("LANG");
+ osl_setEnvironment(LANG_Var.pData, rLocale.pData);
+}
+#endif
+
+static Bool
+IsPosixLocale( const char* p_locale )
+{
+ if ( p_locale == nullptr )
+ return False;
+ if ( (p_locale[ 0 ] == 'C') && (p_locale[ 1 ] == '\0') )
+ return True;
+ if ( strncmp(p_locale, "POSIX", sizeof("POSIX")) == 0 )
+ return True;
+
+ return False;
+}
+
+// Locale handling of the X Window System layer
+
+static Bool
+IsXWindowCompatibleLocale( const char* p_locale )
+{
+ if ( p_locale == nullptr )
+ return False;
+
+ if ( !XSupportsLocale() )
+ {
+ SAL_WARN("vcl.app",
+ "I18N: X Window System doesn't support locale \""
+ << p_locale << "\".");
+ return False;
+ }
+ return True;
+}
+
+// Set the operating system locale prior to trying to open an
+// XIM InputMethod.
+// Handle the cases where the current locale is either not supported by the
+// operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
+// by providing a fallback.
+// Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
+// see i8988, i9188, i8930, i16318
+// on Solaris the environment needs to be set equivalent to the locale (#i37047#)
+
+void
+SalI18N_InputMethod::SetLocale()
+{
+ // check whether we want an Input Method engine, if we don't we
+ // do not need to set the locale
+ if ( !mbUseable )
+ return;
+
+ char *locale = SetSystemLocale( "" );
+ if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) )
+ {
+ osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1);
+ locale = SetSystemLocale( "en_US" );
+#ifdef __sun
+ SetSystemEnvironment( "en_US" );
+#endif
+ if (! IsXWindowCompatibleLocale(locale))
+ {
+ locale = SetSystemLocale( "C" );
+#ifdef __sun
+ SetSystemEnvironment( "C" );
+#endif
+ if (! IsXWindowCompatibleLocale(locale))
+ mbUseable = False;
+ }
+ }
+
+ // must not fail if mbUseable since XSupportsLocale() asserts success
+ if ( mbUseable && XSetLocaleModifiers("") == nullptr )
+ {
+ SAL_WARN("vcl.app",
+ "I18N: Can't set X modifiers for locale \""
+ << locale << "\".");
+ mbUseable = False;
+ }
+}
+
+Bool
+SalI18N_InputMethod::PosixLocale()
+{
+ if (maMethod)
+ return IsPosixLocale (XLocaleOfIM (maMethod));
+ return False;
+}
+
+// Constructor / Destructor / Initialisation
+
+SalI18N_InputMethod::SalI18N_InputMethod( )
+ : mbUseable( bUseInputMethodDefault )
+ , maMethod( nullptr )
+ , mpStyles( nullptr )
+{
+ maDestroyCallback.callback = nullptr;
+ maDestroyCallback.client_data = nullptr;
+ const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" );
+ if ( pUseInputMethod != nullptr )
+ mbUseable = pUseInputMethod[0] != '\0' ;
+}
+
+SalI18N_InputMethod::~SalI18N_InputMethod()
+{
+ if ( mpStyles != nullptr )
+ XFree( mpStyles );
+ if ( maMethod != nullptr )
+ XCloseIM ( maMethod );
+}
+
+// XXX
+// debug routine: lets have a look at the provided method styles
+
+#if OSL_DEBUG_LEVEL > 1
+
+extern "C" char*
+GetMethodName( XIMStyle nStyle, char *pBuf, int nBufSize)
+{
+ struct StyleName {
+ const XIMStyle nStyle;
+ const char *pName;
+ const int nNameLen;
+ };
+
+ StyleName *pDescPtr;
+ static const StyleName pDescription[] = {
+ { XIMPreeditArea, "PreeditArea ", sizeof("PreeditArea ") },
+ { XIMPreeditCallbacks, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
+ { XIMPreeditPosition, "PreeditPosition ", sizeof("PreeditPosition ") },
+ { XIMPreeditNothing, "PreeditNothing ", sizeof("PreeditNothing ") },
+ { XIMPreeditNone, "PreeditNone ", sizeof("PreeditNone ") },
+ { XIMStatusArea, "StatusArea ", sizeof("StatusArea ") },
+ { XIMStatusCallbacks, "StatusCallbacks ", sizeof("StatusCallbacks ") },
+ { XIMStatusNothing, "StatusNothing ", sizeof("StatusNothing ") },
+ { XIMStatusNone, "StatusNone ", sizeof("StatusNone ") },
+ { 0, "NULL", 0 }
+ };
+
+ if ( nBufSize > 0 )
+ pBuf[0] = '\0';
+
+ char *pBufPtr = pBuf;
+ for ( pDescPtr = const_cast<StyleName*>(pDescription); pDescPtr->nStyle != 0; pDescPtr++ )
+ {
+ int nSize = pDescPtr->nNameLen - 1;
+ if ( (nStyle & pDescPtr->nStyle) && (nBufSize > nSize) )
+ {
+ strncpy( pBufPtr, pDescPtr->pName, nSize + 1);
+ pBufPtr += nSize;
+ nBufSize -= nSize;
+ }
+ }
+
+ return pBuf;
+}
+
+extern "C" void
+PrintInputStyle( XIMStyles *pStyle )
+{
+ char pBuf[ 128 ];
+ int nBuf = sizeof( pBuf );
+
+ if ( pStyle == NULL )
+ SAL_INFO("vcl.app", "no input method styles.");
+ else
+ for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ )
+ {
+ SAL_INFO("vcl.app", "style #"
+ << nStyle
+ << " = "
+ << GetMethodName(pStyle->supported_styles[nStyle], pBuf, nBuf));
+ }
+}
+
+#endif
+
+// this is the real constructing routine, since locale setting has to be done
+// prior to xopendisplay, the xopenim call has to be delayed
+
+void
+SalI18N_InputMethod::CreateMethod ( Display *pDisplay )
+{
+ if ( mbUseable )
+ {
+ maMethod = XOpenIM(pDisplay, nullptr, nullptr, nullptr);
+
+ if ((maMethod == nullptr) && (getenv("XMODIFIERS") != nullptr))
+ {
+ OUString envVar("XMODIFIERS");
+ osl_clearEnvironment(envVar.pData);
+ XSetLocaleModifiers("");
+ maMethod = XOpenIM(pDisplay, nullptr, nullptr, nullptr);
+ }
+
+ if ( maMethod != nullptr )
+ {
+ if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, nullptr)
+ != nullptr)
+ mbUseable = False;
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "Creating Mono-Lingual InputMethod.");
+ PrintInputStyle( mpStyles );
+#endif
+ }
+ else
+ {
+ mbUseable = False;
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN_IF(!mbUseable, "vcl.app", "input method creation failed.");
+#endif
+
+ maDestroyCallback.callback = IM_IMDestroyCallback;
+ maDestroyCallback.client_data = reinterpret_cast<XPointer>(this);
+ if (mbUseable && maMethod != nullptr)
+ XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, nullptr);
+}
+
+// give IM the opportunity to look at the event, and possibly hide it
+
+bool
+SalI18N_InputMethod::FilterEvent( XEvent *pEvent, ::Window window )
+{
+ if (!mbUseable)
+ return False;
+
+ bool bFilterEvent = XFilterEvent (pEvent, window);
+
+ if (pEvent->type != KeyPress && pEvent->type != KeyRelease)
+ return bFilterEvent;
+
+ /*
+ * fix broken key release handling of some IMs
+ */
+ XKeyEvent* pKeyEvent = &(pEvent->xkey);
+ static XKeyEventOp s_aLastKeyPress;
+
+ if (bFilterEvent)
+ {
+ if (pKeyEvent->type == KeyRelease)
+ bFilterEvent = !s_aLastKeyPress.match (*pKeyEvent);
+ s_aLastKeyPress.erase();
+ }
+ else /* (!bFilterEvent) */
+ {
+ if (pKeyEvent->type == KeyPress)
+ s_aLastKeyPress = *pKeyEvent;
+ else
+ s_aLastKeyPress.erase();
+ }
+
+ return bFilterEvent;
+}
+
+void
+SalI18N_InputMethod::HandleDestroyIM()
+{
+ mbUseable = False;
+ maMethod = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/i18n_keysym.cxx b/vcl/unx/generic/app/i18n_keysym.cxx
new file mode 100644
index 0000000000..a77632a3e7
--- /dev/null
+++ b/vcl/unx/generic/app/i18n_keysym.cxx
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <X11/X.h>
+#include <sal/types.h>
+
+#include <unx/i18n_keysym.hxx>
+
+// convert keysyms to unicode
+// for all keysyms with byte1 and byte2 equal zero, and of course only for
+// keysyms that have a unicode counterpart
+
+namespace {
+
+struct keymap_t {
+ const int first; const int last;
+ const sal_Unicode *map;
+};
+
+}
+
+// Latin-1 Byte 3 = 0x00
+const sal_Unicode keymap00_map[] = {
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff };
+const keymap_t keymap00 = { 32, 255, keymap00_map };
+
+// Latin-2 Byte 3 = 0x01
+const sal_Unicode keymap01_map[] = {
+ 0x0104, 0x02d8, 0x0141, 0x0000, 0x013d, 0x015a, 0x0000, 0x0000,
+ 0x0160, 0x015e, 0x0164, 0x0179, 0x0000, 0x017d, 0x017b, 0x0000,
+ 0x0105, 0x02db, 0x0142, 0x0000, 0x013e, 0x015b, 0x02c7, 0x0000,
+ 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, 0x0154,
+ 0x0000, 0x0000, 0x0102, 0x0000, 0x0139, 0x0106, 0x0000, 0x010c,
+ 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x0000, 0x010e, 0x0110,
+ 0x0143, 0x0147, 0x0000, 0x0000, 0x0150, 0x0000, 0x0000, 0x0158,
+ 0x016e, 0x0000, 0x0170, 0x0000, 0x0000, 0x0162, 0x0000, 0x0155,
+ 0x0000, 0x0000, 0x0103, 0x0000, 0x013a, 0x0107, 0x0000, 0x010d,
+ 0x0000, 0x0119, 0x0000, 0x011b, 0x0000, 0x0000, 0x010f, 0x0111,
+ 0x0144, 0x0148, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, 0x0159,
+ 0x016f, 0x0000, 0x0171, 0x0000, 0x0000, 0x0163, 0x02d9 };
+const keymap_t keymap01 = { 161, 255, keymap01_map };
+
+// Latin-3 Byte 3 = 0x02
+const sal_Unicode keymap02_map[] = {
+ 0x0126, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0000, 0x0000,
+ 0x0130, 0x0000, 0x011e, 0x0134, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0127, 0x0000, 0x0000, 0x0000, 0x0000, 0x0125, 0x0000, 0x0000,
+ 0x0131, 0x0000, 0x011f, 0x0135, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x010a, 0x0108, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0120, 0x0000, 0x0000, 0x011c,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x015c, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x010b, 0x0109, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0121, 0x0000, 0x0000, 0x011d,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x015d };
+const keymap_t keymap02 = { 161, 254, keymap02_map };
+
+// Latin-4 Byte 3 = 0x03
+const sal_Unicode keymap03_map[] = {
+ 0x0138, 0x0156, 0x0000, 0x0128, 0x013b, 0x0000, 0x0000, 0x0000,
+ 0x0112, 0x0122, 0x0166, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0157, 0x0000, 0x0129, 0x013c, 0x0000, 0x0000, 0x0000,
+ 0x0113, 0x0123, 0x0167, 0x014a, 0x0000, 0x014b, 0x0100, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012e, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0116, 0x0000, 0x0000, 0x012a, 0x0000, 0x0145,
+ 0x014c, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0172,
+ 0x0000, 0x0000, 0x0000, 0x0168, 0x016a, 0x0000, 0x0101, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012f, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0117, 0x0000, 0x0000, 0x012b, 0x0000, 0x0146,
+ 0x014d, 0x0137, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173,
+ 0x0000, 0x0000, 0x0000, 0x0169, 0x016b };
+const keymap_t keymap03 = { 162, 254, keymap03_map };
+
+// Kana Byte 3 = 0x04
+const sal_Unicode keymap04_map[] = {
+ 0x203e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x3002, 0x300c, 0x300d, 0x3001, 0x30fb,
+ 0x30f2, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5,
+ 0x30e7, 0x30c3, 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa,
+ 0x30ab, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9,
+ 0x30bb, 0x30bd, 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca,
+ 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8,
+ 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6,
+ 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3,
+ 0x309b, 0x309c };
+const keymap_t keymap04 = { 126, 223, keymap04_map };
+
+// Arabic Byte 3 = 0x05
+const sal_Unicode keymap05_map[] = {
+ 0x060c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061b,
+ 0x0000, 0x0000, 0x0000, 0x061f, 0x0000, 0x0621, 0x0622, 0x0623,
+ 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b,
+ 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633,
+ 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0640, 0x0641, 0x0642, 0x0643,
+ 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b,
+ 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652 };
+const keymap_t keymap05 = { 172, 242, keymap05_map };
+
+// Cyrillic Byte 3 = 0x06
+const sal_Unicode keymap06_map[] = {
+ 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458,
+ 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, 0x2116,
+ 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408,
+ 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, 0x044e,
+ 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445,
+ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
+ 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c,
+ 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, 0x042e,
+ 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425,
+ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+ 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c,
+ 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a };
+const keymap_t keymap06 = { 161, 255, keymap06_map };
+
+// Greek Byte 3 = 0x07
+const sal_Unicode keymap07_map[] = {
+ 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, 0x038e,
+ 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, 0x0000,
+ 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, 0x03cd,
+ 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
+ 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0,
+ 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8,
+ 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8,
+ 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0,
+ 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8,
+ 0x03c9 };
+const keymap_t keymap07 = { 161, 249, keymap07_map };
+
+// Technical Byte 3 = 0x08
+const sal_Unicode keymap08_map[] = {
+ 0x23b7, 0x250c, 0x2500, 0x2320, 0x2321, 0x2502, 0x23a1, 0x23a3,
+ 0x23a4, 0x23a6, 0x239b, 0x239d, 0x239e, 0x23a0, 0x23a8, 0x23ac,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222b, 0x2234,
+ 0x221d, 0x221e, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, 0x223c,
+ 0x2243, 0x0000, 0x0000, 0x0000, 0x21d4, 0x21d2, 0x2261, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221a, 0x0000, 0x0000,
+ 0x0000, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193 };
+const keymap_t keymap08 = { 161, 254, keymap08_map };
+
+// Special Byte 3 = 0x09
+const sal_Unicode keymap09_map[] = {
+ 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x0000, 0x0000,
+ 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
+ 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
+ 0x2502 };
+const keymap_t keymap09 = { 224, 248, keymap09_map };
+
+// Publishing Byte 3 = 0x0a = 10
+const sal_Unicode keymap10_map[] = {
+ 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, 0x200a,
+ 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, 0x2153,
+ 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x2105,
+ 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, 0x0000,
+ 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, 0x2018,
+ 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, 0x0000,
+ 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, 0x25e6,
+ 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, 0x25b2,
+ 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, 0x2720,
+ 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, 0x2640,
+ 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e };
+const keymap_t keymap10 = { 161, 254, keymap10_map };
+
+// APL Byte 3 = 0x0b = 11
+const sal_Unicode keymap11_map[] = {
+ 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, 0x2228, 0x2227, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00af, 0x0000, 0x22a5,
+ 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, 0x0000, 0x0000, 0x2218,
+ 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, 0x0000, 0x0000, 0x0000,
+ 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, 0x2283, 0x0000, 0x2282,
+ 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x22a3 };
+const keymap_t keymap11 = { 163, 252, keymap11_map };
+
+// Hebrew Byte 3 = 0x0c = 12
+const sal_Unicode keymap12_map[] = {
+ 0x2017, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6,
+ 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de,
+ 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6,
+ 0x05e7, 0x05e8, 0x05e9, 0x05ea };
+const keymap_t keymap12 = { 223, 250, keymap12_map };
+
+// Thai Byte 3 = 0x0d = 13
+const sal_Unicode keymap13_map[] = {
+ 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08,
+ 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10,
+ 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18,
+ 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20,
+ 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28,
+ 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30,
+ 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38,
+ 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0e3f, 0x0e40,
+ 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, 0x0e48,
+ 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0000, 0x0000, 0x0e50,
+ 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58,
+ 0x0e59 };
+const keymap_t keymap13 = { 161, 249, keymap13_map };
+
+// Korean Byte 3 = 0x0e = 14
+const sal_Unicode keymap14_map[] = {
+ 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138,
+ 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, 0x3140,
+ 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148,
+ 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, 0x3150,
+ 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158,
+ 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160,
+ 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, 0x11ac,
+ 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4,
+ 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, 0x11bc,
+ 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, 0x3171,
+ 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, 0x11eb,
+ 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 };
+const keymap_t keymap14 = { 161, 255, keymap14_map };
+
+// missing:
+// Latin-8 Byte 3 = 0x12 = 18
+
+// Latin-9 Byte 3 = 0x13 = 19
+const sal_Unicode keymap19_map[] = {
+ 0x0152, 0x0153, 0x0178 };
+const keymap_t keymap19 = { 188, 190, keymap19_map };
+
+// missing:
+// Armenian Byte 3 = 0x14 = 20
+// Georgian Byte 3 = 0x15 = 21
+// Azeri Byte 3 = 0x16 = 22
+// Vietnamese Byte 3 = 0x1e = 30
+
+// Currency Byte 3 = 0x20 = 32
+const sal_Unicode keymap32_map[] = {
+ 0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7,
+ 0x20a8, 0x0000, 0x20aa, 0x20ab, 0x20ac };
+const keymap_t keymap32 = { 160, 172, keymap32_map };
+
+// Keyboard (Keypad mappings) Byte 3 = 0xff = 255
+const sal_Unicode keymap255_map[] = {
+ 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x0000, 0x0000, 0x0000, 0x003d };
+const keymap_t keymap255 = { 128, 189, keymap255_map };
+
+#define INITIAL_KEYMAPS 33
+const keymap_t* const p_keymap[INITIAL_KEYMAPS] = {
+ &keymap00, &keymap01, &keymap02, &keymap03, /* 00 -- 03 */
+ &keymap04, &keymap05, &keymap06, &keymap07, /* 04 -- 07 */
+ &keymap08, &keymap09, &keymap10, &keymap11, /* 08 -- 11 */
+ &keymap12, &keymap13, &keymap14, nullptr, /* 12 -- 15 */
+ nullptr, nullptr, nullptr, &keymap19, /* 16 -- 19 */
+ nullptr, nullptr, nullptr, nullptr, /* 20 -- 23 */
+ nullptr, nullptr, nullptr, nullptr, /* 24 -- 27 */
+ nullptr, nullptr, nullptr, nullptr, /* 28 -- 31 */
+ &keymap32 /* 32 */
+};
+
+sal_Unicode
+KeysymToUnicode (KeySym nKeySym)
+{
+ // keysym is already unicode
+ if ((nKeySym & 0xff000000) == 0x01000000)
+ {
+ // strip off group indicator and iso10646 plane
+ // FIXME can't handle chars from surrogate area.
+ if (! (nKeySym & 0x00ff0000) )
+ return static_cast<sal_Unicode>(nKeySym & 0x0000ffff);
+ }
+ // legacy keysyms, switch to appropriate codeset
+ else
+ {
+ unsigned char n_byte1 = (nKeySym & 0xff000000) >> 24;
+ unsigned char n_byte2 = (nKeySym & 0x00ff0000) >> 16;
+ unsigned char n_byte3 = (nKeySym & 0x0000ff00) >> 8;
+ unsigned char n_byte4 = (nKeySym & 0x000000ff);
+
+ if (n_byte1 != 0)
+ return 0;
+ if (n_byte2 != 0)
+ return 0;
+
+ keymap_t const* p_map = nullptr;
+ if (n_byte3 < INITIAL_KEYMAPS)
+ p_map = p_keymap[n_byte3];
+ else if (n_byte3 == 255)
+ p_map = &keymap255;
+
+ if ((p_map != nullptr) && (n_byte4 >= p_map->first) && (n_byte4 <= p_map->last) )
+ return p_map->map[n_byte4 - p_map->first];
+ }
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/i18n_xkb.cxx b/vcl/unx/generic/app/i18n_xkb.cxx
new file mode 100644
index 0000000000..0fc4d7933f
--- /dev/null
+++ b/vcl/unx/generic/app/i18n_xkb.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+
+#include <sal/log.hxx>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include <unx/i18n_xkb.hxx>
+
+SalI18N_KeyboardExtension::SalI18N_KeyboardExtension( Display* pDisplay )
+ : mbUseExtension(true)
+ , mnEventBase(0)
+{
+ // allow user to set the default keyboard group idx or to disable the usage
+ // of x keyboard extension at all:
+ // setenv SAL_XKEYBOARDGROUP disables keyboard extension
+ // setenv SAL_XKEYBOARDGROUP 2 sets the keyboard group index to 2
+ // keyboard group index must be in [1,4], may be specified in hex or decimal
+ static char *pUseKeyboardExtension = getenv( "SAL_XKEYBOARDGROUP" );
+ if ( pUseKeyboardExtension != nullptr )
+ {
+ mbUseExtension = pUseKeyboardExtension[0] != '\0' ;
+ }
+
+ // query XServer support for XKB Extension,
+ // do not call XQueryExtension() / XInitExtension() due to possible version
+ // clashes !
+ if ( mbUseExtension )
+ {
+ int nMajorExtOpcode;
+ int nExtMajorVersion = XkbMajorVersion;
+ int nExtMinorVersion = XkbMinorVersion;
+ int nErrorBase = 0;
+
+ mbUseExtension = XkbQueryExtension( pDisplay,
+ &nMajorExtOpcode, &mnEventBase, &nErrorBase,
+ &nExtMajorVersion, &nExtMinorVersion ) != 0;
+ }
+
+ // query notification for changes of the keyboard group
+ if ( mbUseExtension )
+ {
+ constexpr auto XkbGroupMask = XkbGroupStateMask | XkbGroupBaseMask
+ | XkbGroupLatchMask | XkbGroupLockMask;
+
+ mbUseExtension = XkbSelectEventDetails( pDisplay,
+ XkbUseCoreKbd, XkbStateNotify, XkbGroupMask, XkbGroupMask );
+ }
+
+ // query initial keyboard group
+ if ( mbUseExtension )
+ {
+ XkbStateRec aStateRecord;
+ XkbGetState( pDisplay, XkbUseCoreKbd, &aStateRecord );
+ }
+}
+
+void
+SalI18N_KeyboardExtension::Dispatch( XEvent* pEvent )
+{
+ // must the event be handled?
+ if ( !mbUseExtension
+ || (pEvent->type != mnEventBase) )
+ return;
+
+ // only handle state notify events for now, and only interested
+ // in group details
+ sal_uInt32 nXKBType = reinterpret_cast<XkbAnyEvent*>(pEvent)->xkb_type;
+ switch ( nXKBType )
+ {
+ case XkbStateNotify:
+ break;
+
+ default:
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN("vcl.app", "Got unrequested XkbAnyEvent "
+ << std::hex << std::showbase
+ << static_cast<unsigned int>(nXKBType)
+ << "/" << std::dec
+ << static_cast<int>(nXKBType));
+#endif
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/keysymnames.cxx b/vcl/unx/generic/app/keysymnames.cxx
new file mode 100644
index 0000000000..d4842df955
--- /dev/null
+++ b/vcl/unx/generic/app/keysymnames.cxx
@@ -0,0 +1,509 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <o3tl/string_view.hxx>
+#include <unx/saldisp.hxx>
+#include <X11/keysym.h>
+#include <sal/macros.h>
+
+#if !defined (SunXK_Undo)
+#define SunXK_Undo 0x0000FF65 // XK_Undo
+#define SunXK_Again 0x0000FF66 // XK_Redo
+#define SunXK_Find 0x0000FF68 // XK_Find
+#define SunXK_Stop 0x0000FF69 // XK_Cancel
+#define SunXK_Props 0x1005FF70
+#define SunXK_Front 0x1005FF71
+#define SunXK_Copy 0x1005FF72
+#define SunXK_Open 0x1005FF73
+#define SunXK_Paste 0x1005FF74
+#define SunXK_Cut 0x1005FF75
+#endif
+
+#include <string.h>
+#include <rtl/ustring.hxx>
+
+namespace vcl_sal {
+
+ namespace {
+
+ struct KeysymNameReplacement
+ {
+ KeySym aSymbol;
+ const char* pName;
+ };
+
+ struct KeyboardReplacements
+ {
+ const char* pLangName;
+ const KeysymNameReplacement* pReplacements;
+ int nReplacements;
+ };
+
+ }
+
+ // CAUTION CAUTION CAUTION
+ // every string value in the replacements tables must be in UTF8
+ // be careful with your editor !
+
+ const struct KeysymNameReplacement aImplReplacements_English[] =
+ {
+ { XK_Control_L, "Ctrl" },
+ { XK_Control_R, "Ctrl" },
+ { XK_Escape, "Esc" },
+ { XK_space, "Space" },
+ { XK_Page_Up, "PgUp"},
+ { XK_Page_Down, "PgDn"},
+ { XK_grave, "`"}
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Turkish[] =
+ {
+ { XK_Control_L, "Ctrl" },
+ { XK_Control_R, "Ctrl" },
+ { XK_Right, "Sa\304\237" },
+ { XK_Left, "Sol" },
+ { XK_Up, "Yukar\304\261" },
+ { XK_Down, "A\305\237a\304\237\304\261" },
+ { XK_space, "Bo\305\237luk" }
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Russian[] =
+ {
+ { XK_Right, "\320\222\320\277\321\200\320\260\320\262\320\276" },
+ { XK_Left, "\320\222\320\273\320\265\320\262\320\276" },
+ { XK_Up, "\320\222\320\262\320\265\321\200\321\205" },
+ { XK_Down, "\320\222\320\275\320\270\320\267" },
+ { XK_space, "\320\237\321\200\320\276\320\261\320\265\320\273" }
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_German[] =
+ {
+ { XK_Control_L, "Strg" },
+ { XK_Control_R, "Strg" },
+ { XK_Shift_L, "Umschalt" },
+ { XK_Shift_R, "Umschalt" },
+ { XK_Alt_L, "Alt" },
+ { XK_Alt_R, "Alt Gr" },
+ { XK_Page_Up, "Bild auf" },
+ { XK_Page_Down, "Bild ab" },
+ { XK_End, "Ende" },
+ { XK_Home, "Pos 1" },
+ { XK_Insert, "Einfg" },
+ { XK_Delete, "Entf" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Rechts" },
+ { XK_Left, "Links" },
+ { XK_Up, "Oben" },
+ { XK_Down, "Unten" },
+ { XK_BackSpace, "R\303\274ckschritt" },
+ { XK_Return, "Eingabe" },
+ { XK_slash, "Schr\303\244gstrich" },
+ { XK_space, "Leertaste" },
+ { SunXK_Stop, "Stop" },
+ { SunXK_Again, "Wiederholen" },
+ { SunXK_Props, "Eigenschaften" },
+ { SunXK_Undo, "Zur\303\274cknehmen" },
+ { SunXK_Front, "Vordergrund" },
+ { SunXK_Copy, "Kopieren" },
+ { SunXK_Open, "\303\226ffnen" },
+ { SunXK_Paste, "Einsetzen" },
+ { SunXK_Find, "Suchen" },
+ { SunXK_Cut, "Ausschneiden" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_French[] =
+ {
+ { XK_Shift_L, "Maj" },
+ { XK_Shift_R, "Maj" },
+ { XK_Page_Up, "Pg. Pr\303\251c" },
+ { XK_Page_Down, "Pg. Suiv" },
+ { XK_End, "Fin" },
+ { XK_Home, "Origine" },
+ { XK_Insert, "Ins\303\251rer" },
+ { XK_Delete, "Suppr" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Droite" },
+ { XK_Left, "Gauche" },
+ { XK_Up, "Haut" },
+ { XK_Down, "Bas" },
+ { XK_BackSpace, "Ret. Arr" },
+ { XK_Return, "Retour" },
+ { XK_space, "Espace" },
+ { XK_KP_Enter, "Entr\303\251e" },
+ { SunXK_Stop, "Stop" },
+ { SunXK_Again, "Encore" },
+ { SunXK_Props, "Props" },
+ { SunXK_Undo, "Annuler" },
+ { SunXK_Front, "Devant" },
+ { SunXK_Copy, "Copy" },
+ { SunXK_Open, "Ouvrir" },
+ { SunXK_Paste, "Coller" },
+ { SunXK_Find, "Cher." },
+ { SunXK_Cut, "Couper" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Italian[] =
+ {
+ { XK_Shift_L, "Maiusc" },
+ { XK_Shift_R, "Maiusc" },
+ { XK_Page_Up, "PgSu" },
+ { XK_Page_Down, "PgGiu" },
+ { XK_End, "Fine" },
+ { XK_Insert, "Ins" },
+ { XK_Delete, "Canc" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "A destra" },
+ { XK_Left, "A sinistra" },
+ { XK_Up, "Sposta verso l'alto" },
+ { XK_Down, "Sposta verso il basso" },
+ { XK_BackSpace, "Backspace" },
+ { XK_Return, "Invio" },
+ { XK_space, "Spazio" },
+ { SunXK_Stop, "Stop" },
+ { SunXK_Again, "Ancora" },
+ { SunXK_Props, "Propriet\303\240" },
+ { SunXK_Undo, "Annulla" },
+ { SunXK_Front, "Davanti" },
+ { SunXK_Copy, "Copia" },
+ { SunXK_Open, "Apri" },
+ { SunXK_Paste, "Incolla" },
+ { SunXK_Find, "Trova" },
+ { SunXK_Cut, "Taglia" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Dutch[] =
+ {
+ { XK_Page_Up, "PageUp" },
+ { XK_Page_Down, "PageDown" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Rechts" },
+ { XK_Left, "Links" },
+ { XK_Up, "Boven" },
+ { XK_Down, "Onder" },
+ { XK_BackSpace, "Backspace" },
+ { XK_Return, "Return" },
+ { XK_space, "Spatiebalk" },
+ { SunXK_Stop, "Stop" },
+ { SunXK_Again, "Again" },
+ { SunXK_Props, "Props" },
+ { SunXK_Undo, "Undo" },
+ { SunXK_Front, "Front" },
+ { SunXK_Copy, "Copy" },
+ { SunXK_Open, "Open" },
+ { SunXK_Paste, "Paste" },
+ { SunXK_Find, "Find" },
+ { SunXK_Cut, "Cut" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Norwegian[] =
+ {
+ { XK_Shift_L, "Skift" },
+ { XK_Shift_R, "Skift" },
+ { XK_Page_Up, "PageUp" },
+ { XK_Page_Down, "PageDown" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "H\303\270yre" },
+ { XK_Left, "Venstre" },
+ { XK_Up, "Opp" },
+ { XK_Down, "Ned" },
+ { XK_BackSpace, "Tilbake" },
+ { XK_Return, "Enter" },
+ { SunXK_Stop, "Avbryt" },
+ { SunXK_Again, "Gjenta" },
+ { SunXK_Props, "Egenskaper" },
+ { SunXK_Undo, "Angre" },
+ { SunXK_Front, "Front" },
+ { SunXK_Copy, "Kopi" },
+ { SunXK_Open, "\303\205pne" },
+ { SunXK_Paste, "Lim" },
+ { SunXK_Find, "S\303\270k" },
+ { SunXK_Cut, "Klipp" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Swedish[] =
+ {
+ { XK_Shift_L, "Skift" },
+ { XK_Shift_R, "Skift" },
+ { XK_Page_Up, "PageUp" },
+ { XK_Page_Down, "PageDown" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "H\303\266ger" },
+ { XK_Left, "V\303\244nster" },
+ { XK_Up, "Up" },
+ { XK_Down, "Ned" },
+ { XK_BackSpace, "Backsteg" },
+ { XK_Return, "Retur" },
+ { XK_space, "Blank" },
+ { SunXK_Stop, "Avbryt" },
+ { SunXK_Again, "Upprepa" },
+ { SunXK_Props, "Egenskaper" },
+ { SunXK_Undo, "\303\205ngra" },
+ { SunXK_Front, "Fram" },
+ { SunXK_Copy, "Kopiera" },
+ { SunXK_Open, "\303\226ppna" },
+ { SunXK_Paste, "Klistra in" },
+ { SunXK_Find, "S\303\266k" },
+ { SunXK_Cut, "Klipp ut" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Portuguese[] =
+ {
+ { XK_Page_Up, "PageUp" },
+ { XK_Page_Down, "PageDown" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Direita" },
+ { XK_Left, "Esquerda" },
+ { XK_Up, "Acima" },
+ { XK_Down, "Abaixo" },
+ { XK_BackSpace, "Backspace" },
+ { XK_Return, "Enter" },
+ { XK_slash, "Barra" },
+ { SunXK_Stop, "Stop" },
+ { SunXK_Again, "Again" },
+ { SunXK_Props, "Props" },
+ { SunXK_Undo, "Undo" },
+ { SunXK_Front, "Front" },
+ { SunXK_Copy, "Copy" },
+ { SunXK_Open, "Open" },
+ { SunXK_Paste, "Paste" },
+ { SunXK_Find, "Find" },
+ { SunXK_Cut, "Cut" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Slovenian[] =
+ {
+ { XK_Control_L, "Krmilka" },
+ { XK_Control_R, "Krmilka" },
+ { XK_Shift_L, "Dvigalka" },
+ { XK_Shift_R, "Dvigalka" },
+ { XK_Alt_L, "Izmenjalka" },
+ { XK_Alt_R, "Desna izmenjalka" },
+ { XK_Page_Up, "Prej\305\241nja stranf" },
+ { XK_Page_Down, "Naslednja stran" },
+ { XK_End, "Konec" },
+ { XK_Home, "Za\304\215etek" },
+ { XK_Insert, "Vstavljalka" },
+ { XK_Delete, "Brisalka" },
+ { XK_Escape, "Ube\305\276nica" },
+ { XK_Right, "Desno" },
+ { XK_Left, "Levo" },
+ { XK_Up, "Navzgor" },
+ { XK_Down, "Navzdol" },
+ { XK_BackSpace, "Vra\304\215alka" },
+ { XK_Return, "Vna\305\241alka" },
+ { XK_slash, "Po\305\241evnica" },
+ { XK_space, "Preslednica" },
+ { SunXK_Stop, "Ustavi" },
+ { SunXK_Again, "Ponovi" },
+ { SunXK_Props, "Lastnosti" },
+ { SunXK_Undo, "Razveljavi" },
+ { SunXK_Front, "Ospredje" },
+ { SunXK_Copy, "Kopiraj" },
+ { SunXK_Open, "Odpri" },
+ { SunXK_Paste, "Prilepi" },
+ { SunXK_Find, "Najdi" },
+ { SunXK_Cut, "Izre\305\276i" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Spanish[] =
+ {
+ { XK_Shift_L, "May\303\272s" },
+ { XK_Shift_R, "May\303\272s" },
+ { XK_Page_Up, "ReP\303\241g" },
+ { XK_Page_Down, "AvP\303\241g" },
+ { XK_End, "Fin" },
+ { XK_Home, "Inicio" },
+ { XK_Delete, "Supr" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Derecha" },
+ { XK_Left, "Izquierda" },
+ { XK_Up, "Arriba" },
+ { XK_Down, "Abajo" },
+ { XK_BackSpace, "Ret" },
+ { XK_Return, "Entrada" },
+ { XK_space, "Espacio" },
+ { XK_KP_Enter, "Intro" },
+ { SunXK_Stop, "Detener" },
+ { SunXK_Again, "Repetir" },
+ { SunXK_Props, "Props" },
+ { SunXK_Undo, "Anular" },
+ { SunXK_Front, "Delante" },
+ { SunXK_Copy, "Copiar" },
+ { SunXK_Open, "Abrir" },
+ { SunXK_Paste, "Pegar" },
+ { SunXK_Find, "Buscar" },
+ { SunXK_Cut, "Cortar" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Estonian[] =
+ {
+ { XK_Page_Up, "PgUp" },
+ { XK_Page_Down, "PgDown" },
+ { XK_End, "End" },
+ { XK_Home, "Home" },
+ { XK_Insert, "Ins" },
+ { XK_Delete, "Del" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Nool paremale" },
+ { XK_Left, "Nool vasakule" },
+ { XK_Up, "Nool \303\274les" },
+ { XK_Down, "Nool alla" },
+ { XK_BackSpace, "Tagasil\303\274ke" },
+ { XK_Return, "Enter" },
+ { XK_slash, "Kaldkriips" },
+ { XK_space, "T\303\274hik" },
+ { XK_asterisk, "T\303\244rn" },
+ { SunXK_Stop, "Peata" },
+ { SunXK_Again, "Korda" },
+ { SunXK_Props, "Omadused" },
+ { SunXK_Undo, "V\303\265ta tagasi" },
+ { SunXK_Front, "Esiplaanile" },
+ { SunXK_Copy, "Kopeeri" },
+ { SunXK_Open, "Ava" },
+ { SunXK_Paste, "Aseta" },
+ { SunXK_Find, "Otsi" },
+ { SunXK_Cut, "L\303\265ika" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Catalan[] =
+ {
+ { XK_Shift_L, "Maj" },
+ { XK_Shift_R, "Maj" },
+ { XK_Page_Up, "Re P\303\240g" },
+ { XK_Page_Down, "Av P\303\240g" },
+ { XK_End, "Fi" },
+ { XK_Home, "Inici" },
+ { XK_Delete, "Supr" },
+ { XK_Escape, "Esc" },
+ { XK_Right, "Dreta" },
+ { XK_Left, "Esquerra" },
+ { XK_Up, "Amunt" },
+ { XK_Down, "Avall" },
+ { XK_BackSpace, "Retroc\303\251s" },
+ { XK_Return, "Retorn" },
+ { XK_space, "Espai" },
+ { XK_KP_Enter, "Retorn" },
+ { SunXK_Stop, "Atura" },
+ { SunXK_Again, "Repeteix" },
+ { SunXK_Props, "Props" },
+ { SunXK_Undo, "Desf\303\251s" },
+ { SunXK_Front, "Davant" },
+ { SunXK_Copy, "C\303\262pia" },
+ { SunXK_Open, "Obre" },
+ { SunXK_Paste, "Enganxa" },
+ { SunXK_Find, "Cerca" },
+ { SunXK_Cut, "Retalla" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Lithuanian[] =
+ {
+ { XK_Control_L, "Vald" },
+ { XK_Control_R, "Vald" },
+ { XK_Shift_L, "Lyg2" },
+ { XK_Shift_R, "Lyg2" },
+ { XK_Alt_L, "Alt" },
+ { XK_Alt_R, "Lyg3" },
+ { XK_Page_Up, "Psl\342\206\221" },
+ { XK_Page_Down, "Psl\342\206\223" },
+ { XK_End, "Pab" },
+ { XK_Home, "Prad" },
+ { XK_Insert, "\304\256terpti" },
+ { XK_Delete, "\305\240al" },
+ { XK_Escape, "Gr" },
+ { XK_Right, "De\305\241in\304\227n" },
+ { XK_Left, "Kair\304\227n" },
+ { XK_Up, "Auk\305\241tyn" },
+ { XK_Down, "\305\275emyn" },
+ { XK_BackSpace, "Naikinti" },
+ { XK_Return, "\304\256vesti" },
+ { XK_asterisk, "\305\275vaig\305\276dut\304\227" },
+ { XK_slash, "De\305\241ininis br\305\253k\305\241nys" },
+ { XK_space, "Tarpas" },
+ { SunXK_Stop, "Stabdyti" },
+ { SunXK_Again, "Kartoti" },
+ { SunXK_Props, "Savyb\304\227s" },
+ { SunXK_Undo, "At\305\241aukti" },
+ { SunXK_Front, "Priekinis planas" },
+ { SunXK_Copy, "Kopijuoti" },
+ { SunXK_Open, "Atverti" },
+ { SunXK_Paste, "\304\256d\304\227ti" },
+ { SunXK_Find, "Ie\305\241koti" },
+ { SunXK_Cut, "I\305\241kirpti" },
+ };
+
+ const struct KeysymNameReplacement aImplReplacements_Hungarian[] =
+ {
+ { XK_Right, "Jobbra" },
+ { XK_Left, "Balra" },
+ { XK_Up, "Fel" },
+ { XK_Down, "Le" },
+ { XK_Return, "Enter" },
+ { XK_space, "Sz\303\263k\303\266z" },
+ { XK_asterisk, "Csillag" },
+ { XK_slash, "Oszt\303\241sjel" },
+ };
+
+ const struct KeyboardReplacements aKeyboards[] =
+ {
+ { "ca", aImplReplacements_Catalan, std::size(aImplReplacements_Catalan) },
+ { "de", aImplReplacements_German, std::size(aImplReplacements_German) },
+ { "sl", aImplReplacements_Slovenian, std::size(aImplReplacements_Slovenian) },
+ { "es", aImplReplacements_Spanish, std::size(aImplReplacements_Spanish) },
+ { "et", aImplReplacements_Estonian, std::size(aImplReplacements_Estonian) },
+ { "fr", aImplReplacements_French, std::size(aImplReplacements_French) },
+ { "hu", aImplReplacements_Hungarian, std::size(aImplReplacements_Hungarian) },
+ { "it", aImplReplacements_Italian, std::size(aImplReplacements_Italian) },
+ { "lt", aImplReplacements_Lithuanian, std::size(aImplReplacements_Lithuanian) },
+ { "nl", aImplReplacements_Dutch, std::size(aImplReplacements_Dutch) },
+ { "no", aImplReplacements_Norwegian, std::size(aImplReplacements_Norwegian) },
+ { "pt", aImplReplacements_Portuguese, std::size(aImplReplacements_Portuguese) },
+ { "ru", aImplReplacements_Russian, std::size(aImplReplacements_Russian) },
+ { "sv", aImplReplacements_Swedish, std::size(aImplReplacements_Swedish) },
+ { "tr", aImplReplacements_Turkish, std::size(aImplReplacements_Turkish) },
+ };
+
+ // translate keycodes, used within the displayed menu shortcuts
+ OUString getKeysymReplacementName( std::u16string_view pLang, KeySym nSymbol )
+ {
+ for(const auto & rKeyboard : aKeyboards)
+ {
+ if( o3tl::equalsAscii( pLang, rKeyboard.pLangName ) )
+ {
+ const struct KeysymNameReplacement* pRepl = rKeyboard.pReplacements;
+ for( int m = rKeyboard.nReplacements ; m ; )
+ {
+ if( nSymbol == pRepl[--m].aSymbol )
+ return OUString( pRepl[m].pName, strlen(pRepl[m].pName), RTL_TEXTENCODING_UTF8 );
+ }
+ }
+ }
+
+ // try english fallbacks
+ const struct KeysymNameReplacement* pRepl = aImplReplacements_English;
+ for( int m = SAL_N_ELEMENTS(aImplReplacements_English); m ; )
+ {
+ if( nSymbol == pRepl[--m].aSymbol )
+ return OUString( pRepl[m].pName, strlen(pRepl[m].pName), RTL_TEXTENCODING_UTF8 );
+ }
+
+ return OUString();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/randrwrapper.cxx b/vcl/unx/generic/app/randrwrapper.cxx
new file mode 100644
index 0000000000..1ef474c347
--- /dev/null
+++ b/vcl/unx/generic/app/randrwrapper.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifdef USE_RANDR
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <sal/log.hxx>
+
+namespace
+{
+
+class RandRWrapper
+{
+ bool m_bValid;
+
+ explicit RandRWrapper(Display*);
+public:
+ static RandRWrapper& get(Display*);
+ static void releaseWrapper();
+
+ Bool XRRQueryExtension(Display* i_pDisp, int* o_event_base, int* o_error_base )
+ {
+ Bool bRet = False;
+ if( m_bValid )
+ bRet = ::XRRQueryExtension( i_pDisp, o_event_base, o_error_base );
+ return bRet;
+ }
+ XRRScreenConfiguration* XRRGetScreenInfo( Display* i_pDisp, Drawable i_aDrawable )
+ {
+ return m_bValid ? ::XRRGetScreenInfo( i_pDisp, i_aDrawable ) : nullptr;
+ }
+ void XRRFreeScreenConfigInfo( XRRScreenConfiguration* i_pConfig )
+ {
+ if( m_bValid )
+ ::XRRFreeScreenConfigInfo( i_pConfig );
+ }
+ void XRRSelectInput( Display* i_pDisp, ::Window i_window, int i_nMask )
+ {
+ if( m_bValid )
+ ::XRRSelectInput( i_pDisp, i_window, i_nMask );
+ }
+ int XRRUpdateConfiguration( XEvent* i_pEvent )
+ {
+ return m_bValid ? ::XRRUpdateConfiguration( i_pEvent ) : 0;
+ }
+ XRRScreenSize* XRRConfigSizes( XRRScreenConfiguration* i_pConfig, int* o_nSizes )
+ {
+ return m_bValid ? ::XRRConfigSizes( i_pConfig, o_nSizes ) : nullptr;
+ }
+ SizeID XRRConfigCurrentConfiguration( XRRScreenConfiguration* i_pConfig, Rotation* o_pRot )
+ {
+ return m_bValid ? ::XRRConfigCurrentConfiguration( i_pConfig, o_pRot ) : 0;
+ }
+ int XRRRootToScreen( Display *dpy, ::Window root )
+ {
+ return m_bValid ? ::XRRRootToScreen( dpy, root ) : -1;
+ }
+};
+
+RandRWrapper::RandRWrapper( Display* pDisplay ) :
+ m_bValid( true )
+{
+ int nEventBase = 0, nErrorBase = 0;
+ if( !XRRQueryExtension( pDisplay, &nEventBase, &nErrorBase ) )
+ m_bValid = false;
+}
+
+RandRWrapper* pWrapper = nullptr;
+
+RandRWrapper& RandRWrapper::get( Display* i_pDisplay )
+{
+ if( ! pWrapper )
+ pWrapper = new RandRWrapper( i_pDisplay );
+ return *pWrapper;
+}
+
+void RandRWrapper::releaseWrapper()
+{
+ delete pWrapper;
+ pWrapper = nullptr;
+}
+
+} // namespace
+
+#endif
+
+#include <unx/saldisp.hxx>
+#if OSL_DEBUG_LEVEL > 1
+#include <cstdio>
+#endif
+
+void SalDisplay::InitRandR( ::Window aRoot ) const
+{
+ #ifdef USE_RANDR
+ RandRWrapper::get( GetDisplay() ).XRRSelectInput( GetDisplay(), aRoot, RRScreenChangeNotifyMask );
+ #else
+ (void)this;
+ (void)aRoot;
+ #endif
+}
+
+void SalDisplay::DeInitRandR()
+{
+ #ifdef USE_RANDR
+ RandRWrapper::releaseWrapper();
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "SalDisplay::DeInitRandR().");
+#endif
+ #endif
+}
+
+void SalDisplay::processRandREvent( XEvent* pEvent )
+{
+#ifdef USE_RANDR
+ XConfigureEvent* pCnfEvent=reinterpret_cast<XConfigureEvent*>(pEvent);
+ if( !pWrapper || pWrapper->XRRRootToScreen(GetDisplay(),pCnfEvent->window) == -1 )
+ return;
+
+ int nRet = pWrapper->XRRUpdateConfiguration( pEvent );
+ if( nRet != 1 || pEvent->type == ConfigureNotify) // this should then be a XRRScreenChangeNotifyEvent
+ return;
+
+ // update screens
+ bool bNotify = false;
+ for(ScreenData & rScreen : m_aScreens)
+ {
+ if( rScreen.m_bInit )
+ {
+ XRRScreenConfiguration *pConfig = nullptr;
+ XRRScreenSize *pSizes = nullptr;
+ int nSizes = 0;
+ Rotation nRot = 0;
+ SizeID nId = 0;
+
+ pConfig = pWrapper->XRRGetScreenInfo( GetDisplay(), rScreen.m_aRoot );
+ nId = pWrapper->XRRConfigCurrentConfiguration( pConfig, &nRot );
+ pSizes = pWrapper->XRRConfigSizes( pConfig, &nSizes );
+ XRRScreenSize *pTargetSize = pSizes + nId;
+
+ bNotify = bNotify ||
+ rScreen.m_aSize.Width() != pTargetSize->width ||
+ rScreen.m_aSize.Height() != pTargetSize->height;
+
+ rScreen.m_aSize = AbsoluteScreenPixelSize( pTargetSize->width, pTargetSize->height );
+
+ pWrapper->XRRFreeScreenConfigInfo( pConfig );
+
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "screen " << nId
+ << " changed to size " << (int)pTargetSize->width
+ << "x" << (int)pTargetSize->height);
+#endif
+ }
+ }
+ if( bNotify )
+ emitDisplayChanged();
+#else
+ (void)this;
+ (void)pEvent;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/saldata.cxx b/vcl/unx/generic/app/saldata.cxx
new file mode 100644
index 0000000000..34c7c08789
--- /dev/null
+++ b/vcl/unx/generic/app/saldata.cxx
@@ -0,0 +1,775 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <errno.h>
+#ifdef SUN
+#include <sys/systeminfo.h>
+#endif
+#ifdef FREEBSD
+#include <sys/types.h>
+#include <sys/time.h>
+#endif
+
+#include <osl/process.h>
+
+#include <unx/saldisp.hxx>
+#include <unx/saldata.hxx>
+#include <unx/salunxtime.h>
+#include <unx/sm.hxx>
+#include <unx/i18n_im.hxx>
+
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+
+#include <salinst.hxx>
+#include <saltimer.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/signal.h>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#include <vcl/svapp.hxx>
+
+X11SalData* GetX11SalData()
+{
+ return static_cast<X11SalData*>(ImplGetSVData()->mpSalData);
+}
+
+extern "C" {
+
+static int XErrorHdl( Display *pDisplay, XErrorEvent *pEvent )
+{
+ GetX11SalData()->XError( pDisplay, pEvent );
+ return 0;
+}
+
+static int XIOErrorHdl( Display * )
+{
+ if ( Application::IsMainThread() )
+ {
+ /* #106197# hack: until a real shutdown procedure exists
+ * _exit ASAP
+ */
+ if( ImplGetSVData()->maAppData.mbAppQuit )
+ _exit(1);
+
+ // really bad hack
+ if( ! SessionManagerClient::checkDocumentsSaved() )
+ /* oslSignalAction eToDo = */ osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, nullptr);
+ }
+
+ std::fprintf( stderr, "X IO Error\n" );
+ std::fflush( stdout );
+ std::fflush( stderr );
+
+ /* #106197# the same reasons to use _exit instead of exit in salmain
+ * do apply here. Since there is nothing to be done after an XIO
+ * error we have to _exit immediately.
+ */
+ _exit(1);
+ return 0;
+}
+
+}
+
+const struct timeval noyield_ = { 0, 0 };
+const struct timeval yield_ = { 0, 10000 };
+
+static const char* XRequest[] = {
+ // see /usr/lib/X11/XErrorDB, /usr/openwin/lib/XErrorDB ...
+ nullptr,
+ "X_CreateWindow",
+ "X_ChangeWindowAttributes",
+ "X_GetWindowAttributes",
+ "X_DestroyWindow",
+ "X_DestroySubwindows",
+ "X_ChangeSaveSet",
+ "X_ReparentWindow",
+ "X_MapWindow",
+ "X_MapSubwindows",
+ "X_UnmapWindow",
+ "X_UnmapSubwindows",
+ "X_ConfigureWindow",
+ "X_CirculateWindow",
+ "X_GetGeometry",
+ "X_QueryTree",
+ "X_InternAtom",
+ "X_GetAtomName",
+ "X_ChangeProperty",
+ "X_DeleteProperty",
+ "X_GetProperty",
+ "X_ListProperties",
+ "X_SetSelectionOwner",
+ "X_GetSelectionOwner",
+ "X_ConvertSelection",
+ "X_SendEvent",
+ "X_GrabPointer",
+ "X_UngrabPointer",
+ "X_GrabButton",
+ "X_UngrabButton",
+ "X_ChangeActivePointerGrab",
+ "X_GrabKeyboard",
+ "X_UngrabKeyboard",
+ "X_GrabKey",
+ "X_UngrabKey",
+ "X_AllowEvents",
+ "X_GrabServer",
+ "X_UngrabServer",
+ "X_QueryPointer",
+ "X_GetMotionEvents",
+ "X_TranslateCoords",
+ "X_WarpPointer",
+ "X_SetInputFocus",
+ "X_GetInputFocus",
+ "X_QueryKeymap",
+ "X_OpenFont",
+ "X_CloseFont",
+ "X_QueryFont",
+ "X_QueryTextExtents",
+ "X_ListFonts",
+ "X_ListFontsWithInfo",
+ "X_SetFontPath",
+ "X_GetFontPath",
+ "X_CreatePixmap",
+ "X_FreePixmap",
+ "X_CreateGC",
+ "X_ChangeGC",
+ "X_CopyGC",
+ "X_SetDashes",
+ "X_SetClipRectangles",
+ "X_FreeGC",
+ "X_ClearArea",
+ "X_CopyArea",
+ "X_CopyPlane",
+ "X_PolyPoint",
+ "X_PolyLine",
+ "X_PolySegment",
+ "X_PolyRectangle",
+ "X_PolyArc",
+ "X_FillPoly",
+ "X_PolyFillRectangle",
+ "X_PolyFillArc",
+ "X_PutImage",
+ "X_GetImage",
+ "X_PolyText8",
+ "X_PolyText16",
+ "X_ImageText8",
+ "X_ImageText16",
+ "X_CreateColormap",
+ "X_FreeColormap",
+ "X_CopyColormapAndFree",
+ "X_InstallColormap",
+ "X_UninstallColormap",
+ "X_ListInstalledColormaps",
+ "X_AllocColor",
+ "X_AllocNamedColor",
+ "X_AllocColorCells",
+ "X_AllocColorPlanes",
+ "X_FreeColors",
+ "X_StoreColors",
+ "X_StoreNamedColor",
+ "X_QueryColors",
+ "X_LookupColor",
+ "X_CreateCursor",
+ "X_CreateGlyphCursor",
+ "X_FreeCursor",
+ "X_RecolorCursor",
+ "X_QueryBestSize",
+ "X_QueryExtension",
+ "X_ListExtensions",
+ "X_ChangeKeyboardMapping",
+ "X_GetKeyboardMapping",
+ "X_ChangeKeyboardControl",
+ "X_GetKeyboardControl",
+ "X_Bell",
+ "X_ChangePointerControl",
+ "X_GetPointerControl",
+ "X_SetScreenSaver",
+ "X_GetScreenSaver",
+ "X_ChangeHosts",
+ "X_ListHosts",
+ "X_SetAccessControl",
+ "X_SetCloseDownMode",
+ "X_KillClient",
+ "X_RotateProperties",
+ "X_ForceScreenSaver",
+ "X_SetPointerMapping",
+ "X_GetPointerMapping",
+ "X_SetModifierMapping",
+ "X_GetModifierMapping",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "X_NoOperation"
+};
+
+X11SalData::X11SalData()
+ : GenericUnixSalData()
+{
+ pXLib_ = nullptr;
+
+ m_aOrigXIOErrorHandler = XSetIOErrorHandler ( XIOErrorHdl );
+ PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) );
+}
+
+X11SalData::~X11SalData()
+{
+ DeleteDisplay();
+ PopXErrorLevel();
+ XSetIOErrorHandler (m_aOrigXIOErrorHandler);
+}
+
+void X11SalData::Dispose()
+{
+ delete GetDisplay();
+ SetSalData( nullptr );
+}
+
+void X11SalData::DeleteDisplay()
+{
+ delete GetDisplay();
+ SetDisplay( nullptr );
+ pXLib_.reset();
+}
+
+void X11SalData::Init()
+{
+ pXLib_.reset(new SalXLib());
+ pXLib_->Init();
+}
+
+void X11SalData::ErrorTrapPush()
+{
+ PushXErrorLevel( true );
+}
+
+bool X11SalData::ErrorTrapPop( bool bIgnoreError )
+{
+ bool err = false;
+ if( !bIgnoreError )
+ err = HasXErrorOccurred();
+ ResetXErrorOccurred();
+ PopXErrorLevel();
+ return err;
+}
+
+void X11SalData::PushXErrorLevel( bool bIgnore )
+{
+ m_aXErrorHandlerStack.emplace_back( );
+ XErrorStackEntry& rEnt = m_aXErrorHandlerStack.back();
+ rEnt.m_bWas = false;
+ rEnt.m_bIgnore = bIgnore;
+ rEnt.m_aHandler = XSetErrorHandler( XErrorHdl );
+}
+
+void X11SalData::PopXErrorLevel()
+{
+ if( !m_aXErrorHandlerStack.empty() )
+ {
+ XSetErrorHandler( m_aXErrorHandlerStack.back().m_aHandler );
+ m_aXErrorHandlerStack.pop_back();
+ }
+}
+
+SalXLib::SalXLib()
+{
+ m_aTimeout.tv_sec = 0;
+ m_aTimeout.tv_usec = 0;
+ m_nTimeoutMS = 0;
+
+ nFDs_ = 0;
+ FD_ZERO( &aReadFDS_ );
+ FD_ZERO( &aExceptionFDS_ );
+
+ m_pInputMethod = nullptr;
+ m_pDisplay = nullptr;
+
+ m_pTimeoutFDS[0] = m_pTimeoutFDS[1] = -1;
+ if (pipe (m_pTimeoutFDS) == -1)
+ return;
+
+ // initialize 'wakeup' pipe.
+ int flags;
+
+ // set close-on-exec descriptor flag.
+ if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFD)) != -1)
+ {
+ flags |= FD_CLOEXEC;
+ (void)fcntl(m_pTimeoutFDS[0], F_SETFD, flags);
+ }
+ if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFD)) != -1)
+ {
+ flags |= FD_CLOEXEC;
+ (void)fcntl(m_pTimeoutFDS[1], F_SETFD, flags);
+ }
+
+ // set non-blocking I/O flag.
+ if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFL)) != -1)
+ {
+ flags |= O_NONBLOCK;
+ (void)fcntl(m_pTimeoutFDS[0], F_SETFL, flags);
+ }
+ if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFL)) != -1)
+ {
+ flags |= O_NONBLOCK;
+ (void)fcntl(m_pTimeoutFDS[1], F_SETFL, flags);
+ }
+
+ // insert [0] into read descriptor set.
+ FD_SET( m_pTimeoutFDS[0], &aReadFDS_ );
+ nFDs_ = m_pTimeoutFDS[0] + 1;
+}
+
+SalXLib::~SalXLib()
+{
+ // close 'wakeup' pipe.
+ close (m_pTimeoutFDS[0]);
+ close (m_pTimeoutFDS[1]);
+
+ m_pInputMethod.reset();
+}
+
+static Display *OpenX11Display(OString& rDisplay)
+{
+ /*
+ * open connection to X11 Display
+ * try in this order:
+ * o -display command line parameter,
+ * o $DISPLAY environment variable
+ * o default display
+ */
+
+ Display *pDisp = nullptr;
+
+ // is there a -display command line parameter?
+
+ sal_uInt32 nParams = osl_getCommandArgCount();
+ OUString aParam;
+ for (sal_uInt32 i=0; i<nParams; i++)
+ {
+ osl_getCommandArg(i, &aParam.pData);
+ if ( aParam == "-display" )
+ {
+ osl_getCommandArg(i+1, &aParam.pData);
+ rDisplay = OUStringToOString(
+ aParam, osl_getThreadTextEncoding());
+
+ if ((pDisp = XOpenDisplay(rDisplay.getStr()))!=nullptr)
+ {
+ /*
+ * if a -display switch was used, we need
+ * to set the environment accordingly since
+ * the clipboard build another connection
+ * to the xserver using $DISPLAY
+ */
+ OUString envVar("DISPLAY");
+ osl_setEnvironment(envVar.pData, aParam.pData);
+ }
+ break;
+ }
+ }
+
+ if (!pDisp && rDisplay.isEmpty())
+ {
+ // Open $DISPLAY or default...
+ char *pDisplay = getenv("DISPLAY");
+ if (pDisplay != nullptr)
+ rDisplay = OString(pDisplay);
+ pDisp = XOpenDisplay(pDisplay);
+ }
+
+ return pDisp;
+}
+
+void SalXLib::Init()
+{
+ m_pInputMethod.reset( new SalI18N_InputMethod );
+ m_pInputMethod->SetLocale();
+ XrmInitialize();
+
+ OString aDisplay;
+ m_pDisplay = OpenX11Display(aDisplay);
+
+ if ( m_pDisplay )
+ return;
+
+ OUString aProgramFileURL;
+ osl_getExecutableFile( &aProgramFileURL.pData );
+ OUString aProgramSystemPath;
+ osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData);
+ OString aProgramName = OUStringToOString(
+ aProgramSystemPath,
+ osl_getThreadTextEncoding() );
+ std::fprintf( stderr, "%s X11 error: Can't open display: %s\n",
+ aProgramName.getStr(), aDisplay.getStr());
+ std::fprintf( stderr, " Set DISPLAY environment variable, use -display option\n");
+ std::fprintf( stderr, " or check permissions of your X-Server\n");
+ std::fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n");
+ std::fflush( stderr );
+ exit(0);
+
+}
+
+extern "C" {
+static void EmitFontpathWarning()
+{
+ static Bool bOnce = False;
+ if ( !bOnce )
+ {
+ bOnce = True;
+ std::fprintf( stderr, "Please verify your fontpath settings\n"
+ "\t(See \"man xset\" for details"
+ " or ask your system administrator)\n" );
+ }
+}
+
+} /* extern "C" */
+
+static void PrintXError( Display *pDisplay, XErrorEvent *pEvent )
+{
+ char msg[ 120 ] = "";
+ XGetErrorText( pDisplay, pEvent->error_code, msg, sizeof( msg ) );
+ std::fprintf( stderr, "X-Error: %s\n", msg );
+ if( pEvent->request_code < SAL_N_ELEMENTS( XRequest ) )
+ {
+ const char* pName = XRequest[pEvent->request_code];
+ if( !pName )
+ pName = "BadRequest?";
+ std::fprintf( stderr, "\tMajor opcode: %d (%s)\n", pEvent->request_code, pName );
+ }
+ else
+ {
+ std::fprintf( stderr, "\tMajor opcode: %d\n", pEvent->request_code );
+ // TODO: also display extension name?
+ std::fprintf( stderr, "\tMinor opcode: %d\n", pEvent->minor_code );
+ }
+
+ std::fprintf( stderr, "\tResource ID: 0x%lx\n",
+ pEvent->resourceid );
+ std::fprintf( stderr, "\tSerial No: %ld (%ld)\n",
+ pEvent->serial, LastKnownRequestProcessed(pDisplay) );
+
+ if( !getenv( "SAL_SYNCHRONIZE" ) )
+ {
+ std::fprintf( stderr, "These errors are reported asynchronously,\n");
+ std::fprintf( stderr, "set environment variable SAL_SYNCHRONIZE to 1 to help debugging\n");
+ }
+
+ std::fflush( stdout );
+ std::fflush( stderr );
+}
+
+void X11SalData::XError( Display *pDisplay, XErrorEvent *pEvent )
+{
+ if( ! m_aXErrorHandlerStack.back().m_bIgnore )
+ {
+ if ( (pEvent->error_code == BadAlloc)
+ && (pEvent->request_code == X_OpenFont) )
+ {
+ static Bool bOnce = False;
+ if ( !bOnce )
+ {
+ std::fprintf(stderr, "X-Error occurred in a request for X_OpenFont\n");
+ EmitFontpathWarning();
+
+ bOnce = True ;
+ }
+ return;
+ }
+ /* ignore
+ * X_SetInputFocus: it's a hint only anyway
+ * X_GetProperty: this is part of the XGetWindowProperty call and will
+ * be handled by the return value of that function
+ */
+ else if( pEvent->request_code == X_SetInputFocus ||
+ pEvent->request_code == X_GetProperty
+ )
+ return;
+
+ if( pDisplay != vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() )
+ return;
+
+ PrintXError( pDisplay, pEvent );
+
+ oslSignalAction eToDo = osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, nullptr);
+ switch (eToDo)
+ {
+ case osl_Signal_ActIgnore :
+ return;
+ case osl_Signal_ActAbortApp :
+ abort();
+ case osl_Signal_ActKillApp :
+ exit(0);
+ case osl_Signal_ActCallNextHdl :
+ break;
+ default :
+ break;
+ }
+
+ }
+
+ m_aXErrorHandlerStack.back().m_bWas = true;
+}
+
+void X11SalData::Timeout()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if( pSVData->maSchedCtx.mpSalTimer )
+ pSVData->maSchedCtx.mpSalTimer->CallCallback();
+}
+
+namespace {
+
+struct YieldEntry
+{
+ int fd; // file descriptor for reading
+ void* data; // data for predicate and callback
+ YieldFunc pending; // predicate (determines pending events)
+ YieldFunc queued; // read and queue up events
+ YieldFunc handle; // handle pending events
+
+ int HasPendingEvent() const { return pending( fd, data ); }
+ int IsEventQueued() const { return queued( fd, data ); }
+ void HandleNextEvent() const { handle( fd, data ); }
+};
+
+}
+
+#define MAX_NUM_DESCRIPTORS 128
+
+static YieldEntry yieldTable[ MAX_NUM_DESCRIPTORS ];
+
+void SalXLib::Insert( int nFD, void* data,
+ YieldFunc pending,
+ YieldFunc queued,
+ YieldFunc handle )
+{
+ SAL_WARN_IF( !nFD, "vcl", "can not insert stdin descriptor" );
+ SAL_WARN_IF( yieldTable[nFD].fd, "vcl", "SalXLib::Insert fd twice" );
+
+ yieldTable[nFD].fd = nFD;
+ yieldTable[nFD].data = data;
+ yieldTable[nFD].pending = pending;
+ yieldTable[nFD].queued = queued;
+ yieldTable[nFD].handle = handle;
+
+ FD_SET( nFD, &aReadFDS_ );
+ FD_SET( nFD, &aExceptionFDS_ );
+
+ if( nFD >= nFDs_ )
+ nFDs_ = nFD + 1;
+}
+
+void SalXLib::Remove( int nFD )
+{
+ FD_CLR( nFD, &aReadFDS_ );
+ FD_CLR( nFD, &aExceptionFDS_ );
+
+ yieldTable[nFD].fd = 0;
+
+ if ( nFD == nFDs_ )
+ {
+ for ( nFD = nFDs_ - 1;
+ nFD >= 0 && !yieldTable[nFD].fd;
+ nFD-- ) ;
+
+ nFDs_ = nFD + 1;
+ }
+}
+
+bool SalXLib::CheckTimeout( bool bExecuteTimers )
+{
+ bool bRet = false;
+ if( m_aTimeout.tv_sec ) // timer is started
+ {
+ timeval aTimeOfDay;
+ gettimeofday( &aTimeOfDay, nullptr );
+ if( aTimeOfDay >= m_aTimeout )
+ {
+ bRet = true;
+ if( bExecuteTimers )
+ {
+ // timed out, update timeout
+ m_aTimeout = aTimeOfDay;
+ /*
+ * #107827# autorestart immediately, will be stopped (or set
+ * to different value in notify hdl if necessary;
+ * CheckTimeout should return false while
+ * timers are being dispatched.
+ */
+ m_aTimeout += m_nTimeoutMS;
+ // notify
+ X11SalData::Timeout();
+ }
+ }
+ }
+ return bRet;
+}
+
+bool
+SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
+{
+ // check for timeouts here if you want to make screenshots
+ static char* p_prioritize_timer = getenv ("SAL_HIGHPRIORITY_REPAINT");
+ bool bHandledEvent = false;
+ if (p_prioritize_timer != nullptr)
+ bHandledEvent = CheckTimeout();
+
+ const int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
+
+ // first, check for already queued events.
+ for ( int nFD = 0; nFD < nFDs_; nFD++ )
+ {
+ YieldEntry* pEntry = &(yieldTable[nFD]);
+ if ( pEntry->fd )
+ {
+ SAL_WARN_IF( nFD != pEntry->fd, "vcl", "wrong fd in Yield()" );
+ for( int i = 0; i < nMaxEvents && pEntry->HasPendingEvent(); i++ )
+ {
+ pEntry->HandleNextEvent();
+ if( ! bHandleAllCurrentEvents )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ // next, select with or without timeout according to bWait.
+ int nFDs = nFDs_;
+ fd_set ReadFDS = aReadFDS_;
+ fd_set ExceptionFDS = aExceptionFDS_;
+ int nFound = 0;
+
+ timeval Timeout = noyield_;
+ timeval *pTimeout = &Timeout;
+
+
+ if (bWait)
+ {
+ pTimeout = nullptr;
+ if (m_aTimeout.tv_sec) // Timer is started.
+ {
+ // determine remaining timeout.
+ gettimeofday (&Timeout, nullptr);
+ Timeout = m_aTimeout - Timeout;
+ if (yield_ >= Timeout)
+ {
+ // guard against micro timeout.
+ Timeout = yield_;
+ }
+ pTimeout = &Timeout;
+ }
+ }
+
+ {
+ // release YieldMutex (and re-acquire at block end)
+ SolarMutexReleaser aReleaser;
+ nFound = select( nFDs, &ReadFDS, nullptr, &ExceptionFDS, pTimeout );
+ }
+ if( nFound < 0 ) // error
+ {
+#ifdef DBG_UTIL
+ SAL_INFO("vcl.app", "SalXLib::Yield e=" << errno << " f=" << nFound);
+#endif
+ if( EINTR == errno )
+ {
+ errno = 0;
+ }
+ }
+
+ // usually handle timeouts here (as in 5.2)
+ if (p_prioritize_timer == nullptr)
+ bHandledEvent = CheckTimeout() || bHandledEvent;
+
+ // handle wakeup events.
+ if ((nFound > 0) && FD_ISSET(m_pTimeoutFDS[0], &ReadFDS))
+ {
+ int buffer;
+ while (read (m_pTimeoutFDS[0], &buffer, sizeof(buffer)) > 0)
+ continue;
+ nFound -= 1;
+ }
+
+ // handle other events.
+ if( nFound > 0 )
+ {
+ // now we are in the protected section !
+ // recall select if we have acquired fd's, ready for reading,
+
+ struct timeval noTimeout = { 0, 0 };
+ nFound = select( nFDs_, &ReadFDS, nullptr,
+ &ExceptionFDS, &noTimeout );
+
+ // someone-else has done the job for us
+ if (nFound == 0)
+ {
+ return false;
+ }
+
+ for ( int nFD = 0; nFD < nFDs_; nFD++ )
+ {
+ YieldEntry* pEntry = &(yieldTable[nFD]);
+ if ( pEntry->fd )
+ {
+ if ( FD_ISSET( nFD, &ExceptionFDS ) ) {
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN("vcl.app", "SalXLib::Yield exception.");
+#endif
+ nFound--;
+ }
+ if ( FD_ISSET( nFD, &ReadFDS ) )
+ {
+ for( int i = 0; pEntry->IsEventQueued() && i < nMaxEvents; i++ )
+ {
+ pEntry->HandleNextEvent();
+ bHandledEvent = true;
+ // if a recursive call has done the job
+ // so abort here
+ }
+ nFound--;
+ }
+ }
+ }
+ }
+
+ return bHandledEvent;
+}
+
+void SalXLib::Wakeup()
+{
+ OSL_VERIFY(write (m_pTimeoutFDS[1], "", 1) == 1);
+}
+
+void SalXLib::TriggerUserEventProcessing()
+{
+ Wakeup();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/saldisp.cxx b/vcl/unx/generic/app/saldisp.cxx
new file mode 100644
index 0000000000..6733e48323
--- /dev/null
+++ b/vcl/unx/generic/app/saldisp.cxx
@@ -0,0 +1,2489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+
+#if defined(__sun)
+#include <osl/module.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+
+#include <X11/cursorfont.h>
+#include <unx/x11_cursors/salcursors.h>
+#include <unx/x11_cursors/invert50.h>
+#ifdef __sun
+#define XK_KOREAN
+#endif
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xinerama.h>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <unx/i18n_im.hxx>
+#include <unx/i18n_xkb.hxx>
+#include <unx/saldisp.hxx>
+#include <unx/saldata.hxx>
+#include <salinst.hxx>
+#include <unx/salframe.h>
+#include <vcl/keycodes.hxx>
+#include <osl/diagnose.h>
+#include <unx/salobj.h>
+#include <unx/sm.hxx>
+#include <unx/wmadaptor.hxx>
+#include <unx/glyphcache.hxx>
+
+#include <poll.h>
+#include <memory>
+#include <vector>
+
+/* From <X11/Intrinsic.h> */
+typedef unsigned long Pixel;
+
+using namespace vcl_sal;
+
+#ifdef DBG_UTIL
+static const char *Null( const char *p ) { return p ? p : ""; }
+static const char *GetEnv( const char *p ) { return Null( getenv( p ) ); }
+static const char *KeyStr( KeySym n ) { return Null( XKeysymToString( n ) ); }
+
+static const char *GetAtomName( Display *d, Atom a )
+{ return Null( XGetAtomName( d, a ) ); }
+#endif
+
+// check if the resolution is sane
+static bool sal_ValidDPI(tools::Long nDPI)
+{
+ return (nDPI >= 50) && (nDPI <= 500);
+}
+
+static bool sal_GetVisualInfo( Display *pDisplay, XID nVID, XVisualInfo &rVI )
+{
+ int nInfos;
+ XVisualInfo aTemplate;
+ XVisualInfo*pInfo;
+
+ aTemplate.visualid = nVID;
+
+ pInfo = XGetVisualInfo( pDisplay, VisualIDMask, &aTemplate, &nInfos );
+ if( !pInfo )
+ return false;
+
+ rVI = *pInfo;
+ XFree( pInfo );
+
+ SAL_WARN_IF( rVI.visualid != nVID, "vcl",
+ "sal_GetVisualInfo: could not get correct visual by visualId" );
+ return true;
+}
+
+extern "C" srv_vendor_t
+sal_GetServerVendor( Display *p_display )
+{
+ struct vendor_t {
+ srv_vendor_t e_vendor; // vendor as enum
+ const char* p_name; // vendor name as returned by VendorString()
+ unsigned int n_len; // number of chars to compare
+ };
+
+ static const vendor_t vendorlist[] = {
+ { vendor_sun, "Sun Microsystems, Inc.", 10 },
+ };
+
+ // handle regular server vendors
+ char *p_name = ServerVendor( p_display );
+ for (auto const & vendor : vendorlist)
+ {
+ if ( strncmp (p_name, vendor.p_name, vendor.n_len) == 0 )
+ return vendor.e_vendor;
+ }
+
+ // vendor not found in list
+ return vendor_unknown;
+}
+
+bool SalDisplay::BestVisual( Display *pDisplay,
+ int nScreen,
+ XVisualInfo &rVI )
+{
+ VisualID nDefVID = XVisualIDFromVisual( DefaultVisual( pDisplay, nScreen ) );
+ VisualID nVID = 0;
+ char *pVID = getenv( "SAL_VISUAL" );
+ if( pVID )
+ sscanf( pVID, "%li", &nVID );
+
+ if( nVID && sal_GetVisualInfo( pDisplay, nVID, rVI ) )
+ return rVI.visualid == nDefVID;
+
+ XVisualInfo aVI;
+ aVI.screen = nScreen;
+ // get all visuals
+ int nVisuals;
+ XVisualInfo* pVInfos = XGetVisualInfo( pDisplay, VisualScreenMask,
+ &aVI, &nVisuals );
+ // pVInfos should contain at least one visual, otherwise
+ // we're in trouble
+ std::vector<int> aWeights(nVisuals);
+ int i;
+ for( i = 0; i < nVisuals; i++ )
+ {
+ bool bUsable = false;
+ int nTrueColor = 1;
+
+ if ( pVInfos[i].screen != nScreen )
+ {
+ bUsable = false;
+ }
+ else if( pVInfos[i].c_class == TrueColor )
+ {
+ nTrueColor = 2048;
+ if( pVInfos[i].depth == 24 )
+ bUsable = true;
+ }
+ else if( pVInfos[i].c_class == PseudoColor )
+ {
+ bUsable = true;
+ }
+ aWeights[i] = bUsable ? nTrueColor*pVInfos[i].depth : -1024;
+ aWeights[i] -= pVInfos[ i ].visualid;
+ }
+
+ int nBestVisual = 0;
+ int nBestWeight = -1024;
+ for( i = 0; i < nVisuals; i++ )
+ {
+ if (aWeights[i] > nBestWeight)
+ {
+ nBestWeight = aWeights[i];
+ nBestVisual = i;
+ }
+ }
+
+ rVI = pVInfos[ nBestVisual ];
+
+ XFree( pVInfos );
+ return rVI.visualid == nDefVID;
+}
+
+SalDisplay::SalDisplay( Display *display ) :
+ pXLib_( nullptr ),
+ mpKbdExtension( nullptr ),
+ pDisp_( display ),
+ m_nXDefaultScreen( 0 ),
+ nMaxRequestSize_( 0 ),
+ meServerVendor( vendor_unknown ),
+ bNumLockFromXS_( false ),
+ nNumLockIndex_( 0 ),
+ nShiftKeySym_( 0 ),
+ nCtrlKeySym_( 0 ),
+ nMod1KeySym_( 0 ),
+ m_bXinerama( false ),
+ m_nLastUserEventTime( CurrentTime )
+{
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "SalDisplay::SalDisplay().");
+#endif
+ GenericUnixSalData *pData = GetGenericUnixSalData();
+
+ SAL_WARN_IF( pData->GetDisplay(), "vcl", "Second SalDisplay created !!!" );
+ pData->SetDisplay( this );
+
+ m_nXDefaultScreen = SalX11Screen( DefaultScreen( pDisp_ ) );
+}
+
+SalDisplay::~SalDisplay()
+{
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "SalDisplay::~SalDisplay().");
+#endif
+ if( pDisp_ )
+ {
+ doDestruct();
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "display " << pDisp_ << " closed.");
+#endif
+ pDisp_ = nullptr;
+ }
+ // don't do this in doDestruct since RandR extension adds hooks into Display
+ // that is XCloseDisplay still needs the RandR library if it was used
+ DeInitRandR();
+}
+
+void SalDisplay::doDestruct()
+{
+ GenericUnixSalData *pData = GetGenericUnixSalData();
+
+ m_pWMAdaptor.reset();
+
+ FreetypeManager::get().ClearFontCache();
+
+ if( IsDisplay() )
+ {
+ delete mpKbdExtension;
+ mpKbdExtension = nullptr;
+
+ for( size_t i = 0; i < m_aScreens.size(); i++ )
+ {
+ ScreenData& rData = m_aScreens[i];
+ if( rData.m_bInit )
+ {
+ if( rData.m_aMonoGC != rData.m_aCopyGC )
+ XFreeGC( pDisp_, rData.m_aMonoGC );
+ XFreeGC( pDisp_, rData.m_aCopyGC );
+ XFreeGC( pDisp_, rData.m_aAndInvertedGC );
+ XFreeGC( pDisp_, rData.m_aAndGC );
+ XFreeGC( pDisp_, rData.m_aOrGC );
+ XFreeGC( pDisp_, rData.m_aStippleGC );
+ XFreePixmap( pDisp_, rData.m_hInvert50 );
+ XDestroyWindow( pDisp_, rData.m_aRefWindow );
+ Colormap aColMap = rData.m_aColormap.GetXColormap();
+ if( aColMap != None && aColMap != DefaultColormap( pDisp_, i ) )
+ XFreeColormap( pDisp_, aColMap );
+ }
+ }
+
+ for( const Cursor & aCsr : aPointerCache_ )
+ {
+ if( aCsr )
+ XFreeCursor( pDisp_, aCsr );
+ }
+
+ if( pXLib_ )
+ pXLib_->Remove( ConnectionNumber( pDisp_ ) );
+ }
+
+ if( pData->GetDisplay() == static_cast<const SalGenericDisplay *>( this ) )
+ pData->SetDisplay( nullptr );
+}
+
+static int DisplayHasEvent( int fd, void * data )
+{
+ auto pDisplay = static_cast<SalX11Display *>(data);
+ SAL_WARN_IF( ConnectionNumber( pDisplay->GetDisplay() ) != fd, "vcl",
+ "wrong fd in DisplayHasEvent" );
+ if( ! pDisplay->IsDisplay() )
+ return 0;
+
+ bool result;
+
+ SolarMutexGuard aGuard;
+ result = pDisplay->IsEvent();
+ return int(result);
+}
+static int DisplayQueue( int fd, void * data )
+{
+ auto pDisplay = static_cast<SalX11Display *>(data);
+ SAL_WARN_IF( ConnectionNumber( pDisplay->GetDisplay() ) != fd, "vcl",
+ "wrong fd in DisplayHasEvent" );
+ int result;
+
+ SolarMutexGuard aGuard;
+ result = XEventsQueued( pDisplay->GetDisplay(),
+ QueuedAfterReading );
+ return result;
+}
+static int DisplayYield( int fd, void * data )
+{
+ auto pDisplay = static_cast<SalX11Display *>(data);
+ SAL_WARN_IF( ConnectionNumber( pDisplay->GetDisplay() ) != fd, "vcl",
+ "wrong fd in DisplayHasEvent" );
+
+ SolarMutexGuard aGuard;
+ pDisplay->Yield();
+ return 1;
+}
+
+SalX11Display::SalX11Display( Display *display )
+ : SalDisplay( display )
+{
+ Init();
+
+ pXLib_ = GetX11SalData()->GetLib();
+ pXLib_->Insert( ConnectionNumber( pDisp_ ),
+ this,
+ reinterpret_cast<YieldFunc>(DisplayHasEvent),
+ reinterpret_cast<YieldFunc>(DisplayQueue),
+ reinterpret_cast<YieldFunc>(DisplayYield) );
+}
+
+SalX11Display::~SalX11Display()
+{
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "SalX11Display::~SalX11Display().");
+#endif
+ if( pDisp_ )
+ {
+ doDestruct();
+ XCloseDisplay( pDisp_ );
+ pDisp_ = nullptr;
+ }
+}
+
+void SalX11Display::TriggerUserEventProcessing()
+{
+ if( pXLib_ )
+ pXLib_->TriggerUserEventProcessing();
+}
+
+SalDisplay::ScreenData *
+SalDisplay::initScreen( SalX11Screen nXScreen ) const
+{
+ if( nXScreen.getXScreen() >= m_aScreens.size() )
+ nXScreen = m_nXDefaultScreen;
+ ScreenData* pSD = const_cast<ScreenData *>(&m_aScreens[nXScreen.getXScreen()]);
+ if( pSD->m_bInit )
+ return nullptr;
+ pSD->m_bInit = true;
+
+ XVisualInfo aVI;
+ Colormap aColMap;
+
+ if( SalDisplay::BestVisual( pDisp_, nXScreen.getXScreen(), aVI ) ) // DefaultVisual
+ aColMap = DefaultColormap( pDisp_, nXScreen.getXScreen() );
+ else
+ aColMap = XCreateColormap( pDisp_,
+ RootWindow( pDisp_, nXScreen.getXScreen() ),
+ aVI.visual,
+ AllocNone );
+
+ Screen* pScreen = ScreenOfDisplay( pDisp_, nXScreen.getXScreen() );
+
+ pSD->m_aSize = AbsoluteScreenPixelSize( WidthOfScreen( pScreen ), HeightOfScreen( pScreen ) );
+ pSD->m_aRoot = RootWindow( pDisp_, nXScreen.getXScreen() );
+ pSD->m_aVisual = SalVisual( &aVI );
+ pSD->m_aColormap = SalColormap( this, aColMap, nXScreen );
+
+ // we're interested in configure notification of root windows
+ InitRandR( pSD->m_aRoot );
+
+ // - - - - - - - - - - Reference Window/Default Drawable - -
+ XSetWindowAttributes aXWAttributes;
+ aXWAttributes.border_pixel = 0;
+ aXWAttributes.background_pixel = 0;
+ aXWAttributes.colormap = aColMap;
+ pSD->m_aRefWindow = XCreateWindow( pDisp_,
+ pSD->m_aRoot,
+ 0,0, 16,16, 0,
+ pSD->m_aVisual.GetDepth(),
+ InputOutput,
+ pSD->m_aVisual.GetVisual(),
+ CWBorderPixel|CWBackPixel|CWColormap,
+ &aXWAttributes );
+
+ // set client leader (session id gets set when session is started)
+ if( pSD->m_aRefWindow )
+ {
+ // client leader must have WM_CLIENT_LEADER pointing to itself
+ XChangeProperty( pDisp_,
+ pSD->m_aRefWindow,
+ XInternAtom( pDisp_, "WM_CLIENT_LEADER", False ),
+ XA_WINDOW,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&pSD->m_aRefWindow),
+ 1
+ );
+
+ OString aExec(OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding()));
+ const char* argv[1];
+ argv[0] = aExec.getStr();
+ XSetCommand( pDisp_, pSD->m_aRefWindow, const_cast<char**>(argv), 1 );
+ XSelectInput( pDisp_, pSD->m_aRefWindow, PropertyChangeMask );
+
+ // - - - - - - - - - - GCs - - - - - - - - - - - - - - - - -
+ XGCValues values;
+ values.graphics_exposures = False;
+ values.fill_style = FillOpaqueStippled;
+ values.background = (1<<pSD->m_aVisual.GetDepth())-1;
+ values.foreground = 0;
+
+ pSD->m_aCopyGC = XCreateGC( pDisp_,
+ pSD->m_aRefWindow,
+ GCGraphicsExposures
+ | GCForeground
+ | GCBackground,
+ &values );
+ pSD->m_aAndInvertedGC= XCreateGC( pDisp_,
+ pSD->m_aRefWindow,
+ GCGraphicsExposures
+ | GCForeground
+ | GCBackground,
+ &values );
+ pSD->m_aAndGC = XCreateGC( pDisp_,
+ pSD->m_aRefWindow,
+ GCGraphicsExposures
+ | GCForeground
+ | GCBackground,
+ &values );
+ pSD->m_aOrGC = XCreateGC( pDisp_,
+ pSD->m_aRefWindow,
+ GCGraphicsExposures
+ | GCForeground
+ | GCBackground,
+ &values );
+ pSD->m_aStippleGC = XCreateGC( pDisp_,
+ pSD->m_aRefWindow,
+ GCGraphicsExposures
+ | GCFillStyle
+ | GCForeground
+ | GCBackground,
+ &values );
+
+ XSetFunction( pDisp_, pSD->m_aAndInvertedGC, GXandInverted );
+ XSetFunction( pDisp_, pSD->m_aAndGC, GXand );
+ // PowerPC Solaris 2.5 (XSun 3500) Bug: GXor = GXnop
+ XSetFunction( pDisp_, pSD->m_aOrGC, GXxor );
+
+ if( 1 == pSD->m_aVisual.GetDepth() )
+ {
+ XSetFunction( pDisp_, pSD->m_aCopyGC, GXcopyInverted );
+ pSD->m_aMonoGC = pSD->m_aCopyGC;
+ }
+ else
+ {
+ Pixmap hPixmap = XCreatePixmap( pDisp_, pSD->m_aRefWindow, 1, 1, 1 );
+ pSD->m_aMonoGC = XCreateGC( pDisp_,
+ hPixmap,
+ GCGraphicsExposures,
+ &values );
+ XFreePixmap( pDisp_, hPixmap );
+ }
+ pSD->m_hInvert50 = XCreateBitmapFromData( pDisp_,
+ pSD->m_aRefWindow,
+ reinterpret_cast<const char*>(invert50_bits),
+ invert50_width,
+ invert50_height );
+ }
+ return pSD;
+}
+
+void SalDisplay::Init()
+{
+ for( Cursor & aCsr : aPointerCache_ )
+ aCsr = None;
+
+ m_bXinerama = false;
+
+ int nDisplayScreens = ScreenCount( pDisp_ );
+ m_aScreens = std::vector<ScreenData>(nDisplayScreens);
+
+ bool bExactResolution = false;
+ /* #i15507#
+ * Xft resolution should take precedence since
+ * it is what modern desktops use.
+ */
+ const char* pValStr = XGetDefault( pDisp_, "Xft", "dpi" );
+ if( pValStr != nullptr )
+ {
+ const OString aValStr( pValStr );
+ const tools::Long nDPI = static_cast<tools::Long>(aValStr.toDouble());
+ // guard against insane resolution
+ if( sal_ValidDPI(nDPI) )
+ {
+ aResolution_ = Pair( nDPI, nDPI );
+ bExactResolution = true;
+ }
+ }
+ if( !bExactResolution )
+ {
+ /* if Xft.dpi is not set, try and find the DPI from the
+ * reported screen sizes and resolution. If there are multiple
+ * screens, just fall back to the default 96x96
+ */
+ tools::Long xDPI = 96;
+ tools::Long yDPI = 96;
+ if (m_aScreens.size() == 1) {
+ xDPI = static_cast<tools::Long>(round(DisplayWidth(pDisp_, 0)*25.4/DisplayWidthMM(pDisp_, 0)));
+ yDPI = static_cast<tools::Long>(round(DisplayHeight(pDisp_, 0)*25.4/DisplayHeightMM(pDisp_, 0)));
+ // if either is invalid set it equal to the other
+ if (!sal_ValidDPI(xDPI) && sal_ValidDPI(yDPI))
+ xDPI = yDPI;
+ if (!sal_ValidDPI(yDPI) && sal_ValidDPI(xDPI))
+ yDPI = xDPI;
+ // if both are invalid, reset them to the default
+ if (!sal_ValidDPI(xDPI) && !sal_ValidDPI(yDPI))
+ xDPI = yDPI = 96;
+ }
+ aResolution_ = Pair( xDPI, yDPI );
+ }
+
+ nMaxRequestSize_ = XExtendedMaxRequestSize( pDisp_ ) * 4;
+ if( !nMaxRequestSize_ )
+ nMaxRequestSize_ = XMaxRequestSize( pDisp_ ) * 4;
+
+ meServerVendor = sal_GetServerVendor(pDisp_);
+
+ // - - - - - - - - - - Synchronize - - - - - - - - - - - - -
+ if( getenv( "SAL_SYNCHRONIZE" ) )
+ XSynchronize( pDisp_, True );
+
+ // - - - - - - - - - - Keyboardmapping - - - - - - - - - - -
+ ModifierMapping();
+
+ // - - - - - - - - - - Window Manager - - - - - - - - - - -
+ m_pWMAdaptor = ::vcl_sal::WMAdaptor::createWMAdaptor( this );
+
+ InitXinerama();
+
+#ifdef DBG_UTIL
+ PrintInfo();
+#endif
+}
+
+void SalX11Display::SetupInput()
+{
+ GetGenericUnixSalData()->ErrorTrapPush();
+ SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp_ );
+ XSync( pDisp_, False );
+
+ bool bError = GetGenericUnixSalData()->ErrorTrapPop( false );
+ GetGenericUnixSalData()->ErrorTrapPush();
+ pKbdExtension->UseExtension( ! bError );
+ GetGenericUnixSalData()->ErrorTrapPop();
+
+ SetKbdExtension( pKbdExtension );
+}
+
+// Sound
+void SalDisplay::Beep() const
+{
+ XBell( pDisp_, 100 );
+}
+
+// Keyboard
+
+namespace {
+
+bool InitXkb(Display* dpy)
+{
+ int nOpcode, nEvent, nError;
+ int nXkbMajor = XkbMajorVersion;
+ int nXkbMinor = XkbMinorVersion;
+
+ if (!XkbLibraryVersion(&nXkbMajor, &nXkbMinor))
+ return false;
+
+ return XkbQueryExtension(
+ dpy, &nOpcode, &nEvent, &nError, &nXkbMajor, &nXkbMinor);
+}
+
+unsigned int GetKeySymMask(Display* dpy, KeySym nKeySym)
+{
+ int nMask = 0;
+ XModifierKeymap* pXmkMap = XGetModifierMapping(dpy);
+ KeyCode nKeyCode = XKeysymToKeycode(dpy, nKeySym);
+ if (nKeyCode == NoSymbol)
+ return 0;
+
+ for (int i = 0; i < 8; ++i)
+ {
+ KeyCode nThisKeyCode = pXmkMap->modifiermap[pXmkMap->max_keypermod*i];
+ if (nThisKeyCode == nKeyCode)
+ nMask = 1 << i;
+ }
+ XFreeModifiermap(pXmkMap);
+ return nMask;
+}
+
+}
+
+void SalDisplay::SimulateKeyPress( sal_uInt16 nKeyCode )
+{
+ if (nKeyCode != KEY_CAPSLOCK)
+ return;
+
+ Display* dpy = GetDisplay();
+ if (!InitXkb(dpy))
+ return;
+
+ unsigned int nMask = GetKeySymMask(dpy, XK_Caps_Lock);
+ XkbStateRec xkbState;
+ XkbGetState(dpy, XkbUseCoreKbd, &xkbState);
+ unsigned int nCapsLockState = xkbState.locked_mods & nMask;
+ if (nCapsLockState)
+ XkbLockModifiers (dpy, XkbUseCoreKbd, nMask, 0);
+ else
+ XkbLockModifiers (dpy, XkbUseCoreKbd, nMask, nMask);
+}
+
+KeyIndicatorState SalDisplay::GetIndicatorState() const
+{
+ unsigned int _state = 0;
+ KeyIndicatorState nState = KeyIndicatorState::NONE;
+ XkbGetIndicatorState(pDisp_, XkbUseCoreKbd, &_state);
+
+ if (_state & 0x00000001)
+ nState |= KeyIndicatorState::CAPSLOCK;
+ if (_state & 0x00000002)
+ nState |= KeyIndicatorState::NUMLOCK;
+ if (_state & 0x00000004)
+ nState |= KeyIndicatorState::SCROLLLOCK;
+
+ return nState;
+}
+
+OUString SalDisplay::GetKeyNameFromKeySym( KeySym nKeySym ) const
+{
+ OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage();
+ OUString aRet;
+
+ // return an empty string for keysyms that are not bound to
+ // any key code
+ KeyCode aKeyCode = XKeysymToKeycode( GetDisplay(), nKeySym );
+ static_assert(NoSymbol == 0, "X11 inconsistency");
+ if( aKeyCode != NoSymbol )
+ {
+ if( !nKeySym )
+ aRet = "???";
+ else
+ {
+ aRet = ::vcl_sal::getKeysymReplacementName( aLang, nKeySym );
+ if( aRet.isEmpty() )
+ {
+ const char *pString = XKeysymToString( nKeySym );
+ if (pString)
+ {
+ int n = strlen( pString );
+ if( n > 2 && pString[n-2] == '_' )
+ aRet = OUString( pString, n-2, RTL_TEXTENCODING_ISO_8859_1 );
+ else
+ aRet = OUString( pString, n, RTL_TEXTENCODING_ISO_8859_1 );
+ }
+ else
+ aRet = "???";
+ }
+ }
+ }
+ return aRet;
+}
+
+static KeySym sal_XModifier2Keysym( Display *pDisplay,
+ XModifierKeymap const *pXModMap,
+ int n )
+{
+ return XkbKeycodeToKeysym( pDisplay,
+ pXModMap->modifiermap[n*pXModMap->max_keypermod],
+ 0,0 );
+}
+
+void SalDisplay::ModifierMapping()
+{
+ XModifierKeymap *pXModMap = XGetModifierMapping( pDisp_ );
+
+ bNumLockFromXS_ = True;
+ nShiftKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ShiftMapIndex );
+ nCtrlKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ControlMapIndex );
+ nMod1KeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, Mod1MapIndex );
+ // on Sun and SCO servers XLookupString does not account for NumLock
+ if( GetServerVendor() == vendor_sun )
+ {
+ KeyCode aNumLock = XKeysymToKeycode( pDisp_, XK_Num_Lock );
+
+ if( aNumLock )
+ for( int i = ShiftMapIndex; i <= Mod5MapIndex; i++ )
+ {
+ if( pXModMap->modifiermap[i*pXModMap->max_keypermod] == aNumLock )
+ {
+ bNumLockFromXS_ = False;
+ nNumLockIndex_ = i;
+ break;
+ }
+ }
+ }
+
+ XFreeModifiermap( pXModMap );
+}
+
+OUString SalDisplay::GetKeyName( sal_uInt16 nKeyCode ) const
+{
+ OUString aStrMap;
+ OUString aCustomKeyName;
+
+ if( nKeyCode & KEY_MOD1 )
+ aStrMap += GetKeyNameFromKeySym( nCtrlKeySym_ );
+
+ if( nKeyCode & KEY_MOD2 )
+ {
+ if( !aStrMap.isEmpty() )
+ aStrMap += "+";
+ aStrMap += GetKeyNameFromKeySym( nMod1KeySym_ );
+ }
+
+ if( nKeyCode & KEY_SHIFT )
+ {
+ if( !aStrMap.isEmpty() )
+ aStrMap += "+";
+ aStrMap += GetKeyNameFromKeySym( nShiftKeySym_ );
+ }
+ nKeyCode &= 0x0FFF;
+
+ KeySym nKeySym = 0;
+
+ if( KEY_0 <= nKeyCode && nKeyCode <= KEY_9 )
+ nKeySym = XK_0 + (nKeyCode - KEY_0);
+ else if( KEY_A <= nKeyCode && nKeyCode <= KEY_Z )
+ nKeySym = XK_A + (nKeyCode - KEY_A);
+ else if( KEY_F1 <= nKeyCode && nKeyCode <= KEY_F26 ) // does this key exist?
+ nKeySym = XK_F1 + (nKeyCode - KEY_F1);
+ else switch( nKeyCode )
+ {
+ case KEY_DOWN:
+ nKeySym = XK_Down;
+ break;
+ case KEY_UP:
+ nKeySym = XK_Up;
+ break;
+ case KEY_LEFT:
+ nKeySym = XK_Left;
+ break;
+ case KEY_RIGHT:
+ nKeySym = XK_Right;
+ break;
+ case KEY_HOME:
+ nKeySym = XK_Home;
+ break;
+ case KEY_END:
+ nKeySym = XK_End;
+ break;
+ case KEY_PAGEUP:
+ nKeySym = XK_Page_Up;
+ break;
+ case KEY_PAGEDOWN:
+ nKeySym = XK_Page_Down;
+ break;
+ case KEY_RETURN:
+ nKeySym = XK_Return;
+ break;
+ case KEY_ESCAPE:
+ nKeySym = XK_Escape;
+ break;
+ case KEY_TAB:
+ nKeySym = XK_Tab;
+ break;
+ case KEY_BACKSPACE:
+ nKeySym = XK_BackSpace;
+ break;
+ case KEY_SPACE:
+ nKeySym = XK_space;
+ break;
+ case KEY_INSERT:
+ nKeySym = XK_Insert;
+ break;
+ case KEY_DELETE:
+ nKeySym = XK_Delete;
+ break;
+
+ #if !defined (SunXK_Undo)
+ // we don't intend to use SunXK_Undo, but if it has not been
+ // defined already, then we _do_ need the following:
+ #define SunXK_Props 0x1005FF70
+ #define SunXK_Front 0x1005FF71
+ #define SunXK_Copy 0x1005FF72
+ #define SunXK_Open 0x1005FF73
+ #define SunXK_Paste 0x1005FF74
+ #define SunXK_Cut 0x1005FF75
+ #endif
+ // the following are for XF86 systems
+ #define XF86XK_Copy 0x1008FF57
+ #define XF86XK_Cut 0x1008FF58
+ #define XF86XK_Open 0x1008FF6B
+ #define XF86XK_Paste 0x1008FF6D
+ // which leaves Apollo and OSF systems in the lurch
+
+ case KEY_REPEAT:
+ nKeySym = XK_Redo;
+ break;
+ case KEY_PROPERTIES:
+ nKeySym = SunXK_Props;
+ break;
+ case KEY_UNDO:
+ nKeySym = XK_Undo;
+ break;
+ case KEY_FRONT:
+ nKeySym = SunXK_Front;
+ break;
+ case KEY_COPY:
+ nKeySym = GetServerVendor() == vendor_sun ? SunXK_Copy : XF86XK_Copy;
+ break;
+ case KEY_OPEN:
+ nKeySym = GetServerVendor() == vendor_sun ? SunXK_Open : XF86XK_Open;
+ break;
+ case KEY_PASTE:
+ nKeySym = GetServerVendor() == vendor_sun ? SunXK_Paste : XF86XK_Paste;
+ break;
+ case KEY_FIND:
+ nKeySym = XK_Find;
+ break;
+ case KEY_CUT:
+ nKeySym = GetServerVendor() == vendor_sun ? SunXK_Cut : XF86XK_Cut;
+ /* The original code here had:
+ nKeySym = GetServerVendor() == vendor_sun ? SunXK_Cut : XK_L10;
+ if anyone can remember which non-vendor_sun system used this
+ XK_L10 keysym, and why this hack only applied to KEY_CUT,
+ then please re-hack this code to put it back
+ */
+ break;
+ case KEY_ADD:
+ aCustomKeyName = "+";
+ break;
+ case KEY_SUBTRACT:
+ aCustomKeyName = "-";
+ break;
+ case KEY_MULTIPLY:
+ nKeySym = XK_asterisk;
+ break;
+ case KEY_DIVIDE:
+ nKeySym = XK_slash;
+ break;
+ case KEY_POINT:
+ aCustomKeyName = ".";
+ break;
+ case KEY_COMMA:
+ nKeySym = XK_comma;
+ break;
+ case KEY_LESS:
+ nKeySym = XK_less;
+ break;
+ case KEY_GREATER:
+ nKeySym = XK_greater;
+ break;
+ case KEY_EQUAL:
+ nKeySym = XK_equal;
+ break;
+ case KEY_HELP:
+ nKeySym = XK_Help;
+ break;
+ case KEY_HANGUL_HANJA:
+ nKeySym = XK_Hangul_Hanja;
+ break;
+ case KEY_TILDE:
+ nKeySym = XK_asciitilde;
+ break;
+ case KEY_QUOTELEFT:
+ nKeySym = XK_grave;
+ break;
+ case KEY_BRACKETLEFT:
+ aCustomKeyName = "[";
+ break;
+ case KEY_BRACKETRIGHT:
+ aCustomKeyName = "]";
+ break;
+ case KEY_SEMICOLON:
+ aCustomKeyName = ";";
+ break;
+ case KEY_QUOTERIGHT:
+ aCustomKeyName = "'";
+ break;
+ case KEY_RIGHTCURLYBRACKET:
+ aCustomKeyName = "}";
+ break;
+ case KEY_NUMBERSIGN:
+ aCustomKeyName = "#";
+ break;
+ case KEY_XF86FORWARD:
+ aCustomKeyName = "XF86Forward";
+ break;
+ case KEY_XF86BACK:
+ aCustomKeyName = "XF86Back";
+ break;
+ case KEY_COLON:
+ aCustomKeyName = ":";
+ break;
+ default:
+ nKeySym = 0;
+ break;
+ }
+
+ if( nKeySym )
+ {
+ OUString aKeyName = GetKeyNameFromKeySym( nKeySym );
+ if( !aKeyName.isEmpty() )
+ {
+ if( !aStrMap.isEmpty() )
+ aStrMap += "+";
+ aStrMap += aKeyName;
+ }
+ else
+ aStrMap.clear();
+ }
+ else if (!aCustomKeyName.isEmpty())
+ {
+ // For semicolon, bracket left and bracket right, it's better to use
+ // their keys than their names. (fdo#32891)
+ if (!aStrMap.isEmpty())
+ aStrMap += "+";
+ aStrMap += aCustomKeyName;
+ }
+ else
+ aStrMap.clear();
+
+ return aStrMap;
+}
+
+#ifndef IsISOKey
+#define IsISOKey( n ) (0x0000FE00==((n)&0xFFFFFF00))
+#endif
+
+sal_uInt16 SalDisplay::GetKeyCode( KeySym keysym, char*pcPrintable ) const
+{
+ sal_uInt16 nKey = 0;
+
+ if( XK_a <= keysym && XK_z >= keysym )
+ nKey = static_cast<sal_uInt16>(KEY_A + (keysym - XK_a));
+ else if( XK_A <= keysym && XK_Z >= keysym )
+ nKey = static_cast<sal_uInt16>(KEY_A + (keysym - XK_A));
+ else if( XK_0 <= keysym && XK_9 >= keysym )
+ nKey = static_cast<sal_uInt16>(KEY_0 + (keysym - XK_0));
+ else if( IsModifierKey( keysym ) )
+ ;
+ else if( IsKeypadKey( keysym ) )
+ {
+ if( (keysym >= XK_KP_0) && (keysym <= XK_KP_9) )
+ {
+ nKey = static_cast<sal_uInt16>(KEY_0 + (keysym - XK_KP_0));
+ *pcPrintable = '0' + nKey - KEY_0;
+ }
+ else if( IsPFKey( keysym ) )
+ nKey = static_cast<sal_uInt16>(KEY_F1 + (keysym - XK_KP_F1));
+ else switch( keysym )
+ {
+ case XK_KP_Space:
+ nKey = KEY_SPACE;
+ *pcPrintable = ' ';
+ break;
+ case XK_KP_Tab:
+ nKey = KEY_TAB;
+ break;
+ case XK_KP_Enter:
+ nKey = KEY_RETURN;
+ break;
+ case XK_KP_Begin:
+ case XK_KP_Home:
+ nKey = KEY_HOME;
+ break;
+ case XK_KP_Left:
+ nKey = KEY_LEFT;
+ break;
+ case XK_KP_Up:
+ nKey = KEY_UP;
+ break;
+ case XK_KP_Right:
+ nKey = KEY_RIGHT;
+ break;
+ case XK_KP_Down:
+ nKey = KEY_DOWN;
+ break;
+ case XK_KP_Page_Up: // XK_KP_Page_Up
+ nKey = KEY_PAGEUP;
+ break;
+ case XK_KP_Page_Down: // XK_KP_Page_Down
+ nKey = KEY_PAGEDOWN;
+ break;
+ case XK_KP_End:
+ nKey = KEY_END;
+ break;
+ case XK_KP_Insert:
+ nKey = KEY_INSERT;
+ break;
+ case XK_KP_Delete:
+ nKey = KEY_DELETE;
+ break;
+ case XK_KP_Equal:
+ nKey = KEY_EQUAL;
+ *pcPrintable = '=';
+ break;
+ case XK_KP_Multiply:
+ nKey = KEY_MULTIPLY;
+ *pcPrintable = '*';
+ break;
+ case XK_KP_Add:
+ nKey = KEY_ADD;
+ *pcPrintable = '+';
+ break;
+ case XK_KP_Separator:
+ nKey = KEY_DECIMAL;
+ *pcPrintable = ',';
+ break;
+ case XK_KP_Subtract:
+ nKey = KEY_SUBTRACT;
+ *pcPrintable = '-';
+ break;
+ case XK_KP_Decimal:
+ nKey = KEY_DECIMAL;
+ *pcPrintable = '.';
+ break;
+ case XK_KP_Divide:
+ nKey = KEY_DIVIDE;
+ *pcPrintable = '/';
+ break;
+ }
+ }
+ else if( IsFunctionKey( keysym ) )
+ {
+ if( bNumLockFromXS_ )
+ {
+ if( keysym >= XK_F1 && keysym <= XK_F26 )
+ nKey = static_cast<sal_uInt16>(KEY_F1 + keysym - XK_F1);
+ }
+ else switch( keysym )
+ {
+ // - - - - - Sun X-Server keyboard without Cursorblock ??? - - -
+ case XK_R7: // XK_F27:
+ nKey = KEY_HOME;
+ break;
+ case XK_R8: // XK_F28:
+ nKey = KEY_UP;
+ break;
+ case XK_R9: // XK_F29:
+ nKey = KEY_PAGEUP;
+ break;
+ case XK_R10: // XK_F30:
+ nKey = KEY_LEFT;
+ break;
+ case XK_R11: // XK_F31:
+ nKey = 0; // KEY_F31
+ break;
+ case XK_R12: // XK_F32:
+ nKey = KEY_RIGHT;
+ break;
+ case XK_R13: // XK_F33:
+ nKey = KEY_END;
+ break;
+ case XK_R14: // XK_F34:
+ nKey = KEY_DOWN;
+ break;
+ case XK_R15: // XK_F35:
+ nKey = KEY_PAGEDOWN;
+ break;
+ // - - - - - Sun X-Server keyboard ??? - - - - - - - - - - - -
+ case XK_L1: // XK_F11:
+ nKey = KEY_F11; // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel),
+ // but VCL doesn't have a key definition for that
+ break;
+ case XK_L2: // XK_F12:
+ if ( GetServerVendor() == vendor_sun )
+ nKey = KEY_REPEAT;
+ else
+ nKey = KEY_F12;
+ break;
+ case XK_L3: // XK_F13:
+ nKey = KEY_PROPERTIES; // KEY_F13
+ break;
+ case XK_L4: // XK_F14:
+ nKey = KEY_UNDO; // KEY_F14
+ break;
+ case XK_L5: // XK_F15:
+ nKey = KEY_F15; // KEY_FRONT
+ break;
+ case XK_L6: // XK_F16:
+ nKey = KEY_COPY; // KEY_F16
+ break;
+ case XK_L7: // XK_F17:
+ nKey = KEY_F17; // KEY_OPEN
+ break;
+ case XK_L8: // XK_F18:
+ nKey = KEY_PASTE; // KEY_F18
+ break;
+ case XK_L9: // XK_F19:
+ nKey = KEY_F19; // KEY_FIND
+ break;
+ case XK_L10: // XK_F20:
+ nKey = KEY_CUT; // KEY_F20
+ break;
+ default:
+ if( keysym >= XK_F1 && keysym <= XK_F26 )
+ nKey = static_cast<sal_uInt16>(KEY_F1 + keysym - XK_F1);
+ break;
+ }
+ }
+ else if( IsCursorKey( keysym ) )
+ {
+ switch( keysym )
+ {
+ case XK_Begin:
+ case XK_Home:
+ nKey = KEY_HOME;
+ break;
+ case XK_Left:
+ nKey = KEY_LEFT;
+ break;
+ case XK_Up:
+ nKey = KEY_UP;
+ break;
+ case XK_Right:
+ nKey = KEY_RIGHT;
+ break;
+ case XK_Down:
+ nKey = KEY_DOWN;
+ break;
+ case XK_Page_Up: // XK_Page_Up
+ nKey = KEY_PAGEUP;
+ break;
+ case XK_Page_Down: // XK_Page_Down
+ nKey = KEY_PAGEDOWN;
+ break;
+ case XK_End:
+ nKey = KEY_END;
+ break;
+ }
+ }
+ else if( IsMiscFunctionKey( keysym ) )
+ {
+ switch( keysym )
+ {
+ case XK_Insert:
+ nKey = KEY_INSERT;
+ break;
+ case XK_Redo:
+ nKey = KEY_REPEAT;
+ break;
+ case XK_Undo:
+ nKey = KEY_UNDO;
+ break;
+ case XK_Find:
+ nKey = KEY_FIND;
+ break;
+ case XK_Help:
+ nKey = KEY_HELP;
+ break;
+ case XK_Menu:
+ nKey = KEY_CONTEXTMENU;
+ break;
+ }
+ }
+ else if( IsISOKey( keysym ) ) // XK_ISO_
+ {
+ switch( keysym )
+ {
+ case 0xFE20: // XK_ISO_Left_Tab:
+ nKey = KEY_TAB;
+ break;
+ }
+ }
+ else switch( keysym )
+ {
+ case XK_Return:
+ nKey = KEY_RETURN;
+ break;
+ case XK_BackSpace:
+ nKey = KEY_BACKSPACE;
+ break;
+ case XK_Delete:
+ nKey = KEY_DELETE;
+ break;
+ case XK_space:
+ nKey = KEY_SPACE;
+ break;
+ case XK_Tab:
+ nKey = KEY_TAB;
+ break;
+ case XK_Escape:
+ nKey = KEY_ESCAPE;
+ break;
+ case XK_plus:
+ nKey = KEY_ADD;
+ break;
+ case XK_minus:
+ nKey = KEY_SUBTRACT;
+ break;
+ case XK_asterisk:
+ nKey = KEY_MULTIPLY;
+ break;
+ case XK_slash:
+ nKey = KEY_DIVIDE;
+ break;
+ case XK_period:
+ nKey = KEY_POINT;
+ *pcPrintable = '.';
+ break;
+ case XK_comma:
+ nKey = KEY_COMMA;
+ break;
+ case XK_less:
+ nKey = KEY_LESS;
+ break;
+ case XK_greater:
+ nKey = KEY_GREATER;
+ break;
+ case XK_equal:
+ nKey = KEY_EQUAL;
+ break;
+ case XK_Hangul_Hanja:
+ nKey = KEY_HANGUL_HANJA;
+ break;
+ case XK_asciitilde:
+ nKey = KEY_TILDE;
+ *pcPrintable = '~';
+ break;
+ case XK_grave:
+ nKey = KEY_QUOTELEFT;
+ *pcPrintable = '`';
+ break;
+ case XK_bracketleft:
+ nKey = KEY_BRACKETLEFT;
+ *pcPrintable = '[';
+ break;
+ case XK_bracketright:
+ nKey = KEY_BRACKETRIGHT;
+ *pcPrintable = ']';
+ break;
+ case XK_semicolon:
+ nKey = KEY_SEMICOLON;
+ *pcPrintable = ';';
+ break;
+ case XK_quoteright:
+ nKey = KEY_QUOTERIGHT;
+ *pcPrintable = '\'';
+ break;
+ case XK_braceright:
+ nKey = KEY_RIGHTCURLYBRACKET;
+ *pcPrintable = '\'';
+ break;
+ case XK_numbersign:
+ nKey = KEY_NUMBERSIGN;
+ *pcPrintable = '#';
+ break;
+ case XK_colon:
+ nKey = KEY_COLON;
+ *pcPrintable = ':';
+ break;
+ case 0x1008ff27: // tdf#148986: XF86Forward
+ nKey = KEY_XF86FORWARD;
+ break;
+ case 0x1008ff26: // tdf#148986: XF86Back
+ nKey = KEY_XF86BACK;
+ break;
+ // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000
+ case 0x1000FF02: // apXK_Copy
+ nKey = KEY_COPY;
+ break;
+ case 0x1000FF03: // apXK_Cut
+ nKey = KEY_CUT;
+ break;
+ case 0x1000FF04: // apXK_Paste
+ nKey = KEY_PASTE;
+ break;
+ case 0x1000FF14: // apXK_Repeat
+ nKey = KEY_REPEAT;
+ break;
+ // Exit, Save
+ // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000
+ case 0x1000FF00:
+ nKey = KEY_DELETE;
+ break;
+ // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000
+ case 0x1000FF73: // hpXK_DeleteChar
+ nKey = KEY_DELETE;
+ break;
+ case 0x1000FF74: // hpXK_BackTab
+ case 0x1000FF75: // hpXK_KP_BackTab
+ nKey = KEY_TAB;
+ break;
+ // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - -
+ // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004
+ case 0x1004FF02: // osfXK_Copy
+ nKey = KEY_COPY;
+ break;
+ case 0x1004FF03: // osfXK_Cut
+ nKey = KEY_CUT;
+ break;
+ case 0x1004FF04: // osfXK_Paste
+ nKey = KEY_PASTE;
+ break;
+ case 0x1004FF07: // osfXK_BackTab
+ nKey = KEY_TAB;
+ break;
+ case 0x1004FF08: // osfXK_BackSpace
+ nKey = KEY_BACKSPACE;
+ break;
+ case 0x1004FF1B: // osfXK_Escape
+ nKey = KEY_ESCAPE;
+ break;
+ // Up, Down, Left, Right, PageUp, PageDown
+ // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - -
+ // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007
+ // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - -
+ // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005
+ case 0x1005FF10: // SunXK_F36
+ nKey = KEY_F11;
+ break;
+ case 0x1005FF11: // SunXK_F37
+ nKey = KEY_F12;
+ break;
+ case 0x1005FF70: // SunXK_Props
+ nKey = KEY_PROPERTIES;
+ break;
+ case 0x1005FF71: // SunXK_Front
+ nKey = KEY_FRONT;
+ break;
+ case 0x1005FF72: // SunXK_Copy
+ nKey = KEY_COPY;
+ break;
+ case 0x1005FF73: // SunXK_Open
+ nKey = KEY_OPEN;
+ break;
+ case 0x1005FF74: // SunXK_Paste
+ nKey = KEY_PASTE;
+ break;
+ case 0x1005FF75: // SunXK_Cut
+ nKey = KEY_CUT;
+ break;
+ }
+ return nKey;
+}
+
+KeySym SalDisplay::GetKeySym( XKeyEvent *pEvent,
+ char *pPrintable,
+ int *pLen,
+ KeySym *pUnmodifiedKeySym,
+ Status *pStatusReturn,
+ XIC aInputContext ) const
+{
+ KeySym nKeySym = 0;
+ memset( pPrintable, 0, *pLen );
+ *pStatusReturn = 0;
+
+ SalI18N_InputMethod* const pInputMethod =
+ pXLib_ ? pXLib_->GetInputMethod() : nullptr;
+
+ // first get the printable of the possibly modified KeySym
+ if ( (aInputContext == nullptr)
+ || (pEvent->type == KeyRelease)
+ || (pInputMethod != nullptr && pInputMethod->PosixLocale()) )
+ {
+ // XmbLookupString must not be called for KeyRelease events
+ // Cannot enter space in c locale problem #89616# #88978# btraq #4478197
+ *pLen = XLookupString( pEvent, pPrintable, 1, &nKeySym, nullptr );
+ }
+ else
+ {
+ *pLen = XmbLookupString( aInputContext,
+ pEvent, pPrintable, *pLen - 1, &nKeySym, pStatusReturn );
+
+ // Lookup the string again, now with appropriate size
+ if ( *pStatusReturn == XBufferOverflow )
+ {
+ pPrintable[ 0 ] = '\0';
+ return 0;
+ }
+
+ switch ( *pStatusReturn )
+ {
+ case XBufferOverflow:
+ /* unhandled error */
+ break;
+ case XLookupNone:
+ /* unhandled error */
+ break;
+ case XLookupKeySym:
+ /* this is a strange one: on exceed sometimes
+ * no printable is returned for the first char entered,
+ * just to retry lookup solves the problem. The problem
+ * is not yet fully understood, so restrict 2nd lookup
+ * to 7bit ascii chars */
+ if ( (XK_space <= nKeySym) && (XK_asciitilde >= nKeySym) )
+ {
+ *pLen = 1;
+ pPrintable[ 0 ] = static_cast<char>(nKeySym);
+ }
+ break;
+ case XLookupBoth:
+ case XLookupChars:
+
+ /* nothing to, char already in pPrintable */
+ break;
+ }
+ }
+
+ if( !bNumLockFromXS_
+ && (IsCursorKey(nKeySym)
+ || IsFunctionKey(nKeySym)
+ || IsKeypadKey(nKeySym)
+ || XK_Delete == nKeySym ) )
+ {
+ // For some X-servers special care is needed for Keypad keys.
+ // For example Solaris XServer:
+ // 2, 4, 6, 8 are classified as Cursorkeys (Up, Down, Left, Right)
+ // 1, 3, 5, 9 are classified as Functionkeys (F27,F29,F33,F35)
+ // 0 as Keypadkey, and the decimal point key not at all (KP_Insert)
+ KeySym nNewKeySym = XLookupKeysym( pEvent, nNumLockIndex_ );
+ if( nNewKeySym != NoSymbol )
+ nKeySym = nNewKeySym;
+ }
+
+ // Now get the unmodified KeySym for KeyCode retrieval
+ // try to strip off modifiers, e.g. Ctrl-$ becomes Ctrl-Shift-4
+ *pUnmodifiedKeySym = XkbKeycodeToKeysym( GetDisplay(), pEvent->keycode, 0, 0);
+
+ return nKeySym;
+}
+
+// Pointer
+static unsigned char nullmask_bits[] = { 0x00, 0x00, 0x00, 0x00 };
+static unsigned char nullcurs_bits[] = { 0x00, 0x00, 0x00, 0x00 };
+
+#define MAKE_BITMAP( name ) \
+ XCreateBitmapFromData( pDisp_, \
+ DefaultRootWindow( pDisp_ ), \
+ reinterpret_cast<const char*>(name##_bits), \
+ name##_width, \
+ name##_height )
+
+#define MAKE_CURSOR( name ) \
+ aCursBitmap = MAKE_BITMAP( name##curs ); \
+ aMaskBitmap = MAKE_BITMAP( name##mask ); \
+ nXHot = name##curs_x_hot; \
+ nYHot = name##curs_y_hot
+
+Cursor SalDisplay::GetPointer( PointerStyle ePointerStyle )
+{
+ Cursor &aCur = aPointerCache_[ePointerStyle];
+
+ if( aCur != None )
+ return aCur;
+
+ Pixmap aCursBitmap = None, aMaskBitmap = None;
+ unsigned int nXHot = 0, nYHot = 0;
+
+ switch( ePointerStyle )
+ {
+ case PointerStyle::Null:
+ MAKE_CURSOR( null );
+ break;
+ case PointerStyle::Arrow:
+ aCur = XCreateFontCursor( pDisp_, XC_left_ptr );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::Wait:
+ aCur = XCreateFontCursor( pDisp_, XC_watch );
+ break;
+ case PointerStyle::Text: // Mouse Pointer is a "I" Beam
+ aCur = XCreateFontCursor( pDisp_, XC_xterm );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::Help:
+ aCur = XCreateFontCursor( pDisp_, XC_question_arrow );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::Cross: // Mouse Pointer is a cross
+ aCur = XCreateFontCursor( pDisp_, XC_crosshair );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::NSize:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::SSize:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WSize:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::ESize:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowNSize:
+ aCur = XCreateFontCursor( pDisp_, XC_top_side );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowSSize:
+ aCur = XCreateFontCursor( pDisp_, XC_bottom_side );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowWSize:
+ aCur = XCreateFontCursor( pDisp_, XC_left_side );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowESize:
+ aCur = XCreateFontCursor( pDisp_, XC_right_side );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::NWSize:
+ aCur = XCreateFontCursor( pDisp_, XC_top_left_corner );
+ break;
+ case PointerStyle::NESize:
+ aCur = XCreateFontCursor( pDisp_, XC_top_right_corner );
+ break;
+ case PointerStyle::SWSize:
+ aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner );
+ break;
+ case PointerStyle::SESize:
+ aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner );
+ break;
+ case PointerStyle::WindowNWSize:
+ aCur = XCreateFontCursor( pDisp_, XC_top_left_corner );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowNESize:
+ aCur = XCreateFontCursor( pDisp_, XC_top_right_corner );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowSWSize:
+ aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::WindowSESize:
+ aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::HSplit:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow );
+ break;
+ case PointerStyle::VSplit:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow );
+ break;
+ case PointerStyle::HSizeBar:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); // ???
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::VSizeBar:
+ aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); // ???
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::RefHand:
+ aCur = XCreateFontCursor( pDisp_, XC_hand1 );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::Hand:
+ aCur = XCreateFontCursor( pDisp_, XC_hand2 );
+ break;
+ case PointerStyle::Magnify:
+ MAKE_CURSOR( magnify_ );
+ break;
+ case PointerStyle::Fill:
+ MAKE_CURSOR( fill_ );
+ break;
+ case PointerStyle::Move:
+ aCur = XCreateFontCursor( pDisp_, XC_fleur );
+ break;
+ case PointerStyle::MoveData:
+ MAKE_CURSOR( movedata_ );
+ break;
+ case PointerStyle::CopyData:
+ MAKE_CURSOR( copydata_ );
+ break;
+ case PointerStyle::MoveFile:
+ MAKE_CURSOR( movefile_ );
+ break;
+ case PointerStyle::CopyFile:
+ MAKE_CURSOR( copyfile_ );
+ break;
+ case PointerStyle::MoveFiles:
+ MAKE_CURSOR( movefiles_ );
+ break;
+ case PointerStyle::CopyFiles:
+ MAKE_CURSOR( copyfiles_ );
+ break;
+ case PointerStyle::NotAllowed:
+ MAKE_CURSOR( nodrop_ );
+ break;
+ case PointerStyle::Rotate:
+ MAKE_CURSOR( rotate_ );
+ break;
+ case PointerStyle::HShear:
+ MAKE_CURSOR( hshear_ );
+ break;
+ case PointerStyle::VShear:
+ MAKE_CURSOR( vshear_ );
+ break;
+ case PointerStyle::DrawLine:
+ MAKE_CURSOR( drawline_ );
+ break;
+ case PointerStyle::DrawRect:
+ MAKE_CURSOR( drawrect_ );
+ break;
+ case PointerStyle::DrawPolygon:
+ MAKE_CURSOR( drawpolygon_ );
+ break;
+ case PointerStyle::DrawBezier:
+ MAKE_CURSOR( drawbezier_ );
+ break;
+ case PointerStyle::DrawArc:
+ MAKE_CURSOR( drawarc_ );
+ break;
+ case PointerStyle::DrawPie:
+ MAKE_CURSOR( drawpie_ );
+ break;
+ case PointerStyle::DrawCircleCut:
+ MAKE_CURSOR( drawcirclecut_ );
+ break;
+ case PointerStyle::DrawEllipse:
+ MAKE_CURSOR( drawellipse_ );
+ break;
+ case PointerStyle::DrawConnect:
+ MAKE_CURSOR( drawconnect_ );
+ break;
+ case PointerStyle::DrawText:
+ MAKE_CURSOR( drawtext_ );
+ break;
+ case PointerStyle::Mirror:
+ MAKE_CURSOR( mirror_ );
+ break;
+ case PointerStyle::Crook:
+ MAKE_CURSOR( crook_ );
+ break;
+ case PointerStyle::Crop:
+ MAKE_CURSOR( crop_ );
+ break;
+ case PointerStyle::MovePoint:
+ MAKE_CURSOR( movepoint_ );
+ break;
+ case PointerStyle::MoveBezierWeight:
+ MAKE_CURSOR( movebezierweight_ );
+ break;
+ case PointerStyle::DrawFreehand:
+ MAKE_CURSOR( drawfreehand_ );
+ break;
+ case PointerStyle::DrawCaption:
+ MAKE_CURSOR( drawcaption_ );
+ break;
+ case PointerStyle::Pen: // Mouse Pointer is a pencil
+ aCur = XCreateFontCursor( pDisp_, XC_pencil );
+ SAL_WARN_IF( aCur == None, "vcl", "GetPointer: Could not define cursor" );
+ break;
+ case PointerStyle::LinkData:
+ MAKE_CURSOR( linkdata_ );
+ break;
+ case PointerStyle::MoveDataLink:
+ MAKE_CURSOR( movedlnk_ );
+ break;
+ case PointerStyle::CopyDataLink:
+ MAKE_CURSOR( copydlnk_ );
+ break;
+ case PointerStyle::LinkFile:
+ MAKE_CURSOR( linkfile_ );
+ break;
+ case PointerStyle::MoveFileLink:
+ MAKE_CURSOR( moveflnk_ );
+ break;
+ case PointerStyle::CopyFileLink:
+ MAKE_CURSOR( copyflnk_ );
+ break;
+ case PointerStyle::Chart:
+ MAKE_CURSOR( chart_ );
+ break;
+ case PointerStyle::Detective:
+ MAKE_CURSOR( detective_ );
+ break;
+ case PointerStyle::PivotCol:
+ MAKE_CURSOR( pivotcol_ );
+ break;
+ case PointerStyle::PivotRow:
+ MAKE_CURSOR( pivotrow_ );
+ break;
+ case PointerStyle::PivotField:
+ MAKE_CURSOR( pivotfld_ );
+ break;
+ case PointerStyle::PivotDelete:
+ MAKE_CURSOR( pivotdel_ );
+ break;
+ case PointerStyle::Chain:
+ MAKE_CURSOR( chain_ );
+ break;
+ case PointerStyle::ChainNotAllowed:
+ MAKE_CURSOR( chainnot_ );
+ break;
+ case PointerStyle::AutoScrollN:
+ MAKE_CURSOR(asn_ );
+ break;
+ case PointerStyle::AutoScrollS:
+ MAKE_CURSOR( ass_ );
+ break;
+ case PointerStyle::AutoScrollW:
+ MAKE_CURSOR( asw_ );
+ break;
+ case PointerStyle::AutoScrollE:
+ MAKE_CURSOR( ase_ );
+ break;
+ case PointerStyle::AutoScrollNW:
+ MAKE_CURSOR( asnw_ );
+ break;
+ case PointerStyle::AutoScrollNE:
+ MAKE_CURSOR( asne_ );
+ break;
+ case PointerStyle::AutoScrollSW:
+ MAKE_CURSOR( assw_ );
+ break;
+ case PointerStyle::AutoScrollSE:
+ MAKE_CURSOR( asse_ );
+ break;
+ case PointerStyle::AutoScrollNS:
+ MAKE_CURSOR( asns_ );
+ break;
+ case PointerStyle::AutoScrollWE:
+ MAKE_CURSOR( aswe_ );
+ break;
+ case PointerStyle::AutoScrollNSWE:
+ MAKE_CURSOR( asnswe_ );
+ break;
+ case PointerStyle::TextVertical:
+ MAKE_CURSOR( vertcurs_ );
+ break;
+
+ // #i32329# Enhanced table selection
+ case PointerStyle::TabSelectS:
+ MAKE_CURSOR( tblsels_ );
+ break;
+ case PointerStyle::TabSelectE:
+ MAKE_CURSOR( tblsele_ );
+ break;
+ case PointerStyle::TabSelectSE:
+ MAKE_CURSOR( tblselse_ );
+ break;
+ case PointerStyle::TabSelectW:
+ MAKE_CURSOR( tblselw_ );
+ break;
+ case PointerStyle::TabSelectSW:
+ MAKE_CURSOR( tblselsw_ );
+ break;
+
+ case PointerStyle::HideWhitespace:
+ MAKE_CURSOR( hidewhitespace_ );
+ break;
+ case PointerStyle::ShowWhitespace:
+ MAKE_CURSOR( showwhitespace_ );
+ break;
+ case PointerStyle::FatCross:
+ MAKE_CURSOR( fatcross_ );
+ break;
+
+ default:
+ OSL_FAIL("pointer not implemented");
+ aCur = XCreateFontCursor( pDisp_, XC_arrow );
+ break;
+ }
+
+ if( None == aCur )
+ {
+ XColor aBlack, aWhite, aDummy;
+ Colormap hColormap = GetColormap(m_nXDefaultScreen).GetXColormap();
+
+ XAllocNamedColor( pDisp_, hColormap, "black", &aBlack, &aDummy );
+ XAllocNamedColor( pDisp_, hColormap, "white", &aWhite, &aDummy );
+
+ aCur = XCreatePixmapCursor( pDisp_,
+ aCursBitmap, aMaskBitmap,
+ &aBlack, &aWhite,
+ nXHot, nYHot );
+
+ XFreePixmap( pDisp_, aCursBitmap );
+ XFreePixmap( pDisp_, aMaskBitmap );
+ }
+
+ return aCur;
+}
+
+int SalDisplay::CaptureMouse( SalFrame *pCapture )
+{
+ static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" );
+
+ if( !pCapture )
+ {
+ m_pCapture = nullptr;
+ if( !pEnv || !*pEnv )
+ XUngrabPointer( GetDisplay(), CurrentTime );
+ XFlush( GetDisplay() );
+ return 0;
+ }
+
+ m_pCapture = nullptr;
+
+ // FIXME: get rid of X11SalFrame
+ const SystemEnvData* pEnvData = pCapture->GetSystemData();
+ if( !pEnv || !*pEnv )
+ {
+ int ret = XGrabPointer( GetDisplay(),
+ static_cast<::Window>(pEnvData->GetWindowHandle(pCapture)),
+ False,
+ PointerMotionMask| ButtonPressMask|ButtonReleaseMask,
+ GrabModeAsync,
+ GrabModeAsync,
+ None,
+ static_cast<X11SalFrame*>(pCapture)->GetCursor(),
+ CurrentTime );
+
+ if( ret != GrabSuccess )
+ {
+ SAL_WARN("vcl", "SalDisplay::CaptureMouse could not grab pointer: " << ret);
+ return -1;
+ }
+ }
+
+ m_pCapture = pCapture;
+ return 1;
+}
+
+// Events
+
+bool SalX11Display::IsEvent()
+{
+ if( HasUserEvents() || XEventsQueued( pDisp_, QueuedAlready ) )
+ return true;
+
+ XFlush( pDisp_ );
+ return false;
+}
+
+void SalX11Display::Yield()
+{
+ if( DispatchInternalEvent() )
+ return;
+
+ XEvent aEvent;
+ DBG_ASSERT(GetSalInstance()->GetYieldMutex()->IsCurrentThread(),
+ "will crash soon since solar mutex not locked in SalDisplay::Yield" );
+
+ XNextEvent( pDisp_, &aEvent );
+
+ // coverity[overrun-buffer-val : FALSE] - coverity has problems with uno::Sequence
+ Dispatch( &aEvent );
+
+#ifdef DBG_UTIL
+ if( GetX11SalData()->HasXErrorOccurred() )
+ {
+ XFlush( pDisp_ );
+ DbgPrintDisplayEvent("SalDisplay::Yield (WasXError)", &aEvent);
+ }
+#endif
+ GetX11SalData()->ResetXErrorOccurred();
+}
+
+void SalX11Display::Dispatch( XEvent *pEvent )
+{
+ SalI18N_InputMethod* const pInputMethod =
+ pXLib_ ? pXLib_->GetInputMethod() : nullptr;
+
+ if( pInputMethod )
+ {
+ ::Window aFrameWindow = None;
+ if( pEvent->type == KeyPress || pEvent->type == KeyRelease )
+ {
+ const ::Window aWindow = pEvent->xkey.window;
+ for( auto pSalFrame : m_aFrames )
+ {
+ const X11SalFrame* pFrame = static_cast< const X11SalFrame* >( pSalFrame );
+ const ::Window aCurFrameWindow = pFrame->GetWindow();
+ if( aCurFrameWindow == aWindow || pFrame->GetShellWindow() == aWindow )
+ {
+ aFrameWindow = aCurFrameWindow;
+ break;
+ }
+ }
+ }
+ if( pInputMethod->FilterEvent( pEvent, aFrameWindow ) )
+ return;
+ }
+
+ SalInstance* pInstance = GetSalInstance();
+ pInstance->CallEventCallback( pEvent, sizeof( XEvent ) );
+
+ switch( pEvent->type )
+ {
+ case MotionNotify:
+ while( XCheckWindowEvent( pEvent->xany.display,
+ pEvent->xany.window,
+ ButtonMotionMask,
+ pEvent ) )
+ ;
+ m_nLastUserEventTime = pEvent->xmotion.time;
+ break;
+ case PropertyNotify:
+ if( pEvent->xproperty.atom == getWMAdaptor()->getAtom( WMAdaptor::VCL_SYSTEM_SETTINGS ) )
+ {
+ for(const ScreenData & rScreen : m_aScreens)
+ {
+ if( pEvent->xproperty.window == rScreen.m_aRefWindow )
+ {
+ for (auto pSalFrame : m_aFrames )
+ pSalFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
+ return;
+ }
+ }
+ }
+ break;
+ case MappingNotify:
+ if( MappingModifier == pEvent->xmapping.request )
+ {
+ XRefreshKeyboardMapping( &pEvent->xmapping );
+ ModifierMapping();
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ m_nLastUserEventTime = pEvent->xbutton.time;
+ break;
+ case KeyPress:
+ case KeyRelease:
+ m_nLastUserEventTime = pEvent->xkey.time;
+ break;
+ default:
+
+ if ( GetKbdExtension()->UseExtension()
+ && GetKbdExtension()->GetEventBase() == pEvent->type )
+ {
+ GetKbdExtension()->Dispatch( pEvent );
+ return;
+ }
+ break;
+ }
+
+ for (auto pSalFrame : m_aFrames )
+ {
+ X11SalFrame* pFrame = static_cast<X11SalFrame*>( pSalFrame );
+
+ ::Window aDispatchWindow = pEvent->xany.window;
+ if( pFrame->GetWindow() == aDispatchWindow
+ || pFrame->GetShellWindow() == aDispatchWindow
+ || pFrame->GetForeignParent() == aDispatchWindow
+ )
+ {
+ pFrame->Dispatch( pEvent );
+ return;
+ }
+ if( pEvent->type == ConfigureNotify && pEvent->xconfigure.window == pFrame->GetStackingWindow() )
+ {
+ pFrame->Dispatch( pEvent );
+ return;
+ }
+ }
+
+ // dispatch to salobjects
+ X11SalObject::Dispatch( pEvent );
+
+ // is this perhaps a root window that changed size ?
+ processRandREvent( pEvent );
+}
+
+#ifdef DBG_UTIL
+void SalDisplay::DbgPrintDisplayEvent(const char *pComment, const XEvent *pEvent) const
+{
+ static const char* const EventNames[] =
+ {
+ nullptr,
+ nullptr,
+ "KeyPress",
+ "KeyRelease",
+ "ButtonPress",
+ "ButtonRelease",
+ "MotionNotify",
+ "EnterNotify",
+ "LeaveNotify",
+ "FocusIn",
+ "FocusOut",
+ "KeymapNotify",
+ "Expose",
+ "GraphicsExpose",
+ "NoExpose",
+ "VisibilityNotify",
+ "CreateNotify",
+ "DestroyNotify",
+ "UnmapNotify",
+ "MapNotify",
+ "MapRequest",
+ "ReparentNotify",
+ "ConfigureNotify",
+ "ConfigureRequest",
+ "GravityNotify",
+ "ResizeRequest",
+ "CirculateNotify",
+ "CirculateRequest",
+ "PropertyNotify",
+ "SelectionClear",
+ "SelectionRequest",
+ "SelectionNotify",
+ "ColormapNotify",
+ "ClientMessage",
+ "MappingNotify"
+ };
+
+ if( pEvent->type <= MappingNotify )
+ {
+ SAL_INFO("vcl.app", "[" << pComment << "] "
+ << EventNames[pEvent->type]
+ << " s=" << pEvent->xany.send_event
+ << " w=" << pEvent->xany.window);
+
+ switch( pEvent->type )
+ {
+ case KeyPress:
+ case KeyRelease:
+ SAL_INFO("vcl.app", "\t\ts=" << pEvent->xkey.state
+ << " c=" << pEvent->xkey.keycode);
+ break;
+
+ case ButtonPress:
+ case ButtonRelease:
+ SAL_INFO("vcl.app", "\t\ts=" << pEvent->xbutton.state
+ << " b=" << pEvent->xbutton.button
+ << " x=" << pEvent->xbutton.x
+ << " y=" << pEvent->xbutton.y
+ << " rx=" << pEvent->xbutton.x_root
+ << " ry=" << pEvent->xbutton.y_root);
+ break;
+
+ case MotionNotify:
+ SAL_INFO("vcl.app", "\t\ts=" << pEvent->xmotion.state
+ << " x=" << pEvent->xmotion.x
+ << " y=" << pEvent->xmotion.y);
+ break;
+
+ case EnterNotify:
+ case LeaveNotify:
+ SAL_INFO("vcl.app", "\t\tm=" << pEvent->xcrossing.mode
+ << " f=" << pEvent->xcrossing.focus
+ << " x=" << pEvent->xcrossing.x
+ << " y=" << pEvent->xcrossing.y);
+ break;
+
+ case FocusIn:
+ case FocusOut:
+ SAL_INFO("vcl.app", "\t\tm=" << pEvent->xfocus.mode
+ << " d=" << pEvent->xfocus.detail);
+ break;
+
+ case Expose:
+ case GraphicsExpose:
+ SAL_INFO("vcl.app", "\t\tc=" << pEvent->xexpose.count
+ << " " << pEvent->xexpose.width
+ << "*" << pEvent->xexpose.height
+ << " " << pEvent->xexpose.x
+ << "+" << pEvent->xexpose.y );
+ break;
+
+ case VisibilityNotify:
+ SAL_INFO("vcl.app", "\t\ts=" << pEvent->xvisibility.state);
+ break;
+
+ case CreateNotify:
+ case DestroyNotify:
+ break;
+
+ case MapNotify:
+ case UnmapNotify:
+ break;
+
+ case ReparentNotify:
+ SAL_INFO("vcl.app", "\t\tp=" << sal::static_int_cast< int >(
+ pEvent->xreparent.parent)
+ << " x=" << pEvent->xreparent.x
+ << " y=" << pEvent->xreparent.y );
+ break;
+
+ case ConfigureNotify:
+ SAL_INFO("vcl.app", "\t\tb=" << pEvent->xconfigure.border_width
+ << " " << pEvent->xconfigure.width
+ << "*" << pEvent->xconfigure.height
+ << " " << pEvent->xconfigure.x
+ << "+" << pEvent->xconfigure.y);
+ break;
+
+ case PropertyNotify:
+ SAL_INFO("vcl.app", "\t\ta=" << GetAtomName(
+ pDisp_, pEvent->xproperty.atom)
+ << std::showbase << std::hex << std::uppercase
+ << " (" << sal::static_int_cast< unsigned int >(
+ pEvent->xproperty.atom) << ").");
+ break;
+
+ case ColormapNotify:
+ SAL_INFO("vcl.app", "\t\tc=" << pEvent->xcolormap.colormap
+ << " n=" << pEvent->xcolormap.c_new
+ << " s=" << pEvent->xcolormap.state);
+ break;
+
+ case ClientMessage:
+ SAL_INFO("vcl.app", "\t\ta=" << GetAtomName(
+ pDisp_, pEvent->xclient.message_type)
+ << std::showbase << std::hex << std::uppercase
+ << " (" << sal::static_int_cast< unsigned int >(
+ pEvent->xclient.message_type) << ")"
+ << std::dec
+ << " f=" << pEvent->xclient.format
+ << std::hex
+ << " [" << pEvent->xclient.data.l[0]
+ << "," << pEvent->xclient.data.l[1]
+ << "," << pEvent->xclient.data.l[2]
+ << "," << pEvent->xclient.data.l[3]
+ << "," << pEvent->xclient.data.l[4]
+ << "]");
+ break;
+
+ case MappingNotify:
+ SAL_INFO("vcl.app", "\t\tr="
+ << (MappingModifier == pEvent->xmapping.request ?
+ "MappingModifier" :
+ (MappingKeyboard == pEvent->xmapping.request ?
+ "MappingKeyboard" : "MappingPointer"))
+ << "d");
+
+ break;
+ }
+ }
+ else
+ SAL_INFO("vcl.app", "[" << pComment << "] "
+ << pEvent->type
+ << " s=" << pEvent->xany.send_event
+ << " w=" << pEvent->xany.window);
+}
+
+void SalDisplay::PrintInfo() const
+{
+ if( IsDisplay() )
+ {
+ SAL_INFO( "vcl", "Environment" );
+ SAL_INFO( "vcl", "\t$DISPLAY \t\"" << GetEnv( "DISPLAY" ) << "\"");
+ SAL_INFO( "vcl", "\t$SAL_VISUAL \t\"" << GetEnv( "SAL_VISUAL" ) << "\"");
+ SAL_INFO( "vcl", "\t$SAL_IGNOREXERRORS\t\"" << GetEnv( "SAL_IGNOREXERRORS" ) << "\"");
+ SAL_INFO( "vcl", "\t$SAL_PROPERTIES \t\"" << GetEnv( "SAL_PROPERTIES" ) << "\"");
+ SAL_INFO( "vcl", "\t$SAL_SYNCHRONIZE \t\"" << GetEnv( "SAL_SYNCHRONIZE" ) << "\"");
+
+ char sHostname[ 120 ];
+ gethostname (sHostname, 120 );
+ SAL_INFO( "vcl", "Client" );
+ SAL_INFO( "vcl", "\tHost \t\"" << sHostname << "\"");
+
+ SAL_INFO( "vcl", "Display" );
+ SAL_INFO( "vcl", "\tHost \t\"" << DisplayString(pDisp_) << "\"");
+ SAL_INFO( "vcl", "\tVendor (Release) \t\"" << ServerVendor(pDisp_) << " (" << VendorRelease(pDisp_) << ")\"");
+ SAL_INFO( "vcl", "\tProtocol \t" << ProtocolVersion(pDisp_) << "." << ProtocolRevision(pDisp_) );
+ SAL_INFO( "vcl", "\tScreen (count,def)\t" << m_nXDefaultScreen.getXScreen() << " (" << ScreenCount(pDisp_) << "," << DefaultScreen(pDisp_) << ")");
+ SAL_INFO( "vcl", "\tshift ctrl alt \t" << KeyStr( nShiftKeySym_ ) << " (0x" << std::hex << sal::static_int_cast< unsigned int >(nShiftKeySym_) << ") "
+ << KeyStr( nCtrlKeySym_ ) << " (0x" << sal::static_int_cast< unsigned int >(nCtrlKeySym_) << ") "
+ << KeyStr( nMod1KeySym_ ) << " (0x" << sal::static_int_cast< unsigned int >(nMod1KeySym_) << ")");
+ if( XExtendedMaxRequestSize(pDisp_) != 0 )
+ SAL_INFO( "vcl", "\tXMaxRequestSize \t" << XMaxRequestSize(pDisp_) * 4 << " " << XExtendedMaxRequestSize(pDisp_) * 4 << " [bytes]");
+ SAL_INFO( "vcl", "\tWMName \t" << getWMAdaptor()->getWindowManagerName() );
+ }
+ SAL_INFO( "vcl", "Screen" );
+ SAL_INFO( "vcl", "\tResolution/Size \t" << aResolution_.A() << "*" << aResolution_.B()
+ << " " << m_aScreens[m_nXDefaultScreen.getXScreen()].m_aSize.Width() << "*" << m_aScreens[m_nXDefaultScreen.getXScreen()].m_aSize.Height()
+ << " " << (std::hypot( DisplayWidthMM ( pDisp_, m_nXDefaultScreen.getXScreen() ),
+ DisplayHeightMM( pDisp_, m_nXDefaultScreen.getXScreen() ) ) / 25.4 ) << "\"" );
+ SAL_INFO( "vcl", "\tBlack&White \t" << GetColormap(m_nXDefaultScreen).GetBlackPixel() << " "
+ << GetColormap(m_nXDefaultScreen).GetWhitePixel() );
+ SAL_INFO( "vcl", "\tRGB \t0x" << std::hex << GetVisual(m_nXDefaultScreen).red_mask
+ << " 0x" << GetVisual(m_nXDefaultScreen).green_mask
+ << " 0x" << GetVisual(m_nXDefaultScreen).blue_mask);
+}
+#endif
+
+void SalDisplay::addXineramaScreenUnique( int i, tools::Long i_nX, tools::Long i_nY, tools::Long i_nWidth, tools::Long i_nHeight )
+{
+ // see if any frame buffers are at the same coordinates
+ // this can happen with weird configuration e.g. on
+ // XFree86 and Clone displays
+ const size_t nScreens = m_aXineramaScreens.size();
+ for( size_t n = 0; n < nScreens; n++ )
+ {
+ if( m_aXineramaScreens[n].Left() == i_nX &&
+ m_aXineramaScreens[n].Top() == i_nY )
+ {
+ if( m_aXineramaScreens[n].GetWidth() < i_nWidth ||
+ m_aXineramaScreens[n].GetHeight() < i_nHeight )
+ {
+ m_aXineramaScreenIndexMap[i] = n;
+ m_aXineramaScreens[n].SetSize( AbsoluteScreenPixelSize( i_nWidth, i_nHeight ) );
+ }
+ return;
+ }
+ }
+ m_aXineramaScreenIndexMap[i] = m_aXineramaScreens.size();
+ m_aXineramaScreens.emplace_back( AbsoluteScreenPixelPoint( i_nX, i_nY ), AbsoluteScreenPixelSize( i_nWidth, i_nHeight ) );
+}
+
+void SalDisplay::InitXinerama()
+{
+ if( m_aScreens.size() > 1 )
+ {
+ m_bXinerama = false;
+ return; // multiple screens mean no xinerama
+ }
+ if( !XineramaIsActive( pDisp_ ) )
+ return;
+
+ int nFramebuffers = 1;
+ XineramaScreenInfo* pScreens = XineramaQueryScreens( pDisp_, &nFramebuffers );
+ if( !pScreens )
+ return;
+
+ if( nFramebuffers > 1 )
+ {
+ m_aXineramaScreens = std::vector<AbsoluteScreenPixelRectangle>();
+ m_aXineramaScreenIndexMap = std::vector<int>(nFramebuffers);
+ for( int i = 0; i < nFramebuffers; i++ )
+ {
+ addXineramaScreenUnique( i, pScreens[i].x_org,
+ pScreens[i].y_org,
+ pScreens[i].width,
+ pScreens[i].height );
+ }
+ m_bXinerama = m_aXineramaScreens.size() > 1;
+ }
+ XFree( pScreens );
+#if OSL_DEBUG_LEVEL > 1
+ if( m_bXinerama )
+ {
+ for (auto const& screen : m_aXineramaScreens)
+ SAL_INFO("vcl.app", "Xinerama screen: "
+ << screen.GetWidth()
+ << "x" << screen.GetHeight()
+ << "+" << screen.Left()
+ << "+" << screen.Top());
+ }
+#endif
+}
+
+extern "C"
+{
+ static Bool timestamp_predicate( Display*, XEvent* i_pEvent, XPointer i_pArg )
+ {
+ SalDisplay* pSalDisplay = reinterpret_cast<SalDisplay*>(i_pArg);
+ if( i_pEvent->type == PropertyNotify &&
+ i_pEvent->xproperty.window == pSalDisplay->GetDrawable( pSalDisplay->GetDefaultXScreen() ) &&
+ i_pEvent->xproperty.atom == pSalDisplay->getWMAdaptor()->getAtom( WMAdaptor::SAL_GETTIMEEVENT )
+ )
+ return True;
+
+ return False;
+ }
+}
+
+Time SalDisplay::GetEventTimeImpl( bool i_bAlwaysReget ) const
+{
+ if( m_nLastUserEventTime == CurrentTime || i_bAlwaysReget )
+ {
+ // get current server time
+ unsigned char c = 0;
+ XEvent aEvent;
+ Atom nAtom = getWMAdaptor()->getAtom( WMAdaptor::SAL_GETTIMEEVENT );
+ XChangeProperty( GetDisplay(), GetDrawable( GetDefaultXScreen() ),
+ nAtom, nAtom, 8, PropModeReplace, &c, 1 );
+ XIfEvent( GetDisplay(), &aEvent, timestamp_predicate, reinterpret_cast<XPointer>(const_cast<SalDisplay *>(this)));
+ m_nLastUserEventTime = aEvent.xproperty.time;
+ }
+ return m_nLastUserEventTime;
+}
+
+SalVisual::SalVisual()
+{
+ visual = nullptr;
+}
+
+SalVisual::SalVisual( const XVisualInfo* pXVI )
+{
+ *static_cast<XVisualInfo*>(this) = *pXVI;
+}
+
+// Converts the order of bytes of a Pixel into bytes of a Color
+// This is not reversible for the 6 XXXA
+
+// Color is RGB (ABGR) a=0xFF000000, r=0xFF0000, g=0xFF00, b=0xFF
+
+SalColormap::SalColormap( const SalDisplay *pDisplay, Colormap hColormap,
+ SalX11Screen nXScreen )
+ : m_pDisplay( pDisplay ),
+ m_hColormap( hColormap )
+{
+ m_aVisual = m_pDisplay->GetVisual( nXScreen );
+
+ XColor aColor;
+
+ GetXPixel( aColor, 0x00, 0x00, 0x00 );
+ m_nBlackPixel = aColor.pixel;
+
+ GetXPixel( aColor, 0xFF, 0xFF, 0xFF );
+ m_nWhitePixel = aColor.pixel;
+
+ m_nUsed = 1 << m_aVisual.GetDepth();
+
+ if( m_aVisual.GetClass() != PseudoColor )
+ return;
+
+ int r, g, b;
+
+ // black, white, gray, ~gray = 4
+ GetXPixels( aColor, 0xC0, 0xC0, 0xC0 );
+
+ // light colors: 3 * 2 = 6
+
+ GetXPixels( aColor, 0x00, 0x00, 0xFF );
+ GetXPixels( aColor, 0x00, 0xFF, 0x00 );
+ GetXPixels( aColor, 0x00, 0xFF, 0xFF );
+
+ // standard colors: 7 * 2 = 14
+ GetXPixels( aColor, 0x00, 0x00, 0x80 );
+ GetXPixels( aColor, 0x00, 0x80, 0x00 );
+ GetXPixels( aColor, 0x00, 0x80, 0x80 );
+ GetXPixels( aColor, 0x80, 0x00, 0x00 );
+ GetXPixels( aColor, 0x80, 0x00, 0x80 );
+ GetXPixels( aColor, 0x80, 0x80, 0x00 );
+ GetXPixels( aColor, 0x80, 0x80, 0x80 );
+ GetXPixels( aColor, 0x00, 0xB8, 0xFF ); // Blue 7
+
+ // cube: 6*6*6 - 8 = 208
+ for( r = 0; r < 0x100; r += 0x33 ) // 0x33, 0x66, 0x99, 0xCC, 0xFF
+ for( g = 0; g < 0x100; g += 0x33 )
+ for( b = 0; b < 0x100; b += 0x33 )
+ GetXPixels( aColor, r, g, b );
+
+ // gray: 16 - 6 = 10
+ for( g = 0x11; g < 0xFF; g += 0x11 )
+ GetXPixels( aColor, g, g, g );
+
+ // green: 16 - 6 = 10
+ for( g = 0x11; g < 0xFF; g += 0x11 )
+ GetXPixels( aColor, 0, g, 0 );
+
+ // red: 16 - 6 = 10
+ for( r = 0x11; r < 0xFF; r += 0x11 )
+ GetXPixels( aColor, r, 0, 0 );
+
+ // blue: 16 - 6 = 10
+ for( b = 0x11; b < 0xFF; b += 0x11 )
+ GetXPixels( aColor, 0, 0, b );
+
+}
+
+// MonoChrome
+SalColormap::SalColormap()
+ : m_pDisplay( vcl_sal::getSalDisplay(GetGenericUnixSalData()) ),
+ m_hColormap( None ),
+ m_nWhitePixel( 1 ),
+ m_nBlackPixel( 0 ),
+ m_nUsed( 2 )
+{
+ m_aPalette = std::vector<Color>(m_nUsed);
+
+ m_aPalette[m_nBlackPixel] = COL_BLACK;
+ m_aPalette[m_nWhitePixel] = COL_WHITE;
+}
+
+// TrueColor
+SalColormap::SalColormap( sal_uInt16 nDepth )
+ : m_pDisplay( vcl_sal::getSalDisplay(GetGenericUnixSalData()) ),
+ m_hColormap( None ),
+ m_nWhitePixel( (1 << nDepth) - 1 ),
+ m_nBlackPixel( 0x00000000 ),
+ m_nUsed( 1 << nDepth )
+{
+ SalX11Screen nXScreen( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDefaultXScreen() );
+ const SalVisual *pVisual = &m_pDisplay->GetVisual( nXScreen );
+
+ if( pVisual->GetClass() == TrueColor && pVisual->GetDepth() == nDepth )
+ m_aVisual = *pVisual;
+ else
+ {
+ XVisualInfo aVI;
+
+ if( !XMatchVisualInfo( m_pDisplay->GetDisplay(),
+ m_pDisplay->GetDefaultXScreen().getXScreen(),
+ nDepth,
+ TrueColor,
+ &aVI ) )
+ {
+ aVI.visual = new Visual;
+ aVI.visualid = VisualID(-1);
+ aVI.screen = -1;
+ aVI.depth = nDepth;
+ aVI.c_class = TrueColor;
+ if( 24 == nDepth ) // 888
+ {
+ aVI.red_mask = 0xFF0000;
+ aVI.green_mask = 0x00FF00;
+ aVI.blue_mask = 0x0000FF;
+ }
+ else if( 8 == nDepth ) // 332
+ {
+ aVI.red_mask = 0x0000E0;
+ aVI.green_mask = 0x00001C;
+ aVI.blue_mask = 0x000003;
+ }
+ else
+ {
+ aVI.red_mask = 0x000000;
+ aVI.green_mask = 0x000000;
+ aVI.blue_mask = 0x000000;
+ }
+ aVI.colormap_size = 0;
+ aVI.bits_per_rgb = 8;
+
+ aVI.visual->ext_data = nullptr;
+ aVI.visual->visualid = aVI.visualid;
+ aVI.visual->c_class = aVI.c_class;
+ aVI.visual->red_mask = aVI.red_mask;
+ aVI.visual->green_mask = aVI.green_mask;
+ aVI.visual->blue_mask = aVI.blue_mask;
+ aVI.visual->bits_per_rgb = aVI.bits_per_rgb;
+ aVI.visual->map_entries = aVI.colormap_size;
+
+ m_aVisual = SalVisual( &aVI );
+ m_aVisualOwnership.owner = true;
+ }
+ else
+ m_aVisual = SalVisual( &aVI );
+ }
+}
+
+SalColormap::~SalColormap()
+{
+ if (m_aVisualOwnership.owner)
+ {
+ delete m_aVisual.visual;
+ }
+}
+
+inline bool SalColormap::GetXPixel( XColor &rColor,
+ int r,
+ int g,
+ int b ) const
+{
+ rColor.red = r * 257;
+ rColor.green = g * 257;
+ rColor.blue = b * 257;
+ return XAllocColor( GetXDisplay(), m_hColormap, &rColor );
+}
+
+bool SalColormap::GetXPixels( XColor &rColor,
+ int r,
+ int g,
+ int b ) const
+{
+ if( !GetXPixel( rColor, r, g, b ) )
+ return false;
+ if( rColor.pixel & 1 )
+ return true;
+ return GetXPixel( rColor, r^0xFF, g^0xFF, b^0xFF );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/salinst.cxx b/vcl/unx/generic/app/salinst.cxx
new file mode 100644
index 0000000000..a77aca2648
--- /dev/null
+++ b/vcl/unx/generic/app/salinst.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <config_skia.h>
+#if HAVE_FEATURE_SKIA
+#include <skia/x11/gdiimpl.hxx>
+#include <skia/salbmp.hxx>
+#endif
+
+#include <headless/svpbmp.hxx>
+#include <unx/saldata.hxx>
+#include <unx/saldisp.hxx>
+#include <unx/salinst.h>
+#include <unx/geninst.h>
+#include <unx/genpspgraphics.h>
+#include <unx/salframe.h>
+#include <unx/sm.hxx>
+#include <unx/i18n_im.hxx>
+
+#include <vcl/inputtypes.hxx>
+
+#include <salwtype.hxx>
+
+// plugin factory function
+extern "C"
+{
+ VCLPLUG_GEN_PUBLIC SalInstance* create_SalInstance()
+ {
+ /* #i92121# workaround deadlocks in the X11 implementation
+ */
+ static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
+ /* #i90094#
+ from now on we know that an X connection will be
+ established, so protect X against itself
+ */
+ if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
+ XInitThreads();
+
+ X11SalInstance* pInstance = new X11SalInstance( std::make_unique<SalYieldMutex>() );
+
+ // initialize SalData
+ X11SalData *pSalData = new X11SalData();
+
+ pSalData->Init();
+ pInstance->SetLib( pSalData->GetLib() );
+
+ return pInstance;
+ }
+}
+
+X11SalInstance::X11SalInstance(std::unique_ptr<SalYieldMutex> pMutex)
+ : SalGenericInstance(std::move(pMutex))
+ , mpXLib(nullptr)
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maAppData.mxToolkitName = OUString("x11");
+ m_bSupportsOpenGL = true;
+#if HAVE_FEATURE_SKIA
+ X11SkiaSalGraphicsImpl::prepareSkia();
+#if SKIA_USE_BITMAP32
+ if (SkiaHelper::isVCLSkiaEnabled())
+ m_bSupportsBitmap32 = true;
+#endif
+#endif
+}
+
+X11SalInstance::~X11SalInstance()
+{
+ // close session management
+ SessionManagerClient::close();
+
+ // dispose SalDisplay list from SalData
+ // would be done in a static destructor else which is
+ // a little late
+ GetGenericUnixSalData()->Dispose();
+
+#if HAVE_FEATURE_SKIA
+ SkiaHelper::cleanup();
+#endif
+}
+
+SalX11Display* X11SalInstance::CreateDisplay() const
+{
+ return new SalX11Display( mpXLib->GetDisplay() );
+}
+
+// AnyInput from sv/mow/source/app/svapp.cxx
+
+namespace {
+
+struct PredicateReturn
+{
+ VclInputFlags nType;
+ bool bRet;
+};
+
+}
+
+extern "C" {
+static Bool ImplPredicateEvent( Display *, XEvent *pEvent, char *pData )
+{
+ PredicateReturn *pPre = reinterpret_cast<PredicateReturn *>(pData);
+
+ if ( pPre->bRet )
+ return False;
+
+ VclInputFlags nType;
+
+ switch( pEvent->type )
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ nType = VclInputFlags::MOUSE;
+ break;
+
+ case KeyPress:
+ //case KeyRelease:
+ nType = VclInputFlags::KEYBOARD;
+ break;
+ case Expose:
+ case GraphicsExpose:
+ case NoExpose:
+ nType = VclInputFlags::PAINT;
+ break;
+ default:
+ nType = VclInputFlags::NONE;
+ }
+
+ if ( (nType & pPre->nType) || ( nType == VclInputFlags::NONE && (pPre->nType & VclInputFlags::OTHER) ) )
+ pPre->bRet = true;
+
+ return False;
+}
+}
+
+bool X11SalInstance::AnyInput(VclInputFlags nType)
+{
+ GenericUnixSalData *pData = GetGenericUnixSalData();
+ Display *pDisplay = vcl_sal::getSalDisplay(pData)->GetDisplay();
+ bool bRet = false;
+
+ if( (nType & VclInputFlags::TIMER) && (mpXLib && mpXLib->CheckTimeout(false)) )
+ bRet = true;
+
+ if( !bRet && XPending(pDisplay) )
+ {
+ PredicateReturn aInput;
+ XEvent aEvent;
+
+ aInput.bRet = false;
+ aInput.nType = nType;
+
+ XCheckIfEvent(pDisplay, &aEvent, ImplPredicateEvent,
+ reinterpret_cast<char *>(&aInput) );
+
+ bRet = aInput.bRet;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "AnyInput "
+ << std::showbase << std::hex
+ << static_cast<unsigned int>(nType)
+ << " = " << (bRet ? "true" : "false"));
+#endif
+ return bRet;
+}
+
+bool X11SalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
+{
+ return mpXLib->Yield( bWait, bHandleAllCurrentEvents );
+}
+
+OUString X11SalInstance::GetConnectionIdentifier()
+{
+ static const char* pDisplay = getenv( "DISPLAY" );
+ return pDisplay ? OUString::createFromAscii(pDisplay) : OUString();
+}
+
+SalFrame *X11SalInstance::CreateFrame( SalFrame *pParent, SalFrameStyleFlags nSalFrameStyle )
+{
+ SalFrame *pFrame = new X11SalFrame( pParent, nSalFrameStyle );
+
+ return pFrame;
+}
+
+SalFrame* X11SalInstance::CreateChildFrame( SystemParentData* pParentData, SalFrameStyleFlags nStyle )
+{
+ SalFrame* pFrame = new X11SalFrame( nullptr, nStyle, pParentData );
+
+ return pFrame;
+}
+
+void X11SalInstance::DestroyFrame( SalFrame* pFrame )
+{
+ delete pFrame;
+}
+
+void X11SalInstance::AfterAppInit()
+{
+ assert( mpXLib->GetDisplay() );
+ assert( mpXLib->GetInputMethod() );
+
+ SalX11Display *pSalDisplay = CreateDisplay();
+ mpXLib->GetInputMethod()->CreateMethod( mpXLib->GetDisplay() );
+ pSalDisplay->SetupInput();
+}
+
+void X11SalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&) {}
+
+void X11SalInstance::PostPrintersChanged()
+{
+ SalDisplay* pDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ for (auto pSalFrame : pDisp->getFrames() )
+ pDisp->PostEvent( pSalFrame, nullptr, SalEvent::PrinterChanged );
+}
+
+std::unique_ptr<GenPspGraphics> X11SalInstance::CreatePrintGraphics()
+{
+ return std::make_unique<GenPspGraphics>();
+}
+
+std::shared_ptr<SalBitmap> X11SalInstance::CreateSalBitmap()
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return std::make_shared<SkiaSalBitmap>();
+#endif
+ return std::make_shared<SvpSalBitmap>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/saltimer.cxx b/vcl/unx/generic/app/saltimer.cxx
new file mode 100644
index 0000000000..dc7a61dfe0
--- /dev/null
+++ b/vcl/unx/generic/app/saltimer.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sys/time.h>
+
+#include <unx/salunxtime.h>
+#include <unx/saldisp.hxx>
+#include <unx/saltimer.h>
+#include <unx/salinst.h>
+
+void SalXLib::StopTimer()
+{
+ m_aTimeout.tv_sec = 0;
+ m_aTimeout.tv_usec = 0;
+ m_nTimeoutMS = 0;
+}
+
+void SalXLib::StartTimer( sal_uInt64 nMS )
+{
+ timeval Timeout (m_aTimeout); // previous timeout.
+ gettimeofday (&m_aTimeout, nullptr);
+
+ m_nTimeoutMS = nMS;
+ m_aTimeout += m_nTimeoutMS;
+
+ if ((Timeout > m_aTimeout) || (Timeout.tv_sec == 0))
+ {
+ // Wakeup from previous timeout (or stopped timer).
+ Wakeup();
+ }
+}
+
+SalTimer* X11SalInstance::CreateSalTimer()
+{
+ return new X11SalTimer( mpXLib );
+}
+
+X11SalTimer::~X11SalTimer()
+{
+}
+
+void X11SalTimer::Stop()
+{
+ mpXLib->StopTimer();
+}
+
+void X11SalTimer::Start( sal_uInt64 nMS )
+{
+ mpXLib->StartTimer( nMS );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/sm.cxx b/vcl/unx/generic/app/sm.cxx
new file mode 100644
index 0000000000..071ac32fdb
--- /dev/null
+++ b/vcl/unx/generic/app/sm.cxx
@@ -0,0 +1,857 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+#include <fcntl.h>
+
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+#include <rtl/process.h>
+#include <osl/security.h>
+#include <osl/diagnose.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <unx/sm.hxx>
+#include <unx/saldisp.hxx>
+#include <unx/salinst.h>
+
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include <salframe.hxx>
+#include <salsession.hxx>
+
+namespace {
+
+class IceSalSession : public SalSession
+{
+public:
+ IceSalSession() {}
+
+private:
+ virtual ~IceSalSession() override {}
+
+ virtual void queryInteraction() override;
+ virtual void interactionDone() override;
+ virtual void saveDone() override;
+ virtual bool cancelShutdown() override;
+};
+
+}
+
+std::unique_ptr<SalSession> X11SalInstance::CreateSalSession()
+{
+ SAL_INFO("vcl.sm", "X11SalInstance::CreateSalSession");
+
+ std::unique_ptr<SalSession> p(new IceSalSession);
+ SessionManagerClient::open(p.get());
+ return p;
+}
+
+void IceSalSession::queryInteraction()
+{
+ SAL_INFO("vcl.sm", "IceSalSession::queryInteraction");
+
+ if( ! SessionManagerClient::queryInteraction() )
+ {
+ SAL_INFO("vcl.sm.debug", " call SalSessionInteractionEvent");
+ SalSessionInteractionEvent aEvent( false );
+ CallCallback( &aEvent );
+ }
+}
+
+void IceSalSession::interactionDone()
+{
+ SAL_INFO("vcl.sm", "IceSalSession::interactionDone");
+
+ SessionManagerClient::interactionDone( false );
+}
+
+void IceSalSession::saveDone()
+{
+ SAL_INFO("vcl.sm", "IceSalSession::saveDone");
+
+ SessionManagerClient::saveDone();
+}
+
+bool IceSalSession::cancelShutdown()
+{
+ SAL_INFO("vcl.sm", "IceSalSession::cancelShutdown");
+
+ SessionManagerClient::interactionDone( true );
+ return false;
+}
+
+extern "C" {
+
+static void ICEWatchProc(
+ IceConn ice_conn, IcePointer client_data, Bool opening,
+ IcePointer * watch_data);
+
+static void ICEConnectionWorker(void * data);
+
+}
+
+class ICEConnectionObserver
+{
+ friend void ICEWatchProc(IceConn, IcePointer, Bool, IcePointer *);
+
+ friend void ICEConnectionWorker(void *);
+
+ struct pollfd* m_pFilehandles;
+ int m_nConnections;
+ IceConn* m_pConnections;
+ int m_nWakeupFiles[2];
+ oslThread m_ICEThread;
+ IceIOErrorHandler m_origIOErrorHandler;
+ IceErrorHandler m_origErrorHandler;
+
+ void wakeup();
+
+public:
+ osl::Mutex m_ICEMutex;
+
+ ICEConnectionObserver()
+ : m_pFilehandles(nullptr)
+ , m_nConnections(0)
+ , m_pConnections(nullptr)
+ , m_ICEThread(nullptr)
+ , m_origIOErrorHandler(nullptr)
+ , m_origErrorHandler(nullptr)
+ {
+ SAL_INFO("vcl.sm", "ICEConnectionObserver::ICEConnectionObserver");
+
+ m_nWakeupFiles[0] = m_nWakeupFiles[1] = 0;
+ }
+
+ void activate();
+ void deactivate();
+ void terminate(oslThread iceThread);
+};
+
+SalSession * SessionManagerClient::m_pSession = nullptr;
+std::unique_ptr< ICEConnectionObserver >
+SessionManagerClient::m_xICEConnectionObserver;
+SmcConn SessionManagerClient::m_pSmcConnection = nullptr;
+OString SessionManagerClient::m_aClientID = ""_ostr;
+OString SessionManagerClient::m_aTimeID = ""_ostr;
+OString SessionManagerClient::m_aClientTimeID = ""_ostr;
+bool SessionManagerClient::m_bDocSaveDone = false; // HACK
+
+extern "C" {
+
+static void IgnoreIceErrors(
+ SAL_UNUSED_PARAMETER IceConn, SAL_UNUSED_PARAMETER Bool,
+ SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER unsigned long,
+ SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER int,
+ SAL_UNUSED_PARAMETER IcePointer)
+{}
+
+static void IgnoreIceIOErrors(SAL_UNUSED_PARAMETER IceConn) {}
+
+}
+
+static SmProp* pSmProps = nullptr;
+static SmProp** ppSmProps = nullptr;
+static char ** ppSmDel = nullptr;
+
+static int nSmProps = 0;
+static int nSmDel = 0;
+static unsigned char *pSmRestartHint = nullptr;
+
+
+enum { eCloneCommand, eProgram, eRestartCommand, eUserId, eRestartStyleHint };
+enum { eDiscardCommand };
+
+
+static void BuildSmPropertyList()
+{
+ SAL_INFO("vcl.sm", "BuildSmPropertyList");
+
+ if( ! pSmProps )
+ {
+ nSmProps = 5;
+ nSmDel = 1;
+ pSmProps = new SmProp[ nSmProps ];
+ ppSmProps = new SmProp*[ nSmProps ];
+ ppSmDel = new char*[ nSmDel ];
+ }
+
+ OString aExec(OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding()));
+
+ pSmProps[ eCloneCommand ].name = const_cast<char*>(SmCloneCommand);
+ pSmProps[ eCloneCommand ].type = const_cast<char*>(SmLISTofARRAY8);
+ pSmProps[ eCloneCommand ].num_vals = 1;
+ pSmProps[ eCloneCommand ].vals = new SmPropValue;
+ pSmProps[ eCloneCommand ].vals->length = aExec.getLength()+1;
+ pSmProps[ eCloneCommand ].vals->value = strdup( aExec.getStr() );
+
+ pSmProps[ eProgram ].name = const_cast<char*>(SmProgram);
+ pSmProps[ eProgram ].type = const_cast<char*>(SmARRAY8);
+ pSmProps[ eProgram ].num_vals = 1;
+ pSmProps[ eProgram ].vals = new SmPropValue;
+ pSmProps[ eProgram ].vals->length = aExec.getLength()+1;
+ pSmProps[ eProgram ].vals->value = strdup( aExec.getStr() );
+
+ pSmProps[ eRestartCommand ].name = const_cast<char*>(SmRestartCommand);
+ pSmProps[ eRestartCommand ].type = const_cast<char*>(SmLISTofARRAY8);
+ pSmProps[ eRestartCommand ].num_vals = 3;
+ pSmProps[ eRestartCommand ].vals = new SmPropValue[3];
+ pSmProps[ eRestartCommand ].vals[0].length = aExec.getLength()+1;
+ pSmProps[ eRestartCommand ].vals[0].value = strdup( aExec.getStr() );
+ OString aRestartOption = "--session=" + SessionManagerClient::getSessionID();
+ pSmProps[ eRestartCommand ].vals[1].length = aRestartOption.getLength()+1;
+ pSmProps[ eRestartCommand ].vals[1].value = strdup(aRestartOption.getStr());
+ OString aRestartOptionNoLogo("--nologo"_ostr);
+ pSmProps[ eRestartCommand ].vals[2].length = aRestartOptionNoLogo.getLength()+1;
+ pSmProps[ eRestartCommand ].vals[2].value = strdup(aRestartOptionNoLogo.getStr());
+
+ OUString aUserName;
+ OString aUser;
+ oslSecurity aSec = osl_getCurrentSecurity();
+ if( aSec )
+ {
+ osl_getUserName( aSec, &aUserName.pData );
+ aUser = OUStringToOString( aUserName, osl_getThreadTextEncoding() );
+ osl_freeSecurityHandle( aSec );
+ }
+
+ pSmProps[ eUserId ].name = const_cast<char*>(SmUserID);
+ pSmProps[ eUserId ].type = const_cast<char*>(SmARRAY8);
+ pSmProps[ eUserId ].num_vals = 1;
+ pSmProps[ eUserId ].vals = new SmPropValue;
+ pSmProps[ eUserId ].vals->value = strdup( aUser.getStr() );
+ pSmProps[ eUserId ].vals->length = rtl_str_getLength( static_cast<char *>(pSmProps[ 3 ].vals->value) )+1;
+
+ pSmProps[ eRestartStyleHint ].name = const_cast<char*>(SmRestartStyleHint);
+ pSmProps[ eRestartStyleHint ].type = const_cast<char*>(SmCARD8);
+ pSmProps[ eRestartStyleHint ].num_vals = 1;
+ pSmProps[ eRestartStyleHint ].vals = new SmPropValue;
+ pSmProps[ eRestartStyleHint ].vals->value = malloc(1);
+ pSmRestartHint = static_cast<unsigned char *>(pSmProps[ 4 ].vals->value);
+ *pSmRestartHint = SmRestartIfRunning;
+ pSmProps[ eRestartStyleHint ].vals->length = 1;
+
+ for( int i = 0; i < nSmProps; i++ )
+ ppSmProps[ i ] = &pSmProps[i];
+
+ ppSmDel[eDiscardCommand] = const_cast<char*>(SmDiscardCommand);
+}
+
+bool SessionManagerClient::checkDocumentsSaved()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::checkDocumentsSaved");
+
+ SAL_INFO("vcl.sm.debug", " m_bcheckDocumentsSaved = " << (m_bDocSaveDone ? "true" : "false" ));
+ return m_bDocSaveDone;
+}
+
+IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, pStateVal, void )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient, SaveYourselfHdl");
+
+ // Decode argument smuggled in as void*:
+ sal_uIntPtr nStateVal = reinterpret_cast< sal_uIntPtr >(pStateVal);
+ bool shutdown = nStateVal != 0;
+
+ static bool bFirstShutdown=true;
+
+ SAL_INFO("vcl.sm.debug", " shutdown = " << (shutdown ? "true" : "false" ) <<
+ ", bFirstShutdown = " << (bFirstShutdown ? "true" : "false" ));
+ if (shutdown && bFirstShutdown) //first shutdown request
+ {
+ bFirstShutdown = false;
+ /*
+ If we have no actual frames open, e.g. we launched a quickstarter,
+ and then shutdown all our frames leaving just a quickstarter running,
+ then we don't want to launch an empty toplevel frame on the next
+ start. (The job of scheduling the restart of the quick-starter is a
+ task of the quick-starter)
+ */
+ *pSmRestartHint = SmRestartNever;
+ for (auto pSalFrame : vcl_sal::getSalDisplay(GetGenericUnixSalData())->getFrames() )
+ {
+ vcl::Window *pWindow = pSalFrame->GetWindow();
+ if (pWindow && pWindow->IsVisible())
+ {
+ *pSmRestartHint = SmRestartIfRunning;
+ SAL_INFO("vcl.sm.debug", " pSmRestartHint = SmRestartIfRunning");
+ break;
+ }
+ }
+ }
+
+ if( m_pSession )
+ {
+ SalSessionSaveRequestEvent aEvent( shutdown );
+ m_pSession->CallCallback( &aEvent );
+ }
+ else
+ saveDone();
+}
+
+IMPL_STATIC_LINK_NOARG( SessionManagerClient, InteractionHdl, void*, void )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient, InteractionHdl");
+
+ if( m_pSession )
+ {
+ SalSessionInteractionEvent aEvent( true );
+ m_pSession->CallCallback( &aEvent );
+ }
+}
+
+IMPL_STATIC_LINK_NOARG( SessionManagerClient, ShutDownCancelHdl, void*, void )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient, ShutDownCancelHdl");
+
+ if( m_pSession )
+ {
+ SalSessionShutdownCancelEvent aEvent;
+ m_pSession->CallCallback( &aEvent );
+ }
+}
+
+void SessionManagerClient::SaveYourselfProc(
+ SmcConn,
+ SmPointer,
+ int save_type,
+ Bool shutdown,
+ int interact_style,
+ Bool
+ )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::SaveYourselfProc");
+
+ TimeValue now;
+ osl_getSystemTime(&now);
+
+ SAL_INFO("vcl.sm", " save_type = " << ((save_type == SmSaveLocal ) ? "local" :
+ (save_type == SmSaveGlobal) ? "global" : "both") <<
+ ", shutdown = " << (shutdown ? "true" : "false" ) <<
+ ", interact_style = " << ((interact_style == SmInteractStyleNone) ? "SmInteractStyleNone" :
+ (interact_style == SmInteractStyleErrors) ? "SmInteractStyleErrors" :
+ "SmInteractStyleAny"));
+ char num[100];
+ snprintf(num, sizeof(num), "_%" SAL_PRIuUINT32 "_%" SAL_PRIuUINT32, now.Seconds, (now.Nanosec / 1001));
+ m_aTimeID = OString(num);
+
+ BuildSmPropertyList();
+
+ SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eProgram ] );
+ SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eUserId ] );
+
+
+ m_bDocSaveDone = false;
+ /* #i49875# some session managers send a "die" message if the
+ * saveDone does not come early enough for their convenience
+ * this can occasionally happen on startup, especially the first
+ * startup. So shortcut the "not shutting down" case since the
+ * upper layers are currently not interested in that event anyway.
+ */
+ if( ! shutdown )
+ {
+ SessionManagerClient::saveDone();
+ return;
+ }
+ // Smuggle argument in as void*:
+ sal_uIntPtr nStateVal = shutdown;
+ Application::PostUserEvent( LINK( nullptr, SessionManagerClient, SaveYourselfHdl ), reinterpret_cast< void * >(nStateVal) );
+}
+
+IMPL_STATIC_LINK_NOARG( SessionManagerClient, ShutDownHdl, void*, void )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient, ShutDownHdl");
+
+ if( m_pSession )
+ {
+ SalSessionQuitEvent aEvent;
+ m_pSession->CallCallback( &aEvent );
+ }
+
+ SalFrame *pAnyFrame = vcl_sal::getSalDisplay(GetGenericUnixSalData())->anyFrame();
+ SAL_INFO("vcl.sm.debug", " rFrames.empty() = " << (pAnyFrame ? "true" : "false"));
+ if( pAnyFrame )
+ pAnyFrame->CallCallback( SalEvent::Shutdown, nullptr );
+}
+
+void SessionManagerClient::DieProc(
+ SmcConn connection,
+ SmPointer
+ )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::DieProc");
+
+ if( connection == m_pSmcConnection )
+ {
+ SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection" );
+ Application::PostUserEvent( LINK( nullptr, SessionManagerClient, ShutDownHdl ) );
+ }
+}
+
+void SessionManagerClient::SaveCompleteProc(
+ SmcConn,
+ SmPointer
+ )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::SaveCompleteProc");
+}
+
+void SessionManagerClient::ShutdownCanceledProc(
+ SmcConn connection,
+ SmPointer )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::ShutdownCanceledProc" );
+
+ SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection = " << (( connection == m_pSmcConnection ) ? "true" : "false"));
+ if( connection == m_pSmcConnection )
+ Application::PostUserEvent( LINK( nullptr, SessionManagerClient, ShutDownCancelHdl ) );
+}
+
+void SessionManagerClient::InteractProc(
+ SmcConn connection,
+ SmPointer )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::InteractProc" );
+
+ SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection = " << (( connection == m_pSmcConnection ) ? "true" : "false"));
+ if( connection == m_pSmcConnection )
+ Application::PostUserEvent( LINK( nullptr, SessionManagerClient, InteractionHdl ) );
+}
+
+void SessionManagerClient::saveDone()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::saveDone");
+
+ if( !m_pSmcConnection )
+ return;
+
+ assert(m_xICEConnectionObserver);
+ osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
+ //SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eCloneCommand ] );
+ // this message-handling is now equal to kate and plasma desktop
+ SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eRestartCommand ] );
+ SmcDeleteProperties( m_pSmcConnection, 1, &ppSmDel[ eDiscardCommand ] );
+ SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eRestartStyleHint ] );
+
+ SmcSaveYourselfDone( m_pSmcConnection, True );
+ SAL_INFO("vcl.sm.debug", " sent SmRestartHint = " << (*pSmRestartHint) );
+ m_bDocSaveDone = true;
+}
+
+void SessionManagerClient::open(SalSession * pSession)
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::open");
+
+ assert(!m_pSession && !m_xICEConnectionObserver && !m_pSmcConnection);
+ // must only be called once
+ m_pSession = pSession;
+ // This is the way Xt does it, so we can too:
+ if( getenv( "SESSION_MANAGER" ) )
+ {
+ SAL_INFO("vcl.sm.debug", " getenv( SESSION_MANAGER ) = true");
+ m_xICEConnectionObserver.reset(new ICEConnectionObserver);
+ m_xICEConnectionObserver->activate();
+
+ {
+ osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
+
+ static SmcCallbacks aCallbacks; // does this need to be static?
+ aCallbacks.save_yourself.callback = SaveYourselfProc;
+ aCallbacks.save_yourself.client_data = nullptr;
+ aCallbacks.die.callback = DieProc;
+ aCallbacks.die.client_data = nullptr;
+ aCallbacks.save_complete.callback = SaveCompleteProc;
+ aCallbacks.save_complete.client_data = nullptr;
+ aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc;
+ aCallbacks.shutdown_cancelled.client_data = nullptr;
+ OString aPrevId(getPreviousSessionID());
+ char* pClientID = nullptr;
+ char aErrBuf[1024];
+ m_pSmcConnection = SmcOpenConnection( nullptr,
+ nullptr,
+ SmProtoMajor,
+ SmProtoMinor,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask ,
+ &aCallbacks,
+ aPrevId.isEmpty() ? nullptr : const_cast<char*>(aPrevId.getStr()),
+ &pClientID,
+ sizeof( aErrBuf ),
+ aErrBuf );
+ if( !m_pSmcConnection )
+ SAL_INFO("vcl.sm.debug", " SmcOpenConnection failed: " << aErrBuf);
+ else
+ SAL_INFO("vcl.sm.debug", " SmcOpenConnection succeeded, client ID is " << pClientID );
+ m_aClientID = OString(pClientID);
+ free( pClientID );
+ pClientID = nullptr;
+ }
+
+ SalDisplay* pDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ if( pDisp->GetDrawable(pDisp->GetDefaultXScreen()) && !m_aClientID.isEmpty() )
+ {
+ SAL_INFO("vcl.sm.debug", " SmcOpenConnection open: pDisp->GetDrawable = true");
+ XChangeProperty( pDisp->GetDisplay(),
+ pDisp->GetDrawable( pDisp->GetDefaultXScreen() ),
+ XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ),
+ XA_STRING,
+ 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char const *>(m_aClientID.getStr()),
+ m_aClientID.getLength()
+ );
+ }
+ }
+ else
+ {
+ SAL_INFO("vcl.sm.debug", " getenv( SESSION_MANAGER ) = false");
+ }
+}
+
+const OString& SessionManagerClient::getSessionID()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::getSessionID");
+
+ m_aClientTimeID = m_aClientID + m_aTimeID;
+
+ SAL_INFO("vcl.sm", " SessionID = " << m_aClientTimeID);
+
+ return m_aClientTimeID;
+}
+
+void SessionManagerClient::close()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::close");
+
+ if( !m_pSmcConnection )
+ return;
+
+ SAL_INFO("vcl.sm.debug", " attempting SmcCloseConnection");
+ assert(m_xICEConnectionObserver);
+ {
+ osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
+ SmcCloseConnection( m_pSmcConnection, 0, nullptr );
+ SAL_INFO("vcl.sm", " SmcCloseConnection closed");
+ }
+ m_xICEConnectionObserver->deactivate();
+ m_xICEConnectionObserver.reset();
+ m_pSmcConnection = nullptr;
+}
+
+bool SessionManagerClient::queryInteraction()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::queryInteraction");
+
+ bool bRet = false;
+ if( m_pSmcConnection )
+ {
+ assert(m_xICEConnectionObserver);
+ osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
+ SAL_INFO("vcl.sm.debug", " SmcInteractRequest" );
+ if( SmcInteractRequest( m_pSmcConnection, SmDialogNormal, InteractProc, nullptr ) )
+ bRet = true;
+ }
+ return bRet;
+}
+
+void SessionManagerClient::interactionDone( bool bCancelShutdown )
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::interactionDone");
+
+ if( m_pSmcConnection )
+ {
+ assert(m_xICEConnectionObserver);
+ osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
+ SAL_INFO("vcl.sm.debug", " SmcInteractDone = " << (bCancelShutdown ? "true" : "false") );
+ SmcInteractDone( m_pSmcConnection, bCancelShutdown ? True : False );
+ }
+}
+
+OUString SessionManagerClient::getExecName()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::getExecName");
+
+ OUString aExec, aSysExec;
+ osl_getExecutableFile( &aExec.pData );
+ osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData );
+
+ if( aSysExec.endsWith(".bin") )
+ aSysExec = aSysExec.copy( 0, aSysExec.getLength() - RTL_CONSTASCII_LENGTH(".bin") );
+
+ SAL_INFO("vcl.sm.debug", " aSysExec = " << aSysExec);
+ return aSysExec;
+}
+
+OString SessionManagerClient::getPreviousSessionID()
+{
+ SAL_INFO("vcl.sm", "SessionManagerClient::getPreviousSessionID");
+
+ OString aPrevId;
+
+ sal_uInt32 n = rtl_getAppCommandArgCount();
+ for (sal_uInt32 i = 0; i != n; ++i)
+ {
+ OUString aArg;
+ rtl_getAppCommandArg( i, &aArg.pData );
+ if(aArg.match("--session="))
+ {
+ aPrevId = OUStringToOString(
+ aArg.subView(RTL_CONSTASCII_LENGTH("--session=")),
+ osl_getThreadTextEncoding());
+ break;
+ }
+ }
+
+ SAL_INFO("vcl.sm.debug", " previous ID = " << aPrevId);
+ return aPrevId;
+}
+
+void ICEConnectionObserver::activate()
+{
+ SAL_INFO("vcl.sm", "ICEConnectionObserver::activate");
+
+ /*
+ * Default handlers call exit, we don't care that strongly if something
+ * happens to fail
+ */
+ m_origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors );
+ m_origErrorHandler = IceSetErrorHandler( IgnoreIceErrors );
+ IceAddConnectionWatch( ICEWatchProc, this );
+}
+
+void ICEConnectionObserver::deactivate()
+{
+ SAL_INFO("vcl.sm", "ICEConnectionObserver::deactivate");
+
+ oslThread t;
+ {
+ osl::MutexGuard g(m_ICEMutex);
+ IceRemoveConnectionWatch( ICEWatchProc, this );
+ IceSetErrorHandler( m_origErrorHandler );
+ IceSetIOErrorHandler( m_origIOErrorHandler );
+ m_nConnections = 0;
+ t = m_ICEThread;
+ m_ICEThread = nullptr;
+ }
+ if (t)
+ {
+ SAL_INFO("vcl.sm.debug", " terminate");
+ terminate(t);
+ }
+}
+
+void ICEConnectionObserver::wakeup()
+{
+ SAL_INFO("vcl.sm", "ICEConnectionObserver::wakeup");
+
+ char cChar = 'w';
+ OSL_VERIFY(write(m_nWakeupFiles[1], &cChar, 1) == 1);
+}
+
+void ICEConnectionObserver::terminate(oslThread iceThread)
+{
+ SAL_INFO("vcl.sm", "ICEConnectionObserver::terminate");
+
+ osl_terminateThread(iceThread);
+ wakeup();
+ osl_joinWithThread(iceThread);
+ osl_destroyThread(iceThread);
+ close(m_nWakeupFiles[1]);
+ close(m_nWakeupFiles[0]);
+}
+
+void ICEConnectionWorker(void * data)
+{
+ SAL_INFO("vcl.sm", "ICEConnectionWorker");
+
+ osl::Thread::setName("ICEConnectionWorker");
+ ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
+ data);
+ for (;;)
+ {
+ oslThread t;
+ {
+ osl::MutexGuard g(pThis->m_ICEMutex);
+ if (pThis->m_ICEThread == nullptr || pThis->m_nConnections == 0)
+ {
+ break;
+ }
+ t = pThis->m_ICEThread;
+ }
+ if (!osl_scheduleThread(t))
+ {
+ break;
+ }
+
+ int nConnectionsBefore;
+ struct pollfd* pLocalFD;
+ {
+ osl::MutexGuard g(pThis->m_ICEMutex);
+ nConnectionsBefore = pThis->m_nConnections;
+ int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1);
+ pLocalFD = static_cast<struct pollfd*>(std::malloc( nBytes ));
+ memcpy( pLocalFD, pThis->m_pFilehandles, nBytes );
+ }
+
+ int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 );
+ bool bWakeup = (pLocalFD[0].revents & POLLIN);
+ std::free( pLocalFD );
+
+ if( nRet < 1 )
+ continue;
+
+ // clear wakeup pipe
+ if( bWakeup )
+ {
+ char buf[4];
+ while( read( pThis->m_nWakeupFiles[0], buf, sizeof( buf ) ) > 0 )
+ ;
+ SAL_INFO("vcl.sm.debug", " file handles active in wakeup: " << nRet);
+ if( nRet == 1 )
+ continue;
+ }
+
+ // check fd's after we obtained the lock
+ osl::MutexGuard g(pThis->m_ICEMutex);
+ if( pThis->m_nConnections > 0 && pThis->m_nConnections == nConnectionsBefore )
+ {
+ nRet = poll( pThis->m_pFilehandles+1, pThis->m_nConnections, 0 );
+ if( nRet > 0 )
+ {
+ SAL_INFO("vcl.sm.debug", " IceProcessMessages");
+ Bool bReply;
+ for( int i = 0; i < pThis->m_nConnections; i++ )
+ if( pThis->m_pFilehandles[i+1].revents & POLLIN )
+ IceProcessMessages( pThis->m_pConnections[i], nullptr, &bReply );
+ }
+ }
+ }
+
+ SAL_INFO("vcl.sm.debug", " shutting down ICE dispatch thread");
+}
+
+void ICEWatchProc(
+ IceConn ice_conn, IcePointer client_data, Bool opening,
+ SAL_UNUSED_PARAMETER IcePointer *)
+{
+ SAL_INFO("vcl.sm", "ICEWatchProc");
+
+ // Note: This is a callback function for ICE; this implicitly means that a
+ // call into ICE lib is calling this, so the m_ICEMutex MUST already be
+ // locked by the caller.
+ ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
+ client_data);
+ if( opening )
+ {
+ SAL_INFO("vcl.sm.debug", " opening");
+ int fd = IceConnectionNumber( ice_conn );
+ pThis->m_nConnections++;
+ pThis->m_pConnections = static_cast<IceConn*>(std::realloc( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections ));
+ pThis->m_pFilehandles = static_cast<struct pollfd*>(std::realloc( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) ));
+ pThis->m_pConnections[ pThis->m_nConnections-1 ] = ice_conn;
+ pThis->m_pFilehandles[ pThis->m_nConnections ].fd = fd;
+ pThis->m_pFilehandles[ pThis->m_nConnections ].events = POLLIN;
+ if( pThis->m_nConnections == 1 )
+ {
+ SAL_INFO("vcl.sm.debug", " First connection");
+ if (!pipe(pThis->m_nWakeupFiles))
+ {
+ int flags;
+ pThis->m_pFilehandles[0].fd = pThis->m_nWakeupFiles[0];
+ pThis->m_pFilehandles[0].events = POLLIN;
+ // set close-on-exec and nonblock descriptor flag.
+ if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFD)) != -1)
+ {
+ flags |= FD_CLOEXEC;
+ (void)fcntl(pThis->m_nWakeupFiles[0], F_SETFD, flags);
+ }
+ if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFL)) != -1)
+ {
+ flags |= O_NONBLOCK;
+ (void)fcntl(pThis->m_nWakeupFiles[0], F_SETFL, flags);
+ }
+ // set close-on-exec and nonblock descriptor flag.
+ if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFD)) != -1)
+ {
+ flags |= FD_CLOEXEC;
+ (void)fcntl(pThis->m_nWakeupFiles[1], F_SETFD, flags);
+ }
+ if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFL)) != -1)
+ {
+ flags |= O_NONBLOCK;
+ (void)fcntl(pThis->m_nWakeupFiles[1], F_SETFL, flags);
+ }
+ pThis->m_ICEThread = osl_createThread(
+ ICEConnectionWorker, pThis);
+ }
+ }
+ }
+ else // closing
+ {
+ SAL_INFO("vcl.sm.debug", " closing");
+ for( int i = 0; i < pThis->m_nConnections; i++ )
+ {
+ if( pThis->m_pConnections[i] == ice_conn )
+ {
+ if( i < pThis->m_nConnections-1 )
+ {
+ memmove( pThis->m_pConnections+i, pThis->m_pConnections+i+1, sizeof( IceConn )*(pThis->m_nConnections-i-1) );
+ memmove( pThis->m_pFilehandles+i+1, pThis->m_pFilehandles+i+2, sizeof( struct pollfd )*(pThis->m_nConnections-i-1) );
+ }
+ pThis->m_nConnections--;
+ pThis->m_pConnections = static_cast<IceConn*>(std::realloc( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections ));
+ pThis->m_pFilehandles = static_cast<struct pollfd*>(std::realloc( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) ));
+ break;
+ }
+ }
+ if( pThis->m_nConnections == 0 && pThis->m_ICEThread )
+ {
+ SAL_INFO("vcl.sm.debug", " terminating ICEThread");
+ oslThread t = pThis->m_ICEThread;
+ pThis->m_ICEThread = nullptr;
+
+ // must release the mutex here
+ pThis->m_ICEMutex.release();
+
+ pThis->terminate(t);
+
+ // acquire the mutex again, because the caller does not expect
+ // it to be released when calling into SM
+ pThis->m_ICEMutex.acquire();
+ }
+ }
+
+ SAL_INFO( "vcl.sm.debug", " ICE connection on " << IceConnectionNumber( ice_conn ) );
+ SAL_INFO( "vcl.sm.debug", " Display connection is " << ConnectionNumber( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/app/wmadaptor.cxx b/vcl/unx/generic/app/wmadaptor.cxx
new file mode 100644
index 0000000000..240517d7aa
--- /dev/null
+++ b/vcl/unx/generic/app/wmadaptor.cxx
@@ -0,0 +1,2196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string_view>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/locale.h>
+
+#include <osl/thread.h>
+#include <osl/process.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <comphelper/string.hxx>
+#include <configsettings.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <unx/wmadaptor.hxx>
+#include <unx/saldisp.hxx>
+#include <unx/salframe.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+namespace vcl_sal {
+
+class NetWMAdaptor : public WMAdaptor
+{
+ void setNetWMState( X11SalFrame* pFrame ) const;
+ void initAtoms();
+ virtual bool isValid() const override;
+public:
+ explicit NetWMAdaptor( SalDisplay* );
+
+ virtual void setWMName( X11SalFrame* pFrame, const OUString& rWMName ) const override;
+ virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const override;
+ virtual void setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pTransientFrame ) const override;
+ virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const override;
+ virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const override;
+ virtual void showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const override;
+ virtual void frameIsMapping( X11SalFrame* pFrame ) const override;
+ virtual void setUserTime( X11SalFrame* i_pFrame, tools::Long i_nUserTime ) const override;
+};
+
+class GnomeWMAdaptor : public WMAdaptor
+{
+ bool m_bValid;
+
+ void setGnomeWMState( X11SalFrame* pFrame ) const;
+ void initAtoms();
+ virtual bool isValid() const override;
+public:
+ explicit GnomeWMAdaptor( SalDisplay * );
+
+ virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const override;
+ virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const override;
+ virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const override;
+};
+
+}
+
+using namespace vcl_sal;
+
+namespace {
+
+struct WMAdaptorProtocol
+{
+ const char* pProtocol;
+ int nProtocol;
+};
+
+}
+
+/*
+ * table must be sorted ascending in strings
+ * since it is use with bsearch
+ */
+const WMAdaptorProtocol aProtocolTab[] =
+{
+ { "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", WMAdaptor::KDE_NET_WM_WINDOW_TYPE_OVERRIDE },
+ { "_NET_ACTIVE_WINDOW", WMAdaptor::NET_ACTIVE_WINDOW },
+ { "_NET_CURRENT_DESKTOP", WMAdaptor::NET_CURRENT_DESKTOP },
+ { "_NET_NUMBER_OF_DESKTOPS", WMAdaptor::NET_NUMBER_OF_DESKTOPS },
+ { "_NET_WM_DESKTOP", WMAdaptor::NET_WM_DESKTOP },
+ { "_NET_WM_ICON", WMAdaptor::NET_WM_ICON },
+ { "_NET_WM_ICON_NAME", WMAdaptor::NET_WM_ICON_NAME },
+ { "_NET_WM_PING", WMAdaptor::NET_WM_PING },
+ { "_NET_WM_STATE", WMAdaptor::NET_WM_STATE },
+ { "_NET_WM_STATE_ABOVE", WMAdaptor::NET_WM_STATE_STAYS_ON_TOP },
+ { "_NET_WM_STATE_FULLSCREEN", WMAdaptor::NET_WM_STATE_FULLSCREEN },
+ { "_NET_WM_STATE_MAXIMIZED_HORIZ", WMAdaptor::NET_WM_STATE_MAXIMIZED_HORZ }, // common bug in e.g. older kwin and sawfish implementations
+ { "_NET_WM_STATE_MAXIMIZED_HORZ", WMAdaptor::NET_WM_STATE_MAXIMIZED_HORZ },
+ { "_NET_WM_STATE_MAXIMIZED_VERT", WMAdaptor::NET_WM_STATE_MAXIMIZED_VERT },
+ { "_NET_WM_STATE_MODAL", WMAdaptor::NET_WM_STATE_MODAL },
+ { "_NET_WM_STATE_SKIP_PAGER", WMAdaptor::NET_WM_STATE_SKIP_PAGER },
+ { "_NET_WM_STATE_SKIP_TASKBAR", WMAdaptor::NET_WM_STATE_SKIP_TASKBAR },
+ { "_NET_WM_STATE_STAYS_ON_TOP", WMAdaptor::NET_WM_STATE_STAYS_ON_TOP },
+ { "_NET_WM_STATE_STICKY", WMAdaptor::NET_WM_STATE_STICKY },
+ { "_NET_WM_STRUT", WMAdaptor::NET_WM_STRUT },
+ { "_NET_WM_STRUT_PARTIAL", WMAdaptor::NET_WM_STRUT_PARTIAL },
+ { "_NET_WM_WINDOW_TYPE", WMAdaptor::NET_WM_WINDOW_TYPE },
+ { "_NET_WM_WINDOW_TYPE_DESKTOP", WMAdaptor::NET_WM_WINDOW_TYPE_DESKTOP },
+ { "_NET_WM_WINDOW_TYPE_DIALOG", WMAdaptor::NET_WM_WINDOW_TYPE_DIALOG },
+ { "_NET_WM_WINDOW_TYPE_DOCK", WMAdaptor::NET_WM_WINDOW_TYPE_DOCK },
+ { "_NET_WM_WINDOW_TYPE_MENU", WMAdaptor::NET_WM_WINDOW_TYPE_MENU },
+ { "_NET_WM_WINDOW_TYPE_NORMAL", WMAdaptor::NET_WM_WINDOW_TYPE_NORMAL },
+ { "_NET_WM_WINDOW_TYPE_SPLASH", WMAdaptor::NET_WM_WINDOW_TYPE_SPLASH },
+ { "_NET_WM_WINDOW_TYPE_SPLASHSCREEN", WMAdaptor::NET_WM_WINDOW_TYPE_SPLASH }, // bug in Metacity 2.4.1
+ { "_NET_WM_WINDOW_TYPE_TOOLBAR", WMAdaptor::NET_WM_WINDOW_TYPE_TOOLBAR },
+ { "_NET_WM_WINDOW_TYPE_UTILITY", WMAdaptor::NET_WM_WINDOW_TYPE_UTILITY },
+ { "_NET_WORKAREA", WMAdaptor::NET_WORKAREA },
+ { "_WIN_APP_STATE", WMAdaptor::WIN_APP_STATE },
+ { "_WIN_CLIENT_LIST", WMAdaptor::WIN_CLIENT_LIST },
+ { "_WIN_EXPANDED_SIZE", WMAdaptor::WIN_EXPANDED_SIZE },
+ { "_WIN_HINTS", WMAdaptor::WIN_HINTS },
+ { "_WIN_ICONS", WMAdaptor::WIN_ICONS },
+ { "_WIN_LAYER", WMAdaptor::WIN_LAYER },
+ { "_WIN_STATE", WMAdaptor::WIN_STATE },
+ { "_WIN_WORKSPACE", WMAdaptor::WIN_WORKSPACE },
+ { "_WIN_WORKSPACE_COUNT", WMAdaptor::WIN_WORKSPACE_COUNT }
+};
+
+/*
+ * table containing atoms to get anyway
+ */
+
+const WMAdaptorProtocol aAtomTab[] =
+{
+ { "WM_STATE", WMAdaptor::WM_STATE },
+ { "_MOTIF_WM_HINTS", WMAdaptor::MOTIF_WM_HINTS },
+ { "WM_PROTOCOLS", WMAdaptor::WM_PROTOCOLS },
+ { "WM_DELETE_WINDOW", WMAdaptor::WM_DELETE_WINDOW },
+ { "WM_TAKE_FOCUS", WMAdaptor::WM_TAKE_FOCUS },
+ { "WM_COMMAND", WMAdaptor::WM_COMMAND },
+ { "WM_CLIENT_LEADER", WMAdaptor::WM_CLIENT_LEADER },
+ { "WM_LOCALE_NAME", WMAdaptor::WM_LOCALE_NAME },
+ { "WM_TRANSIENT_FOR", WMAdaptor::WM_TRANSIENT_FOR },
+ { "SAL_QUITEVENT", WMAdaptor::SAL_QUITEVENT },
+ { "SAL_USEREVENT", WMAdaptor::SAL_USEREVENT },
+ { "SAL_EXTTEXTEVENT", WMAdaptor::SAL_EXTTEXTEVENT },
+ { "SAL_GETTIMEEVENT", WMAdaptor::SAL_GETTIMEEVENT },
+ { "VCL_SYSTEM_SETTINGS", WMAdaptor::VCL_SYSTEM_SETTINGS },
+ { "_XSETTINGS_SETTINGS", WMAdaptor::XSETTINGS },
+ { "_XEMBED", WMAdaptor::XEMBED },
+ { "_XEMBED_INFO", WMAdaptor::XEMBED_INFO },
+ { "_NET_WM_USER_TIME", WMAdaptor::NET_WM_USER_TIME },
+ { "_NET_WM_PID", WMAdaptor::NET_WM_PID }
+};
+
+extern "C" {
+static int compareProtocol( const void* pLeft, const void* pRight )
+{
+ return strcmp( static_cast<const WMAdaptorProtocol*>(pLeft)->pProtocol, static_cast<const WMAdaptorProtocol*>(pRight)->pProtocol );
+}
+}
+
+std::unique_ptr<WMAdaptor> WMAdaptor::createWMAdaptor( SalDisplay* pSalDisplay )
+{
+ std::unique_ptr<WMAdaptor> pAdaptor;
+
+ // try a NetWM
+ pAdaptor.reset(new NetWMAdaptor( pSalDisplay ));
+ if( ! pAdaptor->isValid() )
+ {
+ pAdaptor.reset();
+ }
+#if OSL_DEBUG_LEVEL > 1
+ else
+ SAL_INFO("vcl.app", "WM supports extended WM hints.");
+#endif
+
+ // try a GnomeWM
+ if( ! pAdaptor )
+ {
+ pAdaptor.reset(new GnomeWMAdaptor( pSalDisplay ));
+ if( ! pAdaptor->isValid() )
+ {
+ pAdaptor.reset();
+ }
+#if OSL_DEBUG_LEVEL > 1
+ else
+ SAL_INFO("vcl.app", "WM supports GNOME WM hints.");
+#endif
+ }
+
+ if( ! pAdaptor )
+ pAdaptor.reset(new WMAdaptor( pSalDisplay ));
+
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "Window Manager's name is \""
+ << pAdaptor->getWindowManagerName()
+ << "\".");
+#endif
+ return pAdaptor;
+}
+
+/*
+ * WMAdaptor constructor
+ */
+
+WMAdaptor::WMAdaptor( SalDisplay* pDisplay ) :
+ m_pSalDisplay( pDisplay ),
+ m_bEnableAlwaysOnTopWorks( false ),
+ m_bLegacyPartialFullscreen( false ),
+ m_nWinGravity( StaticGravity ),
+ m_nInitWinGravity( StaticGravity ),
+ m_bWMshouldSwitchWorkspace( true ),
+ m_bWMshouldSwitchWorkspaceInit( false )
+{
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+
+ // default desktops
+ m_nDesktops = 1;
+ m_aWMWorkAreas = ::std::vector< AbsoluteScreenPixelRectangle >
+ ( 1, AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint(), m_pSalDisplay->GetScreenSize( m_pSalDisplay->GetDefaultXScreen() ) ) );
+ m_bEqualWorkAreas = true;
+
+ memset( m_aWMAtoms, 0, sizeof( m_aWMAtoms ) );
+ m_pDisplay = m_pSalDisplay->GetDisplay();
+
+ initAtoms();
+ getNetWmName(); // try to discover e.g. Sawfish
+
+ if( m_aWMName.isEmpty() )
+ {
+ // check for ReflectionX wm (as it needs a workaround in Windows mode
+ Atom aRwmRunning = XInternAtom( m_pDisplay, "RWM_RUNNING", True );
+ if( aRwmRunning != None &&
+ XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ aRwmRunning,
+ 0, 32,
+ False,
+ aRwmRunning,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0 )
+ {
+ if( aRealType == aRwmRunning )
+ m_aWMName = "ReflectionX";
+ XFree( pProperty );
+ }
+ else
+ {
+ aRwmRunning = XInternAtom( m_pDisplay, "_WRQ_WM_RUNNING", True );
+ if( aRwmRunning != None &&
+ XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ aRwmRunning,
+ 0, 32,
+ False,
+ XA_STRING,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0 )
+ {
+ if( aRealType == XA_STRING )
+ m_aWMName = "ReflectionX Windows";
+ XFree( pProperty );
+ }
+ }
+ }
+ if( !m_aWMName.isEmpty() )
+ return;
+
+ Atom aTTAPlatform = XInternAtom( m_pDisplay, "TTA_CLIENT_PLATFORM", True );
+ if( aTTAPlatform == None ||
+ XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ aTTAPlatform,
+ 0, 32,
+ False,
+ XA_STRING,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) != 0 )
+ return;
+
+ if( aRealType == XA_STRING )
+ {
+ m_aWMName = "Tarantella";
+ // #i62319# pretend that AlwaysOnTop works since
+ // the alwaysontop workaround in salframe.cxx results
+ // in a raise/lower loop on a Windows tarantella client
+ // FIXME: this property contains an identification string that
+ // in theory should be good enough to recognize running on a
+ // Windows client; however this string does not seem to be
+ // documented as well as the property itself.
+ m_bEnableAlwaysOnTopWorks = true;
+ }
+ XFree( pProperty );
+}
+
+/*
+ * WMAdaptor destructor
+ */
+
+WMAdaptor::~WMAdaptor()
+{
+}
+
+/*
+ * NetWMAdaptor constructor
+ */
+
+NetWMAdaptor::NetWMAdaptor( SalDisplay* pSalDisplay ) :
+ WMAdaptor( pSalDisplay )
+{
+ // currently all _NET WMs do transient like expected
+
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+
+ initAtoms();
+
+ // check for NetWM
+ bool bNetWM = getNetWmName();
+ if( bNetWM
+ && XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_SUPPORTED ],
+ 0, 0,
+ False,
+ XA_ATOM,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_ATOM
+ && nFormat == 32
+ )
+ {
+ if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ // collect supported protocols
+ if( XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_SUPPORTED ],
+ 0, nBytesLeft/4,
+ False,
+ XA_ATOM,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && nItems
+ )
+ {
+ Atom* pAtoms = reinterpret_cast<Atom*>(pProperty);
+ char** pAtomNames = static_cast<char**>(alloca( sizeof(char*)*nItems ));
+ if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "supported protocols:");
+#endif
+ for( unsigned long i = 0; i < nItems; i++ )
+ {
+ // #i80971# protect against invalid atoms
+ if( pAtomNames[i] == nullptr )
+ continue;
+
+ WMAdaptorProtocol aSearch;
+ aSearch.pProtocol = pAtomNames[i];
+ WMAdaptorProtocol* pMatch = static_cast<WMAdaptorProtocol*>(
+ bsearch( &aSearch,
+ aProtocolTab,
+ SAL_N_ELEMENTS( aProtocolTab ),
+ sizeof( struct WMAdaptorProtocol ),
+ compareProtocol ));
+ if( pMatch )
+ {
+ m_aWMAtoms[ pMatch->nProtocol ] = pAtoms[ i ];
+ if( pMatch->nProtocol == NET_WM_STATE_STAYS_ON_TOP )
+ m_bEnableAlwaysOnTopWorks = true;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", " "
+ << pAtomNames[i]
+ << (((pMatch)&&(pMatch->nProtocol != -1)) ?
+ "" : " (unsupported)"));
+#endif
+ XFree( pAtomNames[i] );
+ }
+ }
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+
+ // get number of desktops
+ if( m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ]
+ && XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && pProperty
+ )
+ {
+ m_nDesktops = *reinterpret_cast<long*>(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ // get work areas
+ if( m_aWMAtoms[ NET_WORKAREA ]
+ && XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_WORKAREA ],
+ 0, 4*m_nDesktops,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty
+ ) == 0
+ && nItems == 4*static_cast<unsigned>(m_nDesktops)
+ )
+ {
+ m_aWMWorkAreas = ::std::vector< AbsoluteScreenPixelRectangle > ( m_nDesktops );
+ tools::Long* pValues = reinterpret_cast<long*>(pProperty);
+ for( int i = 0; i < m_nDesktops; i++ )
+ {
+ AbsoluteScreenPixelPoint aPoint( pValues[4*i],
+ pValues[4*i+1] );
+ AbsoluteScreenPixelSize aSize( pValues[4*i+2],
+ pValues[4*i+3] );
+ AbsoluteScreenPixelRectangle aWorkArea( aPoint, aSize );
+ m_aWMWorkAreas[i] = aWorkArea;
+ if( aWorkArea != m_aWMWorkAreas[0] )
+ m_bEqualWorkAreas = false;
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "workarea " << i
+ << ": " << m_aWMWorkAreas[i].GetWidth()
+ << "x" << m_aWMWorkAreas[i].GetHeight()
+ << "+" << m_aWMWorkAreas[i].Left()
+ << "+" << m_aWMWorkAreas[i].Top());
+#endif
+ }
+ XFree( pProperty );
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", nItems/4 << " workareas for "
+ << m_nDesktops << " desktops !");
+#endif
+ if( pProperty )
+ {
+ XFree(pProperty);
+ pProperty = nullptr;
+ }
+ }
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+}
+
+/*
+ * GnomeWMAdaptor constructor
+ */
+
+GnomeWMAdaptor::GnomeWMAdaptor( SalDisplay* pSalDisplay ) :
+ WMAdaptor( pSalDisplay ),
+ m_bValid( false )
+{
+ // currently all Gnome WMs do transient like expected
+
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+
+ initAtoms();
+
+ // check for GnomeWM
+ if( m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ WIN_PROTOCOLS ] )
+ {
+ if( XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_CARDINAL
+ && nFormat == 32
+ && nItems != 0
+ )
+ {
+ ::Window aWMChild = *reinterpret_cast< ::Window* >(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ GetGenericUnixSalData()->ErrorTrapPush();
+ if( XGetWindowProperty( m_pDisplay,
+ aWMChild,
+ m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_CARDINAL
+ && nFormat == 32
+ && nItems != 0 )
+ {
+ if (! GetGenericUnixSalData()->ErrorTrapPop( false ) )
+ {
+ GetGenericUnixSalData()->ErrorTrapPush();
+
+ ::Window aCheckWindow = *reinterpret_cast< ::Window* >(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ if( aCheckWindow == aWMChild )
+ {
+ m_bValid = true;
+ /*
+ * get name of WM
+ * this is NOT part of the GNOME WM hints, but e.g. Sawfish
+ * already supports this part of the extended WM hints
+ */
+ m_aWMAtoms[ UTF8_STRING ] = XInternAtom( m_pDisplay, "UTF8_STRING", False );
+ getNetWmName();
+ }
+ }
+ else
+ GetGenericUnixSalData()->ErrorTrapPush();
+ }
+ GetGenericUnixSalData()->ErrorTrapPop();
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ if( m_bValid
+ && XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ WIN_PROTOCOLS ],
+ 0, 0,
+ False,
+ XA_ATOM,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_ATOM
+ && nFormat == 32
+ )
+ {
+ if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ // collect supported protocols
+ if( XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ WIN_PROTOCOLS ],
+ 0, nBytesLeft/4,
+ False,
+ XA_ATOM,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && pProperty
+ )
+ {
+ Atom* pAtoms = reinterpret_cast<Atom*>(pProperty);
+ char** pAtomNames = static_cast<char**>(alloca( sizeof(char*)*nItems ));
+ if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", "supported protocols:");
+#endif
+ for( unsigned long i = 0; i < nItems; i++ )
+ {
+ // #i80971# protect against invalid atoms
+ if( pAtomNames[i] == nullptr )
+ continue;
+
+ WMAdaptorProtocol aSearch;
+ aSearch.pProtocol = pAtomNames[i];
+ WMAdaptorProtocol* pMatch = static_cast<WMAdaptorProtocol*>(
+ bsearch( &aSearch,
+ aProtocolTab,
+ SAL_N_ELEMENTS( aProtocolTab ),
+ sizeof( struct WMAdaptorProtocol ),
+ compareProtocol ));
+ if( pMatch )
+ {
+ m_aWMAtoms[ pMatch->nProtocol ] = pAtoms[ i ];
+ if( pMatch->nProtocol == WIN_LAYER )
+ m_bEnableAlwaysOnTopWorks = true;
+ }
+ if( strncmp( "_ICEWM_TRAY", pAtomNames[i], 11 ) == 0 )
+ {
+ m_aWMName = "IceWM";
+ m_nWinGravity = NorthWestGravity;
+ m_nInitWinGravity = NorthWestGravity;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ SAL_INFO("vcl.app", " "
+ << pAtomNames[i]
+ << (((pMatch) && (pMatch->nProtocol != -1)) ?
+ "" : " (unsupported)"));
+#endif
+ XFree( pAtomNames[i] );
+ }
+ }
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+
+ // get number of desktops
+ if( m_aWMAtoms[ WIN_WORKSPACE_COUNT ]
+ && XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ WIN_WORKSPACE_COUNT ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && pProperty
+ )
+ {
+ m_nDesktops = *reinterpret_cast<long*>(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+}
+
+/*
+ * getNetWmName()
+ */
+bool WMAdaptor::getNetWmName()
+{
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+ bool bNetWM = false;
+
+ if( m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ NET_WM_NAME ] )
+ {
+ if( XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ],
+ 0, 1,
+ False,
+ XA_WINDOW,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_WINDOW
+ && nFormat == 32
+ && nItems != 0
+ )
+ {
+ ::Window aWMChild = *reinterpret_cast< ::Window* >(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ GetGenericUnixSalData()->ErrorTrapPush();
+ if( XGetWindowProperty( m_pDisplay,
+ aWMChild,
+ m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ],
+ 0, 1,
+ False,
+ XA_WINDOW,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && aRealType == XA_WINDOW
+ && nFormat == 32
+ && nItems != 0 )
+ {
+ if ( ! GetGenericUnixSalData()->ErrorTrapPop( false ) )
+ {
+ GetGenericUnixSalData()->ErrorTrapPush();
+ ::Window aCheckWindow = *reinterpret_cast< ::Window* >(pProperty);
+ XFree( pProperty );
+ pProperty = nullptr;
+ if( aCheckWindow == aWMChild )
+ {
+ bNetWM = true;
+ // get name of WM
+ m_aWMAtoms[ UTF8_STRING ] = XInternAtom( m_pDisplay, "UTF8_STRING", False );
+ if( XGetWindowProperty( m_pDisplay,
+ aWMChild,
+ m_aWMAtoms[ NET_WM_NAME ],
+ 0, 256,
+ False,
+ AnyPropertyType, /* m_aWMAtoms[ UTF8_STRING ],*/
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && nItems != 0
+ )
+ {
+ if (aRealType == m_aWMAtoms[ UTF8_STRING ])
+ m_aWMName = OUString( reinterpret_cast<char*>(pProperty), nItems, RTL_TEXTENCODING_UTF8 );
+ else if (aRealType == XA_STRING)
+ m_aWMName = OUString( reinterpret_cast<char*>(pProperty), nItems, RTL_TEXTENCODING_ISO_8859_1 );
+
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+
+ // if this is metacity, check for version to enable a legacy workaround
+ if( m_aWMName == "Metacity" )
+ {
+ int nVersionMajor = 0, nVersionMinor = 0;
+ Atom nVersionAtom = XInternAtom( m_pDisplay, "_METACITY_VERSION", True );
+ if( nVersionAtom )
+ {
+ if( XGetWindowProperty( m_pDisplay,
+ aWMChild,
+ nVersionAtom,
+ 0, 256,
+ False,
+ m_aWMAtoms[ UTF8_STRING ],
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && nItems != 0
+ )
+ {
+ OUString aMetaVersion( reinterpret_cast<char*>(pProperty), nItems, RTL_TEXTENCODING_UTF8 );
+ sal_Int32 nIdx {0};
+ nVersionMajor = o3tl::toInt32(o3tl::getToken(aMetaVersion, 0, '.', nIdx));
+ nVersionMinor = o3tl::toInt32(o3tl::getToken(aMetaVersion, 0, '.', nIdx));
+ }
+ if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ if( nVersionMajor < 2 || (nVersionMajor == 2 && nVersionMinor < 12) )
+ m_bLegacyPartialFullscreen = true;
+ }
+ }
+ }
+ else
+ {
+ if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ GetGenericUnixSalData()->ErrorTrapPush();
+ }
+ }
+
+ GetGenericUnixSalData()->ErrorTrapPop();
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ return bNetWM;
+}
+
+bool WMAdaptor::getWMshouldSwitchWorkspace() const
+{
+ if( ! m_bWMshouldSwitchWorkspaceInit )
+ {
+ WMAdaptor * pWMA = const_cast<WMAdaptor*>(this);
+
+ pWMA->m_bWMshouldSwitchWorkspace = true;
+ vcl::SettingsConfigItem* pItem = vcl::SettingsConfigItem::get();
+ OUString aSetting( pItem->getValue( "WM",
+ "ShouldSwitchWorkspace" ) );
+ if( aSetting.isEmpty() )
+ {
+ if( m_aWMName == "awesome" )
+ {
+ pWMA->m_bWMshouldSwitchWorkspace = false;
+ }
+ }
+ else
+ pWMA->m_bWMshouldSwitchWorkspace = aSetting.toBoolean();
+ pWMA->m_bWMshouldSwitchWorkspaceInit = true;
+ }
+ return m_bWMshouldSwitchWorkspace;
+}
+
+/*
+ * WMAdaptor::isValid()
+ */
+bool WMAdaptor::isValid() const
+{
+ return true;
+}
+
+/*
+ * NetWMAdaptor::isValid()
+ */
+bool NetWMAdaptor::isValid() const
+{
+ // some necessary sanity checks; there are WMs out there
+ // which implement some of the WM hints spec without
+ // real functionality
+ return
+ m_aWMAtoms[ NET_SUPPORTED ]
+ && m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ]
+ && m_aWMAtoms[ NET_WM_NAME ]
+ && m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ]
+ && m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ]
+ ;
+}
+
+/*
+ * GnomeWMAdaptor::isValid()
+ */
+bool GnomeWMAdaptor::isValid() const
+{
+ return m_bValid;
+}
+
+/*
+ * WMAdaptor::initAtoms
+ */
+
+void WMAdaptor::initAtoms()
+{
+ // get basic atoms
+ for(const WMAdaptorProtocol & i : aAtomTab)
+ m_aWMAtoms[ i.nProtocol ] = XInternAtom( m_pDisplay, i.pProtocol, False );
+ m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] = XInternAtom( m_pDisplay, "_NET_SUPPORTING_WM_CHECK", True );
+ m_aWMAtoms[ NET_WM_NAME ] = XInternAtom( m_pDisplay, "_NET_WM_NAME", True );
+}
+
+/*
+ * NetWMAdaptor::initAtoms
+ */
+
+void NetWMAdaptor::initAtoms()
+{
+ WMAdaptor::initAtoms();
+
+ m_aWMAtoms[ NET_SUPPORTED ] = XInternAtom( m_pDisplay, "_NET_SUPPORTED", True );
+}
+
+/*
+ * GnomeWMAdaptor::initAtoms
+ */
+
+void GnomeWMAdaptor::initAtoms()
+{
+ WMAdaptor::initAtoms();
+
+ m_aWMAtoms[ WIN_PROTOCOLS ] = XInternAtom( m_pDisplay, "_WIN_PROTOCOLS", True );
+ m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ] = XInternAtom( m_pDisplay, "_WIN_SUPPORTING_WM_CHECK", True );
+}
+
+/*
+ * WMAdaptor::setWMName
+ * sets WM_NAME
+ * WM_ICON_NAME
+ */
+
+void WMAdaptor::setWMName( X11SalFrame* pFrame, const OUString& rWMName ) const
+{
+ OString aTitle(OUStringToOString(rWMName,
+ osl_getThreadTextEncoding()));
+
+ OString aWMLocale;
+ rtl_Locale* pLocale = nullptr;
+ osl_getProcessLocale( &pLocale );
+ if( pLocale )
+ {
+ OUString aLocaleString(
+ LanguageTag( *pLocale).getGlibcLocaleString( std::u16string_view()));
+ aWMLocale = OUStringToOString( aLocaleString, RTL_TEXTENCODING_ISO_8859_1 );
+ }
+ else
+ {
+ static const char* pLang = getenv( "LANG" );
+ aWMLocale = pLang ? pLang : "C";
+ }
+
+ char* pT = const_cast<char*>(aTitle.getStr());
+ XTextProperty aProp = { nullptr, None, 0, 0 };
+ XmbTextListToTextProperty( m_pDisplay,
+ &pT,
+ 1,
+ XStdICCTextStyle,
+ &aProp );
+
+ unsigned char const * pData = aProp.nitems ? aProp.value : reinterpret_cast<unsigned char const *>(aTitle.getStr());
+ Atom nType = aProp.nitems ? aProp.encoding : XA_STRING;
+ int nFormat = aProp.nitems ? aProp.format : 8;
+ int nBytes = aProp.nitems ? aProp.nitems : aTitle.getLength();
+ const SystemEnvData* pEnv = pFrame->GetSystemData();
+ XChangeProperty( m_pDisplay,
+ static_cast<::Window>(pEnv->aShellWindow),
+ XA_WM_NAME,
+ nType,
+ nFormat,
+ PropModeReplace,
+ pData,
+ nBytes );
+ XChangeProperty( m_pDisplay,
+ static_cast<::Window>(pEnv->aShellWindow),
+ XA_WM_ICON_NAME,
+ nType,
+ nFormat,
+ PropModeReplace,
+ pData,
+ nBytes );
+ XChangeProperty( m_pDisplay,
+ static_cast<::Window>(pEnv->aShellWindow),
+ m_aWMAtoms[ WM_LOCALE_NAME ],
+ XA_STRING,
+ 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char const *>(aWMLocale.getStr()),
+ aWMLocale.getLength() );
+ if (aProp.value != nullptr)
+ XFree( aProp.value );
+}
+
+/*
+ * NetWMAdaptor::setWMName
+ * sets WM_NAME
+ * _NET_WM_NAME
+ * WM_ICON_NAME
+ * _NET_WM_ICON_NAME
+ */
+void NetWMAdaptor::setWMName( X11SalFrame* pFrame, const OUString& rWMName ) const
+{
+ WMAdaptor::setWMName( pFrame, rWMName );
+
+ OString aTitle(OUStringToOString(rWMName, RTL_TEXTENCODING_UTF8));
+ const SystemEnvData* pEnv = pFrame->GetSystemData();
+ if( m_aWMAtoms[ NET_WM_NAME ] )
+ XChangeProperty( m_pDisplay,
+ static_cast<::Window>(pEnv->aShellWindow),
+ m_aWMAtoms[ NET_WM_NAME ],
+ m_aWMAtoms[ UTF8_STRING ],
+ 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char const *>(aTitle.getStr()),
+ aTitle.getLength() );
+ if( m_aWMAtoms[ NET_WM_ICON_NAME ] )
+ XChangeProperty( m_pDisplay,
+ static_cast<::Window>(pEnv->aShellWindow),
+ m_aWMAtoms[ NET_WM_ICON_NAME ],
+ m_aWMAtoms[ UTF8_STRING ],
+ 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char const *>(aTitle.getStr()),
+ aTitle.getLength() );
+}
+
+/*
+ * NetWMAdaptor::setNetWMState
+ * sets _NET_WM_STATE
+ */
+void NetWMAdaptor::setNetWMState( X11SalFrame* pFrame ) const
+{
+ if( !(m_aWMAtoms[ NET_WM_STATE ]) )
+ return;
+
+ Atom aStateAtoms[ 10 ];
+ int nStateAtoms = 0;
+
+ // set NET_WM_STATE_MODAL
+ if( pFrame->mbMaximizedVert
+ && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] )
+ aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ];
+ if( pFrame->mbMaximizedHorz
+ && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] )
+ aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ];
+ if( pFrame->bAlwaysOnTop_ && m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ] )
+ aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ];
+ if( pFrame->mbFullScreen && m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ] )
+ aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ];
+ if( pFrame->meWindowType == WMWindowType::Utility && m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ] )
+ aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ];
+
+ if( nStateAtoms )
+ {
+ XChangeProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ NET_WM_STATE ],
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(aStateAtoms),
+ nStateAtoms
+ );
+ }
+ else
+ XDeleteProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ NET_WM_STATE ] );
+ if( !pFrame->mbMaximizedHorz
+ || !pFrame->mbMaximizedVert
+ || ( pFrame->nStyle_ & SalFrameStyleFlags::SIZEABLE ) )
+ return;
+
+ /*
+ * for maximizing use NorthWestGravity (including decoration)
+ */
+ XSizeHints hints;
+ tools::Long supplied;
+ bool bHint = false;
+ if( XGetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints,
+ &supplied ) )
+ {
+ bHint = true;
+ hints.flags |= PWinGravity;
+ hints.win_gravity = NorthWestGravity;
+ XSetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints );
+ XSync( m_pDisplay, False );
+ }
+
+ // SetPosSize necessary to set width/height, min/max w/h
+ sal_Int32 nCurrent = 0;
+ /*
+ * get current desktop here if work areas have different size
+ * (does this happen on any platform ?)
+ */
+ if( ! m_bEqualWorkAreas )
+ {
+ nCurrent = getCurrentWorkArea();
+ if( nCurrent < 0 )
+ nCurrent = 0;
+ }
+ AbsoluteScreenPixelRectangle aPosSize = m_aWMWorkAreas[nCurrent];
+ const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() );
+ aPosSize = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( aPosSize.Left() + rGeom.leftDecoration(),
+ aPosSize.Top() + rGeom.topDecoration() ),
+ AbsoluteScreenPixelSize( aPosSize.GetWidth()
+ - rGeom.leftDecoration()
+ - rGeom.rightDecoration(),
+ aPosSize.GetHeight()
+ - rGeom.topDecoration()
+ - rGeom.bottomDecoration() )
+ );
+ pFrame->SetPosSize( aPosSize );
+
+ /*
+ * reset gravity hint to static gravity
+ * (this should not move window according to ICCCM)
+ */
+ if( bHint && pFrame->nShowState_ != X11ShowState::Unknown )
+ {
+ hints.win_gravity = StaticGravity;
+ XSetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints );
+ }
+}
+
+/*
+ * GnomeWMAdaptor::setNetWMState
+ * sets _WIN_STATE
+ */
+void GnomeWMAdaptor::setGnomeWMState( X11SalFrame* pFrame ) const
+{
+ if( !(m_aWMAtoms[ WIN_STATE ]) )
+ return;
+
+ sal_uInt32 nWinWMState = 0;
+
+ if( pFrame->mbMaximizedVert )
+ nWinWMState |= 1 << 2;
+ if( pFrame->mbMaximizedHorz )
+ nWinWMState |= 1 << 3;
+
+ XChangeProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ WIN_STATE ],
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&nWinWMState),
+ 1
+ );
+ if( !pFrame->mbMaximizedHorz
+ || !pFrame->mbMaximizedVert
+ || ( pFrame->nStyle_ & SalFrameStyleFlags::SIZEABLE ) )
+ return;
+
+ /*
+ * for maximizing use NorthWestGravity (including decoration)
+ */
+ XSizeHints hints;
+ tools::Long supplied;
+ bool bHint = false;
+ if( XGetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints,
+ &supplied ) )
+ {
+ bHint = true;
+ hints.flags |= PWinGravity;
+ hints.win_gravity = NorthWestGravity;
+ XSetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints );
+ XSync( m_pDisplay, False );
+ }
+
+ // SetPosSize necessary to set width/height, min/max w/h
+ sal_Int32 nCurrent = 0;
+ /*
+ * get current desktop here if work areas have different size
+ * (does this happen on any platform ?)
+ */
+ if( ! m_bEqualWorkAreas )
+ {
+ nCurrent = getCurrentWorkArea();
+ if( nCurrent < 0 )
+ nCurrent = 0;
+ }
+ AbsoluteScreenPixelRectangle aPosSize = m_aWMWorkAreas[nCurrent];
+ const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() );
+ aPosSize = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( aPosSize.Left() + rGeom.leftDecoration(),
+ aPosSize.Top() + rGeom.topDecoration() ),
+ AbsoluteScreenPixelSize( aPosSize.GetWidth()
+ - rGeom.leftDecoration()
+ - rGeom.rightDecoration(),
+ aPosSize.GetHeight()
+ - rGeom.topDecoration()
+ - rGeom.bottomDecoration() )
+ );
+ pFrame->SetPosSize( aPosSize );
+
+ /*
+ * reset gravity hint to static gravity
+ * (this should not move window according to ICCCM)
+ */
+ if( bHint && pFrame->nShowState_ != X11ShowState::Unknown )
+ {
+ hints.win_gravity = StaticGravity;
+ XSetWMNormalHints( m_pDisplay,
+ pFrame->GetShellWindow(),
+ &hints );
+ }
+}
+
+/*
+ * WMAdaptor::setFrameDecoration
+ * sets _MOTIF_WM_HINTS
+ * WM_TRANSIENT_FOR
+ */
+
+void WMAdaptor::setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pReferenceFrame ) const
+{
+ pFrame->meWindowType = eType;
+
+ if( ! pFrame->mbFullScreen )
+ {
+ // set mwm hints
+ struct _mwmhints {
+ unsigned long flags, func, deco;
+ tools::Long input_mode;
+ unsigned long status;
+ } aHint;
+
+ aHint.flags = 15; /* flags for functions, decoration, input mode and status */
+ aHint.deco = 0;
+ aHint.func = 1 << 2;
+ aHint.status = 0;
+ aHint.input_mode = 0;
+
+ // evaluate decoration flags
+ if( nDecorationFlags & decoration_All )
+ {
+ aHint.deco = 1;
+ aHint.func = 1;
+ }
+ else
+ {
+ if( nDecorationFlags & decoration_Title )
+ aHint.deco |= 1 << 3;
+ if( nDecorationFlags & decoration_Border )
+ aHint.deco |= 1 << 1;
+ if( nDecorationFlags & decoration_Resize )
+ {
+ aHint.deco |= 1 << 2;
+ aHint.func |= 1 << 1;
+ }
+ if( nDecorationFlags & decoration_MinimizeBtn )
+ {
+ aHint.deco |= 1 << 5;
+ aHint.func |= 1 << 3;
+ }
+ if( nDecorationFlags & decoration_MaximizeBtn )
+ {
+ aHint.deco |= 1 << 6;
+ aHint.func |= 1 << 4;
+ }
+ if( nDecorationFlags & decoration_CloseBtn )
+ {
+ aHint.deco |= 1 << 4;
+ aHint.func |= 1 << 5;
+ }
+ }
+
+ // set the hint
+ XChangeProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ MOTIF_WM_HINTS ],
+ m_aWMAtoms[ MOTIF_WM_HINTS ],
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&aHint),
+ 5 );
+ }
+
+ // set transientFor hint
+ /* #91030# dtwm will not map a dialogue if the transient
+ * window is iconified. This is deemed undesirable because
+ * message boxes do not get mapped, so use the root as transient
+ * instead.
+ */
+ if( pReferenceFrame )
+ {
+ XSetTransientForHint( m_pDisplay,
+ pFrame->GetShellWindow(),
+ pReferenceFrame->bMapped_ ?
+ pReferenceFrame->GetShellWindow() :
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() )
+ );
+ if( ! pReferenceFrame->bMapped_ )
+ pFrame->mbTransientForRoot = true;
+ }
+}
+
+/*
+ * NetWMAdaptor::setFrameDecoration
+ * sets _MOTIF_WM_HINTS
+ * _NET_WM_WINDOW_TYPE
+ * _NET_WM_STATE
+ * WM_TRANSIENT_FOR
+ */
+
+void NetWMAdaptor::setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pReferenceFrame ) const
+{
+ WMAdaptor::setFrameTypeAndDecoration( pFrame, eType, nDecorationFlags, pReferenceFrame );
+
+ setNetWMState( pFrame );
+
+ // set NET_WM_WINDOW_TYPE
+ if( m_aWMAtoms[ NET_WM_WINDOW_TYPE ] )
+ {
+ Atom aWindowTypes[4];
+ int nWindowTypes = 0;
+ switch( eType )
+ {
+ case WMWindowType::Utility:
+ aWindowTypes[nWindowTypes++] =
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_UTILITY ] ?
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_UTILITY ] :
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ];
+ break;
+ case WMWindowType::ModelessDialogue:
+ aWindowTypes[nWindowTypes++] =
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ];
+ break;
+ case WMWindowType::Splash:
+ aWindowTypes[nWindowTypes++] =
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_SPLASH ] ?
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_SPLASH ] :
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ];
+ break;
+ case WMWindowType::Toolbar:
+ if( m_aWMAtoms[ KDE_NET_WM_WINDOW_TYPE_OVERRIDE ] )
+ aWindowTypes[nWindowTypes++] = m_aWMAtoms[ KDE_NET_WM_WINDOW_TYPE_OVERRIDE ];
+ aWindowTypes[nWindowTypes++] =
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_TOOLBAR ] ?
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_TOOLBAR ] :
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL];
+ break;
+ case WMWindowType::Dock:
+ aWindowTypes[nWindowTypes++] =
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_DOCK ] ?
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_DOCK ] :
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL];
+ break;
+ default:
+ aWindowTypes[nWindowTypes++] = m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ];
+ break;
+ }
+ XChangeProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ NET_WM_WINDOW_TYPE ],
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(aWindowTypes),
+ nWindowTypes );
+ }
+ if( ( eType == WMWindowType::ModelessDialogue )
+ && ! pReferenceFrame )
+ {
+ XSetTransientForHint( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ) );
+ pFrame->mbTransientForRoot = true;
+ }
+}
+
+/*
+ * WMAdaptor::maximizeFrame
+ */
+
+void WMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const
+{
+ pFrame->mbMaximizedVert = bVertical;
+ pFrame->mbMaximizedHorz = bHorizontal;
+
+ const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() );
+
+ // discard pending configure notifies for this frame
+ XSync( m_pDisplay, False );
+ XEvent aDiscard;
+ while( XCheckTypedWindowEvent( m_pDisplay,
+ pFrame->GetShellWindow(),
+ ConfigureNotify,
+ &aDiscard ) )
+ ;
+ while( XCheckTypedWindowEvent( m_pDisplay,
+ pFrame->GetWindow(),
+ ConfigureNotify,
+ &aDiscard ) )
+ ;
+
+ if( bHorizontal || bVertical )
+ {
+ AbsoluteScreenPixelSize aScreenSize( m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() ) );
+ AbsoluteScreenPixelPoint aTL( rGeom.leftDecoration(), rGeom.topDecoration() );
+ if( m_pSalDisplay->IsXinerama() )
+ {
+ AbsoluteScreenPixelPoint aMed( aTL.X() + rGeom.width()/2, aTL.Y() + rGeom.height()/2 );
+ const std::vector< AbsoluteScreenPixelRectangle >& rScreens = m_pSalDisplay->GetXineramaScreens();
+ for(const auto & rScreen : rScreens)
+ if( rScreen.Contains( aMed ) )
+ {
+ aTL += rScreen.TopLeft();
+ aScreenSize = rScreen.GetSize();
+ break;
+ }
+ }
+ AbsoluteScreenPixelRectangle aTarget( aTL,
+ AbsoluteScreenPixelSize( aScreenSize.Width() - rGeom.leftDecoration() - rGeom.topDecoration(),
+ aScreenSize.Height() - rGeom.topDecoration() - rGeom.bottomDecoration() )
+ );
+
+ const AbsoluteScreenPixelRectangle aReferenceGeometry = !pFrame->maRestorePosSize.IsEmpty() ?
+ pFrame->maRestorePosSize : AbsoluteScreenPixelRectangle(rGeom.posSize());
+ if( ! bHorizontal )
+ {
+ aTarget.SetSize({ aReferenceGeometry.GetWidth(), aTarget.GetHeight() });
+ aTarget.SetLeft(aReferenceGeometry.Left());
+ }
+ else if( ! bVertical )
+ {
+ aTarget.SetSize({ aTarget.GetWidth(), aReferenceGeometry.GetHeight() });
+ aTarget.SetTop(aReferenceGeometry.Top());
+ }
+
+ AbsoluteScreenPixelRectangle aRestore(rGeom.posSize());
+ if( pFrame->bMapped_ )
+ {
+ XSetInputFocus( m_pDisplay,
+ pFrame->GetShellWindow(),
+ RevertToNone,
+ CurrentTime
+ );
+ }
+
+ if( pFrame->maRestorePosSize.IsEmpty() )
+ pFrame->maRestorePosSize = aRestore;
+
+ pFrame->SetPosSize( aTarget );
+ pFrame->nWidth_ = aTarget.GetWidth();
+ pFrame->nHeight_ = aTarget.GetHeight();
+ XRaiseWindow( m_pDisplay,
+ pFrame->GetShellWindow()
+ );
+ if( pFrame->GetStackingWindow() )
+ XRaiseWindow( m_pDisplay,
+ pFrame->GetStackingWindow()
+ );
+
+ }
+ else
+ {
+ pFrame->SetPosSize( pFrame->maRestorePosSize );
+ pFrame->maRestorePosSize = AbsoluteScreenPixelRectangle();
+ pFrame->nWidth_ = rGeom.width();
+ pFrame->nHeight_ = rGeom.height();
+ }
+}
+
+/*
+ * NetWMAdaptor::maximizeFrame
+ * changes _NET_WM_STATE by sending a client message
+ */
+
+void NetWMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const
+{
+ pFrame->mbMaximizedVert = bVertical;
+ pFrame->mbMaximizedHorz = bHorizontal;
+
+ if( m_aWMAtoms[ NET_WM_STATE ]
+ && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ]
+ && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ]
+ && ( pFrame->nStyle_ & ~SalFrameStyleFlags::DEFAULT )
+ )
+ {
+ if( pFrame->bMapped_ )
+ {
+ // window already mapped, send WM a message
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = bHorizontal ? 1 : 0;
+ aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ];
+ aEvent.xclient.data.l[2] = bHorizontal == bVertical ? m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] : 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ if( bHorizontal != bVertical )
+ {
+ aEvent.xclient.data.l[0]= bVertical ? 1 : 0;
+ aEvent.xclient.data.l[1]= m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ];
+ aEvent.xclient.data.l[2]= 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ }
+ }
+ else
+ {
+ // window not mapped yet, set _NET_WM_STATE directly
+ setNetWMState( pFrame );
+ }
+ if( !bHorizontal && !bVertical )
+ pFrame->maRestorePosSize = AbsoluteScreenPixelRectangle();
+ else if( pFrame->maRestorePosSize.IsEmpty() )
+ {
+ const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() );
+ pFrame->maRestorePosSize =
+ AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( rGeom.x(), rGeom.y() ), AbsoluteScreenPixelSize( rGeom.width(), rGeom.height() ) );
+ }
+ }
+ else
+ WMAdaptor::maximizeFrame( pFrame, bHorizontal, bVertical );
+}
+
+/*
+ * GnomeWMAdaptor::maximizeFrame
+ * changes _WIN_STATE by sending a client message
+ */
+
+void GnomeWMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const
+{
+ pFrame->mbMaximizedVert = bVertical;
+ pFrame->mbMaximizedHorz = bHorizontal;
+
+ if( m_aWMAtoms[ WIN_STATE ]
+ && ( pFrame->nStyle_ & ~SalFrameStyleFlags::DEFAULT )
+ )
+ {
+ if( pFrame->bMapped_ )
+ {
+ // window already mapped, send WM a message
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ WIN_STATE ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = (1<<2)|(1<<3);
+ aEvent.xclient.data.l[1] =
+ (bVertical ? (1<<2) : 0)
+ | (bHorizontal ? (1<<3) : 0);
+ aEvent.xclient.data.l[2] = 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask,
+ &aEvent
+ );
+ }
+ else
+ // window not mapped yet, set _WIN_STATE directly
+ setGnomeWMState( pFrame );
+
+ if( !bHorizontal && !bVertical )
+ pFrame->maRestorePosSize = AbsoluteScreenPixelRectangle();
+ else if( pFrame->maRestorePosSize.IsEmpty() )
+ {
+ const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() );
+ pFrame->maRestorePosSize =
+ AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( rGeom.x(), rGeom.y() ), AbsoluteScreenPixelSize( rGeom.width(), rGeom.height() ) );
+ }
+ }
+ else
+ WMAdaptor::maximizeFrame( pFrame, bHorizontal, bVertical );
+}
+
+/*
+ * WMAdaptor::enableAlwaysOnTop
+ */
+void WMAdaptor::enableAlwaysOnTop( X11SalFrame*, bool /*bEnable*/ ) const
+{
+}
+
+/*
+ * NetWMAdaptor::enableAlwaysOnTop
+ */
+void NetWMAdaptor::enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const
+{
+ pFrame->bAlwaysOnTop_ = bEnable;
+ if( !(m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ]) )
+ return;
+
+ if( pFrame->bMapped_ )
+ {
+ // window already mapped, send WM a message
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = bEnable ? 1 : 0;
+ aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ];
+ aEvent.xclient.data.l[2] = 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ }
+ else
+ setNetWMState( pFrame );
+}
+
+/*
+ * GnomeWMAdaptor::enableAlwaysOnTop
+ */
+void GnomeWMAdaptor::enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const
+{
+ pFrame->bAlwaysOnTop_ = bEnable;
+ if( !(m_aWMAtoms[ WIN_LAYER ]) )
+ return;
+
+ if( pFrame->bMapped_ )
+ {
+ // window already mapped, send WM a message
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ WIN_LAYER ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = bEnable ? 6 : 4;
+ aEvent.xclient.data.l[1] = 0;
+ aEvent.xclient.data.l[2] = 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ }
+ else
+ {
+ sal_uInt32 nNewLayer = bEnable ? 6 : 4;
+ XChangeProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ WIN_LAYER ],
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&nNewLayer),
+ 1
+ );
+ }
+}
+
+/*
+ * WMAdaptor::changeReferenceFrame
+ */
+void WMAdaptor::changeReferenceFrame( X11SalFrame* pFrame, X11SalFrame const * pReferenceFrame ) const
+{
+ if( ( pFrame->nStyle_ & SalFrameStyleFlags::PLUG )
+ || pFrame->IsOverrideRedirect()
+ || pFrame->IsFloatGrabWindow()
+ )
+ return;
+
+ ::Window aTransient = pFrame->pDisplay_->GetRootWindow( pFrame->GetScreenNumber() );
+ pFrame->mbTransientForRoot = true;
+ if( pReferenceFrame )
+ {
+ aTransient = pReferenceFrame->GetShellWindow();
+ pFrame->mbTransientForRoot = false;
+ }
+ XSetTransientForHint( m_pDisplay,
+ pFrame->GetShellWindow(),
+ aTransient );
+}
+
+/*
+ * WMAdaptor::handlePropertyNotify
+ */
+int WMAdaptor::handlePropertyNotify( X11SalFrame*, XPropertyEvent* ) const
+{
+ return 0;
+}
+
+/*
+ * NetWMAdaptor::handlePropertyNotify
+ */
+int NetWMAdaptor::handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const
+{
+ int nHandled = 1;
+ if( pEvent->atom == m_aWMAtoms[ NET_WM_STATE ] )
+ {
+ pFrame->mbMaximizedHorz = pFrame->mbMaximizedVert = false;
+
+ if( pEvent->state == PropertyNewValue )
+ {
+ Atom nType, *pStates;
+ int nFormat;
+ unsigned long nItems, nBytesLeft;
+ unsigned char* pData = nullptr;
+ tools::Long nOffset = 0;
+ do
+ {
+ XGetWindowProperty( m_pDisplay,
+ pEvent->window,
+ m_aWMAtoms[ NET_WM_STATE ],
+ nOffset, 64,
+ False,
+ XA_ATOM,
+ &nType,
+ &nFormat,
+ &nItems, &nBytesLeft,
+ &pData );
+ if( pData )
+ {
+ if( nType == XA_ATOM && nFormat == 32 && nItems > 0 )
+ {
+ pStates = reinterpret_cast<Atom*>(pData);
+ for( unsigned long i = 0; i < nItems; i++ )
+ {
+ if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] )
+ pFrame->mbMaximizedVert = true;
+ else if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] )
+ pFrame->mbMaximizedHorz = true;
+ }
+ }
+ XFree( pData );
+ pData = nullptr;
+ nOffset += nItems * nFormat / 32;
+ }
+ else
+ break;
+ } while( nBytesLeft > 0 );
+ }
+
+ if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) )
+ pFrame->maRestorePosSize = AbsoluteScreenPixelRectangle();
+ else
+ {
+ const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry();
+ // the current geometry may already be changed by the corresponding
+ // ConfigureNotify, but this cannot be helped
+ pFrame->maRestorePosSize =
+ AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( rGeom.x(), rGeom.y() ),
+ AbsoluteScreenPixelSize( rGeom.width(), rGeom.height() ) );
+ }
+ }
+ else if( pEvent->atom == m_aWMAtoms[ NET_WM_DESKTOP ] )
+ {
+ pFrame->m_nWorkArea = getWindowWorkArea( pFrame->GetShellWindow() );
+ }
+ else
+ nHandled = 0;
+
+ return nHandled;
+}
+
+/*
+ * GnomeWMAdaptor::handlePropertyNotify
+ */
+int GnomeWMAdaptor::handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const
+{
+ int nHandled = 1;
+ if( pEvent->atom == m_aWMAtoms[ WIN_STATE ] )
+ {
+ pFrame->mbMaximizedHorz = pFrame->mbMaximizedVert = false;
+
+ if( pEvent->state == PropertyNewValue )
+ {
+ Atom nType;
+ int nFormat = 0;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pData = nullptr;
+ XGetWindowProperty( m_pDisplay,
+ pEvent->window,
+ m_aWMAtoms[ WIN_STATE ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &nType,
+ &nFormat,
+ &nItems, &nBytesLeft,
+ &pData );
+ if( pData )
+ {
+ if( nType == XA_CARDINAL && nFormat == 32 && nItems == 1 )
+ {
+ sal_uInt32 nWinState = *reinterpret_cast<sal_uInt32*>(pData);
+ if( nWinState & (1<<2) )
+ pFrame->mbMaximizedVert = true;
+ if( nWinState & (1<<3) )
+ pFrame->mbMaximizedHorz = true;
+ }
+ XFree( pData );
+ }
+ }
+
+ if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) )
+ pFrame->maRestorePosSize = AbsoluteScreenPixelRectangle();
+ else
+ {
+ const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry();
+ // the current geometry may already be changed by the corresponding
+ // ConfigureNotify, but this cannot be helped
+ pFrame->maRestorePosSize =
+ AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( rGeom.x(), rGeom.y() ),
+ AbsoluteScreenPixelSize( rGeom.width(), rGeom.height() ) );
+ }
+ }
+ else if( pEvent->atom == m_aWMAtoms[ NET_WM_DESKTOP ] )
+ {
+ pFrame->m_nWorkArea = getWindowWorkArea( pFrame->GetShellWindow() );
+ }
+ else
+ nHandled = 0;
+
+ return nHandled;
+}
+
+/*
+ * WMAdaptor::showFullScreen
+ */
+void WMAdaptor::showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const
+{
+ pFrame->mbFullScreen = bFullScreen;
+ maximizeFrame( pFrame, bFullScreen, bFullScreen );
+}
+
+/*
+ * NetWMAdaptor::showFullScreen
+ */
+void NetWMAdaptor::showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const
+{
+ if( m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ] )
+ {
+ pFrame->mbFullScreen = bFullScreen;
+ if( bFullScreen )
+ {
+ if( m_aWMAtoms[ MOTIF_WM_HINTS ] )
+ {
+ XDeleteProperty( m_pDisplay,
+ pFrame->GetShellWindow(),
+ m_aWMAtoms[ MOTIF_WM_HINTS ] );
+ }
+ }
+ if( pFrame->bMapped_ )
+ {
+ // window already mapped, send WM a message
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = bFullScreen ? 1 : 0;
+ aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ];
+ aEvent.xclient.data.l[2] = 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ }
+ else
+ {
+ // window not mapped yet, set _NET_WM_STATE directly
+ setNetWMState( pFrame );
+ }
+ // #i42750# guess size before resize event shows up
+ if( bFullScreen )
+ {
+ if( m_pSalDisplay->IsXinerama() )
+ {
+ ::Window aRoot, aChild;
+ int root_x = 0, root_y = 0, lx, ly;
+ unsigned int mask;
+ XQueryPointer( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ &aRoot, &aChild,
+ &root_x, &root_y, &lx, &ly, &mask );
+ const std::vector< AbsoluteScreenPixelRectangle >& rScreens = m_pSalDisplay->GetXineramaScreens();
+ AbsoluteScreenPixelPoint aMousePoint( root_x, root_y );
+ for(const auto & rScreen : rScreens)
+ {
+ if( rScreen.Contains( aMousePoint ) )
+ {
+ pFrame->maGeometry.setPosSize(tools::Rectangle(rScreen));
+ break;
+ }
+ }
+ }
+ else
+ {
+ AbsoluteScreenPixelSize aSize = m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() );
+ pFrame->maGeometry.setPosSize({ { 0, 0 }, aSize });
+ }
+ pFrame->CallCallback( SalEvent::MoveResize, nullptr );
+ }
+ }
+ else WMAdaptor::showFullScreen( pFrame, bFullScreen );
+}
+
+/*
+ * WMAdaptor::getCurrentWorkArea
+ */
+// FIXME: multiscreen case
+int WMAdaptor::getCurrentWorkArea() const
+{
+ int nCurrent = -1;
+ if( m_aWMAtoms[ NET_CURRENT_DESKTOP ] )
+ {
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+ if( XGetWindowProperty( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ m_aWMAtoms[ NET_CURRENT_DESKTOP ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && pProperty
+ )
+ {
+ nCurrent = int(*reinterpret_cast<sal_Int32*>(pProperty));
+ XFree( pProperty );
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ return nCurrent;
+}
+
+/*
+ * WMAdaptor::getWindowWorkArea
+ */
+int WMAdaptor::getWindowWorkArea( ::Window aWindow ) const
+{
+ int nCurrent = -1;
+ if( m_aWMAtoms[ NET_WM_DESKTOP ] )
+ {
+ Atom aRealType = None;
+ int nFormat = 8;
+ unsigned long nItems = 0;
+ unsigned long nBytesLeft = 0;
+ unsigned char* pProperty = nullptr;
+ if( XGetWindowProperty( m_pDisplay,
+ aWindow,
+ m_aWMAtoms[ NET_WM_DESKTOP ],
+ 0, 1,
+ False,
+ XA_CARDINAL,
+ &aRealType,
+ &nFormat,
+ &nItems,
+ &nBytesLeft,
+ &pProperty ) == 0
+ && pProperty
+ )
+ {
+ nCurrent = int(*reinterpret_cast<sal_Int32*>(pProperty));
+ XFree( pProperty );
+ }
+ else if( pProperty )
+ {
+ XFree( pProperty );
+ pProperty = nullptr;
+ }
+ }
+ return nCurrent;
+}
+
+/*
+ * WMAdaptor::getCurrentWorkArea
+ */
+// fixme: multi screen case
+void WMAdaptor::switchToWorkArea( int nWorkArea ) const
+{
+ if( ! getWMshouldSwitchWorkspace() )
+ return;
+
+ if( !m_aWMAtoms[ NET_CURRENT_DESKTOP ] )
+ return;
+
+ XEvent aEvent;
+ aEvent.type = ClientMessage;
+ aEvent.xclient.display = m_pDisplay;
+ aEvent.xclient.window = m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() );
+ aEvent.xclient.message_type = m_aWMAtoms[ NET_CURRENT_DESKTOP ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = nWorkArea;
+ aEvent.xclient.data.l[1] = 0;
+ aEvent.xclient.data.l[2] = 0;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultXScreen() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+
+}
+
+/*
+ * WMAdaptor::frameIsMapping
+ */
+void WMAdaptor::frameIsMapping( X11SalFrame* ) const
+{
+}
+
+/*
+ * NetWMAdaptor::frameIsMapping
+ */
+void NetWMAdaptor::frameIsMapping( X11SalFrame* pFrame ) const
+{
+ setNetWMState( pFrame );
+}
+
+/*
+ * WMAdaptor::setUserTime
+ */
+void WMAdaptor::setUserTime( X11SalFrame*, tools::Long ) const
+{
+}
+
+/*
+ * NetWMAdaptor::setUserTime
+ */
+void NetWMAdaptor::setUserTime( X11SalFrame* i_pFrame, tools::Long i_nUserTime ) const
+{
+ if( m_aWMAtoms[NET_WM_USER_TIME] )
+ {
+ XChangeProperty( m_pDisplay,
+ i_pFrame->GetShellWindow(),
+ m_aWMAtoms[NET_WM_USER_TIME],
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&i_nUserTime),
+ 1
+ );
+ }
+}
+
+/*
+ * WMAdaptor::setPID
+ */
+void WMAdaptor::setPID( X11SalFrame const * i_pFrame ) const
+{
+ if( !(m_aWMAtoms[NET_WM_PID]) )
+ return;
+
+ tools::Long nPID = static_cast<tools::Long>(getpid());
+ XChangeProperty( m_pDisplay,
+ i_pFrame->GetShellWindow(),
+ m_aWMAtoms[NET_WM_PID],
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&nPID),
+ 1
+ );
+}
+
+/*
+* WMAdaptor::setClientMachine
+*/
+void WMAdaptor::setClientMachine( X11SalFrame const * i_pFrame ) const
+{
+ OString aWmClient( OUStringToOString( GetGenericUnixSalData()->GetHostname(), RTL_TEXTENCODING_ASCII_US ) );
+ XTextProperty aClientProp = { reinterpret_cast<unsigned char *>(const_cast<char *>(aWmClient.getStr())), XA_STRING, 8, sal::static_int_cast<unsigned long>( aWmClient.getLength() ) };
+ XSetWMClientMachine( m_pDisplay, i_pFrame->GetShellWindow(), &aClientProp );
+}
+
+void WMAdaptor::answerPing( X11SalFrame const * i_pFrame, XClientMessageEvent const * i_pEvent ) const
+{
+ if( !m_aWMAtoms[NET_WM_PING] ||
+ i_pEvent->message_type != m_aWMAtoms[ WM_PROTOCOLS ] ||
+ static_cast<Atom>(i_pEvent->data.l[0]) != m_aWMAtoms[ NET_WM_PING ] )
+ return;
+
+ XEvent aEvent;
+ aEvent.xclient = *i_pEvent;
+ aEvent.xclient.window = m_pSalDisplay->GetRootWindow( i_pFrame->GetScreenNumber() );
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( i_pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent
+ );
+ XFlush( m_pDisplay );
+}
+
+void WMAdaptor::activateWindow( X11SalFrame const *pFrame, Time nTimestamp )
+{
+ if (!pFrame->bMapped_)
+ return;
+
+ XEvent aEvent;
+
+ aEvent.xclient.type = ClientMessage;
+ aEvent.xclient.window = pFrame->GetShellWindow();
+ aEvent.xclient.message_type = m_aWMAtoms[ NET_ACTIVE_WINDOW ];
+ aEvent.xclient.format = 32;
+ aEvent.xclient.data.l[0] = 1;
+ aEvent.xclient.data.l[1] = nTimestamp;
+ aEvent.xclient.data.l[2] = None;
+ aEvent.xclient.data.l[3] = 0;
+ aEvent.xclient.data.l[4] = 0;
+
+ XSendEvent( m_pDisplay,
+ m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ),
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ &aEvent );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */