1 /* 2 * Copyright (c) 1999, 2013, 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; 27 28 import java.awt.*; 29 import java.awt.event.InputEvent; 30 import java.awt.event.InvocationEvent; 31 32 /** 33 * A stateless class which responds to native mouse moves, Component resizes, 34 * Component moves, showing and hiding of Components, minimizing and 35 * maximizing of top level Windows, addition and removal of Components, 36 * and calls to setCursor(). 37 */ 38 public abstract class GlobalCursorManager { 39 40 class NativeUpdater implements Runnable { 41 boolean pending = false; 42 run()43 public void run() { 44 boolean shouldUpdate = false; 45 synchronized (this) { 46 if (pending) { 47 pending = false; 48 shouldUpdate = true; 49 } 50 } 51 if (shouldUpdate) { 52 _updateCursor(false); 53 } 54 } 55 postIfNotPending(Component heavy, InvocationEvent in)56 public void postIfNotPending(Component heavy, InvocationEvent in) { 57 boolean shouldPost = false; 58 synchronized (this) { 59 if (!pending) { 60 pending = shouldPost = true; 61 } 62 } 63 if (shouldPost) { 64 SunToolkit.postEvent(SunToolkit.targetToAppContext(heavy), in); 65 } 66 } 67 } 68 69 /** 70 * Use a singleton NativeUpdater for better performance. We cannot use 71 * a singleton InvocationEvent because we want each event to have a fresh 72 * timestamp. 73 */ 74 private final NativeUpdater nativeUpdater = new NativeUpdater(); 75 76 /** 77 * The last time the cursor was updated, in milliseconds. 78 */ 79 private long lastUpdateMillis; 80 81 /** 82 * Locking object for synchronizing access to lastUpdateMillis. The VM 83 * does not guarantee atomicity of longs. 84 */ 85 private final Object lastUpdateLock = new Object(); 86 87 /** 88 * Should be called for any activity at the Java level which may affect 89 * the global cursor, except for Java MOUSE_MOVED events. 90 */ updateCursorImmediately()91 public void updateCursorImmediately() { 92 synchronized (nativeUpdater) { 93 nativeUpdater.pending = false; 94 } 95 _updateCursor(false); 96 } 97 98 /** 99 * Should be called in response to Java MOUSE_MOVED events. The update 100 * will be discarded if the InputEvent is outdated. 101 * 102 * @param e the InputEvent which triggered the cursor update. 103 */ updateCursorImmediately(InputEvent e)104 public void updateCursorImmediately(InputEvent e) { 105 boolean shouldUpdate; 106 synchronized (lastUpdateLock) { 107 shouldUpdate = (e.getWhen() >= lastUpdateMillis); 108 } 109 if (shouldUpdate) { 110 _updateCursor(true); 111 } 112 } 113 114 /** 115 * Should be called in response to a native mouse enter or native mouse 116 * button released message. Should not be called during a mouse drag. 117 */ updateCursorLater(Component heavy)118 public void updateCursorLater(Component heavy) { 119 nativeUpdater.postIfNotPending(heavy, new InvocationEvent 120 (Toolkit.getDefaultToolkit(), nativeUpdater)); 121 } 122 GlobalCursorManager()123 protected GlobalCursorManager() { } 124 125 /** 126 * Set the global cursor to the specified cursor. The component over 127 * which the Cursor current resides is provided as a convenience. Not 128 * all platforms may require the Component. 129 */ setCursor(Component comp, Cursor cursor, boolean useCache)130 protected abstract void setCursor(Component comp, Cursor cursor, 131 boolean useCache); 132 /** 133 * Returns the global cursor position, in screen coordinates. 134 */ getCursorPos(Point p)135 protected abstract void getCursorPos(Point p); 136 getLocationOnScreen(Component com)137 protected abstract Point getLocationOnScreen(Component com); 138 139 /** 140 * Returns the most specific, visible, heavyweight Component 141 * under the cursor. This method should return null iff the cursor is 142 * not over any Java Window. 143 * 144 * @param useCache If true, the implementation is free to use caching 145 * mechanisms because the Z-order, visibility, and enabled state of the 146 * Components has not changed. If false, the implementation should not 147 * make these assumptions. 148 */ findHeavyweightUnderCursor(boolean useCache)149 protected abstract Component findHeavyweightUnderCursor(boolean useCache); 150 151 /** 152 * Updates the global cursor. We apply a three-step scheme to cursor 153 * updates:<p> 154 * 155 * (1) InputEvent updates which are outdated are discarded by 156 * {@code updateCursorImmediately(InputEvent)}.<p> 157 * 158 * (2) If 'useCache' is true, the native code is free to use a cached 159 * value to determine the most specific, visible, enabled heavyweight 160 * because this update is occurring in response to a mouse move. If 161 * 'useCache' is false, the native code must perform a new search given 162 * the current mouse coordinates. 163 * 164 * (3) Once we have determined the most specific, visible, enabled 165 * heavyweight, we use findComponentAt to find the most specific, visible, 166 * enabled Component. 167 */ _updateCursor(boolean useCache)168 private void _updateCursor(boolean useCache) { 169 170 synchronized (lastUpdateLock) { 171 lastUpdateMillis = System.currentTimeMillis(); 172 } 173 174 Point queryPos = null, p = null; 175 Component comp; 176 177 try { 178 comp = findHeavyweightUnderCursor(useCache); 179 if (comp == null) { 180 updateCursorOutOfJava(); 181 return; 182 } 183 184 if (comp instanceof Window) { 185 p = AWTAccessor.getComponentAccessor().getLocation(comp); 186 } else if (comp instanceof Container) { 187 p = getLocationOnScreen(comp); 188 } 189 if (p != null) { 190 queryPos = new Point(); 191 getCursorPos(queryPos); 192 Component c = AWTAccessor.getContainerAccessor(). 193 findComponentAt((Container) comp, 194 queryPos.x - p.x, queryPos.y - p.y, false); 195 196 // If findComponentAt returns null, then something bad has 197 // happened. For example, the heavyweight Component may 198 // have been hidden or disabled by another thread. In that 199 // case, we'll just use the originial heavyweight. 200 if (c != null) { 201 comp = c; 202 } 203 } 204 205 setCursor(comp, AWTAccessor.getComponentAccessor().getCursor(comp), useCache); 206 207 } catch (IllegalComponentStateException e) { 208 // Shouldn't happen, but if it does, abort. 209 } 210 } 211 updateCursorOutOfJava()212 protected void updateCursorOutOfJava() { 213 // Cursor is not over a Java Window. Do nothing...usually 214 // But we need to update it in case of grab on X. 215 } 216 } 217