1 /* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.lwawt.macosx; 27 28 import sun.awt.SunToolkit; 29 import sun.lwawt.LWWindowPeer; 30 import sun.lwawt.PlatformEventNotifier; 31 32 import java.awt.Toolkit; 33 import java.awt.event.MouseEvent; 34 import java.awt.event.InputEvent; 35 import java.awt.event.MouseWheelEvent; 36 import java.awt.event.KeyEvent; 37 import java.util.Locale; 38 39 /** 40 * Translates NSEvents/NPCocoaEvents into AWT events. 41 */ 42 final class CPlatformResponder { 43 44 private final PlatformEventNotifier eventNotifier; 45 private final boolean isNpapiCallback; 46 private int lastKeyPressCode = KeyEvent.VK_UNDEFINED; 47 48 CPlatformResponder(final PlatformEventNotifier eventNotifier, 49 final boolean isNpapiCallback) { 50 this.eventNotifier = eventNotifier; 51 this.isNpapiCallback = isNpapiCallback; 52 } 53 54 /** 55 * Handles mouse events. 56 */ 57 void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber, 58 int clickCount, int x, int y, int absX, int absY) { 59 final SunToolkit tk = (SunToolkit)Toolkit.getDefaultToolkit(); 60 if ((buttonNumber > 2 && !tk.areExtraMouseButtonsEnabled()) 61 || buttonNumber > tk.getNumberOfButtons() - 1) { 62 return; 63 } 64 65 int jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) : 66 NSEvent.nsToJavaEventType(eventType); 67 68 int jbuttonNumber = MouseEvent.NOBUTTON; 69 int jclickCount = 0; 70 71 if (jeventType != MouseEvent.MOUSE_MOVED && 72 jeventType != MouseEvent.MOUSE_ENTERED && 73 jeventType != MouseEvent.MOUSE_EXITED) 74 { 75 jbuttonNumber = NSEvent.nsToJavaButton(buttonNumber); 76 jclickCount = clickCount; 77 } 78 79 int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags); 80 boolean jpopupTrigger = NSEvent.isPopupTrigger(jmodifiers); 81 82 eventNotifier.notifyMouseEvent(jeventType, System.currentTimeMillis(), jbuttonNumber, 83 x, y, absX, absY, jmodifiers, jclickCount, 84 jpopupTrigger, null); 85 } 86 87 /** 88 * Handles scroll events. 89 */ 90 void handleScrollEvent(final int x, final int y, final int absX, 91 final int absY, final int modifierFlags, 92 final double deltaX, final double deltaY) { 93 int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags); 94 final boolean isShift = (jmodifiers & InputEvent.SHIFT_DOWN_MASK) != 0; 95 96 // Vertical scroll. 97 if (!isShift && deltaY != 0.0) { 98 dispatchScrollEvent(x, y, absX, absY, jmodifiers, deltaY); 99 } 100 // Horizontal scroll or shirt+vertical scroll. 101 final double delta = isShift && deltaY != 0.0 ? deltaY : deltaX; 102 if (delta != 0.0) { 103 jmodifiers |= InputEvent.SHIFT_DOWN_MASK; 104 dispatchScrollEvent(x, y, absX, absY, jmodifiers, delta); 105 } 106 } 107 108 private void dispatchScrollEvent(final int x, final int y, final int absX, 109 final int absY, final int modifiers, 110 final double delta) { 111 final long when = System.currentTimeMillis(); 112 final int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL; 113 final int scrollAmount = 1; 114 int wheelRotation = (int) delta; 115 int signum = (int) Math.signum(delta); 116 if (signum * delta < 1) { 117 wheelRotation = signum; 118 } 119 // invert the wheelRotation for the peer 120 eventNotifier.notifyMouseWheelEvent(when, x, y, absX, absY, modifiers, 121 scrollType, scrollAmount, 122 -wheelRotation, -delta, null); 123 } 124 125 /** 126 * Handles key events. 127 */ 128 void handleKeyEvent(int eventType, int modifierFlags, String chars, String charsIgnoringModifiers, 129 short keyCode, boolean needsKeyTyped, boolean needsKeyReleased) { 130 boolean isFlagsChangedEvent = 131 isNpapiCallback ? (eventType == CocoaConstants.NPCocoaEventFlagsChanged) : 132 (eventType == CocoaConstants.NSFlagsChanged); 133 134 int jeventType = KeyEvent.KEY_PRESSED; 135 int jkeyCode = KeyEvent.VK_UNDEFINED; 136 int jkeyLocation = KeyEvent.KEY_LOCATION_UNKNOWN; 137 boolean postsTyped = false; 138 boolean spaceKeyTyped = false; 139 140 char testChar = KeyEvent.CHAR_UNDEFINED; 141 boolean isDeadChar = (chars!= null && chars.length() == 0); 142 143 if (isFlagsChangedEvent) { 144 int[] in = new int[] {modifierFlags, keyCode}; 145 int[] out = new int[3]; // [jkeyCode, jkeyLocation, jkeyType] 146 147 NSEvent.nsKeyModifiersToJavaKeyInfo(in, out); 148 149 jkeyCode = out[0]; 150 jkeyLocation = out[1]; 151 jeventType = out[2]; 152 } else { 153 if (chars != null && chars.length() > 0) { 154 testChar = chars.charAt(0); 155 156 //Check if String chars contains SPACE character. 157 if (chars.trim().isEmpty()) { 158 spaceKeyTyped = true; 159 } 160 } 161 162 char testCharIgnoringModifiers = charsIgnoringModifiers != null && charsIgnoringModifiers.length() > 0 ? 163 charsIgnoringModifiers.charAt(0) : KeyEvent.CHAR_UNDEFINED; 164 165 int[] in = new int[] {testCharIgnoringModifiers, isDeadChar ? 1 : 0, modifierFlags, keyCode}; 166 int[] out = new int[3]; // [jkeyCode, jkeyLocation, deadChar] 167 168 postsTyped = NSEvent.nsToJavaKeyInfo(in, out); 169 if (!postsTyped) { 170 testChar = KeyEvent.CHAR_UNDEFINED; 171 } 172 173 if(isDeadChar){ 174 testChar = (char) out[2]; 175 if(testChar == 0){ 176 return; 177 } 178 } 179 180 // If Pinyin Simplified input method is selected, CAPS_LOCK key is supposed to switch 181 // input to latin letters. 182 // It is necessary to use testCharIgnoringModifiers instead of testChar for event 183 // generation in such case to avoid uppercase letters in text components. 184 LWCToolkit lwcToolkit = (LWCToolkit)Toolkit.getDefaultToolkit(); 185 if (lwcToolkit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK) && 186 Locale.SIMPLIFIED_CHINESE.equals(lwcToolkit.getDefaultKeyboardLocale())) { 187 testChar = testCharIgnoringModifiers; 188 } 189 190 jkeyCode = out[0]; 191 jkeyLocation = out[1]; 192 jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) : 193 NSEvent.nsToJavaEventType(eventType); 194 } 195 196 char javaChar = NSEvent.nsToJavaChar(testChar, modifierFlags, spaceKeyTyped); 197 // Some keys may generate a KEY_TYPED, but we can't determine 198 // what that character is. That's likely a bug, but for now we 199 // just check for CHAR_UNDEFINED. 200 if (javaChar == KeyEvent.CHAR_UNDEFINED) { 201 postsTyped = false; 202 } 203 204 int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags); 205 long when = System.currentTimeMillis(); 206 207 if (jeventType == KeyEvent.KEY_PRESSED) { 208 lastKeyPressCode = jkeyCode; 209 } 210 eventNotifier.notifyKeyEvent(jeventType, when, jmodifiers, 211 jkeyCode, javaChar, jkeyLocation); 212 213 // Current browser may be sending input events, so don't 214 // post the KEY_TYPED here. 215 postsTyped &= needsKeyTyped; 216 217 // That's the reaction on the PRESSED (not RELEASED) event as it comes to 218 // appear in MacOSX. 219 // Modifier keys (shift, etc) don't want to send TYPED events. 220 // On the other hand we don't want to generate keyTyped events 221 // for clipboard related shortcuts like Meta + [CVX] 222 if (jeventType == KeyEvent.KEY_PRESSED && postsTyped && 223 (jmodifiers & KeyEvent.META_DOWN_MASK) == 0) { 224 // Enter and Space keys finish the input method processing, 225 // KEY_TYPED and KEY_RELEASED events for them are synthesized in handleInputEvent. 226 if (needsKeyReleased && (jkeyCode == KeyEvent.VK_ENTER || jkeyCode == KeyEvent.VK_SPACE)) { 227 return; 228 } 229 eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers, 230 KeyEvent.VK_UNDEFINED, javaChar, 231 KeyEvent.KEY_LOCATION_UNKNOWN); 232 //If events come from Firefox, released events should also be generated. 233 if (needsKeyReleased) { 234 eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED, when, jmodifiers, 235 jkeyCode, javaChar, 236 KeyEvent.KEY_LOCATION_UNKNOWN); 237 } 238 } 239 } 240 241 void handleInputEvent(String text) { 242 if (text != null) { 243 int index = 0, length = text.length(); 244 char c = 0; 245 while (index < length) { 246 c = text.charAt(index); 247 eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, 248 System.currentTimeMillis(), 249 0, KeyEvent.VK_UNDEFINED, c, 250 KeyEvent.KEY_LOCATION_UNKNOWN); 251 index++; 252 } 253 eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED, 254 System.currentTimeMillis(), 255 0, lastKeyPressCode, c, 256 KeyEvent.KEY_LOCATION_UNKNOWN); 257 } 258 } 259 260 void handleWindowFocusEvent(boolean gained, LWWindowPeer opposite) { 261 eventNotifier.notifyActivation(gained, opposite); 262 } 263 }