1 /*
2 * Copyright 2002-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package sun.awt.X11;
26
27 import java.awt.*;
28
29 import java.awt.event.ComponentEvent;
30 import java.awt.event.InvocationEvent;
31 import java.awt.event.WindowEvent;
32
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36 import sun.awt.ComponentAccessor;
37 import sun.awt.SunToolkit;
38
39 abstract class XDecoratedPeer extends XWindowPeer {
40 private static final Logger log = Logger.getLogger("sun.awt.X11.XDecoratedPeer");
41 private static final Logger insLog = Logger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
42 private static final Logger focusLog = Logger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
43 private static final Logger iconLog = Logger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
44
45 // Set to true when we get the first ConfigureNotify after being
46 // reparented - indicates that WM has adopted the top-level.
47 boolean configure_seen;
48 boolean insets_corrected;
49
50 XIconWindow iconWindow;
51 WindowDimensions dimensions;
52 XContentWindow content;
53 Insets currentInsets;
54 XFocusProxyWindow focusProxy;
55
56 XDecoratedPeer(Window target) {
57 super(target);
58 }
59
60 XDecoratedPeer(XCreateWindowParams params) {
61 super(params);
62 }
63
64 public long getShell() {
65 return window;
66 }
67
68 public long getContentWindow() {
69 return (content == null) ? window : content.getWindow();
70 }
71
72 void preInit(XCreateWindowParams params) {
73 super.preInit(params);
74 winAttr.initialFocus = true;
75
76 currentInsets = new Insets(0,0,0,0);
77 applyGuessedInsets();
78
79 Rectangle bounds = (Rectangle)params.get(BOUNDS);
80 dimensions = new WindowDimensions(bounds, getRealInsets(), false);
81 params.put(BOUNDS, dimensions.getClientRect());
82 insLog.log(Level.FINE, "Initial dimensions {0}", new Object[] { dimensions });
83
84 // Deny default processing of these events on the shell - proxy will take care of
85 // them instead
86 Long eventMask = (Long)params.get(EVENT_MASK);
87 params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask)));
88 }
89
90 void postInit(XCreateWindowParams params) {
91 super.postInit(params);
92 // The lines that follow need to be in a postInit, so they
93 // happen after the X window is created.
94 initResizability();
95 updateSizeHints(dimensions);
96 XWM.requestWMExtents(getWindow());
97
98 content = XContentWindow.createContent(this);
99
100 if (warningWindow != null) {
101 warningWindow.toFront();
102 }
103 focusProxy = createFocusProxy();
104 }
105
106 void setIconHints(java.util.List<XIconInfo> icons) {
107 if (!XWM.getWM().setNetWMIcon(this, icons)) {
108 if (icons.size() > 0) {
109 if (iconWindow == null) {
110 iconWindow = new XIconWindow(this);
111 }
112 iconWindow.setIconImages(icons);
113 }
114 }
115 }
116
117 public void updateMinimumSize() {
118 super.updateMinimumSize();
119 updateMinSizeHints();
120 }
121
122 private void updateMinSizeHints() {
123 if (isResizable()) {
124 Dimension minimumSize = getTargetMinimumSize();
125 if (minimumSize != null) {
126 Insets insets = getRealInsets();
127 int minWidth = minimumSize.width - insets.left - insets.right;
128 int minHeight = minimumSize.height - insets.top - insets.bottom;
129 if (minWidth < 0) minWidth = 0;
130 if (minHeight < 0) minHeight = 0;
131 setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),
132 getX(), getY(), minWidth, minHeight);
133 if (isVisible()) {
134 Rectangle bounds = getShellBounds();
135 int nw = (bounds.width < minWidth) ? minWidth : bounds.width;
136 int nh = (bounds.height < minHeight) ? minHeight : bounds.height;
137 if (nw != bounds.width || nh != bounds.height) {
138 setShellSize(new Rectangle(0, 0, nw, nh));
139 }
140 }
141 } else {
142 boolean isMinSizeSet = isMinSizeSet();
143 XWM.removeSizeHints(this, XUtilConstants.PMinSize);
144 /* Some WMs need remap to redecorate the window */
145 if (isMinSizeSet && isShowing() && XWM.needRemap(this)) {
146 /*
214 // NOTE: This method may be called by privileged threads.
215 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
216 public void handleDeiconify() {
217 postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
218 }
219
220 public void handleFocusEvent(XEvent xev) {
221 super.handleFocusEvent(xev);
222 XFocusChangeEvent xfe = xev.get_xfocus();
223
224 // If we somehow received focus events forward it instead to proxy
225 // FIXME: Shouldn't we instead check for inferrior?
226 focusLog.finer("Received focus event on shell: " + xfe);
227 // focusProxy.xRequestFocus();
228 }
229
230 /***************************************************************************************
231 * I N S E T S C O D E
232 **************************************************************************************/
233
234 protected boolean isInitialReshape() {
235 return false;
236 }
237
238 private static Insets difference(Insets i1, Insets i2) {
239 return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right);
240 }
241
242 private static boolean isNull(Insets i) {
243 return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0);
244 }
245
246 private static Insets copy(Insets i) {
247 return new Insets(i.top, i.left, i.bottom, i.right);
248 }
249
250 // insets which we get from WM (e.g from _NET_FRAME_EXTENTS)
251 private Insets wm_set_insets;
252
253 private Insets getWMSetInsets(XAtom changedAtom) {
254 if (isEmbedded()) {
255 return null;
256 }
257
258 if (wm_set_insets != null) {
259 return wm_set_insets;
260 }
261
262 if (changedAtom == null) {
263 wm_set_insets = XWM.getInsetsFromExtents(getWindow());
264 } else {
265 wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom);
266 }
267
268 insLog.log(Level.FINER, "FRAME_EXTENTS: {0}", new Object[]{wm_set_insets});
269
270 if (wm_set_insets != null) {
271 wm_set_insets = copy(wm_set_insets);
272 }
273 return wm_set_insets;
274 }
275
276 private void resetWMSetInsets() {
277 wm_set_insets = null;
278 }
279
280 public void handlePropertyNotify(XEvent xev) {
281 super.handlePropertyNotify(xev);
282
283 XPropertyEvent ev = xev.get_xproperty();
284 if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom()
285 || ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
286 {
287 getWMSetInsets(XAtom.get(ev.get_atom()));
288 }
289 }
290
291 long reparent_serial = 0;
292
293 public void handleReparentNotifyEvent(XEvent xev) {
294 XReparentEvent xe = xev.get_xreparent();
295 if (insLog.isLoggable(Level.FINE)) insLog.fine(xe.toString());
296 reparent_serial = xe.get_serial();
297 XToolkit.awtLock();
298 try {
299 long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
300
301 if (isEmbedded()) {
302 setReparented(true);
303 insets_corrected = true;
304 return;
305 }
306 Component t = (Component)target;
307 if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) {
308 setReparented(true);
309 insets_corrected = true;
310 reshape(dimensions, SET_SIZE, false);
311 } else if (xe.get_parent() == root) {
312 configure_seen = false;
313 insets_corrected = false;
314
315 /*
316 * We can be repareted to root for two reasons:
317 * . setVisible(false)
318 * . WM exited
319 */
320 if (isVisible()) { /* WM exited */
321 /* Work around 4775545 */
322 XWM.getWM().unshadeKludge(this);
323 insLog.fine("- WM exited");
324 } else {
325 insLog.fine(" - reparent due to hide");
326 }
327 } else { /* reparented to WM frame, figure out our insets */
328 setReparented(true);
329 insets_corrected = false;
330
331 // Check if we have insets provided by the WM
332 Insets correctWM = getWMSetInsets(null);
333 if (correctWM != null) {
334 insLog.log(Level.FINER, "wm-provided insets {0}", new Object[]{correctWM});
335 // If these insets are equal to our current insets - no actions are necessary
336 Insets dimInsets = dimensions.getInsets();
337 if (correctWM.equals(dimInsets)) {
338 insLog.finer("Insets are the same as estimated - no additional reshapes necessary");
339 no_reparent_artifacts = true;
340 insets_corrected = true;
341 applyGuessedInsets();
342 return;
343 }
344 } else {
345 correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent());
346
347 if (correctWM != null) {
348 insLog.log(Level.FINER, "correctWM {0}", new Object[] {correctWM});
349 } else {
350 insLog.log(Level.FINER, "correctWM insets are not available, waiting for configureNotify");
351 }
352 }
353
354 if (correctWM != null) {
355 handleCorrectInsets(correctWM);
356 }
357 }
358 } finally {
359 XToolkit.awtUnlock();
360 }
361 }
362
363 protected void handleCorrectInsets(Insets correctWM) {
364 XToolkit.awtLock();
365 try {
366 /*
367 * Ok, now see if we need adjust window size because
368 * initial insets were wrong (most likely they were).
369 */
370 Insets correction = difference(correctWM, currentInsets);
371 insLog.log(Level.FINEST, "Corrention {0}", new Object[] {correction});
372 if (!isNull(correction)) {
373 currentInsets = copy(correctWM);
374 applyGuessedInsets();
375
376 //Fix for 6318109: PIT: Min Size is not honored properly when a
377 //smaller size is specified in setSize(), XToolkit
378 //update minimum size hints
379 updateMinSizeHints();
380 }
381 if (insLog.isLoggable(Level.FINER)) insLog.finer("Dimensions before reparent: " + dimensions);
382
383 dimensions.setInsets(getRealInsets());
384 insets_corrected = true;
385
386 if (isMaximized()) {
387 return;
388 }
389
390 /*
391 * If this window has been sized by a pack() we need
392 * to keep the interior geometry intact. Since pack()
393 * computed width and height with wrong insets, we
394 * must adjust the target dimensions appropriately.
395 */
396 if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) {
397 reshape(dimensions, SET_BOUNDS, false);
398 } else {
399 reshape(dimensions, SET_SIZE, false);
400 }
401 } finally {
402 XToolkit.awtUnlock();
403 }
404 }
405
406 public void handleMoved(WindowDimensions dims) {
407 Point loc = dims.getLocation();
408 ComponentAccessor.setX((Component)target, loc.x);
409 ComponentAccessor.setY((Component)target, loc.y);
410 postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
411 }
412
413
414 protected Insets guessInsets() {
415 if (isEmbedded() || isTargetUndecorated()) {
416 return new Insets(0, 0, 0, 0);
417 } else {
418 if (!isNull(currentInsets)) {
419 /* insets were set on wdata by System Properties */
420 return copy(currentInsets);
421 } else {
422 Insets res = getWMSetInsets(null);
423 if (res == null) {
424 res = XWM.getWM().guessInsets(this);
425 }
426 return res;
427 }
428 }
429 }
430
431 private void applyGuessedInsets() {
432 Insets guessed = guessInsets();
433 currentInsets = copy(guessed);
434 }
435
436 public void revalidate() {
437 XToolkit.executeOnEventHandlerThread(target, new Runnable() {
438 public void run() {
439 target.invalidate();
440 target.validate();
441 }
442 });
443 }
444
445 Insets getRealInsets() {
446 if (isNull(currentInsets)) {
447 applyGuessedInsets();
448 }
449 return currentInsets;
450 }
451
452 public Insets getInsets() {
453 Insets in = copy(getRealInsets());
454 in.top += getMenuBarHeight();
455 if (insLog.isLoggable(Level.FINEST)) {
456 insLog.log(Level.FINEST, "Get insets returns {0}", new Object[] {in});
457 }
458 return in;
459 }
460
461 boolean gravityBug() {
462 return XWM.configureGravityBuggy();
463 }
464
465 // The height of area used to display current active input method
466 int getInputMethodHeight() {
467 return 0;
468 }
469
470 void updateSizeHints(WindowDimensions dims) {
471 Rectangle rec = dims.getClientRect();
472 checkShellRect(rec);
473 updateSizeHints(rec.x, rec.y, rec.width, rec.height);
474 }
475
476 void updateSizeHints() {
477 updateSizeHints(dimensions);
478 }
479
480 // Coordinates are that of the target
481 // Called only on Toolkit thread
482 public void reshape(WindowDimensions newDimensions, int op,
483 boolean userReshape)
484 {
485 if (insLog.isLoggable(Level.FINE)) {
486 insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape);
487 }
488 if (userReshape) {
489 // We handle only userReshape == true cases. It means that
490 // if the window manager or any other part of the windowing
491 // system sets inappropriate size for this window, we can
492 // do nothing but accept it.
493 Rectangle newBounds = newDimensions.getBounds();
494 Insets insets = newDimensions.getInsets();
495 // Inherit isClientSizeSet from newDimensions
496 if (newDimensions.isClientSizeSet()) {
497 newBounds = new Rectangle(newBounds.x, newBounds.y,
498 newBounds.width - insets.left - insets.right,
499 newBounds.height - insets.top - insets.bottom);
500 }
501 newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
502 }
503 XToolkit.awtLock();
504 try {
505 if (!isReparented() || !isVisible()) {
506 insLog.log(Level.FINE, "- not reparented({0}) or not visible({1}), default reshape",
507 new Object[] {Boolean.valueOf(isReparented()), Boolean.valueOf(visible)});
508
509 // Fix for 6323293.
510 // This actually is needed to preserve compatibility with previous releases -
511 // some of licensees are expecting componentMoved event on invisible one while
512 // its location changes.
513 Point oldLocation = getLocation();
514
515 Point newLocation = new Point(ComponentAccessor.getX((Component)target),
516 ComponentAccessor.getY((Component)target));
517
518 if (!newLocation.equals(oldLocation)) {
519 handleMoved(newDimensions);
520 }
521
522 dimensions = new WindowDimensions(newDimensions);
523 updateSizeHints(dimensions);
524 Rectangle client = dimensions.getClientRect();
525 checkShellRect(client);
526 setShellBounds(client);
527 if (content != null &&
528 !content.getSize().equals(newDimensions.getSize()))
529 {
530 reconfigureContentWindow(newDimensions);
531 }
532 return;
533 }
534
535 int wm = XWM.getWMID();
536 updateChildrenSizes();
537 applyGuessedInsets();
538
539 Rectangle shellRect = newDimensions.getClientRect();
540
541 if (gravityBug()) {
542 Insets in = newDimensions.getInsets();
543 shellRect.translate(in.left, in.top);
544 }
545
546 if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) {
547 shellRect.setLocation(0, 0);
548 }
549
550 checkShellRectSize(shellRect);
551 if (!isEmbedded()) {
552 checkShellRectPos(shellRect);
553 }
554
555 op = op & ~NO_EMBEDDED_CHECK;
556
557 if (op == SET_LOCATION) {
558 setShellPosition(shellRect);
559 } else if (isResizable()) {
560 if (op == SET_BOUNDS) {
561 setShellBounds(shellRect);
562 } else {
563 setShellSize(shellRect);
564 }
565 } else {
566 XWM.setShellNotResizable(this, newDimensions, shellRect, true);
567 if (op == SET_BOUNDS) {
568 setShellPosition(shellRect);
569 }
570 }
571
572 reconfigureContentWindow(newDimensions);
573 } finally {
574 XToolkit.awtUnlock();
575 }
576 }
577
578 /**
579 * @param x, y, width, heith - dimensions of the window with insets
580 */
581 private void reshape(int x, int y, int width, int height, int operation,
582 boolean userReshape)
583 {
584 Rectangle newRec;
585 boolean setClient = false;
586 WindowDimensions dims = new WindowDimensions(dimensions);
587 switch (operation & (~NO_EMBEDDED_CHECK)) {
588 case SET_LOCATION:
589 // Set location always sets bounds location. However, until the window is mapped we
590 // should use client coordinates
591 dims.setLocation(x, y);
592 break;
593 case SET_SIZE:
594 // Set size sets bounds size. However, until the window is mapped we
595 // should use client coordinates
596 dims.setSize(width, height);
597 break;
598 case SET_CLIENT_SIZE: {
599 // Sets client rect size. Width and height contain insets.
600 Insets in = currentInsets;
601 width -= in.left+in.right;
602 height -= in.top+in.bottom;
603 dims.setClientSize(width, height);
604 break;
605 }
606 case SET_BOUNDS:
607 default:
608 dims.setLocation(x, y);
609 dims.setSize(width, height);
610 break;
611 }
612 if (insLog.isLoggable(Level.FINE)) insLog.log(Level.FINE, "For the operation {0} new dimensions are {1}",
613 new Object[] {operationToString(operation), dims});
614
615 reshape(dims, operation, userReshape);
616 }
617
618 // This method gets overriden in XFramePeer & XDialogPeer.
619 abstract boolean isTargetUndecorated();
620
621 /**
622 * @see java.awt.peer.ComponentPeer#setBounds
623 */
624 public void setBounds(int x, int y, int width, int height, int op) {
625 // TODO: Rewrite with WindowDimensions
626 reshape(x, y, width, height, op, true);
627 validateSurface();
628 }
629
630 // Coordinates are that of the shell
631 void reconfigureContentWindow(WindowDimensions dims) {
632 if (content == null) {
633 insLog.fine("WARNING: Content window is null");
634 return;
635 }
636 content.setContentBounds(dims);
637 }
638
639 boolean no_reparent_artifacts = false;
640 public void handleConfigureNotifyEvent(XEvent xev) {
641 assert (SunToolkit.isAWTLockHeldByCurrentThread());
642 XConfigureEvent xe = xev.get_xconfigure();
643 insLog.log(Level.FINE, "Configure notify {0}", new Object[] {xe});
644
645 // XXX: should really only consider synthetic events, but
646 if (isReparented()) {
647 configure_seen = true;
648 }
649
650 if (!isMaximized()
651 && (xe.get_serial() == reparent_serial || xe.get_window() != getShell())
652 && !no_reparent_artifacts)
653 {
654 insLog.fine("- reparent artifact, skipping");
655 return;
656 }
657 no_reparent_artifacts = false;
658
659 /**
660 * When there is a WM we receive some CN before being visible and after.
661 * We should skip all CN which are before being visible, because we assume
662 * the gravity is in action while it is not yet.
663 *
664 * When there is no WM we receive CN only _before_ being visible.
665 * We should process these CNs.
666 */
667 if (!isVisible() && XWM.getWMID() != XWM.NO_WM) {
668 insLog.fine(" - not visible, skipping");
669 return;
670 }
671
672 /*
673 * Some window managers configure before we are reparented and
674 * the send event flag is set! ugh... (Enlighetenment for one,
675 * possibly MWM as well). If we haven't been reparented yet
676 * this is just the WM shuffling us into position. Ignore
677 * it!!!! or we wind up in a bogus location.
678 */
679 int runningWM = XWM.getWMID();
680 if (insLog.isLoggable(Level.FINE)) {
681 insLog.log(Level.FINE, "reparented={0}, visible={1}, WM={2}, decorations={3}",
682 new Object[] {isReparented(), isVisible(), runningWM, getDecorations()});
683 }
684 if (!isReparented() && isVisible() && runningWM != XWM.NO_WM
685 && !XWM.isNonReparentingWM()
686 && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
687 insLog.fine("- visible but not reparented, skipping");
688 return;
689 }
690 //Last chance to correct insets
691 if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
692 long parent = XlibUtil.getParentWindow(window);
693 Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null;
694 if (insLog.isLoggable(Level.FINER)) {
695 if (correctWM != null) {
696 insLog.finer("Configure notify - insets : " + correctWM);
697 } else {
698 insLog.finer("Configure notify - insets are still not available");
699 }
700 }
701 if (correctWM != null) {
702 handleCorrectInsets(correctWM);
703 } else {
704 //Only one attempt to correct insets is made (to lower risk)
705 //if insets are still not available we simply set the flag
706 insets_corrected = true;
707 }
708 }
709
710 updateChildrenSizes();
711
712 // Bounds of the window
713 Rectangle targetBounds = new Rectangle(ComponentAccessor.getX((Component)target),
714 ComponentAccessor.getY((Component)target),
715 ComponentAccessor.getWidth((Component)target),
716 ComponentAccessor.getHeight((Component)target));
717
718 Point newLocation = targetBounds.getLocation();
719 if (xe.get_send_event() || runningWM == XWM.NO_WM || XWM.isNonReparentingWM()) {
720 // Location, Client size + insets
721 newLocation = new Point(xe.get_x() - currentInsets.left, xe.get_y() - currentInsets.top);
722 } else {
723 // CDE/MWM/Metacity/Sawfish bug: if shell is resized using
724 // top or left border, we don't receive synthetic
725 // ConfigureNotify, only the one from X with zero
726 // coordinates. This is the workaround to get real
727 // location, 6261336
728 switch (XWM.getWMID()) {
729 case XWM.CDE_WM:
730 case XWM.MOTIF_WM:
731 case XWM.METACITY_WM:
732 case XWM.SAWFISH_WM:
733 {
734 Point xlocation = queryXLocation();
735 if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "New X location: {0}", new Object[]{xlocation});
736 if (xlocation != null) {
737 newLocation = xlocation;
738 }
739 break;
740 }
741 default:
742 break;
743 }
744 }
745
746 WindowDimensions newDimensions =
747 new WindowDimensions(newLocation,
748 new Dimension(xe.get_width(), xe.get_height()),
749 copy(currentInsets),
750 true);
751
752 insLog.log(Level.FINER, "Insets are {0}, new dimensions {1}",
753 new Object[] {currentInsets, newDimensions});
754
755 checkIfOnNewScreen(newDimensions.getBounds());
756
757 Point oldLocation = getLocation();
758 dimensions = newDimensions;
759 if (!newLocation.equals(oldLocation)) {
760 handleMoved(newDimensions);
761 }
762 reconfigureContentWindow(newDimensions);
763 updateChildrenSizes();
764
765 repositionSecurityWarning();
766 }
767
768 private void checkShellRectSize(Rectangle shellRect) {
769 if (shellRect.width < 0) {
770 shellRect.width = 1;
771 }
772 if (shellRect.height < 0) {
773 shellRect.height = 1;
774 }
775 }
776
777 private void checkShellRectPos(Rectangle shellRect) {
778 int wm = XWM.getWMID();
779 if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) {
780 if (shellRect.x == 0 && shellRect.y == 0) {
781 shellRect.x = shellRect.y = 1;
782 }
783 }
784 }
785
815 }
816 public void setShellPosition(Rectangle rec) {
817 if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell position on " +
818 this + " to " + rec);
819 XToolkit.awtLock();
820 try {
821 updateSizeHints(rec.x, rec.y, rec.width, rec.height);
822 XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y);
823 }
824 finally {
825 XToolkit.awtUnlock();
826 }
827 }
828
829 void initResizability() {
830 setResizable(winAttr.initialResizability);
831 }
832 public void setResizable(boolean resizable) {
833 int fs = winAttr.functions;
834 if (!isResizable() && resizable) {
835 currentInsets = new Insets(0, 0, 0, 0);
836 resetWMSetInsets();
837 if (!isEmbedded()) {
838 setReparented(false);
839 }
840 winAttr.isResizable = resizable;
841 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
842 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
843 } else {
844 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
845 }
846 winAttr.functions = fs;
847 XWM.setShellResizable(this);
848 } else if (isResizable() && !resizable) {
849 currentInsets = new Insets(0, 0, 0, 0);
850 resetWMSetInsets();
851 if (!isEmbedded()) {
852 setReparented(false);
853 }
854 winAttr.isResizable = resizable;
855 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
856 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
857 } else {
858 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
859 }
860 winAttr.functions = fs;
861 XWM.setShellNotResizable(this, dimensions, dimensions.getBounds(), false);
862 }
863 }
864
865 Rectangle getShellBounds() {
866 return dimensions.getClientRect();
867 }
868
869 public Rectangle getBounds() {
870 return dimensions.getBounds();
871 }
872
873 public Dimension getSize() {
894 public int getAbsoluteY() {
895 // NOTE: returning this peer's location which is shell location
896 return dimensions.getScreenBounds().y;
897 }
898
899 public int getWidth() {
900 return getSize().width;
901 }
902
903 public int getHeight() {
904 return getSize().height;
905 }
906
907 final public WindowDimensions getDimensions() {
908 return dimensions;
909 }
910
911 public Point getLocationOnScreen() {
912 XToolkit.awtLock();
913 try {
914 if (configure_seen) {
915 return toGlobal(0,0);
916 } else {
917 Point location = target.getLocation();
918 if (insLog.isLoggable(Level.FINE))
919 insLog.log(Level.FINE, "getLocationOnScreen {0} not reparented: {1} ",
920 new Object[] {this, location});
921 return location;
922 }
923 } finally {
924 XToolkit.awtUnlock();
925 }
926 }
927
928
929 /***************************************************************************************
930 * END OF I N S E T S C O D E
931 **************************************************************************************/
932
933 protected boolean isEventDisabled(XEvent e) {
934 switch (e.get_type()) {
935 // Do not generate MOVED/RESIZED events since we generate them by ourselves
936 case XConstants.ConfigureNotify:
937 return true;
938 case XConstants.EnterNotify:
939 case XConstants.LeaveNotify:
940 // Disable crossing event on outer borders of Frame so
1040 final void dumpMe() {
1041 System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height);
1042 }
1043
1044 final void dumpTarget() {
1045 int getWidth = ComponentAccessor.getWidth((Component)target);
1046 int getHeight = ComponentAccessor.getHeight((Component)target);
1047 int getTargetX = ComponentAccessor.getX((Component)target);
1048 int getTargetY = ComponentAccessor.getY((Component)target);
1049 System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight);
1050 }
1051
1052 final void dumpShell() {
1053 dumpWindow("Shell", getShell());
1054 }
1055 final void dumpContent() {
1056 dumpWindow("Content", getContentWindow());
1057 }
1058 final void dumpParent() {
1059 long parent = XlibUtil.getParentWindow(getShell());
1060 if (parent != 0)
1061 {
1062 dumpWindow("Parent", parent);
1063 }
1064 else
1065 {
1066 System.err.println(">>> NO PARENT");
1067 }
1068 }
1069
1070 final void dumpWindow(String id, long window) {
1071 XWindowAttributes pattr = new XWindowAttributes();
1072 try {
1073 XToolkit.awtLock();
1074 try {
1075 int status =
1076 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1077 window, pattr.pData);
1078 }
1079 finally {
1080 XToolkit.awtUnlock();
1083 + ", " + pattr.get_y() + ", " + pattr.get_width()
1084 + ", " + pattr.get_height());
1085 } finally {
1086 pattr.dispose();
1087 }
1088 }
1089
1090 final void dumpAll() {
1091 dumpTarget();
1092 dumpMe();
1093 dumpParent();
1094 dumpShell();
1095 dumpContent();
1096 }
1097
1098 boolean isMaximized() {
1099 return false;
1100 }
1101
1102 boolean isOverrideRedirect() {
1103 // return false;
1104 return ((XToolkit)Toolkit.getDefaultToolkit()).isOverrideRedirect((Window)target);
1105 }
1106
1107 public boolean requestWindowFocus(long time, boolean timeProvided) {
1108 focusLog.fine("Request for decorated window focus");
1109 // If this is Frame or Dialog we can't assure focus request success - but we still can try
1110 // If this is Window and its owner Frame is active we can be sure request succedded.
1111 Window focusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
1112 Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
1113
1114 focusLog.log(Level.FINER, "Current window is: active={0}, focused={1}",
1115 new Object[]{ Boolean.valueOf(target == activeWindow),
1116 Boolean.valueOf(target == focusedWindow)});
1117
1118 XWindowPeer toFocus = this;
1119 while (toFocus.nextTransientFor != null) {
1120 toFocus = toFocus.nextTransientFor;
1121 }
1122 if (toFocus == null || !toFocus.focusAllowedFor()) {
1123 // This might change when WM will have property to determine focus policy.
|
1 /*
2 * Copyright 2002-2009 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package sun.awt.X11;
26
27 import java.awt.*;
28
29 import java.awt.event.ComponentEvent;
30 import java.awt.event.InvocationEvent;
31 import java.awt.event.WindowEvent;
32
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36 import sun.awt.AWTAccessor;
37 import sun.awt.ComponentAccessor;
38 import sun.awt.SunToolkit;
39
40 abstract class XDecoratedPeer extends XWindowPeer {
41 private static final Logger log = Logger.getLogger("sun.awt.X11.XDecoratedPeer");
42 private static final Logger insLog = Logger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
43 private static final Logger focusLog = Logger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
44 private static final Logger iconLog = Logger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
45
46 XIconWindow iconWindow;
47
48 /**
49 * The dimensions of the window.
50 *
51 * The entity encapsulates information about the bounds and the insets of
52 * the window. The value is initialized in the preInit() method with the
53 * bounds of the window as known on the shared level. Further updates
54 * should be performed with the reportReshape() method only.
55 */
56 protected WindowDimensions dimensions;
57
58 XContentWindow content;
59 XFocusProxyWindow focusProxy;
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 Rectangle bounds = (Rectangle)params.get(BOUNDS);
82 dimensions = new WindowDimensions(bounds, getNativeInsets(), false);
83 params.put(BOUNDS, dimensions.getClientRect());
84 insLog.log(Level.FINE, "Initial dimensions {0}", new Object[] { dimensions });
85
86 // Deny default processing of these events on the shell - proxy will take care of
87 // them instead
88 Long eventMask = (Long)params.get(EVENT_MASK);
89 params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask)));
90 }
91
92 void postInit(XCreateWindowParams params) {
93 super.postInit(params);
94 // The lines that follow need to be in a postInit, so they
95 // happen after the X window is created.
96 initResizability();
97 updateSizeHints(dimensions);
98
99 content = XContentWindow.createContent(this);
100
101 if (warningWindow != null) {
102 warningWindow.toFront();
103 }
104 focusProxy = createFocusProxy();
105 }
106
107 void setIconHints(java.util.List<XIconInfo> icons) {
108 if (!XWM.getWM().setNetWMIcon(this, icons)) {
109 if (icons.size() > 0) {
110 if (iconWindow == null) {
111 iconWindow = new XIconWindow(this);
112 }
113 iconWindow.setIconImages(icons);
114 }
115 }
116 }
117
118 public void updateMinimumSize() {
119 super.updateMinimumSize();
120 updateMinSizeHints(getNativeInsets());
121 }
122
123 private void updateMinSizeHints(Insets insets) {
124 if (isResizable()) {
125 Dimension minimumSize = getTargetMinimumSize();
126 if (minimumSize != null) {
127 int minWidth = minimumSize.width - insets.left - insets.right;
128 int minHeight = minimumSize.height - insets.top - insets.bottom;
129 if (minWidth < 0) minWidth = 0;
130 if (minHeight < 0) minHeight = 0;
131 setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),
132 getX(), getY(), minWidth, minHeight);
133 if (isVisible()) {
134 Rectangle bounds = getShellBounds();
135 int nw = (bounds.width < minWidth) ? minWidth : bounds.width;
136 int nh = (bounds.height < minHeight) ? minHeight : bounds.height;
137 if (nw != bounds.width || nh != bounds.height) {
138 setShellSize(new Rectangle(0, 0, nw, nh));
139 }
140 }
141 } else {
142 boolean isMinSizeSet = isMinSizeSet();
143 XWM.removeSizeHints(this, XUtilConstants.PMinSize);
144 /* Some WMs need remap to redecorate the window */
145 if (isMinSizeSet && isShowing() && XWM.needRemap(this)) {
146 /*
214 // NOTE: This method may be called by privileged threads.
215 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
216 public void handleDeiconify() {
217 postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
218 }
219
220 public void handleFocusEvent(XEvent xev) {
221 super.handleFocusEvent(xev);
222 XFocusChangeEvent xfe = xev.get_xfocus();
223
224 // If we somehow received focus events forward it instead to proxy
225 // FIXME: Shouldn't we instead check for inferrior?
226 focusLog.finer("Received focus event on shell: " + xfe);
227 // focusProxy.xRequestFocus();
228 }
229
230 /***************************************************************************************
231 * I N S E T S C O D E
232 **************************************************************************************/
233
234
235 /**
236 * Current native insets of the window on the desktop.
237 * When it's noticed the insets might have been changed (like when we
238 * receive a ReparentNotify), the value must be reset to null by the
239 * setNativeInsets(null) call.
240 *
241 * Synchronization: the awtLock is used. This should have been the state
242 * lock instead, but the retrieveNativeInsets() uses the awtLock, and the
243 * later must be always taken before the state lock.
244 *
245 * The field MUST NOT be used directly. Use get/setNativeInsets() instead.
246 */
247 private Insets nativeInsets = null;
248
249 /**
250 * Gets the current native insets.
251 * This method may only return null if the fallBackToDefault is false.
252 */
253 private Insets getNativeInsets(boolean retrieve, boolean fallBackToDefault) {
254 if (isTargetUndecorated() || isEmbedded() || !XWM.isRunning())
255 {
256 return (Insets)ZERO_INSETS.clone();
257 }
258 if (getWindow() == XConstants.None) {
259 return getDefaultInsets();
260 }
261 XToolkit.awtLock();
262 try {
263 if (nativeInsets == null && retrieve) {
264 retrieveNativeInsets();
265 }
266 return nativeInsets == null ?
267 (fallBackToDefault ? getDefaultInsets() : null) :
268 (Insets)nativeInsets.clone();
269 } finally {
270 XToolkit.awtUnlock();
271 }
272 }
273
274 /**
275 * Gets the current native insets.
276 */
277 public Insets getNativeInsets() {
278 return getNativeInsets(true, true);
279 }
280
281 @Override
282 public Insets getInsets() {
283 Insets insets = getNativeInsets();
284 insets.top += getMenuBarHeight();
285 if (insLog.isLoggable(Level.FINEST)) {
286 insLog.log(Level.FINEST, "Get insets returns {0}",
287 new Object[] {insets});
288 }
289 return insets;
290 }
291
292 /**
293 * Returns the insets that the window probably will get.
294 */
295 private Insets getDefaultInsets() {
296 Insets insets = XWM.getWM().getDefaultInsets();
297 return (Insets)(insets == null ? DEFAULT_INSETS : insets).clone();
298 }
299
300 /**
301 * Get rid of insane insets.
302 * The operation is performed in place - the given object gets modified.
303 */
304 private static Insets sanitize(Insets insets) {
305 //XXX: Perhaps using the marginal values instead of the default would
306 // make more sense?
307 if (insets.top > 64 || insets.top < 0) {
308 insets.top = DEFAULT_INSETS.top;
309 }
310 if (insets.left > 32 || insets.left < 0) {
311 insets.left = DEFAULT_INSETS.left;
312 }
313 if (insets.right > 32 || insets.right < 0) {
314 insets.right = DEFAULT_INSETS.right;
315 }
316 if (insets.bottom > 32 || insets.bottom < 0) {
317 insets.bottom = DEFAULT_INSETS.bottom;
318 }
319 return insets;
320 }
321
322 private void setNativeInsets(Insets insets) {
323 XToolkit.awtLock();
324 try {
325 nativeInsets = insets == null ? null :
326 sanitize((Insets)insets.clone());
327 } finally {
328 XToolkit.awtUnlock();
329 }
330 }
331
332 public static final Insets DEFAULT_INSETS = new Insets(25, 5, 5, 5);
333 public static final Insets ZERO_INSETS = new Insets(0, 0, 0, 0);
334
335 /**
336 * Retrieve the current insets of the window.
337 *
338 * This method must only be called by the getNativeInsets() method. DO NOT
339 * call this method directly. If the insets need to be updated, nullify
340 * them using the setNativeInsets(null) method.
341 */
342 private void retrieveNativeInsets() {
343 long window = getWindow();
344
345 if (XWM.requestWMExtents(window)) {
346 XWM.waitForExtentsUpdateEvent();
347 }
348
349 // Some WMs may provide the extents via a property, but do not require
350 // a request to update them. Hence try getting them unconditionally.
351 // We could use the XEvent returned by the waitForExtentsUpdateEvent()
352 // above, though that doesn't seem to make much sense.
353 Insets insets = XWM.getInsetsFromExtents(window);
354 if (insets != null) {
355 setNativeInsets(insets);
356 return;
357 }
358
359 // The window manager is unable to report any extents, so we need to
360 // calculate them ourselves.
361 Rectangle winRect = XlibUtil.getWindowGeometry(
362 window, XlibWrapper.larg1);
363 if (winRect == null) {
364 // Some error occured
365 return;
366 }
367 // The root window must be got immediately after the previous
368 // getWindowGeometry() call.
369 long root = Native.getWindow(XlibWrapper.larg1);
370
371 long parent = getNativeParent();
372 if (parent == XConstants.None || parent == root) {
373 // Non-reparenting WM. Assume the insets are zero.
374 setNativeInsets(ZERO_INSETS);
375 return;
376 }
377
378 Rectangle parentRect = XlibUtil.getWindowGeometry(parent);
379 if (parentRect == null) {
380 // Some error again
381 return;
382 }
383
384 long grand_parent = XlibUtil.getParentWindow(parent);
385 if (grand_parent == XConstants.None || grand_parent == root ||
386 (winRect.x != 0 || winRect.y != 0 ||
387 winRect.width != parentRect.width ||
388 winRect.height != parentRect.height))
389 {
390 // Single-reparenting WM
391 // Either there's no a valid grand-parent, or the direct parent
392 // has greater bounds than the window itself.
393 setNativeInsets(calculateInsets(winRect, parentRect));
394 } else {
395 // Double-reparenting WM
396 // There's a valid grand-parent. The bounds of the direct
397 // parent are equal to the bounds of the window itself.
398 Rectangle grandParentRect = XlibUtil.getWindowGeometry(
399 grand_parent);
400 if (grandParentRect == null) {
401 // One more error condition. This time we fall back to the
402 // single-reparenting case, though this will produce
403 // ZERO_INSETS actually...
404 setNativeInsets(calculateInsets(winRect, parentRect));
405 } else {
406 setNativeInsets(calculateInsets(parentRect, grandParentRect));
407 }
408 }
409 }
410
411 /**
412 * Calculates the insets for the given interior and exterior rectangles.
413 * Only positive insets are allowed. If the interior rectangle is bigger
414 * than the exterior one, the negative inset values will be ignored,
415 * and zero value used instead.
416 */
417 private static Insets calculateInsets(Rectangle interior, Rectangle exterior) {
418 return new Insets(
419 Math.max(interior.y, 0),
420 Math.max(interior.x, 0),
421 Math.max(exterior.height - interior.height - interior.y, 0),
422 Math.max(exterior.width - interior.width - interior.x, 0));
423 }
424
425 /**
426 * Applies the new insets.
427 */
428 private void setInsets(Insets insets) {
429 setNativeInsets(insets);
430
431 WindowDimensions dims = new WindowDimensions(dimensions);
432 dims.setInsets(insets);
433 reportOrAdjust(dims, false);
434 }
435
436 @Override
437 public void handlePropertyNotify(XEvent xev) {
438 super.handlePropertyNotify(xev);
439
440 XPropertyEvent ev = xev.get_xproperty();
441 if (XWM.isExtentsPropertyAtom(ev.get_atom())) {
442 Insets insets = XWM.getInsetsFromProp(getWindow(),
443 XAtom.get(ev.get_atom()));
444 if (insLog.isLoggable(Level.FINE)) {
445 insLog.fine("" + insets);
446 }
447 if (insets != null) {
448 setInsets(insets);
449 }
450 }
451 }
452
453 // The serial of the last received ReparentNotify event
454 private long reparentNotifySerial = 0;
455
456 // The number of processed ConfigureNotify events with the same serial
457 // as reparentNotifySerial.
458 private int numOfConfigureNotifyJustAfterReparentNotify = 0;
459
460 @Override
461 public void handleReparentNotifyEvent(XEvent xev) {
462 super.handleReparentNotifyEvent(xev);
463
464 XReparentEvent xe = xev.get_xreparent();
465 if (insLog.isLoggable(Level.FINE)) {
466 insLog.fine(xe.toString());
467 }
468
469 if (xe.get_window() != getWindow()) {
470 return;
471 }
472
473 if (!isParented()) {
474 if (isVisible()) {
475 // Either the WM or the embedder exited
476 XWM.getWM().unshadeKludge(this);
477 if (!isEmbedded()) {
478 XWM.reset();
479 }
480 setInsets(null);
481 } //else: The window just got hidden
482 } else {
483 // We just got parented: prepare stuff to recalculate the insets,
484 // and to readjust the bounds if needed
485 reparentNotifySerial = xe.get_serial();
486 numOfConfigureNotifyJustAfterReparentNotify = 0;
487
488 setNativeInsets(null);
489 needToAdjustBounds();
490 }
491 }
492
493 protected boolean isInitialReshape() {
494 return false;
495 }
496
497 public void handleMoved(Point loc) {
498 ComponentAccessor.setX((Component)target, loc.x);
499 ComponentAccessor.setY((Component)target, loc.y);
500 postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
501 }
502
503
504 public void revalidate() {
505 XToolkit.executeOnEventHandlerThread(target, new Runnable() {
506 public void run() {
507 target.invalidate();
508 target.validate();
509 }
510 });
511 }
512
513 boolean gravityBug() {
514 return XWM.configureGravityBuggy();
515 }
516
517 // The height of area used to display current active input method
518 int getInputMethodHeight() {
519 return 0;
520 }
521
522 void updateSizeHints(WindowDimensions dims) {
523 Rectangle rec = dims.getClientRect();
524 checkShellRect(rec);
525 updateSizeHints(rec.x, rec.y, rec.width, rec.height);
526 }
527
528 void updateSizeHints() {
529 updateSizeHints(dimensions);
530 }
531
532 // Coordinates are that of the target
533 // Called only on Toolkit thread
534 private void reshape(WindowDimensions newDimensions, int op)
535 {
536 if (insLog.isLoggable(Level.FINE)) {
537 insLog.fine("Reshaping " + this + " to " + newDimensions +
538 "; op " + operationToString(op));
539 }
540 XToolkit.awtLock();
541 try {
542 if (!isVisible()) {
543 Rectangle client = newDimensions.getClientRect();
544 checkShellRect(client);
545 setShellBounds(client);
546
547 reportReshape(newDimensions);
548 } else {
549 requestReshape(newDimensions, op);
550 }
551 } finally {
552 XToolkit.awtUnlock();
553 }
554 }
555
556 /**
557 * Sets window dimensions and propagates the changes to the target sending
558 * corresponding events if needed.
559 *
560 * MUST be invoked under the AWTLock.
561 */
562 private void reportReshape(WindowDimensions newDims) {
563 if (insLog.isLoggable(Level.FINE)) {
564 insLog.fine("" + newDims);
565 }
566
567 final Insets insets = newDims.getInsets();
568 if (!insets.equals(dimensions.getInsets())) {
569 // Recalculate the minimum size of the client area
570 updateMinSizeHints(insets);
571 }
572
573 // If the client area size changes, we need to revalidate the target.
574 // This may happen in the XContentWindow.setContentBounds(). If this
575 // does not happen there, we need to dispatch the operation here.
576 final boolean needRevalidate =
577 !dimensions.getClientSize().equals(newDims.getClientSize());
578
579 checkIfOnNewScreen(newDims.getBounds());
580
581 Point oldLocation = getLocation();
582 dimensions = newDims;
583 if (!getLocation().equals(oldLocation)) {
584 handleMoved(getLocation());
585 }
586
587 if (!reconfigureContentWindow(dimensions) && needRevalidate) {
588 revalidate();
589 }
590
591 updateChildrenSizes();
592 repositionSecurityWarning();
593 }
594
595 private void reportOrAdjust(WindowDimensions newDims,
596 boolean handlingConfigureNotify)
597 {
598 if (dimensions.equals(newDims) && !areBoundsAdjusting()) {
599 // If nothing has changed and we're already adjusted, return
600 if (insLog.isLoggable(Level.FINE)) {
601 insLog.fine("Ignored: nothing changed: " + newDims);
602 }
603 return;
604 }
605 if (adjustBounds(newDims, handlingConfigureNotify)) {
606 // We expect another ConfigureNotify
607 return;
608 }
609
610 reportReshape(newDims);
611 }
612
613 /**
614 * Requests the system to reshape the window.
615 *
616 * MUST be invoked under the AWTLock.
617 */
618 private void requestReshape(WindowDimensions newDimensions, int op) {
619 if (insLog.isLoggable(Level.FINE)) {
620 insLog.fine("Request reshape: " + newDimensions + "; op: " +
621 operationToString(op));
622 }
623
624 Rectangle shellRect = newDimensions.getClientRect();
625
626 if (gravityBug()) {
627 Insets in = newDimensions.getInsets();
628 shellRect.translate(in.left, in.top);
629 }
630
631 if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) {
632 shellRect.setLocation(0, 0);
633 }
634
635 checkShellRectSize(shellRect);
636 if (!isEmbedded()) {
637 checkShellRectPos(shellRect);
638 }
639
640 op = op & ~NO_EMBEDDED_CHECK;
641
642 if (op == SET_LOCATION) {
643 setShellPosition(shellRect);
644 } else if (isResizable()) {
645 if (op == SET_BOUNDS) {
646 setShellBounds(shellRect);
647 } else {
648 setShellSize(shellRect);
649 }
650 } else {
651 XWM.setShellNotResizable(this, newDimensions, shellRect, true);
652 if (op == SET_BOUNDS) {
653 setShellPosition(shellRect);
654 }
655 }
656 }
657
658 // This method gets overriden in XFramePeer & XDialogPeer.
659 abstract boolean isTargetUndecorated();
660
661 /**
662 * @see java.awt.peer.ComponentPeer#setBounds
663 */
664 public void setBounds(int x, int y, int width, int height, int operation) {
665 WindowDimensions dims = new WindowDimensions(dimensions);
666 switch (operation & (~NO_EMBEDDED_CHECK)) {
667 case SET_LOCATION:
668 // Set location always sets bounds location. However, until the window is mapped we
669 // should use client coordinates
670 dims.setLocation(x, y);
671 break;
672 case SET_SIZE:
673 // Set size sets bounds size. However, until the window is mapped we
674 // should use client coordinates
675 dims.setSize(width, height);
676 break;
677 case SET_CLIENT_SIZE: {
678 // Sets client rect size. Width and height contain insets.
679 // Also update the insets to the latest known value to decrease
680 // (or even eliminate) the bounds adjustment after showing
681 // the window.
682 Insets in = getNativeInsets();
683 width -= in.left+in.right;
684 height -= in.top+in.bottom;
685 dims.setClientSize(width, height);
686 dims.setInsets(in);
687 break;
688 }
689 case SET_BOUNDS:
690 default:
691 dims.setLocation(x, y);
692 dims.setSize(width, height);
693 break;
694 }
695 reshape(dims, operation);
696 validateSurface();
697 }
698
699 /**
700 * Sets the content window bounds.
701 *
702 * @return whether a COMPONENT_RESIZED has been sent
703 */
704 boolean reconfigureContentWindow(WindowDimensions dims) {
705 if (content == null) {
706 insLog.fine("WARNING: Content window is null");
707 return false;
708 }
709 return content.setContentBounds(dims);
710 }
711
712 /**
713 * Indicates if the adjustBounds() needs to be invoked.
714 * Synchronization: state lock.
715 */
716 private boolean areBoundsAdjusted = false;
717
718 /**
719 * Forces the system to re-adjust the bounds of the window after it has
720 * been finally adopted by the windowing system.
721 */
722 private void needToAdjustBounds() {
723 synchronized (getStateLock()) {
724 areBoundsAdjusted = false;
725 }
726 }
727
728 /**
729 * Indicates if the window bounds has not yet been finally configured by
730 * the X server/window manager.
731 */
732 public boolean areBoundsAdjusting() {
733 synchronized (getStateLock()) {
734 return !areBoundsAdjusted;
735 }
736 }
737
738 // XXX: Perhaps the following two might be replaced with Window.isSizeSet,
739 // isLocationSet? Just like we have Window.isPacked...
740 /**
741 * Indicates if the size hints of the window specify a position requested
742 * by the user's code.
743 */
744 private boolean isUserSpecifiedPositionSet() {
745 return (getHints().get_flags() &
746 (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0;
747 }
748
749 /**
750 * Indicates if the size hints of the window specify a position requested
751 * by the user's code.
752 */
753 private boolean isUserSpecifiedSizeSet() {
754 return (getHints().get_flags() &
755 (XUtilConstants.USSize | XUtilConstants.PSize)) != 0;
756 }
757
758 /**
759 * Adjust the bounds of the window.
760 *
761 * @return true if an adjustment has taken place. false if the new bounds
762 * are OK.
763 */
764 private boolean adjustBounds(WindowDimensions newDims,
765 boolean handlingConfigureNotify)
766 {
767 synchronized (getStateLock()) {
768 if (areBoundsAdjusted) {
769 return false;
770 }
771 // We have to adjust bounds upon showing once.
772 // We also may adjust them several times before showing - e.g.
773 // when we receive the PropertyNotify event with the extents.
774 if (handlingConfigureNotify && isVisible()) {
775 insLog.log(Level.FINE, "Final adjustment");
776 areBoundsAdjusted = true;
777 }
778 }
779
780 if (isMaximized()) {
781 return false;
782 }
783
784 int operation = 0;
785 boolean isPacked = AWTAccessor.getWindowAccessor().
786 isPacked((Window)target);
787
788 if (!newDims.getClientSize().equals(dimensions.getClientSize())
789 && isPacked)
790 {
791 // The client area size calculated via pack() needs to be preserved
792 operation = SET_SIZE;
793 } else
794 if (!newDims.getSize().equals(dimensions.getSize())
795 && isUserSpecifiedSizeSet())
796 {
797 // The size set via setSize()/setBounds() needs to be preserved
798 operation = SET_SIZE;
799 }
800
801 if (isUserSpecifiedPositionSet() &&
802 !newDims.getLocation().equals(dimensions.getLocation()))
803 {
804 // The user also requested a specific position
805 if (operation == 0) {
806 operation = SET_LOCATION;
807 } else {
808 operation = SET_BOUNDS;
809 }
810 }
811
812 if (operation == 0) {
813 insLog.fine("No adjustment needed");
814 return false;
815 }
816
817 WindowDimensions dims = new WindowDimensions(
818 dimensions.getLocation(),
819 isPacked ? dimensions.getClientSize() : dimensions.getSize(),
820 newDims.getInsets(),
821 isPacked);
822
823 if (insLog.isLoggable(Level.FINE)) {
824 insLog.fine("Request: " + dims + "; operation=" +
825 operationToString(operation));
826 }
827 requestReshape(dims, operation);
828 return true;
829 }
830
831 @Override
832 public void handleConfigureNotifyEvent(XEvent xev) {
833 // Note: we don't call super because the XWindowPeer immediately calls
834 // the checkIfOnNewScreen() assuming the bounds are correct. This
835 // is not always correct for decorated peers.
836
837 XConfigureEvent xe = xev.get_xconfigure();
838
839 // Due to the SubstructureNotifyMask we should process only those
840 // events that belong to us.
841 if (xe.get_window() != getWindow()) {
842 return;
843 }
844
845 if (insLog.isLoggable(Level.FINE)) {
846 insLog.fine("" + xe);
847 insLog.fine("XWM.isRunning: " + XWM.isRunning());
848 }
849
850 // If there's a WM we have to ignore some events
851 if (XWM.isRunning()) {
852 // Ignore any events until after we become visible
853 if (!isVisible()) {
854 insLog.fine("Ignored: Not visible yet");
855 needToAdjustBounds();
856 return;
857 }
858
859 // We have not yet been parented by the WM
860 if (mayBeReparented() && !isTargetUndecorated()) {
861 insLog.fine("Ignored: Not parented yet");
862 needToAdjustBounds();
863 return;
864 }
865
866 // Just after reparenting we can receive a synthetic and/or real
867 // event(s). Since sometimes we need to adjust the bounds of the
868 // frame, we need to process only one of the events. Justification:
869 // 1. Both events carry the same information: one from the X
870 // server, the other from the WM.
871 // 2. The adjustment operation is performed only once upon
872 // processing the first ConfigureNotify event that is not
873 // ignored.
874 // 3. If the first event causes the adjustment to happen, the
875 // second event will be considered as a normal, not ignored
876 // event that may generate Java events and set incorrect
877 // (not-yet-adjusted) bounds to the frame.
878 // So generally, if the adjustment is needed, the first
879 // ConfigureNotify that we should really process is the one caused
880 // by the adjustment operation. So we process only the first
881 // ConfigureNotify event having the same serial that the preceeding
882 // ReparentNotify.
883 //
884 // There's one exception however: we do not ignore the events for
885 // maximized frames. The real ConfigureNotify that we would like to
886 // act upon usually is the third one (it carries the correct
887 // maximized bounds). However, it has the same serial (which is
888 // obvious), and what's worse - we can't reliably determine if the
889 // window manager/X server send us exactly two events before that,
890 // or maybe one of them might be not sent at all - and in this case
891 // we would need to process the second event, not the third. So we
892 // just process everything for a maximized frame. This does produce
893 // absolutely meaningless bounds dancing (with events sent to the
894 // user space), but at least this is backward-compatible and quite
895 // reliable. Should we find a way to detect and process only the
896 // very last of ConfigureNotify events sent with the same serial,
897 // we would like to use this approach instead of the current
898 // processing of the very first event only.
899 if (xe.get_serial() == reparentNotifySerial && !isMaximized())
900 {
901 if (++numOfConfigureNotifyJustAfterReparentNotify > 1) {
902 insLog.fine("Ignored: serial matches the ReparentNotify");
903 return;
904 }
905 }
906 }
907
908 // At this point we can calculate some reliable insets
909 Insets insets = getNativeInsets();
910
911 // Note that in some cases this kind of event may be caused by the
912 // changed insets (like if the theme changes, or something). However
913 // it's difficult if at all possible to identify this situation, and
914 // not confuse it with a regular reconfiguration w/o introducing some
915 // serious performance degradation. So we only support changing the
916 // insets:
917 // a. when a reparenting WM exits/gets replaced.
918 // b. when the window gets hidden/shown.
919 // c. if the WM is smart enough to send the PropertyNotify
920 // events with the updated extents.
921
922 // x, y, width, height hold the coordinates of the whole frame
923 // including the decorations.
924 int x;
925 int y;
926
927 if (xe.get_send_event() || !isParented()) {
928 // If the event is synthetic or we're not parented, the coordinates
929 // are relative to the root window.
930 x = xe.get_x() - insets.left;
931 y = xe.get_y() - insets.top;
932 } else {
933 // The event is real and we're parented - the coordinates are
934 // relative to the parent
935 Point loc = null;
936 if (XWM.isNoSyntheticConfigureNotifyOnLeftTopResize()) {
937 // there's a bug in the WM, so ask the X server
938 loc = queryXLocation();
939 }
940 if (loc == null) {
941 // consider the current location unchanged
942 // TODO: this may not be true... perhaps querying X in all cases is worthwhile?
943 // after all: we already do this for Metacity which is quite common,
944 // so we should not worry much about the performance.
945 loc = getLocation();
946 }
947
948 x = loc.x;
949 y = loc.y;
950 }
951
952 int width = xe.get_width() + insets.left + insets.right;
953 int height = xe.get_height() + insets.top + insets.bottom;
954
955 // Now apply the new bounds
956 WindowDimensions d = new WindowDimensions(
957 new Rectangle(x, y, width, height), insets, false);
958 reportOrAdjust(d, true);
959 }
960
961 private void checkShellRectSize(Rectangle shellRect) {
962 if (shellRect.width < 0) {
963 shellRect.width = 1;
964 }
965 if (shellRect.height < 0) {
966 shellRect.height = 1;
967 }
968 }
969
970 private void checkShellRectPos(Rectangle shellRect) {
971 int wm = XWM.getWMID();
972 if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) {
973 if (shellRect.x == 0 && shellRect.y == 0) {
974 shellRect.x = shellRect.y = 1;
975 }
976 }
977 }
978
1008 }
1009 public void setShellPosition(Rectangle rec) {
1010 if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell position on " +
1011 this + " to " + rec);
1012 XToolkit.awtLock();
1013 try {
1014 updateSizeHints(rec.x, rec.y, rec.width, rec.height);
1015 XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y);
1016 }
1017 finally {
1018 XToolkit.awtUnlock();
1019 }
1020 }
1021
1022 void initResizability() {
1023 setResizable(winAttr.initialResizability);
1024 }
1025 public void setResizable(boolean resizable) {
1026 int fs = winAttr.functions;
1027 if (!isResizable() && resizable) {
1028 setNativeInsets(null);
1029 winAttr.isResizable = resizable;
1030 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
1031 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
1032 } else {
1033 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
1034 }
1035 winAttr.functions = fs;
1036 XWM.setShellResizable(this);
1037 } else if (isResizable() && !resizable) {
1038 setNativeInsets(null);
1039 winAttr.isResizable = resizable;
1040 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
1041 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
1042 } else {
1043 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
1044 }
1045 winAttr.functions = fs;
1046 XWM.setShellNotResizable(this, dimensions, dimensions.getBounds(), false);
1047 }
1048 }
1049
1050 Rectangle getShellBounds() {
1051 return dimensions.getClientRect();
1052 }
1053
1054 public Rectangle getBounds() {
1055 return dimensions.getBounds();
1056 }
1057
1058 public Dimension getSize() {
1079 public int getAbsoluteY() {
1080 // NOTE: returning this peer's location which is shell location
1081 return dimensions.getScreenBounds().y;
1082 }
1083
1084 public int getWidth() {
1085 return getSize().width;
1086 }
1087
1088 public int getHeight() {
1089 return getSize().height;
1090 }
1091
1092 final public WindowDimensions getDimensions() {
1093 return dimensions;
1094 }
1095
1096 public Point getLocationOnScreen() {
1097 XToolkit.awtLock();
1098 try {
1099 if (!areBoundsAdjusting()) {
1100 return toGlobal(0,0);
1101 } else {
1102 Point location = target.getLocation();
1103 if (insLog.isLoggable(Level.FINE)) {
1104 insLog.log(Level.FINE, "getLocationOnScreen {0} not reparented: {1} ",
1105 new Object[] {this, location});
1106 }
1107 return location;
1108 }
1109 } finally {
1110 XToolkit.awtUnlock();
1111 }
1112 }
1113
1114
1115 /***************************************************************************************
1116 * END OF I N S E T S C O D E
1117 **************************************************************************************/
1118
1119 protected boolean isEventDisabled(XEvent e) {
1120 switch (e.get_type()) {
1121 // Do not generate MOVED/RESIZED events since we generate them by ourselves
1122 case XConstants.ConfigureNotify:
1123 return true;
1124 case XConstants.EnterNotify:
1125 case XConstants.LeaveNotify:
1126 // Disable crossing event on outer borders of Frame so
1226 final void dumpMe() {
1227 System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height);
1228 }
1229
1230 final void dumpTarget() {
1231 int getWidth = ComponentAccessor.getWidth((Component)target);
1232 int getHeight = ComponentAccessor.getHeight((Component)target);
1233 int getTargetX = ComponentAccessor.getX((Component)target);
1234 int getTargetY = ComponentAccessor.getY((Component)target);
1235 System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight);
1236 }
1237
1238 final void dumpShell() {
1239 dumpWindow("Shell", getShell());
1240 }
1241 final void dumpContent() {
1242 dumpWindow("Content", getContentWindow());
1243 }
1244 final void dumpParent() {
1245 long parent = XlibUtil.getParentWindow(getShell());
1246 if (parent != XConstants.None)
1247 {
1248 dumpWindow("Parent", parent);
1249 }
1250 else
1251 {
1252 System.err.println(">>> NO PARENT");
1253 }
1254 }
1255
1256 final void dumpWindow(String id, long window) {
1257 XWindowAttributes pattr = new XWindowAttributes();
1258 try {
1259 XToolkit.awtLock();
1260 try {
1261 int status =
1262 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1263 window, pattr.pData);
1264 }
1265 finally {
1266 XToolkit.awtUnlock();
1269 + ", " + pattr.get_y() + ", " + pattr.get_width()
1270 + ", " + pattr.get_height());
1271 } finally {
1272 pattr.dispose();
1273 }
1274 }
1275
1276 final void dumpAll() {
1277 dumpTarget();
1278 dumpMe();
1279 dumpParent();
1280 dumpShell();
1281 dumpContent();
1282 }
1283
1284 boolean isMaximized() {
1285 return false;
1286 }
1287
1288 boolean isOverrideRedirect() {
1289 return ((XToolkit)Toolkit.getDefaultToolkit()).isOverrideRedirect((Window)target);
1290 }
1291
1292 public boolean requestWindowFocus(long time, boolean timeProvided) {
1293 focusLog.fine("Request for decorated window focus");
1294 // If this is Frame or Dialog we can't assure focus request success - but we still can try
1295 // If this is Window and its owner Frame is active we can be sure request succedded.
1296 Window focusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
1297 Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
1298
1299 focusLog.log(Level.FINER, "Current window is: active={0}, focused={1}",
1300 new Object[]{ Boolean.valueOf(target == activeWindow),
1301 Boolean.valueOf(target == focusedWindow)});
1302
1303 XWindowPeer toFocus = this;
1304 while (toFocus.nextTransientFor != null) {
1305 toFocus = toFocus.nextTransientFor;
1306 }
1307 if (toFocus == null || !toFocus.focusAllowedFor()) {
1308 // This might change when WM will have property to determine focus policy.
|