/* -*- 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() );
}
pGraphics_.reset();
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.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;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland