/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <string.h>
#include <string_view>
#include <stdio.h>
#include <stdlib.h>
#include <tools/debug.hxx>
#include <vcl/event.hxx>
#include <vcl/toolkit/floatwin.hxx>
#include <vcl/keycodes.hxx>
#include <vcl/settings.hxx>
#include <vcl/BitmapReadAccess.hxx>
#include <vcl/opengl/OpenGLContext.hxx>
#include <vcl/BitmapTools.hxx>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/shape.h>
#include <headless/BitmapHelper.hxx>
#include <headless/svpbmp.hxx>
#include <unx/saldisp.hxx>
#include <unx/salgdi.h>
#include <unx/salframe.h>
#include <unx/wmadaptor.hxx>
#include <unx/i18n_ic.hxx>
#include <unx/i18n_keysym.hxx>
#include <opengl/zone.hxx>
#include <unx/gensys.h>
#include <window.h>
#include <sal/macros.h>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <com/sun/star/uno/Exception.hpp>
#include <salinst.hxx>
#include <svdata.hxx>
#include <bitmaps.hlst>
#include <cairo-xlib.h>
#include <optional>
#include <algorithm>
#ifndef Button6
# define Button6 6
#endif
#ifndef Button7
# define Button7 7
#endif
using namespace vcl_sal;
constexpr auto CLIENT_EVENTS = StructureNotifyMask
| SubstructureNotifyMask
| KeyPressMask
| KeyReleaseMask
| ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
| EnterWindowMask
| LeaveWindowMask
| FocusChangeMask
| ExposureMask
| VisibilityChangeMask
| PropertyChangeMask
| ColormapChangeMask;
static ::Window hPresentationWindow = None, hPresFocusWindow = None;
static ::std::list< ::Window > aPresentationReparentList;
static int nVisibleFloats = 0;
static void doReparentPresentationDialogues( SalDisplay const * pDisplay )
{
GetGenericUnixSalData()->ErrorTrapPush();
for (auto const& elem : aPresentationReparentList)
{
int x, y;
::Window aRoot, aChild;
unsigned int w, h, bw, d;
XGetGeometry( pDisplay->GetDisplay(),
elem,
&aRoot,
&x, &y, &w, &h, &bw, &d );
XTranslateCoordinates( pDisplay->GetDisplay(),
hPresentationWindow,
aRoot,
x, y,
&x, &y,
&aChild );
XReparentWindow( pDisplay->GetDisplay(),
elem,
aRoot,
x, y );
}
aPresentationReparentList.clear();
if( hPresFocusWindow )
XSetInputFocus( pDisplay->GetDisplay(), hPresFocusWindow, PointerRoot, CurrentTime );
XSync( pDisplay->GetDisplay(), False );
GetGenericUnixSalData()->ErrorTrapPop();
}
bool X11SalFrame::IsOverrideRedirect() const
{
return
((nStyle_ & SalFrameStyleFlags::INTRO) && !pDisplay_->getWMAdaptor()->supportsSplash())
||
(!( nStyle_ & ~SalFrameStyleFlags::DEFAULT ) && !pDisplay_->getWMAdaptor()->supportsFullScreen())
;
}
bool X11SalFrame::IsFloatGrabWindow() const
{
static const char* pDisableGrab = getenv( "SAL_DISABLE_FLOATGRAB" );
return
( ( !pDisableGrab || !*pDisableGrab ) &&
(
(nStyle_ & SalFrameStyleFlags::FLOAT) &&
! (nStyle_ & SalFrameStyleFlags::TOOLTIP) &&
! (nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION)
)
);
}
void X11SalFrame::setXEmbedInfo()
{
if( !m_bXEmbed )
return;
tools::Long aInfo[2];
aInfo[0] = 1; // XEMBED protocol version
aInfo[1] = (bMapped_ ? 1 : 0); // XEMBED_MAPPED
XChangeProperty( pDisplay_->GetDisplay(),
mhWindow,
pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED_INFO ),
pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED_INFO ),
32,
PropModeReplace,
reinterpret_cast<unsigned char*>(aInfo),
SAL_N_ELEMENTS(aInfo) );
}
void X11SalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode )
{
XEvent aEvent;
memset( &aEvent, 0, sizeof(aEvent) );
aEvent.xclient.window = mhForeignParent;
aEvent.xclient.type = ClientMessage;
aEvent.xclient.message_type = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED );
aEvent.xclient.format = 32;
aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime;
aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS
aEvent.xclient.data.l[2] = 0;
aEvent.xclient.data.l[3] = 0;
aEvent.xclient.data.l[4] = 0;
GetGenericUnixSalData()->ErrorTrapPush();
XSendEvent( pDisplay_->GetDisplay(),
mhForeignParent,
False, NoEventMask, &aEvent );
XSync( pDisplay_->GetDisplay(), False );
GetGenericUnixSalData()->ErrorTrapPop();
}
typedef std::vector< unsigned long > NetWmIconData;
namespace
{
constexpr OUString SV_ICON_SIZE48[] =
{
MAINAPP_48_8,
MAINAPP_48_8,
ODT_48_8,
OTT_48_8,
ODS_48_8,
OTS_48_8,
ODG_48_8,
MAINAPP_48_8,
ODP_48_8,
MAINAPP_48_8,
ODM_48_8,
MAINAPP_48_8,
ODB_48_8,
ODF_48_8
};
constexpr OUString SV_ICON_SIZE32[] =
{
MAINAPP_32_8,
MAINAPP_32_8,
ODT_32_8,
OTT_32_8,
ODS_32_8,
OTS_32_8,
ODG_32_8,
MAINAPP_32_8,
ODP_32_8,
MAINAPP_32_8,
ODM_32_8,
MAINAPP_32_8,
ODB_32_8,
ODF_32_8
};
constexpr OUString SV_ICON_SIZE16[] =
{
MAINAPP_16_8,
MAINAPP_16_8,
ODT_16_8,
OTT_16_8,
ODS_16_8,
OTS_16_8,
ODG_16_8,
MAINAPP_16_8,
ODP_16_8,
MAINAPP_16_8,
ODM_16_8,
MAINAPP_16_8,
ODB_16_8,
ODF_16_8
};
}
static void CreateNetWmAppIcon( sal_uInt16 nIcon, NetWmIconData& netwm_icon )
{
const int sizes[ 3 ] = { 48, 32, 16 };
netwm_icon.resize( 48 * 48 + 32 * 32 + 16 * 16 + 3 * 2 );
int pos = 0;
for(int size : sizes)
{
OUString sIcon;
if( size >= 48 )
sIcon = SV_ICON_SIZE48[nIcon];
else if( size >= 32 )
sIcon = SV_ICON_SIZE32[nIcon];
else
sIcon = SV_ICON_SIZE16[nIcon];
BitmapEx aIcon = vcl::bitmap::loadFromName(sIcon, ImageLoadFlags::IgnoreScalingFactor);
if( aIcon.IsEmpty())
continue;
vcl::bitmap::convertBitmap32To24Plus8(aIcon, aIcon);
Bitmap icon = aIcon.GetBitmap();
AlphaMask mask = aIcon.GetAlphaMask();
BitmapScopedReadAccess iconData(icon);
BitmapScopedReadAccess maskData(mask);
netwm_icon[ pos++ ] = size; // width
netwm_icon[ pos++ ] = size; // height
for( int y = 0; y < size; ++y )
for( int x = 0; x < size; ++x )
{
BitmapColor col = iconData->GetColor( y, x );
BitmapColor alpha = maskData->GetColor( y, x );
netwm_icon[ pos++ ] = (((( 255 - alpha.GetBlue()) * 256U ) + col.GetRed()) * 256 + col.GetGreen()) * 256 + col.GetBlue();
}
}
netwm_icon.resize( pos );
}
void X11SalFrame::Init( SalFrameStyleFlags nSalFrameStyle, SalX11Screen nXScreen, SystemParentData const * pParentData, bool bUseGeometry )
{
if( nXScreen.getXScreen() >= GetDisplay()->GetXScreenCount() )
nXScreen = GetDisplay()->GetDefaultXScreen();
if( mpParent )
nXScreen = mpParent->m_nXScreen;
m_nXScreen = nXScreen;
nStyle_ = nSalFrameStyle;
XWMHints Hints;
Hints.flags = InputHint;
Hints.input = (nSalFrameStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) ? False : True;
NetWmIconData netwm_icon;
int x = 0, y = 0;
unsigned int w = 500, h = 500;
XSetWindowAttributes Attributes;
int nAttrMask = CWBorderPixel
| CWBackPixmap
| CWColormap
| CWOverrideRedirect
| CWEventMask
;
Attributes.border_pixel = 0;
Attributes.background_pixmap = None;
Attributes.colormap = GetDisplay()->GetColormap( m_nXScreen ).GetXColormap();
Attributes.override_redirect = False;
Attributes.event_mask = CLIENT_EVENTS;
const SalVisual& rVis = GetDisplay()->GetVisual( m_nXScreen );
::Window aFrameParent = pParentData ? pParentData->aWindow : GetDisplay()->GetRootWindow( m_nXScreen );
::Window aClientLeader = None;
if( bUseGeometry )
{
x = maGeometry.x();
y = maGeometry.y();
w = maGeometry.width();
h = maGeometry.height();
}
if( (nSalFrameStyle & SalFrameStyleFlags::FLOAT) &&
! (nSalFrameStyle & SalFrameStyleFlags::OWNERDRAWDECORATION)
)
{
if( nShowState_ == X11ShowState::Unknown )
{
w = 10;
h = 10;
}
Attributes.override_redirect = True;
}
else if( nSalFrameStyle & SalFrameStyleFlags::SYSTEMCHILD )
{
SAL_WARN_IF( !mpParent, "vcl", "SalFrameStyleFlags::SYSTEMCHILD window without parent" );
if( mpParent )
{
aFrameParent = mpParent->mhWindow;
// FIXME: since with SalFrameStyleFlags::SYSTEMCHILD
// multiple X11SalFrame objects can have the same shell window
// dispatching events in saldisp.cxx is unclear (the first frame)
// wins. HTH this correctly is unclear yet
// for the time being, treat set the shell window to own window
// like for a normal frame
// mhShellWindow = mpParent->GetShellWindow();
}
}
else if( pParentData )
{
// plugin parent may be killed unexpectedly by plugging
// process; start permanently ignoring X errors...
GetGenericUnixSalData()->ErrorTrapPush();
nStyle_ |= SalFrameStyleFlags::PLUG;
Attributes.override_redirect = True;
if( pParentData->nSize >= sizeof(SystemParentData) )
m_bXEmbed = pParentData->bXEmbedSupport;
int x_ret, y_ret;
unsigned int bw, d;
::Window aRoot, aParent;
XGetGeometry( GetXDisplay(), pParentData->aWindow,
&aRoot, &x_ret, &y_ret, &w, &h, &bw, &d );
mhForeignParent = pParentData->aWindow;
mhShellWindow = aParent = mhForeignParent;
::Window* pChildren;
unsigned int nChildren;
bool bBreak = false;
do
{
XQueryTree( GetDisplay()->GetDisplay(), mhShellWindow,
&aRoot, &aParent, &pChildren, &nChildren );
XFree( pChildren );
if( aParent != aRoot )
mhShellWindow = aParent;
int nCount = 0;
Atom* pProps = XListProperties( GetDisplay()->GetDisplay(),
mhShellWindow,
&nCount );
for( int i = 0; i < nCount && ! bBreak; ++i )
bBreak = (pProps[i] == XA_WM_HINTS);
if( pProps )
XFree( pProps );
} while( aParent != aRoot && ! bBreak );
// check if this is really one of our own frames
// do not change the input mask in that case
bool bIsReallyOurFrame = false;
for (auto pSalFrame : GetDisplay()->getFrames() )
if ( static_cast<const X11SalFrame*>( pSalFrame )->GetWindow() == mhForeignParent )
{
bIsReallyOurFrame = true;
break;
}
if (!bIsReallyOurFrame)
{
XSelectInput( GetDisplay()->GetDisplay(), mhForeignParent, StructureNotifyMask | FocusChangeMask );
XSelectInput( GetDisplay()->GetDisplay(), mhShellWindow, StructureNotifyMask | FocusChangeMask );
}
}
else
{
if( ! bUseGeometry )
{
Size aScreenSize( GetDisplay()->getDataForScreen( m_nXScreen ).m_aSize );
w = aScreenSize.Width();
h = aScreenSize.Height();
if( nSalFrameStyle & SalFrameStyleFlags::SIZEABLE &&
nSalFrameStyle & SalFrameStyleFlags::MOVEABLE )
{
Size aBestFitSize(bestmaxFrameSizeForScreenSize(aScreenSize));
w = aBestFitSize.Width();
h = aBestFitSize.Height();
}
if( ! mpParent )
{
// find the last document window (if any)
const X11SalFrame* pFrame = nullptr;
bool bIsDocumentWindow = false;
for (auto pSalFrame : GetDisplay()->getFrames() )
{
pFrame = static_cast< const X11SalFrame* >( pSalFrame );
if( !pFrame->mpParent
&& !pFrame->mbFullScreen
&& ( pFrame->nStyle_ & SalFrameStyleFlags::SIZEABLE )
&& pFrame->GetUnmirroredGeometry().width()
&& pFrame->GetUnmirroredGeometry().height() )
{
bIsDocumentWindow = true;
break;
}
}
if( bIsDocumentWindow )
{
// set a document position and size
// the first frame gets positioned by the window manager
const SalFrameGeometry aGeom( pFrame->GetUnmirroredGeometry() );
x = aGeom.x();
y = aGeom.y();
if( x+static_cast<int>(w)+40 <= static_cast<int>(aScreenSize.Width()) &&
y+static_cast<int>(h)+40 <= static_cast<int>(aScreenSize.Height())
)
{
y += 40;
x += 40;
}
else
{
x = 10; // leave some space for decoration
y = 20;
}
}
else if( GetDisplay()->IsXinerama() )
{
// place frame on same screen as mouse pointer
::Window aRoot, aChild;
int root_x = 0, root_y = 0, lx, ly;
unsigned int mask;
XQueryPointer( GetXDisplay(),
GetDisplay()->GetRootWindow( m_nXScreen ),
&aRoot, &aChild,
&root_x, &root_y, &lx, &ly, &mask );
const std::vector< AbsoluteScreenPixelRectangle >& rScreens = GetDisplay()->GetXineramaScreens();
for(const auto & rScreen : rScreens)
if( rScreen.Contains( AbsoluteScreenPixelPoint( root_x, root_y ) ) )
{
x = rScreen.Left();
y = rScreen.Top();
break;
}
}
}
}
Attributes.win_gravity = pDisplay_->getWMAdaptor()->getInitWinGravity();
nAttrMask |= CWWinGravity;
if( mpParent )
{
Attributes.save_under = True;
nAttrMask |= CWSaveUnder;
}
if( IsOverrideRedirect() )
Attributes.override_redirect = True;
// default icon
if( !(nStyle_ & SalFrameStyleFlags::INTRO) && !(nStyle_ & SalFrameStyleFlags::NOICON))
{
try
{
CreateNetWmAppIcon( mnIconID != SV_ICON_ID_OFFICE ? mnIconID :
(mpParent ? mpParent->mnIconID : SV_ICON_ID_OFFICE), netwm_icon );
}
catch( css::uno::Exception& )
{
// can happen - no ucb during early startup
}
}
// find the top level frame of the transience hierarchy
X11SalFrame* pFrame = this;
while( pFrame->mpParent )
pFrame = pFrame->mpParent;
if( pFrame->nStyle_ & SalFrameStyleFlags::PLUG )
{
// if the top level window is a plugin window,
// then we should place us in the same window group as
// the parent application (or none if there is no window group
// hint in the parent).
if( pFrame->GetShellWindow() )
{
XWMHints* pWMHints = XGetWMHints( pDisplay_->GetDisplay(),
pFrame->GetShellWindow() );
if( pWMHints )
{
if( pWMHints->flags & WindowGroupHint )
{
Hints.flags |= WindowGroupHint;
Hints.window_group = pWMHints->window_group;
}
XFree( pWMHints );
}
}
}
else
{
Hints.flags |= WindowGroupHint;
Hints.window_group = pFrame->GetShellWindow();
// note: for a normal document window this will produce None
// as the window is not yet created and the shell window is
// initialized to None. This must be corrected after window creation.
aClientLeader = GetDisplay()->GetDrawable( m_nXScreen );
}
}
nShowState_ = X11ShowState::Unknown;
bViewable_ = true;
bMapped_ = false;
nVisibility_ = VisibilityFullyObscured;
mhWindow = XCreateWindow( GetXDisplay(),
aFrameParent,
x, y,
w, h,
0,
rVis.GetDepth(),
InputOutput,
rVis.GetVisual(),
nAttrMask,
&Attributes );
mpSurface = cairo_xlib_surface_create(GetXDisplay(), mhWindow,
rVis.GetVisual(),
w, h);
// FIXME: see above: fake shell window for now to own window
if( pParentData == nullptr )
{
mhShellWindow = mhWindow;
}
// correct window group if necessary
if( (Hints.flags & WindowGroupHint) == WindowGroupHint )
{
if( Hints.window_group == None )
Hints.window_group = GetShellWindow();
}
maGeometry.setPosSize({ x, y }, { static_cast<tools::Long>(w), static_cast<tools::Long>(h) });
updateScreenNumber();
XSync( GetXDisplay(), False );
setXEmbedInfo();
Time nUserTime = (nStyle_ & (SalFrameStyleFlags::OWNERDRAWDECORATION | SalFrameStyleFlags::TOOLWINDOW) ) == SalFrameStyleFlags::NONE ?
pDisplay_->GetLastUserEventTime() : 0;
pDisplay_->getWMAdaptor()->setUserTime( this, nUserTime );
if( ! pParentData && ! IsChildWindow() && ! Attributes.override_redirect )
{
XSetWMHints( GetXDisplay(), mhWindow, &Hints );
// WM Protocols && internals
Atom a[3];
int n = 0;
a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_DELETE_WINDOW );
// LibreOffice advertises NET_WM_PING atom, so mutter rightfully warns of an unresponsive application during debugging.
// Hack that out unconditionally for debug builds, as per https://bugzilla.redhat.com/show_bug.cgi?id=981149
// upstream refuses to make this configurable in any way.
// NOTE: You need to use the 'gen' backend for this to work (SAL_USE_VCLPLUGIN=gen)
#if OSL_DEBUG_LEVEL < 1
if( pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING ) )
a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING );
#endif
if( nSalFrameStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_TAKE_FOCUS );
XSetWMProtocols( GetXDisplay(), GetShellWindow(), a, n );
// force wm class hint
mnExtStyle = ~0;
if (mpParent)
m_sWMClass = mpParent->m_sWMClass;
SetExtendedFrameStyle( 0 );
XSizeHints* pHints = XAllocSizeHints();
pHints->flags = PWinGravity | PPosition;
pHints->win_gravity = GetDisplay()->getWMAdaptor()->getPositionWinGravity();
pHints->x = 0;
pHints->y = 0;
if( mbFullScreen )
{
pHints->flags |= PMaxSize | PMinSize;
pHints->max_width = w+100;
pHints->max_height = h+100;
pHints->min_width = w;
pHints->min_height = h;
}
XSetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints );
XFree (pHints);
// set PID and WM_CLIENT_MACHINE
pDisplay_->getWMAdaptor()->setClientMachine( this );
pDisplay_->getWMAdaptor()->setPID( this );
// set client leader
if( aClientLeader )
{
XChangeProperty( GetXDisplay(),
mhWindow,
pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_CLIENT_LEADER),
XA_WINDOW,
32,
PropModeReplace,
reinterpret_cast<unsigned char*>(&aClientLeader),
1
);
}
#define DECOFLAGS (SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::CLOSEABLE)
int nDecoFlags = WMAdaptor::decoration_All;
if (m_bIsPartialFullScreen || (nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION))
nDecoFlags = 0;
else if( (nStyle_ & DECOFLAGS ) != DECOFLAGS || (nStyle_ & SalFrameStyleFlags::TOOLWINDOW) )
{
if( nStyle_ & DECOFLAGS )
// if any decoration, then show a border
nDecoFlags = WMAdaptor::decoration_Border;
else
nDecoFlags = 0;
if( ! mpParent && (nStyle_ & DECOFLAGS) )
// don't add a min button if window should be decorationless
nDecoFlags |= WMAdaptor::decoration_MinimizeBtn;
if( nStyle_ & SalFrameStyleFlags::CLOSEABLE )
nDecoFlags |= WMAdaptor::decoration_CloseBtn;
if( nStyle_ & SalFrameStyleFlags::SIZEABLE )
{
nDecoFlags |= WMAdaptor::decoration_Resize;
if( ! (nStyle_ & SalFrameStyleFlags::TOOLWINDOW) )
nDecoFlags |= WMAdaptor::decoration_MaximizeBtn;
}
if( nStyle_ & SalFrameStyleFlags::MOVEABLE )
nDecoFlags |= WMAdaptor::decoration_Title;
}
WMWindowType eType = WMWindowType::Normal;
if( nStyle_ & SalFrameStyleFlags::INTRO )
eType = WMWindowType::Splash;
if( (nStyle_ & SalFrameStyleFlags::DIALOG) && hPresentationWindow == None )
eType = WMWindowType::ModelessDialogue;
if( nStyle_ & SalFrameStyleFlags::TOOLWINDOW )
eType = WMWindowType::Utility;
if( nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION )
eType = WMWindowType::Toolbar;
if (m_bIsPartialFullScreen && GetDisplay()->getWMAdaptor()->isLegacyPartialFullscreen())
eType = WMWindowType::Dock;
GetDisplay()->getWMAdaptor()->
setFrameTypeAndDecoration( this,
eType,
nDecoFlags,
hPresentationWindow ? nullptr : mpParent );
if (!m_bIsPartialFullScreen && (nStyle_ & (SalFrameStyleFlags::DEFAULT |
SalFrameStyleFlags::OWNERDRAWDECORATION|
SalFrameStyleFlags::FLOAT |
SalFrameStyleFlags::INTRO))
== SalFrameStyleFlags::DEFAULT )
pDisplay_->getWMAdaptor()->maximizeFrame( this );
if( !netwm_icon.empty() && GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_ICON ))
XChangeProperty( GetXDisplay(), mhWindow,
GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_ICON ),
XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<unsigned char*>(netwm_icon.data()), netwm_icon.size());
}
m_nWorkArea = GetDisplay()->getWMAdaptor()->getCurrentWorkArea();
// Pointer
SetPointer( PointerStyle::Arrow );
}
X11SalFrame::X11SalFrame( SalFrame *pParent, SalFrameStyleFlags nSalFrameStyle,
SystemParentData const * pSystemParent ) :
m_nXScreen( 0 ),
maAlwaysOnTopRaiseTimer( "vcl::X11SalFrame maAlwaysOnTopRaiseTimer" )
{
GenericUnixSalData *pData = GetGenericUnixSalData();
mpParent = static_cast< X11SalFrame* >( pParent );
mbTransientForRoot = false;
pDisplay_ = vcl_sal::getSalDisplay(pData);
// insert frame in framelist
pDisplay_->registerFrame( this );
mhWindow = None;
mpSurface = nullptr;
mhShellWindow = None;
mhStackingWindow = None;
mhForeignParent = None;
m_bSetFocusOnMap = false;
pGraphics_ = nullptr;
pFreeGraphics_ = nullptr;
hCursor_ = None;
nCaptured_ = 0;
mbSendExtKeyModChange = false;
mnExtKeyMod = ModKeyFlags::NONE;
nShowState_ = X11ShowState::Unknown;
nWidth_ = 0;
nHeight_ = 0;
nStyle_ = SalFrameStyleFlags::NONE;
mnExtStyle = 0;
bAlwaysOnTop_ = false;
// set bViewable_ to true: hack GetClientSize to report something
// different to 0/0 before first map
bViewable_ = true;
bMapped_ = false;
bDefaultPosition_ = true;
nVisibility_ = VisibilityFullyObscured;
m_nWorkArea = 0;
m_bXEmbed = false;
mpInputContext = nullptr;
mbInputFocus = False;
maAlwaysOnTopRaiseTimer.SetInvokeHandler( LINK( this, X11SalFrame, HandleAlwaysOnTopRaise ) );
maAlwaysOnTopRaiseTimer.SetTimeout( 100 );
meWindowType = WMWindowType::Normal;
mbMaximizedVert = false;
mbMaximizedHorz = false;
mbFullScreen = false;
m_bIsPartialFullScreen = false;
mnIconID = SV_ICON_ID_OFFICE;
if( mpParent )
mpParent->maChildren.push_back( this );
Init( nSalFrameStyle, GetDisplay()->GetDefaultXScreen(), pSystemParent );
}
X11SalFrame::~X11SalFrame()
{
notifyDelete();
m_vClipRectangles.clear();
if( mhStackingWindow )
aPresentationReparentList.remove( mhStackingWindow );
// remove from parent's list
if( mpParent )
mpParent->maChildren.remove( this );
// deregister on SalDisplay
pDisplay_->deregisterFrame( this );
// unselect all events, some may be still in the queue anyway
if( ! IsSysChildWindow() )
XSelectInput( GetXDisplay(), GetShellWindow(), 0 );
XSelectInput( GetXDisplay(), GetWindow(), 0 );
ShowFullScreen( false, 0 );
if( bMapped_ )
Show( false );
if( mpInputContext )
{
mpInputContext->UnsetICFocus();
mpInputContext->Unmap();
mpInputContext.reset();
}
if( GetWindow() == hPresentationWindow )
{
hPresentationWindow = None;
doReparentPresentationDialogues( GetDisplay() );
}
if( pGraphics_ )
{
pGraphics_->DeInit();
pGraphics_.reset();
}
if( pFreeGraphics_ )
{
pFreeGraphics_->DeInit();
pFreeGraphics_.reset();
}
// reset all OpenGL contexts using this window
rtl::Reference<OpenGLContext> pContext = ImplGetSVData()->maGDIData.mpLastContext;
while( pContext.is() )
{
if (static_cast<const GLX11Window&>(pContext->getOpenGLWindow()).win == mhWindow)
pContext->reset();
pContext = pContext->mpPrevContext;
}
if (mpSurface)
cairo_surface_destroy(mpSurface);
XDestroyWindow( GetXDisplay(), mhWindow );
}
void X11SalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
{
if( nStyle != mnExtStyle && ! IsChildWindow() )
{
mnExtStyle = nStyle;
updateWMClass();
}
}
const SystemEnvData* X11SalFrame::GetSystemData() const
{
X11SalFrame *pFrame = const_cast<X11SalFrame*>(this);
pFrame->maSystemChildData.pDisplay = GetXDisplay();
pFrame->maSystemChildData.SetWindowHandle(pFrame->GetWindow());
pFrame->maSystemChildData.pSalFrame = pFrame;
pFrame->maSystemChildData.pWidget = nullptr;
pFrame->maSystemChildData.pVisual = GetDisplay()->GetVisual( m_nXScreen ).GetVisual();
pFrame->maSystemChildData.nScreen = m_nXScreen.getXScreen();
pFrame->maSystemChildData.aShellWindow = pFrame->GetShellWindow();
pFrame->maSystemChildData.toolkit = SystemEnvData::Toolkit::Gen;
pFrame->maSystemChildData.platform = SystemEnvData::Platform::Xcb;
return &maSystemChildData;
}
SalGraphics *X11SalFrame::AcquireGraphics()
{
if( pGraphics_ )
return nullptr;
if( pFreeGraphics_ )
{
pGraphics_ = std::move(pFreeGraphics_);
}
else
{
pGraphics_.reset(new X11SalGraphics());
pGraphics_->Init(*this, GetWindow(), m_nXScreen);
}
return pGraphics_.get();
}
void X11SalFrame::ReleaseGraphics( SalGraphics *pGraphics )
{
SAL_WARN_IF( pGraphics != pGraphics_.get(), "vcl", "SalFrame::ReleaseGraphics pGraphics!=pGraphics_" );
if( pGraphics != pGraphics_.get() )
return;
pFreeGraphics_ = std::move(pGraphics_);
}
void X11SalFrame::updateGraphics( bool bClear )
{
Drawable aDrawable = bClear ? None : GetWindow();
if( pGraphics_ )
pGraphics_->SetDrawable( aDrawable, mpSurface, m_nXScreen );
if( pFreeGraphics_ )
pFreeGraphics_->SetDrawable( aDrawable, mpSurface, m_nXScreen );
}
void X11SalFrame::SetIcon( sal_uInt16 nIcon )
{
if ( IsChildWindow() )
return;
// 0 == default icon -> #1
if ( nIcon == 0 )
nIcon = 1;
mnIconID = nIcon;
NetWmIconData netwm_icon;
CreateNetWmAppIcon( nIcon, netwm_icon );
if( !netwm_icon.empty() && GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_ICON ))
XChangeProperty( GetXDisplay(), mhWindow,
GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_ICON ),
XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<unsigned char*>(netwm_icon.data()), netwm_icon.size());
}
void X11SalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight )
{
if( IsChildWindow() )
return;
if( !GetShellWindow() ||
(nStyle_ & (SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::OWNERDRAWDECORATION) ) == SalFrameStyleFlags::FLOAT )
return;
XSizeHints* pHints = XAllocSizeHints();
tools::Long nSupplied = 0;
XGetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints,
&nSupplied
);
pHints->max_width = nWidth;
pHints->max_height = nHeight;
pHints->flags |= PMaxSize;
XSetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints );
XFree( pHints );
}
void X11SalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight )
{
if( IsChildWindow() )
return;
if( !GetShellWindow() ||
(nStyle_ & (SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::OWNERDRAWDECORATION) ) == SalFrameStyleFlags::FLOAT )
return;
XSizeHints* pHints = XAllocSizeHints();
tools::Long nSupplied = 0;
XGetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints,
&nSupplied
);
pHints->min_width = nWidth;
pHints->min_height = nHeight;
pHints->flags |= PMinSize;
XSetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints );
XFree( pHints );
}
// Show + Pos (x,y,z) + Size (width,height)
void X11SalFrame::Show( bool bVisible, bool bNoActivate )
{
if( ( bVisible && bMapped_ )
|| ( !bVisible && !bMapped_ ) )
return;
// HACK: this is a workaround for (at least) kwin
// even though transient frames should be kept above their parent
// this does not necessarily hold true for DOCK type windows
// so artificially set ABOVE and remove it again on hide
if( mpParent && mpParent->m_bIsPartialFullScreen && pDisplay_->getWMAdaptor()->isLegacyPartialFullscreen())
pDisplay_->getWMAdaptor()->enableAlwaysOnTop( this, bVisible );
bMapped_ = bVisible;
bViewable_ = bVisible;
setXEmbedInfo();
if( bVisible )
{
if( ! (nStyle_ & SalFrameStyleFlags::INTRO) )
{
// hide all INTRO frames
for (auto pSalFrame : GetDisplay()->getFrames() )
{
const X11SalFrame* pFrame = static_cast< const X11SalFrame* >( pSalFrame );
// look for intro bit map; if present, hide it
if( pFrame->nStyle_ & SalFrameStyleFlags::INTRO )
{
if( pFrame->bMapped_ )
const_cast<X11SalFrame*>(pFrame)->Show( false );
}
}
}
// update NET_WM_STATE which may have been deleted due to earlier Show(false)
if( nShowState_ == X11ShowState::Hidden )
GetDisplay()->getWMAdaptor()->frameIsMapping( this );
/*
* Actually this is rather exotic and currently happens only in conjunction
* with the basic dialogue editor,
* which shows a frame and instantly hides it again. After that the
* editor window is shown and the WM takes this as an opportunity
* to show our hidden transient frame also. So Show( false ) must
* withdraw the frame AND delete the WM_TRANSIENT_FOR property.
* In case the frame is shown again, the transient hint must be restored here.
*/
if( ! IsChildWindow()
&& ! IsOverrideRedirect()
&& ! IsFloatGrabWindow()
&& mpParent
)
{
GetDisplay()->getWMAdaptor()->changeReferenceFrame( this, mpParent );
}
// #i45160# switch to desktop where a dialog with parent will appear
if( mpParent && mpParent->m_nWorkArea != m_nWorkArea )
GetDisplay()->getWMAdaptor()->switchToWorkArea( mpParent->m_nWorkArea );
if( IsFloatGrabWindow() &&
mpParent &&
nVisibleFloats == 0 &&
! GetDisplay()->GetCaptureFrame() )
{
/* #i39420#
* outsmart KWin's "focus strictly under mouse" mode
* which insists on taking the focus from the document
* to the new float. Grab focus to parent frame BEFORE
* showing the float (cannot grab it to the float
* before show).
*/
XGrabPointer( GetXDisplay(),
mpParent->GetWindow(),
True,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync,
GrabModeAsync,
None,
mpParent ? mpParent->GetCursor() : None,
CurrentTime
);
}
Time nUserTime = 0;
if( ! bNoActivate && !(nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION) )
nUserTime = pDisplay_->GetX11ServerTime();
GetDisplay()->getWMAdaptor()->setUserTime( this, nUserTime );
if( ! bNoActivate && (nStyle_ & SalFrameStyleFlags::TOOLWINDOW) )
m_bSetFocusOnMap = true;
// actually map the window
if( m_bXEmbed )
askForXEmbedFocus( 0 );
else
{
if( GetWindow() != GetShellWindow() && ! IsSysChildWindow() )
{
if( IsChildWindow() )
XMapWindow( GetXDisplay(), GetShellWindow() );
XSelectInput( GetXDisplay(), GetShellWindow(), CLIENT_EVENTS );
}
if( nStyle_ & SalFrameStyleFlags::FLOAT )
XMapRaised( GetXDisplay(), GetWindow() );
else
XMapWindow( GetXDisplay(), GetWindow() );
}
XSelectInput( GetXDisplay(), GetWindow(), CLIENT_EVENTS );
if( maGeometry.width() > 0
&& maGeometry.height() > 0
&& ( nWidth_ != static_cast<int>(maGeometry.width())
|| nHeight_ != static_cast<int>(maGeometry.height()) ) )
{
nWidth_ = maGeometry.width();
nHeight_ = maGeometry.height();
}
XSync( GetXDisplay(), False );
if( IsFloatGrabWindow() )
{
/*
* Sawfish and twm can be switched to enter-exit focus behaviour. In this case
* we must grab the pointer else the dumb WM will put the focus to the
* override-redirect float window. The application window will be deactivated
* which causes that the floats are destroyed, so the user can never click on
* a menu because it vanishes as soon as he enters it.
*/
nVisibleFloats++;
if( nVisibleFloats == 1 && ! GetDisplay()->GetCaptureFrame() )
{
/* #i39420# now move grab to the new float window */
XGrabPointer( GetXDisplay(),
GetWindow(),
True,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync,
GrabModeAsync,
None,
mpParent ? mpParent->GetCursor() : None,
CurrentTime
);
}
}
CallCallback( SalEvent::Resize, nullptr );
/*
* sometimes a message box/dialogue is brought up when a frame is not mapped
* the corresponding TRANSIENT_FOR hint is then set to the root window
* so that the dialogue shows in all cases. Correct it here if the
* frame is shown afterwards.
*/
if( ! IsChildWindow()
&& ! IsOverrideRedirect()
&& ! IsFloatGrabWindow()
)
{
for (auto const& child : maChildren)
{
if( child->mbTransientForRoot )
GetDisplay()->getWMAdaptor()->changeReferenceFrame( child, this );
}
}
/*
* leave X11ShowState::Unknown as this indicates first mapping
* and is only reset int HandleSizeEvent
*/
if( nShowState_ != X11ShowState::Unknown )
nShowState_ = X11ShowState::Normal;
/*
* plugged windows don't necessarily get the
* focus on show because the parent may already be mapped
* and have the focus. So try to set the focus
* to the child on Show(true)
*/
if( (nStyle_ & SalFrameStyleFlags::PLUG) && ! m_bXEmbed )
XSetInputFocus( GetXDisplay(),
GetWindow(),
RevertToParent,
CurrentTime );
if( mpParent )
{
// push this frame so it will be in front of its siblings
// only necessary for insane transient behaviour of Dtwm/olwm
mpParent->maChildren.remove( this );
mpParent->maChildren.push_front(this);
}
}
else
{
if( getInputContext() )
getInputContext()->Unmap();
if( ! IsChildWindow() )
{
/* FIXME: Is deleting the property really necessary ? It hurts
* owner drawn windows at least.
*/
if( mpParent && ! (nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION) )
XDeleteProperty( GetXDisplay(), GetShellWindow(), GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::WM_TRANSIENT_FOR ) );
XWithdrawWindow( GetXDisplay(), GetShellWindow(), m_nXScreen.getXScreen() );
}
else if( ! m_bXEmbed )
XUnmapWindow( GetXDisplay(), GetWindow() );
nShowState_ = X11ShowState::Hidden;
if( IsFloatGrabWindow() && nVisibleFloats )
{
nVisibleFloats--;
if( nVisibleFloats == 0 && ! GetDisplay()->GetCaptureFrame() )
XUngrabPointer( GetXDisplay(),
CurrentTime );
}
// flush here; there may be a very seldom race between
// the display connection used for clipboard and our connection
Flush();
}
}
void X11SalFrame::ToTop( SalFrameToTop nFlags )
{
if( ( nFlags & SalFrameToTop::RestoreWhenMin )
&& ! ( nStyle_ & SalFrameStyleFlags::FLOAT )
&& nShowState_ != X11ShowState::Hidden
&& nShowState_ != X11ShowState::Unknown
)
{
GetDisplay()->getWMAdaptor()->frameIsMapping( this );
if( GetWindow() != GetShellWindow() && ! IsSysChildWindow() )
XMapWindow( GetXDisplay(), GetShellWindow() );
XMapWindow( GetXDisplay(), GetWindow() );
}
::Window aToTopWindow = IsSysChildWindow() ? GetWindow() : GetShellWindow();
if( ! (nFlags & SalFrameToTop::GrabFocusOnly) )
{
XRaiseWindow( GetXDisplay(), aToTopWindow );
}
if( ( ( nFlags & SalFrameToTop::GrabFocus ) || ( nFlags & SalFrameToTop::GrabFocusOnly ) )
&& bMapped_ )
{
if( m_bXEmbed )
askForXEmbedFocus( 0 );
else
XSetInputFocus( GetXDisplay(), aToTopWindow, RevertToParent, CurrentTime );
}
else if( ( nFlags & SalFrameToTop::RestoreWhenMin ) || ( nFlags & SalFrameToTop::ForegroundTask ) )
{
Time nTimestamp = pDisplay_->GetX11ServerTime();
GetDisplay()->getWMAdaptor()->activateWindow( this, nTimestamp );
}
}
void X11SalFrame::GetWorkArea( AbsoluteScreenPixelRectangle& rWorkArea )
{
rWorkArea = pDisplay_->getWMAdaptor()->getWorkArea( 0 );
}
void X11SalFrame::GetClientSize( tools::Long &rWidth, tools::Long &rHeight )
{
if( ! bViewable_ )
{
rWidth = rHeight = 0;
return;
}
rWidth = maGeometry.width();
rHeight = maGeometry.height();
if( !rWidth || !rHeight )
{
XWindowAttributes aAttrib;
XGetWindowAttributes( GetXDisplay(), GetWindow(), &aAttrib );
rWidth = aAttrib.width;
rHeight = aAttrib.height;
maGeometry.setSize({ aAttrib.width, aAttrib.height });
}
}
void X11SalFrame::Center( )
{
int nX, nY;
AbsoluteScreenPixelSize aRealScreenSize(GetDisplay()->getDataForScreen(m_nXScreen).m_aSize);
AbsoluteScreenPixelRectangle aScreen({ 0, 0 }, aRealScreenSize);
if( GetDisplay()->IsXinerama() )
{
// get xinerama screen we are on
// if there is a parent, use its center for screen determination
// else use the pointer
::Window aRoot, aChild;
int root_x, root_y, x, y;
unsigned int mask;
if( mpParent )
{
root_x = mpParent->maGeometry.x() + mpParent->maGeometry.width() / 2;
root_y = mpParent->maGeometry.y() + mpParent->maGeometry.height() / 2;
}
else
XQueryPointer( GetXDisplay(),
GetShellWindow(),
&aRoot, &aChild,
&root_x, &root_y,
&x, &y,
&mask );
const std::vector< AbsoluteScreenPixelRectangle >& rScreens = GetDisplay()->GetXineramaScreens();
for(const auto & rScreen : rScreens)
if( rScreen.Contains( AbsoluteScreenPixelPoint( root_x, root_y ) ) )
{
aScreen.SetPos(rScreen.GetPos());
aRealScreenSize = rScreen.GetSize();
break;
}
}
if( mpParent )
{
X11SalFrame* pFrame = mpParent;
while( pFrame->mpParent )
pFrame = pFrame->mpParent;
if( pFrame->maGeometry.width() < 1 || pFrame->maGeometry.height() < 1 )
{
AbsoluteScreenPixelRectangle aRect;
pFrame->GetPosSize( aRect );
pFrame->maGeometry.setPosSize(tools::Rectangle(aRect));
}
if( pFrame->nStyle_ & SalFrameStyleFlags::PLUG )
{
::Window aRoot;
unsigned int nScreenWidth, nScreenHeight, bw, depth;
int nScreenX, nScreenY;
XGetGeometry( GetXDisplay(),
pFrame->GetShellWindow(),
&aRoot,
&nScreenX, &nScreenY,
&nScreenWidth, &nScreenHeight,
&bw, &depth );
aScreen = {{ nScreenX, nScreenY }, Size(nScreenWidth, nScreenHeight)};
}
else
aScreen = AbsoluteScreenPixelRectangle(pFrame->maGeometry.posSize());
}
if( mpParent && mpParent->nShowState_ == X11ShowState::Normal )
{
if( maGeometry.width() >= mpParent->maGeometry.width() &&
maGeometry.height() >= mpParent->maGeometry.height() )
{
nX = aScreen.getX() + 40;
nY = aScreen.getY() + 40;
}
else
{
// center the window relative to the top level frame
nX = (aScreen.GetWidth() - static_cast<int>(maGeometry.width()) ) / 2 + aScreen.getX();
nY = (aScreen.GetHeight() - static_cast<int>(maGeometry.height())) / 2 + aScreen.getY();
}
}
else
{
// center the window relative to screen
nX = (aRealScreenSize.getWidth() - static_cast<int>(maGeometry.width()) ) / 2 + aScreen.getX();
nY = (aRealScreenSize.getHeight() - static_cast<int>(maGeometry.height())) / 2 + aScreen.getY();
}
nX = nX < 0 ? 0 : nX;
nY = nY < 0 ? 0 : nY;
bDefaultPosition_ = False;
if( mpParent )
{
nX -= mpParent->maGeometry.x();
nY -= mpParent->maGeometry.y();
}
SetPosSize({ { nX, nY }, maGeometry.size() });
}
void X11SalFrame::updateScreenNumber()
{
if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 )
{
AbsoluteScreenPixelPoint aPoint( maGeometry.x(), maGeometry.y() );
const std::vector<AbsoluteScreenPixelRectangle>& rScreenRects( GetDisplay()->GetXineramaScreens() );
size_t nScreens = rScreenRects.size();
for( size_t i = 0; i < nScreens; i++ )
{
if( rScreenRects[i].Contains( aPoint ) )
{
maGeometry.setScreen(static_cast<unsigned int>(i));
break;
}
}
}
else
maGeometry.setScreen(m_nXScreen.getXScreen());
}
void X11SalFrame::SetPosSize( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt16 nFlags )
{
if( nStyle_ & SalFrameStyleFlags::PLUG )
return;
// relative positioning in X11SalFrame::SetPosSize
AbsoluteScreenPixelRectangle aPosSize( AbsoluteScreenPixelPoint( maGeometry.x(), maGeometry.y() ), AbsoluteScreenPixelSize( maGeometry.width(), maGeometry.height() ) );
aPosSize.Normalize();
if( ! ( nFlags & SAL_FRAME_POSSIZE_X ) )
{
nX = aPosSize.Left();
if( mpParent )
nX -= mpParent->maGeometry.x();
}
if( ! ( nFlags & SAL_FRAME_POSSIZE_Y ) )
{
nY = aPosSize.Top();
if( mpParent )
nY -= mpParent->maGeometry.y();
}
if( ! ( nFlags & SAL_FRAME_POSSIZE_WIDTH ) )
nWidth = aPosSize.GetWidth();
if( ! ( nFlags & SAL_FRAME_POSSIZE_HEIGHT ) )
nHeight = aPosSize.GetHeight();
aPosSize = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( nX, nY ), AbsoluteScreenPixelSize( nWidth, nHeight ) );
if( ! ( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) )
{
if( bDefaultPosition_ )
{
maGeometry.setSize(Size(aPosSize.GetSize()));
Center();
}
else
SetSize( Size( nWidth, nHeight ) );
}
else
SetPosSize( aPosSize );
bDefaultPosition_ = False;
}
void X11SalFrame::SetAlwaysOnTop( bool bOnTop )
{
if( ! IsOverrideRedirect() )
{
bAlwaysOnTop_ = bOnTop;
pDisplay_->getWMAdaptor()->enableAlwaysOnTop( this, bOnTop );
}
}
constexpr auto FRAMESTATE_MASK_MAXIMIZED_GEOMETRY =
vcl::WindowDataMask::MaximizedX | vcl::WindowDataMask::MaximizedY |
vcl::WindowDataMask::MaximizedWidth | vcl::WindowDataMask::MaximizedHeight;
void X11SalFrame::SetWindowState( const vcl::WindowData *pState )
{
if (pState == nullptr)
return;
// Request for position or size change
if (pState->mask() & vcl::WindowDataMask::PosSize)
{
/* #i44325#
* if maximized, set restore size and guess maximized size from last time
* in state change below maximize window
*/
if( ! IsChildWindow() &&
(pState->mask() & vcl::WindowDataMask::PosSizeState) == vcl::WindowDataMask::PosSizeState &&
(pState->state() & vcl::WindowState::Maximized) &&
(pState->mask() & FRAMESTATE_MASK_MAXIMIZED_GEOMETRY) == FRAMESTATE_MASK_MAXIMIZED_GEOMETRY
)
{
XSizeHints* pHints = XAllocSizeHints();
tools::Long nSupplied = 0;
XGetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints,
&nSupplied );
pHints->flags |= PPosition | PWinGravity;
pHints->x = pState->x();
pHints->y = pState->y();
pHints->win_gravity = pDisplay_->getWMAdaptor()->getPositionWinGravity();
XSetWMNormalHints(GetXDisplay(), GetShellWindow(), pHints);
XFree( pHints );
XMoveResizeWindow(GetXDisplay(), GetShellWindow(), pState->x(), pState->y(),
pState->width(), pState->height());
// guess maximized geometry from last time
maGeometry.setPos({ pState->GetMaximizedX(), pState->GetMaximizedY() });
maGeometry.setSize({ static_cast<tools::Long>(pState->GetMaximizedWidth()), static_cast<tools::Long>(pState->GetMaximizedHeight()) });
cairo_xlib_surface_set_size(mpSurface, pState->GetMaximizedWidth(), pState->GetMaximizedHeight());
updateScreenNumber();
}
else
{
bool bDoAdjust = false;
AbsoluteScreenPixelRectangle aPosSize;
// initialize with current geometry
if ((pState->mask() & vcl::WindowDataMask::PosSize) != vcl::WindowDataMask::PosSize)
GetPosSize(aPosSize);
sal_uInt16 nPosFlags = 0;
// change requested properties
if (pState->mask() & vcl::WindowDataMask::X)
{
aPosSize.SetPosX(pState->x() - (mpParent ? mpParent->maGeometry.x() : 0));
nPosFlags |= SAL_FRAME_POSSIZE_X;
}
if (pState->mask() & vcl::WindowDataMask::Y)
{
aPosSize.SetPosY(pState->y() - (mpParent ? mpParent->maGeometry.y() : 0));
nPosFlags |= SAL_FRAME_POSSIZE_Y;
}
if (pState->mask() & vcl::WindowDataMask::Width)
{
tools::Long nWidth = pState->width() > 0 ? pState->width() - 1 : 0;
aPosSize.setWidth (nWidth);
bDoAdjust = true;
}
if (pState->mask() & vcl::WindowDataMask::Height)
{
int nHeight = pState->height() > 0 ? pState->height() - 1 : 0;
aPosSize.setHeight (nHeight);
bDoAdjust = true;
}
const AbsoluteScreenPixelSize& aScreenSize = pDisplay_->getDataForScreen( m_nXScreen ).m_aSize;
if( bDoAdjust && aPosSize.GetWidth() <= aScreenSize.Width()
&& aPosSize.GetHeight() <= aScreenSize.Height() )
{
SalFrameGeometry aGeom = maGeometry;
if( ! (nStyle_ & ( SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::PLUG ) ) &&
mpParent && aGeom.leftDecoration() == 0 && aGeom.topDecoration() == 0)
{
aGeom = mpParent->maGeometry;
if (aGeom.leftDecoration() == 0 && aGeom.topDecoration() == 0)
aGeom.setDecorations(5, 20, 5, 5);
}
auto nRight = aPosSize.Right() + (mpParent ? mpParent->maGeometry.x() : 0);
auto nBottom = aPosSize.Bottom() + (mpParent ? mpParent->maGeometry.y() : 0);
auto nLeft = aPosSize.Left() + (mpParent ? mpParent->maGeometry.x() : 0);
auto nTop = aPosSize.Top() + (mpParent ? mpParent->maGeometry.y() : 0);
// adjust position so that frame fits onto screen
if( nRight+static_cast<tools::Long>(aGeom.rightDecoration()) > aScreenSize.Width()-1 )
aPosSize.Move( aScreenSize.Width() - nRight - static_cast<tools::Long>(aGeom.rightDecoration()), 0 );
if( nBottom+static_cast<tools::Long>(aGeom.bottomDecoration()) > aScreenSize.Height()-1 )
aPosSize.Move( 0, aScreenSize.Height() - nBottom - static_cast<tools::Long>(aGeom.bottomDecoration()) );
if( nLeft < static_cast<tools::Long>(aGeom.leftDecoration()) )
aPosSize.Move( static_cast<tools::Long>(aGeom.leftDecoration()) - nLeft, 0 );
if( nTop < static_cast<tools::Long>(aGeom.topDecoration()) )
aPosSize.Move( 0, static_cast<tools::Long>(aGeom.topDecoration()) - nTop );
}
SetPosSize(aPosSize.getX(), aPosSize.getY(),
aPosSize.GetWidth(), aPosSize.GetHeight(),
SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT |
nPosFlags);
}
}
// request for status change
if (!(pState->mask() & vcl::WindowDataMask::State))
return;
if (pState->state() & vcl::WindowState::Maximized)
{
nShowState_ = X11ShowState::Normal;
if( ! (pState->state() & (vcl::WindowState::MaximizedHorz|vcl::WindowState::MaximizedVert) ) )
Maximize();
else
{
bool bHorz(pState->state() & vcl::WindowState::MaximizedHorz);
bool bVert(pState->state() & vcl::WindowState::MaximizedVert);
GetDisplay()->getWMAdaptor()->maximizeFrame( this, bHorz, bVert );
}
maRestorePosSize = AbsoluteScreenPixelRectangle(pState->posSize());
}
else if( mbMaximizedHorz || mbMaximizedVert )
GetDisplay()->getWMAdaptor()->maximizeFrame( this, false, false );
if (pState->state() & vcl::WindowState::Minimized)
{
if (nShowState_ == X11ShowState::Unknown)
nShowState_ = X11ShowState::Normal;
Minimize();
}
if (pState->state() & vcl::WindowState::Normal)
{
if (nShowState_ != X11ShowState::Normal)
Restore();
}
}
bool X11SalFrame::GetWindowState( vcl::WindowData* pState )
{
if( X11ShowState::Minimized == nShowState_ )
pState->setState(vcl::WindowState::Minimized);
else
pState->setState(vcl::WindowState::Normal);
AbsoluteScreenPixelRectangle aPosSize;
if( maRestorePosSize.IsEmpty() )
GetPosSize( aPosSize );
else
aPosSize = maRestorePosSize;
if( mbMaximizedHorz )
pState->rState() |= vcl::WindowState::MaximizedHorz;
if( mbMaximizedVert )
pState->rState() |= vcl::WindowState::MaximizedVert;
pState->setPosSize(tools::Rectangle(aPosSize));
pState->setMask(vcl::WindowDataMask::PosSizeState);
if (! maRestorePosSize.IsEmpty() )
{
GetPosSize( aPosSize );
pState->rState() |= vcl::WindowState::Maximized;
pState->SetMaximizedX(aPosSize.Left());
pState->SetMaximizedY(aPosSize.Top());
pState->SetMaximizedWidth(aPosSize.GetWidth());
pState->SetMaximizedHeight(aPosSize.GetHeight());
pState->rMask() |= FRAMESTATE_MASK_MAXIMIZED_GEOMETRY;
}
return true;
}
void X11SalFrame::SetMenu( SalMenu* )
{
}
void X11SalFrame::GetPosSize( AbsoluteScreenPixelRectangle &rPosSize )
{
if( maGeometry.width() < 1 || maGeometry.height() < 1 )
{
const AbsoluteScreenPixelSize& aScreenSize = pDisplay_->getDataForScreen( m_nXScreen ).m_aSize;
tools::Long w = aScreenSize.Width() - maGeometry.leftDecoration() - maGeometry.rightDecoration();
tools::Long h = aScreenSize.Height() - maGeometry.topDecoration() - maGeometry.bottomDecoration();
rPosSize = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( maGeometry.x(), maGeometry.y() ), AbsoluteScreenPixelSize( w, h ) );
}
else
rPosSize = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( maGeometry.x(), maGeometry.y() ),
AbsoluteScreenPixelSize( maGeometry.width(), maGeometry.height() ) );
}
void X11SalFrame::SetSize( const Size &rSize )
{
if( rSize.IsEmpty() )
return;
if( ! ( nStyle_ & SalFrameStyleFlags::SIZEABLE )
&& ! IsChildWindow()
&& ( nStyle_ & (SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::OWNERDRAWDECORATION) ) != SalFrameStyleFlags::FLOAT )
{
XSizeHints* pHints = XAllocSizeHints();
tools::Long nSupplied = 0;
XGetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints,
&nSupplied
);
pHints->min_width = rSize.Width();
pHints->min_height = rSize.Height();
pHints->max_width = rSize.Width();
pHints->max_height = rSize.Height();
pHints->flags |= PMinSize | PMaxSize;
XSetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints );
XFree( pHints );
}
XResizeWindow( GetXDisplay(), IsSysChildWindow() ? GetWindow() : GetShellWindow(), rSize.Width(), rSize.Height() );
if( GetWindow() != GetShellWindow() )
{
if( nStyle_ & SalFrameStyleFlags::PLUG )
XMoveResizeWindow( GetXDisplay(), GetWindow(), 0, 0, rSize.Width(), rSize.Height() );
else
XResizeWindow( GetXDisplay(), GetWindow(), rSize.Width(), rSize.Height() );
}
cairo_xlib_surface_set_size(mpSurface, rSize.Width(), rSize.Height());
maGeometry.setSize(rSize);
// allow the external status window to reposition
if (mbInputFocus && mpInputContext != nullptr)
mpInputContext->SetICFocus ( this );
}
void X11SalFrame::SetPosSize( const AbsoluteScreenPixelRectangle &rPosSize )
{
XWindowChanges values;
values.x = rPosSize.Left();
values.y = rPosSize.Top();
values.width = rPosSize.GetWidth();
values.height = rPosSize.GetHeight();
if( !values.width || !values.height )
return;
if( mpParent && ! IsSysChildWindow() )
{
if( AllSettings::GetLayoutRTL() )
values.x = mpParent->maGeometry.width()-values.width-1-values.x;
::Window aChild;
// coordinates are relative to parent, so translate to root coordinates
XTranslateCoordinates( GetDisplay()->GetDisplay(),
mpParent->GetWindow(),
GetDisplay()->GetRootWindow( m_nXScreen ),
values.x, values.y,
&values.x, &values.y,
& aChild );
}
bool bMoved = false;
bool bSized = false;
if( values.x != maGeometry.x() || values.y != maGeometry.y() )
bMoved = true;
if( values.width != static_cast<int>(maGeometry.width()) || values.height != static_cast<int>(maGeometry.height()) )
bSized = true;
// do not set WMNormalHints for...
if(
// child windows
! IsChildWindow()
// popups (menu, help window, etc.)
&& (nStyle_ & (SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::OWNERDRAWDECORATION) ) != SalFrameStyleFlags::FLOAT
// shown, sizeable windows
&& ( nShowState_ == X11ShowState::Unknown ||
nShowState_ == X11ShowState::Hidden ||
! ( nStyle_ & SalFrameStyleFlags::SIZEABLE )
)
)
{
XSizeHints* pHints = XAllocSizeHints();
tools::Long nSupplied = 0;
XGetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints,
&nSupplied
);
if( ! ( nStyle_ & SalFrameStyleFlags::SIZEABLE ) )
{
pHints->min_width = rPosSize.GetWidth();
pHints->min_height = rPosSize.GetHeight();
pHints->max_width = rPosSize.GetWidth();
pHints->max_height = rPosSize.GetHeight();
pHints->flags |= PMinSize | PMaxSize;
}
if( nShowState_ == X11ShowState::Unknown || nShowState_ == X11ShowState::Hidden )
{
pHints->flags |= PPosition | PWinGravity;
pHints->x = values.x;
pHints->y = values.y;
pHints->win_gravity = pDisplay_->getWMAdaptor()->getPositionWinGravity();
}
if( mbFullScreen )
{
pHints->max_width = 10000;
pHints->max_height = 10000;
pHints->flags |= PMaxSize;
}
XSetWMNormalHints( GetXDisplay(),
GetShellWindow(),
pHints );
XFree( pHints );
}
XMoveResizeWindow( GetXDisplay(), IsSysChildWindow() ? GetWindow() : GetShellWindow(), values.x, values.y, values.width, values.height );
if( GetShellWindow() != GetWindow() )
{
if( nStyle_ & SalFrameStyleFlags::PLUG )
XMoveResizeWindow( GetXDisplay(), GetWindow(), 0, 0, values.width, values.height );
else
XMoveResizeWindow( GetXDisplay(), GetWindow(), values.x, values.y, values.width, values.height );
}
cairo_xlib_surface_set_size(mpSurface, values.width, values.height);
maGeometry.setPosSize({ values.x, values.y }, { values.width, values.height });
if( IsSysChildWindow() && mpParent )
// translate back to root coordinates
maGeometry.move(mpParent->maGeometry.x(), mpParent->maGeometry.y());
updateScreenNumber();
if( bSized && ! bMoved )
CallCallback( SalEvent::Resize, nullptr );
else if( bMoved && ! bSized )
CallCallback( SalEvent::Move, nullptr );
else
CallCallback( SalEvent::MoveResize, nullptr );
// allow the external status window to reposition
if (mbInputFocus && mpInputContext != nullptr)
mpInputContext->SetICFocus ( this );
}
void X11SalFrame::Minimize()
{
if( IsSysChildWindow() )
return;
if( X11ShowState::Unknown == nShowState_ || X11ShowState::Hidden == nShowState_ )
{
SAL_WARN( "vcl", "X11SalFrame::Minimize on withdrawn window" );
return;
}
if( XIconifyWindow( GetXDisplay(),
GetShellWindow(),
pDisplay_->GetDefaultXScreen().getXScreen() ) )
nShowState_ = X11ShowState::Minimized;
}
void X11SalFrame::Maximize()
{
if( IsSysChildWindow() )
return;
if( X11ShowState::Minimized == nShowState_ )
{
GetDisplay()->getWMAdaptor()->frameIsMapping( this );
XMapWindow( GetXDisplay(), GetShellWindow() );
nShowState_ = X11ShowState::Normal;
}
pDisplay_->getWMAdaptor()->maximizeFrame( this );
}
void X11SalFrame::Restore()
{
if( IsSysChildWindow() )
return;
if( X11ShowState::Unknown == nShowState_ || X11ShowState::Hidden == nShowState_ )
{
SAL_INFO( "vcl", "X11SalFrame::Restore on withdrawn window" );
return;
}
if( X11ShowState::Minimized == nShowState_ )
{
GetDisplay()->getWMAdaptor()->frameIsMapping( this );
XMapWindow( GetXDisplay(), GetShellWindow() );
nShowState_ = X11ShowState::Normal;
}
pDisplay_->getWMAdaptor()->maximizeFrame( this, false, false );
}
void X11SalFrame::SetScreenNumber( unsigned int nNewScreen )
{
if( nNewScreen == maGeometry.screen() )
return;
if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 )
{
if( nNewScreen >= GetDisplay()->GetXineramaScreens().size() )
return;
tools::Rectangle aOldScreenRect(GetDisplay()->GetXineramaScreens().at(maGeometry.screen()));
tools::Rectangle aNewScreenRect(GetDisplay()->GetXineramaScreens().at(nNewScreen));
bool bVisible = bMapped_;
if( bVisible )
Show( false );
maGeometry.setX(aNewScreenRect.Left() + (maGeometry.x() - aOldScreenRect.Left()));
maGeometry.setY(aNewScreenRect.Top() + (maGeometry.y() - aOldScreenRect.Top()));
createNewWindow( None, m_nXScreen );
if( bVisible )
Show( true );
maGeometry.setScreen(nNewScreen);
}
else if( nNewScreen < GetDisplay()->GetXScreenCount() )
{
bool bVisible = bMapped_;
if( bVisible )
Show( false );
createNewWindow( None, SalX11Screen( nNewScreen ) );
if( bVisible )
Show( true );
maGeometry.setScreen(nNewScreen);
}
}
void X11SalFrame::SetApplicationID( const OUString &rWMClass )
{
if( rWMClass != m_sWMClass && ! IsChildWindow() )
{
m_sWMClass = rWMClass;
updateWMClass();
for (auto const& child : maChildren)
child->SetApplicationID(rWMClass);
}
}
void X11SalFrame::updateWMClass()
{
XClassHint* pClass = XAllocClassHint();
OString aResName = SalGenericSystem::getFrameResName();
pClass->res_name = const_cast<char*>(aResName.getStr());
OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US);
const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() :
SalGenericSystem::getFrameClassName();
pClass->res_class = const_cast<char*>(pResClass);
XSetClassHint( GetXDisplay(), GetShellWindow(), pClass );
XFree( pClass );
}
void X11SalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen )
{
if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 )
{
if( mbFullScreen == bFullScreen )
return;
if( bFullScreen )
{
maRestorePosSize = AbsoluteScreenPixelRectangle(maGeometry.posSize());
AbsoluteScreenPixelRectangle aRect;
if( nScreen < 0 || o3tl::make_unsigned(nScreen) >= GetDisplay()->GetXineramaScreens().size() )
aRect = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint(0,0), GetDisplay()->GetScreenSize( m_nXScreen ) );
else
aRect = GetDisplay()->GetXineramaScreens().at(nScreen);
m_bIsPartialFullScreen = true;
bool bVisible = bMapped_;
if( bVisible )
Show( false );
maGeometry.setPosSize(tools::Rectangle(aRect));
mbMaximizedHorz = mbMaximizedVert = false;
mbFullScreen = true;
createNewWindow( None, m_nXScreen );
if( GetDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() )
GetDisplay()->getWMAdaptor()->enableAlwaysOnTop( this, true );
else
GetDisplay()->getWMAdaptor()->showFullScreen( this, true );
if( bVisible )
Show(true);
}
else
{
mbFullScreen = false;
m_bIsPartialFullScreen = false;
bool bVisible = bMapped_;
AbsoluteScreenPixelRectangle aRect = maRestorePosSize;
maRestorePosSize = AbsoluteScreenPixelRectangle();
if( bVisible )
Show( false );
createNewWindow( None, m_nXScreen );
if( !aRect.IsEmpty() )
SetPosSize( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(),
SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y |
SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT );
if( bVisible )
Show( true );
}
}
else
{
if( nScreen < 0 || o3tl::make_unsigned(nScreen) >= GetDisplay()->GetXScreenCount() )
nScreen = m_nXScreen.getXScreen();
if( nScreen != static_cast<int>(m_nXScreen.getXScreen()) )
{
bool bVisible = bMapped_;
if( mbFullScreen )
pDisplay_->getWMAdaptor()->showFullScreen( this, false );
if( bVisible )
Show( false );
createNewWindow( None, SalX11Screen( nScreen ) );
if( mbFullScreen )
pDisplay_->getWMAdaptor()->showFullScreen( this, true );
if( bVisible )
Show( true );
}
if( mbFullScreen == bFullScreen )
return;
pDisplay_->getWMAdaptor()->showFullScreen( this, bFullScreen );
}
}
void X11SalFrame::StartPresentation( bool bStart )
{
maSessionManagerInhibitor.inhibit( bStart,
u"presentation",
APPLICATION_INHIBIT_IDLE,
mhWindow,
GetXDisplay() );
if( ! bStart && hPresentationWindow != None )
doReparentPresentationDialogues( GetDisplay() );
hPresentationWindow = (bStart && IsOverrideRedirect() ) ? GetWindow() : None;
if( bStart && hPresentationWindow )
{
/* #i10559# workaround for WindowMaker: try to restore
* current focus after presentation window is gone
*/
int revert_to = 0;
XGetInputFocus( GetXDisplay(), &hPresFocusWindow, &revert_to );
}
}
// Pointer
void X11SalFrame::SetPointer( PointerStyle ePointerStyle )
{
hCursor_ = pDisplay_->GetPointer( ePointerStyle );
XDefineCursor( GetXDisplay(), GetWindow(), hCursor_ );
if( IsCaptured() || nVisibleFloats > 0 )
XChangeActivePointerGrab( GetXDisplay(),
PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
hCursor_,
CurrentTime );
}
void X11SalFrame::SetPointerPos(tools::Long nX, tools::Long nY)
{
/* when the application tries to center the mouse in the dialog the
* window isn't mapped already. So use coordinates relative to the root window.
*/
unsigned int nWindowLeft = maGeometry.x() + nX;
unsigned int nWindowTop = maGeometry.y() + nY;
XWarpPointer( GetXDisplay(), None, pDisplay_->GetRootWindow( pDisplay_->GetDefaultXScreen() ),
0, 0, 0, 0, nWindowLeft, nWindowTop);
}
// delay handling of extended text input
#if !defined(__synchronous_extinput__)
void
X11SalFrame::HandleExtTextEvent (XClientMessageEvent const *pEvent)
{
#if SAL_TYPES_SIZEOFLONG > 4
void* pExtTextEvent = reinterpret_cast<void*>( (pEvent->data.l[0] & 0xffffffff)
| (pEvent->data.l[1] << 32) );
#else
void* pExtTextEvent = reinterpret_cast<void*>(pEvent->data.l[0]);
#endif
SalEvent nExtTextEventType = SalEvent(pEvent->data.l[2]);
CallCallback(nExtTextEventType, pExtTextEvent);
switch (nExtTextEventType)
{
case SalEvent::EndExtTextInput:
break;
case SalEvent::ExtTextInput:
break;
default:
SAL_WARN("vcl.window",
"X11SalFrame::HandleExtTextEvent: invalid extended input.");
}
}
#endif /* defined(__synchronous_extinput__) */
// PostEvent
bool X11SalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
{
GetDisplay()->SendInternalEvent( this, pData.release() );
return true;
}
// Title
void X11SalFrame::SetTitle( const OUString& rTitle )
{
if( ! ( IsChildWindow() || (nStyle_ & SalFrameStyleFlags::FLOAT ) ) )
{
m_aTitle = rTitle;
GetDisplay()->getWMAdaptor()->setWMName( this, rTitle );
}
}
void X11SalFrame::Flush()
{
if( pGraphics_ )
pGraphics_->Flush();
XFlush( GetDisplay()->GetDisplay() );
}
// Keyboard
void X11SalFrame::SetInputContext( SalInputContext* pContext )
{
if (pContext == nullptr)
return;
// 1. We should create an input context for this frame
// only when InputContextFlags::Text is set.
if (!(pContext->mnOptions & InputContextFlags::Text))
{
if( mpInputContext )
mpInputContext->Unmap();
return;
}
// 2. We should use on-the-spot inputstyle
// only when InputContextFlags::ExtTExt is set.
if (mpInputContext == nullptr)
{
mpInputContext.reset( new SalI18N_InputContext( this ) );
if (mpInputContext->UseContext())
{
mpInputContext->ExtendEventMask( GetShellWindow() );
if (mbInputFocus)
mpInputContext->SetICFocus( this );
}
}
else
mpInputContext->Map( this );
}
void X11SalFrame::EndExtTextInput( EndExtTextInputFlags )
{
if (mpInputContext != nullptr)
mpInputContext->EndExtTextInput();
}
OUString X11SalFrame::GetKeyName( sal_uInt16 nKeyCode )
{
return GetDisplay()->GetKeyName( nKeyCode );
}
bool X11SalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& )
{
// not supported yet
return false;
}
LanguageType X11SalFrame::GetInputLanguage()
{
// could be improved by checking unicode ranges of the last input
return LANGUAGE_DONTKNOW;
}
// Settings
void X11SalFrame::UpdateSettings( AllSettings& rSettings )
{
StyleSettings aStyleSettings = rSettings.GetStyleSettings();
aStyleSettings.SetCursorBlinkTime( 500 );
aStyleSettings.SetMenuBarTextColor( aStyleSettings.GetPersonaMenuBarTextColor().value_or( COL_BLACK ) );
rSettings.SetStyleSettings( aStyleSettings );
}
void X11SalFrame::CaptureMouse( bool bCapture )
{
nCaptured_ = pDisplay_->CaptureMouse( bCapture ? this : nullptr );
}
void X11SalFrame::SetParent( SalFrame* pNewParent )
{
if( mpParent != pNewParent )
{
if( mpParent )
mpParent->maChildren.remove( this );
mpParent = static_cast<X11SalFrame*>(pNewParent);
mpParent->maChildren.push_back( this );
if( mpParent->m_nXScreen != m_nXScreen )
createNewWindow( None, mpParent->m_nXScreen );
GetDisplay()->getWMAdaptor()->changeReferenceFrame( this, mpParent );
}
}
SalFrame* X11SalFrame::GetParent() const
{
return mpParent;
}
void X11SalFrame::createNewWindow( ::Window aNewParent, SalX11Screen nXScreen )
{
bool bWasVisible = bMapped_;
if( bWasVisible )
Show( false );
if( nXScreen.getXScreen() >= GetDisplay()->GetXScreenCount() )
nXScreen = m_nXScreen;
SystemParentData aParentData;
aParentData.nSize = sizeof(SystemParentData);
aParentData.aWindow = aNewParent;
aParentData.bXEmbedSupport = (aNewParent != None && m_bXEmbed); // caution: this is guesswork
if( aNewParent == None )
{
aParentData.aWindow = None;
m_bXEmbed = false;
}
else
{
// is new parent a root window ?
Display* pDisp = GetDisplay()->GetDisplay();
int nScreens = GetDisplay()->GetXScreenCount();
for( int i = 0; i < nScreens; i++ )
{
if( aNewParent == RootWindow( pDisp, i ) )
{
nXScreen = SalX11Screen( i );
aParentData.aWindow = None;
m_bXEmbed = false;
break;
}
}
}
// first deinit frame
updateGraphics(true);
if( mpInputContext )
{
mpInputContext->UnsetICFocus();
mpInputContext->Unmap();
}
if( GetWindow() == hPresentationWindow )
{
hPresentationWindow = None;
doReparentPresentationDialogues( GetDisplay() );
}
if (mpSurface)
{
cairo_surface_destroy(mpSurface);
mpSurface = nullptr;
}
XDestroyWindow( GetXDisplay(), mhWindow );
mhWindow = None;
// now init with new parent again
if ( aParentData.aWindow != None )
Init( nStyle_ | SalFrameStyleFlags::PLUG, nXScreen, &aParentData );
else
Init( nStyle_ & ~SalFrameStyleFlags::PLUG, nXScreen, nullptr, true );
// update graphics if necessary
updateGraphics(false);
if( ! m_aTitle.isEmpty() )
SetTitle( m_aTitle );
if( mpParent )
{
if( mpParent->m_nXScreen != m_nXScreen )
SetParent( nullptr );
else
pDisplay_->getWMAdaptor()->changeReferenceFrame( this, mpParent );
}
if( bWasVisible )
Show( true );
std::list< X11SalFrame* > aChildren = maChildren;
for (auto const& child : aChildren)
child->createNewWindow( None, m_nXScreen );
// FIXME: SalObjects
}
void X11SalFrame::SetPluginParent( SystemParentData* pNewParent )
{
if( pNewParent->nSize >= sizeof(SystemParentData) )
m_bXEmbed = pNewParent->aWindow != None && pNewParent->bXEmbedSupport;
createNewWindow(pNewParent->aWindow);
}
// Sound
void X11SalFrame::Beep()
{
GetDisplay()->Beep();
}
// Event Handling
static sal_uInt16 sal_GetCode( int state )
{
sal_uInt16 nCode = 0;
if( state & Button1Mask )
nCode |= MOUSE_LEFT;
if( state & Button2Mask )
nCode |= MOUSE_MIDDLE;
if( state & Button3Mask )
nCode |= MOUSE_RIGHT;
if( state & ShiftMask )
nCode |= KEY_SHIFT;
if( state & ControlMask )
nCode |= KEY_MOD1;
if( state & Mod1Mask )
nCode |= KEY_MOD2;
// Map Meta/Super modifier to MOD3 on all Unix systems
// except macOS
if( state & Mod3Mask )
nCode |= KEY_MOD3;
return nCode;
}
SalFrame::SalPointerState X11SalFrame::GetPointerState()
{
SalPointerState aState;
::Window aRoot, aChild;
int rx, ry, wx, wy;
unsigned int nMask = 0;
XQueryPointer( GetXDisplay(),
GetShellWindow(),
&aRoot,
&aChild,
&rx, &ry,
&wx, &wy,
&nMask
);
aState.maPos = Point(wx, wy);
aState.mnState = sal_GetCode( nMask );
return aState;
}
KeyIndicatorState X11SalFrame::GetIndicatorState()
{
return vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetIndicatorState();
}
void X11SalFrame::SimulateKeyPress( sal_uInt16 nKeyCode )
{
vcl_sal::getSalDisplay(GetGenericUnixSalData())->SimulateKeyPress(nKeyCode);
}
namespace
{
struct CompressWheelEventsData
{
XEvent* firstEvent;
bool ignore;
int count; // number of compressed events
};
Bool compressWheelEvents( Display*, XEvent* event, XPointer p )
{
CompressWheelEventsData* data = reinterpret_cast< CompressWheelEventsData* >( p );
if( data->ignore )
return False; // we're already after the events to compress
if( event->type == ButtonPress || event->type == ButtonRelease )
{
const unsigned int mask = Button1Mask << ( event->xbutton.button - Button1 );
if( event->xbutton.button == data->firstEvent->xbutton.button
&& event->xbutton.window == data->firstEvent->xbutton.window
&& event->xbutton.x == data->firstEvent->xbutton.x
&& event->xbutton.y == data->firstEvent->xbutton.y
&& ( event->xbutton.state | mask ) == ( data->firstEvent->xbutton.state | mask ))
{
// Count if it's another press (i.e. wheel start event).
if( event->type == ButtonPress )
++data->count;
return True; // And remove the event from the queue.
}
}
// Non-matching event, skip certain events that cannot possibly affect input processing,
// but otherwise ignore all further events.
switch( event->type )
{
case Expose:
case NoExpose:
break;
default:
data->ignore = true;
break;
}
return False;
}
} // namespace
bool X11SalFrame::HandleMouseEvent( XEvent *pEvent )
{
SalMouseEvent aMouseEvt;
SalEvent nEvent = SalEvent::NONE;
bool bClosePopups = false;
if( nVisibleFloats && pEvent->type == EnterNotify )
return false;
if( LeaveNotify == pEvent->type || EnterNotify == pEvent->type )
{
/*
* some WMs (and/or) applications have a passive grab on
* mouse buttons (XGrabButton). This leads to enter/leave notifies
* with mouse buttons pressed in the state mask before the actual
* ButtonPress event gets dispatched. But EnterNotify
* is reported in vcl as MouseMove event. Some office code
* decides that a pressed button in a MouseMove belongs to
* a drag operation which leads to doing things differently.
*
* ignore Enter/LeaveNotify resulting from grabs so that
* help windows do not disappear just after appearing
*
* hopefully this workaround will not break anything.
*/
if( pEvent->xcrossing.mode == NotifyGrab || pEvent->xcrossing.mode == NotifyUngrab )
return false;
aMouseEvt.mnX = pEvent->xcrossing.x;
aMouseEvt.mnY = pEvent->xcrossing.y;
aMouseEvt.mnTime = pEvent->xcrossing.time;
aMouseEvt.mnCode = sal_GetCode( pEvent->xcrossing.state );
aMouseEvt.mnButton = 0;
nEvent = LeaveNotify == pEvent->type
? SalEvent::MouseLeave
: SalEvent::MouseMove;
}
else if( pEvent->type == MotionNotify )
{
aMouseEvt.mnX = pEvent->xmotion.x;
aMouseEvt.mnY = pEvent->xmotion.y;
aMouseEvt.mnTime = pEvent->xmotion.time;
aMouseEvt.mnCode = sal_GetCode( pEvent->xmotion.state );
aMouseEvt.mnButton = 0;
nEvent = SalEvent::MouseMove;
if( nVisibleFloats > 0 && mpParent )
{
Cursor aCursor = mpParent->GetCursor();
if( pEvent->xmotion.x >= 0 && pEvent->xmotion.x < static_cast<int>(maGeometry.width()) &&
pEvent->xmotion.y >= 0 && pEvent->xmotion.y < static_cast<int>(maGeometry.height()) )
aCursor = None;
XChangeActivePointerGrab( GetXDisplay(),
PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
aCursor,
CurrentTime );
}
}
else
{
// let mouse events reach the correct window
if( nVisibleFloats < 1 )
{
if( ! (nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION) )
XUngrabPointer( GetXDisplay(), CurrentTime );
}
else if( pEvent->type == ButtonPress )
{
// see if the user clicks outside all of the floats
// if yes release the grab
bool bInside = false;
for (auto pSalFrame : GetDisplay()->getFrames() )
{
const X11SalFrame* pFrame = static_cast< const X11SalFrame* >( pSalFrame );
if( pFrame->IsFloatGrabWindow() &&
pFrame->bMapped_ &&
pEvent->xbutton.x_root >= pFrame->maGeometry.x() &&
pEvent->xbutton.x_root < pFrame->maGeometry.x() + static_cast<int>(pFrame->maGeometry.width()) &&
pEvent->xbutton.y_root >= pFrame->maGeometry.y() &&
pEvent->xbutton.y_root < pFrame->maGeometry.y() + static_cast<int>(pFrame->maGeometry.height()) )
{
bInside = true;
break;
}
}
if( ! bInside )
{
// need not take care of the XUngrabPointer in Show( false )
// because XUngrabPointer does not produce errors if pointer
// is not grabbed
XUngrabPointer( GetXDisplay(), CurrentTime );
bClosePopups = true;
/* #i15246# only close popups if pointer is outside all our frames
* cannot use our own geometry data here because stacking
* is unknown (the above case implicitly assumes
* that floats are on top which should be true)
*/
::Window aRoot, aChild;
int root_x, root_y, win_x, win_y;
unsigned int mask_return;
if( XQueryPointer( GetXDisplay(),
GetDisplay()->GetRootWindow( m_nXScreen ),
&aRoot, &aChild,
&root_x, &root_y,
&win_x, &win_y,
&mask_return )
&& aChild // pointer may not be in any child
)
{
for (auto pSalFrame : GetDisplay()->getFrames() )
{
const X11SalFrame* pFrame = static_cast< const X11SalFrame* >( pSalFrame );
if( ! pFrame->IsFloatGrabWindow()
&& ( pFrame->GetWindow() == aChild ||
pFrame->GetShellWindow() == aChild ||
pFrame->GetStackingWindow() == aChild )
)
{
// #i63638# check that pointer is inside window, not
// only inside stacking window
if( root_x >= pFrame->maGeometry.x() && root_x < sal::static_int_cast< int >(pFrame->maGeometry.x()+pFrame->maGeometry.width()) &&
root_y >= pFrame->maGeometry.y() && root_y < sal::static_int_cast< int >(pFrame->maGeometry.x()+pFrame->maGeometry.height()) )
{
bClosePopups = false;
}
break;
}
}
}
}
}
if( m_bXEmbed && pEvent->xbutton.button == Button1 )
askForXEmbedFocus( pEvent->xbutton.time );
if( pEvent->xbutton.button == Button1 ||
pEvent->xbutton.button == Button2 ||
pEvent->xbutton.button == Button3 )
{
aMouseEvt.mnX = pEvent->xbutton.x;
aMouseEvt.mnY = pEvent->xbutton.y;
aMouseEvt.mnTime = pEvent->xbutton.time;
aMouseEvt.mnCode = sal_GetCode( pEvent->xbutton.state );
if( Button1 == pEvent->xbutton.button )
aMouseEvt.mnButton = MOUSE_LEFT;
else if( Button2 == pEvent->xbutton.button )
aMouseEvt.mnButton = MOUSE_MIDDLE;
else if( Button3 == pEvent->xbutton.button )
aMouseEvt.mnButton = MOUSE_RIGHT;
nEvent = ButtonPress == pEvent->type
? SalEvent::MouseButtonDown
: SalEvent::MouseButtonUp;
}
else if( pEvent->xbutton.button == Button4 ||
pEvent->xbutton.button == Button5 ||
pEvent->xbutton.button == Button6 ||
pEvent->xbutton.button == Button7 )
{
const bool bIncrement(
pEvent->xbutton.button == Button4 ||
pEvent->xbutton.button == Button6 );
const bool bHoriz(
pEvent->xbutton.button == Button6 ||
pEvent->xbutton.button == Button7 );
if( pEvent->type == ButtonRelease )
return false;
static sal_uLong nLines = 0;
if( ! nLines )
{
char* pEnv = getenv( "SAL_WHEELLINES" );
nLines = pEnv ? atoi( pEnv ) : 3;
if( nLines > 10 )
nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
}
// Compress consecutive wheel events (way too fine scrolling may cause lags if one scrolling steps takes long).
CompressWheelEventsData data;
data.firstEvent = pEvent;
data.count = 1;
XEvent dummy;
do
{
data.ignore = false;
} while( XCheckIfEvent( pEvent->xany.display, &dummy, compressWheelEvents, reinterpret_cast< XPointer >( &data )));
SalWheelMouseEvent aWheelEvt;
aWheelEvt.mnTime = pEvent->xbutton.time;
aWheelEvt.mnX = pEvent->xbutton.x;
aWheelEvt.mnY = pEvent->xbutton.y;
aWheelEvt.mnDelta = ( bIncrement ? 120 : -120 ) * data.count;
aWheelEvt.mnNotchDelta = bIncrement ? 1 : -1;
aWheelEvt.mnScrollLines = nLines * data.count;
aWheelEvt.mnCode = sal_GetCode( pEvent->xbutton.state );
aWheelEvt.mbHorz = bHoriz;
nEvent = SalEvent::WheelMouse;
if( AllSettings::GetLayoutRTL() )
aWheelEvt.mnX = nWidth_-1-aWheelEvt.mnX;
return CallCallback( nEvent, &aWheelEvt );
}
}
bool nRet = false;
if( nEvent == SalEvent::MouseLeave
|| ( aMouseEvt.mnX < nWidth_ && aMouseEvt.mnX > -1 &&
aMouseEvt.mnY < nHeight_ && aMouseEvt.mnY > -1 )
|| pDisplay_->MouseCaptured( this )
)
{
if( AllSettings::GetLayoutRTL() )
aMouseEvt.mnX = nWidth_-1-aMouseEvt.mnX;
nRet = CallCallback( nEvent, &aMouseEvt );
}
if( bClosePopups )
{
/* #108213# close popups after dispatching the event outside the popup;
* applications do weird things.
*/
ImplSVData* pSVData = ImplGetSVData();
if (pSVData->mpWinData->mpFirstFloat)
{
if (!(pSVData->mpWinData->mpFirstFloat->GetPopupModeFlags()
& FloatWinPopupFlags::NoAppFocusClose))
pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll);
}
}
return nRet;
}
namespace {
// F10 means either KEY_F10 or KEY_MENU, which has to be decided
// in the independent part.
struct KeyAlternate
{
sal_uInt16 nKeyCode;
sal_Unicode nCharCode;
KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {}
KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {}
};
}
static KeyAlternate
GetAlternateKeyCode( const sal_uInt16 nKeyCode )
{
KeyAlternate aAlternate;
switch( nKeyCode )
{
case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break;
case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break;
}
return aAlternate;
}
void X11SalFrame::beginUnicodeSequence()
{
OUString& rSeq( GetGenericUnixSalData()->GetUnicodeCommand() );
vcl::DeletionListener aDeleteWatch( this );
if( !rSeq.isEmpty() )
endUnicodeSequence();
rSeq = "u";
if( ! aDeleteWatch.isDeleted() )
{
ExtTextInputAttr nTextAttr = ExtTextInputAttr::Underline;
SalExtTextInputEvent aEv;
aEv.maText = rSeq;
aEv.mpTextAttr = &nTextAttr;
aEv.mnCursorPos = 0;
aEv.mnCursorFlags = 0;
CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&aEv));
}
}
bool X11SalFrame::appendUnicodeSequence( sal_Unicode c )
{
bool bRet = false;
OUString& rSeq( GetGenericUnixSalData()->GetUnicodeCommand() );
if( !rSeq.isEmpty() )
{
// range check
if( (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F') )
{
rSeq += OUStringChar(c);
std::vector<ExtTextInputAttr> attribs( rSeq.getLength(), ExtTextInputAttr::Underline );
SalExtTextInputEvent aEv;
aEv.maText = rSeq;
aEv.mpTextAttr = attribs.data();
aEv.mnCursorPos = 0;
aEv.mnCursorFlags = 0;
CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&aEv));
bRet = true;
}
else
bRet = endUnicodeSequence();
}
else
endUnicodeSequence();
return bRet;
}
bool X11SalFrame::endUnicodeSequence()
{
OUString& rSeq( GetGenericUnixSalData()->GetUnicodeCommand() );
vcl::DeletionListener aDeleteWatch( this );
if( rSeq.getLength() > 1 && rSeq.getLength() < 6 )
{
// cut the "u"
std::u16string_view aNumbers( rSeq.subView( 1 ) );
sal_uInt32 nValue = o3tl::toUInt32(aNumbers, 16);
if( nValue >= 32 )
{
ExtTextInputAttr nTextAttr = ExtTextInputAttr::Underline;
SalExtTextInputEvent aEv;
aEv.maText = OUString( sal_Unicode(nValue) );
aEv.mpTextAttr = &nTextAttr;
aEv.mnCursorPos = 0;
aEv.mnCursorFlags = 0;
CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&aEv));
}
}
bool bWasInput = !rSeq.isEmpty();
rSeq.clear();
if( bWasInput && ! aDeleteWatch.isDeleted() )
CallCallback(SalEvent::EndExtTextInput, nullptr);
return bWasInput;
}
bool X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent )
{
if( pEvent->type == KeyRelease )
{
// Ignore autorepeat keyrelease events. If there is a series of keypress+keyrelease+keypress events
// generated by holding down a key, and if these are from autorepeat (keyrelease and the following keypress
// have the same timestamp), drop the autorepeat keyrelease event. Not exactly sure why this is done
// (possibly hiding differences between platforms, or just making it more sensible, because technically
// the key has not been released at all).
bool ignore = false;
// Discard queued excessive autorepeat events.
// If the user presses and holds down a key, the autorepeating keypress events
// may overload LO (e.g. if the key is PageDown and the LO cannot keep up scrolling).
// Reduce the load by simply discarding such excessive events (so for a KeyRelease event,
// check if it's followed by matching KeyPress+KeyRelease pair(s) and discard those).
// This shouldn't have any negative effects - unlike with normal (non-autorepeat
// events), the user is unlikely to rely on the exact number of resulting actions
// (since autorepeat generates keypress events rather quickly and it's hard to estimate
// how many exactly) and the idea should be just keeping the key pressed until something
// happens (in which case more events that just lag LO shouldn't make a difference).
Display* dpy = pEvent->display;
XKeyEvent previousRelease = *pEvent;
while( XPending( dpy ))
{
XEvent nextEvent1;
bool discard1 = false;
XNextEvent( dpy, &nextEvent1 );
if( nextEvent1.type == KeyPress && nextEvent1.xkey.time == previousRelease.time
&& !nextEvent1.xkey.send_event && nextEvent1.xkey.window == previousRelease.window
&& nextEvent1.xkey.state == previousRelease.state && nextEvent1.xkey.keycode == previousRelease.keycode )
{ // This looks like another autorepeat keypress.
ignore = true;
if( XPending( dpy ))
{
XEvent nextEvent2;
XNextEvent( dpy, &nextEvent2 );
if( nextEvent2.type == KeyRelease && nextEvent2.xkey.time <= ( previousRelease.time + 100 )
&& !nextEvent2.xkey.send_event && nextEvent2.xkey.window == previousRelease.window
&& nextEvent2.xkey.state == previousRelease.state && nextEvent2.xkey.keycode == previousRelease.keycode )
{ // And the matching keyrelease -> drop them both.
discard1 = true;
previousRelease = nextEvent2.xkey;
ignore = false; // There either will be another autorepeating keypress that'll lead to discarding
// the pEvent keyrelease, it this discarding makes that keyrelease the last one.
}
else
{
XPutBackEvent( dpy, &nextEvent2 );
break;
}
}
}
if( !discard1 )
{ // Unrelated event, put back and stop compressing.
XPutBackEvent( dpy, &nextEvent1 );
break;
}
}
if( ignore ) // This autorepeating keyrelease is followed by another keypress.
return false;
}
KeySym nKeySym;
KeySym nUnmodifiedKeySym;
int nLen = 2048;
char *pPrintable = static_cast<char*>(alloca( nLen ));
// singlebyte code composed by input method, the new default
if (mpInputContext != nullptr && mpInputContext->UseContext())
{
// returns a keysym as well as the pPrintable (in system encoding)
// printable may be empty.
Status nStatus;
nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen,
&nUnmodifiedKeySym,
&nStatus, mpInputContext->GetContext() );
if ( nStatus == XBufferOverflow )
{
// In case of overflow, XmbLookupString (called by GetKeySym)
// returns required size
// TODO : check if +1 is needed for 0 terminator
nLen += 1;
pPrintable = static_cast<char*>(alloca( nLen ));
nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen,
&nUnmodifiedKeySym,
&nStatus, mpInputContext->GetContext() );
}
}
else
{
// fallback, this should never ever be called
Status nStatus = 0;
nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen, &nUnmodifiedKeySym, &nStatus );
}
SalKeyEvent aKeyEvt;
sal_uInt16 nKeyCode;
sal_uInt16 nModCode = 0;
char aDummy;
if( pEvent->state & ShiftMask )
nModCode |= KEY_SHIFT;
if( pEvent->state & ControlMask )
nModCode |= KEY_MOD1;
if( pEvent->state & Mod1Mask )
nModCode |= KEY_MOD2;
if( nModCode != (KEY_SHIFT|KEY_MOD1) )
endUnicodeSequence();
if( nKeySym == XK_Shift_L || nKeySym == XK_Shift_R
|| nKeySym == XK_Control_L || nKeySym == XK_Control_R
|| nKeySym == XK_Alt_L || nKeySym == XK_Alt_R
|| nKeySym == XK_Meta_L || nKeySym == XK_Meta_R
|| nKeySym == XK_Super_L || nKeySym == XK_Super_R )
{
SalKeyModEvent aModEvt;
aModEvt.mbDown = false; // auto-accelerator feature not supported here.
aModEvt.mnModKeyCode = ModKeyFlags::NONE;
if( pEvent->type == KeyPress && mnExtKeyMod == ModKeyFlags::NONE )
mbSendExtKeyModChange = true;
else if( pEvent->type == KeyRelease && mbSendExtKeyModChange )
{
aModEvt.mnModKeyCode = mnExtKeyMod;
mnExtKeyMod = ModKeyFlags::NONE;
}
// pressing just the ctrl key leads to a keysym of XK_Control but
// the event state does not contain ControlMask. In the release
// event it's the other way round: it does contain the Control mask.
// The modifier mode therefore has to be adapted manually.
ModKeyFlags nExtModMask = ModKeyFlags::NONE;
sal_uInt16 nModMask = 0;
switch( nKeySym )
{
case XK_Control_L:
nExtModMask = ModKeyFlags::LeftMod1;
nModMask = KEY_MOD1;
break;
case XK_Control_R:
nExtModMask = ModKeyFlags::RightMod1;
nModMask = KEY_MOD1;
break;
case XK_Alt_L:
nExtModMask = ModKeyFlags::LeftMod2;
nModMask = KEY_MOD2;
break;
case XK_Alt_R:
nExtModMask = ModKeyFlags::RightMod2;
nModMask = KEY_MOD2;
break;
case XK_Shift_L:
nExtModMask = ModKeyFlags::LeftShift;
nModMask = KEY_SHIFT;
break;
case XK_Shift_R:
nExtModMask = ModKeyFlags::RightShift;
nModMask = KEY_SHIFT;
break;
// Map Meta/Super keys to MOD3 modifier on all Unix systems
// except macOS
case XK_Meta_L:
case XK_Super_L:
nExtModMask = ModKeyFlags::LeftMod3;
nModMask = KEY_MOD3;
break;
case XK_Meta_R:
case XK_Super_R:
nExtModMask = ModKeyFlags::RightMod3;
nModMask = KEY_MOD3;
break;
}
if( pEvent->type == KeyRelease )
{
nModCode &= ~nModMask;
mnExtKeyMod &= ~nExtModMask;
}
else
{
nModCode |= nModMask;
mnExtKeyMod |= nExtModMask;
}
aModEvt.mnCode = nModCode;
return CallCallback( SalEvent::KeyModChange, &aModEvt );
}
mbSendExtKeyModChange = false;
// try to figure out the vcl code for the keysym
// #i52338# use the unmodified KeySym if there is none for the real KeySym
// because the independent part has only keycodes for unshifted keys
nKeyCode = pDisplay_->GetKeyCode( nKeySym, &aDummy );
if( nKeyCode == 0 )
nKeyCode = pDisplay_->GetKeyCode( nUnmodifiedKeySym, &aDummy );
// try to figure out a printable if XmbLookupString returns only a keysym
// and NOT a printable. Do not store it in pPrintable[0] since it is expected to
// be in system encoding, not unicode.
// #i8988##, if KeySym and printable look equally promising then prefer KeySym
// the printable is bound to the encoding so the KeySym might contain more
// information (in et_EE locale: "Compose + Z + <" delivers "," in printable and
// (the desired) Zcaron in KeySym
sal_Unicode nKeyString = 0x0;
if ( (nLen == 0)
|| ((nLen == 1) && (nKeySym > 0)) )
nKeyString = KeysymToUnicode (nKeySym);
// if we have nothing we give up
if( !nKeyCode && !nLen && !nKeyString)
return false;
vcl::DeletionListener aDeleteWatch( this );
if( nModCode == (KEY_SHIFT | KEY_MOD1) && pEvent->type == KeyPress )
{
sal_uInt16 nSeqKeyCode = pDisplay_->GetKeyCode( nUnmodifiedKeySym, &aDummy );
if( nSeqKeyCode == KEY_U )
{
beginUnicodeSequence();
return true;
}
else if( nSeqKeyCode >= KEY_0 && nSeqKeyCode <= KEY_9 )
{
if( appendUnicodeSequence( u'0' + sal_Unicode(nSeqKeyCode - KEY_0) ) )
return true;
}
else if( nSeqKeyCode >= KEY_A && nSeqKeyCode <= KEY_F )
{
if( appendUnicodeSequence( u'a' + sal_Unicode(nSeqKeyCode - KEY_A) ) )
return true;
}
else
endUnicodeSequence();
}
if( aDeleteWatch.isDeleted() )
return false;
rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
sal_Unicode *pBuffer;
sal_Unicode *pString;
sal_Size nBufferSize = nLen * 2;
sal_Size nSize;
pBuffer = static_cast<sal_Unicode*>(malloc( nBufferSize + 2 ));
pBuffer[ 0 ] = 0;
if (nKeyString != 0)
{
pString = &nKeyString;
nSize = 1;
}
else if (nLen > 0 && nEncoding != RTL_TEXTENCODING_UNICODE)
{
// create text converter
rtl_TextToUnicodeConverter aConverter =
rtl_createTextToUnicodeConverter( nEncoding );
rtl_TextToUnicodeContext aContext =
rtl_createTextToUnicodeContext( aConverter );
sal_uInt32 nConversionInfo;
sal_Size nConvertedChars;
// convert to single byte text stream
nSize = rtl_convertTextToUnicode(
aConverter, aContext,
pPrintable, nLen,
pBuffer, nBufferSize,
RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE,
&nConversionInfo, &nConvertedChars );
// destroy converter
rtl_destroyTextToUnicodeContext( aConverter, aContext );
rtl_destroyTextToUnicodeConverter( aConverter );
pString = pBuffer;
}
else if (nLen > 0 /* nEncoding == RTL_TEXTENCODING_UNICODE */)
{
pString = reinterpret_cast<sal_Unicode*>(pPrintable);
nSize = nLen;
}
else
{
pString = pBuffer;
nSize = 0;
}
if ( mpInputContext != nullptr
&& mpInputContext->UseContext()
&& KeyRelease != pEvent->type
&& ( (nSize > 1)
|| (nSize > 0 && mpInputContext->IsPreeditMode())) )
{
mpInputContext->CommitKeyEvent(pString, nSize);
}
else
// normal single character keyinput
{
aKeyEvt.mnCode = nKeyCode | nModCode;
aKeyEvt.mnRepeat = 0;
aKeyEvt.mnCharCode = pString[ 0 ];
if( KeyRelease == pEvent->type )
{
CallCallback( SalEvent::KeyUp, &aKeyEvt );
}
else
{
if ( ! CallCallback(SalEvent::KeyInput, &aKeyEvt) )
{
// independent layer doesn't want to handle key-event, so check
// whether the keycode may have an alternate meaning
KeyAlternate aAlternate = GetAlternateKeyCode( nKeyCode );
if ( aAlternate.nKeyCode != 0 )
{
aKeyEvt.mnCode = aAlternate.nKeyCode | nModCode;
if( aAlternate.nCharCode )
aKeyEvt.mnCharCode = aAlternate.nCharCode;
CallCallback(SalEvent::KeyInput, &aKeyEvt);
}
}
}
}
// update the spot location for PreeditPosition IME style
if (! aDeleteWatch.isDeleted())
{
if (mpInputContext != nullptr && mpInputContext->UseContext())
mpInputContext->UpdateSpotLocation();
}
free (pBuffer);
return true;
}
bool X11SalFrame::HandleFocusEvent( XFocusChangeEvent const *pEvent )
{
// ReflectionX in Windows mode changes focus while mouse is grabbed
if( nVisibleFloats > 0 && GetDisplay()->getWMAdaptor()->getWindowManagerName() == "ReflectionX Windows" )
return true;
/* ignore focusout resulting from keyboard grabs
* we do not grab it and are not interested when
* someone else does CDE e.g. does a XGrabKey on arrow keys
* handle focus events with mode NotifyWhileGrabbed
* because with CDE alt-tab focus changing we do not get
* normal focus events
* cast focus event to the input context, otherwise the
* status window does not follow the application frame
*/
if ( mpInputContext != nullptr )
{
if( FocusIn == pEvent->type )
mpInputContext->SetICFocus( this );
}
if ( pEvent->mode == NotifyNormal || pEvent->mode == NotifyWhileGrabbed ||
( ( nStyle_ & SalFrameStyleFlags::PLUG ) && pEvent->window == GetShellWindow() )
)
{
if( hPresentationWindow != None && hPresentationWindow != GetShellWindow() )
return false;
if( FocusIn == pEvent->type )
{
GetSalInstance()->updatePrinterUpdate();
mbInputFocus = True;
ImplSVData* pSVData = ImplGetSVData();
bool nRet = CallCallback( SalEvent::GetFocus, nullptr );
if ((mpParent != nullptr && nStyle_ == SalFrameStyleFlags::NONE)
&& pSVData->mpWinData->mpFirstFloat)
{
FloatWinPopupFlags nMode = pSVData->mpWinData->mpFirstFloat->GetPopupModeFlags();
pSVData->mpWinData->mpFirstFloat->SetPopupModeFlags(
nMode & ~FloatWinPopupFlags::NoAppFocusClose);
}
return nRet;
}
else
{
mbInputFocus = False;
mbSendExtKeyModChange = false;
mnExtKeyMod = ModKeyFlags::NONE;
return CallCallback( SalEvent::LoseFocus, nullptr );
}
}
return false;
}
bool X11SalFrame::HandleExposeEvent( XEvent const *pEvent )
{
XRectangle aRect = { 0, 0, 0, 0 };
sal_uInt16 nCount = 0;
if( pEvent->type == Expose )
{
aRect.x = pEvent->xexpose.x;
aRect.y = pEvent->xexpose.y;
aRect.width = pEvent->xexpose.width;
aRect.height = pEvent->xexpose.height;
nCount = pEvent->xexpose.count;
}
else if( pEvent->type == GraphicsExpose )
{
aRect.x = pEvent->xgraphicsexpose.x;
aRect.y = pEvent->xgraphicsexpose.y;
aRect.width = pEvent->xgraphicsexpose.width;
aRect.height = pEvent->xgraphicsexpose.height;
nCount = pEvent->xgraphicsexpose.count;
}
if( IsOverrideRedirect() && mbFullScreen &&
aPresentationReparentList.empty() )
// we are in fullscreen mode -> override redirect
// focus is possibly lost, so reget it
XSetInputFocus( GetXDisplay(), GetShellWindow(), RevertToNone, CurrentTime );
// width and height are extents, so they are of by one for rectangle
maPaintRegion.Union( tools::Rectangle( Point(aRect.x, aRect.y), Size(aRect.width+1, aRect.height+1) ) );
if( nCount )
// wait for last expose rectangle, do not wait for resize timer
// if a completed graphics expose sequence is available
return true;
SalPaintEvent aPEvt( maPaintRegion.Left(), maPaintRegion.Top(), maPaintRegion.GetWidth(), maPaintRegion.GetHeight() );
CallCallback( SalEvent::Paint, &aPEvt );
maPaintRegion = tools::Rectangle();
return true;
}
void X11SalFrame::RestackChildren( ::Window* pTopLevelWindows, int nTopLevelWindows )
{
if( maChildren.empty() )
return;
int nWindow = nTopLevelWindows;
while( nWindow-- )
if( pTopLevelWindows[nWindow] == GetStackingWindow() )
break;
if( nWindow < 0 )
return;
for (auto const& child : maChildren)
{
if( child->bMapped_ )
{
int nChild = nWindow;
while( nChild-- )
{
if( pTopLevelWindows[nChild] == child->GetStackingWindow() )
{
// if a child is behind its parent, place it above the
// parent (for insane WMs like Dtwm and olwm)
XWindowChanges aCfg;
aCfg.sibling = GetStackingWindow();
aCfg.stack_mode = Above;
XConfigureWindow( GetXDisplay(), child->GetStackingWindow(), CWSibling|CWStackMode, &aCfg );
break;
}
}
}
}
for (auto const& child : maChildren)
{
child->RestackChildren( pTopLevelWindows, nTopLevelWindows );
}
}
void X11SalFrame::RestackChildren()
{
if( maChildren.empty() )
return;
::Window aRoot, aParent, *pChildren = nullptr;
unsigned int nChildren;
if( XQueryTree( GetXDisplay(),
GetDisplay()->GetRootWindow( m_nXScreen ),
&aRoot,
&aParent,
&pChildren,
&nChildren ) )
{
RestackChildren( pChildren, nChildren );
XFree( pChildren );
}
}
static Bool size_event_predicate( Display*, XEvent* event, XPointer arg )
{
if( event->type != ConfigureNotify )
return False;
X11SalFrame* frame = reinterpret_cast< X11SalFrame* >( arg );
XConfigureEvent* pEvent = &event->xconfigure;
if( pEvent->window != frame->GetShellWindow()
&& pEvent->window != frame->GetWindow()
&& pEvent->window != frame->GetForeignParent()
&& pEvent->window != frame->GetStackingWindow())
{ // ignored at top of HandleSizeEvent()
return False;
}
if( pEvent->window == frame->GetStackingWindow())
return False; // filtered later in HandleSizeEvent()
// at this point we know that there is another similar event in the queue
frame->setPendingSizeEvent();
return False; // but do not process the new event out of order
}
void X11SalFrame::setPendingSizeEvent()
{
mPendingSizeEvent = true;
}
bool X11SalFrame::HandleSizeEvent( XConfigureEvent *pEvent )
{
// NOTE: if you add more tests in this function, make sure to update size_event_predicate()
// so that it finds exactly the same events
if ( pEvent->window != GetShellWindow()
&& pEvent->window != GetWindow()
&& pEvent->window != GetForeignParent()
&& pEvent->window != GetStackingWindow()
)
{
// could be as well a sys-child window (aka SalObject)
return true;
}
if( ( nStyle_ & SalFrameStyleFlags::PLUG ) && pEvent->window == GetShellWindow() )
{
// just update the children's positions
RestackChildren();
return true;
}
if( pEvent->window == GetForeignParent() )
{
XResizeWindow( GetXDisplay(),
GetWindow(),
pEvent->width,
pEvent->height );
cairo_xlib_surface_set_size(mpSurface, pEvent->width, pEvent->height);
}
::Window hDummy;
XTranslateCoordinates( GetXDisplay(),
GetWindow(),
pDisplay_->GetRootWindow( pDisplay_->GetDefaultXScreen() ),
0, 0,
&pEvent->x, &pEvent->y,
&hDummy );
if( pEvent->window == GetStackingWindow() )
{
if( maGeometry.x() != pEvent->x || maGeometry.y() != pEvent->y )
{
maGeometry.setPos({ pEvent->x, pEvent->y });
CallCallback( SalEvent::Move, nullptr );
}
return true;
}
// check size hints in first time SalFrame::Show
if( X11ShowState::Unknown == nShowState_ && bMapped_ )
nShowState_ = X11ShowState::Normal;
// Avoid a race condition where resizing this window to one size and shortly after that
// to another size generates first size event with the old size and only after that
// with the new size, temporarily making us think the old size is valid (bnc#674806).
// So if there is another size event for this window pending, ignore this one.
mPendingSizeEvent = false;
XEvent dummy;
XCheckIfEvent( GetXDisplay(), &dummy, size_event_predicate, reinterpret_cast< XPointer >( this ));
if( mPendingSizeEvent )
return true;
nWidth_ = pEvent->width;
nHeight_ = pEvent->height;
bool bMoved = ( pEvent->x != maGeometry.x() || pEvent->y != maGeometry.y() );
bool bSized = ( pEvent->width != static_cast<int>(maGeometry.width()) || pEvent->height != static_cast<int>(maGeometry.height()) );
cairo_xlib_surface_set_size(mpSurface, pEvent->width, pEvent->height);
maGeometry.setPosSize({ pEvent->x, pEvent->y }, { pEvent->width, pEvent->height });
updateScreenNumber();
// update children's position
RestackChildren();
if( bSized && ! bMoved )
CallCallback( SalEvent::Resize, nullptr );
else if( bMoved && ! bSized )
CallCallback( SalEvent::Move, nullptr );
else if( bMoved && bSized )
CallCallback( SalEvent::MoveResize, nullptr );
return true;
}
IMPL_LINK_NOARG(X11SalFrame, HandleAlwaysOnTopRaise, Timer *, void)
{
if( bMapped_ )
ToTop( SalFrameToTop::NONE );
}
bool X11SalFrame::HandleReparentEvent( XReparentEvent *pEvent )
{
Display *pDisplay = pEvent->display;
::Window hWM_Parent;
::Window hRoot, *Children, hDummy;
unsigned int nChildren;
static const char* pDisableStackingCheck = getenv( "SAL_DISABLE_STACKING_CHECK" );
GetGenericUnixSalData()->ErrorTrapPush();
/*
* don't rely on the new parent from the event.
* the event may be "out of date", that is the window manager
* window may not exist anymore. This can happen if someone
* shows a frame and hides it again quickly (not that it would
* be very sensible)
*/
hWM_Parent = GetShellWindow();
do
{
Children = nullptr;
XQueryTree( pDisplay,
hWM_Parent,
&hRoot,
&hDummy,
&Children,
&nChildren );
bool bError = GetGenericUnixSalData()->ErrorTrapPop( false );
GetGenericUnixSalData()->ErrorTrapPush();
if( bError )
{
hWM_Parent = GetShellWindow();
break;
}
/* this sometimes happens if a Show(true) is
* immediately followed by Show(false) (which is braindead anyway)
*/
if( hDummy == hWM_Parent )
hDummy = hRoot;
if( hDummy != hRoot )
hWM_Parent = hDummy;
if( Children )
XFree( Children );
} while( hDummy != hRoot );
if( GetStackingWindow() == None
&& hWM_Parent != hPresentationWindow
&& hWM_Parent != GetShellWindow()
&& ( ! pDisableStackingCheck || ! *pDisableStackingCheck )
)
{
mhStackingWindow = hWM_Parent;
XSelectInput( pDisplay, GetStackingWindow(), StructureNotifyMask );
}
if( hWM_Parent == pDisplay_->GetRootWindow( pDisplay_->GetDefaultXScreen() )
|| hWM_Parent == GetForeignParent()
|| pEvent->parent == pDisplay_->GetRootWindow( pDisplay_->GetDefaultXScreen() )
|| ( nStyle_ & SalFrameStyleFlags::FLOAT ) )
{
// Reparenting before Destroy
aPresentationReparentList.remove( GetStackingWindow() );
mhStackingWindow = None;
GetGenericUnixSalData()->ErrorTrapPop();
return false;
}
/*
* evil hack to show decorated windows on top
* of override redirect presentation windows:
* reparent the window manager window to the presentation window
* does not work with non-reparenting WMs
* in future this should not be necessary anymore with
* _NET_WM_STATE_FULLSCREEN available
*/
if( hPresentationWindow != None
&& hPresentationWindow != GetWindow()
&& GetStackingWindow() != None
&& GetStackingWindow() != GetDisplay()->GetRootWindow( m_nXScreen )
)
{
int x = 0, y = 0;
::Window aChild;
XTranslateCoordinates( GetXDisplay(),
GetStackingWindow(),
GetDisplay()->GetRootWindow( m_nXScreen ),
0, 0,
&x, &y,
&aChild
);
XReparentWindow( GetXDisplay(),
GetStackingWindow(),
hPresentationWindow,
x, y
);
aPresentationReparentList.push_back( GetStackingWindow() );
}
int nLeft = 0, nTop = 0;
XTranslateCoordinates( GetXDisplay(),
GetShellWindow(),
hWM_Parent,
0, 0,
&nLeft,
&nTop,
&hDummy );
maGeometry.setLeftDecoration(nLeft > 0 ? nLeft-1 : 0);
maGeometry.setTopDecoration(nTop > 0 ? nTop-1 : 0);
/*
* decorations are not symmetric,
* so need real geometries here
* (this will fail with virtual roots ?)
*/
// reset error occurred
GetGenericUnixSalData()->ErrorTrapPop();
GetGenericUnixSalData()->ErrorTrapPush();
int xp, yp, x, y;
unsigned int wp, w, hp, h, bw, d;
XGetGeometry( GetXDisplay(),
GetShellWindow(),
&hRoot,
&x, &y, &w, &h, &bw, &d );
XGetGeometry( GetXDisplay(),
hWM_Parent,
&hRoot,
&xp, &yp, &wp, &hp, &bw, &d );
bool bResized = false;
bool bError = GetGenericUnixSalData()->ErrorTrapPop( false );
GetGenericUnixSalData()->ErrorTrapPush();
if( ! bError )
{
maGeometry.setRightDecoration(wp - w - maGeometry.leftDecoration());
maGeometry.setBottomDecoration(hp - h - maGeometry.topDecoration());
bResized = w != o3tl::make_unsigned(maGeometry.width()) ||
h != o3tl::make_unsigned(maGeometry.height());
/*
* note: this works because hWM_Parent is direct child of root,
* not necessarily parent of GetShellWindow()
*/
maGeometry.setPosSize({ xp + nLeft, yp + nTop }, { static_cast<tools::Long>(w), static_cast<tools::Long>(h) });
}
// limit width and height if we are too large: #47757
// olwm and fvwm need this, it doesn't harm the rest
// #i81311# do this only for sizable frames
if( nStyle_ & SalFrameStyleFlags::SIZEABLE )
{
AbsoluteScreenPixelSize aScreenSize = GetDisplay()->GetScreenSize( m_nXScreen );
int nScreenWidth = aScreenSize.Width();
int nScreenHeight = aScreenSize.Height();
int nFrameWidth = maGeometry.width() + maGeometry.leftDecoration() + maGeometry.rightDecoration();
int nFrameHeight = maGeometry.height() + maGeometry.topDecoration() + maGeometry.bottomDecoration();
if ((nFrameWidth > nScreenWidth) || (nFrameHeight > nScreenHeight))
{
Size aSize(maGeometry.width(), maGeometry.height());
if (nFrameWidth > nScreenWidth)
aSize.setWidth( nScreenWidth - maGeometry.rightDecoration() - maGeometry.leftDecoration() );
if (nFrameHeight > nScreenHeight)
aSize.setHeight( nScreenHeight - maGeometry.bottomDecoration() - maGeometry.topDecoration() );
SetSize( aSize );
bResized = false;
}
}
if( bResized )
CallCallback( SalEvent::Resize, nullptr );
GetGenericUnixSalData()->ErrorTrapPop();
return true;
}
bool X11SalFrame::HandleStateEvent( XPropertyEvent const *pEvent )
{
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
unsigned char *prop = nullptr;
if( 0 != XGetWindowProperty( GetXDisplay(),
GetShellWindow(),
pEvent->atom, // property
0, // long_offset (32bit)
2, // long_length (32bit)
False, // delete
pEvent->atom, // req_type
&actual_type,
&actual_format,
&nitems,
&bytes_after,
&prop )
|| ! prop
)
return false;
DBG_ASSERT( actual_type == pEvent->atom
&& 32 == actual_format
&& 2 == nitems
&& 0 == bytes_after, "HandleStateEvent" );
if( *reinterpret_cast<unsigned long*>(prop) == NormalState )
nShowState_ = X11ShowState::Normal;
else if( *reinterpret_cast<unsigned long*>(prop) == IconicState )
nShowState_ = X11ShowState::Minimized;
XFree( prop );
return true;
}
bool X11SalFrame::HandleClientMessage( XClientMessageEvent *pEvent )
{
const WMAdaptor& rWMAdaptor( *pDisplay_->getWMAdaptor() );
#if !defined(__synchronous_extinput__)
if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::SAL_EXTTEXTEVENT ) )
{
HandleExtTextEvent (pEvent);
return true;
}
#endif
else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::SAL_QUITEVENT ) )
{
SAL_WARN( "vcl", "X11SalFrame::Dispatch Quit" );
Close(); // ???
return true;
}
else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::WM_PROTOCOLS ) )
{
if( static_cast<Atom>(pEvent->data.l[0]) == rWMAdaptor.getAtom( WMAdaptor::NET_WM_PING ) )
rWMAdaptor.answerPing( this, pEvent );
else if( ! ( nStyle_ & SalFrameStyleFlags::PLUG )
&& ! (( nStyle_ & SalFrameStyleFlags::FLOAT ) && (nStyle_ & SalFrameStyleFlags::OWNERDRAWDECORATION))
)
{
if( static_cast<Atom>(pEvent->data.l[0]) == rWMAdaptor.getAtom( WMAdaptor::WM_DELETE_WINDOW ) )
{
Close();
return true;
}
else if( static_cast<Atom>(pEvent->data.l[0]) == rWMAdaptor.getAtom( WMAdaptor::WM_TAKE_FOCUS ) )
{
// do nothing, we set the input focus in ToTop() if necessary
#if OSL_DEBUG_LEVEL > 1
SAL_INFO("vcl.window", "got WM_TAKE_FOCUS on "
<< ((nStyle_ &
SalFrameStyleFlags::OWNERDRAWDECORATION) ?
"ownerdraw" :
"NON OWNERDRAW" )
<< " window.");
#endif
}
}
}
else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::XEMBED ) &&
pEvent->window == GetWindow() )
{
if( pEvent->data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE
pEvent->data.l[1] == 2 ) // XEMBED_WINDOW_DEACTIVATE
{
XFocusChangeEvent aEvent;
aEvent.type = (pEvent->data.l[1] == 1 ? FocusIn : FocusOut);
aEvent.serial = pEvent->serial;
aEvent.send_event = True;
aEvent.display = pEvent->display;
aEvent.window = pEvent->window;
aEvent.mode = NotifyNormal;
aEvent.detail = NotifyDetailNone;
HandleFocusEvent( &aEvent );
}
}
return false;
}
bool X11SalFrame::Dispatch( XEvent *pEvent )
{
bool nRet = false;
if( -1 == nCaptured_ )
{
CaptureMouse( true );
#ifdef DBG_UTIL
if( -1 != nCaptured_ )
pDisplay_->DbgPrintDisplayEvent("Captured", pEvent);
#endif
}
if( pEvent->xany.window == GetShellWindow() || pEvent->xany.window == GetWindow() )
{
switch( pEvent->type )
{
case KeyPress:
nRet = HandleKeyEvent( &pEvent->xkey );
break;
case KeyRelease:
nRet = HandleKeyEvent( &pEvent->xkey );
break;
case ButtonPress:
// if we lose the focus in presentation mode
// there are good chances that we never get it back
// since the WM ignores us
if( IsOverrideRedirect() )
{
XSetInputFocus( GetXDisplay(), GetShellWindow(),
RevertToNone, CurrentTime );
}
[[fallthrough]];
case ButtonRelease:
case MotionNotify:
case EnterNotify:
case LeaveNotify:
nRet = HandleMouseEvent( pEvent );
break;
case FocusIn:
case FocusOut:
nRet = HandleFocusEvent( &pEvent->xfocus );
break;
case Expose:
case GraphicsExpose:
nRet = HandleExposeEvent( pEvent );
break;
case MapNotify:
if( pEvent->xmap.window == GetShellWindow() )
{
if( nShowState_ == X11ShowState::Hidden )
{
/*
* workaround for (at least) KWin 2.2.2
* which will map windows that were once transient
* even if they are withdrawn when the respective
* document is mapped.
*/
if( ! (nStyle_ & SalFrameStyleFlags::PLUG) )
XUnmapWindow( GetXDisplay(), GetShellWindow() );
break;
}
bMapped_ = true;
bViewable_ = true;
nRet = true;
if ( mpInputContext != nullptr )
mpInputContext->Map( this );
CallCallback( SalEvent::Resize, nullptr );
bool bSetFocus = m_bSetFocusOnMap;
/*
* sometimes a message box/dialogue is brought up when a frame is not mapped
* the corresponding TRANSIENT_FOR hint is then set to the root window
* so that the dialogue shows in all cases. Correct it here if the
* frame is shown afterwards.
*/
if( ! IsChildWindow()
&& ! IsOverrideRedirect()
&& ! IsFloatGrabWindow()
)
{
for (auto const& child : maChildren)
{
if( child->mbTransientForRoot )
pDisplay_->getWMAdaptor()->changeReferenceFrame( child, this );
}
}
if( hPresentationWindow != None && GetShellWindow() == hPresentationWindow )
XSetInputFocus( GetXDisplay(), GetShellWindow(), RevertToParent, CurrentTime );
if( bSetFocus )
{
XSetInputFocus( GetXDisplay(),
GetShellWindow(),
RevertToParent,
CurrentTime );
}
RestackChildren();
m_bSetFocusOnMap = false;
}
break;
case UnmapNotify:
if( pEvent->xunmap.window == GetShellWindow() )
{
bMapped_ = false;
bViewable_ = false;
nRet = true;
if ( mpInputContext != nullptr )
mpInputContext->Unmap();
CallCallback( SalEvent::Resize, nullptr );
}
break;
case ConfigureNotify:
if( pEvent->xconfigure.window == GetShellWindow()
|| pEvent->xconfigure.window == GetWindow() )
nRet = HandleSizeEvent( &pEvent->xconfigure );
break;
case VisibilityNotify:
nVisibility_ = pEvent->xvisibility.state;
nRet = true;
if( bAlwaysOnTop_
&& bMapped_
&& ! GetDisplay()->getWMAdaptor()->isAlwaysOnTopOK()
&& nVisibility_ != VisibilityUnobscured )
maAlwaysOnTopRaiseTimer.Start();
break;
case ReparentNotify:
nRet = HandleReparentEvent( &pEvent->xreparent );
break;
case MappingNotify:
break;
case ColormapNotify:
nRet = false;
break;
case PropertyNotify:
{
if( pEvent->xproperty.atom == pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_STATE ) )
nRet = HandleStateEvent( &pEvent->xproperty );
else
nRet = pDisplay_->getWMAdaptor()->handlePropertyNotify( this, &pEvent->xproperty );
break;
}
case ClientMessage:
nRet = HandleClientMessage( &pEvent->xclient );
break;
}
}
else
{
switch( pEvent->type )
{
case FocusIn:
case FocusOut:
if( ( nStyle_ & SalFrameStyleFlags::PLUG )
&& ( pEvent->xfocus.window == GetShellWindow()
|| pEvent->xfocus.window == GetForeignParent() )
)
{
nRet = HandleFocusEvent( &pEvent->xfocus );
}
break;
case ConfigureNotify:
if( pEvent->xconfigure.window == GetForeignParent() ||
pEvent->xconfigure.window == GetShellWindow() )
nRet = HandleSizeEvent( &pEvent->xconfigure );
if( pEvent->xconfigure.window == GetStackingWindow() )
nRet = HandleSizeEvent( &pEvent->xconfigure );
RestackChildren();
break;
}
}
return nRet;
}
void X11SalFrame::ResetClipRegion()
{
m_vClipRectangles.clear();
const int dest_kind = ShapeBounding;
const int op = ShapeSet;
const int ordering = YSorted;
XWindowAttributes win_attrib;
XRectangle win_size;
::Window aShapeWindow = mhShellWindow;
XGetWindowAttributes ( GetDisplay()->GetDisplay(),
aShapeWindow,
&win_attrib );
win_size.x = 0;
win_size.y = 0;
win_size.width = win_attrib.width;
win_size.height = win_attrib.height;
XShapeCombineRectangles ( GetDisplay()->GetDisplay(),
aShapeWindow,
dest_kind,
0, 0, // x_off, y_off
&win_size, // list of rectangles
1, // number of rectangles
op, ordering );
}
void X11SalFrame::BeginSetClipRegion( sal_uInt32 /*nRects*/ )
{
m_vClipRectangles.clear();
}
void X11SalFrame::UnionClipRegion( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
{
m_vClipRectangles.emplace_back( XRectangle { static_cast<short>(nX), static_cast<short>(nY),
static_cast<unsigned short>(nWidth), static_cast<unsigned short>(nHeight) } );
}
void X11SalFrame::EndSetClipRegion()
{
const int dest_kind = ShapeBounding;
const int ordering = YSorted;
const int op = ShapeSet;
::Window aShapeWindow = mhShellWindow;
XShapeCombineRectangles ( GetDisplay()->GetDisplay(),
aShapeWindow,
dest_kind,
0, 0, // x_off, y_off
m_vClipRectangles.data(),
m_vClipRectangles.size(),
op, ordering );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V547 Expression '3 == pEvent->xbutton.button' is always true.
↑ V595 The 'pProps' pointer was utilized before it was verified against nullptr. Check lines: 391, 392.
↑ V1053 Calling the 'ShowFullScreen' virtual function in the destructor may lead to unexpected result at runtime.
↑ V522 There might be dereferencing of a potential null pointer 'pBuffer'. Check lines: 2967, 2966.
↑ V547 Expression 'mpParent' is always true.
↑ V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.
↑ V1037 Two or more case-branches perform the same actions. Check lines: 3636, 3640