PageRenderTime 33ms CodeModel.GetById 13ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/mt4j/input/inputSources/Win7NativeTouchSource.java

http://mt4j.googlecode.com/
Java | 313 lines | 173 code | 67 blank | 73 comment | 20 complexity | ce13dd33f290ab83f1d07072b8d1d7c5 MD5 | raw file
  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 ***********************************************************************/
 18package org.mt4j.input.inputSources;
 19
 20import java.util.HashMap;
 21
 22import javax.swing.SwingUtilities;
 23
 24import org.mt4j.MTApplication;
 25import org.mt4j.input.inputData.ActiveCursorPool;
 26import org.mt4j.input.inputData.InputCursor;
 27import org.mt4j.input.inputData.MTFingerInputEvt;
 28import org.mt4j.input.inputData.MTWin7TouchInputEvt;
 29import org.mt4j.util.MT4jSettings;
 30import org.mt4j.util.logging.ILogger;
 31import 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 */
 40public 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 MTApplication 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(MTApplication 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}