diff options
Diffstat (limited to 'vcl/unx/generic/app')
-rw-r--r-- | vcl/unx/generic/app/i18n_cb.cxx | 664 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_ic.cxx | 781 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_im.cxx | 619 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_keysym.cxx | 365 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_status.cxx | 733 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_wrp.cxx | 260 | ||||
-rw-r--r-- | vcl/unx/generic/app/i18n_xkb.cxx | 163 | ||||
-rw-r--r-- | vcl/unx/generic/app/keysymnames.cxx | 688 | ||||
-rw-r--r-- | vcl/unx/generic/app/makefile.mk | 110 | ||||
-rw-r--r-- | vcl/unx/generic/app/randrwrapper.cxx | 360 | ||||
-rw-r--r-- | vcl/unx/generic/app/saldata.cxx | 867 | ||||
-rw-r--r-- | vcl/unx/generic/app/saldisp.cxx | 3435 | ||||
-rw-r--r-- | vcl/unx/generic/app/salinst.cxx | 452 | ||||
-rw-r--r-- | vcl/unx/generic/app/salsys.cxx | 226 | ||||
-rw-r--r-- | vcl/unx/generic/app/saltimer.cxx | 96 | ||||
-rw-r--r-- | vcl/unx/generic/app/sm.cxx | 801 | ||||
-rw-r--r-- | vcl/unx/generic/app/soicon.cxx | 116 | ||||
-rw-r--r-- | vcl/unx/generic/app/wmadaptor.cxx | 2548 |
18 files changed, 13284 insertions, 0 deletions
diff --git a/vcl/unx/generic/app/i18n_cb.cxx b/vcl/unx/generic/app/i18n_cb.cxx new file mode 100644 index 000000000000..5a314c19d3e5 --- /dev/null +++ b/vcl/unx/generic/app/i18n_cb.cxx @@ -0,0 +1,664 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <string.h> + +#include <sal/alloca.h> +#include <osl/thread.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <tools/postx.h> + +#include "unx/salunx.h" +#include "unx/XIM.h" +#include "unx/i18n_cb.hxx" +#include "unx/i18n_status.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 = (preedit_data_t*)client_data; + if ( pPreeditData->eState == ePreeditStatusActivationRequired ) + { + pPreeditData->eState = ePreeditStatusActive; + pPreeditData->aText.nCursorPos = 0; + pPreeditData->aText.nLength = 0; + } + + return -1; +} + +// ------------------------------------------------------------------------- +// +// ii. preedit done callback +// +// ------------------------------------------------------------------------- + +void +PreeditDoneCallback ( XIC, XPointer client_data, XPointer ) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + if (pPreeditData->eState == ePreeditStatusActive ) + { + if( pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + pPreeditData->eState = ePreeditStatusStartPending; +} + +// ------------------------------------------------------------------------- +// +// iii. preedit draw callback +// +// ------------------------------------------------------------------------- + +// +// Handle deletion of text in a preedit_draw_callback +// from and howmuch are guaranteed to be nonnegative +// + +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 == (int)ptext->nLength) + { + // delete from the end of the text + ptext->nLength = from; + } + else + if (to < (int)ptext->nLength) + { + // cut out of the middle of the text + memmove( (void*)(ptext->pUnicodeBuffer + from), + (void*)(ptext->pUnicodeBuffer + to), + (ptext->nLength - to) * sizeof(sal_Unicode)); + memmove( (void*)(ptext->pCharStyle + from), + (void*)(ptext->pCharStyle + to), + (ptext->nLength - to) * sizeof(XIMFeedback)); + ptext->nLength -= howmuch; + } + else + // if ( to > pText->nLength ) + { + // XXX this indicates an error, are we out of sync ? + fprintf(stderr, "Preedit_DeleteText( from=%i to=%i length=%i )\n", + from, to, ptext->nLength ); + fprintf (stderr, "\t XXX internal error, out of sync XXX\n"); + + ptext->nLength = from; + } + + // NULL-terminate the string + ptext->pUnicodeBuffer[ptext->nLength] = (sal_Unicode)0; +} + +// reallocate the textbuffer with sufficiently large size 2^x +// nnewlimit is presupposed to be larger than ptext->size +void +enlarge_buffer ( preedit_text_t *ptext, int nnewlimit ) +{ + size_t nnewsize = ptext->nSize; + + while ( nnewsize <= (size_t)nnewlimit ) + nnewsize *= 2; + + ptext->nSize = nnewsize; + ptext->pUnicodeBuffer = (sal_Unicode*)realloc((void*)ptext->pUnicodeBuffer, + nnewsize * sizeof(sal_Unicode)); + ptext->pCharStyle = (XIMFeedback*)realloc((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 +// + +void +Preedit_InsertText(preedit_text_t *pText, XIMText *pInsertText, int where, + Bool isMultilingual) +{ + sal_Unicode *pInsertTextString; + int nInsertTextLength = 0; + XIMFeedback *pInsertTextCharStyle = pInsertText->feedback; + + nInsertTextLength = pInsertText->length; + + if (isMultilingual) + { + XIMUnicodeText *pUniText = (XIMUnicodeText*)pInsertText; + pInsertTextString = pUniText->string.utf16_char; + } + else + { + // 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 ( NULL, pWCString, 1024 /* dont care */); + pMBString = (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 = (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 = (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((void*)(pText->pUnicodeBuffer + to), + (void*)(pText->pUnicodeBuffer + from), + howmany * sizeof(sal_Unicode)); + memmove((void*)(pText->pCharStyle + to), + (void*)(pText->pCharStyle + from), + howmany * sizeof(XIMFeedback)); + + to = from; + howmany = nInsertTextLength; + + memcpy((void*)(pText->pUnicodeBuffer + to), (void*)pInsertTextString, + howmany * sizeof(sal_Unicode)); + memcpy((void*)(pText->pCharStyle + to), (void*)pInsertTextCharStyle, + howmany * sizeof(XIMFeedback)); + + pText->nLength += howmany; + + // NULL-terminate the string + pText->pUnicodeBuffer[pText->nLength] = (sal_Unicode)0; +} + +// +// Handle the change of attributes in a preedit_draw_callback +// +void +Preedit_UpdateAttributes ( preedit_text_t* ptext, XIMFeedback* feedback, + int from, int amount ) +{ + if ( (from + amount) > (int)ptext->nLength ) + { + // XXX this indicates an error, are we out of sync ? + fprintf (stderr, "Preedit_UpdateAttributes( %i + %i > %i )\n", + 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 +// SAL_EXTTEXTINPUT_ATTR values +// returns an allocate list of attributes, which must be freed by caller +USHORT* +Preedit_FeedbackToSAL ( XIMFeedback* pfeedback, int nlength, std::vector<USHORT>& rSalAttr ) +{ + USHORT *psalattr; + USHORT nval; + USHORT noldval = 0; + XIMFeedback nfeedback; + + // only work with reasonable length + if (nlength > 0 && nlength > sal::static_int_cast<int>(rSalAttr.size()) ) + { + rSalAttr.reserve( nlength ); + psalattr = &rSalAttr[0]; + } + else + return (USHORT*)NULL; + + for (int npos = 0; npos < nlength; npos++) + { + nval = 0; + 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 |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + if (nfeedback & XIMUnderline) + nval |= SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + if (nfeedback & XIMHighlight) + nval |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + if (nfeedback & XIMPrimary) + nval |= SAL_EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE; + if (nfeedback & XIMSecondary) + nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; + if (nfeedback & XIMTertiary) // same as 2ery + nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; + + /* + // visibility feedback not supported now + if ( (nfeedback & XIMVisibleToForward) + || (nfeedback & XIMVisibleToBackward) + || (nfeedback & XIMVisibleCenter) ) + { } + */ + } + // 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 = (preedit_data_t*)client_data; + + // if there's nothing to change then change nothing + if ( ( (call_data->text == NULL) && (call_data->chg_length == 0) ) + || pPreeditData->pFrame == NULL ) + return; + + // #88564# 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 == ePreeditStatusStartPending ) + pPreeditData->eState = ePreeditStatusActivationRequired; + PreeditStartCallback( ic, client_data, NULL ); + + // 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 == NULL) + { + 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 != NULL)) + { + Preedit_InsertText(&(pPreeditData->aText), call_data->text, + call_data->chg_first, pPreeditData->bIsMultilingual); + } + else + // handle text replacement by deletion and insertion of text, + // not smart, just good enough + if ( (call_data->chg_length != 0) + && (call_data->text->string.wide_char != NULL)) + { + Preedit_DeleteText(&(pPreeditData->aText), + call_data->chg_first, call_data->chg_length); + Preedit_InsertText(&(pPreeditData->aText), call_data->text, + call_data->chg_first, pPreeditData->bIsMultilingual); + } + else + // not really a text update, only attributes are concerned + if ( (call_data->chg_length != 0) + && (call_data->text->string.wide_char == NULL)) + { + 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.mnTime = 0; + pPreeditData->aInputEv.mpTextAttr = Preedit_FeedbackToSAL( + pPreeditData->aText.pCharStyle, pPreeditData->aText.nLength, pPreeditData->aInputFlags); + pPreeditData->aInputEv.mnCursorPos = call_data->caret; + pPreeditData->aInputEv.maText = String (pPreeditData->aText.pUnicodeBuffer, + pPreeditData->aText.nLength); + pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible + pPreeditData->aInputEv.mnDeltaStart = 0; // call_data->chg_first; + pPreeditData->aInputEv.mbOnlyCursor = False; + + if ( pPreeditData->eState == ePreeditStatusActive && pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); + if (pPreeditData->aText.nLength == 0 && pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + + if (pPreeditData->aText.nLength == 0) + pPreeditData->eState = ePreeditStatusStartPending; + + GetPreeditSpotLocation(ic, (XPointer)pPreeditData); +} + +void +GetPreeditSpotLocation(XIC ic, XPointer client_data) +{ + // + // Send SalEventExtTextInputPos event to get spotlocation + // + SalExtTextInputPosEvent mPosEvent; + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + + if( pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&mPosEvent); + + XPoint point; + point.x = mPosEvent.mnX + mPosEvent.mnWidth; + point.y = mPosEvent.mnY + mPosEvent.mnHeight; + + XVaNestedList preedit_attr; + preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, NULL); + XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + return; +} + +// ------------------------------------------------------------------------- +// +// iv. preedit caret callback +// +// ------------------------------------------------------------------------- + +#if OSL_DEBUG_LEVEL > 1 +void +PreeditCaretCallback ( XIC ic, XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data ) +#else +void +PreeditCaretCallback ( XIC, XPointer,XIMPreeditCaretCallbackStruct* ) +#endif +{ + #if OSL_DEBUG_LEVEL > 1 + // 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 = "Dont change"; break; + } + + fprintf (stderr, "PreeditCaretCallback( ic=%p, client=%p,\n", + ic, client_data ); + fprintf (stderr, "\t position=%i, direction=\"%s\", style=\"%s\" )\n", + call_data->position, direction, style ); + #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 + /* || (0x80 <= nChar && nChar <= 0x9F) C1 controls */ ) + return True; + else + return False; +} + +int +CommitStringCallback( XIC ic, XPointer client_data, XPointer call_data ) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + + XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data; + sal_Unicode *p_unicode_data = (sal_Unicode*)cbtext->string.utf16_char; + + // #86964# filter unexpected pure control events + if (cbtext->length == 1 && IsControlCode(p_unicode_data[0]) ) + { + if( pPreeditData->pFrame ) + { + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + } + else + { + if( pPreeditData->pFrame ) + { + pPreeditData->aInputEv.mnTime = 0; + pPreeditData->aInputEv.mpTextAttr = 0; + pPreeditData->aInputEv.mnCursorPos = cbtext->length; + pPreeditData->aInputEv.maText = UniString(p_unicode_data, cbtext->length); + pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible + pPreeditData->aInputEv.mnDeltaStart = 0; + pPreeditData->aInputEv.mbOnlyCursor = False; + + pPreeditData->pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + } + pPreeditData->eState = ePreeditStatusStartPending; + + GetPreeditSpotLocation(ic, (XPointer)pPreeditData); + + return 0; +} + +// ---------------------------------------------------------------------------------- +// +// vi. status callbacks: for now these are empty, they are just needed for turbo linux +// +// ---------------------------------------------------------------------------------- + +void +StatusStartCallback (XIC, XPointer, XPointer) +{ + return; +} + +void +StatusDoneCallback (XIC, XPointer, XPointer) +{ + return; +} + +void +StatusDrawCallback (XIC ic, XPointer client_data, XIMStatusDrawCallbackStruct *call_data) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + if( pPreeditData->bIsMultilingual ) + { + // IIIMP + XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data->data.text; + ::vcl::I18NStatus::get().setStatusText( String( cbtext->string.utf16_char, call_data->data.text->length ) ); + XIMUnicodeCharacterSubset* pSubset = NULL; + if( ! XGetICValues( ic, + XNUnicodeCharacterSubset, & pSubset, + NULL ) + && pSubset ) + { + ::vcl::I18NStatus::get().changeIM( String( ByteString( pSubset->name ), RTL_TEXTENCODING_UTF8 ) ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "got XNUnicodeCharacterSubset\n %d\n %d\n %s\n %d\n", pSubset->index, pSubset->subset_id, pSubset->name, pSubset->is_active ); +#endif + } + } + else if( call_data->type == XIMTextType ) + { + String aText; + if( call_data->data.text ) + { + // XIM with text + sal_Char* pMBString = NULL; + size_t nLength = 0; + if( call_data->data.text->encoding_is_wchar ) + { + if( call_data->data.text->string.wide_char ) + { + wchar_t* pWString = call_data->data.text->string.wide_char; + size_t nBytes = wcstombs( NULL, pWString, 1024 ); + pMBString = (sal_Char*)alloca( nBytes+1 ); + nLength = wcstombs( pMBString, pWString, nBytes+1 ); + } + } + else + { + if( call_data->data.text->string.multi_byte ) + { + pMBString = call_data->data.text->string.multi_byte; + nLength = strlen( pMBString ); + } + } + if( nLength ) + aText = String( pMBString, nLength, gsl_getSystemTextEncoding() ); + } + ::vcl::I18NStatus::get().setStatusText( aText ); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "XIMStatusDataType %s not supported\n", + call_data->type == XIMBitmapType ? "XIMBitmapType" : ByteString::CreateFromInt32( call_data->type ).GetBuffer() ); +#endif + return; +} + +void +SwitchIMCallback (XIC, XPointer, XPointer call_data) +{ + XIMSwitchIMNotifyCallbackStruct* pCallData = (XIMSwitchIMNotifyCallbackStruct*)call_data; + ::vcl::I18NStatus::get().changeIM( String( ByteString( pCallData->to->name ), RTL_TEXTENCODING_UTF8 ) ); +} + +// ---------------------------------------------------------------------------------- +// +// vii. destroy callbacks: internally disable all IC/IM calls +// +// ---------------------------------------------------------------------------------- + +void +IC_IMDestroyCallback (XIM, XPointer client_data, XPointer) +{ + SalI18N_InputContext *pContext = (SalI18N_InputContext*)client_data; + if (pContext != NULL) + pContext->HandleDestroyIM(); +} + +void +IM_IMDestroyCallback (XIM, XPointer client_data, XPointer) +{ + SalI18N_InputMethod *pMethod = (SalI18N_InputMethod*)client_data; + if (pMethod != NULL) + pMethod->HandleDestroyIM(); +} diff --git a/vcl/unx/generic/app/i18n_ic.cxx b/vcl/unx/generic/app/i18n_ic.cxx new file mode 100644 index 000000000000..517eb37a1b2e --- /dev/null +++ b/vcl/unx/generic/app/i18n_ic.cxx @@ -0,0 +1,781 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> + +#include <sal/alloca.h> +#include <osl/thread.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/XIM.h> +#include <unx/i18n_ic.hxx> +#include <unx/i18n_im.hxx> +#include <unx/i18n_status.hxx> + +#include <unx/salframe.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> + +using namespace vcl; + +static void sendEmptyCommit( SalFrame* pFrame ) +{ + vcl::DeletionListener aDel( pFrame ); + + SalExtTextInputEvent aEmptyEv; + aEmptyEv.mnTime = 0; + aEmptyEv.mpTextAttr = 0; + aEmptyEv.maText = String(); + aEmptyEv.mnCursorPos = 0; + aEmptyEv.mnCursorFlags = 0; + aEmptyEv.mnDeltaStart = 0; + aEmptyEv.mbOnlyCursor = False; + pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); + if( ! aDel.isDeleted() ) + pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +// --------------------------------------------------------------------------- +// +// 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 != NULL ) + XDestroyIC( maContext ); + if ( mpAttributes != NULL ) + XFree( mpAttributes ); + if ( mpStatusAttributes != NULL ) + XFree( mpStatusAttributes ); + if ( mpPreeditAttributes != NULL ) + XFree( mpPreeditAttributes ); + + if (maClientData.aText.pUnicodeBuffer != NULL) + free(maClientData.aText.pUnicodeBuffer); + if (maClientData.aText.pCharStyle != NULL) + 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 == NULL ) + { + a_dstlist = XVaCreateNestedList( + 0, + name, value, + NULL ); + } + else + { + a_dstlist = XVaCreateNestedList( + 0, + XNVaNestedList, a_srclist, + name, value, + NULL ); + } + + return a_dstlist != NULL ? a_dstlist : a_srclist ; +} + +// ---------------------------------------------------------------------------- +// convenience routine to create a fontset +// ---------------------------------------------------------------------------- + +static XFontSet +get_font_set( Display *p_display ) +{ + static XFontSet p_font_set = NULL; + + if (p_font_set == NULL) + { + 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; +} + +// --------------------------------------------------------------------------- +// +// Constructor for a InputContext (IC) +// +// ---------------------------------------------------------------------------- + +SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) : + mbUseable( True ), + maContext( (XIC)NULL ), + mnSupportedStatusStyle( + XIMStatusCallbacks | + XIMStatusNothing | + XIMStatusNone + ), + mnSupportedPreeditStyle( + XIMPreeditCallbacks | + XIMPreeditNothing | + XIMPreeditNone + ), + mnStatusStyle( 0 ), + mnPreeditStyle( 0 ), + mpAttributes( NULL ), + mpStatusAttributes( NULL ), + mpPreeditAttributes( NULL ) +{ +#ifdef SOLARIS + static const char* pIIIMPEnable = getenv( "SAL_DISABLE_OWN_IM_STATUS" ); + if( pIIIMPEnable && *pIIIMPEnable ) + mnSupportedStatusStyle &= ~XIMStatusCallbacks; +#endif + + maClientData.aText.pUnicodeBuffer = NULL; + maClientData.aText.pCharStyle = NULL; + maClientData.aInputEv.mnTime = 0; + maClientData.aInputEv.mpTextAttr = NULL; + maClientData.aInputEv.mnCursorPos = 0; + maClientData.aInputEv.mnDeltaStart = 0; + maClientData.aInputEv.mnCursorFlags = 0; + maClientData.aInputEv.mbOnlyCursor = FALSE; + + SalI18N_InputMethod *pInputMethod; + pInputMethod = GetX11SalData()->GetDisplay()->GetInputMethod(); + mbMultiLingual = pInputMethod->IsMultiLingual(); + + mnSupportedPreeditStyle = XIMPreeditCallbacks | XIMPreeditPosition + | XIMPreeditNothing | XIMPreeditNone; + if (pInputMethod->UseMethod() + && SupportInputMethodStyle( pInputMethod->GetSupportedStyles() ) ) + { + const SystemEnvData* pEnv = pFrame->GetSystemData(); + XLIB_Window aClientWindow = pEnv->aShellWindow; + XLIB_Window aFocusWindow = pEnv->aWindow; + + // for status callbacks and commit string callbacks +#define PREEDIT_BUFSZ 16 + maClientData.bIsMultilingual = mbMultiLingual; + maClientData.eState = ePreeditStatusStartPending; + maClientData.pFrame = pFrame; + maClientData.aText.pUnicodeBuffer = + (sal_Unicode*)malloc(PREEDIT_BUFSZ * sizeof(sal_Unicode)); + maClientData.aText.pCharStyle = + (XIMFeedback*)malloc(PREEDIT_BUFSZ * sizeof(XIMFeedback));; + maClientData.aText.nSize = PREEDIT_BUFSZ; + maClientData.aText.nCursorPos = 0; + maClientData.aText.nLength = 0; + + // + // Status attributes + // + + switch ( mnStatusStyle ) + { + case XIMStatusCallbacks: + { + static XIMCallback aStatusStartCallback; + static XIMCallback aStatusDoneCallback; + static XIMCallback aStatusDrawCallback; + + aStatusStartCallback.callback = (XIMProc)StatusStartCallback; + aStatusStartCallback.client_data = (XPointer)&maClientData; + aStatusDoneCallback.callback = (XIMProc)StatusDoneCallback; + aStatusDoneCallback.client_data = (XPointer)&maClientData; + aStatusDrawCallback.callback = (XIMProc)StatusDrawCallback; + aStatusDrawCallback.client_data = (XPointer)&maClientData; + + mpStatusAttributes = XVaCreateNestedList ( + 0, + XNStatusStartCallback, &aStatusStartCallback, + XNStatusDoneCallback, &aStatusDoneCallback, + XNStatusDrawCallback, &aStatusDrawCallback, + NULL ); + + break; + } + + case XIMStatusArea: + /* not supported */ + break; + + case XIMStatusNone: + case XIMStatusNothing: + default: + /* no arguments needed */ + break; + } + + // + // set preedit attributes + // + + switch ( mnPreeditStyle ) + { + case XIMPreeditCallbacks: + + maPreeditCaretCallback.callback = (XIMProc)PreeditCaretCallback; + maPreeditStartCallback.callback = (XIMProc)PreeditStartCallback; + maPreeditDoneCallback.callback = (XIMProc)PreeditDoneCallback; + maPreeditDrawCallback.callback = (XIMProc)PreeditDrawCallback; + maPreeditCaretCallback.client_data = (XPointer)&maClientData; + maPreeditStartCallback.client_data = (XPointer)&maClientData; + maPreeditDoneCallback.client_data = (XPointer)&maClientData; + maPreeditDrawCallback.client_data = (XPointer)&maClientData; + + mpPreeditAttributes = XVaCreateNestedList ( + 0, + XNPreeditStartCallback, &maPreeditStartCallback, + XNPreeditDoneCallback, &maPreeditDoneCallback, + XNPreeditDrawCallback, &maPreeditDrawCallback, + XNPreeditCaretCallback, &maPreeditCaretCallback, + NULL ); + + break; + + case XIMPreeditArea: + /* not supported */ + break; + + case XIMPreeditPosition: + { + // spot location + SalExtTextInputPosEvent aPosEvent; + pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (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, + NULL ); + + // 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 = GetX11SalData()->GetDisplay()->GetDisplay(); + XFontSet pFontSet = get_font_set(pDisplay); + + if (pFontSet != NULL) + { + mpPreeditAttributes = XVaAddToNestedList( mpPreeditAttributes, + const_cast<char*>(XNFontSet), (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, + NULL ); + + if ( mnPreeditStyle != XIMPreeditNone ) + { +#if defined LINUX || defined FREEBSD || defined NETBSD + if ( mpPreeditAttributes != NULL ) +#endif + mpAttributes = XVaAddToNestedList( mpAttributes, + const_cast<char*>(XNPreeditAttributes), (XPointer)mpPreeditAttributes ); + } + if ( mnStatusStyle != XIMStatusNone ) + { +#if defined LINUX || defined FREEBSD || defined NETBSD + if ( mpStatusAttributes != NULL ) +#endif + mpAttributes = XVaAddToNestedList( mpAttributes, + const_cast<char*>(XNStatusAttributes), (XPointer)mpStatusAttributes ); + } + maContext = XCreateIC( pInputMethod->GetMethod(), + XNVaNestedList, mpAttributes, + NULL ); + } + + if ( maContext == NULL ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "input context creation failed\n"); +#endif + + mbUseable = False; + mbMultiLingual = False; + + if ( mpAttributes != NULL ) + XFree( mpAttributes ); + if ( mpStatusAttributes != NULL ) + XFree( mpStatusAttributes ); + if ( mpPreeditAttributes != NULL ) + XFree( mpPreeditAttributes ); + if ( maClientData.aText.pUnicodeBuffer != NULL ) + free ( maClientData.aText.pUnicodeBuffer ); + if ( maClientData.aText.pCharStyle != NULL ) + free ( maClientData.aText.pCharStyle ); + + mpAttributes = NULL; + mpStatusAttributes = NULL; + mpPreeditAttributes = NULL; + maClientData.aText.pUnicodeBuffer = NULL; + maClientData.aText.pCharStyle = NULL; + } + + if ( maContext != NULL && mbMultiLingual ) + { + maCommitStringCallback.callback = (XIMProc)::CommitStringCallback; + maCommitStringCallback.client_data = (XPointer)&maClientData; + maSwitchIMCallback.callback = (XIMProc)::SwitchIMCallback; + maSwitchIMCallback.client_data = (XPointer)&maClientData; + XSetICValues( maContext, + XNCommitStringCallback, &maCommitStringCallback, + XNSwitchIMNotifyCallback, &maSwitchIMCallback, + NULL ); + } + if ( maContext != NULL) + { + maDestroyCallback.callback = (XIMProc)IC_IMDestroyCallback; + maDestroyCallback.client_data = (XPointer)this; + XSetICValues( maContext, + XNDestroyCallback, &maDestroyCallback, + NULL ); + } + + if( mbMultiLingual ) + { + // set initial IM status + XIMUnicodeCharacterSubset* pSubset = NULL; + if( ! XGetICValues( maContext, + XNUnicodeCharacterSubset, & pSubset, + NULL ) + && pSubset ) + { + String aCurrent( ByteString( pSubset->name ), RTL_TEXTENCODING_UTF8 ); + ::vcl::I18NStatus::get().changeIM( aCurrent ); + ::vcl::I18NStatus::get().setStatusText( aCurrent ); + } + } +} + +// --------------------------------------------------------------------------- +// +// In Solaris 8 the status window does not unmap if the frame unmapps, so +// unmap it the hard way +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::Unmap( SalFrame* pFrame ) +{ + if ( maContext != NULL ) + { + I18NStatus& rStatus( I18NStatus::get() ); + if( rStatus.getParent() == pFrame ) + rStatus.show( false, I18NStatus::contextmap ); + + } + UnsetICFocus( pFrame ); + maClientData.pFrame = NULL; +} + +void +SalI18N_InputContext::Map( SalFrame *pFrame ) +{ + if( mbUseable ) + { + I18NStatus& rStatus(I18NStatus::get() ); + rStatus.setParent( pFrame ); + if( pFrame ) + { + rStatus.show( true, I18NStatus::contextmap ); + if ( maContext == NULL ) + { + SalI18N_InputMethod *pInputMethod; + pInputMethod = GetX11SalData()->GetDisplay()->GetInputMethod(); + + maContext = XCreateIC( pInputMethod->GetMethod(), + XNVaNestedList, mpAttributes, + NULL ); + if ( maContext != NULL && mbMultiLingual ) + XSetICValues( maContext, + XNCommitStringCallback, &maCommitStringCallback, + XNSwitchIMNotifyCallback, &maSwitchIMCallback, + NULL ); + } + if( maClientData.pFrame != pFrame ) + SetICFocus( pFrame ); + } + } +} + +// -------------------------------------------------------------------------- +// +// Handle DestroyCallbacks +// in fact this is a callback called from the XNDestroyCallback +// +// -------------------------------------------------------------------------- + +void +SalI18N_InputContext::HandleDestroyIM() +{ + maContext = 0; // noli me tangere + mbUseable = False; +} + +// --------------------------------------------------------------------------- +// +// make sure, the input method gets all the X-Events it needs, this is only +// called once on each frame, it relys on a valid maContext +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::ExtendEventMask( XLIB_Window aFocusWindow ) +{ + unsigned long nIMEventMask; + XWindowAttributes aWindowAttributes; + + if ( mbUseable ) + { + Display *pDisplay = XDisplayOfIM( XIMOfIC(maContext) ); + + XGetWindowAttributes( pDisplay, aFocusWindow, + &aWindowAttributes ); + XGetICValues ( maContext, + XNFilterEvents, &nIMEventMask, + NULL); + 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 ) const +{ + struct StyleWeightingT { + const XIMStyle nStyle; + const unsigned int nWeight; + }; + + StyleWeightingT const *pWeightPtr; + 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 +{ + if ( (nStyle & mnSupportedPreeditStyle) + && (nStyle & mnSupportedStatusStyle) ) + { + return True; + } + return False; +} + +Bool +SalI18N_InputContext::SupportInputMethodStyle( XIMStyles *pIMStyles ) +{ + int nBestScore = 0; + int nActualScore = 0; + + mnPreeditStyle = 0; + mnStatusStyle = 0; + + if ( pIMStyles != NULL ) + { + // check whether the XIM supports one of the desired styles + // only a single preedit and a single status style must occure + // in a inpuut 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 & mnSupportedStatusStyle; + } + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + char pBuf[ 128 ]; + fprintf( stderr, "selected inputmethod style = %s\n", + GetMethodName(mnPreeditStyle | mnStatusStyle, pBuf, sizeof(pBuf)) ); +#endif + + return (mnPreeditStyle != 0) && (mnStatusStyle != 0) ; +} + +// --------------------------------------------------------------------------- +// +// handle extended and normal key input +// +// --------------------------------------------------------------------------- + +int +SalI18N_InputContext::CommitStringCallback (sal_Unicode* pText, sal_Size nLength) +{ + XIMUnicodeText call_data; + + call_data.string.utf16_char = pText; + call_data.length = nLength; + call_data.annotations = NULL; + call_data.count_annotations = 0; + call_data.feedback = NULL; + + return ::CommitStringCallback( maContext, + (XPointer)&maClientData, (XPointer)&call_data ); +} + +int +SalI18N_InputContext::CommitKeyEvent(sal_Unicode* pText, sal_Size nLength) +{ + if (nLength == 1 && IsControlCode(pText[0])) + return 0; + + if( maClientData.pFrame ) + { + SalExtTextInputEvent aTextEvent; + + aTextEvent.mnTime = 0; + aTextEvent.mpTextAttr = 0; + aTextEvent.mnCursorPos = nLength; + aTextEvent.maText = UniString(pText, nLength); + aTextEvent.mnCursorFlags = 0; + aTextEvent.mnDeltaStart = 0; + aTextEvent.mbOnlyCursor = False; + + maClientData.pFrame->CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&aTextEvent); + maClientData.pFrame->CallCallback(SALEVENT_ENDEXTTEXTINPUT, (void*)NULL); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf(stderr, "CommitKeyEvent without frame\n" ); +#endif + + return 0; +} + +int +SalI18N_InputContext::UpdateSpotLocation() +{ + if (maContext == 0 || maClientData.pFrame == NULL) + return -1; + + SalExtTextInputPosEvent aPosEvent; + maClientData.pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent); + + XPoint aSpot; + aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth; + aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight; + + XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &aSpot, NULL); + XSetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + I18NStatus::get().show( true, I18NStatus::contextmap ); + + return 0; +} + +// --------------------------------------------------------------------------- +// +// set and unset the focus for the Input Context +// the context may be NULL despite it is useable if the framewindow is +// in unmapped state +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::SetICFocus( SalFrame* pFocusFrame ) +{ + I18NStatus::get().setParent( pFocusFrame ); + if ( mbUseable && (maContext != NULL) ) + { + maClientData.pFrame = pFocusFrame; + + const SystemEnvData* pEnv = pFocusFrame->GetSystemData(); + XLIB_Window aClientWindow = pEnv->aShellWindow; + XLIB_Window aFocusWindow = pEnv->aWindow; + + XSetICValues( maContext, + XNFocusWindow, aFocusWindow, + XNClientWindow, aClientWindow, + NULL ); + + if( maClientData.aInputEv.mpTextAttr ) + { + sendEmptyCommit(pFocusFrame); + // begin preedit again + GetX11SalData()->GetDisplay()->SendInternalEvent( pFocusFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + } + + XSetICFocus( maContext ); + } +} + +void +SalI18N_InputContext::UnsetICFocus( SalFrame* pFrame ) +{ + I18NStatus& rStatus( I18NStatus::get() ); + if( rStatus.getParent() == pFrame ) + rStatus.setParent( NULL ); + + if ( mbUseable && (maContext != NULL) ) + { + // cancel an eventual event posted to begin preedit again + GetX11SalData()->GetDisplay()->CancelInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + maClientData.pFrame = NULL; + XUnsetICFocus( maContext ); + } +} + +// --------------------------------------------------------------------------- +// +// multi byte input method only +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::SetPreeditState(Bool aPreeditState) +{ + XIMPreeditState preedit_state = XIMPreeditUnKnown; + XVaNestedList preedit_attr; + + preedit_attr = XVaCreateNestedList( + 0, + XNPreeditState, &preedit_state, + NULL); + if (!XGetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL)) + { + XFree(preedit_attr); + + preedit_state = aPreeditState? XIMPreeditEnable : XIMPreeditDisable; + preedit_attr = XVaCreateNestedList( + 0, + XNPreeditState, preedit_state, + NULL); + XSetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL); + } + + XFree(preedit_attr); + + return; +} + +void +SalI18N_InputContext::SetLanguage(LanguageType) +{ + // not yet implemented + return; +} + +void +SalI18N_InputContext::EndExtTextInput( USHORT /*nFlags*/ ) +{ + if ( mbUseable && (maContext != NULL) && maClientData.pFrame ) + { + 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[0]; + if( static_cast<X11SalFrame*>(maClientData.pFrame)->hasFocus() ) + { + // begin preedit again + GetX11SalData()->GetDisplay()->SendInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + } + } + } +} + + diff --git a/vcl/unx/generic/app/i18n_im.cxx b/vcl/unx/generic/app/i18n_im.cxx new file mode 100644 index 000000000000..176212f681d5 --- /dev/null +++ b/vcl/unx/generic/app/i18n_im.cxx @@ -0,0 +1,619 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <string.h> + +#ifdef LINUX +# ifndef __USE_XOPEN +# define __USE_XOPEN +# endif +#endif +#include <poll.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <unx/XIM.h> +#include <tools/postx.h> + +#include "unx/salunx.h" +#include "unx/saldisp.hxx" +#include "unx/i18n_im.hxx" +#include "unx/i18n_status.hxx" + +#include <osl/thread.h> +#include <osl/process.h> + +using namespace vcl; +#include "unx/i18n_cb.hxx" +#if defined(SOLARIS) || defined(LINUX) +extern "C" char * XSetIMValues(XIM im, ...); +#endif + +// ------------------------------------------------------------------------------------ +// +// kinput2 IME needs special key handling since key release events are filtered in +// preeditmode and XmbResetIC does not work +// +// ------------------------------------------------------------------------------------ + +Bool +IMServerKinput2 () +{ + const static char* p_xmodifiers = getenv ("XMODIFIERS"); + const static Bool b_kinput2 = (p_xmodifiers != NULL) + && (strcmp(p_xmodifiers, "@im=kinput2") == 0); + + return b_kinput2; +} + +class XKeyEventOp : XKeyEvent +{ + private: + void init(); + + public: + XKeyEventOp(); + ~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 = 0; + 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() +{ +} + +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 == XLIB_KeyPress && rEvent.type == KeyRelease) + || (type == KeyRelease && rEvent.type == XLIB_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; + + if ( (p_outlocale = setlocale(LC_ALL, p_inlocale)) == NULL ) + { + fprintf( stderr, "I18N: Operating system doesn't support locale \"%s\"\n", + p_inlocale ); + } + + return p_outlocale; +} + +#ifdef SOLARIS +static void +SetSystemEnvironment( const rtl::OUString& rLocale ) +{ + rtl::OUString LC_ALL_Var(RTL_CONSTASCII_USTRINGPARAM("LC_ALL")); + osl_setEnvironment(LC_ALL_Var.pData, rLocale.pData); + + rtl::OUString LANG_Var(RTL_CONSTASCII_USTRINGPARAM("LANG")); + osl_setEnvironment(LANG_Var.pData, rLocale.pData); +} +#endif + +static Bool +IsPosixLocale( const char* p_locale ) +{ + if ( p_locale == NULL ) + 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 == NULL ) + return False; + + if ( !XSupportsLocale() ) + { + fprintf (stderr, "I18N: X Window System doesn't support locale \"%s\"\n", + 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#) + +Bool +SalI18N_InputMethod::SetLocale( const char* pLocale ) +{ + // check whether we want an Input Method engine, if we don't we + // do not need to set the locale + if ( mbUseable ) + { + char *locale = SetSystemLocale( pLocale ); + if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) ) + { + osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1); + locale = SetSystemLocale( "en_US" ); + #ifdef SOLARIS + SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en_US")) ); + #endif + if (! IsXWindowCompatibleLocale(locale)) + { + locale = SetSystemLocale( "C" ); + #ifdef SOLARIS + SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("C")) ); + #endif + if (! IsXWindowCompatibleLocale(locale)) + mbUseable = False; + } + } + + // must not fail if mbUseable since XSupportsLocale() asserts success + if ( mbUseable && XSetLocaleModifiers("") == NULL ) + { + fprintf (stderr, "I18N: Can't set X modifiers for locale \"%s\"\n", + locale); + mbUseable = False; + } + } + + return mbUseable; +} + +Bool +SalI18N_InputMethod::PosixLocale() +{ + if (mbMultiLingual) + return False; + if (maMethod) + return IsPosixLocale (XLocaleOfIM (maMethod)); + return False; +} + +// ------------------------------------------------------------------------ +// +// Constructor / Destructor / Initialisation +// +// ------------------------------------------------------------------------ + +SalI18N_InputMethod::SalI18N_InputMethod( ) : mbUseable( bUseInputMethodDefault ), + mbMultiLingual( False ), + maMethod( (XIM)NULL ), + mpStyles( (XIMStyles*)NULL ) +{ + const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" ); + if ( pUseInputMethod != NULL ) + mbUseable = pUseInputMethod[0] != '\0' ; +} + +SalI18N_InputMethod::~SalI18N_InputMethod() +{ + ::vcl::I18NStatus::free(); + if ( mpStyles != NULL ) + XFree( mpStyles ); + if ( maMethod != NULL ) + 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 ) + fprintf( stderr, "no input method styles\n"); + else + for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ ) + { + fprintf( stderr, "style #%i = %s\n", 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 +// + +Bool +SalI18N_InputMethod::CreateMethod ( Display *pDisplay ) +{ + if ( mbUseable ) + { + const bool bTryMultiLingual = + #ifdef LINUX + false; + #else + true; + #endif + if ( bTryMultiLingual && getenv("USE_XOPENIM") == NULL ) + { + mbMultiLingual = True; // set ml-input flag to create input-method + maMethod = XvaOpenIM(pDisplay, NULL, NULL, NULL, + XNMultiLingualInput, mbMultiLingual, /* dummy */ + (void *)0); + // get ml-input flag from input-method + if ( maMethod == (XIM)NULL ) + mbMultiLingual = False; + else + if ( XGetIMValues(maMethod, + XNMultiLingualInput, &mbMultiLingual, NULL ) != NULL ) + mbMultiLingual = False; + if( mbMultiLingual ) + { + XIMUnicodeCharacterSubsets* subsets; + if( XGetIMValues( maMethod, + XNQueryUnicodeCharacterSubset, &subsets, NULL ) == NULL ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "IM reports %d subsets: ", subsets->count_subsets ); +#endif + I18NStatus& rStatus( I18NStatus::get() ); + rStatus.clearChoices(); + for( int i = 0; i < subsets->count_subsets; i++ ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr,"\"%s\" ", subsets->supported_subsets[i].name ); +#endif + rStatus.addChoice( String( subsets->supported_subsets[i].name, RTL_TEXTENCODING_UTF8 ), &subsets->supported_subsets[i] ); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "\n" ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "query subsets failed\n" ); +#endif + } + } + else + { + maMethod = XOpenIM(pDisplay, NULL, NULL, NULL); + mbMultiLingual = False; + } + + if ((maMethod == (XIM)NULL) && (getenv("XMODIFIERS") != NULL)) + { + rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("XMODIFIERS")); + osl_clearEnvironment(envVar.pData); + XSetLocaleModifiers(""); + maMethod = XOpenIM(pDisplay, NULL, NULL, NULL); + mbMultiLingual = False; + } + + if ( maMethod != (XIM)NULL ) + { + if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, NULL) + != NULL) + mbUseable = False; + #if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "Creating %s-Lingual InputMethod\n", + mbMultiLingual ? "Multi" : "Mono" ); + PrintInputStyle( mpStyles ); + #endif + } + else + { + mbUseable = False; + } + } + + #if OSL_DEBUG_LEVEL > 1 + if ( !mbUseable ) + fprintf(stderr, "input method creation failed\n"); + #endif + + maDestroyCallback.callback = (XIMProc)IM_IMDestroyCallback; + maDestroyCallback.client_data = (XPointer)this; + if (mbUseable && maMethod != NULL) + XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, NULL); + + return mbUseable; +} + +// +// give IM the opportunity to look at the event, and possibly hide it +// + +Bool +SalI18N_InputMethod::FilterEvent( XEvent *pEvent, XLIB_Window window ) +{ + if (!mbUseable) + return False; + + Bool bFilterEvent = XFilterEvent (pEvent, window); + + if (pEvent->type != XLIB_KeyPress && pEvent->type != KeyRelease) + return bFilterEvent; + + /* + * fix broken key release handling of some IMs + */ + XKeyEvent* pKeyEvent = &(pEvent->xkey); + static XKeyEventOp maLastKeyPress; + + if (bFilterEvent) + { + if (pKeyEvent->type == KeyRelease) + bFilterEvent = !maLastKeyPress.match (*pKeyEvent); + maLastKeyPress.erase(); + } + else /* (!bFilterEvent) */ + { + if (pKeyEvent->type == XLIB_KeyPress) + maLastKeyPress = *pKeyEvent; + else + maLastKeyPress.erase(); + } + + return bFilterEvent; +} + +void +SalI18N_InputMethod::HandleDestroyIM() +{ + mbUseable = False; + mbMultiLingual = False; + maMethod = NULL; +} + +// ------------------------------------------------------------------------ +// +// add a connection watch into the SalXLib yieldTable to allow iiimp +// connection processing: soffice waits in select() not in XNextEvent(), so +// there may be requests pending on the iiimp internal connection that will +// not be processed until XNextEvent is called the next time. If we do not +// have the focus because the atok12 lookup choice aux window has it we stay +// deaf and dump otherwise. +// +// ------------------------------------------------------------------------ + +int +InputMethod_HasPendingEvent(int nFileDescriptor, void *pData) +{ + if (pData == NULL) + return 0; + + struct pollfd aFileDescriptor; + #ifdef SOLARIS + nfds_t nNumDescriptor = 1; + #else + unsigned int nNumDescriptor = 1; + #endif + aFileDescriptor.fd = nFileDescriptor; + aFileDescriptor.events = POLLRDNORM; + aFileDescriptor.revents = 0; + + int nPoll = poll (&aFileDescriptor, nNumDescriptor, 0 /* timeout */ ); + + if (nPoll > 0) + { + /* at least some conditions in revent are set */ + if ( (aFileDescriptor.revents & POLLHUP) + || (aFileDescriptor.revents & POLLERR) + || (aFileDescriptor.revents & POLLNVAL)) + return 0; /* oops error condition set */ + + if (aFileDescriptor.revents & POLLRDNORM) + return 1; /* success */ + } + + /* nPoll == 0 means timeout, nPoll < 0 means error */ + return 0; +} + +int +InputMethod_IsEventQueued(int nFileDescriptor, void *pData) +{ + return InputMethod_HasPendingEvent (nFileDescriptor, pData); +} + +int +InputMethod_HandleNextEvent(int nFileDescriptor, void *pData) +{ + if (pData != NULL) + XProcessInternalConnection((Display*)pData, nFileDescriptor); + + return 0; +} + +extern "C" void +InputMethod_ConnectionWatchProc (Display *pDisplay, XPointer pClientData, + int nFileDescriptor, Bool bOpening, XPointer*) +{ + SalXLib *pConnectionHandler = (SalXLib*)pClientData; + + if (pConnectionHandler == NULL) + return; + + if (bOpening) + { + pConnectionHandler->Insert (nFileDescriptor, pDisplay, + InputMethod_HasPendingEvent, + InputMethod_IsEventQueued, + InputMethod_HandleNextEvent); + } + else + { + pConnectionHandler->Remove (nFileDescriptor); + } +} + +Bool +SalI18N_InputMethod::AddConnectionWatch(Display *pDisplay, void *pConnectionHandler) +{ + // sanity check + if (pDisplay == NULL || pConnectionHandler == NULL) + return False; + + // if we are not ml all the extended text input comes on the stock X queue, + // so there is no need to monitor additional file descriptors. +#ifndef SOLARIS + if (!mbMultiLingual || !mbUseable) + return False; +#endif + + // pConnectionHandler must be really a pointer to a SalXLib + Status nStatus = XAddConnectionWatch (pDisplay, InputMethod_ConnectionWatchProc, + (XPointer)pConnectionHandler); + return (Bool)nStatus; +} + + + diff --git a/vcl/unx/generic/app/i18n_keysym.cxx b/vcl/unx/generic/app/i18n_keysym.cxx new file mode 100644 index 000000000000..122a88517baf --- /dev/null +++ b/vcl/unx/generic/app/i18n_keysym.cxx @@ -0,0 +1,365 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#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 + +typedef const sal_Unicode unicode_t; +typedef struct { + const int first; const int last; + unicode_t *map; +} keymap_t; + +// Latin-1 Byte 3 = 0x00 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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 +unicode_t 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* 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, (keymap_t*)NULL, /* 12 -- 15 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, &keymap19, /* 16 -- 19 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 20 -- 23 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 24 -- 27 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 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 (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 = NULL; + if (n_byte3 < INITIAL_KEYMAPS) + p_map = p_keymap[n_byte3]; + else + if (n_byte3 == 255) + p_map = &keymap255; + + if ((p_map != NULL) && (n_byte4 >= p_map->first) && (n_byte4 <= p_map->last) ) + return p_map->map[n_byte4 - p_map->first]; + } + + return 0; +} diff --git a/vcl/unx/generic/app/i18n_status.cxx b/vcl/unx/generic/app/i18n_status.cxx new file mode 100644 index 000000000000..76193f0ef842 --- /dev/null +++ b/vcl/unx/generic/app/i18n_status.cxx @@ -0,0 +1,733 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif +#include <sal/alloca.h> + +#include <tools/prex.h> +#include <X11/Xlib.h> +#include <unx/XIM.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/i18n_status.hxx> +#include <unx/i18n_ic.hxx> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/saldata.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/fixed.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> +#include <vcl/sysdata.hxx> + +#include <svdata.hxx> + +using namespace vcl; +using namespace rtl; + +namespace vcl { + +class StatusWindow : public WorkWindow +{ +protected: + StatusWindow( WinBits nWinBits ); +public: + virtual ~StatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ) = 0; + virtual String getText() const = 0; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ) = 0; + virtual void toggle( bool bOn ) = 0; +}; + +} + +StatusWindow::StatusWindow( WinBits nWinBits ) : + WorkWindow( NULL, nWinBits ) +{ +} + +StatusWindow::~StatusWindow() {} + +void StatusWindow::setPosition( SalFrame* ) +{ +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class XIMStatusWindow : public StatusWindow +{ + FixedText m_aStatusText; + SalFrame* m_pLastParent; + Size m_aWindowSize; + bool m_bAnchoredAtRight; + // true if the right edge (instead of the left edge) should stay at a + // fixed position when re-sizing the window + + // for delayed showing + bool m_bDelayedShow; + I18NStatus::ShowReason m_eDelayedReason; + ULONG m_nDelayedEvent; + // for toggling + bool m_bOn; + + Point updatePosition(); + void layout(); + bool checkLastParent() const; + + DECL_LINK( DelayedShowHdl, void* ); +public: + XIMStatusWindow( bool bOn ); + virtual ~XIMStatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +XIMStatusWindow::XIMStatusWindow( bool bOn ) : + StatusWindow( WB_BORDER | WB_SYSTEMFLOATWIN | WB_TOOLTIPWIN ), + m_aStatusText( this, 0 ), + m_pLastParent( NULL ), + m_bAnchoredAtRight( false ), + m_bDelayedShow( false ), + m_eDelayedReason( I18NStatus::contextmap ), + m_nDelayedEvent( 0 ), + m_bOn( bOn ) +{ + layout(); +} + +XIMStatusWindow::~XIMStatusWindow() +{ + if( m_nDelayedEvent ) + Application::RemoveUserEvent( m_nDelayedEvent ); +} + +void XIMStatusWindow::toggle( bool bOn ) +{ + m_bOn = bOn; + show( bOn, I18NStatus::contextmap ); +} + +void XIMStatusWindow::layout() +{ + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( m_aStatusText.GetText() )+8; + Font aFont( m_aStatusText.GetFont() ); + m_aWindowSize.Height() = aFont.GetHeight()+10; + m_aWindowSize = LogicToPixel( m_aWindowSize ); + + Size aControlSize( m_aWindowSize ); + aControlSize.Width() -= 4; + aControlSize.Height() -= 4; + + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + m_aStatusText.SetFont( aFont ); + m_aStatusText.Show( TRUE ); + + if (m_bAnchoredAtRight && IsVisible()) + { + SalFrame* pFrame = (SalFrame*)GetSystemData()->pSalFrame; + long nDelta = pFrame->maGeometry.nWidth - m_aWindowSize.Width(); + pFrame->SetPosSize( pFrame->maGeometry.nX + nDelta, + pFrame->maGeometry.nY, + m_aWindowSize.Width(), + m_aWindowSize.Height(), + SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + else + SetOutputSizePixel( m_aWindowSize ); +} + +bool XIMStatusWindow::checkLastParent() const +{ + if( m_pLastParent ) + { + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + if( *it == m_pLastParent ) + return true; + } + } + return false; +} + +void XIMStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusText.SetSettings( GetSettings() ); + layout(); +} + +Point XIMStatusWindow::updatePosition() +{ + Point aRet; + if( checkLastParent() ) + { + const SystemEnvData* pParentEnvData = m_pLastParent->GetSystemData(); + + SalExtTextInputPosEvent aPosEvent; + m_pLastParent->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + int x, y; + XLIB_Window aChild; + XTranslateCoordinates( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + GetX11SalData()->GetDisplay()->GetRootWindow( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ), + 0, 0, + &x, &y, + &aChild ); + + // TODO: Currently, place the status window to the (physical) left of + // the cursor iff in vertical mode (assuming that the columns in + // vertical mode are always written from right to left, this causes the + // status window to keep out of the text already written). This + // heuristic would break if there is ever a vertical mode in which the + // columns are written from left to right. Also, more elaborate + // positioning for (both horizontal and vertical) left-to-right and + // right-to-left text would be possible. + bool bLeft = aPosEvent.mbVertical; + // true if status window is to the left of the cursor + + int const nGap = 4; // between cursor and status window + if (aPosEvent.mbVertical) + { + aRet.X() = x + aPosEvent.mnX + (bLeft + ? -m_aWindowSize.Width() - nGap + : aPosEvent.mnHeight + nGap); + aRet.Y() = y + aPosEvent.mnY; + } + else + { + aRet.X() = x + aPosEvent.mnX + (bLeft ? -m_aWindowSize.Width() : 0); + aRet.Y() = y + aPosEvent.mnY+aPosEvent.mnHeight + nGap; + } + + m_bAnchoredAtRight = bLeft; + } + return aRet; +} + +void XIMStatusWindow::setPosition( SalFrame* pParent ) +{ + if( pParent ) + { + if( pParent != m_pLastParent ) + { + setText( String() ); + m_pLastParent = pParent; + Show( FALSE, SHOW_NOACTIVATE ); + } + if( IsVisible() ) + { + const SystemEnvData* pEnvData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pEnvData->pSalFrame; + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + } +} + +IMPL_LINK( XIMStatusWindow, DelayedShowHdl, void*, EMPTYARG ) +{ + m_nDelayedEvent = 0; + const SystemEnvData* pData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pData->pSalFrame; + if( m_bDelayedShow ) + { + Size aControlSize( m_aWindowSize.Width()-4, m_aWindowSize.Height()-4 ); + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + Show( m_bDelayedShow && m_bOn, SHOW_NOACTIVATE ); + if( m_bDelayedShow ) + { + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } + return 0; +} + +void XIMStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + if( bShow && ! m_aStatusText.GetText().Len() ) + bShow = false; + + m_bDelayedShow = bShow; + m_eDelayedReason = eReason; + if( ! m_nDelayedEvent ) + m_nDelayedEvent = Application::PostUserEvent( LINK( this, XIMStatusWindow, DelayedShowHdl ) ); +} + +void XIMStatusWindow::setText( const String& rText ) +{ + m_aStatusText.SetText( rText ); + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( rText )+8; +} + +String XIMStatusWindow::getText() const +{ + return m_aStatusText.GetText(); +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class IIIMPStatusWindow : public StatusWindow +{ + MenuButton m_aStatusBtn; + PopupMenu m_aMenu; + SalFrame* m_pResetFocus; + bool m_bShow; + bool m_bOn; + + DECL_LINK( SelectHdl, MenuButton* ); + + void show(); + +public: + IIIMPStatusWindow( SalFrame* pParent, bool bOn ); // for initial position + virtual ~IIIMPStatusWindow(); + + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + void layout(); + + // overload Window focus handler + virtual void GetFocus(); + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +IIIMPStatusWindow::IIIMPStatusWindow( SalFrame* pParent, bool bOn ) : + StatusWindow( WB_MOVEABLE ), + m_aStatusBtn( this, WB_BORDER ), + m_pResetFocus( pParent ), + m_bShow( true ), + m_bOn( bOn ) +{ + SetText( String( RTL_CONSTASCII_USTRINGPARAM( "IME Status" ) ) ); + + layout(); + + m_aStatusBtn.SetSelectHdl( LINK( this, IIIMPStatusWindow, SelectHdl ) ); + m_aStatusBtn.SetPopupMenu( &m_aMenu ); + m_aStatusBtn.Show( TRUE ); + + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + int i = 1; + for( ::std::vector< I18NStatus::ChoiceData >::const_iterator it = rChoices.begin(); it != rChoices.end(); ++it, i++ ) + m_aMenu.InsertItem( i, it->aString ); + + if( pParent ) + { + const SystemEnvData* pEnvData = GetSystemData(); + + const SalFrameGeometry& rGeom( pParent->GetUnmirroredGeometry() ); + int nDistance = rGeom.nTopDecoration; + if( nDistance < 20 ) + nDistance = 20; + XMoveWindow( (Display*)pEnvData->pDisplay, + (XLIB_Window)pEnvData->aShellWindow, + rGeom.nX, + rGeom.nY + rGeom.nHeight + nDistance + ); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "Warning: could not reposition status window since no frame\n" ); +#endif + EnableAlwaysOnTop( TRUE ); +} + +IIIMPStatusWindow::~IIIMPStatusWindow() +{ +} + +void IIIMPStatusWindow::layout() +{ + Font aFont( m_aStatusBtn.GetFont() ); + Size aSize( 15*aFont.GetHeight(), aFont.GetHeight()+14 ); + aSize = m_aStatusBtn.LogicToPixel( aSize ); + + m_aStatusBtn.SetPosSizePixel( Point( 0, 0 ), aSize ); + SetOutputSizePixel( aSize ); + if( IsVisible() ) + Invalidate(); +} + +void IIIMPStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusBtn.SetSettings( GetSettings() ); + layout(); +} + +void IIIMPStatusWindow::setText( const String& rText ) +{ + m_aStatusBtn.SetText( rText ); +} + +String IIIMPStatusWindow::getText() const +{ + return m_aStatusBtn.GetText(); +} + +void IIIMPStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + // hide IIIMPStatusWindow only in presentations + if( ! bShow + && eReason != I18NStatus::presentation + ) + return; + + m_bShow = bShow; + show(); +} + +void IIIMPStatusWindow::toggle( bool bOn ) +{ + if (bOn != m_bOn) + { + m_bOn = bOn; + show(); + } +} + +void IIIMPStatusWindow::show() +{ + if (m_bOn && m_bShow && !IsVisible()) + m_pResetFocus = I18NStatus::get().getParent(); + Show(m_bOn && m_bShow); +} + +void IIIMPStatusWindow::GetFocus() +{ + /* + * this is here just to put the focus back to the application + * window at startup on clickToFocus WMs + */ + WorkWindow::GetFocus(); + if( m_pResetFocus ) + { + /* + * look if reset focus still exists + * since reset focus really is an internal hack there should + * not be a method to be called in SalFrame destructor + */ + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it; + for( it = rFrames.begin(); it != rFrames.end() && *it != m_pResetFocus; ++it ) + ; + if( it != rFrames.end() ) + { + const SystemEnvData* pParentEnvData = m_pResetFocus->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pParentEnvData->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + m_pResetFocus = NULL; + } +} + +// -------------------------------------------------------------------------- + +IMPL_LINK( IIIMPStatusWindow, SelectHdl, MenuButton*, pBtn ) +{ + if( pBtn == & m_aStatusBtn ) + { + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + unsigned int nIndex = m_aStatusBtn.GetCurItemId()-1; + if( nIndex < rChoices.size() ) + { + XSetICValues( static_cast<X11SalFrame*>(I18NStatus::get().getParent())->getInputContext()->GetContext(), + XNUnicodeCharacterSubset, + rChoices[nIndex].pData, + NULL); + // FIXME: get rid of X11SalFrame + X11SalFrame* pParent = static_cast<X11SalFrame*>(I18NStatus::get().getParent()); + if( pParent && pParent->isMapped() ) + { + const SystemEnvData* pEnv = pParent->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pEnv->pDisplay, + (XLIB_Window)pEnv->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pEnv->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + } + } + return 0; +} + +/* + * I18NStatus + */ + +I18NStatus* I18NStatus::pInstance = NULL; + +I18NStatus& I18NStatus::get() +{ + if( ! pInstance ) + pInstance = new I18NStatus(); + return *pInstance; +} + +// -------------------------------------------------------------------------- + +bool I18NStatus::exists() +{ + return pInstance != NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::free() +{ + if( pInstance ) + delete pInstance, pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +I18NStatus::I18NStatus() : + m_pParent( NULL ), + m_pStatusWindow( NULL ) +{ +} + +// -------------------------------------------------------------------------- + +I18NStatus::~I18NStatus() +{ + if( m_pStatusWindow ) + delete m_pStatusWindow, m_pStatusWindow = NULL; + if( pInstance == this ) + pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setParent( SalFrame* pParent ) +{ + m_pParent = pParent; + if( ! m_pStatusWindow ) + { + bool bIIIMPmode = m_aChoices.begin() != m_aChoices.end(); + if( bIIIMPmode ) + m_pStatusWindow = new IIIMPStatusWindow( pParent, + getStatusWindowMode() ); + else + m_pStatusWindow = new XIMStatusWindow( getStatusWindowMode() ); + setStatusText( m_aCurrentIM ); + } + m_pStatusWindow->setPosition( m_pParent ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::show( bool bShow, ShowReason eReason ) +{ + if( m_pStatusWindow ) + { + m_pStatusWindow->setPosition( m_pParent ); + m_pStatusWindow->show( bShow, eReason ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setStatusText( const String& rText ) +{ + if( m_pStatusWindow ) + { + /* + * #93614# convert fullwidth ASCII forms to ascii + */ + int nChars = rText.Len()+1; + sal_Unicode* pBuffer = (sal_Unicode*)alloca( nChars*sizeof( sal_Unicode ) ); + const sal_Unicode* pCopy = rText.GetBuffer(); + for( int i = 0; i < nChars; i++ ) + { + if( pCopy[i] >=0xff00 && pCopy[i] <= 0xff5f ) + pBuffer[i] = (pCopy[i] & 0xff) + 0x20; + else + pBuffer[i] = pCopy[i]; + } + String aText( pBuffer ); + m_pStatusWindow->setText( aText ); + m_pStatusWindow->setPosition( m_pParent ); + + bool bVisible = true; + if( m_pParent ) + { + long w, h; + m_pParent->GetClientSize( w, h ); + if( w == 0 || h == 0 ) + { + bVisible = false; + } + } + + m_pStatusWindow->show( bVisible, contextmap ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::changeIM( const String& rIM ) +{ + m_aCurrentIM = rIM; +} + +// -------------------------------------------------------------------------- + +String I18NStatus::getStatusText() const +{ + return m_pStatusWindow ? m_pStatusWindow->getText() : String(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::clearChoices() +{ + m_aChoices.clear(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::addChoice( const String& rChoice, void* pData ) +{ + ChoiceData aData; + aData.pData = pData; + aData.aString = rChoice; + m_aChoices.push_back( aData ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::toTop() const +{ + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } +} + +// -------------------------------------------------------------------------- + +SalFrame* I18NStatus::getStatusFrame() const +{ + SalFrame* pRet = NULL; + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + pRet = (SalFrame*)pData->pSalFrame; + } + return pRet; +} + +bool I18NStatus::canToggleStatusWindow() const +{ + return true; +} + +void I18NStatus::toggleStatusWindow() +{ + if (m_pStatusWindow != 0) + m_pStatusWindow->toggle(getStatusWindowMode()); +} + +bool I18NStatus::getStatusWindowMode() +{ + switch (ImplGetSVData()->maAppData.meShowImeStatusWindow) + { + default: // ImplSVAppData::ImeStatusWindowMode_UNKNOWN + return Application::GetShowImeStatusWindowDefault(); + case ImplSVAppData::ImeStatusWindowMode_HIDE: + return false; + case ImplSVAppData::ImeStatusWindowMode_SHOW: + return true; + } +} + +/* + * X11ImeStatus + */ +X11ImeStatus::~X11ImeStatus() +{ + vcl::I18NStatus::free(); +} + +bool X11ImeStatus::canToggle() +{ + return vcl::I18NStatus::get().canToggleStatusWindow(); +} + +void X11ImeStatus::toggle() +{ + vcl::I18NStatus::get().toggleStatusWindow(); +} + +SalI18NImeStatus* X11SalInstance::CreateI18NImeStatus() +{ + return new X11ImeStatus(); +} diff --git a/vcl/unx/generic/app/i18n_wrp.cxx b/vcl/unx/generic/app/i18n_wrp.cxx new file mode 100644 index 000000000000..ff56f0ed0647 --- /dev/null +++ b/vcl/unx/generic/app/i18n_wrp.cxx @@ -0,0 +1,260 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +struct XIMArg +{ + char *name; + char *value; +}; + +#if defined(SOLARIS) && !defined(__GNUC__) +#include <varargs.h> +#else +#include <stdarg.h> +#endif +#include <sal/alloca.h> + +#include <string.h> +#include <dlfcn.h> + +#include <X11/Xlib.h> +#include <X11/Xlibint.h> +#include "unx/XIM.h" + +#define XIIIMP_LIB "xiiimp.so.2" + +#ifdef SOLARIS +#define XIIIMP_PATH "/usr/openwin/lib/locale/common/" XIIIMP_LIB +#else /* Linux */ +#define XIIIMP_PATH "/usr/lib/im/" XIIIMP_LIB +#endif + +extern "C" { +typedef XIM (*OpenFunction)(Display*, XrmDatabase, char*, char*, XIMArg*); +} + +/* global variables */ +static void *g_dlmodule = 0; +static OpenFunction g_open_im = (OpenFunction)NULL; + +/* utility function to transform vararg list into an array of XIMArg */ + +int +XvaCountArgs( XIMArg *pInArgs ) +{ + int nArgs = 0; + char *pName, *pValue; + + while ( (pName = pInArgs->name) != NULL ) + { + pValue = pInArgs->value; + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + nArgs += XvaCountArgs( (XIMArg*)pValue ); + } + else + { + nArgs += 1; + } + pInArgs++; + } + + return nArgs; +} + +int +XvaCountArgs( va_list pInArgs ) +{ + int nArgs = 0; + char *pName, *pValue; + + while ( (pName = va_arg(pInArgs, char*)) != NULL) + { + pValue = va_arg(pInArgs, char*); + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + nArgs += XvaCountArgs( (XIMArg*)pValue ); + } + else + { + nArgs += 1; + } + } + + return nArgs; +} + +XIMArg* +XvaGetArgs( XIMArg *pInArgs, XIMArg *pOutArgs ) +{ + char *pName, *pValue; + + while ( (pName = pInArgs->name) != NULL ) + { + pValue = pInArgs->value; + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + pOutArgs = XvaGetArgs( (XIMArg*)pValue, pOutArgs ); + } + else + { + pOutArgs->name = pName; + pOutArgs->value = pValue; + pOutArgs++; + } + pInArgs++; + } + + return pOutArgs; +} + +void +XvaGetArgs( va_list pInArgs, XIMArg *pOutArgs ) +{ + char *pName, *pValue; + + while ((pName = va_arg(pInArgs, char*)) != NULL) + { + pValue = va_arg(pInArgs, char*); + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + pOutArgs = XvaGetArgs( (XIMArg*)pValue, pOutArgs ); + } + else + { + pOutArgs->name = pName; + pOutArgs->value = pValue; + pOutArgs++; + } + } + + pOutArgs->name = NULL; + pOutArgs->value = NULL; +} + + +/* Puplic functions */ + +#ifdef __cplusplus +extern "C" +#endif +XIM +XvaOpenIM(Display *display, XrmDatabase rdb, + char *res_name, char *res_class, ...) +{ + XIM xim = (XIM)0; + va_list variable; + int total_count = 0; + + /* + * so count the stuff dangling here + */ + +#if defined(SOLARIS) && !defined(__GNUC__) + va_start(variable); +#else + va_start(variable, res_class); +#endif + total_count = XvaCountArgs(variable); + va_end(variable); + + if (total_count > 0) + { + /* call a new open IM method */ + + XIMArg* args = (XIMArg*)alloca( (total_count + 1) * sizeof(XIMArg) ); + + /* + * now package it up so we can set it along + */ +#if defined(SOLARIS) && !defined(__GNUC__) + va_start(variable); +#else + va_start(variable, res_class); +#endif + XvaGetArgs( variable, args ); + va_end(variable); + + if (!g_dlmodule) + { + g_dlmodule = dlopen(XIIIMP_LIB, RTLD_LAZY); + if(!g_dlmodule) + { + g_dlmodule = dlopen(XIIIMP_PATH, RTLD_LAZY); + if (!g_dlmodule) + goto legacy_XIM; + } + g_open_im = (OpenFunction)(long)dlsym(g_dlmodule, "__XOpenIM"); + if (!g_open_im) + goto legacy_XIM; + + xim = (*g_open_im)(display, (XrmDatabase)rdb, + (char*)res_name, (char *)res_class, (XIMArg*)args); + } + else + { + goto legacy_XIM; + } + } + +// in #if to prevent warning "warning: label 'legacy_XIM' defined but not used" + legacy_XIM: + + if (!xim) + xim = XOpenIM(display, rdb, res_name, res_class); + + return xim; +} + +/* + * Close the connection to the input manager, and free the XIM structure + */ + +Status XvaCloseIM(XIM) +{ + Status s = False; + + if (!g_dlmodule) + { + /* assuming one XvaOpenIM call */ + dlclose(g_dlmodule); + g_dlmodule = (void*)0; + g_open_im = (OpenFunction)NULL; + s = True; + } + return (s); +} + + + diff --git a/vcl/unx/generic/app/i18n_xkb.cxx b/vcl/unx/generic/app/i18n_xkb.cxx new file mode 100644 index 000000000000..5587bbf02339 --- /dev/null +++ b/vcl/unx/generic/app/i18n_xkb.cxx @@ -0,0 +1,163 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + + +#include <stdio.h> + +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/i18n_xkb.hxx" + +SalI18N_KeyboardExtension::SalI18N_KeyboardExtension( Display* +#if __XKeyboardExtension__ +pDisplay +#endif +) + : mbUseExtension( (sal_Bool)__XKeyboardExtension__ ), + mnDefaultGroup( 0 ) +{ + #if __XKeyboardExtension__ + + mpDisplay = pDisplay; + + // 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 != NULL ) + { + mbUseExtension = pUseKeyboardExtension[0] != '\0' ; + if ( mbUseExtension ) + mnDefaultGroup = strtol( pUseKeyboardExtension, NULL, 0 ); + if ( mnDefaultGroup > XkbMaxKbdGroup ) + mnDefaultGroup = 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; + + mbUseExtension = (sal_Bool)XkbQueryExtension( mpDisplay, + &nMajorExtOpcode, (int*)&mnEventBase, (int*)&mnErrorBase, + &nExtMajorVersion, &nExtMinorVersion ); + } + + // query notification for changes of the keyboard group + if ( mbUseExtension ) + { + #define XkbGroupMask ( XkbGroupStateMask | XkbGroupBaseMask \ + | XkbGroupLatchMask | XkbGroupLockMask ) + + mbUseExtension = XkbSelectEventDetails( mpDisplay, + XkbUseCoreKbd, XkbStateNotify, XkbGroupMask, XkbGroupMask ); + } + + // query initial keyboard group + if ( mbUseExtension ) + { + XkbStateRec aStateRecord; + XkbGetState( mpDisplay, XkbUseCoreKbd, &aStateRecord ); + mnGroup = aStateRecord.group; + } + + #endif // __XKeyboardExtension__ +} + +void +SalI18N_KeyboardExtension::Dispatch( XEvent* +#if __XKeyboardExtension__ +pEvent +#endif +) +{ + #if __XKeyboardExtension__ + + // 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 = ((XkbAnyEvent*)pEvent)->xkb_type; + switch ( nXKBType ) + { + case XkbStateNotify: + + mnGroup = ((XkbStateNotifyEvent*)pEvent)->group; + break; + + default: + + #if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "Got unrequested XkbAnyEvent %#x/%i\n", + static_cast<unsigned int>(nXKBType), static_cast<int>(nXKBType) ); + #endif + break; + } + #endif // __XKeyboardExtension__ +} + +#if __XKeyboardExtension__ +sal_uInt32 +SalI18N_KeyboardExtension::LookupKeysymInGroup( sal_uInt32 nKeyCode, + sal_uInt32 nShiftState, + sal_uInt32 nGroup ) const +#else +sal_uInt32 +SalI18N_KeyboardExtension::LookupKeysymInGroup( sal_uInt32,sal_uInt32,sal_uInt32 ) const +#endif +{ + #if __XKeyboardExtension__ + + if ( !mbUseExtension ) + return NoSymbol; + + nShiftState &= ShiftMask; + + KeySym nKeySymbol; + nKeySymbol = XkbKeycodeToKeysym( mpDisplay, nKeyCode, nGroup, nShiftState ); + return nKeySymbol; + + #else + + return NoSymbol; + + #endif // __XKeyboardExtension__ +} + + diff --git a/vcl/unx/generic/app/keysymnames.cxx b/vcl/unx/generic/app/keysymnames.cxx new file mode 100644 index 000000000000..45a07ac66987 --- /dev/null +++ b/vcl/unx/generic/app/keysymnames.cxx @@ -0,0 +1,688 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#ifndef SOLARIS +#include <tools/prex.h> +#include <X11/XKBlib.h> +#include <tools/postx.h> +#endif + +#include <unx/saldisp.hxx> +#include <X11/keysym.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 + +#ifdef SOLARIS +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/kbio.h> +#include <sys/kbd.h> +#include <stdio.h> +#include <fcntl.h> +#include <deflt.h> +#include <unistd.h> +#include <stdlib.h> +#endif + +#include <string.h> + +namespace vcl_sal { + + struct KeysymNameReplacement + { + KeySym aSymbol; + const char* pName; + }; + + struct KeyboardReplacements + { + const char* pKeyboardName; + const KeysymNameReplacement* pReplacements; + int nReplacements; + }; + + // ==================================================================== + // + // CAUTION CAUTION CAUTION + // every string value in the replacements tables must be in UTF8 + // be careful with your editor ! + // + // ==================================================================== + + static const struct KeysymNameReplacement aImplReplacements_English[] = + { + { XK_Control_L, "Ctrl" }, + { XK_Control_R, "Ctrl" }, + { XK_Escape, "Esc" }, + { XK_space, "Space" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Turkish[] = + { + { XK_Control_L, "Ctrl" }, + { XK_Control_R, "Ctrl" }, + { XK_Right, "Sağ" }, + { XK_Left, "Sol" }, + { XK_Up, "Yukarı" }, + { XK_Down, "Aşağı" }, + { XK_space, "Boşluk" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Russian[] = + { + { XK_Right, "Вправо" }, + { XK_Left, "Влево" }, + { XK_Up, "Вверх" }, + { XK_Down, "Вниз" }, + { XK_space, "Пробел" } + }; + + static 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ückschritt" }, + { XK_Return, "Eingabe" }, + { XK_slash, "Schrägstrich" }, + { XK_space, "Leertaste" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Wiederholen" }, + { SunXK_Props, "Eigenschaften" }, + { SunXK_Undo, "Zurücknehmen" }, + { SunXK_Front, "Vordergrund" }, + { SunXK_Copy, "Kopieren" }, + { SunXK_Open, "Öffnen" }, + { SunXK_Paste, "Einsetzen" }, + { SunXK_Find, "Suchen" }, + { SunXK_Cut, "Ausschneiden" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_French[] = + { + { XK_Shift_L, "Maj" }, + { XK_Shift_R, "Maj" }, + { XK_Page_Up, "Pg. Préc" }, + { XK_Page_Down, "Pg. Suiv" }, + { XK_End, "Fin" }, + { XK_Home, "Origine" }, + { XK_Insert, "Insérer" }, + { 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_KP_Enter, "Entrée" }, + { 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" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static 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à " }, + { SunXK_Undo, "Annulla" }, + { SunXK_Front, "Davanti" }, + { SunXK_Copy, "Copia" }, + { SunXK_Open, "Apri" }, + { SunXK_Paste, "Incolla" }, + { SunXK_Find, "Trova" }, + { SunXK_Cut, "Taglia" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static 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" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static 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, "Hyre" }, + { 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, "Åpne" }, + { SunXK_Paste, "Lim" }, + { SunXK_Find, "Søk" }, + { SunXK_Cut, "Klipp" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static 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öger" }, + { XK_Left, "Vänster" }, + { 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, "Ångra" }, + { SunXK_Front, "Fram" }, + { SunXK_Copy, "Kopiera" }, + { SunXK_Open, "Öppna" }, + { SunXK_Paste, "Klistra in" }, + { SunXK_Find, "Sök" }, + { SunXK_Cut, "Klipp ut" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static 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" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Spanish[] = + { + { XK_Shift_L, "Mayús" }, + { XK_Shift_R, "Mayús" }, + { XK_Page_Up, "RePág" }, + { XK_Page_Down, "AvPág" }, + { XK_End, "Fin" }, + { XK_Home, "Inicio" }, + { XK_Delete, "Supr" }, + { XK_Escape, "Esc" }, + { XK_Right, "Hacia la derecha" }, + { XK_Left, "Hacia la izquierda" }, + { XK_Up, "Hacia arriba" }, + { XK_Down, "Hacia abajo" }, + { XK_BackSpace, "Ret" }, + { XK_Return, "Entrada" }, + { XK_space, "Espacio" }, + { XK_KP_Enter, "Intro" }, + { SunXK_Stop, "Stop" }, + { 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" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeyboardReplacements aKeyboards[] = + { +#ifdef SOLARIS + { "Germany5", aImplReplacements_German, sizeof(aImplReplacements_German)/sizeof(aImplReplacements_German[0]) }, + { "Germany4", aImplReplacements_German, sizeof(aImplReplacements_German)/sizeof(aImplReplacements_German[0]) }, + { "France5", aImplReplacements_French, sizeof(aImplReplacements_French)/sizeof(aImplReplacements_French[0]) }, + { "France6", aImplReplacements_French, sizeof(aImplReplacements_French)/sizeof(aImplReplacements_French[0]) }, + { "France_x86", aImplReplacements_French, sizeof(aImplReplacements_French)/sizeof(aImplReplacements_French[0]) }, + { "Italy5", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + { "Italy5-Hobo", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + { "Italy4", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + { "Italy6", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + { "Italy_x86", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + { "Netherland4", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Netherland5", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Netherland5-Hobo", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Netherland6", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Netherland_x86", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Norway5", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + { "Norway5-Hobo", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + { "Norway4", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + { "Norway6", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + { "Norway_x86", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + { "Portugal5", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Portugal5-Hobo", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Portugal4", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Portugal6", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Portugal_x86", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Spain5", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Spain5-Hobo", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Spain4", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Spain6", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Spain_x86", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Sweden5", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, + { "Sweden5-Hobo", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, + { "Sweden4", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, + { "Sweden6", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, + { "Sweden_x86", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, +#endif + { "U.S. English", aImplReplacements_English, sizeof(aImplReplacements_English)/sizeof(aImplReplacements_English[0]) }, + { "United Kingdom", aImplReplacements_English, sizeof(aImplReplacements_English)/sizeof(aImplReplacements_English[0]) }, + // Germany, German + { "German", aImplReplacements_German, sizeof(aImplReplacements_German)/sizeof(aImplReplacements_German[0]) }, + { "France", aImplReplacements_French, sizeof(aImplReplacements_French)/sizeof(aImplReplacements_French[0]) }, + { "French", aImplReplacements_French, sizeof(aImplReplacements_French)/sizeof(aImplReplacements_French[0]) }, + // Italy, Italian + { "Ital", aImplReplacements_Italian, sizeof(aImplReplacements_Italian)/sizeof(aImplReplacements_Italian[0]) }, + // Norway, Norwegian + { "Norw", aImplReplacements_Norwegian, sizeof(aImplReplacements_Norwegian)/sizeof(aImplReplacements_Norwegian[0]) }, + // Portugal, Portuguese + { "Portu", aImplReplacements_Portuguese, sizeof(aImplReplacements_Portuguese)/sizeof(aImplReplacements_Portuguese[0]) }, + { "Spain", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + { "Spanish", aImplReplacements_Spanish, sizeof(aImplReplacements_Spanish)/sizeof(aImplReplacements_Spanish[0]) }, + // Sweden, Swedish + { "Swed", aImplReplacements_Swedish, sizeof(aImplReplacements_Swedish)/sizeof(aImplReplacements_Swedish[0]) }, + { "Netherland", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + { "Dutch", aImplReplacements_Dutch, sizeof(aImplReplacements_Dutch)/sizeof(aImplReplacements_Dutch[0]) }, + // Turkish, Turkey + { "Turk", aImplReplacements_Turkish, sizeof(aImplReplacements_Turkish)/sizeof(aImplReplacements_Turkish[0]) }, + // Russian, Russia + { "Russia", aImplReplacements_Russian, sizeof(aImplReplacements_Russian)/sizeof(aImplReplacements_Russian[0]) }, + { "English", aImplReplacements_English, sizeof(aImplReplacements_English)/sizeof(aImplReplacements_English[0]) } + }; + + String getKeysymReplacementName( const char* pKeyboard, KeySym nSymbol ) + { + for( unsigned int n = 0; n < sizeof(aKeyboards)/sizeof(aKeyboards[0]); n++ ) + { + if( ! strncasecmp( pKeyboard, aKeyboards[n].pKeyboardName, strlen( aKeyboards[n].pKeyboardName ) ) ) + { + const struct KeysymNameReplacement* pRepl = aKeyboards[n].pReplacements; + for( int m = aKeyboards[n].nReplacements ; m ; ) + { + if( nSymbol == pRepl[--m].aSymbol ) + return String( pRepl[m].pName, RTL_TEXTENCODING_UTF8 ); + } + } + } + // try english fallbacks + const struct KeysymNameReplacement* pRepl = aImplReplacements_English; + for( int m = sizeof(aImplReplacements_English)/sizeof(aImplReplacements_English[0]) ; m ; ) + { + if( nSymbol == pRepl[--m].aSymbol ) + return String( pRepl[m].pName, RTL_TEXTENCODING_UTF8 ); + } + return String(); + } + +} + +#ifdef SOLARIS +typedef struct { + int n_layout; + const char* p_description; +} keyboard_layout; + +static const keyboard_layout type0_layout[] = +{ + { 0, "US4" }, + { -1, NULL } +}; + +static const keyboard_layout type3_layout[] = +{ + { 0, "US3" }, + { -1, NULL } +}; + +static const keyboard_layout type4_layout[] = +{ + { 0, "US4" }, + { 1, "US4" }, + { 2, "FranceBelg4" }, + { 3, "Canada4" }, + { 4, "Denmark4" }, + { 5, "Germany4" }, + { 6, "Italy4" }, + { 7, "Netherland4" }, + { 8, "Norway4" }, + { 9, "Portugal4" }, + { 10, "SpainLatAm4" }, + { 11, "SwedenFin4" }, + { 12, "Switzer_Fr4" }, + { 13, "Switzer_Ge4" }, + { 14, "UK4" }, + { 16, "Korea4" }, + { 17, "Taiwan4" }, + { 19, "US101A_PC" }, + { 19, "US101A_Sun" }, + { 32, "Japan4" }, + { 33, "US5" }, + { 34, "US_UNIX5" }, + { 35, "France5" }, + { 36, "Denmark5" }, + { 37, "Germany5" }, + { 38, "Italy5" }, + { 39, "Netherland5" }, + { 40, "Norway5" }, + { 41, "Portugal5" }, + { 42, "Spain5" }, + { 43, "Sweden5" }, + { 44, "Switzer_Fr5" }, + { 45, "Switzer_Ge5" }, + { 46, "UK5" }, + { 47, "Korea5" }, + { 48, "Taiwan5" }, + { 49, "Japan5" }, + { 50, "Canada_Fr5" }, + { 51, "Hungary5" }, + { 52, "Poland5" }, + { 53, "Czech5" }, + { 54, "Russia5" }, + { 55, "Latvia5" }, + { 56, "Turkey5" }, + { 57, "Greece5" }, + { 58, "Estonia5" }, + { 59, "Lithuania5" }, + { 63, "Canada_Fr5_TBITS5" }, + { 80, "US5_Hobo" }, + { 81, "US_UNIX5_Hobo" }, + { 82, "France5_Hobo" }, + { 83, "Denmark5_Hobo" }, + { 84, "Germany5_Hobo" }, + { 85, "Italy5_Hobo" }, + { 86, "Netherland5_Hobo" }, + { 87, "Norway5_Hobo" }, + { 88, "Portugal5_Hobo" }, + { 89, "Spain5_Hobo" }, + { 90, "Sweden5_Hobo" }, + { 91, "Switzer_Fr5_Hobo" }, + { 92, "Switzer_Ge5_Hobo" }, + { 93, "UK5_Hobo" }, + { 94, "Korea5_Hobo" }, + { 95, "Taiwan5_Hobo" }, + { 96, "Japan5_Hobo" }, + { 97, "Canada_Fr5_Hobo" }, + { -1, NULL } +}; + +static const keyboard_layout type101_layout[] = +{ + { 0, "US101A_x86" }, + { 1, "US101A_x86" }, + { 34, "J3100_x86" }, + { 35, "France_x86" }, + { 36, "Denmark_x86" }, + { 37, "Germany_x86" }, + { 38, "Italy_x86" }, + { 39, "Netherland_x86" }, + { 40, "Norway_x86" }, + { 41, "Portugal_x86" }, + { 42, "Spain_x86" }, + { 43, "Sweden_x86" }, + { 44, "Switzer_Fr_x86" }, + { 45, "Switzer_Ge_x86" }, + { 46, "UK_x86" }, + { 47, "Korea_x86" }, + { 48, "Taiwan_x86" }, + { 49, "Japan_x86" }, + { 50, "Canada_Fr2_x86" }, + { 51, "Hungary_x86" }, + { 52, "Poland_x86" }, + { 53, "Czech_x86" }, + { 54, "Russia_x86" }, + { 55, "Latvia_x86" }, + { 56, "Turkey_x86" }, + { 57, "Greece_x86" }, + { 59, "Lithuania_x86" }, + { 1001, "MS_US101A_x86" }, + { -1, NULL } +}; + +static const keyboard_layout type6_layout[] = +{ + { 0, "US6" }, + { 6, "Denmark6" }, + { 7, "Finnish6" }, + { 8, "France6" }, + { 9, "Germany6" }, + { 14, "Italy6" }, + { 15, "Japan6" }, + { 16, "Korea6" }, + { 18, "Netherland6" }, + { 19, "Norway6" }, + { 22, "Portugal6" }, + { 25, "Spain6" }, + { 26, "Sweden6" }, + { 27, "Switzer_Fr6" }, + { 28, "Switzer_Ge6" }, + { 30, "Taiwan6" }, + { 32, "UK6" }, + { 33, "US6" }, + { -1, NULL } +}; +#endif + + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +const char* SalDisplay::GetKeyboardName( BOOL bRefresh ) +{ + if( bRefresh || ! m_aKeyboardName.Len() ) + { +#ifdef SOLARIS + if( IsLocal() ) + { + int kbd = open( "/dev/kbd", O_RDONLY ); + if( kbd >= 0 ) + { + int kbd_type = 0; + if( ! ioctl( kbd, KIOCTYPE, &kbd_type ) ) + { + int kbd_layout = 0; + if( ! ioctl( kbd, KIOCLAYOUT, &kbd_layout ) ) + { + const keyboard_layout *p_layout = NULL; + switch( kbd_type ) + { + case KB_KLUNK: p_layout = type0_layout; break; + case KB_SUN3: p_layout = type3_layout; break; + case KB_SUN4: p_layout = type4_layout; break; + case KB_USB: p_layout = type6_layout; break; + case KB_PC: p_layout = type101_layout; break; + } + + if( p_layout ) + { + while( p_layout->n_layout != -1 ) + { + if ( p_layout->n_layout == kbd_layout ) + { + m_aKeyboardName = p_layout->p_description; + break; + } + p_layout++; + } + } + } + } + close(kbd); + } + } +#else + int opcode, event, error; + int major = XkbMajorVersion, minor = XkbMinorVersion; + if( XkbQueryExtension( GetDisplay(), &opcode, &event,&error, &major, &minor ) ) + { + XkbDescPtr pXkbDesc = NULL; + // try X keyboard extension + if( (pXkbDesc = XkbGetKeyboard( GetDisplay(), XkbAllComponentsMask, XkbUseCoreKbd )) ) + { + const char* pAtom = NULL; + if( pXkbDesc->names->groups[0] ) + { + pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->groups[0] ); + m_aKeyboardName = pAtom; + XFree( (void*)pAtom ); + } + else + m_aKeyboardName = "<unknown keyboard>"; +#if OSL_DEBUG_LEVEL > 1 +#define PRINT_ATOM( x ) { if( pXkbDesc->names->x ) { pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->x ); fprintf( stderr, "%s: %s\n", #x, pAtom ); XFree( (void*)pAtom ); } else fprintf( stderr, "%s: <nil>\n", #x ); } + + PRINT_ATOM( keycodes ); + PRINT_ATOM( geometry ); + PRINT_ATOM( symbols ); + PRINT_ATOM( types ); + PRINT_ATOM( compat ); + PRINT_ATOM( phys_symbols ); + +#define PRINT_ATOM_2( x ) { if( pXkbDesc->names->x[i] ) { pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->x[i] ); fprintf( stderr, "%s[%d]: %s\n", #x, i, pAtom ); XFree( (void*)pAtom ); } else fprintf( stderr, "%s[%d]: <nil>\n", #x, i ); } + int i; + for( i = 0; i < XkbNumVirtualMods; i++ ) + PRINT_ATOM_2( vmods ); + for( i = 0; i < XkbNumIndicators; i++ ) + PRINT_ATOM_2( indicators ); + for( i = 0; i < XkbNumKbdGroups; i++ ) + PRINT_ATOM_2( groups ); +#endif + XkbFreeKeyboard( pXkbDesc, XkbAllComponentsMask, True ); + } + } +#endif + if( ! m_aKeyboardName.Len() ) + m_aKeyboardName = "<unknown keyboard>"; + } + return m_aKeyboardName.GetBuffer(); +} diff --git a/vcl/unx/generic/app/makefile.mk b/vcl/unx/generic/app/makefile.mk new file mode 100644 index 000000000000..bd7549945c7c --- /dev/null +++ b/vcl/unx/generic/app/makefile.mk @@ -0,0 +1,110 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=salapp +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="unx" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="unx" + +SLOFILES=\ + $(SLO)$/i18n_cb.obj \ + $(SLO)$/i18n_ic.obj \ + $(SLO)$/i18n_im.obj \ + $(SLO)$/i18n_xkb.obj \ + $(SLO)$/i18n_wrp.obj \ + $(SLO)$/i18n_status.obj \ + $(SLO)$/i18n_keysym.obj \ + $(SLO)$/saldata.obj \ + $(SLO)$/saltimer.obj \ + $(SLO)$/saldisp.obj \ + $(SLO)$/randrwrapper.obj \ + $(SLO)$/salinst.obj \ + $(SLO)$/salsys.obj \ + $(SLO)$/soicon.obj \ + $(SLO)$/sm.obj \ + $(SLO)$/keysymnames.obj \ + $(SLO)$/wmadaptor.obj + +EXCEPTIONSFILES=\ + $(SLO)$/wmadaptor.obj \ + $(SLO)$/saldata.obj \ + $(SLO)$/salinst.obj \ + $(SLO)$/saldisp.obj \ + $(SLO)$/i18n_status.obj \ + $(SLO)$/i18n_cb.obj \ + $(SLO)$/i18n_ic.obj \ + $(SLO)$/salsys.obj + + +.IF "$(ENABLE_RANDR)" != "" +CDEFS+=-DUSE_RANDR +.IF "$(XRANDR_DLOPEN)" == "FALSE" +CDEFS+=$(XRANDR_CFLAGS) +.ELSE +CDEFS+=-DXRANDR_DLOPEN +.ENDIF +.ENDIF + +.IF "$(USE_XINERAMA)" != "NO" +CDEFS+=-DUSE_XINERAMA +.IF "$(USE_XINERAMA_VERSION)" == "Xorg" +CDEFS+=-DUSE_XINERAMA_XORG +.ELIF "$(USE_XINERAMA_VERSION)" == "Xsun" +CDEFS+=-DUSE_XINERAMA_XSUN +.ELSE +# provide sensible default +.IF "$(OS)" != "SOLARIS" +CDEFS+=-DUSE_XINERAMA_XORG +.ELSE +CDEFS+=-DUSE_XINERAMA_XSUN +.ENDIF +.ENDIF +.ENDIF + +.ENDIF # "$(GUIBASE)"!="unx" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk + diff --git a/vcl/unx/generic/app/randrwrapper.cxx b/vcl/unx/generic/app/randrwrapper.cxx new file mode 100644 index 000000000000..e7f37c00e2ca --- /dev/null +++ b/vcl/unx/generic/app/randrwrapper.cxx @@ -0,0 +1,360 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifdef USE_RANDR + +#include <tools/prex.h> +#include <X11/extensions/Xrandr.h> +#include <tools/postx.h> + +#include "osl/module.h" +#include "rtl/ustring.hxx" + +namespace +{ + +# ifdef XRANDR_DLOPEN + +class RandRWrapper +{ + oslModule m_pRandRLib; + + // function pointers + Bool(*m_pXRRQueryExtension)(Display*,int*,int*); + Status(*m_pXRRQueryVersion)(Display*,int*,int*); + XRRScreenConfiguration*(*m_pXRRGetScreenInfo)(Display*,Drawable); + void(*m_pXRRFreeScreenConfigInfo)(XRRScreenConfiguration*); + void(*m_pXRRSelectInput)(Display*,XLIB_Window,int); + int(*m_pXRRUpdateConfiguration)(XEvent*); + XRRScreenSize*(*m_pXRRSizes)(Display*,int,int*); + XRRScreenSize*(*m_pXRRConfigSizes)(XRRScreenConfiguration*,int*); + SizeID(*m_pXRRConfigCurrentConfiguration)(XRRScreenConfiguration*,Rotation*); + int(*m_pXRRRootToScreen)(Display*, XLIB_Window); + + bool m_bValid; + + void initFromModule(); + + RandRWrapper(Display*); + ~RandRWrapper(); +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 = m_pXRRQueryExtension( i_pDisp, o_event_base, o_error_base ); + return bRet; + } + Status XRRQueryVersion( Display* i_pDisp, int* o_major, int* o_minor ) + { + return m_bValid ? m_pXRRQueryVersion( i_pDisp, o_major, o_minor ) : 0; + } + XRRScreenConfiguration* XRRGetScreenInfo( Display* i_pDisp, Drawable i_aDrawable ) + { + return m_bValid ? m_pXRRGetScreenInfo( i_pDisp, i_aDrawable ) : NULL; + } + void XRRFreeScreenConfigInfo( XRRScreenConfiguration* i_pConfig ) + { + if( m_bValid ) + m_pXRRFreeScreenConfigInfo( i_pConfig ); + } + void XRRSelectInput( Display* i_pDisp, XLIB_Window i_window, int i_nMask ) + { + if( m_bValid ) + m_pXRRSelectInput( i_pDisp, i_window, i_nMask ); + } + int XRRUpdateConfiguration( XEvent* i_pEvent ) + { + return m_bValid ? m_pXRRUpdateConfiguration( i_pEvent ) : 0; + } + XRRScreenSize* XRRSizes( Display* i_pDisp, int i_screen, int* o_nscreens ) + { + return m_bValid ? m_pXRRSizes( i_pDisp, i_screen, o_nscreens ) : NULL; + } + XRRScreenSize* XRRConfigSizes( XRRScreenConfiguration* i_pConfig, int* o_nSizes ) + { + return m_bValid ? m_pXRRConfigSizes( i_pConfig, o_nSizes ) : NULL; + } + SizeID XRRConfigCurrentConfiguration( XRRScreenConfiguration* i_pConfig, Rotation* o_pRot ) + { + return m_bValid ? m_pXRRConfigCurrentConfiguration( i_pConfig, o_pRot ) : 0; + } + int XRRRootToScreen( Display *dpy, XLIB_Window root ) + { + return m_bValid ? m_pXRRRootToScreen( dpy, root ) : -1; + } +}; + +void RandRWrapper::initFromModule() +{ + m_pXRRQueryExtension = (Bool(*)(Display*,int*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRQueryExtension" ); + m_pXRRQueryVersion = (Status(*)(Display*,int*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRQueryVersion" ); + m_pXRRGetScreenInfo = (XRRScreenConfiguration*(*)(Display*,Drawable))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRGetScreenInfo" ); + m_pXRRFreeScreenConfigInfo = (void(*)(XRRScreenConfiguration*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRFreeScreenConfigInfo" ); + m_pXRRSelectInput = (void(*)(Display*,XLIB_Window,int))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRSelectInput" ); + m_pXRRUpdateConfiguration = (int(*)(XEvent*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRUpdateConfiguration" ); + m_pXRRSizes = (XRRScreenSize*(*)(Display*,int,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRSizes" ); + m_pXRRConfigSizes = (XRRScreenSize*(*)(XRRScreenConfiguration*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRConfigSizes" ); + m_pXRRConfigCurrentConfiguration = (SizeID(*)(XRRScreenConfiguration*,Rotation*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRConfigCurrentConfiguration" ); + m_pXRRRootToScreen = (int(*)(Display*,XLIB_Window))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRRootToScreen" ); + + m_bValid = m_pXRRQueryExtension && + m_pXRRQueryVersion && + m_pXRRGetScreenInfo && + m_pXRRFreeScreenConfigInfo && + m_pXRRSelectInput && + m_pXRRUpdateConfiguration && + m_pXRRSizes && + m_pXRRConfigSizes && + m_pXRRConfigCurrentConfiguration && + m_pXRRRootToScreen + ; +} + +RandRWrapper::RandRWrapper( Display* pDisplay ) : + m_pRandRLib( NULL ), + m_pXRRQueryExtension( NULL ), + m_pXRRQueryVersion( NULL ), + m_pXRRGetScreenInfo( NULL ), + m_pXRRFreeScreenConfigInfo( NULL ), + m_pXRRSelectInput( NULL ), + m_pXRRUpdateConfiguration( NULL ), + m_pXRRSizes( NULL ), + m_pXRRConfigSizes( NULL ), + m_pXRRConfigCurrentConfiguration( NULL ), + m_pXRRRootToScreen( NULL ), + m_bValid( false ) +{ + // first try in process space (e.g. gtk links that ?) + initFromModule(); + if( ! m_bValid ) + { + rtl::OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libXrandr.so.2" ) ); + // load and resolve dependencies immediately + // rationale: there are older distributions where libXrandr.so.2 is not linked + // with libXext.so, resulting in a missing symbol and terminating the office + // obviously they expected libXext to be linked in global symbolspace (that is + // linked by the application), which is not the case with us (because we want + // to be able to run in headless mode even without an installed X11 library) + m_pRandRLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT | SAL_LOADMODULE_NOW ); + initFromModule(); + } + if( m_bValid ) + { + int nEventBase = 0, nErrorBase = 0; + if( ! m_pXRRQueryExtension( pDisplay, &nEventBase, &nErrorBase ) ) + m_bValid = false; + } +} + +RandRWrapper::~RandRWrapper() +{ + if( m_pRandRLib ) + osl_unloadModule( m_pRandRLib ); +} + +static RandRWrapper* pWrapper = NULL; + +RandRWrapper& RandRWrapper::get( Display* i_pDisplay ) +{ + if( ! pWrapper ) + pWrapper = new RandRWrapper( i_pDisplay ); + return *pWrapper; +} + +void RandRWrapper::releaseWrapper() +{ + delete pWrapper; + pWrapper = NULL; +} + +# else + +class RandRWrapper +{ + bool m_bValid; + + 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; + } + Status XRRQueryVersion( Display* i_pDisp, int* o_major, int* o_minor ) + { + return m_bValid ? ::XRRQueryVersion( i_pDisp, o_major, o_minor ) : 0; + } + XRRScreenConfiguration* XRRGetScreenInfo( Display* i_pDisp, Drawable i_aDrawable ) + { + return m_bValid ? ::XRRGetScreenInfo( i_pDisp, i_aDrawable ) : NULL; + } + void XRRFreeScreenConfigInfo( XRRScreenConfiguration* i_pConfig ) + { + if( m_bValid ) + ::XRRFreeScreenConfigInfo( i_pConfig ); + } + void XRRSelectInput( Display* i_pDisp, XLIB_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* XRRSizes( Display* i_pDisp, int i_screen, int* o_nscreens ) + { + return m_bValid ? ::XRRSizes( i_pDisp, i_screen, o_nscreens ) : NULL; + } + XRRScreenSize* XRRConfigSizes( XRRScreenConfiguration* i_pConfig, int* o_nSizes ) + { + return m_bValid ? ::XRRConfigSizes( i_pConfig, o_nSizes ) : NULL; + } + SizeID XRRConfigCurrentConfiguration( XRRScreenConfiguration* i_pConfig, Rotation* o_pRot ) + { + return m_bValid ? ::XRRConfigCurrentConfiguration( i_pConfig, o_pRot ) : 0; + } + int XRRRootToScreen( Display *dpy, XLIB_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; +} + +static RandRWrapper* pWrapper = NULL; + +RandRWrapper& RandRWrapper::get( Display* i_pDisplay ) +{ + if( ! pWrapper ) + pWrapper = new RandRWrapper( i_pDisplay ); + return *pWrapper; +} + +void RandRWrapper::releaseWrapper() +{ + delete pWrapper; + pWrapper = NULL; +} + +#endif + +} // namespace + +#endif + +#include "unx/saldisp.hxx" +#include "unx/salframe.h" +#if OSL_DEBUG_LEVEL > 1 +#include <cstdio> +#endif + +void SalDisplay::InitRandR( XLIB_Window aRoot ) const +{ + #ifdef USE_RANDR + if( m_bUseRandRWrapper ) + RandRWrapper::get( GetDisplay() ).XRRSelectInput( GetDisplay(), aRoot, RRScreenChangeNotifyMask ); + #else + (void)aRoot; + #endif +} + +void SalDisplay::DeInitRandR() +{ + #ifdef USE_RANDR + if( m_bUseRandRWrapper ) + RandRWrapper::releaseWrapper(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::DeInitRandR()\n" ); +#endif + #endif +} + +int SalDisplay::processRandREvent( XEvent* pEvent ) +{ + int nRet = 0; + #ifdef USE_RANDR + XConfigureEvent* pCnfEvent=(XConfigureEvent*)pEvent; + if( m_bUseRandRWrapper && pWrapper && pWrapper->XRRRootToScreen(GetDisplay(),pCnfEvent->window) != -1 ) + { + nRet = pWrapper->XRRUpdateConfiguration( pEvent ); + if( nRet == 1 && pEvent->type != ConfigureNotify) // this should then be a XRRScreenChangeNotifyEvent + { + // update screens + bool bNotify = false; + for( size_t i = 0; i < m_aScreens.size(); i++ ) + { + if( m_aScreens[i].m_bInit ) + { + XRRScreenConfiguration *pConfig = NULL; + XRRScreenSize *pSizes = NULL; + int nSizes = 0; + Rotation nRot = 0; + SizeID nId = 0; + + pConfig = pWrapper->XRRGetScreenInfo( GetDisplay(), m_aScreens[i].m_aRoot ); + nId = pWrapper->XRRConfigCurrentConfiguration( pConfig, &nRot ); + pSizes = pWrapper->XRRConfigSizes( pConfig, &nSizes ); + XRRScreenSize *pTargetSize = pSizes + nId; + + bNotify = bNotify || + m_aScreens[i].m_aSize.Width() != pTargetSize->width || + m_aScreens[i].m_aSize.Height() != pTargetSize->height; + + m_aScreens[i].m_aSize = Size( pTargetSize->width, pTargetSize->height ); + + pWrapper->XRRFreeScreenConfigInfo( pConfig ); + + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "screen %d changed to size %dx%d\n", (int)i, (int)pTargetSize->width, (int)pTargetSize->height ); + #endif + } + } + if( bNotify && ! m_aFrames.empty() ) + m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 ); + } + } + #else + (void)pEvent; + #endif + return nRet; +} diff --git a/vcl/unx/generic/app/saldata.cxx b/vcl/unx/generic/app/saldata.cxx new file mode 100644 index 000000000000..1b72d55e21c5 --- /dev/null +++ b/vcl/unx/generic/app/saldata.cxx @@ -0,0 +1,867 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#ifdef USE_XTOOLKIT +# define SAL_XT +#endif + +// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include <unistd.h> +#include <fcntl.h> + +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <stdio.h> // snprintf, seems not to be in namespace std on every platform +#include <limits.h> +#include <errno.h> +#include <pthread.h> +#include <sys/resource.h> +#ifdef SUN +#include <sys/systeminfo.h> +#endif +#ifdef AIX +#include <strings.h> +#endif +#ifdef FREEBSD +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#endif + +#include <vos/process.hxx> +#include <vos/mutex.hxx> + +#include "unx/Xproto.h" +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/salframe.h" +#include "unx/sm.hxx" +#include "unx/i18n_im.hxx" +#include "unx/i18n_xkb.hxx" +#include "salinst.hxx" + +#include <osl/signal.h> +#include <osl/thread.h> +#include <osl/process.h> +#include <rtl/strbuf.hxx> +#include <rtl/bootstrap.hxx> + +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> + +// -=-= <signal.h> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#ifndef UNX +#ifndef SIGBUS +#define SIGBUS 10 +#endif +#ifndef SIGSEGV +#define SIGSEGV 11 +#endif +#ifndef SIGIOT +#define SIGIOT SIGABRT +#endif +#endif + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static const struct timeval noyield__ = { 0, 0 }; +static const struct timeval yield__ = { 0, 10000 }; + +static const char* XRequest[] = { + // see /usr/lib/X11/XErrorDB, /usr/openwin/lib/XErrorDB ... + NULL, + "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", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "X_NoOperation" +}; + +// -=-= C statics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +int X11SalData::XErrorHdl( Display *pDisplay, XErrorEvent *pEvent ) +{ + GetX11SalData()->XError( pDisplay, pEvent ); + return 0; +} + +int X11SalData::XIOErrorHdl( Display * ) +{ + /* #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, NULL); + + 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(0); + return 0; +} + +// -=-= SalData =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#include <pthread.h> + +X11SalData::X11SalData() +{ + bNoExceptions_ = !!getenv( "SAL_NOSEGV" ); + + pXLib_ = NULL; + m_pSalDisplay = NULL; + m_pInstance = NULL; + m_pPlugin = NULL; + + hMainThread_ = pthread_self(); + osl_getLocalHostname( &maLocalHostName.pData ); +} + +X11SalData::~X11SalData() +{ + DeleteDisplay(); +} + +void X11SalData::DeleteDisplay() +{ + delete m_pSalDisplay; + m_pSalDisplay = NULL; + delete pXLib_; + pXLib_ = NULL; +} + +void X11SalData::Init() +{ + pXLib_ = new SalXLib(); + pXLib_->Init(); +} + +void X11SalData::initNWF( void ) +{ +} + +void X11SalData::deInitNWF( void ) +{ +} + +// -=-= SalXLib =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalXLib::SalXLib() +{ + m_aTimeout.tv_sec = 0; + m_aTimeout.tv_usec = 0; + m_nTimeoutMS = 0; + + nFDs_ = 0; + FD_ZERO( &aReadFDS_ ); + FD_ZERO( &aExceptionFDS_ ); + + m_pTimeoutFDS[0] = m_pTimeoutFDS[1] = -1; + if (pipe (m_pTimeoutFDS) != -1) + { + // initialize 'wakeup' pipe. + int flags; + + // set close-on-exec descriptor flag. + if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (m_pTimeoutFDS[0], F_SETFD, flags); + } + if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + 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; + fcntl (m_pTimeoutFDS[0], F_SETFL, flags); + } + if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (m_pTimeoutFDS[1], F_SETFL, flags); + } + + // insert [0] into read descriptor set. + FD_SET( m_pTimeoutFDS[0], &aReadFDS_ ); + nFDs_ = m_pTimeoutFDS[0] + 1; + } + + m_bHaveSystemChildFrames = false; + m_aOrigXIOErrorHandler = XSetIOErrorHandler ( (XIOErrorHandler)X11SalData::XIOErrorHdl ); + PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) ); +} + +SalXLib::~SalXLib() +{ + // close 'wakeup' pipe. + close (m_pTimeoutFDS[0]); + close (m_pTimeoutFDS[1]); + + PopXErrorLevel(); + XSetIOErrorHandler (m_aOrigXIOErrorHandler); +} + +void SalXLib::PushXErrorLevel( bool bIgnore ) +{ + m_aXErrorHandlerStack.push_back( XErrorStackEntry() ); + XErrorStackEntry& rEnt = m_aXErrorHandlerStack.back(); + rEnt.m_bWas = false; + rEnt.m_bIgnore = bIgnore; + rEnt.m_nLastErrorRequest = 0; + rEnt.m_aHandler = XSetErrorHandler( (XErrorHandler)X11SalData::XErrorHdl ); +} + +void SalXLib::PopXErrorLevel() +{ + if( m_aXErrorHandlerStack.size() ) + { + XSetErrorHandler( m_aXErrorHandlerStack.back().m_aHandler ); + m_aXErrorHandlerStack.pop_back(); + } +} + +void SalXLib::Init() +{ + SalI18N_InputMethod* pInputMethod = new SalI18N_InputMethod; + pInputMethod->SetLocale(); + XrmInitialize(); + + /* + * open connection to X11 Display + * try in this order: + * o -display command line parameter, + * o $DISPLAY environment variable + * o default display + */ + + Display *pDisp = NULL; + + // is there a -display command line parameter? + vos::OExtCommandLine aCommandLine; + sal_uInt32 nParams = aCommandLine.getCommandArgCount(); + rtl::OUString aParam; + rtl::OString aDisplay; + for (USHORT i=0; i<nParams; i++) + { + aCommandLine.getCommandArg(i, aParam); + if (aParam.equalsAscii("-display")) + { + aCommandLine.getCommandArg(i+1, aParam); + aDisplay = rtl::OUStringToOString( + aParam, osl_getThreadTextEncoding()); + + if ((pDisp = XOpenDisplay(aDisplay.getStr()))!=NULL) + { + /* + * if a -display switch was used, we need + * to set the environment accoringly since + * the clipboard build another connection + * to the xserver using $DISPLAY + */ + rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("DISPLAY")); + osl_setEnvironment(envVar.pData, aParam.pData); + } + break; + } + } + + if (!pDisp && !aDisplay.getLength()) + { + // Open $DISPLAY or default... + char *pDisplay = getenv("DISPLAY"); + if (pDisplay != NULL) + aDisplay = rtl::OString(pDisplay); + pDisp = XOpenDisplay(pDisplay); + } + + if ( !pDisp ) + { + rtl::OUString aProgramFileURL; + osl_getExecutableFile( &aProgramFileURL.pData ); + rtl::OUString aProgramSystemPath; + osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData); + rtl::OString aProgramName = rtl::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); + } + + SalDisplay *pSalDisplay = new SalX11Display( pDisp ); + + pInputMethod->CreateMethod( pDisp ); + pInputMethod->AddConnectionWatch( pDisp, (void*)this ); + pSalDisplay->SetInputMethod( pInputMethod ); + + PushXErrorLevel( true ); + SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp ); + XSync( pDisp, False ); + + pKbdExtension->UseExtension( ! HasXErrorOccured() ); + PopXErrorLevel(); + + pSalDisplay->SetKbdExtension( pKbdExtension ); +} + +extern "C" { +void EmitFontpathWarning( void ) +{ + 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 ] = ""; +#if ! ( defined LINUX && defined PPC ) + XGetErrorText( pDisplay, pEvent->error_code, msg, sizeof( msg ) ); +#endif + std::fprintf( stderr, "X-Error: %s\n", msg ); + if( pEvent->request_code < capacityof( 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 SalXLib::XError( Display *pDisplay, XErrorEvent *pEvent ) +{ + if( m_bHaveSystemChildFrames ) + return; + + 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 occured 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 != GetX11SalData()->GetDisplay()->GetDisplay() ) + return; + + PrintXError( pDisplay, pEvent ); + + oslSignalAction eToDo = osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, NULL); + 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; +} + +struct YieldEntry +{ + YieldEntry* next; // pointer to next entry + int fd; // file descriptor for reading + void* data; // data for predicate and callback + YieldFunc pending; // predicate (determins pending events) + YieldFunc queued; // read and queue up events + YieldFunc handle; // handle pending events + + inline int HasPendingEvent() const { return pending( fd, data ); } + inline int IsEventQueued() const { return queued( fd, data ); } + inline 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 ) +{ + DBG_ASSERT( nFD, "can not insert stdin descriptor" ); + DBG_ASSERT( !yieldTable[nFD].fd, "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, 0 ); + 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 + GetX11SalData()->Timeout(); + } + } + } + return bRet; +} + +void 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"); + if (p_prioritize_timer != NULL) + CheckTimeout(); + + // first, check for already queued events. + for ( int nFD = 0; nFD < nFDs_; nFD++ ) + { + YieldEntry* pEntry = &(yieldTable[nFD]); + if ( pEntry->fd ) + { + DBG_ASSERT( nFD == pEntry->fd, "wrong fd in Yield()" ); + if ( pEntry->HasPendingEvent() ) + { + pEntry->HandleNextEvent(); + // #63862# da jetzt alle user-events ueber die interne + // queue kommen, wird die Kontrolle analog zum select + // gesteuerten Zweig einmal bei bWait abgegeben + + /* #i9277# do not reschedule since performance gets down the + the drain under heavy load + YieldMutexReleaser aReleaser; + if ( bWait ) osl_yieldThread(); + */ + + return; + } + } + } + + // 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 = 0; + if (m_aTimeout.tv_sec) // Timer is started. + { + // determine remaining timeout. + gettimeofday (&Timeout, 0); + Timeout = m_aTimeout - Timeout; + if (yield__ >= Timeout) + { + // guard against micro timeout. + Timeout = yield__; + } + pTimeout = &Timeout; + } + } + + { + // release YieldMutex (and re-acquire at block end) + YieldMutexReleaser aReleaser; + nFound = select( nFDs, &ReadFDS, NULL, &ExceptionFDS, pTimeout ); + } + if( nFound < 0 ) // error + { +#ifdef DBG_UTIL + std::fprintf( stderr, "SalXLib::Yield e=%d f=%d\n", errno, nFound ); +#endif + if( EINTR == errno ) + { + errno = 0; + } + } + + // usually handle timeouts here (as in 5.2) + if (p_prioritize_timer == NULL) + CheckTimeout(); + + // 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, NULL, + &ExceptionFDS, &noTimeout ); + + // someone-else has done the job for us + if (nFound == 0) + return; + + for ( int nFD = 0; nFD < nFDs_; nFD++ ) + { + YieldEntry* pEntry = &(yieldTable[nFD]); + if ( pEntry->fd ) + { + if ( FD_ISSET( nFD, &ExceptionFDS ) ) { +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "SalXLib::Yield exception\n" ); +#endif + nFound--; + } + if ( FD_ISSET( nFD, &ReadFDS ) ) + { + int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1; + for( int i = 0; pEntry->IsEventQueued() && i < nMaxEvents; i++ ) + { + pEntry->HandleNextEvent(); + // if a recursive call has done the job + // so abort here + } + nFound--; + } + } + } + } +} + +void SalXLib::Wakeup() +{ + write (m_pTimeoutFDS[1], "", 1); +} + +void SalXLib::PostUserEvent() +{ + Wakeup(); +} + +const char* X11SalData::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 rtl::OStringBuffer aResName; + if( !aResName.getLength() ) + { + int nArgs = osl_getCommandArgCount(); + for( int n = 0; n < nArgs-1; n++ ) + { + rtl::OUString aArg; + if( ! osl_getCommandArg( n, &aArg.pData ) && + aArg.equalsIgnoreAsciiCaseAscii( "-name" ) && + ! osl_getCommandArg( n+1, &aArg.pData ) ) + { + aResName.append( rtl::OUStringToOString( aArg, osl_getThreadTextEncoding() ) ); + break; + } + } + if( !aResName.getLength() ) + { + const char* pEnv = getenv( "RESOURCE_NAME" ); + if( pEnv && *pEnv ) + aResName.append( pEnv ); + } + if( !aResName.getLength() ) + aResName.append( "VCLSalFrame" ); + } + return aResName.getStr(); +} + +const char* X11SalData::getFrameClassName() +{ + static rtl::OStringBuffer aClassName; + if( !aClassName.getLength() ) + { + rtl::OUString aIni, aProduct; + rtl::Bootstrap::get( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aIni ); + aIni += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) ); + rtl::Bootstrap aBootstrap( aIni ); + aBootstrap.getFrom( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ProductKey" ) ), aProduct ); + + if( aProduct.getLength() ) + aClassName.append( rtl::OUStringToOString( aProduct, osl_getThreadTextEncoding() ) ); + else + aClassName.append( "VCLSalFrame" ); + } + return aClassName.getStr(); +} + +rtl::OString X11SalData::getFrameResName( SalExtStyle nStyle ) +{ + rtl::OStringBuffer aBuf( 64 ); + aBuf.append( getFrameResName() ); + if( (nStyle & SAL_FRAME_EXT_STYLE_DOCUMENT) ) + aBuf.append( ".DocumentWindow" ); + + return aBuf.makeStringAndClear(); +} diff --git a/vcl/unx/generic/app/saldisp.cxx b/vcl/unx/generic/app/saldisp.cxx new file mode 100644 index 000000000000..923d3d3e9ac4 --- /dev/null +++ b/vcl/unx/generic/app/saldisp.cxx @@ -0,0 +1,3435 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define SAL_XT + +// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <sys/time.h> +#include <pthread.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> + +#if defined(SOLARIS) +#include <sal/alloca.h> +#include <osl/module.h> +#endif + +#include <tools/prex.h> +#include <X11/cursorfont.h> +#include "unx/x11_cursors/salcursors.h" +#include "unx/x11_cursors/invert50.h" +#ifdef SOLARIS +#define XK_KOREAN +#endif +#include <X11/keysym.h> + +#include <X11/Xatom.h> + +#ifdef USE_XINERAMA +#ifdef USE_XINERAMA_XORG +#include <X11/extensions/Xinerama.h> +#elif defined USE_XINERAMA_XSUN +#if defined(SOLARIS) && defined(INTEL) // missing extension header in standard installation +#define MAXFRAMEBUFFERS 16 +Bool XineramaGetState(Display*, int); +Status XineramaGetInfo(Display*, int, XRectangle*, unsigned char*, int*); +#else +#include <X11/extensions/xinerama.h> +#endif +#else +#error USE_XINERAMA but no xinerama version +#endif +#endif + +#include <tools/postx.h> + +#include <unx/salunx.h> +#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/salgdi.h> +#include <unx/salframe.h> +#include <vcl/keycodes.hxx> +#include <vcl/salbtype.hxx> +#include <unx/salbmp.h> +#ifndef _OSL_THREADMUTEX_H_ +#include <osl/mutex.h> +#endif +#include <unx/salobj.h> +#include <unx/sm.hxx> +#include <unx/wmadaptor.hxx> +#include <unx/dtint.hxx> + +#include <osl/socket.h> +#include <poll.h> + +using namespace rtl; +using namespace vcl_sal; + +// -=-= #defines -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define PSEUDOCOLOR12 +#define PSEUDOCOLOR8 +#define TRUECOLOR24 +#define TRUECOLOR16 +#define TRUECOLOR15 +#define TRUECOLOR12 +#define TRUECOLOR8 + +#define SALCOLOR_WHITE MAKE_SALCOLOR( 0xFF, 0xFF, 0xFF ) +#define SALCOLOR_BLACK MAKE_SALCOLOR( 0x00, 0x00, 0x00 ) + +// -=-= Prototyps =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-= static variables -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static const char* const VisualClassName[] = { + "StaticGray", + "GrayScale", + "StaticColor", + "PseudoColor", + "TrueColor", + "DirectColor" +}; + +static const char* const EventNames[] = +{ + NULL, + NULL, + "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" +}; + +// -=-= global inline =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +inline const char *Null( const char *p ) { return p ? p : ""; } +inline const char *GetEnv( const char *p ) { return Null( getenv( p ) ); } +inline const char *KeyStr( KeySym n ) { return Null( XKeysymToString( n ) ); } + +inline const char *GetAtomName( Display *d, Atom a ) +{ return Null( XGetAtomName( d, a ) ); } + +inline double Hypothenuse( long w, long h ) +{ return sqrt( (double)((w*w)+(h*h)) ); } + +inline int ColorDiff( int r, int g, int b ) +{ return (r*r)+(g*g)+(b*b); } + +inline int ColorDiff( SalColor c1, int r, int g, int b ) +{ return ColorDiff( (int)SALCOLOR_RED (c1)-r, + (int)SALCOLOR_GREEN(c1)-g, + (int)SALCOLOR_BLUE (c1)-b ); } + +// -=-= global functions -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static int sal_Shift( Pixel nMask ) +{ + int i = 24; + if( nMask < 0x00010000 ) { nMask <<= 16; i -= 16; } + if( nMask < 0x01000000 ) { nMask <<= 8; i -= 8; } + if( nMask < 0x10000000 ) { nMask <<= 4; i -= 4; } + if( nMask < 0x40000000 ) { nMask <<= 2; i -= 2; } + if( nMask < 0x80000000 ) { nMask <<= 1; i -= 1; } + return i; +} + +static int sal_significantBits( Pixel nMask ) +{ + int nRotate = sizeof(Pixel)*4; + int nBits = 0; + while( nRotate-- ) + { + if( nMask & 1 ) + nBits++; + nMask >>= 1; + } + return nBits; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static BOOL sal_GetVisualInfo( Display *pDisplay, XID nVID, XVisualInfo &rVI ) +{ + int nInfos; + XVisualInfo aTemplate; + XVisualInfo*pInfos; + + aTemplate.visualid = nVID; + + pInfos = XGetVisualInfo( pDisplay, VisualIDMask, &aTemplate, &nInfos ); + if( !pInfos ) + return FALSE; + + rVI = *pInfos; + XFree( pInfos ); + + DBG_ASSERT( rVI.visualid == nVID, + "sal_GetVisualInfo: could not get correct visual by visualId" ); + return TRUE; +} + +// --------------------------------------------------------------------------- + +// check wether displaystring is in format N.M or N. or just N +// with N and M beeing natural numbers +static BOOL +sal_IsDisplayNumber( const char *pDisplayString ) +{ + if ( ! isdigit(*pDisplayString) ) + return FALSE; + while ( isdigit(*(++pDisplayString)) ) + ; /* do nothing */ + + if ( *pDisplayString == '.' ) + { + while ( isdigit(*(++pDisplayString)) ) + ; /* do nothing */ + } + + return (*pDisplayString == '\0'); +} + +// check whether host1 and host2 point to the same ip address +static BOOL +sal_EqualHosts( const OUString& Host1, const OUString& Host2) +{ + oslSocketAddr pHostAddr1; + oslSocketAddr pHostAddr2; + BOOL bEqualAddress = FALSE; + + if ( Host1.toChar() >= '0' && Host1.toChar() <= '9' ) + pHostAddr1 = osl_createInetSocketAddr( Host1.pData, 0 ); + else + pHostAddr1 = osl_resolveHostname( Host1.pData ); + + if ( Host2.toChar() >= '0' && Host2.toChar() <= '9' ) + pHostAddr2 = osl_createInetSocketAddr( Host2.pData, 0 ); + else + pHostAddr2 = osl_resolveHostname( Host2.pData ); + + if( pHostAddr1 && pHostAddr2 ) + bEqualAddress = osl_isEqualSocketAddr( pHostAddr1, pHostAddr2 ) ? TRUE : FALSE; + + if( pHostAddr1 ) + osl_destroySocketAddr( pHostAddr1 ); + if( pHostAddr2 ) + osl_destroySocketAddr( pHostAddr2 ); + + return bEqualAddress; +} + +static BOOL +sal_IsLocalDisplay( Display *pDisplay ) +{ + const char *pDisplayString = DisplayString( pDisplay ); + + // no string, no idea + if ( pDisplayString == NULL || pDisplayString[ 0 ] == '\0') + return FALSE; + + // check for ":x.y" + if ( pDisplayString[ 0 ] == ':' ) + return sal_IsDisplayNumber( pDisplayString + 1 ); + + // check for fixed token which all mean localhost:x.y + const char pLocal[] = "localhost:"; + const int nLocalLen = sizeof(pLocal) - 1; + if ( strncmp(pDisplayString, pLocal, nLocalLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nLocalLen ); + + const char pUnix[] = "unix:"; + const int nUnixLen = sizeof(pUnix) - 1; + if ( strncmp(pDisplayString, pUnix, nUnixLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nUnixLen ); + + const char pLoopback[] = "127.0.0.1:"; + const int nLoopbackLen= sizeof(pLoopback) - 1; + if ( strncmp(pDisplayString, pLoopback, nLoopbackLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nLoopbackLen ); + + // compare local hostname to displaystring, both may be ip address or + // hostname + BOOL bEqual = FALSE; + char *pDisplayHost = strdup( pDisplayString ); + char *pPtr = strrchr( pDisplayHost, ':' ); + + if( pPtr != NULL ) + { + const OUString& rLocalHostname( GetX11SalData()->GetLocalHostName() ); + if( rLocalHostname.getLength() ) + { + *pPtr = '\0'; + OUString aDisplayHostname( pDisplayHost, strlen( pDisplayHost ), osl_getThreadTextEncoding() ); + bEqual = sal_EqualHosts( rLocalHostname, aDisplayHostname ); + bEqual = bEqual && sal_IsDisplayNumber( pPtr + 1 ); + } + } + free( pDisplayHost ); + + return bEqual; +} + +// --------------------------------------------------------------------------- +// IsLocal means soffice is running on the same host as the xserver +// since it is not called very often and sal_IsLocalDisplay() is relative +// expensive bLocal_ is initialized on first call + +BOOL SalDisplay::IsLocal() +{ + if ( ! mbLocalIsValid ) + { + bLocal_ = sal_IsLocalDisplay( pDisp_ ); + mbLocalIsValid = TRUE; + } + return (BOOL)bLocal_; +} + +// --------------------------------------------------------------------------- +extern "C" srv_vendor_t +sal_GetServerVendor( Display *p_display ) +{ + typedef struct { + 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 + } vendor_t; + + const vendor_t p_vendorlist[] = { + { vendor_xfree, "The XFree86 Project, Inc", 13 }, + { vendor_sun, "Sun Microsystems, Inc.", 10 }, + { vendor_attachmate, "Attachmate Corporation", 10 }, + { vendor_excursion, + "DECWINDOWS DigitalEquipmentCorporation, eXcursion", 42 }, + { vendor_hp, "Hewlett-Packard Company", 17 }, + { vendor_hummingbird, "Hummingbird Communications Ltd.", 11 }, + { vendor_ibm, "International Business Machines", 24 }, + { vendor_sgi, "Silicon Graphics", 9 }, + { vendor_sco, "The Santa Cruz Operation", 16 }, + { vendor_xinside, "X Inside Inc.", 10 }, + // allways the last entry: vendor_none to indicate eol + { vendor_none, NULL, 0 }, + }; + + // handle regular server vendors + char *p_name = ServerVendor( p_display ); + vendor_t *p_vendor; + for (p_vendor = const_cast<vendor_t*>(p_vendorlist); p_vendor->e_vendor != vendor_none; p_vendor++) + { + if ( strncmp (p_name, p_vendor->p_name, p_vendor->n_len) == 0 ) + return p_vendor->e_vendor; + } + + // vendor not found in list + return vendor_unknown; +} + +static sal_Bool sal_IsTrustedSolaris (Display *p_display) +{ + int n_numextensions = 0; + char **p_extensions = XListExtensions (p_display, &n_numextensions); + sal_Bool b_is = sal_False; + + if (p_extensions != NULL) + { + for (int i = 0; !b_is && i < n_numextensions; i++) + b_is = (strcmp (p_extensions[i], "SUN_TSOL") == 0); + XFreeExtensionList (p_extensions); + } + + return b_is; +} + +// -=-= SalDisplay -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +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 + int* pWeight = (int*)alloca( sizeof(int)*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; +#ifdef TRUECOLOR8 + else if( pVInfos[i].depth == 8 ) + { + nTrueColor = -1; // strongly discourage 8 bit true color + bUsable = TRUE; + } +#endif +#ifdef TRUECOLOR15 + else if( pVInfos[i].depth == 15 ) + bUsable = TRUE; +#endif +#ifdef TRUECOLOR16 + else if( pVInfos[i].depth == 16 ) + bUsable = TRUE; +#endif +#ifdef TRUECOLOR32 + else if( pVInfos[i].depth == 32 ) + { + nTrueColor = 256; + // we do not have use for an alpha channel + // better use a 24 or 16 bit truecolor visual if possible + bUsable = TRUE; + } +#endif + } + else if( pVInfos[i].c_class == PseudoColor ) + { + if( pVInfos[i].depth <= 8 ) + bUsable = TRUE; +#ifdef PSEUDOCOLOR12 + else if( pVInfos[i].depth == 12 ) + bUsable = TRUE; +#endif + } + pWeight[ i ] = bUsable ? nTrueColor*pVInfos[i].depth : -1024; + pWeight[ i ] -= pVInfos[ i ].visualid; + } + + int nBestVisual = 0; + int nBestWeight = -1024; + for( i = 0; i < nVisuals; i++ ) + { + if( pWeight[ i ] > nBestWeight ) + { + nBestWeight = pWeight[ i ]; + nBestVisual = i; + } + } + + rVI = pVInfos[ nBestVisual ]; + + XFree( pVInfos ); + return rVI.visualid == nDefVID; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +SalDisplay::SalDisplay( Display *display ) : + mpInputMethod( NULL ), + pDisp_( display ), + m_pWMAdaptor( NULL ), + m_pDtIntegrator( NULL ), + m_bUseRandRWrapper( true ), + m_nLastUserEventTime( CurrentTime ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::SalDisplay()\n" ); +#endif + X11SalData *pSalData = GetX11SalData(); + + DBG_ASSERT( ! pSalData->GetDisplay(), "Second SalDisplay created !!!\n" ); + pSalData->SetSalDisplay( this ); + + pXLib_ = pSalData->GetLib(); + m_nDefaultScreen = DefaultScreen( pDisp_ ); + +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalDisplay::~SalDisplay( ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::~SalDisplay()\n" ); +#endif + if( pDisp_ ) + { + doDestruct(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "display %p closed\n", pDisp_ ); +#endif + pDisp_ = NULL; + } + // 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() +{ + X11SalData *pSalData = GetX11SalData(); + + delete m_pWMAdaptor; + m_pWMAdaptor = NULL; + delete m_pDtIntegrator; + m_pDtIntegrator = NULL; + X11SalBitmap::ImplDestroyCache(); + X11SalGraphics::releaseGlyphPeer(); + + if( IsDisplay() ) + { + delete mpInputMethod, mpInputMethod = (SalI18N_InputMethod*)ILLEGAL_POINTER; + delete mpKbdExtension, mpKbdExtension = (SalI18N_KeyboardExtension*)ILLEGAL_POINTER; + + // do not call anything that could implicitly call back into + // this object after this point + osl_destroyMutex( hEventGuard_ ); + + for( unsigned int 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 ); + } + } + + hEventGuard_ = (oslMutex)ILLEGAL_POINTER; + + for( size_t i = 0; i < POINTER_COUNT; i++ ) + { + if( aPointerCache_[i] ) + XFreeCursor( pDisp_, aPointerCache_[i] ); + } + + pXLib_->Remove( ConnectionNumber( pDisp_ ) ); + } + + if( pSalData->GetDisplay() == this ) + pSalData->SetSalDisplay( NULL ); +} + +static int DisplayHasEvent( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + if( ! pDisplay->IsDisplay() ) + return 0; + + vos::IMutex* pSalInstYieldMutex = + GetSalData()->m_pInstance->GetYieldMutex(); + ::vos::OGuard aGuard( *pSalInstYieldMutex ); + return pDisplay->IsEvent(); +} +static int DisplayQueue( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + vos::IMutex* pSalInstYieldMutex = + GetSalData()->m_pInstance->GetYieldMutex(); + ::vos::OGuard aGuard( *pSalInstYieldMutex ); + return XEventsQueued( pDisplay->GetDisplay(), + QueuedAfterReading ); +} +static int DisplayYield( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + vos::IMutex* pSalInstYieldMutex = + GetSalData()->m_pInstance->GetYieldMutex(); + ::vos::OGuard aGuard( *pSalInstYieldMutex ); + pDisplay->Yield(); + return TRUE; +} + +SalX11Display::SalX11Display( Display *display ) + : SalDisplay( display ) +{ + Init(); + + pXLib_->Insert( ConnectionNumber( pDisp_ ), + this, + (YieldFunc) DisplayHasEvent, + (YieldFunc) DisplayQueue, + (YieldFunc) DisplayYield ); +} + +SalX11Display::~SalX11Display() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalX11Display::~SalX11Display()\n" ); +#endif + if( pDisp_ ) + { + doDestruct(); + XCloseDisplay( pDisp_ ); + pDisp_ = NULL; + } +} + +void SalDisplay::initScreen( int nScreen ) const +{ + if( nScreen < 0 || nScreen >= static_cast<int>(m_aScreens.size()) ) + nScreen = m_nDefaultScreen; + ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]); + if( rSD.m_bInit ) + return; + rSD.m_bInit = true; + + XVisualInfo aVI; + Colormap aColMap; + + if( SalDisplay::BestVisual( pDisp_, nScreen, aVI ) ) // DefaultVisual + aColMap = DefaultColormap( pDisp_, nScreen ); + else + aColMap = XCreateColormap( pDisp_, + RootWindow( pDisp_, nScreen ), + aVI.visual, + AllocNone ); + + Screen* pScreen = ScreenOfDisplay( pDisp_, nScreen ); + + rSD.m_aSize = Size( WidthOfScreen( pScreen ), HeightOfScreen( pScreen ) ); + rSD.m_aRoot = RootWindow( pDisp_, nScreen ); + rSD.m_aVisual = SalVisual( &aVI ); + rSD.m_aColormap = SalColormap( this, aColMap, nScreen ); + + // we're interested in configure notification of root windows + InitRandR( rSD.m_aRoot ); + + // - - - - - - - - - - Reference Window/Default Drawable - - + XSetWindowAttributes aXWAttributes; + aXWAttributes.border_pixel = 0; + aXWAttributes.background_pixel = 0; + aXWAttributes.colormap = aColMap; + rSD.m_aRefWindow = XCreateWindow( pDisp_, + rSD.m_aRoot, + 0,0, 16,16, 0, + rSD.m_aVisual.GetDepth(), + InputOutput, + rSD.m_aVisual.GetVisual(), + CWBorderPixel|CWBackPixel|CWColormap, + &aXWAttributes ); + + // set client leader (session id gets set when session is started) + if( rSD.m_aRefWindow ) + { + // client leader must have WM_CLIENT_LEADER pointing to itself + XChangeProperty( pDisp_, + rSD.m_aRefWindow, + XInternAtom( pDisp_, "WM_CLIENT_LEADER", False ), + XA_WINDOW, + 32, + PropModeReplace, + (unsigned char*)&rSD.m_aRefWindow, + 1 + ); + + ByteString aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() ); + const char* argv[2]; + argv[0] = "/bin/sh"; + argv[1] = aExec.GetBuffer(); + XSetCommand( pDisp_, rSD.m_aRefWindow, const_cast<char**>(argv), 2 ); + XSelectInput( pDisp_, rSD.m_aRefWindow, PropertyChangeMask ); + + // - - - - - - - - - - GCs - - - - - - - - - - - - - - - - - + XGCValues values; + values.graphics_exposures = False; + values.fill_style = FillOpaqueStippled; + values.background = (1<<rSD.m_aVisual.GetDepth())-1; + values.foreground = 0; + + rSD.m_aCopyGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aAndInvertedGC= XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aAndGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aOrGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aStippleGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCFillStyle + | GCForeground + | GCBackground, + &values ); + + XSetFunction( pDisp_, rSD.m_aAndInvertedGC, GXandInverted ); + XSetFunction( pDisp_, rSD.m_aAndGC, GXand ); + // #44556# PowerPC Solaris 2.5 (XSun 3500) Bug: GXor = GXnop + //XSetFunction( pDisp_, pOrGC_, GXor ); + XSetFunction( pDisp_, rSD.m_aOrGC, GXxor ); + + if( 1 == rSD.m_aVisual.GetDepth() ) + { + XSetFunction( pDisp_, rSD.m_aCopyGC, GXcopyInverted ); + rSD.m_aMonoGC = rSD.m_aCopyGC; + } + else + { + Pixmap hPixmap = XCreatePixmap( pDisp_, rSD.m_aRefWindow, 1, 1, 1 ); + rSD.m_aMonoGC = XCreateGC( pDisp_, + hPixmap, + GCGraphicsExposures, + &values ); + XFreePixmap( pDisp_, hPixmap ); + } + rSD.m_hInvert50 = XCreateBitmapFromData( pDisp_, + rSD.m_aRefWindow, + invert50_bits, + invert50_width, + invert50_height ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::Init() +{ + for( size_t i = 0; i < POINTER_COUNT; i++ ) + aPointerCache_[i] = None; + + eWindowManager_ = otherwm; + nProperties_ = PROPERTY_DEFAULT; + hEventGuard_ = NULL; + mpFactory = (AttributeProvider*)NULL; + m_pCapture = NULL; + m_bXinerama = false; + + int nDisplayScreens = ScreenCount( pDisp_ ); + m_aScreens = std::vector<ScreenData>(nDisplayScreens); + + mbExactResolution = false; + /* #i15507# + * Xft resolution should take precedence since + * it is what modern desktops use. + */ + const char* pValStr = XGetDefault( pDisp_, "Xft", "dpi" ); + if( pValStr != NULL ) + { + const rtl::OString aValStr( pValStr ); + const long nDPI = (long) aValStr.toDouble(); + // guard against insane resolution + if( (nDPI >= 50) && (nDPI <= 500) ) + { + aResolution_ = Pair( nDPI, nDPI ); + mbExactResolution = true; + } + } + if( mbExactResolution == false ) + { + aResolution_ = + Pair( DPI( WidthOfScreen( DefaultScreenOfDisplay( pDisp_ ) ), DisplayWidthMM ( pDisp_, m_nDefaultScreen ) ), + DPI( HeightOfScreen( DefaultScreenOfDisplay( pDisp_ ) ), DisplayHeightMM( pDisp_, m_nDefaultScreen ) ) ); + } + + nMaxRequestSize_ = XExtendedMaxRequestSize( pDisp_ ) * 4; + if( !nMaxRequestSize_ ) + nMaxRequestSize_ = XMaxRequestSize( pDisp_ ) * 4; + + SetServerVendor(); + X11SalBitmap::ImplCreateCache(); + + hEventGuard_ = osl_createMutex(); + bLocal_ = FALSE; /* dont care, initialize later by + calling SalDisplay::IsLocal() */ + mbLocalIsValid = FALSE; /* bLocal_ is not yet initialized */ + + // - - - - - - - - - - Synchronize - - - - - - - - - - - - - + if( getenv( "SAL_SYNCHRONIZE" ) ) + XSynchronize( pDisp_, True ); + + // - - - - - - - - - - Keyboardmapping - - - - - - - - - - - + ModifierMapping(); + + // - - - - - - - - - - Window Manager - - - - - - - - - - - + m_pWMAdaptor = ::vcl_sal::WMAdaptor::createWMAdaptor( this ); + const char *pWM = getenv( "SAL_WM" ); + if( pWM ) + { + long int nWM = 0; + sscanf( pWM, "%li", &nWM ); + eWindowManager_ = SalWM(nWM); + } + else if( XInternAtom( pDisp_, "_SGI_TELL_WM", True ) ) + eWindowManager_ = FourDwm; + else if( XInternAtom( pDisp_, "KWM_RUNNING", True ) ) + eWindowManager_ = mwm; // naja, eigentlich kwm ... + else if( XInternAtom( pDisp_, "_OL_WIN_ATTR", True ) ) + eWindowManager_ = olwm; + else if( m_pWMAdaptor->getWindowManagerName().EqualsAscii( "Dtwm" ) ) + eWindowManager_ = dtwm; + + // - - - - - - - - - - Properties - - - - - - - - - - - - - + const char *pProperties = getenv( "SAL_PROPERTIES" ); + if( pProperties ) + sscanf( pProperties, "%li", &nProperties_ ); + else + { +#if defined DBG_UTIL || defined SUN || defined LINUX || defined FREEBSD + nProperties_ |= PROPERTY_FEATURE_Maximize; +#endif + // Server Bugs & Properties + if( GetServerVendor() == vendor_excursion ) + { + nProperties_ |= PROPERTY_BUG_Stipple; + nProperties_ |= PROPERTY_BUG_DrawLine; + nProperties_ &= ~PROPERTY_SUPPORT_XSetClipMask; + } + else + if( GetServerVendor() == vendor_attachmate ) + { + nProperties_ |= PROPERTY_BUG_CopyPlane_RevertBWPixel; + } + else + if( GetServerVendor() == vendor_ibm ) + { + nProperties_ |= PROPERTY_BUG_XA_FAMILY_NAME_nil; + + if( otherwm == eWindowManager_ ) eWindowManager_ = mwm; + } + else + if( GetServerVendor() == vendor_xfree ) + { + nProperties_ |= PROPERTY_BUG_XCopyArea_GXxor; +#if defined LINUX || defined FREEBSD + // otherwm and olwm are a kind of default, which are not detected + // carefully. if we are running linux (i.e. not netbsd) on an xfree + // display, fvwm is most probable the wm to choose, confusing with mwm + // doesn't harm. #57791# start maximized if possible + if( (otherwm == eWindowManager_) + || (olwm == eWindowManager_ )) + { + eWindowManager_ = fvwm; // ??? + nProperties_ |= PROPERTY_FEATURE_Maximize; + } +#else + if( otherwm == eWindowManager_ ) eWindowManager_ = winmgr; +#endif +#if defined SOLARIS && defined SPARC + nProperties_ |= PROPERTY_BUG_Bitmap_Bit_Order; + // solaris xlib seems to have problems with putting images + // in correct bit order to xfree 8 bit displays +#endif + } + else + if( GetServerVendor() == vendor_sun ) + { + // nicht alle! (bekannt: nur Sparc II CG3, CG6?) + nProperties_ &= ~PROPERTY_SUPPORT_XSetClipMask; + + // trusted solaris doesn't allow to change properties on the + // wm decoration window + if (sal_IsTrustedSolaris (pDisp_)) + nProperties_ |= PROPERTY_FEATURE_TrustedSolaris; + + // Fehler im Sun-Solaris X86 Server ! + if (ImageByteOrder(GetDisplay()) == LSBFirst) + { + nProperties_ |= PROPERTY_BUG_Tile; + nProperties_ |= PROPERTY_SUPPORT_3ButtonMouse; + } + else // MSBFirst Sun-Solaris Sparc Server + { + // XCopyPlane reverts black and white for 1bit bitmaps + // only sun, only 8bit pseudocolor target + if ( (GetVisual(m_nDefaultScreen).GetDepth() == 8) + && (GetVisual(m_nDefaultScreen).GetClass() == PseudoColor)) + nProperties_ |= PROPERTY_BUG_CopyPlane_RevertBWPixel; + // Fehler in Solaris 2.5.1 + if (VendorRelease ( GetDisplay() ) < 3600) + nProperties_ |= PROPERTY_BUG_FillPolygon_Tile; + } + + if( otherwm == eWindowManager_ ) + eWindowManager_ = olwm; + } + else + if( GetServerVendor() == vendor_sco ) + { + if( otherwm == eWindowManager_ ) eWindowManager_ = pmwm; + } + else + if( GetServerVendor() == vendor_sgi ) + { + if( GetVisual( m_nDefaultScreen ).GetDepth() > 8 && GetVisual( m_nDefaultScreen ).GetDepth() <= 16 ) + nProperties_ |= PROPERTY_BUG_XCopyArea_GXxor; + nProperties_ |= PROPERTY_SUPPORT_XSetClipMask; + + if( otherwm == eWindowManager_ ) + eWindowManager_ = FourDwm; + } + else + if( GetServerVendor() == vendor_hp ) + { + if( otherwm == eWindowManager_ ) eWindowManager_ = dtwm; + } + else + if( GetServerVendor() == vendor_hummingbird ) + { + if (GetVisual(m_nDefaultScreen).GetDepth() == 24) + nProperties_ |= PROPERTY_BUG_CopyArea_OnlySmallSlices; + } + + if( otherwm == eWindowManager_ ) + { + if( !XInternAtom( pDisp_, "_MOTIF_WM_INFO", True ) ) + eWindowManager_ = olwm; + // ??? + } + + if( winmgr == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_SetPos; + nProperties_ &= ~PROPERTY_SUPPORT_WM_Screen; + nProperties_ |= PROPERTY_FEATURE_Maximize; + } + else if( dtwm == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_ClientPos; + } + else if( pmwm == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_ClientPos; + } + } + + InitXinerama(); + + // initialize system settings update + m_pDtIntegrator = DtIntegrator::CreateDtIntegrator(); + +#ifdef DBG_UTIL + PrintInfo(); +#endif +} + +// Sound +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::Beep() const +{ + XBell( pDisp_, 0 ); +} + +// Keyboard +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +String SalDisplay::GetKeyNameFromKeySym( KeySym nKeySym ) const +{ + String aRet; + + // return an empty string for keysyms that are not bound to + // any key code + XLIB_KeyCode aKeyCode = XKeysymToKeycode( GetDisplay(), nKeySym ); + if( aKeyCode != 0 && aKeyCode != NoSymbol ) + { + if( !nKeySym ) + aRet = String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "???" ) ); + else + { + aRet = ::vcl_sal::getKeysymReplacementName( const_cast<SalDisplay*>(this)->GetKeyboardName(), nKeySym ); + if( ! aRet.Len() ) + { + const char *pString = XKeysymToString( nKeySym ); + int n = strlen( pString ); + if( n > 2 && pString[n-2] == '_' ) + aRet = String( pString, n-2, RTL_TEXTENCODING_ISO_8859_1 ); + else + aRet = String( pString, n, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + } + return aRet; +} + +inline KeySym sal_XModifier2Keysym( Display *pDisplay, + XModifierKeymap *pXModMap, + int n ) +{ + return XKeycodeToKeysym( pDisplay, + pXModMap->modifiermap[n*pXModMap->max_keypermod], + 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 ); + // Auf Sun-Servern und SCO-Severn beruecksichtigt XLookupString + // nicht den NumLock Modifier. + if( (GetServerVendor() == vendor_sun) + || (GetServerVendor() == vendor_sco) ) + { + XLIB_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; + nNumLockMask_ = 1<<i; + break; + } + } + } + + XFreeModifiermap( pXModMap ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +XubString SalDisplay::GetKeyName( USHORT nKeyCode ) const +{ + String aStrMap; + + if( nKeyCode & KEY_MOD1 ) + aStrMap += GetKeyNameFromKeySym( nCtrlKeySym_ ); + + if( nKeyCode & KEY_MOD2 ) + { + if( aStrMap.Len() ) + aStrMap += '+'; + aStrMap += GetKeyNameFromKeySym( nMod1KeySym_ ); + } + + if( nKeyCode & KEY_SHIFT ) + { + if( aStrMap.Len() ) + 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 ) // Existiert die Taste + 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_Prior; + break; + case KEY_PAGEDOWN: + nKeySym = XK_Next; + 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) + #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 + + 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 = SunXK_Copy; + break; + case KEY_OPEN: + nKeySym = SunXK_Open; + break; + case KEY_PASTE: + nKeySym = SunXK_Paste; + break; + case KEY_FIND: + nKeySym = XK_Find; + break; + case KEY_CUT: + nKeySym = GetServerVendor() == vendor_sun ? SunXK_Cut : XK_L10; + break; + case KEY_ADD: + nKeySym = XK_plus; + break; + case KEY_SUBTRACT: + nKeySym = XK_minus; + break; + case KEY_MULTIPLY: + nKeySym = XK_asterisk; + break; + case KEY_DIVIDE: + nKeySym = XK_slash; + break; + case KEY_POINT: + nKeySym = XK_period; + 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; + + default: + nKeySym = 0; + break; + } + + if( nKeySym ) + { + String aKeyName = GetKeyNameFromKeySym( nKeySym ); + if( aKeyName.Len() ) + { + if( aStrMap.Len() ) + aStrMap += '+'; + aStrMap += aKeyName; + } + else + aStrMap.Erase(); + } + else + aStrMap.Erase(); + + return aStrMap; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#ifndef IsISOKey +#define IsISOKey( n ) (0x0000FE00==((n)&0xFFFFFF00)) +#endif + +USHORT SalDisplay::GetKeyCode( KeySym keysym, char*pcPrintable ) const +{ + USHORT nKey = 0; + + if( XK_a <= keysym && XK_z >= keysym ) + nKey = (USHORT)(KEY_A + (keysym - XK_a)); + else if( XK_A <= keysym && XK_Z >= keysym ) + nKey = (USHORT)(KEY_A + (keysym - XK_A)); + else if( XK_0 <= keysym && XK_9 >= keysym ) + nKey = (USHORT)(KEY_0 + (keysym - XK_0)); + else if( IsModifierKey( keysym ) ) + ; + else if( IsKeypadKey( keysym ) ) + { + if( (keysym >= XK_KP_0) && (keysym <= XK_KP_9) ) + { + nKey = (USHORT)(KEY_0 + (keysym - XK_KP_0)); + *pcPrintable = '0' + nKey - KEY_0; + } + else if( IsPFKey( keysym ) ) + nKey = (USHORT)(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_Prior: // XK_KP_Page_Up + nKey = KEY_PAGEUP; + break; + case XK_KP_Next: // 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 = (USHORT)(KEY_F1 + keysym - XK_F1); + } + else switch( keysym ) + { + // - - - - - Sun X-Server Tastatur ohne 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 Tastatur ??? - - - - - - - - - - - - + case XK_L1: // XK_F11: + nKey = KEY_F11; // on a sun keyboard this actually is usally SunXK_Stop, + // but VCL doesn't have a key defintion 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 = (USHORT)(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_Prior: // XK_Page_Up + nKey = KEY_PAGEUP; + break; + case XK_Next: // 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; +/* + case XK_Break: + case XK_Select: + case XK_Execute: + case XK_Print: + case XK_Cancel: +*/ + } + } + 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; + 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_Linefeed: +// *pcPrintable = '\n'; +// 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, + unsigned char *pPrintable, + int *pLen, + KeySym *pUnmodifiedKeySym, + Status *pStatusReturn, + XIC aInputContext ) const +{ + KeySym nKeySym = 0; + memset( pPrintable, 0, *pLen ); + *pStatusReturn = 0; + + // first get the printable of the possibly modified KeySym + if ( (aInputContext == 0) + || (pEvent->type == KeyRelease) + || (mpInputMethod != NULL && mpInputMethod->PosixLocale()) ) + { + // XmbLookupString must not be called for KeyRelease events + // Cannot enter space in c locale problem #89616# #88978# btraq #4478197 + *pLen = XLookupString( pEvent, (char*)pPrintable, 1, &nKeySym, NULL ); + } + else + { + *pLen = XmbLookupString( aInputContext, + pEvent, (char*)pPrintable, *pLen - 1, &nKeySym, pStatusReturn ); + + // Lookup the string again, now with appropriate size + if ( *pStatusReturn == XBufferOverflow ) + { + pPrintable[ 0 ] = (char)0; + return 0; + } + + switch ( *pStatusReturn ) + { + case XBufferOverflow: + /* unhandled error */ + break; + case XLookupNone: + /* unhandled error */ + break; + case XLookupKeySym: + /* #72223# 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 ] = (char)nKeySym; + } + break; + case XLookupBoth: + case XLookupChars: + + /* nothing to, char allready in pPrintable */ + break; + } + } + + if( !bNumLockFromXS_ + && (IsCursorKey(nKeySym) + || IsFunctionKey(nKeySym) + || IsKeypadKey(nKeySym) + || XK_Delete == nKeySym ) ) + { + // Bei einigen X-Servern muss man bei den Keypadtasten + // schon sehr genau hinschauen. ZB. Solaris XServer: + // 2, 4, 6, 8 werden als Cursorkeys klassifiziert (Up, Down, Left, Right + // 1, 3, 5, 9 werden als Functionkeys klassifiziert (F27,F29,F33,F35) + // 0 als Keypadkey und der Dezimalpunkt gar nicht (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 = XKeycodeToKeysym( GetDisplay(), pEvent->keycode, 0); + + return nKeySym; +} + +// Pointer +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define MAKE_BITMAP( name ) \ + XCreateBitmapFromData( pDisp_, \ + DefaultRootWindow( pDisp_ ), \ + 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 + +XLIB_Cursor SalDisplay::GetPointer( int ePointerStyle ) +{ + if( ePointerStyle >= POINTER_COUNT ) + return 0; + + XLIB_Cursor &aCur = aPointerCache_[ePointerStyle]; + + if( aCur != None ) + return aCur; + + Pixmap aCursBitmap = None, aMaskBitmap = None; + unsigned int nXHot = 0, nYHot = 0; + + switch( ePointerStyle ) + { + case POINTER_NULL: + MAKE_CURSOR( null ); + break; + case POINTER_ARROW: + aCur = XCreateFontCursor( pDisp_, XC_left_ptr ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WAIT: + aCur = XCreateFontCursor( pDisp_, XC_watch ); + break; + case POINTER_TEXT: // Mouse Pointer ist ein "I" Beam + aCur = XCreateFontCursor( pDisp_, XC_xterm ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HELP: + aCur = XCreateFontCursor( pDisp_, XC_question_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_CROSS: // Mouse Pointer ist ein Kreuz + aCur = XCreateFontCursor( pDisp_, XC_crosshair ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_NSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_SSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_ESIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_NSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_WSIZE: + aCur = XCreateFontCursor( pDisp_, XC_left_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_ESIZE: + aCur = XCreateFontCursor( pDisp_, XC_right_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_NWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_left_corner ); + break; + case POINTER_NESIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_right_corner ); + break; + case POINTER_SWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner ); + break; + case POINTER_SESIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner ); + break; + case POINTER_WINDOW_NWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_left_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_NESIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_right_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SESIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HSPLIT: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + break; + case POINTER_VSPLIT: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + break; + case POINTER_HSIZEBAR: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); // ??? + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_VSIZEBAR: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); // ??? + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_REFHAND: + aCur = XCreateFontCursor( pDisp_, XC_hand1 ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HAND: + aCur = XCreateFontCursor( pDisp_, XC_hand2 ); + break; + case POINTER_MAGNIFY: + MAKE_CURSOR( magnify_ ); + break; + case POINTER_FILL: + MAKE_CURSOR( fill_ ); + break; + case POINTER_MOVE: + aCur = XCreateFontCursor( pDisp_, XC_fleur ); + break; + case POINTER_MOVEDATA: + MAKE_CURSOR( movedata_ ); + break; + case POINTER_COPYDATA: + MAKE_CURSOR( copydata_ ); + break; + case POINTER_MOVEFILE: + MAKE_CURSOR( movefile_ ); + break; + case POINTER_COPYFILE: + MAKE_CURSOR( copyfile_ ); + break; + case POINTER_MOVEFILES: + MAKE_CURSOR( movefiles_ ); + break; + case POINTER_COPYFILES: + MAKE_CURSOR( copyfiles_ ); + break; + case POINTER_NOTALLOWED: + MAKE_CURSOR( nodrop_ ); + break; + case POINTER_ROTATE: + MAKE_CURSOR( rotate_ ); + break; + case POINTER_HSHEAR: + MAKE_CURSOR( hshear_ ); + break; + case POINTER_VSHEAR: + MAKE_CURSOR( vshear_ ); + break; + case POINTER_DRAW_LINE: + MAKE_CURSOR( drawline_ ); + break; + case POINTER_DRAW_RECT: + MAKE_CURSOR( drawrect_ ); + break; + case POINTER_DRAW_POLYGON: + MAKE_CURSOR( drawpolygon_ ); + break; + case POINTER_DRAW_BEZIER: + MAKE_CURSOR( drawbezier_ ); + break; + case POINTER_DRAW_ARC: + MAKE_CURSOR( drawarc_ ); + break; + case POINTER_DRAW_PIE: + MAKE_CURSOR( drawpie_ ); + break; + case POINTER_DRAW_CIRCLECUT: + MAKE_CURSOR( drawcirclecut_ ); + break; + case POINTER_DRAW_ELLIPSE: + MAKE_CURSOR( drawellipse_ ); + break; + case POINTER_DRAW_CONNECT: + MAKE_CURSOR( drawconnect_ ); + break; + case POINTER_DRAW_TEXT: + MAKE_CURSOR( drawtext_ ); + break; + case POINTER_MIRROR: + MAKE_CURSOR( mirror_ ); + break; + case POINTER_CROOK: + MAKE_CURSOR( crook_ ); + break; + case POINTER_CROP: + MAKE_CURSOR( crop_ ); + break; + case POINTER_MOVEPOINT: + MAKE_CURSOR( movepoint_ ); + break; + case POINTER_MOVEBEZIERWEIGHT: + MAKE_CURSOR( movebezierweight_ ); + break; + case POINTER_DRAW_FREEHAND: + MAKE_CURSOR( drawfreehand_ ); + break; + case POINTER_DRAW_CAPTION: + MAKE_CURSOR( drawcaption_ ); + break; + case POINTER_PEN: // Mouse Pointer ist ein Stift + aCur = XCreateFontCursor( pDisp_, XC_pencil ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_LINKDATA: + MAKE_CURSOR( linkdata_ ); + break; + case POINTER_MOVEDATALINK: + MAKE_CURSOR( movedlnk_ ); + break; + case POINTER_COPYDATALINK: + MAKE_CURSOR( copydlnk_ ); + break; + case POINTER_LINKFILE: + MAKE_CURSOR( linkfile_ ); + break; + case POINTER_MOVEFILELINK: + MAKE_CURSOR( moveflnk_ ); + break; + case POINTER_COPYFILELINK: + MAKE_CURSOR( copyflnk_ ); + break; + case POINTER_CHART: + MAKE_CURSOR( chart_ ); + break; + case POINTER_DETECTIVE: + MAKE_CURSOR( detective_ ); + break; + case POINTER_PIVOT_COL: + MAKE_CURSOR( pivotcol_ ); + break; + case POINTER_PIVOT_ROW: + MAKE_CURSOR( pivotrow_ ); + break; + case POINTER_PIVOT_FIELD: + MAKE_CURSOR( pivotfld_ ); + break; + case POINTER_PIVOT_DELETE: + MAKE_CURSOR( pivotdel_ ); + break; + case POINTER_CHAIN: + MAKE_CURSOR( chain_ ); + break; + case POINTER_CHAIN_NOTALLOWED: + MAKE_CURSOR( chainnot_ ); + break; + case POINTER_TIMEEVENT_MOVE: + MAKE_CURSOR( timemove_ ); + break; + case POINTER_TIMEEVENT_SIZE: + MAKE_CURSOR( timesize_ ); + break; + case POINTER_AUTOSCROLL_N: + MAKE_CURSOR(asn_ ); + break; + case POINTER_AUTOSCROLL_S: + MAKE_CURSOR( ass_ ); + break; + case POINTER_AUTOSCROLL_W: + MAKE_CURSOR( asw_ ); + break; + case POINTER_AUTOSCROLL_E: + MAKE_CURSOR( ase_ ); + break; + case POINTER_AUTOSCROLL_NW: + MAKE_CURSOR( asnw_ ); + break; + case POINTER_AUTOSCROLL_NE: + MAKE_CURSOR( asne_ ); + break; + case POINTER_AUTOSCROLL_SW: + MAKE_CURSOR( assw_ ); + break; + case POINTER_AUTOSCROLL_SE: + MAKE_CURSOR( asse_ ); + break; + case POINTER_AUTOSCROLL_NS: + MAKE_CURSOR( asns_ ); + break; + case POINTER_AUTOSCROLL_WE: + MAKE_CURSOR( aswe_ ); + break; + case POINTER_AUTOSCROLL_NSWE: + MAKE_CURSOR( asnswe_ ); + break; + case POINTER_AIRBRUSH: + MAKE_CURSOR( airbrush_ ); + break; + case POINTER_TEXT_VERTICAL: + MAKE_CURSOR( vertcurs_ ); + break; + + // --> FME 2004-07-30 #i32329# Enhanced table selection + case POINTER_TAB_SELECT_S: + MAKE_CURSOR( tblsels_ ); + break; + case POINTER_TAB_SELECT_E: + MAKE_CURSOR( tblsele_ ); + break; + case POINTER_TAB_SELECT_SE: + MAKE_CURSOR( tblselse_ ); + break; + case POINTER_TAB_SELECT_W: + MAKE_CURSOR( tblselw_ ); + break; + case POINTER_TAB_SELECT_SW: + MAKE_CURSOR( tblselsw_ ); + break; + // <-- + + // --> FME 2004-08-16 #i20119# Paintbrush tool + case POINTER_PAINTBRUSH : + MAKE_CURSOR( paintbrush_ ); + break; + // <-- + + default: + DBG_ERROR("pointer not implemented"); + aCur = XCreateFontCursor( pDisp_, XC_arrow ); + break; + } + + if( None == aCur ) + { + XColor aBlack, aWhite, aDummy; + Colormap hColormap = GetColormap(m_nDefaultScreen).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 ) +{ + if( !pCapture ) + { + m_pCapture = NULL; + XUngrabPointer( GetDisplay(), CurrentTime ); + XFlush( GetDisplay() ); + return 0; + } + + m_pCapture = NULL; + + // FIXME: get rid of X11SalFrame + const SystemEnvData* pEnvData = pCapture->GetSystemData(); + int ret = XGrabPointer( GetDisplay(), + (XLIB_Window)pEnvData->aWindow, + False, + PointerMotionMask| ButtonPressMask|ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + static_cast<X11SalFrame*>(pCapture)->GetCursor(), + CurrentTime ); + + if( ret != GrabSuccess ) + { + DBG_ASSERT( 1, "SalDisplay::CaptureMouse could not grab pointer\n"); + return -1; + } + + m_pCapture = pCapture; + return 1; +} + +// Events +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void SalDisplay::SendInternalEvent( SalFrame* pFrame, void* pData, USHORT nEvent ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + m_aUserEvents.push_back( SalUserEvent( pFrame, pData, nEvent ) ); + + // Notify SalXLib::Yield() of a pending event. + pXLib_->PostUserEvent(); + + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::SendInternalEvent !acquireMutex\n" ); + } +} + +void SalDisplay::CancelInternalEvent( SalFrame* pFrame, void* pData, USHORT nEvent ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( ! m_aUserEvents.empty() ) + { + std::list< SalUserEvent >::iterator it, next; + next = m_aUserEvents.begin(); + do + { + it = next++; + if( it->m_pFrame == pFrame && + it->m_pData == pData && + it->m_nEvent == nEvent ) + { + m_aUserEvents.erase( it ); + } + } while( next != m_aUserEvents.end() ); + } + + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::CancelInternalEvent !acquireMutex\n" ); + } +} + +BOOL SalX11Display::IsEvent() +{ + BOOL bRet = FALSE; + + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( m_aUserEvents.begin() != m_aUserEvents.end() ) + bRet = TRUE; + osl_releaseMutex( hEventGuard_ ); + } + + if( bRet || XEventsQueued( pDisp_, QueuedAlready ) ) + return TRUE; + + XFlush( pDisp_ ); + return FALSE; +} + +bool SalDisplay::DispatchInternalEvent() +{ + SalFrame* pFrame = NULL; + void* pData = NULL; + USHORT nEvent = 0; + + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( m_aUserEvents.begin() != m_aUserEvents.end() ) + { + pFrame = m_aUserEvents.front().m_pFrame; + pData = m_aUserEvents.front().m_pData; + nEvent = m_aUserEvents.front().m_nEvent; + + m_aUserEvents.pop_front(); + } + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::Yield !acquireMutex\n" ); + } + + if( pFrame ) + pFrame->CallCallback( nEvent, pData ); + + return pFrame != NULL; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void SalX11Display::Yield() +{ + if( DispatchInternalEvent() ) + return; + + XEvent aEvent; + DBG_ASSERT( static_cast<SalYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())->GetThreadId() == + vos::OThread::getCurrentIdentifier(), + "will crash soon since solar mutex not locked in SalDisplay::Yield" ); + + XNextEvent( pDisp_, &aEvent ); + + Dispatch( &aEvent ); + +#ifdef DBG_UTIL + if( pXLib_->HasXErrorOccured() ) + { + XFlush( pDisp_ ); + PrintEvent( "SalDisplay::Yield (WasXError)", &aEvent ); + } +#endif + pXLib_->ResetXErrorOccured(); +} + +long SalX11Display::Dispatch( XEvent *pEvent ) +{ + if( pEvent->type == XLIB_KeyPress || pEvent->type == KeyRelease ) + { + XLIB_Window aWindow = pEvent->xkey.window; + + std::list< SalFrame* >::const_iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + { + const X11SalFrame* pFrame = static_cast< const X11SalFrame* >(*it); + if( pFrame->GetWindow() == aWindow || pFrame->GetShellWindow() == aWindow ) + { + aWindow = pFrame->GetWindow(); + break; + } + } + if( it != m_aFrames.end() ) + { + if ( mpInputMethod->FilterEvent( pEvent , aWindow ) ) + return 0; + } + } + else + if ( mpInputMethod->FilterEvent( pEvent, None ) ) + return 0; + + SalInstance* pInstance = GetSalData()->m_pInstance; + 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( unsigned int i = 0; i < m_aScreens.size(); i++ ) + { + if( pEvent->xproperty.window == m_aScreens[i].m_aRefWindow ) + { + std::list< SalFrame* >::const_iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + (*it)->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); + return 0; + } + } + } + break; + case MappingNotify: + if( MappingKeyboard == pEvent->xmapping.request || + MappingModifier == pEvent->xmapping.request ) + { + XRefreshKeyboardMapping( &pEvent->xmapping ); + if( MappingModifier == pEvent->xmapping.request ) + ModifierMapping(); + if( MappingKeyboard == pEvent->xmapping.request ) // refresh mapping + GetKeyboardName( TRUE ); + } + break; + case ButtonPress: + case ButtonRelease: + m_nLastUserEventTime = pEvent->xbutton.time; + break; + case XLIB_KeyPress: + case KeyRelease: + m_nLastUserEventTime = pEvent->xkey.time; + break; + default: + + if ( GetKbdExtension()->UseExtension() + && GetKbdExtension()->GetEventBase() == pEvent->type ) + { + GetKbdExtension()->Dispatch( pEvent ); + return 1; + } + break; + } + + std::list< SalFrame* >::iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + { + X11SalFrame* pFrame = static_cast< X11SalFrame* >(*it); + XLIB_Window aDispatchWindow = pEvent->xany.window; + if( pFrame->GetWindow() == aDispatchWindow + || pFrame->GetShellWindow() == aDispatchWindow + || pFrame->GetForeignParent() == aDispatchWindow + ) + { + return pFrame->Dispatch( pEvent ); + } + if( pEvent->type == ConfigureNotify && pEvent->xconfigure.window == pFrame->GetStackingWindow() ) + { + return pFrame->Dispatch( pEvent ); + } + } + + // dispatch to salobjects + X11SalObject::Dispatch( pEvent ); + + // is this perhaps a root window that changed size ? + processRandREvent( pEvent ); + + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::PrintEvent( const ByteString &rComment, + XEvent *pEvent ) const +{ + if( pEvent->type <= MappingNotify ) + { + fprintf( stderr, "[%s] %s s=%d w=%ld\n", + rComment.GetBuffer(), + EventNames[pEvent->type], + pEvent->xany.send_event, + pEvent->xany.window ); + + switch( pEvent->type ) + { + case XLIB_KeyPress: + case KeyRelease: + fprintf( stderr, "\t\ts=%d c=%d\n", + pEvent->xkey.state, + pEvent->xkey.keycode ); + break; + + case ButtonPress: + case ButtonRelease: + fprintf( stderr, "\t\ts=%d b=%d x=%d y=%d rx=%d ry=%d\n", + pEvent->xbutton.state, + pEvent->xbutton.button, + pEvent->xbutton.x, + pEvent->xbutton.y, + pEvent->xbutton.x_root, + pEvent->xbutton.y_root ); + break; + + case MotionNotify: + fprintf( stderr, "\t\ts=%d x=%d y=%d\n", + pEvent->xmotion.state, + pEvent->xmotion.x, + pEvent->xmotion.y ); + break; + + case EnterNotify: + case LeaveNotify: + fprintf( stderr, "\t\tm=%d f=%d x=%d y=%d\n", + pEvent->xcrossing.mode, + pEvent->xcrossing.focus, + pEvent->xcrossing.x, + pEvent->xcrossing.y ); + break; + + case FocusIn: + case FocusOut: + fprintf( stderr, "\t\tm=%d d=%d\n", + pEvent->xfocus.mode, + pEvent->xfocus.detail ); + break; + + case Expose: + case GraphicsExpose: + fprintf( stderr, "\t\tc=%d %d*%d %d+%d\n", + pEvent->xexpose.count, + pEvent->xexpose.width, + pEvent->xexpose.height, + pEvent->xexpose.x, + pEvent->xexpose.y ); + break; + + case VisibilityNotify: + fprintf( stderr, "\t\ts=%d\n", + pEvent->xvisibility.state ); + break; + + case CreateNotify: + case DestroyNotify: + break; + + case MapNotify: + case UnmapNotify: + break; + + case ReparentNotify: + fprintf( stderr, "\t\tp=%d x=%d y=%d\n", + sal::static_int_cast< int >(pEvent->xreparent.parent), + pEvent->xreparent.x, + pEvent->xreparent.y ); + break; + + case ConfigureNotify: + fprintf( stderr, "\t\tb=%d %d*%d %d+%d\n", + pEvent->xconfigure.border_width, + pEvent->xconfigure.width, + pEvent->xconfigure.height, + pEvent->xconfigure.x, + pEvent->xconfigure.y ); + break; + + case PropertyNotify: + fprintf( stderr, "\t\ta=%s (0x%X)\n", + GetAtomName( pDisp_, pEvent->xproperty.atom ), + sal::static_int_cast< unsigned int >( + pEvent->xproperty.atom) ); + break; + + case ColormapNotify: + fprintf( stderr, "\t\tc=%ld n=%d s=%d\n", + pEvent->xcolormap.colormap, + pEvent->xcolormap.c_new, + pEvent->xcolormap.state ); + break; + + case ClientMessage: + fprintf( stderr, "\t\ta=%s (0x%X) f=%i [0x%lX,0x%lX,0x%lX,0x%lX,0x%lX])\n", + GetAtomName( pDisp_, pEvent->xclient.message_type ), + sal::static_int_cast< unsigned int >( + pEvent->xclient.message_type), + pEvent->xclient.format, + 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: + fprintf( stderr, "\t\tr=%sd\n", + MappingModifier == pEvent->xmapping.request + ? "MappingModifier" + : MappingKeyboard == pEvent->xmapping.request + ? "MappingKeyboard" + : "MappingPointer" ); + + break; + } + } + else + fprintf( stderr, "[%s] %d s=%d w=%ld\n", + rComment.GetBuffer(), + pEvent->type, + pEvent->xany.send_event, + pEvent->xany.window ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::PrintInfo() const +{ + if( IsDisplay() ) + { + fprintf( stderr, "\n" ); + fprintf( stderr, "Environment\n" ); + fprintf( stderr, "\t$XENVIRONMENT \t\"%s\"\n", + GetEnv( "XENVIRONMENT" ) ); + fprintf( stderr, "\t$DISPLAY \t\"%s\"\n", + GetEnv( "DISPLAY" ) ); + fprintf( stderr, "\t$SAL_VISUAL \t\"%s\"\n", + GetEnv( "SAL_VISUAL" ) ); + fprintf( stderr, "\t$SAL_FONTPATH \t\"%s\"\n", + GetEnv( "SAL_FONTPATH" ) ); + fprintf( stderr, "\t$SAL_NOSEGV \t\"%s\"\n", + GetEnv( "SAL_NOSEGV" ) ); + fprintf( stderr, "\t$SAL_IGNOREXERRORS\t\"%s\"\n", + GetEnv( "SAL_IGNOREXERRORS" ) ); + fprintf( stderr, "\t$SAL_PROPERTIES \t\"%s\"\n", + GetEnv( "SAL_PROPERTIES" ) ); + fprintf( stderr, "\t$SAL_WM \t\"%s\"\n", + GetEnv( "SAL_WM" ) ); + fprintf( stderr, "\t$SAL_SYNCHRONIZE \t\"%s\"\n", + GetEnv( "SAL_SYNCHRONIZE" ) ); + + char sHostname[ 120 ]; + gethostname (sHostname, 120 ); + fprintf( stderr, "Client\n" ); + fprintf( stderr, "\tHost \t\"%s\"\n", + sHostname ); + + fprintf( stderr, "Display\n" ); + fprintf( stderr, "\tHost \t\"%s\"\n", + DisplayString(pDisp_) ); + fprintf( stderr, "\tVendor (Release) \t\"%s (%d)\"\n", + ServerVendor(pDisp_), VendorRelease(pDisp_) ); + fprintf( stderr, "\tProtocol \t%d.%d\n", + ProtocolVersion(pDisp_), ProtocolRevision(pDisp_) ); + fprintf( stderr, "\tScreen (count,def)\t%d (%d,%d)\n", + m_nDefaultScreen, ScreenCount(pDisp_), DefaultScreen(pDisp_) ); + fprintf( stderr, "\tshift ctrl alt \t%s (0x%X) %s (0x%X) %s (0x%X)\n", + KeyStr( nShiftKeySym_ ), sal::static_int_cast< unsigned int >(nShiftKeySym_), + KeyStr( nCtrlKeySym_ ), sal::static_int_cast< unsigned int >(nCtrlKeySym_), + KeyStr( nMod1KeySym_ ), sal::static_int_cast< unsigned int >(nMod1KeySym_) ); + if( XExtendedMaxRequestSize(pDisp_) * 4 ) + fprintf( stderr, "\tXMaxRequestSize \t%ld %ld [bytes]\n", + XMaxRequestSize(pDisp_) * 4, XExtendedMaxRequestSize(pDisp_) * 4 ); + if( GetProperties() != PROPERTY_DEFAULT ) + fprintf( stderr, "\tProperties \t0x%lX\n", GetProperties() ); + if( eWindowManager_ != otherwm ) + fprintf( stderr, "\tWindowmanager \t%d\n", eWindowManager_ ); + fprintf( stderr, "\tWMName \t%s\n", rtl::OUStringToOString( getWMAdaptor()->getWindowManagerName(), osl_getThreadTextEncoding() ).getStr() ); + } + fprintf( stderr, "Screen\n" ); + fprintf( stderr, "\tResolution/Size \t%ld*%ld %ld*%ld %.1lf\"\n", + aResolution_.A(), aResolution_.B(), + m_aScreens[m_nDefaultScreen].m_aSize.Width(), m_aScreens[m_nDefaultScreen].m_aSize.Height(), + Hypothenuse( DisplayWidthMM ( pDisp_, m_nDefaultScreen ), + DisplayHeightMM( pDisp_, m_nDefaultScreen ) ) / 25.4 ); + fprintf( stderr, "\tBlack&White \t%lu %lu\n", + GetColormap(m_nDefaultScreen).GetBlackPixel(), GetColormap(m_nDefaultScreen).GetWhitePixel() ); + fprintf( stderr, "\tRGB \t0x%lx 0x%lx 0x%lx\n", + GetVisual(m_nDefaultScreen).red_mask, GetVisual(m_nDefaultScreen).green_mask, GetVisual(m_nDefaultScreen).blue_mask ); + fprintf( stderr, "\tVisual \t%d-bit %s ID=0x%x\n", + GetVisual(m_nDefaultScreen).GetDepth(), + VisualClassName[ GetVisual(m_nDefaultScreen).GetClass() ], + sal::static_int_cast< unsigned int >(GetVisual(m_nDefaultScreen).GetVisualId()) ); +} + +int SalDisplay::addXineramaScreenUnique( long i_nX, long i_nY, long i_nWidth, 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_aXineramaScreens[n].SetSize( Size( i_nWidth, i_nHeight ) ); + } + return (int)n; + } + } + m_aXineramaScreens.push_back( Rectangle( Point( i_nX, i_nY ), Size( i_nWidth, i_nHeight ) ) ); + return (int)m_aXineramaScreens.size()-1; +} + +void SalDisplay::InitXinerama() +{ + if( m_aScreens.size() > 1 ) + { + m_bXinerama = false; + return; // multiple screens mean no xinerama + } +#ifdef USE_XINERAMA +#if defined(USE_XINERAMA_XSUN) + int nFramebuffers = 1; + if( XineramaGetState( pDisp_, m_nDefaultScreen ) ) + { + XRectangle pFramebuffers[MAXFRAMEBUFFERS]; + unsigned char hints[MAXFRAMEBUFFERS]; + int result = XineramaGetInfo( pDisp_, + m_nDefaultScreen, + pFramebuffers, + hints, + &nFramebuffers ); + if( result > 0 && nFramebuffers > 1 ) + { + m_bXinerama = true; + m_aXineramaScreens = std::vector<Rectangle>(); + for( int i = 0; i < nFramebuffers; i++ ) + addXineramaScreenUnique( pFramebuffers[i].x, + pFramebuffers[i].y, + pFramebuffers[i].width, + pFramebuffers[i].height ); + } + } +#elif defined(USE_XINERAMA_XORG) +if( XineramaIsActive( pDisp_ ) ) +{ + int nFramebuffers = 1; + XineramaScreenInfo* pScreens = XineramaQueryScreens( pDisp_, &nFramebuffers ); + if( pScreens ) + { + if( nFramebuffers > 1 ) + { + m_aXineramaScreens = std::vector<Rectangle>(); + for( int i = 0; i < nFramebuffers; i++ ) + { + addXineramaScreenUnique( pScreens[i].x_org, + pScreens[i].y_org, + pScreens[i].width, + pScreens[i].height ); + } + m_bXinerama = m_aXineramaScreens.size() > 1; + } + XFree( pScreens ); + } +} +#endif +#if OSL_DEBUG_LEVEL > 1 + if( m_bXinerama ) + { + for( std::vector< Rectangle >::const_iterator it = m_aXineramaScreens.begin(); it != m_aXineramaScreens.end(); ++it ) + fprintf( stderr, "Xinerama screen: %ldx%ld+%ld+%ld\n", it->GetWidth(), it->GetHeight(), it->Left(), it->Top() ); + } +#endif +#endif // USE_XINERAMA +} + +void SalDisplay::registerFrame( SalFrame* pFrame ) +{ + m_aFrames.push_front( pFrame ); +} + +void SalDisplay::deregisterFrame( SalFrame* pFrame ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + std::list< SalUserEvent >::iterator it = m_aUserEvents.begin(); + while ( it != m_aUserEvents.end() ) + { + if( it->m_pFrame == pFrame ) + it = m_aUserEvents.erase( it ); + else + ++it; + } + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ERROR( "SalDisplay::deregisterFrame !acquireMutex\n" ); + } + + m_aFrames.remove( pFrame ); +} + + +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->GetDefaultScreenNumber() ) && + i_pEvent->xproperty.atom == pSalDisplay->getWMAdaptor()->getAtom( WMAdaptor::SAL_GETTIMEEVENT ) + ) + return True; + + return False; + } +} + +XLIB_Time SalDisplay::GetLastUserEventTime( 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( GetDefaultScreenNumber() ), + nAtom, nAtom, 8, PropModeReplace, &c, 1 ); + XFlush( GetDisplay() ); + + if( ! XIfEventWithTimeout( &aEvent, (XPointer)this, timestamp_predicate ) ) + { + // this should not happen at all; still sometimes it happens + aEvent.xproperty.time = CurrentTime; + } + + m_nLastUserEventTime = aEvent.xproperty.time; + } + return m_nLastUserEventTime; +} + +bool SalDisplay::XIfEventWithTimeout( XEvent* o_pEvent, XPointer i_pPredicateData, + X_if_predicate i_pPredicate, long i_nTimeout ) const +{ + /* #i99360# ugly workaround an X11 library bug + this replaces the following call: + XIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ); + */ + bool bRet = true; + + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + // wait for some event to arrive + struct pollfd aFD; + aFD.fd = ConnectionNumber(GetDisplay()); + aFD.events = POLLIN; + aFD.revents = 0; + poll( &aFD, 1, i_nTimeout ); + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + poll( &aFD, 1, i_nTimeout ); // try once more for a packet of events from the Xserver + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + bRet = false; + } + } + } + return bRet; +} + +// -=-= SalVisual -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalVisual::SalVisual() +{ + rtl_zeroMemory( this, sizeof( SalVisual ) ); +} + +SalVisual::SalVisual( const XVisualInfo* pXVI ) +{ + *(XVisualInfo*)this = *pXVI; + if( GetClass() == TrueColor ) + { + nRedShift_ = sal_Shift( red_mask ); + nGreenShift_ = sal_Shift( green_mask ); + nBlueShift_ = sal_Shift( blue_mask ); + + nRedBits_ = sal_significantBits( red_mask ); + nGreenBits_ = sal_significantBits( green_mask ); + nBlueBits_ = sal_significantBits( blue_mask ); + + if( GetDepth() == 24 ) + if( red_mask == 0xFF0000 ) + if( green_mask == 0xFF00 ) + if( blue_mask == 0xFF ) + eRGBMode_ = RGB; + else + eRGBMode_ = other; + else if( blue_mask == 0xFF00 ) + if( green_mask == 0xFF ) + eRGBMode_ = RBG; + else + eRGBMode_ = other; + else + eRGBMode_ = other; + else if( green_mask == 0xFF0000 ) + if( red_mask == 0xFF00 ) + if( blue_mask == 0xFF ) + eRGBMode_ = GRB; + else + eRGBMode_ = other; + else if( blue_mask == 0xFF00 ) + if( red_mask == 0xFF ) + eRGBMode_ = GBR; + else + eRGBMode_ = other; + else + eRGBMode_ = other; + else if( blue_mask == 0xFF0000 ) + if( red_mask == 0xFF00 ) + if( green_mask == 0xFF ) + eRGBMode_ = BRG; + else + eRGBMode_ = other; + else if( green_mask == 0xFF00 ) + if( red_mask == 0xFF ) + eRGBMode_ = BGR; + else + eRGBMode_ = other; + else + eRGBMode_ = other; + else + eRGBMode_ = other; + else + eRGBMode_ = other; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalVisual::~SalVisual() +{ + if( -1 == screen && VisualID(-1) == visualid ) delete visual; +} + +// Konvertiert die Reihenfolge der Bytes eines Pixel in Bytes eines SalColors +// fuer die 6 XXXA ist das nicht reversibel +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// SalColor is RGB (ABGR) a=0xFF000000, r=0xFF0000, g=0xFF00, b=0xFF + +#define SALCOLOR RGB +#define SALCOLORREVERSE BGR + +BOOL SalVisual::Convert( int &n0, int &n1, int &n2, int &n3 ) +{ + int n; + + switch( GetMode() ) + { + case other: + return FALSE; + case SALCOLOR: + break; + case SALCOLORREVERSE: + case RBG: + case BRG: + case GBR: + case GRB: + return Convert( n0, n1, n2 ); + case RGBA: + n = n0; + n0 = n1; + n1 = n2; + n2 = n3; + n3 = n; + break; + case BGRA: + case RBGA: + case BRGA: + case GBRA: + case GRBA: + default: + fprintf( stderr, "SalVisual::Convert %d\n", GetMode() ); + abort(); + } + return TRUE; +} + +BOOL SalVisual::Convert( int &n0, int &n1, int &n2 ) +{ + int n; + + switch( GetMode() ) + { + case other: + return FALSE; + case SALCOLOR: + break; + case RBG: + n = n0; + n0 = n1; + n1 = n; + break; + case GRB: + n = n1; + n1 = n2; + n2 = n; + break; + case SALCOLORREVERSE: + n = n0; + n0 = n2; + n2 = n; + break; + case BRG: + n = n0; + n0 = n1; + n1 = n2; + n2 = n; + break; + case GBR: + n = n2; + n2 = n1; + n1 = n0; + n0 = n; + break; + default: + fprintf( stderr, "SalVisual::Convert %d\n", GetMode() ); + abort(); + } + return TRUE; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColor SalVisual::GetTCColor( Pixel nPixel ) const +{ + if( SALCOLOR == eRGBMode_ ) + return (SalColor)nPixel; + + if( SALCOLORREVERSE == eRGBMode_ ) + return MAKE_SALCOLOR( (nPixel & 0x0000FF), + (nPixel & 0x00FF00) >> 8, + (nPixel & 0xFF0000) >> 16); + + Pixel r = nPixel & red_mask; + Pixel g = nPixel & green_mask; + Pixel b = nPixel & blue_mask; + + if( other != eRGBMode_ ) // 8+8+8=24 + return MAKE_SALCOLOR( r >> nRedShift_, + g >> nGreenShift_, + b >> nBlueShift_ ); + + if( nRedShift_ > 0 ) r >>= nRedShift_; else r <<= -nRedShift_; + if( nGreenShift_ > 0 ) g >>= nGreenShift_; else g <<= -nGreenShift_; + if( nBlueShift_ > 0 ) b >>= nBlueShift_; else b <<= -nBlueShift_; + + if( nRedBits_ != 8 ) + r |= (r & 0xff) >> (8-nRedBits_); + if( nGreenBits_ != 8 ) + g |= (g & 0xff) >> (8-nGreenBits_); + if( nBlueBits_ != 8 ) + b |= (b & 0xff) >> (8-nBlueBits_); + + return MAKE_SALCOLOR( r, g, b ); +} + +Pixel SalVisual::GetTCPixel( SalColor nSalColor ) const +{ + if( SALCOLOR == eRGBMode_ ) + return (Pixel)nSalColor; + + Pixel r = (Pixel)SALCOLOR_RED( nSalColor ); + Pixel g = (Pixel)SALCOLOR_GREEN( nSalColor ); + Pixel b = (Pixel)SALCOLOR_BLUE( nSalColor ); + + if( SALCOLORREVERSE == eRGBMode_ ) + return (b << 16) | (g << 8) | (r); + + if( other != eRGBMode_ ) // 8+8+8=24 + return (r << nRedShift_) | (g << nGreenShift_) | (b << nBlueShift_); + + if( nRedShift_ > 0 ) r <<= nRedShift_; else r >>= -nRedShift_; + if( nGreenShift_ > 0 ) g <<= nGreenShift_; else g >>= -nGreenShift_; + if( nBlueShift_ > 0 ) b <<= nBlueShift_; else b >>= -nBlueShift_; + + return (r&red_mask) | (g&green_mask) | (b&blue_mask); +} + +// -=-= SalColormap -=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColormap::SalColormap( const SalDisplay *pDisplay, Colormap hColormap, int nScreen ) + : m_pDisplay( pDisplay ), + m_hColormap( hColormap ), + m_nScreen( nScreen ) +{ + m_aVisual = m_pDisplay->GetVisual( m_nScreen ); + + 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 ) + { + int r, g, b; + + // black, white, gray, ~gray = 4 + GetXPixels( aColor, 0xC0, 0xC0, 0xC0 ); + + // light colors: 3 * 2 = 6 +// GetXPixels( aColor, 0x00, 0x00, 0x00 ); + GetXPixels( aColor, 0x00, 0x00, 0xFF ); + GetXPixels( aColor, 0x00, 0xFF, 0x00 ); + GetXPixels( aColor, 0x00, 0xFF, 0xFF ); +// GetXPixels( aColor, 0xFF, 0x00, 0x00 ); +// GetXPixels( aColor, 0xFF, 0x00, 0xFF ); +// GetXPixels( aColor, 0xFF, 0xFF, 0x00 ); +// GetXPixels( aColor, 0xFF, 0xFF, 0xFF ); + + // standard colors: 7 * 2 = 14 +// GetXPixels( aColor, 0x00, 0x00, 0x00 ); + 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 ); // Blau 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 ); + } +} + +// PseudoColor +SalColormap::SalColormap( const BitmapPalette &rPalette ) + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( SALCOLOR_NONE ), + m_nBlackPixel( SALCOLOR_NONE ), + m_nUsed( rPalette.GetEntryCount() ), + m_nScreen( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ) +{ + m_aPalette = std::vector<SalColor>(m_nUsed); + + for( unsigned int i = 0; i < m_nUsed; i++ ) + { + const BitmapColor &rColor = rPalette[i]; + m_aPalette[i] = MAKE_SALCOLOR( rColor.GetRed(), + rColor.GetGreen(), + rColor.GetBlue() ); + if( (m_nBlackPixel == SALCOLOR_NONE) && (SALCOLOR_BLACK == m_aPalette[i]) ) + m_nBlackPixel = i; + else if( (m_nWhitePixel == SALCOLOR_NONE) && (SALCOLOR_WHITE == m_aPalette[i]) ) + m_nWhitePixel = i; + } +} + +// MonoChrome +SalColormap::SalColormap() + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( 1 ), + m_nBlackPixel( 0 ), + m_nUsed( 2 ), + m_nScreen( 0 ) +{ + if( m_pDisplay ) + m_nScreen = m_pDisplay->GetDefaultScreenNumber(); + m_aPalette = std::vector<SalColor>(m_nUsed); + + m_aPalette[m_nBlackPixel] = SALCOLOR_BLACK; + m_aPalette[m_nWhitePixel] = SALCOLOR_WHITE; +} + +// TrueColor +SalColormap::SalColormap( USHORT nDepth ) + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( (1 << nDepth) - 1 ), + m_nBlackPixel( 0x00000000 ), + m_nUsed( 1 << nDepth ), + m_nScreen( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ) +{ + const SalVisual *pVisual = &m_pDisplay->GetVisual( m_nScreen ); + + if( pVisual->GetClass() == TrueColor && pVisual->GetDepth() == nDepth ) + m_aVisual = *pVisual; + else + { + XVisualInfo aVI; + + if( !XMatchVisualInfo( m_pDisplay->GetDisplay(), + m_pDisplay->GetDefaultScreenNumber(), + nDepth, + TrueColor, + &aVI ) ) + { + aVI.visual = new Visual(); + aVI.visualid = (VisualID)0; // beware of temporary destructor below + aVI.screen = 0; + 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( 16 == nDepth ) // 565 + { + aVI.red_mask = 0x00F800; + aVI.green_mask = 0x0007E0; + aVI.blue_mask = 0x00001F; + } + else if( 15 == nDepth ) // 555 + { + aVI.red_mask = 0x007C00; + aVI.green_mask = 0x0003E0; + aVI.blue_mask = 0x00001F; + } + else if( 12 == nDepth ) // 444 + { + aVI.red_mask = 0x000F00; + aVI.green_mask = 0x0000F0; + aVI.blue_mask = 0x00000F; + } + 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 = NULL; + 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 ); + // give ownership of constructed Visual() to m_aVisual + // see SalVisual destructor + m_aVisual.visualid = (VisualID)-1; + m_aVisual.screen = -1; + } + else + m_aVisual = SalVisual( &aVI ); + } +} + +SalColormap::~SalColormap() +{ +#ifdef DBG_UTIL + m_hColormap = (Colormap)ILLEGAL_POINTER; + m_pDisplay = (SalDisplay*)ILLEGAL_POINTER; +#endif +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalColormap::SetPalette( const BitmapPalette &rPalette ) +{ + if( this != &GetX11SalData()->GetDisplay()->GetColormap(m_nScreen) ) + { + m_nBlackPixel = SALCOLOR_NONE; + m_nWhitePixel = SALCOLOR_NONE; + } + + if( rPalette.GetEntryCount() > m_nUsed ) + { + m_nBlackPixel = SALCOLOR_NONE; + m_nWhitePixel = SALCOLOR_NONE; + m_nUsed = rPalette.GetEntryCount(); + m_aPalette = std::vector<SalColor>(m_nUsed); + } + + for( int i = 0; i < rPalette.GetEntryCount(); i++ ) + { + const BitmapColor &rColor = rPalette[i]; + m_aPalette[i] = MAKE_SALCOLOR( rColor.GetRed(), + rColor.GetGreen(), + rColor.GetBlue() ); + if( (m_nBlackPixel == SALCOLOR_NONE) && (SALCOLOR_BLACK == m_aPalette[i]) ) + m_nBlackPixel = i; + else if( (m_nWhitePixel == SALCOLOR_NONE) && (SALCOLOR_WHITE == m_aPalette[i]) ) + m_nWhitePixel = i; + } +} + +void SalColormap::GetPalette() +{ + Pixel i; + m_aPalette = std::vector<SalColor>(m_nUsed); + + XColor *aColor = new XColor[m_nUsed]; + + for( i = 0; i < m_nUsed; i++ ) + { + aColor[i].red = aColor[i].green = aColor[i].blue = 0; + aColor[i].pixel = i; + } + + XQueryColors( m_pDisplay->GetDisplay(), m_hColormap, aColor, m_nUsed ); + + for( i = 0; i < m_nUsed; i++ ) + { + m_aPalette[i] = MAKE_SALCOLOR( aColor[i].red >> 8, + aColor[i].green >> 8, + aColor[i].blue >> 8 ); + } + + delete [] aColor; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static USHORT sal_Lookup( const std::vector<SalColor>& rPalette, + int r, int g, int b, + Pixel nUsed ) +{ + USHORT nPixel = 0; + int nBest = ColorDiff( rPalette[0], r, g, b ); + + for( USHORT i = 1; i < nUsed; i++ ) + { + int n = ColorDiff( rPalette[i], r, g, b ); + + if( n < nBest ) + { + if( !n ) + return i; + + nPixel = i; + nBest = n; + } + } + return nPixel; +} + +void SalColormap::GetLookupTable() +{ + m_aLookupTable = std::vector<USHORT>(16*16*16); + + int i = 0; + for( int r = 0; r < 256; r += 17 ) + for( int g = 0; g < 256; g += 17 ) + for( int b = 0; b < 256; b += 17 ) + m_aLookupTable[i++] = sal_Lookup( m_aPalette, r, g, b, m_nUsed ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColor SalColormap::GetColor( Pixel nPixel ) const +{ + if( m_nBlackPixel == nPixel ) return SALCOLOR_BLACK; + if( m_nWhitePixel == nPixel ) return SALCOLOR_WHITE; + + if( m_aVisual.GetVisual() ) + { + if( m_aVisual.GetClass() == TrueColor ) + return m_aVisual.GetTCColor( nPixel ); + + if( m_aPalette.empty() + && m_hColormap +#ifdef PSEUDOCOLOR12 + && m_aVisual.GetDepth() <= 12 +#else + && m_aVisual.GetDepth() <= 8 +#endif + && m_aVisual.GetClass() == PseudoColor ) + ((SalColormap*)this)->GetPalette(); + } + + if( !m_aPalette.empty() && nPixel < m_nUsed ) + return m_aPalette[nPixel]; + + if( m_hColormap ) + { + DBG_ASSERT( 1, "SalColormap::GetColor() !hColormap_\n" ); + return nPixel; + } + + // DirectColor, StaticColor, StaticGray, GrayScale + XColor aColor; + + aColor.pixel = nPixel; + + XQueryColor( m_pDisplay->GetDisplay(), m_hColormap, &aColor ); + + return MAKE_SALCOLOR( aColor.red>>8, aColor.green>>8, aColor.blue>>8 ); +} + +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 ); +} + +Pixel SalColormap::GetPixel( SalColor nSalColor ) const +{ + if( SALCOLOR_NONE == nSalColor ) return 0; + if( SALCOLOR_BLACK == nSalColor ) return m_nBlackPixel; + if( SALCOLOR_WHITE == nSalColor ) return m_nWhitePixel; + + if( m_aVisual.GetClass() == TrueColor ) + return m_aVisual.GetTCPixel( nSalColor ); + + if( m_aLookupTable.empty() ) + { + if( m_aPalette.empty() + && m_hColormap +#ifdef PSEUDOCOLOR12 + && m_aVisual.GetDepth() <= 12 +#else + && m_aVisual.GetDepth() <= 8 +#endif + && m_aVisual.GetClass() == PseudoColor ) // what else ??? + ((SalColormap*)this)->GetPalette(); + + if( !m_aPalette.empty() ) + for( Pixel i = 0; i < m_nUsed; i++ ) + if( m_aPalette[i] == nSalColor ) + return i; + + if( m_hColormap ) + { + // DirectColor, StaticColor, StaticGray, GrayScale (PseudoColor) + XColor aColor; + + if( GetXPixel( aColor, + SALCOLOR_RED ( nSalColor ), + SALCOLOR_GREEN( nSalColor ), + SALCOLOR_BLUE ( nSalColor ) ) ) + { + if( !m_aPalette.empty() && !m_aPalette[aColor.pixel] ) + { + const_cast<SalColormap*>(this)->m_aPalette[aColor.pixel] = nSalColor; + + if( !(aColor.pixel & 1) && !m_aPalette[aColor.pixel+1] ) + { + XColor aInversColor; + + SalColor nInversColor = nSalColor ^ 0xFFFFFF; + + GetXPixel( aInversColor, + SALCOLOR_RED ( nInversColor ), + SALCOLOR_GREEN( nInversColor ), + SALCOLOR_BLUE ( nInversColor ) ); + + if( !m_aPalette[aInversColor.pixel] ) + const_cast<SalColormap*>(this)->m_aPalette[aInversColor.pixel] = nInversColor; +#ifdef DBG_UTIL + else + fprintf( stderr, "SalColormap::GetPixel() 0x%06lx=%lu 0x%06lx=%lu\n", + static_cast< unsigned long >(nSalColor), aColor.pixel, + static_cast< unsigned long >(nInversColor), aInversColor.pixel); +#endif + } + } + + return aColor.pixel; + } + +#ifdef DBG_UTIL + fprintf( stderr, "SalColormap::GetPixel() !XAllocColor %lx\n", + static_cast< unsigned long >(nSalColor) ); +#endif + } + + if( m_aPalette.empty() ) + { +#ifdef DBG_UTIL + fprintf( stderr, "SalColormap::GetPixel() Palette empty %lx\n", + static_cast< unsigned long >(nSalColor)); +#endif + return nSalColor; + } + + ((SalColormap*)this)->GetLookupTable(); + } + + // Colormatching ueber Palette + USHORT r = SALCOLOR_RED ( nSalColor ); + USHORT g = SALCOLOR_GREEN( nSalColor ); + USHORT b = SALCOLOR_BLUE ( nSalColor ); + return m_aLookupTable[ (((r+8)/17) << 8) + + (((g+8)/17) << 4) + + ((b+8)/17) ]; +} + diff --git a/vcl/unx/generic/app/salinst.cxx b/vcl/unx/generic/app/salinst.cxx new file mode 100644 index 000000000000..98bce72d6bce --- /dev/null +++ b/vcl/unx/generic/app/salinst.cxx @@ -0,0 +1,452 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "osl/module.hxx" +#include "tools/solarmutex.hxx" +#include "vos/mutex.hxx" + + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salinst.h" +#include "unx/salframe.h" +#include "unx/dtint.hxx" +#include "unx/salprn.h" +#include "unx/sm.hxx" + +#include "vcl/apptypes.hxx" +#include "vcl/helper.hxx" + +#include "salwtype.hxx" + +// ------------------------------------------------------------------------- +// +// SalYieldMutex +// +// ------------------------------------------------------------------------- + +SalYieldMutex::SalYieldMutex() +{ + mnCount = 0; + mnThreadId = 0; + ::tools::SolarMutex::SetSolarMutex( this ); +} + +void SalYieldMutex::acquire() +{ + OMutex::acquire(); + mnThreadId = vos::OThread::getCurrentIdentifier(); + mnCount++; +} + +void SalYieldMutex::release() +{ + if ( mnThreadId == vos::OThread::getCurrentIdentifier() ) + { + if ( mnCount == 1 ) + mnThreadId = 0; + mnCount--; + } + OMutex::release(); +} + +sal_Bool SalYieldMutex::tryToAcquire() +{ + if ( OMutex::tryToAcquire() ) + { + mnThreadId = vos::OThread::getCurrentIdentifier(); + mnCount++; + return True; + } + else + return False; +} + +//---------------------------------------------------------------------------- + +// -=-= SalInstance =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// plugin factory function +extern "C" +{ + VCL_DLLPUBLIC 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( new SalYieldMutex() ); + + // initialize SalData + X11SalData *pSalData = new X11SalData; + SetSalData( pSalData ); + pSalData->m_pInstance = pInstance; + pSalData->Init(); + + return pInstance; + } +} + +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 + + X11SalData *pSalData = GetX11SalData(); + pSalData->deInitNWF(); + delete pSalData; + SetSalData( NULL ); + + ::tools::SolarMutex::SetSolarMutex( 0 ); + delete mpSalYieldMutex; +} + + +// -------------------------------------------------------- +// AnyInput from sv/mow/source/app/svapp.cxx + +struct PredicateReturn +{ + USHORT nType; + BOOL bRet; +}; + +extern "C" { +Bool ImplPredicateEvent( Display *, XEvent *pEvent, char *pData ) +{ + PredicateReturn *pPre = (PredicateReturn *)pData; + + if ( pPre->bRet ) + return False; + + USHORT nType; + + switch( pEvent->type ) + { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + nType = INPUT_MOUSE; + break; + + case XLIB_KeyPress: + //case KeyRelease: + nType = INPUT_KEYBOARD; + break; + case Expose: + case GraphicsExpose: + case NoExpose: + nType = INPUT_PAINT; + break; + default: + nType = 0; + } + + if ( (nType & pPre->nType) || ( ! nType && (pPre->nType & INPUT_OTHER) ) ) + pPre->bRet = TRUE; + + return False; +} +} + +bool X11SalInstance::AnyInput(USHORT nType) +{ + X11SalData *pSalData = GetX11SalData(); + Display *pDisplay = pSalData->GetDisplay()->GetDisplay(); + BOOL bRet = FALSE; + + if( (nType & INPUT_TIMER) && + pSalData->GetDisplay()->GetXLib()->CheckTimeout( false ) ) + { + bRet = TRUE; + } + else if (XPending(pDisplay) ) + { + PredicateReturn aInput; + XEvent aEvent; + + aInput.bRet = FALSE; + aInput.nType = nType; + + XCheckIfEvent(pDisplay, &aEvent, ImplPredicateEvent, + (char *)&aInput ); + + bRet = aInput.bRet; + } + return bRet; +} + +vos::IMutex* X11SalInstance::GetYieldMutex() +{ + return mpSalYieldMutex; +} + +// ----------------------------------------------------------------------- + +ULONG X11SalInstance::ReleaseYieldMutex() +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() == + vos::OThread::getCurrentIdentifier() ) + { + ULONG nCount = pYieldMutex->GetAcquireCount(); + ULONG n = nCount; + while ( n ) + { + pYieldMutex->release(); + n--; + } + + return nCount; + } + else + return 0; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::AcquireYieldMutex( ULONG nCount ) +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + while ( nCount ) + { + pYieldMutex->acquire(); + nCount--; + } +} + +// ----------------------------------------------------------------------- + +bool X11SalInstance::CheckYieldMutex() +{ + bool bRet = true; + + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() != + vos::OThread::getCurrentIdentifier() ) + { + bRet = false; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ GetX11SalData()->GetLib()->Yield( bWait, bHandleAllCurrentEvents ); } + +void* X11SalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) +{ + static const char* pDisplay = getenv( "DISPLAY" ); + rReturnedType = AsciiCString; + rReturnedBytes = pDisplay ? strlen( pDisplay )+1 : 1; + return pDisplay ? (void*)pDisplay : (void*)""; +} + +SalFrame *X11SalInstance::CreateFrame( SalFrame *pParent, ULONG nSalFrameStyle ) +{ + SalFrame *pFrame = new X11SalFrame( pParent, nSalFrameStyle ); + + return pFrame; +} + +SalFrame* X11SalInstance::CreateChildFrame( SystemParentData* pParentData, ULONG nStyle ) +{ + SalFrame* pFrame = new X11SalFrame( NULL, nStyle, pParentData ); + + return pFrame; +} + +void X11SalInstance::DestroyFrame( SalFrame* pFrame ) +{ + delete pFrame; +} + +static void getServerDirectories( std::list< rtl::OString >& o_rFontPaths ) +{ +#ifdef LINUX + /* + * chkfontpath exists on some (RH derived) Linux distributions + */ + static const char* pCommands[] = { + "/usr/sbin/chkfontpath 2>/dev/null", "chkfontpath 2>/dev/null" + }; + ::std::list< ByteString > aLines; + + for( unsigned int i = 0; i < sizeof(pCommands)/sizeof(pCommands[0]); i++ ) + { + FILE* pPipe = popen( pCommands[i], "r" ); + aLines.clear(); + if( pPipe ) + { + char line[1024]; + char* pSearch; + while( fgets( line, sizeof(line), pPipe ) ) + { + int nLen = strlen( line ); + if( line[nLen-1] == '\n' ) + line[nLen-1] = 0; + pSearch = strstr( line, ": " ); + if( pSearch ) + aLines.push_back( pSearch+2 ); + } + if( ! pclose( pPipe ) ) + break; + } + } + + for( ::std::list< ByteString >::iterator it = aLines.begin(); it != aLines.end(); ++it ) + { + if( ! access( it->GetBuffer(), F_OK ) ) + { + o_rFontPaths.push_back( *it ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding fs dir %s\n", it->GetBuffer() ); +#endif + } + } +#else + (void)o_rFontPaths; +#endif +} + + + +void X11SalInstance::FillFontPathList( std::list< rtl::OString >& o_rFontPaths ) +{ + Display *pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + + DBG_ASSERT( pDisplay, "No Display !" ); + if( pDisplay ) + { + // get font paths to look for fonts + int nPaths = 0, i; + char** pPaths = XGetFontPath( pDisplay, &nPaths ); + + bool bServerDirs = false; + for( i = 0; i < nPaths; i++ ) + { + OString aPath( pPaths[i] ); + sal_Int32 nPos = 0; + if( ! bServerDirs + && ( nPos = aPath.indexOf( ':' ) ) > 0 + && ( !aPath.copy(nPos).equals( ":unscaled" ) ) ) + { + bServerDirs = true; + getServerDirectories( o_rFontPaths ); + } + else + { + psp::normPath( aPath ); + o_rFontPaths.push_back( aPath ); + } + } + + if( nPaths ) + XFreeFontPath( pPaths ); + } + + // insert some standard directories + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/TrueType" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1/sun" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/truetype" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/Type1" ); + + #ifdef SOLARIS + /* cde specials, from /usr/dt/bin/Xsession: here are the good fonts, + the OWfontpath file may contain as well multiple lines as a comma + separated list of fonts in each line. to make it even more weird + environment variables are allowed as well */ + + const char* lang = getenv("LANG"); + if ( lang != NULL ) + { + String aOpenWinDir( String::CreateFromAscii( "/usr/openwin/lib/locale/" ) ); + aOpenWinDir.AppendAscii( lang ); + aOpenWinDir.AppendAscii( "/OWfontpath" ); + + SvFileStream aStream( aOpenWinDir, STREAM_READ ); + + // TODO: replace environment variables + while( aStream.IsOpen() && ! aStream.IsEof() ) + { + ByteString aLine; + aStream.ReadLine( aLine ); + // need an OString for normpath + OString aNLine( aLine ); + psp::normPath( aNLine ); + aLine = aNLine; + // try to avoid bad fonts in some cases + static bool bAvoid = (strncasecmp( lang, "ar", 2 ) == 0) || (strncasecmp( lang, "he", 2 ) == 0) || strncasecmp( lang, "iw", 2 ) == 0 || (strncasecmp( lang, "hi", 2 ) == 0); + if( bAvoid && aLine.Search( "iso_8859" ) != STRING_NOTFOUND ) + continue; + o_rFontPaths.push_back( aLine ); + } + } + #endif /* SOLARIS */ +} + +extern "C" { static void SAL_CALL thisModule() {} } + +void X11SalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType) +{ + const rtl::OUString SYM_ADD_TO_RECENTLY_USED_FILE_LIST(RTL_CONSTASCII_USTRINGPARAM("add_to_recently_used_file_list")); + const rtl::OUString LIB_RECENT_FILE(RTL_CONSTASCII_USTRINGPARAM("librecentfile.so")); + typedef void (*PFUNC_ADD_TO_RECENTLY_USED_LIST)(const rtl::OUString&, const rtl::OUString&); + + PFUNC_ADD_TO_RECENTLY_USED_LIST add_to_recently_used_file_list = 0; + + osl::Module module; + module.loadRelative( &thisModule, LIB_RECENT_FILE ); + if (module.is()) + add_to_recently_used_file_list = (PFUNC_ADD_TO_RECENTLY_USED_LIST)module.getFunctionSymbol(SYM_ADD_TO_RECENTLY_USED_FILE_LIST); + if (add_to_recently_used_file_list) + add_to_recently_used_file_list(rFileUrl, rMimeType); +} diff --git a/vcl/unx/generic/app/salsys.cxx b/vcl/unx/generic/app/salsys.cxx new file mode 100644 index 000000000000..92f0748b6460 --- /dev/null +++ b/vcl/unx/generic/app/salsys.cxx @@ -0,0 +1,226 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unx/salunx.h> +#include <unx/dtint.hxx> +#include <unx/saldata.hxx> +#include <unx/salinst.h> +#include <unx/saldisp.hxx> +#include <unx/salsys.h> + +#include <vcl/msgbox.hxx> +#include <vcl/button.hxx> + +#include <svdata.hxx> + +#include <rtl/ustrbuf.hxx> +#include <osl/thread.h> + + +SalSystem* X11SalInstance::CreateSalSystem() +{ + return new X11SalSystem(); +} + +// ----------------------------------------------------------------------- + +X11SalSystem::~X11SalSystem() +{ +} + +// for the moment only handle xinerama case +unsigned int X11SalSystem::GetDisplayScreenCount() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + return pSalDisp->IsXinerama() ? pSalDisp->GetXineramaScreens().size() : pSalDisp->GetScreenCount(); +} + +bool X11SalSystem::IsMultiDisplay() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + unsigned int nScreenCount = pSalDisp->GetScreenCount(); + return pSalDisp->IsXinerama() ? false : (nScreenCount > 1); +} + +unsigned int X11SalSystem::GetDefaultDisplayNumber() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + return pSalDisp->IsXinerama() ? pSalDisp->GetDefaultMonitorNumber() : pSalDisp->GetDefaultScreenNumber(); +} + +Rectangle X11SalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen ) +{ + Rectangle aRet; + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + if( pSalDisp->IsXinerama() ) + { + const std::vector< Rectangle >& rScreens = pSalDisp->GetXineramaScreens(); + if( nScreen < rScreens.size() ) + aRet = rScreens[nScreen]; + } + else + { + const SalDisplay::ScreenData& rScreen = pSalDisp->getDataForScreen( nScreen ); + aRet = Rectangle( Point( 0, 0 ), rScreen.m_aSize ); + } + + return aRet; +} + +Rectangle X11SalSystem::GetDisplayWorkAreaPosSizePixel( unsigned int nScreen ) +{ + // FIXME: workareas + return GetDisplayScreenPosSizePixel( nScreen ); +} + +rtl::OUString X11SalSystem::GetScreenName( unsigned int nScreen ) +{ + rtl::OUString aScreenName; + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + if( pSalDisp->IsXinerama() ) + { + const std::vector< Rectangle >& rScreens = pSalDisp->GetXineramaScreens(); + if( nScreen >= rScreens.size() ) + nScreen = 0; + rtl::OUStringBuffer aBuf( 256 ); + aBuf.append( rtl::OStringToOUString( rtl::OString( DisplayString( pSalDisp->GetDisplay() ) ), osl_getThreadTextEncoding() ) ); + aBuf.appendAscii( " [" ); + aBuf.append( static_cast<sal_Int32>(nScreen) ); + aBuf.append( sal_Unicode(']') ); + aScreenName = aBuf.makeStringAndClear(); + } + else + { + if( nScreen >= static_cast<unsigned int>(pSalDisp->GetScreenCount()) ) + nScreen = 0; + rtl::OUStringBuffer aBuf( 256 ); + aBuf.append( rtl::OStringToOUString( rtl::OString( DisplayString( pSalDisp->GetDisplay() ) ), osl_getThreadTextEncoding() ) ); + // search backwards for ':' + int nPos = aBuf.getLength(); + if( nPos > 0 ) + nPos--; + while( nPos > 0 && aBuf.charAt( nPos ) != ':' ) + nPos--; + // search forward to '.' + while( nPos < aBuf.getLength() && aBuf.charAt( nPos ) != '.' ) + nPos++; + if( nPos < aBuf.getLength() ) + aBuf.setLength( nPos+1 ); + else + aBuf.append( sal_Unicode('.') ); + aBuf.append( static_cast<sal_Int32>(nScreen) ); + aScreenName = aBuf.makeStringAndClear(); + } + return aScreenName; +} + +int X11SalSystem::ShowNativeDialog( const String& rTitle, const String& rMessage, const std::list< String >& rButtons, int nDefButton ) +{ + int nRet = -1; + + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpIntroWindow ) + pSVData->mpIntroWindow->Hide(); + + WarningBox aWarn( NULL, WB_STDWORK, rMessage ); + aWarn.SetText( rTitle ); + aWarn.Clear(); + + USHORT nButton = 0; + for( std::list< String >::const_iterator it = rButtons.begin(); it != rButtons.end(); ++it ) + { + aWarn.AddButton( *it, nButton+1, nButton == (USHORT)nDefButton ? BUTTONDIALOG_DEFBUTTON : 0 ); + nButton++; + } + aWarn.SetFocusButton( (USHORT)nDefButton+1 ); + + nRet = ((int)aWarn.Execute()) - 1; + + // normalize behaviour, actually this should never happen + if( nRet < -1 || nRet >= int(rButtons.size()) ) + nRet = -1; + + return nRet; +} + +int X11SalSystem::ShowNativeMessageBox(const String& rTitle, const String& rMessage, int nButtonCombination, int nDefaultButton) +{ + int nDefButton = 0; + std::list< String > aButtons; + int nButtonIds[5], nBut = 0; + + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_OK ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_YES ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES; + aButtons.push_back( Button::GetStandardText( BUTTON_NO ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO; + if( nDefaultButton == SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO ) + nDefButton = 1; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL ) + { + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_RETRY ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; + } + aButtons.push_back( Button::GetStandardText( BUTTON_CANCEL ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL; + if( nDefaultButton == SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL ) + nDefButton = aButtons.size()-1; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_ABORT ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT; + aButtons.push_back( Button::GetStandardText( BUTTON_RETRY ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; + aButtons.push_back( Button::GetStandardText( BUTTON_IGNORE ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE; + switch( nDefaultButton ) + { + case SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY: nDefButton = 1;break; + case SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE: nDefButton = 2;break; + } + } + int nResult = ShowNativeDialog( rTitle, rMessage, aButtons, nDefButton ); + + return nResult != -1 ? nButtonIds[ nResult ] : 0; +} diff --git a/vcl/unx/generic/app/saltimer.cxx b/vcl/unx/generic/app/saltimer.cxx new file mode 100644 index 000000000000..d68ec388448e --- /dev/null +++ b/vcl/unx/generic/app/saltimer.cxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <sys/time.h> +#include <sys/times.h> +#include <time.h> +#include <unistd.h> + +#include <unx/salunx.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/saltimer.h> +#include <unx/salinst.h> + +// -=-= SalData =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalData::Timeout() const +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpSalTimer ) + pSVData->mpSalTimer->CallCallback(); +} + +// -=-= SalXLib =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalXLib::StopTimer() +{ + m_aTimeout.tv_sec = 0; + m_aTimeout.tv_usec = 0; + m_nTimeoutMS = 0; +} + +void SalXLib::StartTimer( ULONG nMS ) +{ + timeval Timeout (m_aTimeout); // previous timeout. + gettimeofday (&m_aTimeout, 0); + + m_nTimeoutMS = nMS; + m_aTimeout += m_nTimeoutMS; + + if ((Timeout > m_aTimeout) || (Timeout.tv_sec == 0)) + { + // Wakeup from previous timeout (or stopped timer). + Wakeup(); + } +} + +// -=-= SalTimer -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalTimer* X11SalInstance::CreateSalTimer() +{ + return new X11SalTimer(); +} + +X11SalTimer::~X11SalTimer() +{ +} + +void X11SalTimer::Stop() +{ + GetX11SalData()->GetLib()->StopTimer(); +} + +void X11SalTimer::Start( ULONG nMS ) +{ + GetX11SalData()->GetLib()->StartTimer( nMS ); +} + diff --git a/vcl/unx/generic/app/sm.cxx b/vcl/unx/generic/app/sm.cxx new file mode 100644 index 000000000000..c421ceeaf0e3 --- /dev/null +++ b/vcl/unx/generic/app/sm.cxx @@ -0,0 +1,801 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <string.h> +#include <unistd.h> +#include <sys/poll.h> +#include <fcntl.h> + +#include <stdio.h> + +#include <osl/process.h> +#include <osl/security.h> +#include <osl/conditn.h> + +#include <tools/prex.h> +#include <X11/Xatom.h> +#include <tools/postx.h> + +#include <unx/sm.hxx> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/salinst.h> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#define USE_SM_EXTENSION + +#if OSL_DEBUG_LEVEL > 1 +#include <cstdarg> +static bool bFirstAssert = true; +#endif + +#if OSL_DEBUG_LEVEL > 1 +inline void SMprintf( const char* pFormat, ... ) +#else +inline void SMprintf( const char*, ... ) +#endif +{ +#if OSL_DEBUG_LEVEL > 1 + FILE* fp = fopen( "/tmp/sessionlog.txt", bFirstAssert ? "w" : "a" ); + if(!fp) return; + bFirstAssert = false; + std::va_list ap; + va_start( ap, pFormat ); + vfprintf( fp, pFormat, ap ); + fclose( fp ); + va_end( ap ); +#endif +}; + +static IceSalSession* pOneInstance = NULL; + +SalSession* X11SalInstance::CreateSalSession() +{ + if( ! pOneInstance ) + pOneInstance = new IceSalSession(); + return pOneInstance; +} + +/* + * class IceSalSession + */ + +static X11SalFrame* pOldStyleSaveFrame = NULL; + +IceSalSession::IceSalSession() +{ +} + +IceSalSession::~IceSalSession() +{ + if( pOneInstance == this ) + pOneInstance = NULL; +} + +void IceSalSession::queryInteraction() +{ + if( ! SessionManagerClient::queryInteraction() ) + { + SalSessionInteractionEvent aEvent( false ); + CallCallback( &aEvent ); + } +} + +void IceSalSession::interactionDone() +{ + SessionManagerClient::interactionDone( false ); +} + +void IceSalSession::saveDone() +{ + SessionManagerClient::saveDone(); + if( pOldStyleSaveFrame ) + { + // note: does nothing if not running in generic plugin + X11SalFrame::SaveYourselfDone( pOldStyleSaveFrame ); + } +} + +bool IceSalSession::cancelShutdown() +{ + SessionManagerClient::interactionDone( true ); + return false; +} + +void IceSalSession::handleOldX11SaveYourself( SalFrame* pFrame ) +{ + // do this only once + if( ! pOldStyleSaveFrame ) + { + pOldStyleSaveFrame = static_cast<X11SalFrame*>(pFrame); + if( pOneInstance ) + { + SalSessionSaveRequestEvent aEvent( true, false ); + pOneInstance->CallCallback( &aEvent ); + } + } +} + +extern "C" void SAL_CALL ICEConnectionWorker( void* ); + +class ICEConnectionObserver +{ + friend void SAL_CALL ICEConnectionWorker(void*); + static BOOL bIsWatching; + static void ICEWatchProc( IceConn connection, IcePointer client_data, + Bool opening, IcePointer* watch_data ); + + static struct pollfd* pFilehandles; + static IceConn* pConnections; + static int nConnections; + static int nWakeupFiles[2]; + static oslMutex ICEMutex; + static oslThread ICEThread; +#ifdef USE_SM_EXTENSION + static IceIOErrorHandler origIOErrorHandler; + static IceErrorHandler origErrorHandler; +#endif +public: + + static void activate(); + static void deactivate(); + static void lock(); + static void unlock(); + static void wakeup(); +}; + + +SmcConn SessionManagerClient::aSmcConnection = NULL; +ByteString SessionManagerClient::aClientID; +BOOL ICEConnectionObserver::bIsWatching = FALSE; +struct pollfd* ICEConnectionObserver::pFilehandles = NULL; +IceConn* ICEConnectionObserver::pConnections = NULL; +int ICEConnectionObserver::nConnections = 0; +oslMutex ICEConnectionObserver::ICEMutex = NULL; +oslThread ICEConnectionObserver::ICEThread = NULL; +int ICEConnectionObserver::nWakeupFiles[2] = { 0, 0 }; + +#ifdef USE_SM_EXTENSION +IceIOErrorHandler ICEConnectionObserver::origIOErrorHandler = NULL; +IceErrorHandler ICEConnectionObserver::origErrorHandler = NULL; + +static void IgnoreIceErrors(IceConn, Bool, int, unsigned long, int, int, IcePointer) +{ +} + +static void IgnoreIceIOErrors(IceConn) +{ +} +#endif + +// HACK +bool SessionManagerClient::bDocSaveDone = false; + + +static SmProp* pSmProps = NULL; +static SmProp** ppSmProps = NULL; +static int nSmProps = 0; +static unsigned char *pSmRestartHint = NULL; + + +static void BuildSmPropertyList() +{ + if( ! pSmProps ) + { + ByteString aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() ); + + nSmProps = 5; + pSmProps = new SmProp[ nSmProps ]; + + pSmProps[ 0 ].name = const_cast<char*>(SmCloneCommand); + pSmProps[ 0 ].type = const_cast<char*>(SmLISTofARRAY8); + pSmProps[ 0 ].num_vals = 1; + pSmProps[ 0 ].vals = new SmPropValue; + pSmProps[ 0 ].vals->length = aExec.Len()+1; + pSmProps[ 0 ].vals->value = strdup( aExec.GetBuffer() ); + + pSmProps[ 1 ].name = const_cast<char*>(SmProgram); + pSmProps[ 1 ].type = const_cast<char*>(SmARRAY8); + pSmProps[ 1 ].num_vals = 1; + pSmProps[ 1 ].vals = new SmPropValue; + pSmProps[ 1 ].vals->length = aExec.Len()+1; + pSmProps[ 1 ].vals->value = strdup( aExec.GetBuffer() ); + + pSmProps[ 2 ].name = const_cast<char*>(SmRestartCommand); + pSmProps[ 2 ].type = const_cast<char*>(SmLISTofARRAY8); + pSmProps[ 2 ].num_vals = 3; + pSmProps[ 2 ].vals = new SmPropValue[3]; + pSmProps[ 2 ].vals[0].length = aExec.Len()+1; + pSmProps[ 2 ].vals[0].value = strdup( aExec.GetBuffer() ); + ByteString aRestartOption( "-session=" ); + aRestartOption.Append( SessionManagerClient::getSessionID() ); + pSmProps[ 2 ].vals[1].length = aRestartOption.Len()+1; + pSmProps[ 2 ].vals[1].value = strdup( aRestartOption.GetBuffer() ); + ByteString aRestartOptionNoLogo( "-nologo" ); + pSmProps[ 2 ].vals[2].length = aRestartOptionNoLogo.Len()+1; + pSmProps[ 2 ].vals[2].value = strdup( aRestartOptionNoLogo.GetBuffer() ); + + rtl::OUString aUserName; + rtl::OString aUser; + oslSecurity aSec = osl_getCurrentSecurity(); + if( aSec ) + { + osl_getUserName( aSec, &aUserName.pData ); + aUser = rtl::OUStringToOString( aUserName, osl_getThreadTextEncoding() ); + osl_freeSecurityHandle( aSec ); + } + + pSmProps[ 3 ].name = const_cast<char*>(SmUserID); + pSmProps[ 3 ].type = const_cast<char*>(SmARRAY8); + pSmProps[ 3 ].num_vals = 1; + pSmProps[ 3 ].vals = new SmPropValue; + pSmProps[ 3 ].vals->value = strdup( aUser.getStr() ); + pSmProps[ 3 ].vals->length = strlen( (char *)pSmProps[ 3 ].vals->value )+1; + + pSmProps[ 4 ].name = const_cast<char*>(SmRestartStyleHint); + pSmProps[ 4 ].type = const_cast<char*>(SmCARD8); + pSmProps[ 4 ].num_vals = 1; + pSmProps[ 4 ].vals = new SmPropValue; + pSmProps[ 4 ].vals->value = malloc(1); + pSmRestartHint = (unsigned char *)pSmProps[ 4 ].vals->value; + *pSmRestartHint = SmRestartIfRunning; + pSmProps[ 4 ].vals->length = 1; + + ppSmProps = new SmProp*[ nSmProps ]; + for( int i = 0; i < nSmProps; i++ ) + ppSmProps[ i ] = &pSmProps[i]; + } +} + +bool SessionManagerClient::checkDocumentsSaved() +{ + return bDocSaveDone; +} + +IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, EMPTYARG ) +{ + SMprintf( "posting save documents event shutdown = %s\n", (pThis!=0) ? "true" : "false" ); + + static bool bFirstShutdown=true; + if (pThis != 0 && 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; + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + Window *pWindow = (*it)->GetWindow(); + if (pWindow && pWindow->IsVisible()) + { + *pSmRestartHint = SmRestartIfRunning; + break; + } + } + } + + if( pOneInstance ) + { + SalSessionSaveRequestEvent aEvent( pThis != 0, false ); + pOneInstance->CallCallback( &aEvent ); + } + else + saveDone(); + + return 0; +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, InteractionHdl, void*, EMPTYARG ) +{ + SMprintf( "interaction link\n" ); + if( pOneInstance ) + { + SalSessionInteractionEvent aEvent( true ); + pOneInstance->CallCallback( &aEvent ); + } + + return 0; +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownCancelHdl, void*, EMPTYARG ) +{ + SMprintf( "shutdown cancel\n" ); + if( pOneInstance ) + { + SalSessionShutdownCancelEvent aEvent; + pOneInstance->CallCallback( &aEvent ); + } + + return 0; +} + +void SessionManagerClient::SaveYourselfProc( + SmcConn, + SmPointer, + int save_type, + Bool shutdown, + int interact_style, + Bool + ) +{ + SMprintf( "Session: save yourself, save_type = %s, shutdown = %s, interact_style = %s, fast = %s\n", + save_type == SmSaveLocal ? "SmcSaveLocal" : + ( save_type == SmSaveGlobal ? "SmcSaveGlobal" : + ( save_type == SmSaveBoth ? "SmcSaveBoth" : "<unknown>" ) ), + shutdown ? "true" : "false", + interact_style == SmInteractStyleNone ? "SmInteractStyleNone" : + ( interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + ( interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : "<unknown>" ) ), + false ? "true" : "false" + ); + BuildSmPropertyList(); +#ifdef USE_SM_EXTENSION + 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; + } + Application::PostUserEvent( STATIC_LINK( (void*)(shutdown ? 0xffffffff : 0x0), SessionManagerClient, SaveYourselfHdl ) ); + SMprintf( "waiting for save yourself event to be processed\n" ); +#endif +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownHdl, void*, EMPTYARG ) +{ + if( pOneInstance ) + { + SalSessionQuitEvent aEvent; + pOneInstance->CallCallback( &aEvent ); + } + + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + SMprintf( rFrames.begin() != rFrames.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" ); + if( rFrames.begin() != rFrames.end() ) + rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 ); + return 0; +} + +void SessionManagerClient::DieProc( + SmcConn connection, + SmPointer + ) +{ + SMprintf( "Session: die\n" ); + if( connection == aSmcConnection ) + { + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) ); + SMprintf( "waiting for shutdown event to be processed\n" ); + } +} + +void SessionManagerClient::SaveCompleteProc( + SmcConn, + SmPointer + ) +{ + SMprintf( "Session: save complete\n" ); +} + +void SessionManagerClient::ShutdownCanceledProc( + SmcConn connection, + SmPointer ) +{ + SMprintf( "Session: shutdown canceled\n" ); + if( connection == aSmcConnection ) + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownCancelHdl ) ); +} + +void SessionManagerClient::InteractProc( + SmcConn connection, + SmPointer ) +{ + SMprintf( "Session: interaction request completed\n" ); + if( connection == aSmcConnection ) + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, InteractionHdl ) ); +} + +void SessionManagerClient::saveDone() +{ + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + SmcSetProperties( aSmcConnection, nSmProps, ppSmProps ); + SmcSaveYourselfDone( aSmcConnection, True ); + SMprintf( "sent SaveYourselfDone SmRestartHint of %d\n", *pSmRestartHint ); + bDocSaveDone = true; + ICEConnectionObserver::unlock(); + } +} + + +void SessionManagerClient::open() +{ + static SmcCallbacks aCallbacks; + +#ifdef USE_SM_EXTENSION + // this is the way Xt does it, so we can too + if( ! aSmcConnection && getenv( "SESSION_MANAGER" ) ) + { + char aErrBuf[1024]; + ICEConnectionObserver::activate(); + ICEConnectionObserver::lock(); + + char* pClientID = NULL; + const ByteString& rPrevId( getPreviousSessionID() ); + + aCallbacks.save_yourself.callback = SaveYourselfProc; + aCallbacks.save_yourself.client_data = NULL; + aCallbacks.die.callback = DieProc; + aCallbacks.die.client_data = NULL; + aCallbacks.save_complete.callback = SaveCompleteProc; + aCallbacks.save_complete.client_data = NULL; + aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc; + aCallbacks.shutdown_cancelled.client_data = NULL; + aSmcConnection = SmcOpenConnection( NULL, + NULL, + SmProtoMajor, + SmProtoMinor, + SmcSaveYourselfProcMask | + SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask , + &aCallbacks, + rPrevId.Len() ? const_cast<char*>(rPrevId.GetBuffer()) : NULL, + &pClientID, + sizeof( aErrBuf ), + aErrBuf ); + if( ! aSmcConnection ) + SMprintf( "SmcOpenConnection failed: %s\n", aErrBuf ); + else + SMprintf( "SmcOpenConnection succeeded, client ID is \"%s\"\n", pClientID ); + aClientID = ByteString( pClientID ); + free( pClientID ); + pClientID = NULL; + ICEConnectionObserver::unlock(); + + SalDisplay* pDisp = GetX11SalData()->GetDisplay(); + if( pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ) && aClientID.Len() ) + { + XChangeProperty( pDisp->GetDisplay(), + pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ), + XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ), + XA_STRING, + 8, + PropModeReplace, + (unsigned char*)aClientID.GetBuffer(), + aClientID.Len() + ); + } + } + else if( ! aSmcConnection ) + SMprintf( "no SESSION_MANAGER\n" ); +#endif +} + +const ByteString& SessionManagerClient::getSessionID() +{ + return aClientID; +} + +void SessionManagerClient::close() +{ + if( aSmcConnection ) + { +#ifdef USE_SM_EXTENSION + ICEConnectionObserver::lock(); + SMprintf( "attempting SmcCloseConnection\n" ); + SmcCloseConnection( aSmcConnection, 0, NULL ); + SMprintf( "SmcConnection closed\n" ); + ICEConnectionObserver::unlock(); + ICEConnectionObserver::deactivate(); +#endif + aSmcConnection = NULL; + } +} + +bool SessionManagerClient::queryInteraction() +{ + bool bRet = false; + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + if( SmcInteractRequest( aSmcConnection, SmDialogNormal, InteractProc, NULL ) ) + bRet = true; + ICEConnectionObserver::unlock(); + } + return bRet; +} + +void SessionManagerClient::interactionDone( bool bCancelShutdown ) +{ + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + SmcInteractDone( aSmcConnection, bCancelShutdown ? True : False ); + ICEConnectionObserver::unlock(); + } +} + + +String SessionManagerClient::getExecName() +{ + rtl::OUString aExec, aSysExec; + osl_getExecutableFile( &aExec.pData ); + osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData ); + + int nPos = aSysExec.indexOf( rtl::OUString::createFromAscii( ".bin" ) ); + if( nPos != -1 ) + aSysExec = aSysExec.copy( 0, nPos ); + return aSysExec; +} + + +const ByteString& SessionManagerClient::getPreviousSessionID() +{ + static ByteString aPrevId; + + int nCommands = osl_getCommandArgCount(); + for( int i = 0; i < nCommands; i++ ) + { + ::rtl::OUString aArg; + osl_getCommandArg( i, &aArg.pData ); + if( aArg.compareToAscii( "-session=", 9 ) == 0 ) + { + aPrevId = ByteString( ::rtl::OUStringToOString( aArg.copy( 9 ), osl_getThreadTextEncoding() ) ); + break; + } + } + SMprintf( "previous ID = \"%s\"\n", aPrevId.GetBuffer() ); + return aPrevId; +} + +void ICEConnectionObserver::lock() +{ + osl_acquireMutex( ICEMutex ); +} + +void ICEConnectionObserver::unlock() +{ + osl_releaseMutex( ICEMutex ); +} + +void ICEConnectionObserver::activate() +{ + if( ! bIsWatching ) + { + nWakeupFiles[0] = nWakeupFiles[1] = 0; + ICEMutex = osl_createMutex(); + bIsWatching = TRUE; +#ifdef USE_SM_EXTENSION + /* + * Default handlers call exit, we don't care that strongly if something + * happens to fail + */ + origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors ); + origErrorHandler = IceSetErrorHandler( IgnoreIceErrors ); + IceAddConnectionWatch( ICEWatchProc, NULL ); +#endif + } +} + +void ICEConnectionObserver::deactivate() +{ + if( bIsWatching ) + { + lock(); + bIsWatching = FALSE; +#ifdef USE_SM_EXTENSION + IceRemoveConnectionWatch( ICEWatchProc, NULL ); + IceSetErrorHandler( origErrorHandler ); + IceSetIOErrorHandler( origIOErrorHandler ); +#endif + nConnections = 0; + if( ICEThread ) + { + osl_terminateThread( ICEThread ); + wakeup(); + } + unlock(); + if( ICEThread ) + { + osl_joinWithThread( ICEThread ); + osl_destroyThread( ICEThread ); + close( nWakeupFiles[1] ); + close( nWakeupFiles[0] ); + ICEThread = NULL; + } + osl_destroyMutex( ICEMutex ); + ICEMutex = NULL; + } +} + +void ICEConnectionObserver::wakeup() +{ + char cChar = 'w'; + write( nWakeupFiles[1], &cChar, 1 ); +} + +void ICEConnectionWorker( void* ) +{ +#ifdef USE_SM_EXTENSION + while( osl_scheduleThread(ICEConnectionObserver::ICEThread) && ICEConnectionObserver::nConnections ) + { + ICEConnectionObserver::lock(); + int nConnectionsBefore = ICEConnectionObserver::nConnections; + int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1); + struct pollfd* pLocalFD = (struct pollfd*)rtl_allocateMemory( nBytes ); + rtl_copyMemory( pLocalFD, ICEConnectionObserver::pFilehandles, nBytes ); + ICEConnectionObserver::unlock(); + + int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 ); + bool bWakeup = (pLocalFD[0].revents & POLLIN); + rtl_freeMemory( pLocalFD ); + + if( nRet < 1 ) + continue; + + // clear wakeup pipe + if( bWakeup ) + { + char buf[4]; + while( read( ICEConnectionObserver::nWakeupFiles[0], buf, sizeof( buf ) ) > 0 ) + ; + SMprintf( "file handles active in wakeup: %d\n", nRet ); + if( nRet == 1 ) + continue; + } + + // check fd's after we obtained the lock + ICEConnectionObserver::lock(); + if( ICEConnectionObserver::nConnections > 0 && ICEConnectionObserver::nConnections == nConnectionsBefore ) + { + nRet = poll( ICEConnectionObserver::pFilehandles+1, ICEConnectionObserver::nConnections, 0 ); + if( nRet > 0 ) + { + SMprintf( "IceProcessMessages\n" ); + Bool bReply; + for( int i = 0; i < ICEConnectionObserver::nConnections; i++ ) + if( ICEConnectionObserver::pFilehandles[i+1].revents & POLLIN ) + IceProcessMessages( ICEConnectionObserver::pConnections[i], NULL, &bReply ); + } + } + ICEConnectionObserver::unlock(); + } +#endif + SMprintf( "shutting donw ICE dispatch thread\n" ); +} + +void ICEConnectionObserver::ICEWatchProc( + IceConn connection, + IcePointer, + Bool opening, + IcePointer* + ) +{ + // note: this is a callback function for ICE + // this implicitly means that a call into ICE lib is calling this + // so the ICEMutex MUST already be locked by the caller + +#ifdef USE_SM_EXTENSION + if( opening ) + { + int fd = IceConnectionNumber( connection ); + nConnections++; + pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections ); + pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) ); + pConnections[ nConnections-1 ] = connection; + pFilehandles[ nConnections ].fd = fd; + pFilehandles[ nConnections ].events = POLLIN; + if( nConnections == 1 ) + { + if( ! pipe( nWakeupFiles ) ) + { + int flags; + pFilehandles[0].fd = nWakeupFiles[0]; + pFilehandles[0].events = POLLIN; + // set close-on-exec and nonblock descriptor flag. + if ((flags = fcntl (nWakeupFiles[0], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (nWakeupFiles[0], F_SETFD, flags); + } + if ((flags = fcntl (nWakeupFiles[0], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (nWakeupFiles[0], F_SETFL, flags); + } + // set close-on-exec and nonblock descriptor flag. + if ((flags = fcntl (nWakeupFiles[1], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (nWakeupFiles[1], F_SETFD, flags); + } + if ((flags = fcntl (nWakeupFiles[1], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (nWakeupFiles[1], F_SETFL, flags); + } + ICEThread = osl_createSuspendedThread( ICEConnectionWorker, NULL ); + osl_resumeThread( ICEThread ); + } + } + } + else + { + for( int i = 0; i < nConnections; i++ ) + { + if( pConnections[i] == connection ) + { + if( i < nConnections-1 ) + { + rtl_moveMemory( pConnections+i, pConnections+i+1, sizeof( IceConn )*(nConnections-i-1) ); + rtl_moveMemory( pFilehandles+i+1, pFilehandles+i+2, sizeof( struct pollfd )*(nConnections-i-1) ); + } + nConnections--; + pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections ); + pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) ); + break; + } + } + if( nConnections == 0 && ICEThread ) + { + SMprintf( "terminating ICEThread\n" ); + osl_terminateThread( ICEThread ); + wakeup(); + // must release the mutex here + osl_releaseMutex( ICEMutex ); + osl_joinWithThread( ICEThread ); + osl_destroyThread( ICEThread ); + close( nWakeupFiles[1] ); + close( nWakeupFiles[0] ); + ICEThread = NULL; + } + } + SMprintf( "ICE connection on %d %s\n", + IceConnectionNumber( connection ), + opening ? "inserted" : "removed" ); + SMprintf( "Display connection is %d\n", ConnectionNumber( GetX11SalData()->GetDisplay()->GetDisplay() ) ); +#endif +} diff --git a/vcl/unx/generic/app/soicon.cxx b/vcl/unx/generic/app/soicon.cxx new file mode 100644 index 000000000000..663dc9371caf --- /dev/null +++ b/vcl/unx/generic/app/soicon.cxx @@ -0,0 +1,116 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unx/salunx.h> +#include <unx/saldisp.hxx> +#include <unx/salbmp.h> +#include <unx/soicon.hxx> + +#include <vcl/salbtype.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/graph.hxx> + +#include <svdata.hxx> +#include <svids.hrc> +#include <salbmp.hxx> +#include <impbmp.hxx> + + +BOOL SelectAppIconPixmap( SalDisplay *pDisplay, int nScreen,USHORT nIcon, USHORT iconSize, + Pixmap& icon_pixmap, Pixmap& icon_mask) +{ + if( ! ImplGetResMgr() ) + return FALSE; + + USHORT nIconSizeOffset; + + if( iconSize >= 48 ) + nIconSizeOffset = SV_ICON_SIZE48_START; + else if( iconSize >= 32 ) + nIconSizeOffset = SV_ICON_SIZE32_START; + else if( iconSize >= 16 ) + nIconSizeOffset = SV_ICON_SIZE16_START; + else + return FALSE; + + BitmapEx aIcon( ResId(nIconSizeOffset + nIcon, *ImplGetResMgr())); + if( TRUE == aIcon.IsEmpty() ) + return FALSE; + + SalTwoRect aRect; + aRect.mnSrcX = 0; aRect.mnSrcY = 0; + aRect.mnSrcWidth = iconSize; aRect.mnSrcHeight = iconSize; + aRect.mnDestX = 0; aRect.mnDestY = 0; + aRect.mnDestWidth = iconSize; aRect.mnDestHeight = iconSize; + + X11SalBitmap *pBitmap = static_cast < X11SalBitmap * > + (aIcon.ImplGetBitmapImpBitmap()->ImplGetSalBitmap()); + + icon_pixmap = XCreatePixmap( pDisplay->GetDisplay(), + pDisplay->GetRootWindow( nScreen ), + iconSize, iconSize, + DefaultDepth( pDisplay->GetDisplay(), nScreen ) + ); + + pBitmap->ImplDraw( icon_pixmap, + nScreen, + DefaultDepth( pDisplay->GetDisplay(), nScreen ), + aRect, + DefaultGC(pDisplay->GetDisplay(), nScreen ) ); + + icon_mask = None; + + if( TRANSPARENT_BITMAP == aIcon.GetTransparentType() ) + { + icon_mask = XCreatePixmap( pDisplay->GetDisplay(), + pDisplay->GetRootWindow( pDisplay->GetDefaultScreenNumber() ), + iconSize, iconSize, 1); + + XGCValues aValues; + aValues.foreground = 0xffffffff; + aValues.background = 0; + aValues.function = GXcopy; + GC aMonoGC = XCreateGC( pDisplay->GetDisplay(), icon_mask, + GCFunction|GCForeground|GCBackground, &aValues ); + + Bitmap aMask = aIcon.GetMask(); + aMask.Invert(); + + X11SalBitmap *pMask = static_cast < X11SalBitmap * > + (aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + + pMask->ImplDraw(icon_mask, nScreen, 1, aRect, aMonoGC); + XFreeGC( pDisplay->GetDisplay(), aMonoGC ); + } + + return TRUE; +} + diff --git a/vcl/unx/generic/app/wmadaptor.cxx b/vcl/unx/generic/app/wmadaptor.cxx new file mode 100644 index 000000000000..90b2e5426bdf --- /dev/null +++ b/vcl/unx/generic/app/wmadaptor.cxx @@ -0,0 +1,2548 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sal/alloca.h" +#include "rtl/locale.h" + +#include "osl/thread.h" +#include "osl/process.h" + +#include "vcl/configsettings.hxx" + +#include "unx/wmadaptor.hxx" +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/salframe.h" + +#include "salgdi.hxx" + +#include "tools/prex.h" +#include <X11/X.h> +#include <X11/Xatom.h> +#include <X11/Xresource.h> +#include "tools/postx.h" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +namespace vcl_sal { + +class NetWMAdaptor : public WMAdaptor +{ + void setNetWMState( X11SalFrame* pFrame ) const; + void initAtoms(); + virtual bool isValid() const; +public: + NetWMAdaptor( SalDisplay* ); + virtual ~NetWMAdaptor(); + + virtual void setWMName( X11SalFrame* pFrame, const String& rWMName ) const; + virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const; + virtual void shade( X11SalFrame* pFrame, bool bToShaded ) const; + virtual void setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pTransientFrame = NULL ) const; + virtual bool supportsICCCMPos() const; + virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const; + virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const; + virtual void showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const; + virtual void frameIsMapping( X11SalFrame* pFrame ) const; + virtual void setFrameStruts( X11SalFrame* pFrame, + int left, int right, int top, int bottom, + int left_start_y, int left_end_y, + int right_start_y, int right_end_y, + int top_start_x, int top_end_x, + int bottom_start_x, int bottom_end_x ) const; + virtual void setUserTime( X11SalFrame* i_pFrame, long i_nUserTime ) const; +}; + +class GnomeWMAdaptor : public WMAdaptor +{ + bool m_bValid; + + void setGnomeWMState( X11SalFrame* pFrame ) const; + void initAtoms(); + virtual bool isValid() const; +public: + GnomeWMAdaptor( SalDisplay * ); + virtual ~GnomeWMAdaptor(); + + virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const; + virtual void shade( X11SalFrame* pFrame, bool bToShaded ) const; + virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const; + virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const; +}; + +} + +using namespace vcl_sal; + +struct WMAdaptorProtocol +{ + const char* pProtocol; + int nProtocol; +}; + + +/* + * table must be sorted ascending in strings + * since it is use with bsearch + */ +static const WMAdaptorProtocol aProtocolTab[] = +{ + { "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", WMAdaptor::KDE_NET_WM_WINDOW_TYPE_OVERRIDE }, + { "_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_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_SHADED", WMAdaptor::NET_WM_STATE_SHADED }, + { "_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 + */ + +static 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_SAVE_YOURSELF", WMAdaptor::WM_SAVE_YOURSELF }, + { "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 }, + { "DTWM_IS_RUNNING", WMAdaptor::DTWM_IS_RUNNING }, + { "_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( ((const WMAdaptorProtocol*)pLeft)->pProtocol, ((const WMAdaptorProtocol*)pRight)->pProtocol ); +} +} + +WMAdaptor* WMAdaptor::createWMAdaptor( SalDisplay* pSalDisplay ) +{ + WMAdaptor* pAdaptor = NULL; + + // try a NetWM + pAdaptor = new NetWMAdaptor( pSalDisplay ); + if( ! pAdaptor->isValid() ) + delete pAdaptor, pAdaptor = NULL; +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "WM supports extended WM hints\n" ); +#endif + + // try a GnomeWM + if( ! pAdaptor ) + { + pAdaptor = new GnomeWMAdaptor( pSalDisplay ); + if( ! pAdaptor->isValid() ) + delete pAdaptor, pAdaptor = NULL; +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "WM supports GNOME WM hints\n" ); +#endif + } + + if( ! pAdaptor ) + pAdaptor = new WMAdaptor( pSalDisplay ); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Window Manager's name is \"%s\"\n", + ByteString( pAdaptor->getWindowManagerName(), RTL_TEXTENCODING_ISO_8859_1 ).GetBuffer() ); +#endif + return pAdaptor; +} + + +/* + * WMAdaptor constructor + */ + +WMAdaptor::WMAdaptor( SalDisplay* pDisplay ) : + m_pSalDisplay( pDisplay ), + m_bTransientBehaviour( true ), + 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 = NULL; + + // default desktops + m_nDesktops = 1; + m_aWMWorkAreas = ::std::vector< Rectangle > + ( 1, Rectangle( Point(), m_pSalDisplay->GetScreenSize( m_pSalDisplay->GetDefaultScreenNumber() ) ) ); + m_bEqualWorkAreas = true; + + memset( m_aWMAtoms, 0, sizeof( m_aWMAtoms ) ); + m_pDisplay = m_pSalDisplay->GetDisplay(); + + initAtoms(); + getNetWmName(); // try to discover e.g. Sawfish + + // check for dtwm running + if( m_aWMAtoms[ DTWM_IS_RUNNING ] ) + { + if ( (XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ DTWM_IS_RUNNING ], + 0, 1, + False, + XA_INTEGER, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty) == 0 + && nItems) + || (XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ DTWM_IS_RUNNING ], + 0, 1, + False, + m_aWMAtoms[ DTWM_IS_RUNNING ], + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty) == 0 + && nItems)) + { + if (*pProperty) + { + m_aWMName = String(RTL_CONSTASCII_USTRINGPARAM("Dtwm")); + m_bTransientBehaviour = false; + m_nWinGravity = CenterGravity; + } + XFree (pProperty); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_aWMName.Len() == 0 ) + { + // check for window maker - needs different gravity + Atom aWMakerRunning = XInternAtom( m_pDisplay, "_WINDOWMAKER_WM_PROTOCOLS", True ); + if( aWMakerRunning != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aWMakerRunning, + 0, 32, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_ATOM ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("Windowmaker" ) ); + XFree( pProperty ); + m_nInitWinGravity = NorthWestGravity; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_aWMName.Len() == 0 ) + { + if( XInternAtom( m_pDisplay, "_OL_WIN_ATTR", True ) ) + { + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM( "Olwm" ) ); + m_nInitWinGravity = NorthWestGravity; + } + } + if( m_aWMName.Len() == 0 ) + { + // 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->GetDefaultScreenNumber() ), + aRwmRunning, + 0, 32, + False, + aRwmRunning, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == aRwmRunning ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("ReflectionX" ) ); + XFree( pProperty ); + } + else if( (aRwmRunning = XInternAtom( m_pDisplay, "_WRQ_WM_RUNNING", True )) != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aRwmRunning, + 0, 32, + False, + XA_STRING, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_STRING ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM( "ReflectionX Windows" ) ); + XFree( pProperty ); + } + } + if( m_aWMName.Len() == 0 ) + { + Atom aTTAPlatform = XInternAtom( m_pDisplay, "TTA_CLIENT_PLATFORM", True ); + if( aTTAPlatform != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aTTAPlatform, + 0, 32, + False, + XA_STRING, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_STRING ) + { + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("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 + m_bTransientBehaviour = true; + + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + bool bNetWM = false; + + initAtoms(); + + // check for NetWM + bNetWM = getNetWmName(); + if( bNetWM + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + 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 = NULL; + } + // collect supported protocols + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_SUPPORTED ], + 0, nBytesLeft/4, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && nItems + ) + { + Atom* pAtoms = (Atom*)pProperty; + char** pAtomNames = (char**)alloca( sizeof(char*)*nItems ); + if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "supported protocols:\n" ); +#endif + for( unsigned int i = 0; i < nItems; i++ ) + { + // #i80971# protect against invalid atoms + if( pAtomNames[i] == NULL ) + continue; + + int nProtocol = -1; + WMAdaptorProtocol aSearch; + aSearch.pProtocol = pAtomNames[i]; + WMAdaptorProtocol* pMatch = (WMAdaptorProtocol*) + bsearch( &aSearch, + aProtocolTab, + sizeof( aProtocolTab )/sizeof( aProtocolTab[0] ), + sizeof( struct WMAdaptorProtocol ), + compareProtocol ); + if( pMatch ) + { + nProtocol = pMatch->nProtocol; + m_aWMAtoms[ nProtocol ] = pAtoms[ i ]; + if( pMatch->nProtocol == NET_WM_STATE_STAYS_ON_TOP ) + m_bEnableAlwaysOnTopWorks = true; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, " %s%s\n", pAtomNames[i], nProtocol != -1 ? "" : " (unsupported)" ); +#endif + + XFree( pAtomNames[i] ); + } + } + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + + // get number of desktops + if( m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + m_nDesktops = *(long*)pProperty; + XFree( pProperty ); + pProperty = NULL; + // get work areas + if( m_aWMAtoms[ NET_WORKAREA ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_WORKAREA ], + 0, 4*m_nDesktops, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty + ) == 0 + && nItems == 4*(unsigned)m_nDesktops + ) + { + m_aWMWorkAreas = ::std::vector< Rectangle > ( m_nDesktops ); + long* pValues = (long*)pProperty; + for( int i = 0; i < m_nDesktops; i++ ) + { + Point aPoint( pValues[4*i], + pValues[4*i+1] ); + Size aSize( pValues[4*i+2], + pValues[4*i+3] ); + Rectangle aWorkArea( aPoint, aSize ); + m_aWMWorkAreas[i] = aWorkArea; + if( aWorkArea != m_aWMWorkAreas[0] ) + m_bEqualWorkAreas = false; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "workarea %d: %ldx%ld+%ld+%ld\n", + i, + m_aWMWorkAreas[i].GetWidth(), + m_aWMWorkAreas[i].GetHeight(), + m_aWMWorkAreas[i].Left(), + m_aWMWorkAreas[i].Top() ); +#endif + } + XFree( pProperty ); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%ld workareas for %d desktops !\n", nItems/4, m_nDesktops ); +#endif + if( pProperty ) + { + XFree(pProperty); + pProperty = NULL; + } + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } +} + +/* + * NetWMAdaptor destructor + */ +NetWMAdaptor::~NetWMAdaptor() +{ +} + +/* + * GnomeWMAdaptor constructor + */ + +GnomeWMAdaptor::GnomeWMAdaptor( SalDisplay* pSalDisplay ) : + WMAdaptor( pSalDisplay ), + m_bValid( false ) +{ + // currently all Gnome WMs do transient like expected + m_bTransientBehaviour = true; + + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + + initAtoms(); + + // check for GnomeWM + if( m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ WIN_PROTOCOLS ] ) + { + XLIB_Window aWMChild = None; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_CARDINAL + && nFormat == 32 + && nItems != 0 + ) + { + aWMChild = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + XLIB_Window aCheckWindow = None; + m_pSalDisplay->GetXLib()->PushXErrorLevel( true ); + 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 + && ! m_pSalDisplay->GetXLib()->HasXErrorOccured() + ) + { + aCheckWindow = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + 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(); + } + } + m_pSalDisplay->GetXLib()->PopXErrorLevel(); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_bValid + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + 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 = NULL; + } + // collect supported protocols + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_PROTOCOLS ], + 0, nBytesLeft/4, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + Atom* pAtoms = (Atom*)pProperty; + char** pAtomNames = (char**)alloca( sizeof(char*)*nItems ); + if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "supported protocols:\n" ); +#endif + for( unsigned int i = 0; i < nItems; i++ ) + { + // #i80971# protect against invalid atoms + if( pAtomNames[i] == NULL ) + continue; + + int nProtocol = -1; + WMAdaptorProtocol aSearch; + aSearch.pProtocol = pAtomNames[i]; + WMAdaptorProtocol* pMatch = (WMAdaptorProtocol*) + bsearch( &aSearch, + aProtocolTab, + sizeof( aProtocolTab )/sizeof( aProtocolTab[0] ), + sizeof( struct WMAdaptorProtocol ), + compareProtocol ); + if( pMatch ) + { + nProtocol = pMatch->nProtocol; + m_aWMAtoms[ nProtocol ] = pAtoms[ i ]; + if( pMatch->nProtocol == WIN_LAYER ) + m_bEnableAlwaysOnTopWorks = true; + } + if( strncmp( "_ICEWM_TRAY", pAtomNames[i], 11 ) == 0 ) + { + m_aWMName = String(RTL_CONSTASCII_USTRINGPARAM("IceWM" )); + m_nWinGravity = NorthWestGravity; + m_nInitWinGravity = NorthWestGravity; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, " %s%s\n", pAtomNames[i], nProtocol != -1 ? "" : " (unsupported)" ); +#endif + + XFree( pAtomNames[i] ); + } + } + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + + // get number of desktops + if( m_aWMAtoms[ WIN_WORKSPACE_COUNT ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_WORKSPACE_COUNT ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + m_nDesktops = *(long*)pProperty; + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } +} + +/* + * GnomeWMAdaptor destructor + */ +GnomeWMAdaptor::~GnomeWMAdaptor() +{ +} + +/* + * getNetWmName() + */ +bool WMAdaptor::getNetWmName() +{ + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + bool bNetWM = false; + + if( m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ NET_WM_NAME ] ) + { + XLIB_Window aWMChild = None; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_WINDOW, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_WINDOW + && nFormat == 32 + && nItems != 0 + ) + { + aWMChild = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + XLIB_Window aCheckWindow = None; + m_pSalDisplay->GetXLib()->PushXErrorLevel( true ); + 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 + && ! m_pSalDisplay->GetXLib()->HasXErrorOccured() + ) + { + aCheckWindow = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + 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 = String( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_UTF8 ); + } + else + if (aRealType == XA_STRING) + { + m_aWMName = String( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_ISO_8859_1 ); + } + + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + // if this is metacity, check for version to enable a legacy workaround + if( m_aWMName.EqualsAscii( "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 + ) + { + String aMetaVersion( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_UTF8 ); + nVersionMajor = aMetaVersion.GetToken( 0, '.' ).ToInt32(); + nVersionMinor = aMetaVersion.GetToken( 1, '.' ).ToInt32(); + } + if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( nVersionMajor < 2 || (nVersionMajor == 2 && nVersionMinor < 12) ) + m_bLegacyPartialFullscreen = true; + } + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + m_pSalDisplay->GetXLib()->PopXErrorLevel(); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + 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(); + rtl::OUString aSetting( pItem->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "WM" ) ), + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ShouldSwitchWorkspace" ) ) ) ); + if( aSetting.getLength() == 0 ) + { + if( m_aWMName.EqualsAscii( "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( unsigned int i = 0; i < sizeof( aAtomTab )/sizeof( aAtomTab[0] ); i++ ) + m_aWMAtoms[ aAtomTab[i].nProtocol ] = XInternAtom( m_pDisplay, aAtomTab[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 String& rWMName ) const +{ + ByteString aTitle( rWMName, osl_getThreadTextEncoding() ); + + if( ! rWMName.Len() && m_aWMName.EqualsAscii( "Dtwm" ) ) + aTitle = " "; + + ::rtl::OString aWMLocale; + rtl_Locale* pLocale = NULL; + osl_getProcessLocale( &pLocale ); + if( pLocale ) + { + ::rtl::OUString aLocaleString( pLocale->Language ); + ::rtl::OUString aCountry( pLocale->Country ); + ::rtl::OUString aVariant( pLocale->Variant ); + + if( aCountry.getLength() ) + { + aLocaleString += ::rtl::OUString::createFromAscii( "_" ); + aLocaleString += aCountry; + } + if( aVariant.getLength() ) + aLocaleString += aVariant; + aWMLocale = ::rtl::OUStringToOString( aLocaleString, RTL_TEXTENCODING_ISO_8859_1 ); + } + else + { + static const char* pLang = getenv( "LANG" ); + aWMLocale = pLang ? pLang : "C"; + } + + static bool bTrustXmb = true; + #ifdef SOLARIS + /* #i64273# there are some weird cases when using IIIMP on Solaris + * where for unknown reasons XmbTextListToTextProperty results in + * garbage. Test one string once to ensure safety. + * + * FIXME: This must be a bug in xiiimp.so.2 somewhere. However + * it was not possible to recreate this in a small sample program. + * This reeks of memory corruption somehow. + */ + static bool bOnce = true; + if( bOnce ) + { + bOnce = false; + XTextProperty aTestProp = { NULL, None, 0, 0 }; + const char *pText = "trustme"; + XmbTextListToTextProperty( m_pDisplay, + &const_cast<char*>(pText), + 1, + XStdICCTextStyle, + &aTestProp ); + bTrustXmb = (aTestProp.nitems == 7) && + (aTestProp.value != NULL ) && + (strncmp( (char*)aTestProp.value, pText, 7 ) == 0) && + (aTestProp.encoding == XA_STRING); + if( aTestProp.value ) + XFree( aTestProp.value ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s\n", + bTrustXmb ? + "XmbTextListToTextProperty seems to work" : + "XmbTextListToTextProperty does not seem to work" ); + #endif + } + #endif + + char* pT = const_cast<char*>(aTitle.GetBuffer()); + XTextProperty aProp = { NULL, None, 0, 0 }; + if( bTrustXmb ) + { + XmbTextListToTextProperty( m_pDisplay, + &pT, + 1, + XStdICCTextStyle, + &aProp ); + } + + unsigned char* pData = aProp.nitems ? aProp.value : (unsigned char*)aTitle.GetBuffer(); + Atom nType = aProp.nitems ? aProp.encoding : XA_STRING; + int nFormat = aProp.nitems ? aProp.format : 8; + int nBytes = aProp.nitems ? aProp.nitems : aTitle.Len(); + const SystemEnvData* pEnv = pFrame->GetSystemData(); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + XA_WM_NAME, + nType, + nFormat, + PropModeReplace, + pData, + nBytes ); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + XA_WM_ICON_NAME, + nType, + nFormat, + PropModeReplace, + pData, + nBytes ); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ WM_LOCALE_NAME ], + XA_STRING, + 8, + PropModeReplace, + (unsigned char*)aWMLocale.getStr(), + aWMLocale.getLength() ); + if (aProp.value != NULL) + XFree( aProp.value ); +} + +/* + * NetWMAdaptor::setWMName + * sets WM_NAME + * _NET_WM_NAME + * WM_ICON_NAME + * _NET_WM_ICON_NAME + */ +void NetWMAdaptor::setWMName( X11SalFrame* pFrame, const String& rWMName ) const +{ + WMAdaptor::setWMName( pFrame, rWMName ); + + ByteString aTitle( rWMName, RTL_TEXTENCODING_UTF8 ); + const SystemEnvData* pEnv = pFrame->GetSystemData(); + if( m_aWMAtoms[ NET_WM_NAME ] ) + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ NET_WM_NAME ], + m_aWMAtoms[ UTF8_STRING ], + 8, + PropModeReplace, + (unsigned char*)aTitle.GetBuffer(), + aTitle.Len()+1 ); + if( m_aWMAtoms[ NET_WM_ICON_NAME ] ) + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ NET_WM_ICON_NAME ], + m_aWMAtoms[ UTF8_STRING ], + 8, + PropModeReplace, + (unsigned char*)aTitle.GetBuffer(), + aTitle.Len()+1 ); + // The +1 copies the terminating null byte. Although + // the spec says, this should not be necessary + // at least the kwin implementation seems to depend + // on the null byte +} + +/* + * NetWMAdaptor::setNetWMState + * sets _NET_WM_STATE + */ +void NetWMAdaptor::setNetWMState( X11SalFrame* pFrame ) const +{ + if( m_aWMAtoms[ NET_WM_STATE ] ) + { + Atom aStateAtoms[ 10 ]; + int nStateAtoms = 0; + + // set NET_WM_STATE_MODAL + if( m_aWMAtoms[ NET_WM_STATE_MODAL ] + && pFrame->meWindowType == windowType_ModalDialogue ) + { + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MODAL ]; + /* + * #90998# NET_WM_STATE_SKIP_TASKBAR set on a frame will + * cause kwin not to give it the focus on map request + * this seems to be a bug in kwin + * aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ]; + */ + } + 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->mbShaded && m_aWMAtoms[ NET_WM_STATE_SHADED ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SHADED ]; + if( pFrame->mbFullScreen && m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ]; + if( pFrame->meWindowType == windowType_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, + (unsigned char*)aStateAtoms, + nStateAtoms + ); + } + else + XDeleteProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ NET_WM_STATE ] ); + if( pFrame->mbMaximizedHorz + && pFrame->mbMaximizedVert + && ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) ) + { + /* + * for maximizing use NorthWestGravity (including decoration) + */ + XSizeHints hints; + 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; + } + Rectangle aPosSize = m_aWMWorkAreas[nCurrent]; + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + aPosSize = Rectangle( Point( aPosSize.Left() + rGeom.nLeftDecoration, + aPosSize.Top() + rGeom.nTopDecoration ), + Size( aPosSize.GetWidth() + - rGeom.nLeftDecoration + - rGeom.nRightDecoration, + aPosSize.GetHeight() + - rGeom.nTopDecoration + - rGeom.nBottomDecoration ) + ); + pFrame->SetPosSize( aPosSize ); + + /* + * reset gravity hint to static gravity + * (this should not move window according to ICCCM) + */ + if( bHint && pFrame->nShowState_ != SHOWSTATE_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 ] ) + { + sal_uInt32 nWinWMState = 0; + + if( pFrame->mbMaximizedVert ) + nWinWMState |= 1 << 2; + if( pFrame->mbMaximizedHorz ) + nWinWMState |= 1 << 3; + if( pFrame->mbShaded ) + nWinWMState |= 1 << 5; + + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ WIN_STATE ], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nWinWMState, + 1 + ); + if( pFrame->mbMaximizedHorz + && pFrame->mbMaximizedVert + && ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) ) + { + /* + * for maximizing use NorthWestGravity (including decoration) + */ + XSizeHints hints; + 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; + } + Rectangle aPosSize = m_aWMWorkAreas[nCurrent]; + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + aPosSize = Rectangle( Point( aPosSize.Left() + rGeom.nLeftDecoration, + aPosSize.Top() + rGeom.nTopDecoration ), + Size( aPosSize.GetWidth() + - rGeom.nLeftDecoration + - rGeom.nRightDecoration, + aPosSize.GetHeight() + - rGeom.nTopDecoration + - rGeom.nBottomDecoration ) + ); + pFrame->SetPosSize( aPosSize ); + + /* + * reset gravity hint to static gravity + * (this should not move window according to ICCCM) + */ + if( bHint && pFrame->nShowState_ != SHOWSTATE_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; + pFrame->mnDecorationFlags = nDecorationFlags; + + if( ! pFrame->mbFullScreen ) + { + // set mwm hints + struct _mwmhints { + unsigned long flags, func, deco; + long input_mode; + unsigned long status; + } aHint; + + aHint.flags = 15; /* flags for functions, decoration, input mode and status */ + aHint.deco = 0; + aHint.func = 1L << 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 |= 1L << 3; + if( nDecorationFlags & decoration_Border ) + aHint.deco |= 1L << 1; + if( nDecorationFlags & decoration_Resize ) + aHint.deco |= 1L << 2, aHint.func |= 1L << 1; + if( nDecorationFlags & decoration_MinimizeBtn ) + aHint.deco |= 1L << 5, aHint.func |= 1L << 3; + if( nDecorationFlags & decoration_MaximizeBtn ) + aHint.deco |= 1L << 6, aHint.func |= 1L << 4; + if( nDecorationFlags & decoration_CloseBtn ) + aHint.deco |= 1L << 4, aHint.func |= 1L << 5; + } + // evaluate window type + switch( eType ) + { + case windowType_ModalDialogue: + aHint.input_mode = 1; + break; + default: + break; + } + + // set the hint + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ MOTIF_WM_HINTS ], + m_aWMAtoms[ MOTIF_WM_HINTS ], + 32, + PropModeReplace, + (unsigned char*)&aHint, + 5 ); + } + + // set transientFor hint + /* #91030# dtwm will not map a dialogue if the transient + * window is iconified. This is deemed undesireable 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; + } + // #110333# in case no one ever sets a title prevent + // the Dtwm taking the class instead + if( m_aWMName.EqualsAscii( "Dtwm" ) ) + setWMName( pFrame, String() ); +} + +/* + * 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 windowType_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 windowType_ModelessDialogue: + case windowType_ModalDialogue: + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ]; + break; + case windowType_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 windowType_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 windowType_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, + (unsigned char*)aWindowTypes, + nWindowTypes ); + } + if( ( eType == windowType_ModalDialogue || + eType == windowType_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 ) + { + Size aScreenSize( m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() ) ); + Point aTL( rGeom.nLeftDecoration, rGeom.nTopDecoration ); + if( m_pSalDisplay->IsXinerama() ) + { + Point aMed( aTL.X() + rGeom.nWidth/2, aTL.Y() + rGeom.nHeight/2 ); + const std::vector< Rectangle >& rScreens = m_pSalDisplay->GetXineramaScreens(); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + if( rScreens[i].IsInside( aMed ) ) + { + aTL += rScreens[i].TopLeft(); + aScreenSize = rScreens[i].GetSize(); + break; + } + } + Rectangle aTarget( aTL, + Size( aScreenSize.Width() - rGeom.nLeftDecoration - rGeom.nTopDecoration, + aScreenSize.Height() - rGeom.nTopDecoration - rGeom.nBottomDecoration ) + ); + if( ! bHorizontal ) + { + aTarget.SetSize( + Size( + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nWidth : pFrame->maRestorePosSize.GetWidth(), + aTarget.GetHeight() + ) + ); + aTarget.Left() = + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nX : pFrame->maRestorePosSize.Left(); + } + else if( ! bVertical ) + { + aTarget.SetSize( + Size( + aTarget.GetWidth(), + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nHeight : pFrame->maRestorePosSize.GetHeight() + ) + ); + aTarget.Top() = + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nY : pFrame->maRestorePosSize.Top(); + } + + Rectangle aRestore( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + if( pFrame->bMapped_ ) + { + XSetInputFocus( m_pDisplay, + pFrame->GetShellWindow(), + RevertToNone, + CurrentTime + ); + if( m_aWMName.EqualsAscii( "Dtwm" ) ) + { + /* + * Dtwm will only position correctly with center gravity + * and in this case the request actually changes the frame + * not the shell window + */ + aTarget = Rectangle( Point( 0, 0 ), aScreenSize ); + aRestore.Move( -rGeom.nLeftDecoration, -rGeom.nTopDecoration ); + } + } + + 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 = Rectangle(); + pFrame->nWidth_ = rGeom.nWidth; + pFrame->nHeight_ = rGeom.nHeight; + if( m_aWMName.EqualsAscii( "Dtwm" ) && pFrame->bMapped_ ) + { + pFrame->maGeometry.nX += rGeom.nLeftDecoration; + pFrame->maGeometry.nY += rGeom.nTopDecoration; + } + } +} + +/* + * 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_ & ~SAL_FRAME_STYLE_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 = Rectangle(); + else if( pFrame->maRestorePosSize.IsEmpty() ) + { + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + 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_ & ~SAL_FRAME_STYLE_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 = Rectangle(); + else if( pFrame->maRestorePosSize.IsEmpty() ) + { + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else + WMAdaptor::maximizeFrame( pFrame, bHorizontal, bVertical ); +} + +/* + * WMAdaptor::supportsICCCMPos + */ + +bool WMAdaptor::supportsICCCMPos() const +{ + return + m_aWMName.EqualsAscii( "Sawfish" ) + || m_aWMName.EqualsAscii( "Dtwm" ); +} + +/* + * NetWMAdaptor::supportsICCCMPos + */ + +bool NetWMAdaptor::supportsICCCMPos() const +{ + return true; +} + + +/* + * 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 ] ) + { + 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 ] ) + { + 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, + (unsigned char*)&nNewLayer, + 1 + ); + } + } +} + +/* + * WMAdaptor::changeReferenceFrame + */ +void WMAdaptor::changeReferenceFrame( X11SalFrame* pFrame, X11SalFrame* pReferenceFrame ) const +{ + if( ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_PLUG ) + && ! pFrame->IsOverrideRedirect() + && ! pFrame->IsFloatGrabWindow() + ) + { + XLIB_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; + pFrame->mbShaded = false; + + if( pEvent->state == PropertyNewValue ) + { + Atom nType, *pStates; + int nFormat; + unsigned long nItems, nBytesLeft; + unsigned char* pData = NULL; + 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 = (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; + else if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_SHADED ] && m_aWMAtoms[ NET_WM_STATE_SHADED ] ) + pFrame->mbShaded = true; + } + } + XFree( pData ); + pData = NULL; + nOffset += nItems * nFormat / 32; + } + else + break; + } while( nBytesLeft > 0 ); + } + + if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) ) + pFrame->maRestorePosSize = Rectangle(); + else + { + const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry(); + // the current geometry may already be changed by the corresponding + // ConfigureNotify, but this cannot be helped + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), + Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + 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; + pFrame->mbShaded = false; + + if( pEvent->state == PropertyNewValue ) + { + Atom nType; + int nFormat = 0; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pData = 0; + 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 = *(sal_uInt32*)pData; + if( nWinState & (1<<2) ) + pFrame->mbMaximizedVert = true; + if( nWinState & (1<<3) ) + pFrame->mbMaximizedHorz = true; + if( nWinState & (1<<5) ) + pFrame->mbShaded = true; + } + XFree( pData ); + } + } + + if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) ) + pFrame->maRestorePosSize = Rectangle(); + else + { + const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry(); + // the current geometry may already be changed by the corresponding + // ConfigureNotify, but this cannot be helped + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), + Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else if( pEvent->atom == m_aWMAtoms[ NET_WM_DESKTOP ] ) + { + pFrame->m_nWorkArea = getWindowWorkArea( pFrame->GetShellWindow() ); + } + else + nHandled = 0; + + return nHandled; +} + +/* + * WMAdaptor::shade + */ +void WMAdaptor::shade( X11SalFrame*, bool /*bToShaded*/ ) const +{ +} + +/* + * NetWMAdaptor::shade + */ +void NetWMAdaptor::shade( X11SalFrame* pFrame, bool bToShaded ) const +{ + if( m_aWMAtoms[ NET_WM_STATE ] + && m_aWMAtoms[ NET_WM_STATE_SHADED ] + && ( pFrame->nStyle_ & ~SAL_FRAME_STYLE_DEFAULT ) + ) + { + pFrame->mbShaded = bToShaded; + 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] = bToShaded ? 1 : 0; + aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_SHADED ]; + 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 ); + } + } +} + +/* + * GnomeWMAdaptor::shade + */ +void GnomeWMAdaptor::shade( X11SalFrame* pFrame, bool bToShaded ) const +{ + if( m_aWMAtoms[ WIN_STATE ] ) + { + pFrame->mbShaded = bToShaded; + 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<<5); + aEvent.xclient.data.l[1] = bToShaded ? (1<<5) : 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 + setGnomeWMState( pFrame ); + } +} + +/* + * 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() ) + { + XLIB_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< Rectangle >& rScreens = m_pSalDisplay->GetXineramaScreens(); + Point aMousePoint( root_x, root_y ); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + { + if( rScreens[i].IsInside( aMousePoint ) ) + { + pFrame->maGeometry.nX = rScreens[i].Left(); + pFrame->maGeometry.nY = rScreens[i].Top(); + pFrame->maGeometry.nWidth = rScreens[i].GetWidth(); + pFrame->maGeometry.nHeight = rScreens[i].GetHeight(); + break; + } + } + } + else + { + Size aSize = m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() ); + pFrame->maGeometry.nX = 0; + pFrame->maGeometry.nY = 0; + pFrame->maGeometry.nWidth = aSize.Width(); + pFrame->maGeometry.nHeight = aSize.Height(); + } + pFrame->CallCallback( SALEVENT_MOVERESIZE, NULL ); + } + } + 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 = NULL; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_CURRENT_DESKTOP ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + nCurrent = int(*(sal_Int32*)pProperty); + XFree( pProperty ); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return nCurrent; +} + +/* + * WMAdaptor::getWindowWorkArea + */ +int WMAdaptor::getWindowWorkArea( XLIB_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 = NULL; + if( XGetWindowProperty( m_pDisplay, + aWindow, + m_aWMAtoms[ NET_WM_DESKTOP ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + nCurrent = int(*(sal_Int32*)pProperty); + XFree( pProperty ); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return nCurrent; +} + +/* + * WMAdaptor::getCurrentWorkArea + */ +// fixme: multi screen case +void WMAdaptor::switchToWorkArea( int nWorkArea, bool bConsiderWM ) const +{ + if( bConsiderWM && ! getWMshouldSwitchWorkspace() ) + return; + + if( m_aWMAtoms[ NET_CURRENT_DESKTOP ] ) + { + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ); + 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->GetDefaultScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } +} + +/* + * WMAdaptor::frameIsMapping + */ +void WMAdaptor::frameIsMapping( X11SalFrame* ) const +{ +} + +/* + * NetWMAdaptor::frameIsMapping + */ +void NetWMAdaptor::frameIsMapping( X11SalFrame* pFrame ) const +{ + setNetWMState( pFrame ); +} + +/* + * WMAdaptor::setFrameStruts + */ +void WMAdaptor::setFrameStruts( X11SalFrame*, + int, int, int, int, + int, int, int, int, + int, int, int, int ) const +{ +} + +/* + * NetWMAdaptor::setFrameStruts + */ +void NetWMAdaptor::setFrameStruts( X11SalFrame* pFrame, + int left, int right, int top, int bottom, + int left_start_y, int left_end_y, + int right_start_y, int right_end_y, + int top_start_x, int top_end_x, + int bottom_start_x, int bottom_end_x ) const +{ + long nData[12]; + nData[0] = left; + nData[1] = right; + nData[2] = top; + nData[3] = bottom; + nData[4] = left_start_y; + nData[5] = left_end_y; + nData[6] = right_start_y; + nData[7] = right_end_y; + nData[8] = top_start_x; + nData[9] = top_end_x; + nData[10]= bottom_start_x; + nData[11]= bottom_end_x; + Atom aProperty = None; + int nSetData = 0; + + if( m_aWMAtoms[NET_WM_STRUT_PARTIAL] ) + { + aProperty = m_aWMAtoms[NET_WM_STRUT_PARTIAL]; + nSetData = 12; + } + else if( m_aWMAtoms[NET_WM_STRUT] ) + { + aProperty = m_aWMAtoms[NET_WM_STRUT]; + nSetData = 4; + } + if( nSetData ) + { + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + aProperty, + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nData, + nSetData + ); + } +} + +/* + * WMAdaptor::setUserTime + */ +void WMAdaptor::setUserTime( X11SalFrame*, long ) const +{ +} + +/* + * NetWMAdaptor::setUserTime + */ +void NetWMAdaptor::setUserTime( X11SalFrame* i_pFrame, 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, + (unsigned char*)&i_nUserTime, + 1 + ); + } +} + +/* + * WMAdaptor::setPID + */ +void WMAdaptor::setPID( X11SalFrame* i_pFrame ) const +{ + if( m_aWMAtoms[NET_WM_PID] ) + { + long nPID = (long)getpid(); + XChangeProperty( m_pDisplay, + i_pFrame->GetShellWindow(), + m_aWMAtoms[NET_WM_PID], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nPID, + 1 + ); + } +} + +/* +* WMAdaptor::setClientMachine +*/ +void WMAdaptor::setClientMachine( X11SalFrame* i_pFrame ) const +{ + rtl::OString aWmClient( rtl::OUStringToOString( GetX11SalData()->GetLocalHostName(), RTL_TEXTENCODING_ASCII_US ) ); + XTextProperty aClientProp = { (unsigned char*)aWmClient.getStr(), XA_STRING, 8, aWmClient.getLength() }; + XSetWMClientMachine( m_pDisplay, i_pFrame->GetShellWindow(), &aClientProp ); +} + +void WMAdaptor::answerPing( X11SalFrame* i_pFrame, XClientMessageEvent* i_pEvent ) const +{ + if( m_aWMAtoms[NET_WM_PING] && + i_pEvent->message_type == m_aWMAtoms[ WM_PROTOCOLS ] && + (Atom)i_pEvent->data.l[0] == m_aWMAtoms[ NET_WM_PING ] ) + { + 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 ); + } +} |