1/*
2 * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package sun.awt.X11;
26
27import java.awt.*;
28
29import java.awt.event.ComponentEvent;
30import java.awt.event.InvocationEvent;
31import java.awt.event.WindowEvent;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.Map;
35
36import sun.awt.IconInfo;
37import sun.util.logging.PlatformLogger;
38
39import sun.awt.AWTAccessor;
40import sun.awt.SunToolkit;
41
42abstract class XDecoratedPeer extends XWindowPeer {
43    private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XDecoratedPeer");
44    private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
45    private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
46    private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
47
48    // Set to true when we get the first ConfigureNotify after being
49    // reparented - indicates that WM has adopted the top-level.
50    boolean configure_seen;
51    boolean insets_corrected;
52
53    XIconWindow iconWindow;
54    volatile WindowDimensions dimensions;
55    XContentWindow content;
56    volatile Insets currentInsets;
57    XFocusProxyWindow focusProxy;
58    static final Map<Class<?>,Insets> lastKnownInsets =
59                                   Collections.synchronizedMap(new HashMap<>());
60
61    XDecoratedPeer(Window target) {
62        super(target);
63    }
64
65    XDecoratedPeer(XCreateWindowParams params) {
66        super(params);
67    }
68
69    public long getShell() {
70        return window;
71    }
72
73    public long getContentWindow() {
74        return (content == null) ? window : content.getWindow();
75    }
76
77    void preInit(XCreateWindowParams params) {
78        super.preInit(params);
79        winAttr.initialFocus = true;
80
81        currentInsets = new Insets(0,0,0,0);
82        if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
83            currentInsets = lastKnownInsets.get(getClass());
84        }
85        applyGuessedInsets();
86
87        Rectangle bounds = (Rectangle)params.get(BOUNDS);
88        dimensions = new WindowDimensions(bounds, getRealInsets(), false);
89        params.put(BOUNDS, dimensions.getClientRect());
90        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
91            insLog.fine("Initial dimensions {0}", dimensions);
92        }
93
94        // Deny default processing of these events on the shell - proxy will take care of
95        // them instead
96        Long eventMask = (Long)params.get(EVENT_MASK);
97        params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask)));
98    }
99
100    void postInit(XCreateWindowParams params) {
101        // The size hints must be set BEFORE mapping the window (see 6895647)
102        updateSizeHints(dimensions);
103
104        // The super method maps the window if it's visible on the shared level
105        super.postInit(params);
106
107        // The lines that follow need to be in a postInit, so they
108        // happen after the X window is created.
109        setResizable(winAttr.initialResizability);
110        XWM.requestWMExtents(getWindow());
111
112        content = XContentWindow.createContent(this);
113
114        if (warningWindow != null) {
115            warningWindow.toFront();
116        }
117        focusProxy = createFocusProxy();
118    }
119
120    void setIconHints(java.util.List<IconInfo> icons) {
121        if (!XWM.getWM().setNetWMIcon(this, icons)) {
122            if (icons.size() > 0) {
123                if (iconWindow == null) {
124                    iconWindow = new XIconWindow(this);
125                }
126                iconWindow.setIconImages(icons);
127            }
128        }
129    }
130
131    public void updateMinimumSize() {
132        super.updateMinimumSize();
133        XToolkit.awtLock();
134        try {
135            updateMinSizeHints();
136        } finally {
137            XToolkit.awtUnlock();
138        }
139    }
140
141    private void updateMinSizeHints() {
142        if (isResizable()) {
143            Dimension minimumSize = getTargetMinimumSize();
144            if (minimumSize != null) {
145                Insets insets = getRealInsets();
146                int minWidth = minimumSize.width - insets.left - insets.right;
147                int minHeight = minimumSize.height - insets.top - insets.bottom;
148                if (minWidth < 0) minWidth = 0;
149                if (minHeight < 0) minHeight = 0;
150                setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),
151                             getX(), getY(), minWidth, minHeight);
152                if (isVisible()) {
153                    Rectangle bounds = getShellBounds();
154                    int nw = (bounds.width < minWidth) ? minWidth : bounds.width;
155                    int nh = (bounds.height < minHeight) ? minHeight : bounds.height;
156                    if (nw != bounds.width || nh != bounds.height) {
157                        setShellSize(new Rectangle(0, 0, nw, nh));
158                    }
159                }
160            } else {
161                boolean isMinSizeSet = isMinSizeSet();
162                XWM.removeSizeHints(this, XUtilConstants.PMinSize);
163                /* Some WMs need remap to redecorate the window */
164                if (isMinSizeSet && isShowing() && XWM.needRemap(this)) {
165                    /*
166                     * Do the re/mapping at the Xlib level.  Since we essentially
167                     * work around a WM bug we don't want this hack to be exposed
168                     * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
169                     */
170                    xSetVisible(false);
171                    XToolkit.XSync();
172                    xSetVisible(true);
173                }
174            }
175        }
176    }
177
178    XFocusProxyWindow createFocusProxy() {
179        return new XFocusProxyWindow(this);
180    }
181
182    protected XAtomList getWMProtocols() {
183        XAtomList protocols = super.getWMProtocols();
184        protocols.add(wm_delete_window);
185        protocols.add(wm_take_focus);
186        return protocols;
187    }
188
189    public Graphics getGraphics() {
190        AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
191        return getGraphics(content.surfaceData,
192                           compAccessor.getForeground(target),
193                           compAccessor.getBackground(target),
194                           compAccessor.getFont(target));
195    }
196
197    public void setTitle(String title) {
198        if (log.isLoggable(PlatformLogger.Level.FINE)) {
199            log.fine("Title is " + title);
200        }
201        XToolkit.awtLock();
202        try {
203            winAttr.title = title;
204            updateWMName();
205        } finally {
206            XToolkit.awtUnlock();
207        }
208    }
209
210    protected String getWMName() {
211        if (winAttr.title == null || winAttr.title.trim().equals("")) {
212            return " ";
213        } else {
214            return winAttr.title;
215        }
216    }
217
218    void updateWMName() {
219        XToolkit.awtLock();
220        try {
221            super.updateWMName();
222            String name = getWMName();
223            if (name == null || name.trim().equals("")) {
224                name = "Java";
225            }
226            XAtom iconNameAtom = XAtom.get(XAtom.XA_WM_ICON_NAME);
227            iconNameAtom.setProperty(getWindow(), name);
228            XAtom netIconNameAtom = XAtom.get("_NET_WM_ICON_NAME");
229            netIconNameAtom.setPropertyUTF8(getWindow(), name);
230        } finally {
231            XToolkit.awtUnlock();
232        }
233    }
234
235    // NOTE: This method may be called by privileged threads.
236    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
237    public void handleIconify() {
238        postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED));
239    }
240
241    // NOTE: This method may be called by privileged threads.
242    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
243    public void handleDeiconify() {
244        postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
245    }
246
247    public void handleFocusEvent(XEvent xev) {
248        super.handleFocusEvent(xev);
249        XFocusChangeEvent xfe = xev.get_xfocus();
250
251        // If we somehow received focus events forward it instead to proxy
252        // FIXME: Shouldn't we instead check for inferrior?
253        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
254            focusLog.finer("Received focus event on shell: " + xfe);
255        }
256//         focusProxy.xRequestFocus();
257   }
258
259/***************************************************************************************
260 *                             I N S E T S   C O D E
261 **************************************************************************************/
262
263    protected boolean isInitialReshape() {
264        return false;
265    }
266
267    private static Insets difference(Insets i1, Insets i2) {
268        return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right);
269    }
270
271    private static boolean isNull(Insets i) {
272        return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0);
273    }
274
275    private static Insets copy(Insets i) {
276        return new Insets(i.top, i.left, i.bottom, i.right);
277    }
278
279    private Insets copyAndScaleDown(Insets i) {
280        return new Insets(scaleDown(i.top), scaleDown(i.left),
281                          scaleDown(i.bottom), scaleDown(i.right));
282    }
283
284
285    // insets which we get from WM (e.g from _NET_FRAME_EXTENTS)
286    private Insets wm_set_insets;
287
288    private Insets getWMSetInsets(XAtom changedAtom) {
289        if (isEmbedded()) {
290            return null;
291        }
292
293        if (wm_set_insets != null) {
294            return wm_set_insets;
295        }
296
297        if (changedAtom == null) {
298            wm_set_insets = XWM.getInsetsFromExtents(getWindow());
299        } else {
300            wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom);
301        }
302
303        if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
304            insLog.finer("FRAME_EXTENTS: {0}", wm_set_insets);
305        }
306
307        if (wm_set_insets != null) {
308            wm_set_insets = copyAndScaleDown(wm_set_insets);
309        }
310        return wm_set_insets;
311    }
312
313    private void resetWMSetInsets() {
314        if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) {
315            currentInsets = new Insets(0, 0, 0, 0);
316            wm_set_insets = null;
317        } else {
318            insets_corrected = false;
319        }
320    }
321
322    public void handlePropertyNotify(XEvent xev) {
323        super.handlePropertyNotify(xev);
324
325        XPropertyEvent ev = xev.get_xproperty();
326        if( !insets_corrected && isReparented() &&
327                                         XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
328            int state = XWM.getWM().getState(this);
329            if ((state & Frame.MAXIMIZED_BOTH) ==  Frame.MAXIMIZED_BOTH) {
330                // Stop ignoring ConfigureNotify because no extents will be sent
331                // by WM for initially maximized decorated window.
332                // Re-request window bounds to ensure actual dimensions and
333                // notify the target with the initial size.
334                insets_corrected = true;
335                XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
336                                                             getWindow(), 0, 0);
337            }
338        }
339        if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom()
340            || ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
341        {
342            if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) {
343                getWMSetInsets(XAtom.get(ev.get_atom()));
344            } else {
345                if (!isReparented()) {
346                    return;
347                }
348                wm_set_insets = null;
349                Insets in = getWMSetInsets(XAtom.get(ev.get_atom()));
350                if (isNull(in)) {
351                    return;
352                }
353                if (!isEmbedded() && !isTargetUndecorated()) {
354                    lastKnownInsets.put(getClass(), in);
355                }
356                if (!in.equals(dimensions.getInsets())) {
357                    if (insets_corrected || isMaximized()) {
358                        currentInsets = in;
359                        insets_corrected = true;
360                        // insets were changed by WM. To handle this situation
361                        // re-request window bounds because the current
362                        // dimensions may be not actual as well.
363                        XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
364                                                             getWindow(), 0, 0);
365                    } else {
366                        // recalculate dimensions when window is just created
367                        // and the initially guessed insets were wrong
368                        handleCorrectInsets(in);
369                    }
370                } else if (!insets_corrected || !dimensions.isClientSizeSet()) {
371                    insets_corrected = true;
372                    // initial insets were guessed correctly. Re-request
373                    // frame bounds because they may be changed by WM if the
374                    // initial window position overlapped desktop's toolbars.
375                    // This should initiate the final ConfigureNotify upon which
376                    // the target will be notified with the final size.
377                    XlibWrapper.XConfigureWindow(XToolkit.getDisplay(),
378                                                             getWindow(), 0, 0);
379                }
380            }
381        }
382    }
383
384    long reparent_serial = 0;
385
386    public void handleReparentNotifyEvent(XEvent xev) {
387        XReparentEvent  xe = xev.get_xreparent();
388        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
389            insLog.fine(xe.toString());
390        }
391        reparent_serial = xe.get_serial();
392        long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
393
394        if (isEmbedded()) {
395            setReparented(true);
396            insets_corrected = true;
397            return;
398        }
399        if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) {
400            setReparented(true);
401            insets_corrected = true;
402            reshape(dimensions, SET_SIZE, false);
403        } else if (xe.get_parent() == root) {
404            configure_seen = false;
405            insets_corrected = false;
406
407            /*
408             * We can be repareted to root for two reasons:
409             *   . setVisible(false)
410             *   . WM exited
411             */
412            if (isVisible()) { /* WM exited */
413                /* Work around 4775545 */
414                XWM.getWM().unshadeKludge(this);
415                insLog.fine("- WM exited");
416            } else {
417                insLog.fine(" - reparent due to hide");
418            }
419        } else { /* reparented to WM frame, figure out our insets */
420            setReparented(true);
421            insets_corrected = false;
422            if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
423                return;
424            }
425
426            // Check if we have insets provided by the WM
427            Insets correctWM = getWMSetInsets(null);
428            if (correctWM != null) {
429                if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
430                    insLog.finer("wm-provided insets {0}", correctWM);
431                }
432                // If these insets are equal to our current insets - no actions are necessary
433                Insets dimInsets = dimensions.getInsets();
434                if (correctWM.equals(dimInsets)) {
435                    insLog.finer("Insets are the same as estimated - no additional reshapes necessary");
436                    no_reparent_artifacts = true;
437                    insets_corrected = true;
438                    applyGuessedInsets();
439                    return;
440                }
441            } else {
442                correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent());
443                if (correctWM != null) {
444                    correctWM = copyAndScaleDown(correctWM);
445                }
446
447                if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
448                    if (correctWM != null) {
449                        insLog.finer("correctWM {0}", correctWM);
450                    } else {
451                        insLog.finer("correctWM insets are not available, waiting for configureNotify");
452                    }
453                }
454            }
455
456            if (correctWM != null) {
457                handleCorrectInsets(correctWM);
458            }
459        }
460    }
461
462    private void handleCorrectInsets(Insets correctWM) {
463        /*
464         * Ok, now see if we need adjust window size because
465         * initial insets were wrong (most likely they were).
466         */
467        Insets correction = difference(correctWM, currentInsets);
468        if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
469            insLog.finest("Corrention {0}", correction);
470        }
471        if (!isNull(correction)) {
472            currentInsets = copy(correctWM);
473            applyGuessedInsets();
474
475            //Fix for 6318109: PIT: Min Size is not honored properly when a
476            //smaller size is specified in setSize(), XToolkit
477            //update minimum size hints
478            updateMinSizeHints();
479        }
480        if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
481            insLog.finer("Dimensions before reparent: " + dimensions);
482        }
483        WindowDimensions newDimensions = new WindowDimensions(dimensions);
484        newDimensions.setInsets(getRealInsets());
485        dimensions = newDimensions;
486        insets_corrected = true;
487
488        if (isMaximized()) {
489            return;
490        }
491
492        /*
493         * If this window has been sized by a pack() we need
494         * to keep the interior geometry intact.  Since pack()
495         * computed width and height with wrong insets, we
496         * must adjust the target dimensions appropriately.
497         */
498        if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) {
499            reshape(dimensions, SET_BOUNDS, false);
500        } else {
501            reshape(dimensions, SET_SIZE, false);
502        }
503    }
504
505    void handleMoved(WindowDimensions dims) {
506        Point loc = dims.getLocation();
507        AWTAccessor.getComponentAccessor().setLocation(target, loc.x, loc.y);
508        postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
509    }
510
511
512    private Insets guessInsets() {
513        if (isEmbedded() || isTargetUndecorated()) {
514            return new Insets(0, 0, 0, 0);
515        } else {
516            if (!isNull(currentInsets)) {
517                /* insets were set on wdata by System Properties */
518                return copy(currentInsets);
519            } else {
520                Insets res = getWMSetInsets(null);
521                if (res == null) {
522                    res = XWM.getWM().guessInsets(this);
523                    if (res != null) {
524                        res = copyAndScaleDown(res);
525                    }
526                }
527                return res;
528            }
529        }
530    }
531
532    private void applyGuessedInsets() {
533        Insets guessed = guessInsets();
534        currentInsets = copy(guessed);
535    }
536
537    private Insets getRealInsets() {
538        if (isNull(currentInsets)) {
539            applyGuessedInsets();
540        }
541        return currentInsets;
542    }
543
544    public Insets getInsets() {
545        Insets in = copy(getRealInsets());
546        in.top += getMenuBarHeight();
547        if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
548            insLog.finest("Get insets returns {0}", in);
549        }
550        return in;
551    }
552
553    boolean gravityBug() {
554        return XWM.configureGravityBuggy();
555    }
556
557    // The height of area used to display current active input method
558    int getInputMethodHeight() {
559        return 0;
560    }
561
562    void updateSizeHints(WindowDimensions dims) {
563        Rectangle rec = dims.getClientRect();
564        checkShellRect(rec);
565        updateSizeHints(rec.x, rec.y, rec.width, rec.height);
566    }
567
568    void updateSizeHints() {
569        updateSizeHints(dimensions);
570    }
571
572    // Coordinates are that of the target
573    // Called only on Toolkit thread
574    private void reshape(WindowDimensions newDimensions, int op,
575                        boolean userReshape)
576    {
577        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
578            insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape);
579        }
580        if (userReshape) {
581            // We handle only userReshape == true cases. It means that
582            // if the window manager or any other part of the windowing
583            // system sets inappropriate size for this window, we can
584            // do nothing but accept it.
585            Rectangle newBounds = newDimensions.getBounds();
586            Insets insets = newDimensions.getInsets();
587            // Inherit isClientSizeSet from newDimensions
588            if (newDimensions.isClientSizeSet()) {
589                newBounds = new Rectangle(newBounds.x, newBounds.y,
590                                          newBounds.width - insets.left - insets.right,
591                                          newBounds.height - insets.top - insets.bottom);
592            }
593            newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
594        }
595        if (!isReparented() || !isVisible()) {
596            if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
597                insLog.fine("- not reparented({0}) or not visible({1}), default reshape",
598                       Boolean.valueOf(isReparented()), Boolean.valueOf(visible));
599            }
600
601            // Fix for 6323293.
602            // This actually is needed to preserve compatibility with previous releases -
603            // some of licensees are expecting componentMoved event on invisible one while
604            // its location changes.
605            Point oldLocation = getLocation();
606
607            Point newLocation = new Point(AWTAccessor.getComponentAccessor().getX(target),
608                                          AWTAccessor.getComponentAccessor().getY(target));
609
610            if (!newLocation.equals(oldLocation)) {
611                handleMoved(newDimensions);
612            }
613
614            dimensions = new WindowDimensions(newDimensions);
615            updateSizeHints(dimensions);
616            Rectangle client = dimensions.getClientRect();
617            checkShellRect(client);
618            setShellBounds(client);
619            if (content != null &&
620                !content.getSize().equals(newDimensions.getSize()))
621            {
622                reconfigureContentWindow(newDimensions);
623            }
624            return;
625        }
626
627        updateChildrenSizes();
628        applyGuessedInsets();
629
630        Rectangle shellRect = newDimensions.getClientRect();
631
632        if (gravityBug()) {
633            Insets in = newDimensions.getInsets();
634            shellRect.translate(in.left, in.top);
635        }
636
637        if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) {
638            shellRect.setLocation(0, 0);
639        }
640
641        checkShellRectSize(shellRect);
642        if (!isEmbedded()) {
643            checkShellRectPos(shellRect);
644        }
645
646        op = op & ~NO_EMBEDDED_CHECK;
647
648        if (op == SET_LOCATION) {
649            setShellPosition(shellRect);
650        } else if (isResizable()) {
651            if (op == SET_BOUNDS) {
652                setShellBounds(shellRect);
653            } else {
654                setShellSize(shellRect);
655            }
656        } else {
657            XWM.setShellNotResizable(this, newDimensions, shellRect, true);
658            if (op == SET_BOUNDS) {
659                setShellPosition(shellRect);
660            }
661        }
662
663        reconfigureContentWindow(newDimensions);
664    }
665
666    /**
667     * @param x, y, width, heith - dimensions of the window with insets
668     */
669    private void reshape(int x, int y, int width, int height, int operation,
670                         boolean userReshape)
671    {
672        WindowDimensions dims = new WindowDimensions(dimensions);
673        switch (operation & (~NO_EMBEDDED_CHECK)) {
674          case SET_LOCATION:
675              // Set location always sets bounds location. However, until the window is mapped we
676              // should use client coordinates
677              dims.setLocation(x, y);
678              break;
679          case SET_SIZE:
680              // Set size sets bounds size. However, until the window is mapped we
681              // should use client coordinates
682              dims.setSize(width, height);
683              break;
684          case SET_CLIENT_SIZE: {
685              // Sets client rect size. Width and height contain insets.
686              Insets in = currentInsets;
687              width -= in.left+in.right;
688              height -= in.top+in.bottom;
689              dims.setClientSize(width, height);
690              break;
691          }
692          case SET_BOUNDS:
693          default:
694              dims.setLocation(x, y);
695              dims.setSize(width, height);
696              break;
697        }
698        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
699            insLog.fine("For the operation {0} new dimensions are {1}",
700                        operationToString(operation), dims);
701        }
702
703        reshape(dims, operation, userReshape);
704    }
705
706    // This method gets overriden in XFramePeer & XDialogPeer.
707    abstract boolean isTargetUndecorated();
708
709    /**
710     * @see java.awt.peer.ComponentPeer#setBounds
711     */
712    public void setBounds(int x, int y, int width, int height, int op) {
713        // TODO: Rewrite with WindowDimensions
714        XToolkit.awtLock();
715        try {
716            reshape(x, y, width, height, op, true);
717        } finally {
718            XToolkit.awtUnlock();
719        }
720        validateSurface();
721    }
722
723    // Coordinates are that of the shell
724    void reconfigureContentWindow(WindowDimensions dims) {
725        if (content == null) {
726            insLog.fine("WARNING: Content window is null");
727            return;
728        }
729        content.setContentBounds(dims);
730    }
731
732    boolean no_reparent_artifacts = false;
733    public void handleConfigureNotifyEvent(XEvent xev) {
734        if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM && !insets_corrected) {
735            return;
736        }
737        assert (SunToolkit.isAWTLockHeldByCurrentThread());
738        XConfigureEvent xe = xev.get_xconfigure();
739        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
740            insLog.fine("Configure notify {0}", xe);
741        }
742
743        // XXX: should really only consider synthetic events, but
744        if (isReparented()) {
745            configure_seen = true;
746        }
747
748        if (!isMaximized()
749            && (xe.get_serial() == reparent_serial || xe.get_window() != getShell())
750            && !no_reparent_artifacts)
751        {
752            insLog.fine("- reparent artifact, skipping");
753            return;
754        }
755        no_reparent_artifacts = false;
756
757        /**
758         * When there is a WM we receive some CN before being visible and after.
759         * We should skip all CN which are before being visible, because we assume
760         * the gravity is in action while it is not yet.
761         *
762         * When there is no WM we receive CN only _before_ being visible.
763         * We should process these CNs.
764         */
765        if (!isVisible() && XWM.getWMID() != XWM.NO_WM) {
766            insLog.fine(" - not visible, skipping");
767            return;
768        }
769
770        /*
771         * Some window managers configure before we are reparented and
772         * the send event flag is set! ugh... (Enlighetenment for one,
773         * possibly MWM as well).  If we haven't been reparented yet
774         * this is just the WM shuffling us into position.  Ignore
775         * it!!!! or we wind up in a bogus location.
776         */
777        int runningWM = XWM.getWMID();
778        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
779            insLog.fine("reparented={0}, visible={1}, WM={2}, decorations={3}",
780                        isReparented(), isVisible(), runningWM, getDecorations());
781        }
782        if (!isReparented() && isVisible() && runningWM != XWM.NO_WM
783                &&  !XWM.isNonReparentingWM()
784                && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
785            insLog.fine("- visible but not reparented, skipping");
786            return;
787        }
788        //Last chance to correct insets
789        if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
790            long parent = XlibUtil.getParentWindow(window);
791            Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null;
792            if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
793                if (correctWM != null) {
794                    insLog.finer("Configure notify - insets : " + correctWM);
795                } else {
796                    insLog.finer("Configure notify - insets are still not available");
797                }
798            }
799            if (correctWM != null) {
800                handleCorrectInsets(copyAndScaleDown(correctWM));
801            } else {
802                //Only one attempt to correct insets is made (to lower risk)
803                //if insets are still not available we simply set the flag
804                insets_corrected = true;
805            }
806        }
807
808        updateChildrenSizes();
809
810        Point newLocation = getNewLocation(xe, currentInsets.left, currentInsets.top);
811        WindowDimensions newDimensions =
812                new WindowDimensions(newLocation,
813                                     new Dimension(scaleDown(xe.get_width()),
814                                                   scaleDown(xe.get_height())),
815                                     copy(currentInsets), true);
816
817        if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
818            insLog.finer("Insets are {0}, new dimensions {1}",
819                     currentInsets, newDimensions);
820        }
821
822        checkIfOnNewScreen(newDimensions.getBounds());
823
824        Point oldLocation = getLocation();
825        dimensions = newDimensions;
826        if (!newLocation.equals(oldLocation)) {
827            handleMoved(newDimensions);
828        }
829        reconfigureContentWindow(newDimensions);
830        updateChildrenSizes();
831
832        repositionSecurityWarning();
833    }
834
835    private void checkShellRectSize(Rectangle shellRect) {
836        shellRect.width = Math.max(MIN_SIZE, shellRect.width);
837        shellRect.height = Math.max(MIN_SIZE, shellRect.height);
838    }
839
840    private void checkShellRectPos(Rectangle shellRect) {
841        int wm = XWM.getWMID();
842        if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) {
843            if (shellRect.x == 0 && shellRect.y == 0) {
844                shellRect.x = shellRect.y = 1;
845            }
846        }
847    }
848
849    private void checkShellRect(Rectangle shellRect) {
850        checkShellRectSize(shellRect);
851        checkShellRectPos(shellRect);
852    }
853
854    private void setShellBounds(Rectangle rec) {
855        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
856            insLog.fine("Setting shell bounds on " + this + " to " + rec);
857        }
858        updateSizeHints(rec.x, rec.y, rec.width, rec.height);
859        XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getShell(),
860                                      scaleUp(rec.x), scaleUp(rec.y),
861                                      scaleUp(rec.width), scaleUp(rec.height));
862    }
863
864    private void setShellSize(Rectangle rec) {
865        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
866            insLog.fine("Setting shell size on " + this + " to " + rec);
867        }
868        updateSizeHints(rec.x, rec.y, rec.width, rec.height);
869        XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(),
870                                  scaleUp(rec.width), scaleUp(rec.height));
871    }
872
873    private void setShellPosition(Rectangle rec) {
874        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
875            insLog.fine("Setting shell position on " + this + " to " + rec);
876        }
877        updateSizeHints(rec.x, rec.y, rec.width, rec.height);
878        XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(),
879                                scaleUp(rec.x), scaleUp(rec.y));
880    }
881
882    public void setResizable(boolean resizable) {
883        XToolkit.awtLock();
884        try {
885            int fs = winAttr.functions;
886            if (!isResizable() && resizable) {
887                resetWMSetInsets();
888                if (!isEmbedded()) {
889                    setReparented(false);
890                }
891                winAttr.isResizable = resizable;
892                if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
893                    fs &= ~(MWMConstants.MWM_FUNC_RESIZE
894                          | MWMConstants.MWM_FUNC_MAXIMIZE);
895                } else {
896                    fs |= (MWMConstants.MWM_FUNC_RESIZE
897                         | MWMConstants.MWM_FUNC_MAXIMIZE);
898                }
899                winAttr.functions = fs;
900                XWM.setShellResizable(this);
901            } else if (isResizable() && !resizable) {
902                resetWMSetInsets();
903                if (!isEmbedded()) {
904                    setReparented(false);
905                }
906                winAttr.isResizable = resizable;
907                if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
908                    fs |= (MWMConstants.MWM_FUNC_RESIZE
909                         | MWMConstants.MWM_FUNC_MAXIMIZE);
910                } else {
911                    fs &= ~(MWMConstants.MWM_FUNC_RESIZE
912                          | MWMConstants.MWM_FUNC_MAXIMIZE);
913                }
914                winAttr.functions = fs;
915                XWM.setShellNotResizable(this, dimensions,
916                        XWM.getWMID() == XWM.UNITY_COMPIZ_WM && configure_seen ?
917                        dimensions.getScreenBounds() :
918                        dimensions.getBounds(), false);
919            }
920        } finally {
921            XToolkit.awtUnlock();
922        }
923    }
924
925    Rectangle getShellBounds() {
926        return dimensions.getClientRect();
927    }
928
929    public Rectangle getBounds() {
930        return dimensions.getBounds();
931    }
932
933    public Dimension getSize() {
934        return dimensions.getSize();
935    }
936
937    public int getX() {
938        return dimensions.getLocation().x;
939    }
940
941    public int getY() {
942        return dimensions.getLocation().y;
943    }
944
945    public Point getLocation() {
946        return dimensions.getLocation();
947    }
948
949    public int getAbsoluteX() {
950        // NOTE: returning this peer's location which is shell location
951        return dimensions.getScreenBounds().x;
952    }
953
954    public int getAbsoluteY() {
955        // NOTE: returning this peer's location which is shell location
956        return dimensions.getScreenBounds().y;
957    }
958
959    public int getWidth() {
960        return getSize().width;
961    }
962
963    public int getHeight() {
964        return getSize().height;
965    }
966
967    public final WindowDimensions getDimensions() {
968        return dimensions;
969    }
970
971    public Point getLocationOnScreen() {
972        XToolkit.awtLock();
973        try {
974            if (configure_seen) {
975                return toGlobal(0,0);
976            }
977        } finally {
978            XToolkit.awtUnlock();
979        }
980        Point location = target.getLocation();
981        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
982            insLog.fine("getLocationOnScreen {0} not reparented: {1} ",
983                        this, location);
984        }
985        return location;
986    }
987
988
989/***************************************************************************************
990 *              END            OF             I N S E T S   C O D E
991 **************************************************************************************/
992
993    protected boolean isEventDisabled(XEvent e) {
994        switch (e.get_type()) {
995            // Do not generate MOVED/RESIZED events since we generate them by ourselves
996          case XConstants.ConfigureNotify:
997              return true;
998          case XConstants.EnterNotify:
999          case XConstants.LeaveNotify:
1000              // Disable crossing event on outer borders of Frame so
1001              // we receive only one set of cross notifications(first set is from content window)
1002              return true;
1003          default:
1004              return super.isEventDisabled(e);
1005        }
1006    }
1007
1008    int getDecorations() {
1009        return winAttr.decorations;
1010    }
1011
1012    int getFunctions() {
1013        return winAttr.functions;
1014    }
1015
1016    public void setVisible(boolean vis) {
1017        if (log.isLoggable(PlatformLogger.Level.FINER)) {
1018            log.finer("Setting {0} to visible {1}", this, Boolean.valueOf(vis));
1019        }
1020        if (vis && !isVisible()) {
1021            XWM.setShellDecor(this);
1022            super.setVisible(vis);
1023            if (winAttr.isResizable) {
1024                //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
1025                //We need to update frame's minimum size, not to reset it
1026                XWM.removeSizeHints(this, XUtilConstants.PMaxSize);
1027                updateMinimumSize();
1028            }
1029        } else {
1030            super.setVisible(vis);
1031        }
1032    }
1033
1034    protected void suppressWmTakeFocus(boolean doSuppress) {
1035        XAtomList protocols = getWMProtocols();
1036        if (doSuppress) {
1037            protocols.remove(wm_take_focus);
1038        } else {
1039            protocols.add(wm_take_focus);
1040        }
1041        wm_protocols.setAtomListProperty(this, protocols);
1042    }
1043
1044    public void dispose() {
1045        if (content != null) {
1046            content.destroy();
1047        }
1048        focusProxy.destroy();
1049
1050        if (iconWindow != null) {
1051            iconWindow.destroy();
1052        }
1053
1054        super.dispose();
1055    }
1056
1057    public void handleClientMessage(XEvent xev) {
1058        super.handleClientMessage(xev);
1059        XClientMessageEvent cl = xev.get_xclient();
1060        if ((wm_protocols != null) && (cl.get_message_type() == wm_protocols.getAtom())) {
1061            if (cl.get_data(0) == wm_delete_window.getAtom()) {
1062                handleQuit();
1063            } else if (cl.get_data(0) == wm_take_focus.getAtom()) {
1064                handleWmTakeFocus(cl);
1065            }
1066        }
1067    }
1068
1069    private void handleWmTakeFocus(XClientMessageEvent cl) {
1070        if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1071            focusLog.fine("WM_TAKE_FOCUS on {0}", this);
1072        }
1073
1074        if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) {
1075            // JDK-8159460
1076            Window focusedWindow = XKeyboardFocusManagerPeer.getInstance()
1077                    .getCurrentFocusedWindow();
1078            Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
1079            if (activeWindow != target) {
1080                requestWindowFocus(cl.get_data(1), true);
1081            } else {
1082                WindowEvent we = new WindowEvent(focusedWindow,
1083                        WindowEvent.WINDOW_GAINED_FOCUS);
1084                sendEvent(we);
1085            }
1086        } else {
1087            requestWindowFocus(cl.get_data(1), true);
1088        }
1089    }
1090
1091    /**
1092     * Requests focus to this decorated top-level by requesting X input focus
1093     * to the shell window.
1094     */
1095    protected void requestXFocus(long time, boolean timeProvided) {
1096        // We have proxied focus mechanism - instead of shell the focus is held
1097        // by "proxy" - invisible mapped window. When we want to set X input focus to
1098        // toplevel set it on proxy instead.
1099        if (focusProxy == null) {
1100            if (focusLog.isLoggable(PlatformLogger.Level.WARNING)) {
1101                focusLog.warning("Focus proxy is null for " + this);
1102            }
1103        } else {
1104            if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1105                focusLog.fine("Requesting focus to proxy: " + focusProxy);
1106            }
1107            if (timeProvided) {
1108                focusProxy.xRequestFocus(time);
1109            } else {
1110                focusProxy.xRequestFocus();
1111            }
1112        }
1113    }
1114
1115    XFocusProxyWindow getFocusProxy() {
1116        return focusProxy;
1117    }
1118
1119    private void handleQuit() {
1120        postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING));
1121    }
1122
1123    final void dumpMe() {
1124        System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height);
1125    }
1126
1127    final void dumpTarget() {
1128        AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
1129        int getWidth = compAccessor.getWidth(target);
1130        int getHeight = compAccessor.getHeight(target);
1131        int getTargetX = compAccessor.getX(target);
1132        int getTargetY = compAccessor.getY(target);
1133        System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight);
1134    }
1135
1136    final void dumpShell() {
1137        dumpWindow("Shell", getShell());
1138    }
1139    final void dumpContent() {
1140        dumpWindow("Content", getContentWindow());
1141    }
1142    final void dumpParent() {
1143        long parent = XlibUtil.getParentWindow(getShell());
1144        if (parent != 0)
1145        {
1146            dumpWindow("Parent", parent);
1147        }
1148        else
1149        {
1150            System.err.println(">>> NO PARENT");
1151        }
1152    }
1153
1154    final void dumpWindow(String id, long window) {
1155        XWindowAttributes pattr = new XWindowAttributes();
1156        try {
1157            XToolkit.awtLock();
1158            try {
1159                int status =
1160                    XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1161                                                     window, pattr.pData);
1162            }
1163            finally {
1164                XToolkit.awtUnlock();
1165            }
1166            System.err.println(">>>> " + id + ": " + pattr.get_x()
1167                               + ", " + pattr.get_y() + ", " + pattr.get_width()
1168                               + ", " + pattr.get_height());
1169        } finally {
1170            pattr.dispose();
1171        }
1172    }
1173
1174    final void dumpAll() {
1175        dumpTarget();
1176        dumpMe();
1177        dumpParent();
1178        dumpShell();
1179        dumpContent();
1180    }
1181
1182    boolean isMaximized() {
1183        return false;
1184    }
1185
1186    @Override
1187    boolean isOverrideRedirect() {
1188        return Window.Type.POPUP.equals(getWindowType());
1189    }
1190
1191    public boolean requestWindowFocus(long time, boolean timeProvided) {
1192        focusLog.fine("Request for decorated window focus");
1193        // If this is Frame or Dialog we can't assure focus request success - but we still can try
1194        // If this is Window and its owner Frame is active we can be sure request succedded.
1195        Window focusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow();
1196        Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
1197
1198        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
1199            focusLog.finer("Current window is: active={0}, focused={1}",
1200                       Boolean.valueOf(target == activeWindow),
1201                       Boolean.valueOf(target == focusedWindow));
1202        }
1203
1204        XWindowPeer toFocus = this;
1205        while (toFocus.nextTransientFor != null) {
1206            toFocus = toFocus.nextTransientFor;
1207        }
1208        if (toFocus == null || !toFocus.focusAllowedFor()) {
1209            // This might change when WM will have property to determine focus policy.
1210            // Right now, because policy is unknown we can't be sure we succedded
1211            return false;
1212        }
1213        if (this == toFocus) {
1214            if (isWMStateNetHidden()) {
1215                focusLog.fine("The window is unmapped, so rejecting the request");
1216                return false;
1217            }
1218            if (target == activeWindow && target != focusedWindow) {
1219                // Happens when an owned window is currently focused
1220                focusLog.fine("Focus is on child window - transferring it back to the owner");
1221                handleWindowFocusInSync(-1);
1222                return true;
1223            }
1224            Window realNativeFocusedWindow = XWindowPeer.getNativeFocusedWindow();
1225            if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
1226                focusLog.finest("Real native focused window: " + realNativeFocusedWindow +
1227                            "\nKFM's focused window: " + focusedWindow);
1228            }
1229
1230            // A workaround for Metacity. See 6522725, 6613426, 7147075.
1231            if (target == realNativeFocusedWindow && XWM.getWMID() == XWM.METACITY_WM) {
1232                if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1233                    focusLog.fine("The window is already natively focused.");
1234                }
1235                return true;
1236            }
1237        }
1238        if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1239            focusLog.fine("Requesting focus to " + (this == toFocus ? "this window" : toFocus));
1240        }
1241
1242        if (timeProvided) {
1243            toFocus.requestXFocus(time);
1244        } else {
1245            toFocus.requestXFocus();
1246        }
1247        return (this == toFocus);
1248    }
1249
1250    XWindowPeer actualFocusedWindow = null;
1251    void setActualFocusedWindow(XWindowPeer actualFocusedWindow) {
1252        synchronized(getStateLock()) {
1253            this.actualFocusedWindow = actualFocusedWindow;
1254        }
1255    }
1256
1257    boolean requestWindowFocus(XWindowPeer actualFocusedWindow,
1258                               long time, boolean timeProvided)
1259    {
1260        setActualFocusedWindow(actualFocusedWindow);
1261        return requestWindowFocus(time, timeProvided);
1262    }
1263    public void handleWindowFocusIn(long serial) {
1264        if (null == actualFocusedWindow) {
1265            super.handleWindowFocusIn(serial);
1266        } else {
1267            /*
1268             * Fix for 6314575.
1269             * If this is a result of clicking on one of the Frame's component
1270             * then 'actualFocusedWindow' shouldn't be focused. A decision of focusing
1271             * it or not should be made after the appropriate Java mouse event (if any)
1272             * is handled by the component where 'actualFocusedWindow' value may be reset.
1273             *
1274             * The fix is based on the empiric fact consisting in that the component
1275             * receives native mouse event nearly at the same time the Frame receives
1276             * WM_TAKE_FOCUS (when FocusIn is generated via XSetInputFocus call) but
1277             * definetely before the Frame gets FocusIn event (when this method is called).
1278             */
1279            postEvent(new InvocationEvent(target, new Runnable() {
1280                public void run() {
1281                    XWindowPeer fw = null;
1282                    synchronized (getStateLock()) {
1283                        fw = actualFocusedWindow;
1284                        actualFocusedWindow = null;
1285                        if (null == fw || !fw.isVisible() || !fw.isFocusableWindow()) {
1286                            fw = XDecoratedPeer.this;
1287                        }
1288                    }
1289                    fw.handleWindowFocusIn_Dispatch();
1290                }
1291            }));
1292        }
1293    }
1294
1295    public void handleWindowFocusOut(Window oppositeWindow, long serial) {
1296        Window actualFocusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow();
1297
1298        // If the actual focused window is not this decorated window then retain it.
1299        if (actualFocusedWindow != null && actualFocusedWindow != target) {
1300            Window owner = XWindowPeer.getDecoratedOwner(actualFocusedWindow);
1301
1302            if (owner != null && owner == target) {
1303                setActualFocusedWindow(AWTAccessor.getComponentAccessor().getPeer(actualFocusedWindow));
1304            }
1305        }
1306        super.handleWindowFocusOut(oppositeWindow, serial);
1307    }
1308}
1309