1 /* 2 * Copyright (c) 1999, 2007, 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 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 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 */ 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 */ 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 */ 118 public void updateCursorLater(Component heavy) { 119 nativeUpdater.postIfNotPending(heavy, new InvocationEvent 120 (Toolkit.getDefaultToolkit(), nativeUpdater)); 121 } 122 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 */ 130 protected abstract void setCursor(Component comp, Cursor cursor, 131 boolean useCache); 132 /** 133 * Returns the global cursor position, in screen coordinates. 134 */ 135 protected abstract void getCursorPos(Point p); 136 137 protected abstract Component findComponentAt(Container con, int x, int y); 138 protected abstract Point getLocationOnScreen(Component com); 139 140 /** 141 * Returns the most specific, visible, heavyweight Component 142 * under the cursor. This method should return null iff the cursor is 143 * not over any Java Window. 144 * 145 * @param useCache If true, the implementation is free to use caching 146 * mechanisms because the Z-order, visibility, and enabled state of the 147 * Components has not changed. If false, the implementation should not 148 * make these assumptions. 149 */ 150 protected abstract Component findHeavyweightUnderCursor(boolean useCache); 151 152 /** 153 * Updates the global cursor. We apply a three-step scheme to cursor 154 * updates:<p> 155 * 156 * (1) InputEvent updates which are outdated are discarded by 157 * <code>updateCursorImmediately(InputEvent)</code>.<p> 158 * 159 * (2) If 'useCache' is true, the native code is free to use a cached 160 * value to determine the most specific, visible, enabled heavyweight 161 * because this update is occuring in response to a mouse move. If 162 * 'useCache' is false, the native code must perform a new search given 163 * the current mouse coordinates. 164 * 165 * (3) Once we have determined the most specific, visible, enabled 166 * heavyweight, we use findComponentAt to find the most specific, visible, 167 * enabled Component. 168 */ 169 private void _updateCursor(boolean useCache) { 170 171 synchronized (lastUpdateLock) { 172 lastUpdateMillis = System.currentTimeMillis(); 173 } 174 175 Point queryPos = null, p = null; 176 Component comp; 177 178 try { 179 comp = findHeavyweightUnderCursor(useCache); 180 if (comp == null) { 181 updateCursorOutOfJava(); 182 return; 183 } 184 185 if (comp instanceof Window) { 186 p = AWTAccessor.getComponentAccessor().getLocation(comp); 187 } else if (comp instanceof Container) { 188 p = getLocationOnScreen(comp); 189 } 190 if (p != null) { 191 queryPos = new Point(); 192 getCursorPos(queryPos); 193 Component c = findComponentAt((Container)comp, 194 queryPos.x - p.x, 195 queryPos.y - p.y); 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 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 }