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