Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 538 | chris | 1 | /*********************************************************************** |
| 2 | * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved. |
||
| 3 | * |
||
| 4 | * This program is free software: you can redistribute it and/or modify |
||
| 5 | * it under the terms of the GNU General Public License as published by |
||
| 6 | * the Free Software Foundation, either version 3 of the License, or |
||
| 7 | * (at your option) any later version. |
||
| 8 | * |
||
| 9 | * This program is distributed in the hope that it will be useful, |
||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 12 | * GNU General Public License for more details. |
||
| 13 | * |
||
| 14 | * You should have received a copy of the GNU General Public License |
||
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
| 16 | * |
||
| 17 | ***********************************************************************/ |
||
| 18 | package org.mt4j.input.inputSources; |
||
| 19 | |||
| 20 | import java.util.HashMap; |
||
| 21 | |||
| 22 | import javax.swing.SwingUtilities; |
||
| 23 | |||
| 24 | import org.mt4j.AbstractMTApplication; |
||
| 25 | import org.mt4j.input.inputData.ActiveCursorPool; |
||
| 26 | import org.mt4j.input.inputData.InputCursor; |
||
| 27 | import org.mt4j.input.inputData.MTFingerInputEvt; |
||
| 28 | import org.mt4j.input.inputData.MTWin7TouchInputEvt; |
||
| 29 | import org.mt4j.util.MT4jSettings; |
||
| 30 | import org.mt4j.util.logging.ILogger; |
||
| 31 | import org.mt4j.util.logging.MTLoggerFactory; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * Input source for native Windows 7 WM_TOUCH messages for single/multi-touch. |
||
| 35 | * <br>Be careful to instantiate this class only ONCE! |
||
| 36 | * |
||
| 37 | * @author C.Ruff |
||
| 38 | * |
||
| 39 | */ |
||
| 40 | public class Win7NativeTouchSource extends AbstractInputSource { |
||
| 41 | /** The Constant logger. */ |
||
| 42 | private static final ILogger logger = MTLoggerFactory.getLogger(Win7NativeTouchSource.class.getName()); |
||
| 43 | static{ |
||
| 44 | // logger.setLevel(ILogger.ERROR); |
||
| 45 | // logger.setLevel(ILogger.DEBUG); |
||
| 46 | logger.setLevel(ILogger.INFO); |
||
| 47 | } |
||
| 48 | |||
| 49 | static boolean loaded = false; |
||
| 50 | |||
| 51 | private AbstractMTApplication app; |
||
| 52 | |||
| 53 | private int sunAwtCanvasHandle; |
||
| 54 | |||
| 55 | private int awtFrameHandle; |
||
| 56 | |||
| 57 | private Native_WM_TOUCH_Event wmTouchEvent; |
||
| 58 | |||
| 59 | private boolean initialized; |
||
| 60 | |||
| 61 | private boolean success; |
||
| 62 | |||
| 63 | private HashMap<Integer, Long> touchToCursorID; |
||
| 64 | |||
| 65 | private static final String dllName32 = "Win7Touch"; |
||
| 66 | |||
| 67 | private static final String dllName64 = "Win7Touch64"; |
||
| 68 | |||
| 69 | private static final String canvasClassName = "SunAwtCanvas"; |
||
| 70 | |||
| 71 | |||
| 72 | // NATIVE METHODS // |
||
| 73 | private native int findWindow(String tmpTitle, String subWindowTitle); |
||
| 74 | |||
| 75 | private native boolean init(long HWND); |
||
| 76 | |||
| 77 | private native boolean getSystemMetrics(); |
||
| 78 | |||
| 79 | private native boolean quit(); |
||
| 80 | |||
| 81 | private native boolean pollEvent(Native_WM_TOUCH_Event myEvent); |
||
| 82 | // NATIVE METHODS // |
||
| 83 | |||
| 84 | |||
| 85 | //TODO remove points[] array? -> if digitizer has more than 255 touchpoints we could get out of bounds in points[] |
||
| 86 | //TODO did we "delete [] ti;" in wndProc? |
||
| 87 | //TODO- check dpi, if higher than 96 - if the screen is set to High DPI (more than 96 DPI), |
||
| 88 | // you may also need to divide the values by 96 and multiply by the current DPI. (or already handled by ScreenToClient()?) |
||
| 89 | //TODO try again getWindow() in windows -> no success in all thread (we probably need to do it in the awt-windows thread?) |
||
| 90 | |||
| 91 | //TODO make singleton to avoid multiple instances |
||
| 92 | |||
| 93 | /** |
||
| 94 | * Instantiates a new win7 native touch source. |
||
| 95 | * |
||
| 96 | * @param mtApp the mt app |
||
| 97 | */ |
||
| 98 | public Win7NativeTouchSource(AbstractMTApplication mtApp) { |
||
| 99 | super(mtApp); |
||
| 100 | this.app = mtApp; |
||
| 101 | this.success = false; |
||
| 102 | |||
| 103 | String platform = System.getProperty("os.name").toLowerCase(); |
||
| 104 | logger.debug("Platform: \"" + platform + "\""); |
||
| 105 | |||
| 106 | // /* |
||
| 107 | if (!platform.contains("windows 7")) { |
||
| 108 | logger.error("Win7NativeTouchSource input source can only be used on platforms running windows 7!"); |
||
| 109 | return; |
||
| 110 | } |
||
| 111 | |||
| 112 | if (!loaded){ |
||
| 113 | loaded = true; |
||
| 114 | String dllName = (MT4jSettings.getInstance().getArchitecture() == MT4jSettings.ARCHITECTURE_32_BIT)? dllName32 : dllName64; |
||
| 115 | System.loadLibrary(dllName); |
||
| 116 | // System.load(System.getProperty("user.dir") + File.separator + dllName + ".dll"); |
||
| 117 | }else{ |
||
| 118 | logger.error("Win7NativeTouchSource may only be instantiated once."); |
||
| 119 | return; |
||
| 120 | } |
||
| 121 | |||
| 122 | boolean touchAvailable = this.getSystemMetrics(); |
||
| 123 | if (!touchAvailable){ |
||
| 124 | logger.error("Windows 7 Touch Input currently not available!"); |
||
| 125 | return; |
||
| 126 | }else{ |
||
| 127 | logger.info("Windows 7 Touch Input available."); |
||
| 128 | } |
||
| 129 | // */ |
||
| 130 | |||
| 131 | wmTouchEvent = new Native_WM_TOUCH_Event(); |
||
| 132 | wmTouchEvent.id = -1; |
||
| 133 | wmTouchEvent.type = -1; |
||
| 134 | wmTouchEvent.x = -1; |
||
| 135 | wmTouchEvent.y = -1; |
||
| 136 | |||
| 137 | initialized = false; |
||
| 138 | |||
| 139 | touchToCursorID = new HashMap<Integer, Long>(); |
||
| 140 | |||
| 141 | this.getNativeWindowHandles(); |
||
| 142 | success = true; |
||
| 143 | |||
| 144 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { |
||
| 145 | public void run() { |
||
| 146 | if (isSuccessfullySetup()){ |
||
| 147 | logger.debug("Cleaning up Win7 touch source.."); |
||
| 148 | quit(); |
||
| 149 | } |
||
| 150 | } |
||
| 151 | })); |
||
| 152 | } |
||
| 153 | |||
| 154 | |||
| 155 | // private boolean addedArtificalTouchDown = false; //FIXME REMOVE |
||
| 156 | |||
| 157 | public boolean isSuccessfullySetup() { |
||
| 158 | return success; |
||
| 159 | } |
||
| 160 | |||
| 161 | |||
| 162 | @Override |
||
| 163 | public void pre(){ //we dont have to call registerPre() again (already in superclass and called there) |
||
| 164 | if (initialized){ //Only poll events if native c++ core was initialized successfully |
||
| 165 | while (pollEvent(wmTouchEvent)) { |
||
| 166 | /* |
||
| 167 | //FIXME TEST, make a artifical TOUCH_DOWN event REMOVE LATER! |
||
| 168 | if (!addedArtificalTouchDown){ |
||
| 169 | addedArtificalTouchDown = true; |
||
| 170 | wmTouchEvent.type = Native_WM_TOUCH_Event.TOUCH_DOWN; |
||
| 171 | } |
||
| 172 | */ |
||
| 173 | |||
| 174 | switch (wmTouchEvent.type) { |
||
| 175 | case Native_WM_TOUCH_Event.TOUCH_DOWN:{ |
||
| 176 | // logger.debug("TOUCH_DOWN ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); |
||
| 177 | |||
| 178 | InputCursor c = new InputCursor(); |
||
| 179 | long cursorID = c.getId(); |
||
| 180 | MTWin7TouchInputEvt touchEvt = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_STARTED, c); |
||
| 181 | int touchID = wmTouchEvent.id; |
||
| 182 | ActiveCursorPool.getInstance().putActiveCursor(cursorID, c); |
||
| 183 | touchToCursorID.put(touchID, cursorID); |
||
| 184 | this.enqueueInputEvent(touchEvt); |
||
| 185 | |||
| 186 | break; |
||
| 187 | }case Native_WM_TOUCH_Event.TOUCH_MOVE:{ |
||
| 188 | // logger.debug("TOUCH_MOVE ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); |
||
| 189 | // System.out.println("Contact area X:" + wmTouchEvent.contactSizeX + " Y:" + wmTouchEvent.contactSizeY); |
||
| 190 | |||
| 191 | Long cursorID = touchToCursorID.get(wmTouchEvent.id); |
||
| 192 | if (cursorID != null){ |
||
| 193 | InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID); |
||
| 194 | if (c != null){ |
||
| 195 | MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_UPDATED, c); |
||
| 196 | this.enqueueInputEvent(te); |
||
| 197 | } |
||
| 198 | } |
||
| 199 | |||
| 200 | break; |
||
| 201 | }case Native_WM_TOUCH_Event.TOUCH_UP:{ |
||
| 202 | // logger.debug("TOUCH_UP ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); |
||
| 203 | |||
| 204 | Long cursorID = touchToCursorID.get(wmTouchEvent.id); |
||
| 205 | if (cursorID != null){ |
||
| 206 | InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID); |
||
| 207 | if (c != null){ |
||
| 208 | MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_ENDED, c); |
||
| 209 | this.enqueueInputEvent(te); |
||
| 210 | } |
||
| 211 | ActiveCursorPool.getInstance().removeCursor(cursorID); |
||
| 212 | touchToCursorID.remove(wmTouchEvent.id); |
||
| 213 | } |
||
| 214 | |||
| 215 | break; |
||
| 216 | }default: |
||
| 217 | break; |
||
| 218 | } |
||
| 219 | } |
||
| 220 | } |
||
| 221 | |||
| 222 | super.pre(); |
||
| 223 | } |
||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | private void getNativeWindowHandles(){ |
||
| 228 | if (app.frame == null){ |
||
| 229 | logger.error("applet.frame == null! -> cant set up windows 7 input!"); |
||
| 230 | return; |
||
| 231 | } |
||
| 232 | |||
| 233 | //TODO kind of hacky way of getting the HWND..but there seems to be no real alternative(?) |
||
| 234 | final String oldTitle = app.frame.getTitle(); |
||
| 235 | final String tmpTitle = "Initializing Native Windows 7 Touch Input " + Math.random(); |
||
| 236 | app.frame.setTitle(tmpTitle); |
||
| 237 | logger.debug("Temp title: " + tmpTitle); |
||
| 238 | |||
| 239 | //FIXME TEST REMOVE |
||
| 240 | //Window window = SwingUtilities.getWindowAncestor(app); |
||
| 241 | // AWTUtilities.setWindowOpacity(window, 0.5f); //works! |
||
| 242 | |||
| 243 | //Invokelater because of some crash issue |
||
| 244 | //-> maybe we need to wait a frame until windows is informed of the window name change |
||
| 245 | SwingUtilities.invokeLater(new Runnable() { |
||
| 246 | public void run() { |
||
| 247 | int awtCanvasHandle = 0; |
||
| 248 | try { |
||
| 249 | // //TODO also search for window class? |
||
| 250 | awtCanvasHandle = (int)findWindow(tmpTitle, canvasClassName); |
||
| 251 | setSunAwtCanvasHandle(awtCanvasHandle); |
||
| 252 | } catch (Exception e) { |
||
| 253 | System.err.println(e.getMessage()); |
||
| 254 | } |
||
| 255 | app.frame.setTitle(oldTitle); //Reset title text |
||
| 256 | } |
||
| 257 | }); |
||
| 258 | } |
||
| 259 | |||
| 260 | |||
| 261 | private void setTopWindowHandle(int HWND){ |
||
| 262 | if (HWND > 0){ |
||
| 263 | this.awtFrameHandle = HWND; |
||
| 264 | logger.debug("-> Found AWT HWND: " + this.awtFrameHandle); |
||
| 265 | }else{ |
||
| 266 | logger.error("-> Couldnt retrieve the top window handle!"); |
||
| 267 | } |
||
| 268 | } |
||
| 269 | |||
| 270 | |||
| 271 | private void setSunAwtCanvasHandle(int HWND){ |
||
| 272 | if (HWND > 0){ |
||
| 273 | this.sunAwtCanvasHandle = HWND; |
||
| 274 | logger.debug("-> Found SunAwtCanvas HWND: " + this.sunAwtCanvasHandle); |
||
| 275 | //Initialize c++ core (subclass etc) |
||
| 276 | this.init(this.sunAwtCanvasHandle); |
||
| 277 | this.initialized = true; |
||
| 278 | }else{ |
||
| 279 | logger.error("-> Couldnt retrieve the SunAwtCanvas handle!"); |
||
| 280 | } |
||
| 281 | } |
||
| 282 | |||
| 283 | private class Native_WM_TOUCH_Event{ |
||
| 284 | //can be real enums in Java 5.0. |
||
| 285 | /** The Constant TOUCH_DOWN. */ |
||
| 286 | public static final int TOUCH_DOWN = 0; |
||
| 287 | |||
| 288 | /** The Constant TOUCH_MOVE. */ |
||
| 289 | public static final int TOUCH_MOVE = 1; |
||
| 290 | |||
| 291 | /** The Constant TOUCH_UP. */ |
||
| 292 | public static final int TOUCH_UP = 2; |
||
| 293 | |||
| 294 | /** The type. */ |
||
| 295 | public int type; |
||
| 296 | |||
| 297 | /** The id. */ |
||
| 298 | public int id; |
||
| 299 | |||
| 300 | /** The x value. */ |
||
| 301 | public int x; |
||
| 302 | |||
| 303 | /** The y value. */ |
||
| 304 | public int y; |
||
| 305 | |||
| 306 | /** The contact size area X dimension */ |
||
| 307 | public int contactSizeX; |
||
| 308 | |||
| 309 | /** The contact size area Y dimension */ |
||
| 310 | public int contactSizeY; |
||
| 311 | } |
||
| 312 | |||
| 313 | } |