1 /*
2  * Copyright (c) 2002, 2020, 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  */
25 
26 package sun.awt.X11;
27 
28 import java.awt.AWTEvent;
29 import java.awt.AWTKeyStroke;
30 import java.awt.Color;
31 import java.awt.Component;
32 import java.awt.Container;
33 import java.awt.Cursor;
34 import java.awt.Font;
35 import java.awt.FontMetrics;
36 import java.awt.Graphics;
37 import java.awt.GraphicsConfiguration;
38 import java.awt.Point;
39 import java.awt.Rectangle;
40 import java.awt.SystemColor;
41 import java.awt.Toolkit;
42 import java.awt.Window;
43 import java.awt.event.ComponentEvent;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseWheelEvent;
49 import java.awt.event.PaintEvent;
50 import java.awt.image.ColorModel;
51 import java.awt.peer.ComponentPeer;
52 import java.lang.ref.WeakReference;
53 
54 import sun.awt.AWTAccessor;
55 import sun.awt.AWTAccessor.ComponentAccessor;
56 import sun.awt.PaintEventDispatcher;
57 import sun.awt.PeerEvent;
58 import sun.awt.SunToolkit;
59 import sun.awt.X11ComponentPeer;
60 import sun.awt.X11GraphicsConfig;
61 import sun.java2d.SunGraphics2D;
62 import sun.java2d.SurfaceData;
63 import sun.util.logging.PlatformLogger;
64 
65 class XWindow extends XBaseWindow implements X11ComponentPeer {
66     private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XWindow");
67     private static PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XWindow");
68     private static PlatformLogger eventLog = PlatformLogger.getLogger("sun.awt.X11.event.XWindow");
69     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XWindow");
70     private static PlatformLogger keyEventLog = PlatformLogger.getLogger("sun.awt.X11.kye.XWindow");
71   /* If a motion comes in while a multi-click is pending,
72    * allow a smudge factor so that moving the mouse by a small
73    * amount does not wipe out the multi-click state variables.
74    */
75     private static final int AWT_MULTICLICK_SMUDGE = 4;
76     // ButtonXXX events stuff
77     static int lastX = 0, lastY = 0;
78     static long lastTime = 0;
79     static long lastButton = 0;
80     static WeakReference<XWindow> lastWindowRef = null;
81     static int clickCount = 0;
82 
83     // used to check if we need to re-create surfaceData.
84     int oldWidth = -1;
85     int oldHeight = -1;
86 
87     protected PropMwmHints mwm_hints;
88     protected static XAtom wm_protocols;
89     protected static XAtom wm_delete_window;
90     protected static XAtom wm_take_focus;
91 
92     private boolean stateChanged; // Indicates whether the value on savedState is valid
93     private int savedState; // Holds last known state of the top-level window
94 
95     XWindowAttributesData winAttr;
96 
97     protected X11GraphicsConfig graphicsConfig;
98     protected AwtGraphicsConfigData graphicsConfigData;
99 
100     private boolean reparented;
101 
102     XWindow parent;
103 
104     Component target;
105 
106     private static int JAWT_LOCK_ERROR=0x00000001;
107     private static int JAWT_LOCK_CLIP_CHANGED=0x00000002;
108     private static int JAWT_LOCK_BOUNDS_CHANGED=0x00000004;
109     private static int JAWT_LOCK_SURFACE_CHANGED=0x00000008;
110     private int drawState = JAWT_LOCK_CLIP_CHANGED |
111     JAWT_LOCK_BOUNDS_CHANGED |
112     JAWT_LOCK_SURFACE_CHANGED;
113 
114     public static final String TARGET = "target",
115         REPARENTED = "reparented"; // whether it is reparented by default
116 
117     SurfaceData surfaceData;
118 
119     XRepaintArea paintArea;
120 
121     // fallback default font object
122     private static Font defaultFont;
123 
getDefaultFont()124     static synchronized Font getDefaultFont() {
125         if (null == defaultFont) {
126             defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12);
127         }
128         return defaultFont;
129     }
130 
131     /* A bitmask keeps the button's numbers as Button1Mask, Button2Mask, Button3Mask
132      * which are allowed to
133      * generate the CLICK event after the RELEASE has happened.
134      * There are conditions that must be true for that sending CLICK event:
135      * 1) button was initially PRESSED
136      * 2) no movement or drag has happened until RELEASE
137     */
138     private int mouseButtonClickAllowed = 0;
139 
initIDs()140     private static native void initIDs();
141 
142     static {
initIDs()143         initIDs();
144     }
145 
XWindow(XCreateWindowParams params)146     XWindow(XCreateWindowParams params) {
147         super(params);
148     }
149 
XWindow()150     XWindow() {
151     }
152 
XWindow(long parentWindow, Rectangle bounds)153     XWindow(long parentWindow, Rectangle bounds) {
154         super(new XCreateWindowParams(new Object[] {
155             BOUNDS, bounds,
156             PARENT_WINDOW, Long.valueOf(parentWindow)}));
157     }
158 
XWindow(Component target, long parentWindow, Rectangle bounds)159     XWindow(Component target, long parentWindow, Rectangle bounds) {
160         super(new XCreateWindowParams(new Object[] {
161             BOUNDS, bounds,
162             PARENT_WINDOW, Long.valueOf(parentWindow),
163             TARGET, target}));
164     }
165 
XWindow(Component target, long parentWindow)166     XWindow(Component target, long parentWindow) {
167         this(target, parentWindow, new Rectangle(target.getBounds()));
168     }
169 
XWindow(Component target)170     XWindow(Component target) {
171         this(target, (target.getParent() == null) ? 0 : getParentWindowID(target), new Rectangle(target.getBounds()));
172     }
173 
XWindow(Object target)174     XWindow(Object target) {
175         this(null, 0, null);
176     }
177 
178     /* This create is used by the XEmbeddedFramePeer since it has to create the window
179        as a child of the netscape window. This netscape window is passed in as wid */
XWindow(long parentWindow)180     XWindow(long parentWindow) {
181         super(new XCreateWindowParams(new Object[] {
182             PARENT_WINDOW, Long.valueOf(parentWindow),
183             REPARENTED, Boolean.TRUE,
184             EMBEDDED, Boolean.TRUE}));
185     }
186 
initGraphicsConfiguration()187     protected void initGraphicsConfiguration() {
188         graphicsConfig = (X11GraphicsConfig) target.getGraphicsConfiguration();
189         graphicsConfigData = new AwtGraphicsConfigData(graphicsConfig.getAData());
190     }
191 
preInit(XCreateWindowParams params)192     void preInit(XCreateWindowParams params) {
193         super.preInit(params);
194         reparented = Boolean.TRUE.equals(params.get(REPARENTED));
195 
196         target = (Component)params.get(TARGET);
197 
198         initGraphicsConfiguration();
199 
200         AwtGraphicsConfigData gData = getGraphicsConfigurationData();
201         X11GraphicsConfig config = (X11GraphicsConfig) getGraphicsConfiguration();
202         XVisualInfo visInfo = gData.get_awt_visInfo();
203         params.putIfNull(EVENT_MASK, XConstants.KeyPressMask | XConstants.KeyReleaseMask
204             | XConstants.FocusChangeMask | XConstants.ButtonPressMask | XConstants.ButtonReleaseMask
205             | XConstants.EnterWindowMask | XConstants.LeaveWindowMask | XConstants.PointerMotionMask
206             | XConstants.ButtonMotionMask | XConstants.ExposureMask | XConstants.StructureNotifyMask);
207 
208         if (target != null) {
209             params.putIfNull(BOUNDS, new Rectangle(target.getBounds()));
210         } else {
211             params.putIfNull(BOUNDS, new Rectangle(0, 0, MIN_SIZE, MIN_SIZE));
212         }
213         params.putIfNull(BORDER_PIXEL, Long.valueOf(0));
214         getColorModel(); // fix 4948833: this call forces the color map to be initialized
215         params.putIfNull(COLORMAP, gData.get_awt_cmap());
216         params.putIfNull(DEPTH, gData.get_awt_depth());
217         params.putIfNull(VISUAL_CLASS, Integer.valueOf(XConstants.InputOutput));
218         params.putIfNull(VISUAL, visInfo.get_visual());
219         params.putIfNull(VALUE_MASK, XConstants.CWBorderPixel | XConstants.CWEventMask | XConstants.CWColormap);
220         Long parentWindow = (Long)params.get(PARENT_WINDOW);
221         if (parentWindow == null || parentWindow.longValue() == 0) {
222             XToolkit.awtLock();
223             try {
224                 int screen = visInfo.get_screen();
225                 if (screen != -1) {
226                     params.add(PARENT_WINDOW, XlibWrapper.RootWindow(XToolkit.getDisplay(), screen));
227                 } else {
228                     params.add(PARENT_WINDOW, XToolkit.getDefaultRootWindow());
229                 }
230             } finally {
231                 XToolkit.awtUnlock();
232             }
233         }
234 
235         paintArea = new XRepaintArea();
236         if (target != null) {
237             this.parent = getParentXWindowObject(target.getParent());
238         }
239 
240         params.putIfNull(BACKING_STORE, XToolkit.getBackingStoreType());
241 
242         XToolkit.awtLock();
243         try {
244             if (wm_protocols == null) {
245                 wm_protocols = XAtom.get("WM_PROTOCOLS");
246                 wm_delete_window = XAtom.get("WM_DELETE_WINDOW");
247                 wm_take_focus = XAtom.get("WM_TAKE_FOCUS");
248             }
249         }
250         finally {
251             XToolkit.awtUnlock();
252         }
253         winAttr = new XWindowAttributesData();
254         savedState = XUtilConstants.WithdrawnState;
255     }
256 
postInit(XCreateWindowParams params)257     void postInit(XCreateWindowParams params) {
258         super.postInit(params);
259 
260         setWMClass(getWMClass());
261 
262         surfaceData = graphicsConfig.createSurfaceData(this);
263         Color c;
264         if (target != null && (c = target.getBackground()) != null) {
265             // We need a version of setBackground that does not call repaint !!
266             // and one that does not get overridden. The problem is that in postInit
267             // we call setBackground and we don't have all the stuff initialized to
268             // do a full paint for most peers. So we cannot call setBackground in postInit.
269             // instead we need to call xSetBackground.
270             xSetBackground(c);
271         }
272     }
273 
getGraphicsConfiguration()274     public GraphicsConfiguration getGraphicsConfiguration() {
275         if (graphicsConfig == null) {
276             initGraphicsConfiguration();
277         }
278         return graphicsConfig;
279     }
280 
getGraphicsConfigurationData()281     public AwtGraphicsConfigData getGraphicsConfigurationData() {
282         if (graphicsConfigData == null) {
283             initGraphicsConfiguration();
284         }
285         return graphicsConfigData;
286     }
287 
getWMClass()288     protected String[] getWMClass() {
289         return new String[] {XToolkit.getAWTAppClassName(),
290                 XToolkit.getAWTAppClassName()};
291     }
292 
setReparented(boolean newValue)293     void setReparented(boolean newValue) {
294         reparented = newValue;
295     }
296 
isReparented()297     boolean isReparented() {
298         return reparented;
299     }
300 
getParentWindowID(Component target)301     static long getParentWindowID(Component target) {
302 
303         Component temp = target.getParent();
304         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
305         ComponentPeer peer = acc.getPeer(temp);
306         while (!(peer instanceof XWindow))
307         {
308             temp = temp.getParent();
309             peer = acc.getPeer(temp);
310         }
311 
312         if (peer != null && peer instanceof XWindow)
313             return ((XWindow)peer).getContentWindow();
314         else return 0;
315     }
316 
317 
getParentXWindowObject(Component target)318     static XWindow getParentXWindowObject(Component target) {
319         if (target == null) return null;
320         Component temp = target.getParent();
321         if (temp == null) return null;
322         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
323         ComponentPeer peer = acc.getPeer(temp);
324         if (peer == null) return null;
325         while ((peer != null) && !(peer instanceof XWindow))
326         {
327             temp = temp.getParent();
328             peer = acc.getPeer(temp);
329         }
330         if (peer != null && peer instanceof XWindow)
331             return (XWindow) peer;
332         else return null;
333     }
334 
335 
isParentOf(XWindow win)336     boolean isParentOf(XWindow win) {
337         if (!(target instanceof Container) || win == null || win.getTarget() == null) {
338             return false;
339         }
340         Container parent = AWTAccessor.getComponentAccessor().getParent(win.target);
341         while (parent != null && parent != target) {
342             parent = AWTAccessor.getComponentAccessor().getParent(parent);
343         }
344         return (parent == target);
345     }
346 
getTarget()347     public Object getTarget() {
348         return target;
349     }
getEventSource()350     public Component getEventSource() {
351         return target;
352     }
353 
getColorModel(int transparency)354     public ColorModel getColorModel(int transparency) {
355         return graphicsConfig.getColorModel (transparency);
356     }
357 
358     @Override
getColorModel()359     public ColorModel getColorModel() {
360         if (graphicsConfig != null) {
361             return graphicsConfig.getColorModel ();
362         }
363         else {
364             return Toolkit.getDefaultToolkit().getColorModel();
365         }
366     }
367 
getGraphics(SurfaceData surfData, Color afore, Color aback, Font afont)368     Graphics getGraphics(SurfaceData surfData, Color afore, Color aback, Font afont) {
369         if (surfData == null) return null;
370 
371         Component target = this.target;
372 
373         /* Fix for bug 4746122. Color and Font shouldn't be null */
374         Color bgColor = aback;
375         if (bgColor == null) {
376             bgColor = SystemColor.window;
377         }
378         Color fgColor = afore;
379         if (fgColor == null) {
380             fgColor = SystemColor.windowText;
381         }
382         Font font = afont;
383         if (font == null) {
384             font = XWindow.getDefaultFont();
385         }
386         return new SunGraphics2D(surfData, fgColor, bgColor, font);
387     }
388 
getGraphics()389     public Graphics getGraphics() {
390         return getGraphics(surfaceData,
391                            target.getForeground(),
392                            target.getBackground(),
393                            target.getFont());
394     }
395 
396     @SuppressWarnings("deprecation")
getFontMetrics(Font font)397     public FontMetrics getFontMetrics(Font font) {
398         return Toolkit.getDefaultToolkit().getFontMetrics(font);
399     }
400 
getTargetBounds()401     public Rectangle getTargetBounds() {
402         return target.getBounds();
403     }
404 
405     /**
406      * Returns true if the event has been handled and should not be
407      * posted to Java.
408      */
prePostEvent(AWTEvent e)409     boolean prePostEvent(AWTEvent e) {
410         return false;
411     }
412 
sendEvent(final AWTEvent e)413     static void sendEvent(final AWTEvent e) {
414         // The uses of this method imply that the incoming event is system-generated
415         SunToolkit.setSystemGenerated(e);
416         PeerEvent pe = new PeerEvent(Toolkit.getDefaultToolkit(), new Runnable() {
417                 public void run() {
418                     AWTAccessor.getAWTEventAccessor().setPosted(e);
419                     ((Component)e.getSource()).dispatchEvent(e);
420                 }
421             }, PeerEvent.ULTIMATE_PRIORITY_EVENT);
422         if (focusLog.isLoggable(PlatformLogger.Level.FINER) && (e instanceof FocusEvent)) {
423             focusLog.finer("Sending " + e);
424         }
425         XToolkit.postEvent(XToolkit.targetToAppContext(e.getSource()), pe);
426     }
427 
428 
429 /*
430  * Post an event to the event queue.
431  */
432 // NOTE: This method may be called by privileged threads.
433 //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
postEvent(AWTEvent event)434     void postEvent(AWTEvent event) {
435         XToolkit.postEvent(XToolkit.targetToAppContext(event.getSource()), event);
436     }
437 
postEventStatic(AWTEvent event)438     static void postEventStatic(AWTEvent event) {
439         XToolkit.postEvent(XToolkit.targetToAppContext(event.getSource()), event);
440     }
441 
postEventToEventQueue(final AWTEvent event)442     public void postEventToEventQueue(final AWTEvent event) {
443         //fix for 6239938 : Choice drop-down does not disappear when it loses focus, on XToolkit
444         if (!prePostEvent(event)) {
445             //event hasn't been handled and must be posted to EventQueue
446             postEvent(event);
447         }
448     }
449 
450     // overriden in XCanvasPeer
doEraseBackground()451     protected boolean doEraseBackground() {
452         return true;
453     }
454 
455     // We need a version of setBackground that does not call repaint !!
456     // and one that does not get overridden. The problem is that in postInit
457     // we call setBackground and we don't have all the stuff initialized to
458     // do a full paint for most peers. So we cannot call setBackground in postInit.
xSetBackground(Color c)459     public final void xSetBackground(Color c) {
460         XToolkit.awtLock();
461         try {
462             winBackground(c);
463             // fix for 6558510: handle sun.awt.noerasebackground flag,
464             // see doEraseBackground() and preInit() methods in XCanvasPeer
465             if (!doEraseBackground()) {
466                 return;
467             }
468             int pixel = surfaceData.pixelFor(c.getRGB());
469             XlibWrapper.XSetWindowBackground(XToolkit.getDisplay(), getContentWindow(), pixel);
470             XlibWrapper.XClearWindow(XToolkit.getDisplay(), getContentWindow());
471         }
472         finally {
473             XToolkit.awtUnlock();
474         }
475     }
476 
setBackground(Color c)477     public void setBackground(Color c) {
478         xSetBackground(c);
479     }
480 
481     Color backgroundColor;
winBackground(Color c)482     void winBackground(Color c) {
483         backgroundColor = c;
484     }
485 
getWinBackground()486     public Color getWinBackground() {
487         Color c = null;
488 
489         if (backgroundColor != null) {
490             c = backgroundColor;
491         } else if (parent != null) {
492             c = parent.getWinBackground();
493         }
494 
495         if (c instanceof SystemColor) {
496             c = new Color(c.getRGB());
497         }
498 
499         return c;
500     }
501 
isEmbedded()502     public boolean isEmbedded() {
503         return embedded;
504     }
505 
repaint(int x, int y, int width, int height)506     public final void repaint(int x, int y, int width, int height) {
507         if (!isVisible() || getWidth() == 0 || getHeight() == 0) {
508             return;
509         }
510         Graphics g = getGraphics();
511         if (g != null) {
512             try {
513                 g.setClip(x, y, width, height);
514                 if (SunToolkit.isDispatchThreadForAppContext(getTarget())) {
515                     paint(g); // The native and target will be painted in place.
516                 } else {
517                     paintPeer(g);
518                     postPaintEvent(target, x, y, width, height);
519                 }
520             } finally {
521                 g.dispose();
522             }
523         }
524     }
525 
repaint()526     void repaint() {
527         repaint(0, 0, getWidth(), getHeight());
528     }
529 
paint(final Graphics g)530     public void paint(final Graphics g) {
531         // paint peer
532         paintPeer(g);
533     }
534 
paintPeer(final Graphics g)535     void paintPeer(final Graphics g) {
536     }
537     //used by Peers to avoid flickering withing paint()
flush()538     protected void flush(){
539         XToolkit.awtLock();
540         try {
541             XlibWrapper.XFlush(XToolkit.getDisplay());
542         } finally {
543             XToolkit.awtUnlock();
544         }
545     }
546 
popup(int x, int y, int width, int height)547     public void popup(int x, int y, int width, int height) {
548         // TBD: grab the pointer
549         xSetBounds(x, y, width, height);
550     }
551 
handleExposeEvent(XEvent xev)552     public void handleExposeEvent(XEvent xev) {
553         super.handleExposeEvent(xev);
554         XExposeEvent xe = xev.get_xexpose();
555         if (isEventDisabled(xev)) {
556             return;
557         }
558 
559         int x = scaleDown(xe.get_x());
560         int y = scaleDown(xe.get_y());
561         int w = scaleDown(xe.get_width());
562         int h = scaleDown(xe.get_height());
563 
564         Component target = getEventSource();
565         ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
566 
567         if (!compAccessor.getIgnoreRepaint(target)
568             && compAccessor.getWidth(target) != 0
569             && compAccessor.getHeight(target) != 0)
570         {
571             postPaintEvent(target, x, y, w, h);
572         }
573     }
574 
postPaintEvent(Component target, int x, int y, int w, int h)575     public void postPaintEvent(Component target, int x, int y, int w, int h) {
576         PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
577             createPaintEvent(target, x, y, w, h);
578         if (event != null) {
579             postEventToEventQueue(event);
580         }
581     }
582 
getModifiers(int state, int button, int keyCode)583     static int getModifiers(int state, int button, int keyCode) {
584         return getModifiers(state, button, keyCode, false);
585     }
586 
getWheelModifiers(int state, int button)587     static int getWheelModifiers(int state, int button) {
588         return getModifiers(state, button, 0, true);
589     }
590 
getModifiers(int state, int button, int keyCode, boolean isWheelMouse)591     private static int getModifiers(int state, int button, int keyCode, boolean isWheelMouse) {
592         int modifiers = 0;
593 
594         if (((state & XConstants.ShiftMask) != 0) ^ (keyCode == KeyEvent.VK_SHIFT)) {
595             modifiers |= InputEvent.SHIFT_DOWN_MASK;
596         }
597         if (((state & XConstants.ControlMask) != 0) ^ (keyCode == KeyEvent.VK_CONTROL)) {
598             modifiers |= InputEvent.CTRL_DOWN_MASK;
599         }
600         if (((state & XToolkit.metaMask) != 0) ^ (keyCode == KeyEvent.VK_META)) {
601             modifiers |= InputEvent.META_DOWN_MASK;
602         }
603         if (((state & XToolkit.altMask) != 0) ^ (keyCode == KeyEvent.VK_ALT)) {
604             modifiers |= InputEvent.ALT_DOWN_MASK;
605         }
606         if (((state & XToolkit.modeSwitchMask) != 0) ^ (keyCode == KeyEvent.VK_ALT_GRAPH)) {
607             modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
608         }
609         //InputEvent.BUTTON_DOWN_MASK array is starting from BUTTON1_DOWN_MASK on index == 0.
610         // button currently reflects a real button number and starts from 1. (except NOBUTTON which is zero )
611 
612         /* this is an attempt to refactor button IDs in : MouseEvent, InputEvent, XlibWrapper and XWindow.*/
613 
614         //reflects a button number similar to MouseEvent.BUTTON1, 2, 3 etc.
615         for (int i = 0; i < XConstants.buttons.length; i ++){
616             //modifier should be added if :
617             // 1) current button is now still in PRESSED state (means that user just pressed mouse but not released yet) or
618             // 2) if Xsystem reports that "state" represents that button was just released. This only happens on RELEASE with 1,2,3 buttons.
619             // ONLY one of these conditions should be TRUE to add that modifier.
620             if (((state & XlibUtil.getButtonMask(i + 1)) != 0) != (button == XConstants.buttons[i])){
621                 //exclude wheel buttons from adding their numbers as modifiers
622                 if (!isWheelMouse || !isWheel(XConstants.buttons[i])) {
623                     modifiers |= InputEvent.getMaskForButton(i+1);
624                 }
625             }
626         }
627         return modifiers;
628     }
629 
isWheel(int button)630     static boolean isWheel(int button) {
631         // 4 and 5 buttons are usually considered assigned to a first wheel
632         return button == XConstants.buttons[3] || button == XConstants.buttons[4];
633     }
634     @SuppressWarnings("deprecation")
getXModifiers(AWTKeyStroke stroke)635     static int getXModifiers(AWTKeyStroke stroke) {
636         int mods = stroke.getModifiers();
637         int res = 0;
638         if ((mods & (InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK)) != 0) {
639             res |= XConstants.ShiftMask;
640         }
641         if ((mods & (InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK)) != 0) {
642             res |= XConstants.ControlMask;
643         }
644         if ((mods & (InputEvent.ALT_DOWN_MASK | InputEvent.ALT_MASK)) != 0) {
645             res |= XToolkit.altMask;
646         }
647         if ((mods & (InputEvent.META_DOWN_MASK | InputEvent.META_MASK)) != 0) {
648             res |= XToolkit.metaMask;
649         }
650         if ((mods & (InputEvent.ALT_GRAPH_DOWN_MASK | InputEvent.ALT_GRAPH_MASK)) != 0) {
651             res |= XToolkit.modeSwitchMask;
652         }
653         return res;
654     }
655 
getMouseMovementSmudge()656     static int getMouseMovementSmudge() {
657         //TODO: It's possible to read corresponding settings
658         return AWT_MULTICLICK_SMUDGE;
659     }
660 
handleButtonPressRelease(XEvent xev)661     public void handleButtonPressRelease(XEvent xev) {
662         super.handleButtonPressRelease(xev);
663         XButtonEvent xbe = xev.get_xbutton();
664         if (isEventDisabled(xev)) {
665             return;
666         }
667         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
668             eventLog.fine(xbe.toString());
669         }
670         long when;
671         int modifiers;
672         boolean popupTrigger = false;
673         int button=0;
674         int lbutton = xbe.get_button();
675         /*
676          * Ignore the buttons above 20 due to the bit limit for
677          * InputEvent.BUTTON_DOWN_MASK.
678          * One more bit is reserved for FIRST_HIGH_BIT.
679          */
680         if (lbutton > SunToolkit.MAX_BUTTONS_SUPPORTED) {
681             return;
682         }
683         int type = xev.get_type();
684         when = xbe.get_time();
685         long jWhen = XToolkit.nowMillisUTC_offset(when);
686 
687         int x = scaleDown(xbe.get_x());
688         int y = scaleDown(xbe.get_y());
689         if (xev.get_xany().get_window() != window) {
690             Point localXY = toLocal(scaleDown(xbe.get_x_root()),
691                                     scaleDown(xbe.get_y_root()));
692             x = localXY.x;
693             y = localXY.y;
694         }
695 
696         if (type == XConstants.ButtonPress) {
697             //Allow this mouse button to generate CLICK event on next ButtonRelease
698             mouseButtonClickAllowed |= XlibUtil.getButtonMask(lbutton);
699             XWindow lastWindow = (lastWindowRef != null) ? (lastWindowRef.get()):(null);
700             /*
701                multiclick checking
702             */
703             if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
704                 eventLog.finest("lastWindow = " + lastWindow + ", lastButton "
705                 + lastButton + ", lastTime " + lastTime + ", multiClickTime "
706                 + XToolkit.getMultiClickTime());
707             }
708             if (lastWindow == this && lastButton == lbutton && (when - lastTime) < XToolkit.getMultiClickTime()) {
709                 clickCount++;
710             } else {
711                 clickCount = 1;
712                 lastWindowRef = new WeakReference<>(this);
713                 lastButton = lbutton;
714                 lastX = x;
715                 lastY = y;
716             }
717             lastTime = when;
718 
719 
720             /*
721                Check for popup trigger !!
722             */
723             popupTrigger = (lbutton == 3);
724         }
725 
726         button = XConstants.buttons[lbutton - 1];
727 
728         // mapping extra buttons to numbers starting from 4.
729         if ((button > XConstants.buttons[4]) && (!Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled())){
730             return;
731         }
732 
733         if (button > XConstants.buttons[4]){
734             button -= 2;
735         }
736 
737         if (!isWheel(lbutton)) {
738             modifiers = getModifiers(xbe.get_state(), button, 0);
739             MouseEvent me = new MouseEvent(getEventSource(),
740                                            type == XConstants.ButtonPress ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED,
741                                            jWhen,modifiers, x, y,
742                                            scaleDown(xbe.get_x_root()),
743                                            scaleDown(xbe.get_y_root()),
744                                            clickCount,popupTrigger,button);
745 
746             postEventToEventQueue(me);
747 
748             if ((type == XConstants.ButtonRelease) &&
749                 ((mouseButtonClickAllowed & XlibUtil.getButtonMask(lbutton)) != 0) ) // No up-button in the drag-state
750             {
751                 postEventToEventQueue(me = new MouseEvent(getEventSource(),
752                                                      MouseEvent.MOUSE_CLICKED,
753                                                      jWhen,
754                                                      modifiers,
755                                                      x, y,
756                                                      scaleDown(xbe.get_x_root()),
757                                                      scaleDown(xbe.get_y_root()),
758                                                      clickCount,
759                                                      false, button));
760             }
761 
762         }
763         else {
764             modifiers = getWheelModifiers(xbe.get_state(), button);
765             if (xev.get_type() == XConstants.ButtonPress) {
766                 MouseWheelEvent mwe = new MouseWheelEvent(getEventSource(),MouseEvent.MOUSE_WHEEL, jWhen,
767                                                           modifiers,
768                                                           x, y,
769                                                           scaleDown(xbe.get_x_root()),
770                                                           scaleDown(xbe.get_y_root()),
771                                                           1,false,MouseWheelEvent.WHEEL_UNIT_SCROLL,
772                                                           3,button==4 ?  -1 : 1);
773                 postEventToEventQueue(mwe);
774             }
775         }
776 
777         /* Update the state variable AFTER the CLICKED event post. */
778         if (type == XConstants.ButtonRelease) {
779             /* Exclude this mouse button from allowed list.*/
780             mouseButtonClickAllowed &= ~ XlibUtil.getButtonMask(lbutton);
781         }
782     }
783 
handleMotionNotify(XEvent xev)784     public void handleMotionNotify(XEvent xev) {
785         super.handleMotionNotify(xev);
786         XMotionEvent xme = xev.get_xmotion();
787         if (isEventDisabled(xev)) {
788             return;
789         }
790 
791         int mouseKeyState = 0; //(xme.get_state() & (XConstants.buttonsMask[0] | XConstants.buttonsMask[1] | XConstants.buttonsMask[2]));
792 
793         //this doesn't work for extra buttons because Xsystem is sending state==0 for every extra button event.
794         // we can't correct it in MouseEvent class as we done it with modifiers, because exact type (DRAG|MOVE)
795         // should be passed from XWindow.
796         final int buttonsNumber = XToolkit.getNumberOfButtonsForMask();
797 
798         for (int i = 0; i < buttonsNumber; i++){
799             // TODO : here is the bug in WM: extra buttons doesn't have state!=0 as they should.
800             if ((i != 4) && (i != 5)) {
801                 mouseKeyState = mouseKeyState | (xme.get_state() & XlibUtil.getButtonMask(i + 1));
802             }
803         }
804 
805         boolean isDragging = (mouseKeyState != 0);
806         int mouseEventType = 0;
807 
808         if (isDragging) {
809             mouseEventType = MouseEvent.MOUSE_DRAGGED;
810         } else {
811             mouseEventType = MouseEvent.MOUSE_MOVED;
812         }
813 
814         /*
815            Fix for 6176814 .  Add multiclick checking.
816         */
817         int x = scaleDown(xme.get_x());
818         int y = scaleDown(xme.get_y());
819         XWindow lastWindow = (lastWindowRef != null) ? (lastWindowRef.get()):(null);
820 
821         if (!(lastWindow == this &&
822               (xme.get_time() - lastTime) < XToolkit.getMultiClickTime()  &&
823               (Math.abs(lastX - x) < AWT_MULTICLICK_SMUDGE &&
824                Math.abs(lastY - y) < AWT_MULTICLICK_SMUDGE))) {
825           clickCount = 0;
826           lastWindowRef = null;
827           mouseButtonClickAllowed = 0;
828           lastTime = 0;
829           lastX = 0;
830           lastY = 0;
831         }
832 
833         long jWhen = XToolkit.nowMillisUTC_offset(xme.get_time());
834         int modifiers = getModifiers(xme.get_state(), 0, 0);
835         boolean popupTrigger = false;
836 
837         Component source = getEventSource();
838 
839         if (xme.get_window() != window) {
840             Point localXY = toLocal(scaleDown(xme.get_x_root()),
841                                     scaleDown(xme.get_y_root()));
842             x = localXY.x;
843             y = localXY.y;
844         }
845         /* Fix for 5039416.
846          * According to canvas.c we shouldn't post any MouseEvent if mouse is dragging and clickCount!=0.
847          */
848         if ((isDragging && clickCount == 0) || !isDragging) {
849             MouseEvent mme = new MouseEvent(source, mouseEventType, jWhen,
850                                             modifiers, x, y,
851                                             scaleDown(xme.get_x_root()),
852                                             scaleDown(xme.get_y_root()),
853                                             clickCount, popupTrigger, MouseEvent.NOBUTTON);
854             postEventToEventQueue(mme);
855         }
856     }
857 
858 
859     // REMIND: need to implement looking for disabled events
x11inputMethodLookupString(long event, long[] keysymArray)860     private native boolean x11inputMethodLookupString(long event,
861                                                       long[] keysymArray);
862 
haveCurrentX11InputMethodInstance()863     private native boolean haveCurrentX11InputMethodInstance();
864 
865     private boolean mouseAboveMe;
866 
isMouseAbove()867     public boolean isMouseAbove() {
868         synchronized (getStateLock()) {
869             return mouseAboveMe;
870         }
871     }
setMouseAbove(boolean above)872     protected void setMouseAbove(boolean above) {
873         synchronized (getStateLock()) {
874             mouseAboveMe = above;
875         }
876     }
877 
enterNotify(long window)878     protected void enterNotify(long window) {
879         if (window == getWindow()) {
880             setMouseAbove(true);
881         }
882     }
leaveNotify(long window)883     protected void leaveNotify(long window) {
884         if (window == getWindow()) {
885             setMouseAbove(false);
886         }
887     }
888 
handleXCrossingEvent(XEvent xev)889     public void handleXCrossingEvent(XEvent xev) {
890         super.handleXCrossingEvent(xev);
891         XCrossingEvent xce = xev.get_xcrossing();
892 
893         if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
894             eventLog.finest(xce.toString());
895         }
896 
897         if (xce.get_type() == XConstants.EnterNotify) {
898             enterNotify(xce.get_window());
899         } else { // LeaveNotify:
900             leaveNotify(xce.get_window());
901         }
902 
903         // Skip event If it was caused by a grab
904         // This is needed because on displays with focus-follows-mouse on MousePress X system generates
905         // two XCrossing events with mode != NormalNotify. First of them notifies that the mouse has left
906         // current component. Second one notifies that it has entered into the same component.
907         // This looks like the window under the mouse has actually changed and Java handle these  events
908         // accordingly. This leads to impossibility to make a double click on Component (6404708)
909         XWindowPeer toplevel = getToplevelXWindow();
910         if (toplevel != null && !toplevel.isModalBlocked()){
911             if (xce.get_mode() != XConstants.NotifyNormal) {
912                 // 6404708 : need update cursor in accordance with skipping Leave/EnterNotify event
913                 // whereas it doesn't need to handled further.
914                 if (xce.get_type() == XConstants.EnterNotify) {
915                     XAwtState.setComponentMouseEntered(getEventSource());
916                     XGlobalCursorManager.nativeUpdateCursor(getEventSource());
917                 } else { // LeaveNotify:
918                     XAwtState.setComponentMouseEntered(null);
919                 }
920                 return;
921             }
922         }
923         // X sends XCrossing to all hierarchy so if the edge of child equals to
924         // ancestor and mouse enters child, the ancestor will get an event too.
925         // From java point the event is bogus as ancestor is obscured, so if
926         // the child can get java event itself, we skip it on ancestor.
927         long childWnd = xce.get_subwindow();
928         if (childWnd != XConstants.None) {
929             XBaseWindow child = XToolkit.windowToXWindow(childWnd);
930             if (child != null && child instanceof XWindow &&
931                 !child.isEventDisabled(xev))
932             {
933                 return;
934             }
935         }
936 
937         // Remember old component with mouse to have the opportunity to send it MOUSE_EXITED.
938         final Component compWithMouse = XAwtState.getComponentMouseEntered();
939         if (toplevel != null) {
940             if(!toplevel.isModalBlocked()){
941                 if (xce.get_type() == XConstants.EnterNotify) {
942                     // Change XAwtState's component mouse entered to the up-to-date one before requesting
943                     // to update the cursor since XAwtState.getComponentMouseEntered() is used when the
944                     // cursor is updated (in XGlobalCursorManager.findHeavyweightUnderCursor()).
945                     XAwtState.setComponentMouseEntered(getEventSource());
946                     XGlobalCursorManager.nativeUpdateCursor(getEventSource());
947                 } else { // LeaveNotify:
948                     XAwtState.setComponentMouseEntered(null);
949                 }
950             } else {
951                 ((XComponentPeer) AWTAccessor.getComponentAccessor().getPeer(target))
952                     .pSetCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
953             }
954         }
955 
956         if (isEventDisabled(xev)) {
957             return;
958         }
959 
960         long jWhen = XToolkit.nowMillisUTC_offset(xce.get_time());
961         int modifiers = getModifiers(xce.get_state(),0,0);
962         int clickCount = 0;
963         boolean popupTrigger = false;
964         int x = scaleDown(xce.get_x());
965         int y = scaleDown(xce.get_y());
966         if (xce.get_window() != window) {
967             Point localXY = toLocal(scaleDown(xce.get_x_root()),
968                                     scaleDown(xce.get_y_root()));
969             x = localXY.x;
970             y = localXY.y;
971         }
972 
973         // This code tracks boundary crossing and ensures MOUSE_ENTER/EXIT
974         // are posted in alternate pairs
975         if (compWithMouse != null) {
976             MouseEvent me = new MouseEvent(compWithMouse, MouseEvent.MOUSE_EXITED,
977                                            jWhen, modifiers,
978                                            scaleDown(xce.get_x()),
979                                            scaleDown(xce.get_y()),
980                                            scaleDown(xce.get_x_root()),
981                                            scaleDown(xce.get_y_root()),
982                                            clickCount, popupTrigger,
983                                            MouseEvent.NOBUTTON);
984             postEventToEventQueue(me);
985             eventLog.finest("Clearing last window ref");
986             lastWindowRef = null;
987         }
988         if (xce.get_type() == XConstants.EnterNotify) {
989             MouseEvent me = new MouseEvent(getEventSource(), MouseEvent.MOUSE_ENTERED,
990                                            jWhen, modifiers,
991                                            scaleDown(xce.get_x()),
992                                            scaleDown(xce.get_y()),
993                                            scaleDown(xce.get_x_root()),
994                                            scaleDown(xce.get_y_root()),
995                                            clickCount, popupTrigger,
996                                            MouseEvent.NOBUTTON);
997             postEventToEventQueue(me);
998         }
999     }
1000 
doLayout(int x, int y, int width, int height)1001     public void doLayout(int x, int y, int width, int height) {}
1002 
handleConfigureNotifyEvent(XEvent xev)1003     public void handleConfigureNotifyEvent(XEvent xev) {
1004         Rectangle oldBounds = getBounds();
1005 
1006         super.handleConfigureNotifyEvent(xev);
1007         if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
1008             insLog.finer("Configure, {0}, event disabled: {1}",
1009                      xev.get_xconfigure(), isEventDisabled(xev));
1010         }
1011         if (isEventDisabled(xev)) {
1012             return;
1013         }
1014 
1015 //  if ( Check if it's a resize, a move, or a stacking order change )
1016 //  {
1017         Rectangle bounds = getBounds();
1018         if (!bounds.getSize().equals(oldBounds.getSize())) {
1019             postEventToEventQueue(new ComponentEvent(getEventSource(), ComponentEvent.COMPONENT_RESIZED));
1020         }
1021         if (!bounds.getLocation().equals(oldBounds.getLocation())) {
1022             postEventToEventQueue(new ComponentEvent(getEventSource(), ComponentEvent.COMPONENT_MOVED));
1023         }
1024 //  }
1025     }
1026 
handleMapNotifyEvent(XEvent xev)1027     public void handleMapNotifyEvent(XEvent xev) {
1028         super.handleMapNotifyEvent(xev);
1029         if (log.isLoggable(PlatformLogger.Level.FINE)) {
1030             log.fine("Mapped {0}", this);
1031         }
1032         if (isEventDisabled(xev)) {
1033             return;
1034         }
1035         ComponentEvent ce;
1036 
1037         ce = new ComponentEvent(getEventSource(), ComponentEvent.COMPONENT_SHOWN);
1038         postEventToEventQueue(ce);
1039     }
1040 
handleUnmapNotifyEvent(XEvent xev)1041     public void handleUnmapNotifyEvent(XEvent xev) {
1042         super.handleUnmapNotifyEvent(xev);
1043         if (isEventDisabled(xev)) {
1044             return;
1045         }
1046         ComponentEvent ce;
1047 
1048         ce = new ComponentEvent(target, ComponentEvent.COMPONENT_HIDDEN);
1049         postEventToEventQueue(ce);
1050     }
1051 
dumpKeysymArray(XKeyEvent ev)1052     private void dumpKeysymArray(XKeyEvent ev) {
1053         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1054             keyEventLog.fine("  "+Long.toHexString(XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(), ev.get_keycode(), 0))+
1055                              "\n        "+Long.toHexString(XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(), ev.get_keycode(), 1))+
1056                              "\n        "+Long.toHexString(XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(), ev.get_keycode(), 2))+
1057                              "\n        "+Long.toHexString(XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(), ev.get_keycode(), 3)));
1058         }
1059     }
1060     /**
1061        Return unicode character or 0 if no correspondent character found.
1062        Parameter is a keysym basically from keysymdef.h
1063        XXX: how about vendor keys? Is there some with Unicode value and not in the list?
1064     */
keysymToUnicode( long keysym, int state )1065     int keysymToUnicode( long keysym, int state ) {
1066         return XKeysym.convertKeysym( keysym, state );
1067     }
keyEventType2Id( int xEventType )1068     int keyEventType2Id( int xEventType ) {
1069         return xEventType == XConstants.KeyPress ? java.awt.event.KeyEvent.KEY_PRESSED :
1070                xEventType == XConstants.KeyRelease ? java.awt.event.KeyEvent.KEY_RELEASED : 0;
1071     }
xkeycodeToKeysym(XKeyEvent ev)1072     private static long xkeycodeToKeysym(XKeyEvent ev) {
1073         return XKeysym.getKeysym( ev );
1074     }
xkeycodeToPrimaryKeysym(XKeyEvent ev)1075     private long xkeycodeToPrimaryKeysym(XKeyEvent ev) {
1076         return XKeysym.xkeycode2primary_keysym( ev );
1077     }
primaryUnicode2JavaKeycode(int uni)1078     private static int primaryUnicode2JavaKeycode(int uni) {
1079         return (uni > 0? sun.awt.ExtendedKeyCodes.getExtendedKeyCodeForChar(uni) : 0);
1080         //return (uni > 0? uni + 0x01000000 : 0);
1081     }
logIncomingKeyEvent(XKeyEvent ev)1082     void logIncomingKeyEvent(XKeyEvent ev) {
1083         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1084             keyEventLog.fine("--XWindow.java:handleKeyEvent:"+ev);
1085         }
1086         dumpKeysymArray(ev);
1087         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1088             keyEventLog.fine("XXXXXXXXXXXXXX javakeycode will be most probably:0x"+ Integer.toHexString(XKeysym.getJavaKeycodeOnly(ev)));
1089         }
1090     }
handleKeyPress(XEvent xev)1091     public void handleKeyPress(XEvent xev) {
1092         super.handleKeyPress(xev);
1093         XKeyEvent ev = xev.get_xkey();
1094         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
1095             eventLog.fine(ev.toString());
1096         }
1097         if (isEventDisabled(xev)) {
1098             return;
1099         }
1100         handleKeyPress(ev);
1101     }
1102     // called directly from this package, unlike handleKeyRelease.
1103     // un-final it if you need to override it in a subclass.
handleKeyPress(XKeyEvent ev)1104     final void handleKeyPress(XKeyEvent ev) {
1105         long[] keysym = new long[2];
1106         int unicodeKey = 0;
1107         keysym[0] = XConstants.NoSymbol;
1108 
1109         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1110             logIncomingKeyEvent( ev );
1111         }
1112         if ( //TODO check if there's an active input method instance
1113              // without calling a native method. Is it necessary though?
1114             haveCurrentX11InputMethodInstance()) {
1115             if (x11inputMethodLookupString(ev.pData, keysym)) {
1116                 if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1117                     keyEventLog.fine("--XWindow.java XIM did process event; return; dec keysym processed:"+(keysym[0])+
1118                                    "; hex keysym processed:"+Long.toHexString(keysym[0])
1119                                    );
1120                 }
1121                 return;
1122             }else {
1123                 unicodeKey = keysymToUnicode( keysym[0], ev.get_state() );
1124                 if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1125                     keyEventLog.fine("--XWindow.java XIM did NOT process event, hex keysym:"+Long.toHexString(keysym[0])+"\n"+
1126                                      "                                         unicode key:"+Integer.toHexString(unicodeKey));
1127                 }
1128             }
1129         }else  {
1130             // No input method instance found. For example, there's a Java Input Method.
1131             // Produce do-it-yourself keysym and perhaps unicode character.
1132             keysym[0] = xkeycodeToKeysym(ev);
1133             unicodeKey = keysymToUnicode( keysym[0], ev.get_state() );
1134             if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1135                 keyEventLog.fine("--XWindow.java XIM is absent;             hex keysym:"+Long.toHexString(keysym[0])+"\n"+
1136                                  "                                         unicode key:"+Integer.toHexString(unicodeKey));
1137             }
1138         }
1139         // Keysym should be converted to Unicode, if possible and necessary,
1140         // and Java KeyEvent keycode should be calculated.
1141         // For press we should post pressed & typed Java events.
1142         //
1143         // Press event might be not processed to this time because
1144         //  (1) either XIM could not handle it or
1145         //  (2) it was Latin 1:1 mapping.
1146         //
1147         // Preserve modifiers to get Java key code for dead keys
1148         boolean isDeadKey = isDeadKey(keysym[0]);
1149         XKeysym.Keysym2JavaKeycode jkc = isDeadKey ? XKeysym.getJavaKeycode(keysym[0])
1150                 : XKeysym.getJavaKeycode(ev);
1151         if( jkc == null ) {
1152             jkc = new XKeysym.Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_UNDEFINED, java.awt.event.KeyEvent.KEY_LOCATION_UNKNOWN);
1153         }
1154 
1155         // Take the first keysym from a keysym array associated with the XKeyevent
1156         // and convert it to Unicode. Then, even if a Java keycode for the keystroke
1157         // is undefined, we still have a guess of what has been engraved on a keytop.
1158         int unicodeFromPrimaryKeysym = keysymToUnicode( xkeycodeToPrimaryKeysym(ev) ,0);
1159 
1160         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1161             keyEventLog.fine(">>>Fire Event:"+
1162                (ev.get_type() == XConstants.KeyPress ? "KEY_PRESSED; " : "KEY_RELEASED; ")+
1163                "jkeycode:decimal="+jkc.getJavaKeycode()+
1164                ", hex=0x"+Integer.toHexString(jkc.getJavaKeycode())+"; "+
1165                " legacy jkeycode: decimal="+XKeysym.getLegacyJavaKeycodeOnly(ev)+
1166                ", hex=0x"+Integer.toHexString(XKeysym.getLegacyJavaKeycodeOnly(ev))+"; "
1167             );
1168         }
1169 
1170         int jkeyToReturn = XKeysym.getLegacyJavaKeycodeOnly(ev); // someway backward compatible
1171         int jkeyExtended = jkc.getJavaKeycode() == java.awt.event.KeyEvent.VK_UNDEFINED ?
1172                            primaryUnicode2JavaKeycode( unicodeFromPrimaryKeysym ) :
1173                              jkc.getJavaKeycode();
1174         postKeyEvent( java.awt.event.KeyEvent.KEY_PRESSED,
1175                           ev.get_time(),
1176                           isDeadKey ? jkeyExtended : jkeyToReturn,
1177                           (unicodeKey == 0 ? java.awt.event.KeyEvent.CHAR_UNDEFINED : unicodeKey),
1178                           jkc.getKeyLocation(),
1179                           ev.get_state(),ev.getPData(), XKeyEvent.getSize(), (long)(ev.get_keycode()),
1180                           unicodeFromPrimaryKeysym,
1181                           jkeyExtended);
1182 
1183 
1184         if (unicodeKey > 0 && !isDeadKey) {
1185                 if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1186                     keyEventLog.fine("fire _TYPED on "+unicodeKey);
1187                 }
1188                 postKeyEvent( java.awt.event.KeyEvent.KEY_TYPED,
1189                               ev.get_time(),
1190                               java.awt.event.KeyEvent.VK_UNDEFINED,
1191                               unicodeKey,
1192                               java.awt.event.KeyEvent.KEY_LOCATION_UNKNOWN,
1193                               ev.get_state(),ev.getPData(), XKeyEvent.getSize(), (long)0,
1194                               unicodeFromPrimaryKeysym,
1195                               java.awt.event.KeyEvent.VK_UNDEFINED);
1196 
1197         }
1198 
1199 
1200     }
1201 
handleKeyRelease(XEvent xev)1202     public void handleKeyRelease(XEvent xev) {
1203         super.handleKeyRelease(xev);
1204         XKeyEvent ev = xev.get_xkey();
1205         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
1206             eventLog.fine(ev.toString());
1207         }
1208         if (isEventDisabled(xev)) {
1209             return;
1210         }
1211         handleKeyRelease(ev);
1212     }
1213     // un-private it if you need to call it from elsewhere
handleKeyRelease(XKeyEvent ev)1214     private void handleKeyRelease(XKeyEvent ev) {
1215         int unicodeKey = 0;
1216 
1217         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1218             logIncomingKeyEvent( ev );
1219         }
1220         // Keysym should be converted to Unicode, if possible and necessary,
1221         // and Java KeyEvent keycode should be calculated.
1222         // For release we should post released event.
1223         //
1224         // Preserve modifiers to get Java key code for dead keys
1225         long keysym = xkeycodeToKeysym(ev);
1226         boolean isDeadKey = isDeadKey(keysym);
1227         XKeysym.Keysym2JavaKeycode jkc = isDeadKey ? XKeysym.getJavaKeycode(keysym)
1228                 : XKeysym.getJavaKeycode(ev);
1229         if( jkc == null ) {
1230             jkc = new XKeysym.Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_UNDEFINED, java.awt.event.KeyEvent.KEY_LOCATION_UNKNOWN);
1231         }
1232         if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
1233             keyEventLog.fine(">>>Fire Event:"+
1234                (ev.get_type() == XConstants.KeyPress ? "KEY_PRESSED; " : "KEY_RELEASED; ")+
1235                "jkeycode:decimal="+jkc.getJavaKeycode()+
1236                ", hex=0x"+Integer.toHexString(jkc.getJavaKeycode())+"; "+
1237                " legacy jkeycode: decimal="+XKeysym.getLegacyJavaKeycodeOnly(ev)+
1238                ", hex=0x"+Integer.toHexString(XKeysym.getLegacyJavaKeycodeOnly(ev))+"; "
1239             );
1240         }
1241         // We obtain keysym from IM and derive unicodeKey from it for KeyPress only.
1242         // We used to cache that value and retrieve it on KeyRelease,
1243         // but in case for example of a dead key+vowel pair, a vowel after a deadkey
1244         // might never be cached before.
1245         // Also, switching between keyboard layouts, we might cache a wrong letter.
1246         // That's why we use the same procedure as if there was no IM instance: do-it-yourself unicode.
1247         unicodeKey = keysymToUnicode( xkeycodeToKeysym(ev), ev.get_state() );
1248 
1249         // Take a first keysym from a keysym array associated with the XKeyevent
1250         // and convert it to Unicode. Then, even if Java keycode for the keystroke
1251         // is undefined, we still will have a guess of what was engraved on a keytop.
1252         int unicodeFromPrimaryKeysym = keysymToUnicode( xkeycodeToPrimaryKeysym(ev) ,0);
1253 
1254         int jkeyToReturn = XKeysym.getLegacyJavaKeycodeOnly(ev); // someway backward compatible
1255         int jkeyExtended = jkc.getJavaKeycode() == java.awt.event.KeyEvent.VK_UNDEFINED ?
1256                            primaryUnicode2JavaKeycode( unicodeFromPrimaryKeysym ) :
1257                              jkc.getJavaKeycode();
1258         postKeyEvent(  java.awt.event.KeyEvent.KEY_RELEASED,
1259                           ev.get_time(),
1260                           isDeadKey ? jkeyExtended : jkeyToReturn,
1261                           (unicodeKey == 0 ? java.awt.event.KeyEvent.CHAR_UNDEFINED : unicodeKey),
1262                           jkc.getKeyLocation(),
1263                           ev.get_state(),ev.getPData(), XKeyEvent.getSize(), (long)(ev.get_keycode()),
1264                           unicodeFromPrimaryKeysym,
1265                           jkeyExtended);
1266 
1267 
1268     }
1269 
1270 
isDeadKey(long keysym)1271     private boolean isDeadKey(long keysym){
1272         return XKeySymConstants.XK_dead_grave <= keysym && keysym <= XKeySymConstants.XK_dead_semivoiced_sound;
1273     }
1274 
1275     /*
1276      * XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
1277      * unreliable, since mapping changes can happen for a virtual desktop
1278      * switch or MacOS style shading that became quite popular under X as
1279      * well.  Yes, it probably should not be this way, as it violates
1280      * ICCCM, but reality is that quite a lot of window managers abuse
1281      * mapping state.
1282      */
getWMState()1283     int getWMState() {
1284         if (stateChanged) {
1285             stateChanged = false;
1286             WindowPropertyGetter getter =
1287                 new WindowPropertyGetter(window, XWM.XA_WM_STATE, 0, 1, false,
1288                                          XWM.XA_WM_STATE);
1289             try {
1290                 int status = getter.execute();
1291                 if (status != XConstants.Success || getter.getData() == 0) {
1292                     return savedState = XUtilConstants.WithdrawnState;
1293                 }
1294 
1295                 if (getter.getActualType() != XWM.XA_WM_STATE.getAtom() && getter.getActualFormat() != 32) {
1296                     return savedState = XUtilConstants.WithdrawnState;
1297                 }
1298                 savedState = (int)Native.getCard32(getter.getData());
1299             } finally {
1300                 getter.dispose();
1301             }
1302         }
1303         return savedState;
1304     }
1305 
1306     /**
1307      * Override this methods to get notifications when top-level window state changes. The state is
1308      * meant in terms of ICCCM: WithdrawnState, IconicState, NormalState
1309      */
stateChanged(long time, int oldState, int newState)1310     protected void stateChanged(long time, int oldState, int newState) {
1311     }
1312 
1313     @Override
handlePropertyNotify(XEvent xev)1314     public void handlePropertyNotify(XEvent xev) {
1315         super.handlePropertyNotify(xev);
1316         XPropertyEvent ev = xev.get_xproperty();
1317         if (ev.get_atom() == XWM.XA_WM_STATE.getAtom()) {
1318             // State has changed, invalidate saved value
1319             stateChanged = true;
1320             stateChanged(ev.get_time(), savedState, getWMState());
1321         }
1322     }
1323 
reshape(Rectangle bounds)1324     public void reshape(Rectangle bounds) {
1325         reshape(bounds.x, bounds.y, bounds.width, bounds.height);
1326     }
1327 
reshape(int x, int y, int width, int height)1328     public void reshape(int x, int y, int width, int height) {
1329         if (width <= 0) {
1330             width = 1;
1331         }
1332         if (height <= 0) {
1333             height = 1;
1334         }
1335         this.x = x;
1336         this.y = y;
1337         this.width = width;
1338         this.height = height;
1339         xSetBounds(x, y, width, height);
1340         // Fixed 6322593, 6304251, 6315137:
1341         // XWindow's SurfaceData should be invalidated and recreated as part
1342         // of the process of resizing the window
1343         // see the evaluation of the bug 6304251 for more information
1344         validateSurface();
1345         layout();
1346     }
1347 
layout()1348     public void layout() {}
1349 
isShowing()1350     boolean isShowing() {
1351         return visible;
1352     }
1353 
isResizable()1354     boolean isResizable() {
1355         return true;
1356     }
1357 
isLocationByPlatform()1358     boolean isLocationByPlatform() {
1359         return false;
1360     }
1361 
updateSizeHints()1362     void updateSizeHints() {
1363         updateSizeHints(x, y, width, height);
1364     }
1365 
updateSizeHints(int x, int y, int width, int height)1366     void updateSizeHints(int x, int y, int width, int height) {
1367         long flags = XUtilConstants.PSize | (isLocationByPlatform() ? 0 : (XUtilConstants.PPosition | XUtilConstants.USPosition));
1368         if (!isResizable()) {
1369             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1370                 log.finer("Window {0} is not resizable", this);
1371             }
1372             flags |= XUtilConstants.PMinSize | XUtilConstants.PMaxSize;
1373         } else {
1374             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1375                 log.finer("Window {0} is resizable", this);
1376             }
1377         }
1378         setSizeHints(flags, x, y, width, height);
1379     }
1380 
updateSizeHints(int x, int y)1381     void updateSizeHints(int x, int y) {
1382         long flags = isLocationByPlatform() ? 0 : (XUtilConstants.PPosition | XUtilConstants.USPosition);
1383         if (!isResizable()) {
1384             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1385                 log.finer("Window {0} is not resizable", this);
1386             }
1387             flags |= XUtilConstants.PMinSize | XUtilConstants.PMaxSize | XUtilConstants.PSize;
1388         } else {
1389             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1390                 log.finer("Window {0} is resizable", this);
1391             }
1392         }
1393         setSizeHints(flags, x, y, width, height);
1394     }
1395 
validateSurface()1396     void validateSurface() {
1397         if ((width != oldWidth) || (height != oldHeight)) {
1398             doValidateSurface();
1399 
1400             oldWidth = width;
1401             oldHeight = height;
1402         }
1403     }
1404 
doValidateSurface()1405     final void doValidateSurface() {
1406         SurfaceData oldData = surfaceData;
1407         if (oldData != null) {
1408             surfaceData = graphicsConfig.createSurfaceData(this);
1409             oldData.invalidate();
1410         }
1411     }
1412 
getSurfaceData()1413     public SurfaceData getSurfaceData() {
1414         return surfaceData;
1415     }
1416 
dispose()1417     public void dispose() {
1418         SurfaceData oldData = surfaceData;
1419         surfaceData = null;
1420         if (oldData != null) {
1421             oldData.invalidate();
1422         }
1423         XToolkit.targetDisposedPeer(target, this);
1424         destroy();
1425     }
1426 
getLocationOnScreen()1427     public Point getLocationOnScreen() {
1428         synchronized (target.getTreeLock()) {
1429             Component comp = target;
1430 
1431             while (comp != null && !(comp instanceof Window)) {
1432                 comp = AWTAccessor.getComponentAccessor().getParent(comp);
1433             }
1434 
1435             // applets, embedded, etc - translate directly
1436             // XXX: override in subclass?
1437             if (comp == null || comp instanceof sun.awt.EmbeddedFrame) {
1438                 return toGlobal(0, 0);
1439             }
1440 
1441             XToolkit.awtLock();
1442             try {
1443                 Object wpeer = XToolkit.targetToPeer(comp);
1444                 if (wpeer == null
1445                     || !(wpeer instanceof XDecoratedPeer)
1446                     || ((XDecoratedPeer)wpeer).configure_seen)
1447                 {
1448                     return toGlobal(0, 0);
1449                 }
1450 
1451                 // wpeer is an XDecoratedPeer not yet fully adopted by WM
1452                 Point pt = toOtherWindow(getContentWindow(),
1453                                          ((XDecoratedPeer)wpeer).getContentWindow(),
1454                                          0, 0);
1455 
1456                 if (pt == null) {
1457                     pt = new Point(((XBaseWindow)wpeer).getAbsoluteX(), ((XBaseWindow)wpeer).getAbsoluteY());
1458                 }
1459                 pt.x += comp.getX();
1460                 pt.y += comp.getY();
1461                 return pt;
1462             } finally {
1463                 XToolkit.awtUnlock();
1464             }
1465         }
1466     }
1467 
1468 
setBData(KeyEvent e, byte[] data)1469     static void setBData(KeyEvent e, byte[] data) {
1470         AWTAccessor.getAWTEventAccessor().setBData(e, data);
1471     }
1472 
postKeyEvent(int id, long when, int keyCode, int keyChar, int keyLocation, int state, long event, int eventSize, long rawCode, int unicodeFromPrimaryKeysym, int extendedKeyCode)1473     public void postKeyEvent(int id, long when, int keyCode, int keyChar,
1474         int keyLocation, int state, long event, int eventSize, long rawCode,
1475         int unicodeFromPrimaryKeysym, int extendedKeyCode)
1476 
1477     {
1478         long jWhen = XToolkit.nowMillisUTC_offset(when);
1479         int modifiers = getModifiers(state, 0, keyCode);
1480 
1481         KeyEvent ke = new KeyEvent(getEventSource(), id, jWhen,
1482                                    modifiers, keyCode, (char)keyChar, keyLocation);
1483         if (event != 0) {
1484             byte[] data = Native.toBytes(event, eventSize);
1485             setBData(ke, data);
1486         }
1487 
1488         AWTAccessor.KeyEventAccessor kea = AWTAccessor.getKeyEventAccessor();
1489         kea.setRawCode(ke, rawCode);
1490         kea.setPrimaryLevelUnicode(ke, (long)unicodeFromPrimaryKeysym);
1491         kea.setExtendedKeyCode(ke, (long)extendedKeyCode);
1492         postEventToEventQueue(ke);
1493     }
1494 
getAWTKeyCodeForKeySym(int keysym)1495     static native int getAWTKeyCodeForKeySym(int keysym);
getKeySymForAWTKeyCode(int keycode)1496     static native int getKeySymForAWTKeyCode(int keycode);
1497 
1498     /* These two methods are actually applicable to toplevel windows only.
1499      * However, the functionality is required by both the XWindowPeer and
1500      * XWarningWindow, both of which have the XWindow as a common ancestor.
1501      * See XWM.setMotifDecor() for details.
1502      */
getMWMHints()1503     public PropMwmHints getMWMHints() {
1504         if (mwm_hints == null) {
1505             mwm_hints = new PropMwmHints();
1506             if (!XWM.XA_MWM_HINTS.getAtomData(getWindow(), mwm_hints.pData, MWMConstants.PROP_MWM_HINTS_ELEMENTS)) {
1507                 mwm_hints.zero();
1508             }
1509         }
1510         return mwm_hints;
1511     }
1512 
setMWMHints(PropMwmHints hints)1513     public void setMWMHints(PropMwmHints hints) {
1514         mwm_hints = hints;
1515         if (hints != null) {
1516             XWM.XA_MWM_HINTS.setAtomData(getWindow(), mwm_hints.pData, MWMConstants.PROP_MWM_HINTS_ELEMENTS);
1517         }
1518     }
1519 
initWMProtocols()1520     protected final void initWMProtocols() {
1521         wm_protocols.setAtomListProperty(this, getWMProtocols());
1522     }
1523 
1524     /**
1525      * Returns list of protocols which should be installed on this window.
1526      * Descendants can override this method to add class-specific protocols
1527      */
getWMProtocols()1528     protected XAtomList getWMProtocols() {
1529         // No protocols on simple window
1530         return new XAtomList();
1531     }
1532 
1533     /**
1534      * Indicates if the window is currently in the FSEM.
1535      * Synchronization: state lock.
1536      */
1537     private boolean fullScreenExclusiveModeState = false;
1538 
1539     // Implementation of the X11ComponentPeer
1540     @Override
setFullScreenExclusiveModeState(boolean state)1541     public void setFullScreenExclusiveModeState(boolean state) {
1542         synchronized (getStateLock()) {
1543             fullScreenExclusiveModeState = state;
1544         }
1545     }
1546 
isFullScreenExclusiveMode()1547     public final boolean isFullScreenExclusiveMode() {
1548         synchronized (getStateLock()) {
1549             return fullScreenExclusiveModeState;
1550         }
1551     }
1552 
1553     @Override
getScale()1554     protected int getScale() {
1555         return graphicsConfig.getScale();
1556     }
1557 
1558     @Override
scaleUp(int x)1559     protected int scaleUp(int x) {
1560         return graphicsConfig.scaleUp(x);
1561     }
1562 
1563     @Override
scaleDown(int x)1564     protected int scaleDown(int x) {
1565         return graphicsConfig.scaleDown(x);
1566     }
1567 }
1568