blob: 7fd1ca701748c49d39a130ee2aca0ba89e414555 [file] [log] [blame]
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.awt.X11;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.WindowEvent;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import sun.awt.IconInfo;
import sun.util.logging.PlatformLogger;
import sun.awt.AWTAccessor;
import sun.awt.SunToolkit;
abstract class XDecoratedPeer extends XWindowPeer {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XDecoratedPeer");
private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
// Set to true when we get the first ConfigureNotify after being
// reparented - indicates that WM has adopted the top-level.
boolean configure_seen;
boolean insets_corrected;
XIconWindow iconWindow;
volatile WindowDimensions dimensions;
XContentWindow content;
volatile Insets currentInsets;
XFocusProxyWindow focusProxy;
static final Map<Class<?>,Insets> lastKnownInsets =
Collections.synchronizedMap(new HashMap<>());
XDecoratedPeer(Window target) {
super(target);
}
XDecoratedPeer(XCreateWindowParams params) {
super(params);
}
public long getShell() {
return window;
}
public long getContentWindow() {
return (content == null) ? window : content.getWindow();
}
void preInit(XCreateWindowParams params) {
super.preInit(params);
winAttr.initialFocus = true;
currentInsets = new Insets(0,0,0,0);
if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
currentInsets = lastKnownInsets.get(getClass());
}
applyGuessedInsets();
Rectangle bounds = (Rectangle)params.get(BOUNDS);
dimensions = new WindowDimensions(bounds, getRealInsets(), false);
params.put(BOUNDS, dimensions.getClientRect());
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Initial dimensions {0}", dimensions);
}
// Deny default processing of these events on the shell - proxy will take care of
// them instead
Long eventMask = (Long)params.get(EVENT_MASK);
params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask)));
}
void postInit(XCreateWindowParams params) {
// The size hints must be set BEFORE mapping the window (see 6895647)
updateSizeHints(dimensions);
// The super method maps the window if it's visible on the shared level
super.postInit(params);
// The lines that follow need to be in a postInit, so they
// happen after the X window is created.
setResizable(winAttr.initialResizability);
XWM.requestWMExtents(getWindow());
content = XContentWindow.createContent(this);
if (warningWindow != null) {
warningWindow.toFront();
}
focusProxy = createFocusProxy();
}
void setIconHints(java.util.List<IconInfo> icons) {
if (!XWM.getWM().setNetWMIcon(this, icons)) {
if (icons.size() > 0) {
if (iconWindow == null) {
iconWindow = new XIconWindow(this);
}
iconWindow.setIconImages(icons);
}
}
}
public void updateMinimumSize() {
super.updateMinimumSize();
XToolkit.awtLock();
try {
updateMinSizeHints();
} finally {
XToolkit.awtUnlock();
}
}
private void updateMinSizeHints() {
if (isResizable()) {
Dimension minimumSize = getTargetMinimumSize();
if (minimumSize != null) {
Insets insets = getRealInsets();
int minWidth = minimumSize.width - insets.left - insets.right;
int minHeight = minimumSize.height - insets.top - insets.bottom;
if (minWidth < 0) minWidth = 0;
if (minHeight < 0) minHeight = 0;
setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),
getX(), getY(), minWidth, minHeight);
if (isVisible()) {
Rectangle bounds = getShellBounds();
int nw = (bounds.width < minWidth) ? minWidth : bounds.width;
int nh = (bounds.height < minHeight) ? minHeight : bounds.height;
if (nw != bounds.width || nh != bounds.height) {
setShellSize(new Rectangle(0, 0, nw, nh));
}
}
} else {
boolean isMinSizeSet = isMinSizeSet();
XWM.removeSizeHints(this, XUtilConstants.PMinSize);
/* Some WMs need remap to redecorate the window */
if (isMinSizeSet && isShowing() && XWM.needRemap(this)) {
/*
* Do the re/mapping at the Xlib level. Since we essentially
* work around a WM bug we don't want this hack to be exposed
* to Intrinsics (i.e. don't mess with grabs, callbacks etc).
*/
xSetVisible(false);
XToolkit.XSync();
xSetVisible(true);
}
}
}
}
XFocusProxyWindow createFocusProxy() {
return new XFocusProxyWindow(this);
}
protected XAtomList getWMProtocols() {
XAtomList protocols = super.getWMProtocols();
protocols.add(wm_delete_window);
protocols.add(wm_take_focus);
return protocols;
}
public Graphics getGraphics() {
AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
return getGraphics(content.surfaceData,
compAccessor.getForeground(target),
compAccessor.getBackground(target),
compAccessor.getFont(target));
}
public void setTitle(String title) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Title is " + title);
}
XToolkit.awtLock();
try {
winAttr.title = title;
updateWMName();
} finally {
XToolkit.awtUnlock();
}
}
protected String getWMName() {
if (winAttr.title == null || winAttr.title.trim().equals("")) {
return " ";
} else {
return winAttr.title;
}
}
void updateWMName() {
XToolkit.awtLock();
try {
super.updateWMName();
String name = getWMName();
if (name == null || name.trim().equals("")) {
name = "Java";
}
XAtom iconNameAtom = XAtom.get(XAtom.XA_WM_ICON_NAME);
iconNameAtom.setProperty(getWindow(), name);
XAtom netIconNameAtom = XAtom.get("_NET_WM_ICON_NAME");
netIconNameAtom.setPropertyUTF8(getWindow(), name);
} finally {
XToolkit.awtUnlock();
}
}
// NOTE: This method may be called by privileged threads.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
public void handleIconify() {
postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED));
}
// NOTE: This method may be called by privileged threads.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
public void handleDeiconify() {
postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
}
public void handleFocusEvent(XEvent xev) {
super.handleFocusEvent(xev);
XFocusChangeEvent xfe = xev.get_xfocus();
// If we somehow received focus events forward it instead to proxy
// FIXME: Shouldn't we instead check for inferrior?
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Received focus event on shell: " + xfe);
}
// focusProxy.xRequestFocus();
}
/***************************************************************************************
* I N S E T S C O D E
**************************************************************************************/
protected boolean isInitialReshape() {
return false;
}
private static Insets difference(Insets i1, Insets i2) {
return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right);
}
private static boolean isNull(Insets i) {
return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0);
}
private static Insets copy(Insets i) {
return new Insets(i.top, i.left, i.bottom, i.right);
}
// insets which we get from WM (e.g from _NET_FRAME_EXTENTS)
private Insets wm_set_insets;
private Insets getWMSetInsets(XAtom changedAtom) {
if (isEmbedded()) {
return null;
}
if (wm_set_insets != null) {
return wm_set_insets;
}
if (changedAtom == null) {
wm_set_insets = XWM.getInsetsFromExtents(getWindow());
} else {
wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom);
}
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
insLog.finer("FRAME_EXTENTS: {0}", wm_set_insets);
}
if (wm_set_insets != null) {
wm_set_insets = copy(wm_set_insets);
}
return wm_set_insets;
}
private void resetWMSetInsets() {
if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) {
currentInsets = new Insets(0, 0, 0, 0);
wm_set_insets = null;
} else {
insets_corrected = false;
}
}
public void handlePropertyNotify(XEvent xev) {
super.handlePropertyNotify(xev);
XPropertyEvent ev = xev.get_xproperty();
if( !insets_corrected && isReparented() &&
XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
int state = XWM.getWM().getState(this);
if ((state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) {
// Stop ignoring ConfigureNotify because no extents will be sent
// by WM for initially maximized decorated window.
// Re-request window bounds to ensure actual dimensions and
// notify the target with the initial size.
insets_corrected = true;
XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
getWindow(), 0, 0);
}
}
if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom()
|| ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
{
if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) {
getWMSetInsets(XAtom.get(ev.get_atom()));
} else {
if (!isReparented()) {
return;
}
wm_set_insets = null;
Insets in = getWMSetInsets(XAtom.get(ev.get_atom()));
if (isNull(in)) {
return;
}
if (!isEmbedded() && !isTargetUndecorated()) {
lastKnownInsets.put(getClass(), in);
}
if (!in.equals(dimensions.getInsets())) {
if (insets_corrected || isMaximized()) {
currentInsets = in;
insets_corrected = true;
// insets were changed by WM. To handle this situation
// re-request window bounds because the current
// dimensions may be not actual as well.
XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
getWindow(), 0, 0);
} else {
// recalculate dimensions when window is just created
// and the initially guessed insets were wrong
handleCorrectInsets(in);
}
} else if (!insets_corrected || !dimensions.isClientSizeSet()) {
insets_corrected = true;
// initial insets were guessed correctly. Re-request
// frame bounds because they may be changed by WM if the
// initial window position overlapped desktop's toolbars.
// This should initiate the final ConfigureNotify upon which
// the target will be notified with the final size.
XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
getWindow(), 0, 0);
}
}
}
}
long reparent_serial = 0;
public void handleReparentNotifyEvent(XEvent xev) {
XReparentEvent xe = xev.get_xreparent();
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine(xe.toString());
}
reparent_serial = xe.get_serial();
long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
if (isEmbedded()) {
setReparented(true);
insets_corrected = true;
return;
}
if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) {
setReparented(true);
insets_corrected = true;
reshape(dimensions, SET_SIZE, false);
} else if (xe.get_parent() == root) {
configure_seen = false;
insets_corrected = false;
/*
* We can be repareted to root for two reasons:
* . setVisible(false)
* . WM exited
*/
if (isVisible()) { /* WM exited */
/* Work around 4775545 */
XWM.getWM().unshadeKludge(this);
insLog.fine("- WM exited");
} else {
insLog.fine(" - reparent due to hide");
}
} else { /* reparented to WM frame, figure out our insets */
setReparented(true);
insets_corrected = false;
if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
return;
}
// Check if we have insets provided by the WM
Insets correctWM = getWMSetInsets(null);
if (correctWM != null) {
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
insLog.finer("wm-provided insets {0}", correctWM);
}
// If these insets are equal to our current insets - no actions are necessary
Insets dimInsets = dimensions.getInsets();
if (correctWM.equals(dimInsets)) {
insLog.finer("Insets are the same as estimated - no additional reshapes necessary");
no_reparent_artifacts = true;
insets_corrected = true;
applyGuessedInsets();
return;
}
} else {
correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent());
if (correctWM != null) {
correctWM = copy(correctWM);
}
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
if (correctWM != null) {
insLog.finer("correctWM {0}", correctWM);
} else {
insLog.finer("correctWM insets are not available, waiting for configureNotify");
}
}
}
}
if (correctWM != null) {
handleCorrectInsets(correctWM);
}
}
}
private void handleCorrectInsets(Insets correctWM) {
/*
* Ok, now see if we need adjust window size because
* initial insets were wrong (most likely they were).
*/
Insets correction = difference(correctWM, currentInsets);
if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
insLog.finest("Corrention {0}", correction);
}
if (!isNull(correction)) {
currentInsets = copy(correctWM);
applyGuessedInsets();
//Fix for 6318109: PIT: Min Size is not honored properly when a
//smaller size is specified in setSize(), XToolkit
//update minimum size hints
updateMinSizeHints();
}
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
insLog.finer("Dimensions before reparent: " + dimensions);
}
WindowDimensions newDimensions = new WindowDimensions(dimensions);
newDimensions.setInsets(getRealInsets());
dimensions = newDimensions;
insets_corrected = true;
if (isMaximized()) {
return;
}
/*
* If this window has been sized by a pack() we need
* to keep the interior geometry intact. Since pack()
* computed width and height with wrong insets, we
* must adjust the target dimensions appropriately.
*/
if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) {
reshape(dimensions, SET_BOUNDS, false);
} else {
reshape(dimensions, SET_SIZE, false);
}
}
void handleMoved(WindowDimensions dims) {
Point loc = dims.getLocation();
AWTAccessor.getComponentAccessor().setLocation((Component)target, loc.x, loc.y);
postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
}
private Insets guessInsets() {
if (isEmbedded() || isTargetUndecorated()) {
return new Insets(0, 0, 0, 0);
} else {
if (!isNull(currentInsets)) {
/* insets were set on wdata by System Properties */
return copy(currentInsets);
} else {
Insets res = getWMSetInsets(null);
if (res == null) {
res = XWM.getWM().guessInsets(this);
if (res != null) {
res = copy(res);
}
}
return res;
}
}
}
private void applyGuessedInsets() {
Insets guessed = guessInsets();
currentInsets = copy(guessed);
}
private Insets getRealInsets() {
if (isNull(currentInsets)) {
applyGuessedInsets();
}
return currentInsets;
}
public Insets getInsets() {
Insets in = copy(getRealInsets());
in.top += getMenuBarHeight();
if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
insLog.finest("Get insets returns {0}", in);
}
return in;
}
boolean gravityBug() {
return XWM.configureGravityBuggy();
}
// The height of area used to display current active input method
int getInputMethodHeight() {
return 0;
}
void updateSizeHints(WindowDimensions dims) {
Rectangle rec = dims.getClientRect();
checkShellRect(rec);
updateSizeHints(rec.x, rec.y, rec.width, rec.height);
}
void updateSizeHints() {
updateSizeHints(dimensions);
}
// Coordinates are that of the target
// Called only on Toolkit thread
private void reshape(WindowDimensions newDimensions, int op,
boolean userReshape)
{
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape);
}
if (userReshape) {
// We handle only userReshape == true cases. It means that
// if the window manager or any other part of the windowing
// system sets inappropriate size for this window, we can
// do nothing but accept it.
Rectangle newBounds = newDimensions.getBounds();
Insets insets = newDimensions.getInsets();
// Inherit isClientSizeSet from newDimensions
if (newDimensions.isClientSizeSet()) {
newBounds = new Rectangle(newBounds.x, newBounds.y,
newBounds.width - insets.left - insets.right,
newBounds.height - insets.top - insets.bottom);
}
newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
}
if (!isReparented() || !isVisible()) {
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("- not reparented({0}) or not visible({1}), default reshape",
Boolean.valueOf(isReparented()), Boolean.valueOf(visible));
}
// Fix for 6323293.
// This actually is needed to preserve compatibility with previous releases -
// some of licensees are expecting componentMoved event on invisible one while
// its location changes.
Point oldLocation = getLocation();
Point newLocation = newDimensions.getLocation();
if (!newLocation.equals(oldLocation)) {
handleMoved(newDimensions);
}
dimensions = new WindowDimensions(newDimensions);
updateSizeHints(dimensions);
Rectangle client = dimensions.getClientRect();
checkShellRect(client);
setShellBounds(client);
if (content != null &&
!content.getSize().equals(newDimensions.getSize()))
{
reconfigureContentWindow(newDimensions);
}
return;
}
updateChildrenSizes();
applyGuessedInsets();
Rectangle shellRect = newDimensions.getClientRect();
if (gravityBug()) {
Insets in = newDimensions.getInsets();
shellRect.translate(in.left, in.top);
}
if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) {
shellRect.setLocation(0, 0);
}
checkShellRectSize(shellRect);
if (!isEmbedded()) {
checkShellRectPos(shellRect);
}
op = op & ~NO_EMBEDDED_CHECK;
if (op == SET_LOCATION) {
setShellPosition(shellRect);
} else if (isResizable()) {
if (op == SET_BOUNDS) {
setShellBounds(shellRect);
} else {
setShellSize(shellRect);
}
} else {
XWM.setShellNotResizable(this, newDimensions, shellRect, true);
if (op == SET_BOUNDS) {
setShellPosition(shellRect);
}
}
reconfigureContentWindow(newDimensions);
}
/**
* @param x, y, width, heith - dimensions of the window with insets
*/
private void reshape(int x, int y, int width, int height, int operation,
boolean userReshape)
{
WindowDimensions dims = new WindowDimensions(dimensions);
switch (operation & (~NO_EMBEDDED_CHECK)) {
case SET_LOCATION:
// Set location always sets bounds location. However, until the window is mapped we
// should use client coordinates
dims.setLocation(x, y);
break;
case SET_SIZE:
// Set size sets bounds size. However, until the window is mapped we
// should use client coordinates
dims.setSize(width, height);
break;
case SET_CLIENT_SIZE: {
// Sets client rect size. Width and height contain insets.
Insets in = currentInsets;
width -= in.left+in.right;
height -= in.top+in.bottom;
dims.setClientSize(width, height);
break;
}
case SET_BOUNDS:
default:
dims.setLocation(x, y);
dims.setSize(width, height);
break;
}
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("For the operation {0} new dimensions are {1}",
operationToString(operation), dims);
}
reshape(dims, operation, userReshape);
}
// This method gets overriden in XFramePeer & XDialogPeer.
abstract boolean isTargetUndecorated();
/**
* @see java.awt.peer.ComponentPeer#setBounds
*/
public void setBounds(int x, int y, int width, int height, int op) {
// TODO: Rewrite with WindowDimensions
XToolkit.awtLock();
try {
reshape(x, y, width, height, op, true);
} finally {
XToolkit.awtUnlock();
}
validateSurface();
}
// Coordinates are that of the shell
void reconfigureContentWindow(WindowDimensions dims) {
if (content == null) {
insLog.fine("WARNING: Content window is null");
return;
}
content.setContentBounds(dims);
}
boolean no_reparent_artifacts = false;
public void handleConfigureNotifyEvent(XEvent xev) {
if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM && !insets_corrected) {
return;
}
assert (SunToolkit.isAWTLockHeldByCurrentThread());
XConfigureEvent xe = xev.get_xconfigure();
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Configure notify {0}", xe);
}
// XXX: should really only consider synthetic events, but
if (isReparented()) {
configure_seen = true;
}
if (!isMaximized()
&& (xe.get_serial() == reparent_serial || xe.get_window() != getShell())
&& !no_reparent_artifacts)
{
insLog.fine("- reparent artifact, skipping");
return;
}
no_reparent_artifacts = false;
/**
* When there is a WM we receive some CN before being visible and after.
* We should skip all CN which are before being visible, because we assume
* the gravity is in action while it is not yet.
*
* When there is no WM we receive CN only _before_ being visible.
* We should process these CNs.
*/
if (!isVisible() && XWM.getWMID() != XWM.NO_WM) {
insLog.fine(" - not visible, skipping");
return;
}
/*
* Some window managers configure before we are reparented and
* the send event flag is set! ugh... (Enlighetenment for one,
* possibly MWM as well). If we haven't been reparented yet
* this is just the WM shuffling us into position. Ignore
* it!!!! or we wind up in a bogus location.
*/
int runningWM = XWM.getWMID();
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("reparented={0}, visible={1}, WM={2}, decorations={3}",
isReparented(), isVisible(), runningWM, getDecorations());
}
if (!isReparented() && isVisible() && runningWM != XWM.NO_WM
&& !XWM.isNonReparentingWM()
&& getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
insLog.fine("- visible but not reparented, skipping");
return;
}
//Last chance to correct insets
if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
long parent = XlibUtil.getParentWindow(window);
Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null;
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
if (correctWM != null) {
insLog.finer("Configure notify - insets : " + correctWM);
} else {
insLog.finer("Configure notify - insets are still not available");
}
}
if (correctWM != null) {
handleCorrectInsets(correctWM);
} else {
//Only one attempt to correct insets is made (to lower risk)
//if insets are still not available we simply set the flag
insets_corrected = true;
}
}
updateChildrenSizes();
// Bounds of the window
Rectangle targetBounds = AWTAccessor.getComponentAccessor().getBounds((Component)target);
Point newLocation = getNewLocation(xe, currentInsets.left, currentInsets.top);
WindowDimensions newDimensions =
new WindowDimensions(newLocation,
new Dimension(xe.get_width(), xe.get_height()),
copy(currentInsets),
true);
if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
insLog.finer("Insets are {0}, new dimensions {1}",
currentInsets, newDimensions);
}
checkIfOnNewScreen(newDimensions.getBounds());
Point oldLocation = getLocation();
dimensions = newDimensions;
if (!newLocation.equals(oldLocation)) {
handleMoved(newDimensions);
}
reconfigureContentWindow(newDimensions);
updateChildrenSizes();
repositionSecurityWarning();
}
private void checkShellRectSize(Rectangle shellRect) {
shellRect.width = Math.max(MIN_SIZE, shellRect.width);
shellRect.height = Math.max(MIN_SIZE, shellRect.height);
}
private void checkShellRectPos(Rectangle shellRect) {
int wm = XWM.getWMID();
if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) {
if (shellRect.x == 0 && shellRect.y == 0) {
shellRect.x = shellRect.y = 1;
}
}
}
private void checkShellRect(Rectangle shellRect) {
checkShellRectSize(shellRect);
checkShellRectPos(shellRect);
}
private void setShellBounds(Rectangle rec) {
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Setting shell bounds on " + this + " to " + rec);
}
updateSizeHints(rec.x, rec.y, rec.width, rec.height);
XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getShell(),
rec.x, rec.y, rec.width, rec.height);
}
private void setShellSize(Rectangle rec) {
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Setting shell size on " + this + " to " + rec);
}
updateSizeHints(rec.x, rec.y, rec.width, rec.height);
XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), rec.width, rec.height);
}
private void setShellPosition(Rectangle rec) {
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("Setting shell position on " + this + " to " + rec);
}
updateSizeHints(rec.x, rec.y, rec.width, rec.height);
XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y);
}
public void setResizable(boolean resizable) {
XToolkit.awtLock();
try {
int fs = winAttr.functions;
if (!isResizable() && resizable) {
resetWMSetInsets();
if (!isEmbedded()) {
setReparented(false);
}
winAttr.isResizable = resizable;
if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
fs &= ~(MWMConstants.MWM_FUNC_RESIZE
| MWMConstants.MWM_FUNC_MAXIMIZE);
} else {
fs |= (MWMConstants.MWM_FUNC_RESIZE
| MWMConstants.MWM_FUNC_MAXIMIZE);
}
winAttr.functions = fs;
XWM.setShellResizable(this);
} else if (isResizable() && !resizable) {
resetWMSetInsets();
if (!isEmbedded()) {
setReparented(false);
}
winAttr.isResizable = resizable;
if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
fs |= (MWMConstants.MWM_FUNC_RESIZE
| MWMConstants.MWM_FUNC_MAXIMIZE);
} else {
fs &= ~(MWMConstants.MWM_FUNC_RESIZE
| MWMConstants.MWM_FUNC_MAXIMIZE);
}
winAttr.functions = fs;
XWM.setShellNotResizable(this, dimensions,
XWM.getWMID() == XWM.UNITY_COMPIZ_WM && configure_seen ?
dimensions.getScreenBounds() :
dimensions.getBounds(), false);
}
} finally {
XToolkit.awtUnlock();
}
}
Rectangle getShellBounds() {
return dimensions.getClientRect();
}
public Rectangle getBounds() {
return dimensions.getBounds();
}
public Dimension getSize() {
return dimensions.getSize();
}
public int getX() {
return dimensions.getLocation().x;
}
public int getY() {
return dimensions.getLocation().y;
}
public Point getLocation() {
return dimensions.getLocation();
}
public int getAbsoluteX() {
// NOTE: returning this peer's location which is shell location
return dimensions.getScreenBounds().x;
}
public int getAbsoluteY() {
// NOTE: returning this peer's location which is shell location
return dimensions.getScreenBounds().y;
}
public int getWidth() {
return getSize().width;
}
public int getHeight() {
return getSize().height;
}
final public WindowDimensions getDimensions() {
return dimensions;
}
public Point getLocationOnScreen() {
XToolkit.awtLock();
try {
if (configure_seen) {
return toGlobal(0,0);
}
} finally {
XToolkit.awtUnlock();
}
Point location = target.getLocation();
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
insLog.fine("getLocationOnScreen {0} not reparented: {1} ",
this, location);
}
return location;
}
/***************************************************************************************
* END OF I N S E T S C O D E
**************************************************************************************/
protected boolean isEventDisabled(XEvent e) {
switch (e.get_type()) {
// Do not generate MOVED/RESIZED events since we generate them by ourselves
case XConstants.ConfigureNotify:
return true;
case XConstants.EnterNotify:
case XConstants.LeaveNotify:
// Disable crossing event on outer borders of Frame so
// we receive only one set of cross notifications(first set is from content window)
return true;
default:
return super.isEventDisabled(e);
}
}
int getDecorations() {
return winAttr.decorations;
}
int getFunctions() {
return winAttr.functions;
}
public void setVisible(boolean vis) {
if (log.isLoggable(PlatformLogger.Level.FINER)) {
log.finer("Setting {0} to visible {1}", this, Boolean.valueOf(vis));
}
if (vis && !isVisible()) {
XWM.setShellDecor(this);
super.setVisible(vis);
if (winAttr.isResizable) {
//Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
//We need to update frame's minimum size, not to reset it
XWM.removeSizeHints(this, XUtilConstants.PMaxSize);
updateMinimumSize();
}
} else {
super.setVisible(vis);
}
}
public void dispose() {
if (content != null) {
content.destroy();
}
focusProxy.destroy();
if (iconWindow != null) {
iconWindow.destroy();
}
super.dispose();
}
public void handleClientMessage(XEvent xev) {
super.handleClientMessage(xev);
XClientMessageEvent cl = xev.get_xclient();
if ((wm_protocols != null) && (cl.get_message_type() == wm_protocols.getAtom())) {
if (cl.get_data(0) == wm_delete_window.getAtom()) {
handleQuit();
} else if (cl.get_data(0) == wm_take_focus.getAtom()) {
handleWmTakeFocus(cl);
}
}
}
private void handleWmTakeFocus(XClientMessageEvent cl) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("WM_TAKE_FOCUS on {0}", this);
}
if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
// JDK-8159460
Window focusedWindow = XKeyboardFocusManagerPeer.getInstance()
.getCurrentFocusedWindow();
Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
if (activeWindow != target) {
requestWindowFocus(cl.get_data(1), true);
} else {
WindowEvent we = new WindowEvent(focusedWindow,
WindowEvent.WINDOW_GAINED_FOCUS);
sendEvent(we);
}
} else {
requestWindowFocus(cl.get_data(1), true);
}
}
/**
* Requests focus to this decorated top-level by requesting X input focus
* to the shell window.
*/
protected void requestXFocus(long time, boolean timeProvided) {
// We have proxied focus mechanism - instead of shell the focus is held
// by "proxy" - invisible mapped window. When we want to set X input focus to
// toplevel set it on proxy instead.
if (focusProxy == null) {
if (focusLog.isLoggable(PlatformLogger.Level.WARNING)) {
focusLog.warning("Focus proxy is null for " + this);
}
} else {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("Requesting focus to proxy: " + focusProxy);
}
if (timeProvided) {
focusProxy.xRequestFocus(time);
} else {
focusProxy.xRequestFocus();
}
}
}
XFocusProxyWindow getFocusProxy() {
return focusProxy;
}
private void handleQuit() {
postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING));
}
final void dumpMe() {
System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height);
}
final void dumpTarget() {
AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
int getWidth = compAccessor.getWidth((Component)target);
int getHeight = compAccessor.getHeight((Component)target);
int getTargetX = compAccessor.getX((Component)target);
int getTargetY = compAccessor.getY((Component)target);
System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight);
}
final void dumpShell() {
dumpWindow("Shell", getShell());
}
final void dumpContent() {
dumpWindow("Content", getContentWindow());
}
final void dumpParent() {
long parent = XlibUtil.getParentWindow(getShell());
if (parent != 0)
{
dumpWindow("Parent", parent);
}
else
{
System.err.println(">>> NO PARENT");
}
}
final void dumpWindow(String id, long window) {
XWindowAttributes pattr = new XWindowAttributes();
try {
XToolkit.awtLock();
try {
int status =
XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
window, pattr.pData);
}
finally {
XToolkit.awtUnlock();
}
System.err.println(">>>> " + id + ": " + pattr.get_x()
+ ", " + pattr.get_y() + ", " + pattr.get_width()
+ ", " + pattr.get_height());
} finally {
pattr.dispose();
}
}
final void dumpAll() {
dumpTarget();
dumpMe();
dumpParent();
dumpShell();
dumpContent();
}
boolean isMaximized() {
return false;
}
@Override
boolean isOverrideRedirect() {
return Window.Type.POPUP.equals(getWindowType());
}
public boolean requestWindowFocus(long time, boolean timeProvided) {
focusLog.fine("Request for decorated window focus");
// If this is Frame or Dialog we can't assure focus request success - but we still can try
// If this is Window and its owner Frame is active we can be sure request succedded.
Window focusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow();
Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Current window is:" +
" active={0}, focused={1}",
Boolean.valueOf(target == activeWindow),
Boolean.valueOf(target == focusedWindow));
}
if (WindowStateMachine.get().isWaitingForWindowShow()) {
focusLog.finer("Is waiting for window show : " + WindowStateMachine.get());
return false;
}
XWindowPeer toFocus = this;
while (toFocus.nextTransientFor != null) {
toFocus = toFocus.nextTransientFor;
}
if (toFocus == null || !toFocus.focusAllowedFor()) {
// This might change when WM will have property to determine focus policy.
// Right now, because policy is unknown we can't be sure we succedded
return false;
}
if (this == toFocus) {
if (isWMStateNetHidden()) {
focusLog.fine("The window is unmapped, so rejecting the request");
return false;
}
if (target == activeWindow && target != focusedWindow) {
// Happens when an owned window is currently focused
focusLog.fine("Focus is on child window - transferring it back to the owner");
handleWindowFocusInSync(-1, () -> {});
return true;
}
Window realNativeFocusedWindow = XWindowPeer.getNativeFocusedWindow();
if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
focusLog.finest("Real native focused window: " + realNativeFocusedWindow +
"\nKFM's focused window: " + focusedWindow);
}
// A workaround for Metacity. See 6522725, 6613426, 7147075.
if (target == realNativeFocusedWindow && XWM.getWMID() == XWM.METACITY_WM) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("The window is already natively focused.");
}
return true;
}
}
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("Requesting focus to " + (this == toFocus ? "this window" : toFocus));
}
if (timeProvided) {
toFocus.requestXFocus(time);
} else {
toFocus.requestXFocus();
}
return (this == toFocus);
}
XWindowPeer actualFocusedWindow = null;
void setActualFocusedWindow(XWindowPeer actualFocusedWindow) {
synchronized(getStateLock()) {
this.actualFocusedWindow = actualFocusedWindow;
}
}
boolean requestWindowFocus(XWindowPeer actualFocusedWindow,
long time, boolean timeProvided)
{
setActualFocusedWindow(actualFocusedWindow);
return requestWindowFocus(time, timeProvided);
}
public void handleWindowFocusIn(long serial) {
if (null == actualFocusedWindow) {
super.handleWindowFocusIn(serial);
} else {
/*
* Fix for 6314575.
* If this is a result of clicking on one of the Frame's component
* then 'actualFocusedWindow' shouldn't be focused. A decision of focusing
* it or not should be made after the appropriate Java mouse event (if any)
* is handled by the component where 'actualFocusedWindow' value may be reset.
*
* The fix is based on the empiric fact consisting in that the component
* receives native mouse event nearly at the same time the Frame receives
* WM_TAKE_FOCUS (when FocusIn is generated via XSetInputFocus call) but
* definetely before the Frame gets FocusIn event (when this method is called).
*/
postEvent(new InvocationEvent(target, new Runnable() {
public void run() {
XWindowPeer fw = null;
synchronized (getStateLock()) {
fw = actualFocusedWindow;
actualFocusedWindow = null;
if (null == fw || !fw.isVisible() || !fw.isFocusableWindow()) {
fw = XDecoratedPeer.this;
}
}
fw.handleWindowFocusIn_Dispatch();
}
}));
}
}
public void handleWindowFocusOut(Window oppositeWindow, long serial) {
Window actualFocusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow();
// If the actual focused window is not this decorated window then retain it.
if (actualFocusedWindow != null && actualFocusedWindow != target) {
Window owner = XWindowPeer.getDecoratedOwner(actualFocusedWindow);
if (owner != null && owner == target) {
setActualFocusedWindow((XWindowPeer) AWTAccessor.getComponentAccessor().getPeer(actualFocusedWindow));
}
}
super.handleWindowFocusOut(oppositeWindow, serial);
}
}