1 /* 2 * Copyright (c) 2014, 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 test.com.sun.glass.ui.monocle; 27 28 import com.sun.glass.ui.monocle.LinuxInputShim; 29 import com.sun.glass.ui.monocle.LinuxSystemShim; 30 import javafx.animation.AnimationTimer; 31 import javafx.application.Platform; 32 33 import java.io.File; 34 import java.io.IOException; 35 import java.nio.ByteBuffer; 36 import java.nio.ByteOrder; 37 import java.util.BitSet; 38 import java.util.concurrent.CountDownLatch; 39 40 public class LensUInput extends NativeUInput { 41 42 private long fd = -1; 43 private long devFD = -1; 44 private String deviceName; 45 private short bustype, product, vendor, version; 46 private String devNode; 47 private static int devNodeSuffix = 0; 48 49 private static final String INPUT_PATH = "/tmp/testInput"; 50 private static final String DEVNODE_PREFIX = "/tmp/input"; 51 52 private static ByteBuffer byteBuffer = ByteBuffer.allocateDirect(256) 53 .order(ByteOrder.nativeOrder()); 54 private static boolean isSetup; 55 56 @Override 57 public void setup() { 58 if (isSetup) { 59 return; 60 } 61 try { 62 LinuxSystemShim.loadLibrary(); 63 LinuxSystemShim.setenv("LENS_TEST_INPUT", INPUT_PATH, true); 64 File pipe = new File(INPUT_PATH); 65 if (pipe.exists()) { 66 pipe.delete(); 67 } 68 pipe.deleteOnExit(); 69 if (LinuxSystemShim.mkfifo(pipe.getPath(), LinuxSystemShim.S_IRWXU) != 0) { 70 throw new IOException(LinuxSystemShim.getErrorMessage()); 71 } 72 isSetup = true; 73 } catch (IOException e) { 74 e.printStackTrace(); 75 } 76 } 77 78 @Override 79 public void init() { 80 super.init(); 81 fd = -1; 82 devFD = -1; 83 deviceName = "Test Input"; 84 bustype = 0x18; // BUS_USB 85 product = 0x01; 86 vendor = 0x01; 87 version = 0x01; 88 } 89 90 @Override 91 public void dispose() { 92 } 93 94 @Override 95 public void waitForQuiet() throws InterruptedException { 96 final CountDownLatch frameCounter = new CountDownLatch(3); 97 Platform.runLater(() -> new AnimationTimer() { 98 @Override 99 public void handle(long now) { 100 frameCounter.countDown(); 101 if (frameCounter.getCount() == 0l) { 102 stop(); 103 } 104 } 105 }.start()); 106 frameCounter.await(); 107 } 108 109 private void writeBytes(int length) throws IOException { 110 int offset = 0; 111 while (offset < length) { 112 int bytesWritten = (int) LinuxSystemShim.write(fd, byteBuffer, offset, length); 113 if (bytesWritten < 0) { 114 throw new IOException(LinuxSystemShim.getErrorMessage()); 115 } else { 116 offset += bytesWritten; 117 } 118 } 119 } 120 121 private void writeInt(int i) throws IOException { 122 byteBuffer.putInt(0, i); 123 writeBytes(4); 124 } 125 126 private void writeShort(short s) throws IOException { 127 byteBuffer.putShort(0, s); 128 writeBytes(2); 129 } 130 131 private void writeString(String s) throws IOException { 132 byte[] bytes = s.getBytes("UTF-8"); 133 int offset = 0; 134 while (offset < bytes.length) { 135 int length = Math.min(byteBuffer.capacity(), bytes.length - offset); 136 byteBuffer.clear(); 137 byteBuffer.put(bytes, offset, length); 138 writeBytes(length); 139 offset += length; 140 } 141 // add a terminating null byte 142 byteBuffer.put(0, (byte) 0); 143 writeBytes(1); 144 } 145 146 @Override 147 protected void createDevice() throws IOException { 148 devNode = DEVNODE_PREFIX + (++devNodeSuffix); 149 File devNodeFile = new File(devNode); 150 if (devNodeFile.exists()) { 151 devNodeFile.delete(); 152 } 153 if (LinuxSystemShim.mkfifo(devNode, LinuxSystemShim.S_IRWXU) != 0) { 154 throw new IOException(LinuxSystemShim.getErrorMessage()); 155 } 156 writeInt(1); // attach device 157 writeShort(bustype); 158 writeShort(product); 159 writeShort(vendor); 160 writeShort(version); 161 writeString(deviceName); 162 writeString(devNode); 163 writeString("Test Input Device"); 164 BitSet evBits = capabilities.get("ev"); 165 if (evBits != null) { 166 for (int i = 0; i < LinuxInputShim.EV_MAX; i++) { 167 if (evBits.get(i)) { 168 writeInt(i); 169 } 170 } 171 } 172 writeInt(-1); 173 BitSet keyBits = capabilities.get("key"); 174 if (keyBits != null) { 175 for (int i = 0; i < LinuxInputShim.KEY_MAX; i++) { 176 if (keyBits.get(i)) { 177 writeInt(i); 178 } 179 } 180 } 181 writeInt(-1); 182 BitSet relBits = capabilities.get("rel"); 183 if (relBits != null) { 184 for (int i = 0; i < LinuxInputShim.REL_MAX; i++) { 185 if (relBits.get(i)) { 186 writeInt(i); 187 } 188 } 189 } 190 writeInt(-1); 191 BitSet absBits = capabilities.get("abs"); 192 if (absBits != null) { 193 for (int i = 0; i < LinuxInputShim.ABS_MAX; i++) { 194 if (absBits.get(i)) { 195 writeInt(i); 196 int[] caps = absCaps.get(i); 197 if (caps == null) { 198 caps = new int[6]; 199 } 200 for (int val : caps) { 201 writeInt(val); 202 } 203 } 204 } 205 } 206 writeInt(-1); 207 for (String key : udevManifest.keySet()) { 208 writeString(key); 209 writeString(udevManifest.get(key)); 210 } 211 writeString(""); 212 devFD = openPipe(devNode); 213 } 214 215 @Override 216 protected void destroyDevice() throws IOException { 217 if (devFD != -1l) { 218 LinuxSystemShim.close(devFD); 219 devFD = -1; 220 } 221 new File(devNode).delete(); 222 writeInt(2); // detach device 223 writeString(devNode); 224 } 225 226 private long openPipe(String path) throws IOException { 227 long timeOut = System.currentTimeMillis() + 10000l; 228 while (System.currentTimeMillis() < timeOut) { 229 long pipeFD = LinuxSystemShim.open(path, 230 LinuxSystemShim.O_WRONLY | LinuxSystemShim.O_NONBLOCK); 231 if (pipeFD < 0l) { 232 if (LinuxSystemShim.errno() == LinuxSystemShim.ENXIO) { // no reader on pipe 233 try { 234 Thread.sleep(100l); 235 } catch (InterruptedException e) { } 236 } else { 237 break; 238 } 239 } else { 240 return pipeFD; 241 } 242 } 243 throw new IOException(LinuxSystemShim.getErrorMessage()); 244 } 245 246 @Override 247 protected void openConnection() throws IOException { 248 fd = openPipe(INPUT_PATH); 249 } 250 251 @Override 252 protected void closeConnection() { 253 if (fd >= 0l) { 254 LinuxSystemShim.close(fd); 255 fd = -1l; 256 } 257 } 258 259 @Override 260 public void write(ByteBuffer buffer) throws IOException { 261 int offset = 0; 262 while (offset < buffer.limit()) { 263 int bytesWritten = (int) LinuxSystemShim.write(devFD, buffer, offset, buffer.limit()); 264 if (bytesWritten < 0) { 265 if (LinuxSystemShim.errno() == LinuxSystemShim.EAGAIN) { 266 try { 267 Thread.sleep(1); 268 } catch (InterruptedException e) { 269 e.printStackTrace(); 270 } 271 } else { 272 throw new IOException(LinuxSystemShim.getErrorMessage()); 273 } 274 } else { 275 offset += bytesWritten; 276 } 277 } 278 LinuxSystemShim.ioctl(devFD, LinuxSystemShim.I_FLUSH, LinuxSystemShim.FLUSHRW); 279 } 280 }