/*********************************************************************** * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***********************************************************************/ package org.mt4j.input.inputSources; import java.util.HashMap; import javax.swing.SwingUtilities; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.SimpleLayout; import org.mt4j.MTApplication; import org.mt4j.input.inputData.ActiveCursorPool; import org.mt4j.input.inputData.InputCursor; import org.mt4j.input.inputData.MTFingerInputEvt; import org.mt4j.input.inputData.MTWin7TouchInputEvt; import org.mt4j.util.MT4jSettings; /** * Input source for native Windows 7 WM_TOUCH messages for single/multi-touch. * <br>Be careful to instantiate this class only ONCE! * * @author C.Ruff * */ public class Win7NativeTouchSource extends AbstractInputSource { /** The Constant logger. */ private static final Logger logger = Logger.getLogger(Win7NativeTouchSource.class.getName()); static{ // logger.setLevel(Level.ERROR); // logger.setLevel(Level.DEBUG); logger.setLevel(Level.INFO); SimpleLayout l = new SimpleLayout(); ConsoleAppender ca = new ConsoleAppender(l); logger.addAppender(ca); } static boolean loaded = false; private MTApplication app; private int sunAwtCanvasHandle; private int awtFrameHandle; private Native_WM_TOUCH_Event wmTouchEvent; private boolean initialized; private boolean success; private HashMap<Integer, Long> touchToCursorID; private static final String dllName32 = "Win7Touch"; private static final String dllName64 = "Win7Touch64"; private static final String canvasClassName = "SunAwtCanvas"; // NATIVE METHODS // private native int findWindow(String tmpTitle, String subWindowTitle); private native boolean init(long HWND); private native boolean getSystemMetrics(); private native boolean quit(); private native boolean pollEvent(Native_WM_TOUCH_Event myEvent); // NATIVE METHODS // //TODO remove points[] array? -> if digitizer has more than 255 touchpoints we could get out of bounds in points[] //TODO did we "delete [] ti;" in wndProc? //TODO- check dpi, if higher than 96 - if the screen is set to High DPI (more than 96 DPI), // you may also need to divide the values by 96 and multiply by the current DPI. (or already handled by ScreenToClient()?) //TODO try again getWindow() in windows -> no success in all thread (we probably need to do it in the awt-windows thread?) //TODO make singleton to avoid multiple instances /** * Instantiates a new win7 native touch source. * * @param mtApp the mt app */ public Win7NativeTouchSource(MTApplication mtApp) { super(mtApp); this.app = mtApp; this.success = false; String platform = System.getProperty("os.name").toLowerCase(); logger.debug("Platform: \"" + platform + "\""); // /* if (!platform.contains("windows 7")) { logger.error("Win7NativeTouchSource input source can only be used on platforms running windows 7!"); return; } if (!loaded){ loaded = true; String dllName = (MT4jSettings.getInstance().getArchitecture() == MT4jSettings.ARCHITECTURE_32_BIT)? dllName32 : dllName64; System.loadLibrary(dllName); // System.load(System.getProperty("user.dir") + File.separator + dllName + ".dll"); }else{ logger.error("Win7NativeTouchSource may only be instantiated once."); return; } boolean touchAvailable = this.getSystemMetrics(); if (!touchAvailable){ logger.error("Windows 7 Touch Input currently not available!"); return; }else{ logger.info("Windows 7 Touch Input available."); } // */ wmTouchEvent = new Native_WM_TOUCH_Event(); wmTouchEvent.id = -1; wmTouchEvent.type = -1; wmTouchEvent.x = -1; wmTouchEvent.y = -1; initialized = false; touchToCursorID = new HashMap<Integer, Long>(); this.getNativeWindowHandles(); success = true; } // private boolean addedArtificalTouchDown = false; //FIXME REMOVE public boolean isSuccessfullySetup() { return success; } @Override public void pre(){ //we dont have to call registerPre() again (already in superclass and called there) if (initialized){ //Only poll events if native c++ core was initialized successfully while (pollEvent(wmTouchEvent)) { /* //FIXME TEST, make a artifical TOUCH_DOWN event REMOVE LATER! if (!addedArtificalTouchDown){ addedArtificalTouchDown = true; wmTouchEvent.type = Native_WM_TOUCH_Event.TOUCH_DOWN; } */ switch (wmTouchEvent.type) { case Native_WM_TOUCH_Event.TOUCH_DOWN:{ // logger.debug("TOUCH_DOWN ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); InputCursor c = new InputCursor(); long cursorID = c.getId(); MTWin7TouchInputEvt touchEvt = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_DETECTED, c); int touchID = wmTouchEvent.id; ActiveCursorPool.getInstance().putActiveCursor(cursorID, c); touchToCursorID.put(touchID, cursorID); this.enqueueInputEvent(touchEvt); break; }case Native_WM_TOUCH_Event.TOUCH_MOVE:{ // logger.debug("TOUCH_MOVE ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); // System.out.println("Contact area X:" + wmTouchEvent.contactSizeX + " Y:" + wmTouchEvent.contactSizeY); Long cursorID = touchToCursorID.get(wmTouchEvent.id); if (cursorID != null){ InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID); if (c != null){ MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_UPDATED, c); this.enqueueInputEvent(te); } } break; }case Native_WM_TOUCH_Event.TOUCH_UP:{ // logger.debug("TOUCH_UP ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y); Long cursorID = touchToCursorID.get(wmTouchEvent.id); if (cursorID != null){ InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID); if (c != null){ MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_ENDED, c); this.enqueueInputEvent(te); } ActiveCursorPool.getInstance().removeCursor(cursorID); touchToCursorID.remove(wmTouchEvent.id); } break; }default: break; } } } super.pre(); } private void getNativeWindowHandles(){ if (app.frame == null){ logger.error("applet.frame == null! -> cant set up windows 7 input!"); return; } //TODO kind of hacky way of getting the HWND..but there seems to be no real alternative(?) final String oldTitle = app.frame.getTitle(); final String tmpTitle = "Initializing Native Windows 7 Touch Input " + Math.random(); app.frame.setTitle(tmpTitle); logger.debug("Temp title: " + tmpTitle); //FIXME TEST REMOVE //Window window = SwingUtilities.getWindowAncestor(app); // AWTUtilities.setWindowOpacity(window, 0.5f); //works! //Invokelater because of some crash issue //-> maybe we need to wait a frame until windows is informed of the window name change SwingUtilities.invokeLater(new Runnable() { public void run() { int awtCanvasHandle = 0; try { // //TODO also search for window class? awtCanvasHandle = (int)findWindow(tmpTitle, canvasClassName); setSunAwtCanvasHandle(awtCanvasHandle); } catch (Exception e) { System.err.println(e.getMessage()); } app.frame.setTitle(oldTitle); //Reset title text } }); } private void setTopWindowHandle(int HWND){ if (HWND > 0){ this.awtFrameHandle = HWND; logger.debug("-> Found AWT HWND: " + this.awtFrameHandle); }else{ logger.error("-> Couldnt retrieve the top window handle!"); } } private void setSunAwtCanvasHandle(int HWND){ if (HWND > 0){ this.sunAwtCanvasHandle = HWND; logger.debug("-> Found SunAwtCanvas HWND: " + this.sunAwtCanvasHandle); //Initialize c++ core (subclass etc) this.init(this.sunAwtCanvasHandle); this.initialized = true; }else{ logger.error("-> Couldnt retrieve the SunAwtCanvas handle!"); } } private class Native_WM_TOUCH_Event{ //can be real enums in Java 5.0. /** The Constant TOUCH_DOWN. */ public static final int TOUCH_DOWN = 0; /** The Constant TOUCH_MOVE. */ public static final int TOUCH_MOVE = 1; /** The Constant TOUCH_UP. */ public static final int TOUCH_UP = 2; /** The type. */ public int type; /** The id. */ public int id; /** The x value. */ public int x; /** The y value. */ public int y; /** The contact size area X dimension */ public int contactSizeX; /** The contact size area Y dimension */ public int contactSizeY; } }