/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. ***********************************************************************/
  18. package org.mt4j.input.inputSources;
  19. import java.util.HashMap;
  20. import javax.swing.SwingUtilities;
  21. import org.mt4j.MTApplication;
  22. import org.mt4j.input.inputData.ActiveCursorPool;
  23. import org.mt4j.input.inputData.InputCursor;
  24. import org.mt4j.input.inputData.MTFingerInputEvt;
  25. import org.mt4j.input.inputData.MTWin7TouchInputEvt;
  26. import org.mt4j.util.MT4jSettings;
  27. import org.mt4j.util.logging.ILogger;
  28. import org.mt4j.util.logging.MTLoggerFactory;
  29. /**
  30. * Input source for native Windows 7 WM_TOUCH messages for single/multi-touch.
  31. * <br>Be careful to instantiate this class only ONCE!
  32. *
  33. * @author C.Ruff
  34. *
  35. */
  36. public class Win7NativeTouchSource extends AbstractInputSource {
  37. /** The Constant logger. */
  38. private static final ILogger logger = MTLoggerFactory.getLogger(Win7NativeTouchSource.class.getName());
  39. static{
  40. // logger.setLevel(ILogger.ERROR);
  41. // logger.setLevel(ILogger.DEBUG);
  42. logger.setLevel(ILogger.INFO);
  43. }
  44. static boolean loaded = false;
  45. private MTApplication app;
  46. private int sunAwtCanvasHandle;
  47. private int awtFrameHandle;
  48. private Native_WM_TOUCH_Event wmTouchEvent;
  49. private boolean initialized;
  50. private boolean success;
  51. private HashMap<Integer, Long> touchToCursorID;
  52. private static final String dllName32 = "Win7Touch";
  53. private static final String dllName64 = "Win7Touch64";
  54. private static final String canvasClassName = "SunAwtCanvas";
  55. // NATIVE METHODS //
  56. private native int findWindow(String tmpTitle, String subWindowTitle);
  57. private native boolean init(long HWND);
  58. private native boolean getSystemMetrics();
  59. private native boolean quit();
  60. private native boolean pollEvent(Native_WM_TOUCH_Event myEvent);
  61. // NATIVE METHODS //
  62. //TODO remove points[] array? -> if digitizer has more than 255 touchpoints we could get out of bounds in points[]
  63. //TODO did we "delete [] ti;" in wndProc?
  64. //TODO- check dpi, if higher than 96 - if the screen is set to High DPI (more than 96 DPI),
  65. // you may also need to divide the values by 96 and multiply by the current DPI. (or already handled by ScreenToClient()?)
  66. //TODO try again getWindow() in windows -> no success in all thread (we probably need to do it in the awt-windows thread?)
  67. //TODO make singleton to avoid multiple instances
  68. /**
  69. * Instantiates a new win7 native touch source.
  70. *
  71. * @param mtApp the mt app
  72. */
  73. public Win7NativeTouchSource(MTApplication mtApp) {
  74. super(mtApp);
  75. this.app = mtApp;
  76. this.success = false;
  77. String platform = System.getProperty("os.name").toLowerCase();
  78. logger.debug("Platform: \"" + platform + "\"");
  79. // /*
  80. if (!platform.contains("windows 7")) {
  81. logger.error("Win7NativeTouchSource input source can only be used on platforms running windows 7!");
  82. return;
  83. }
  84. if (!loaded){
  85. loaded = true;
  86. String dllName = (MT4jSettings.getInstance().getArchitecture() == MT4jSettings.ARCHITECTURE_32_BIT)? dllName32 : dllName64;
  87. System.loadLibrary(dllName);
  88. // System.load(System.getProperty("user.dir") + File.separator + dllName + ".dll");
  89. }else{
  90. logger.error("Win7NativeTouchSource may only be instantiated once.");
  91. return;
  92. }
  93. boolean touchAvailable = this.getSystemMetrics();
  94. if (!touchAvailable){
  95. logger.error("Windows 7 Touch Input currently not available!");
  96. return;
  97. }else{
  98. logger.info("Windows 7 Touch Input available.");
  99. }
  100. // */
  101. wmTouchEvent = new Native_WM_TOUCH_Event();
  102. wmTouchEvent.id = -1;
  103. wmTouchEvent.type = -1;
  104. wmTouchEvent.x = -1;
  105. wmTouchEvent.y = -1;
  106. initialized = false;
  107. touchToCursorID = new HashMap<Integer, Long>();
  108. this.getNativeWindowHandles();
  109. success = true;
  110. Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
  111. public void run() {
  112. if (isSuccessfullySetup()){
  113. logger.debug("Cleaning up Win7 touch source..");
  114. quit();
  115. }
  116. }
  117. }));
  118. }
  119. // private boolean addedArtificalTouchDown = false; //FIXME REMOVE
  120. public boolean isSuccessfullySetup() {
  121. return success;
  122. }
  123. @Override
  124. public void pre(){ //we dont have to call registerPre() again (already in superclass and called there)
  125. if (initialized){ //Only poll events if native c++ core was initialized successfully
  126. while (pollEvent(wmTouchEvent)) {
  127. /*
  128. //FIXME TEST, make a artifical TOUCH_DOWN event REMOVE LATER!
  129. if (!addedArtificalTouchDown){
  130. addedArtificalTouchDown = true;
  131. wmTouchEvent.type = Native_WM_TOUCH_Event.TOUCH_DOWN;
  132. }
  133. */
  134. switch (wmTouchEvent.type) {
  135. case Native_WM_TOUCH_Event.TOUCH_DOWN:{
  136. // logger.debug("TOUCH_DOWN ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y);
  137. InputCursor c = new InputCursor();
  138. long cursorID = c.getId();
  139. MTWin7TouchInputEvt touchEvt = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_STARTED, c);
  140. int touchID = wmTouchEvent.id;
  141. ActiveCursorPool.getInstance().putActiveCursor(cursorID, c);
  142. touchToCursorID.put(touchID, cursorID);
  143. this.enqueueInputEvent(touchEvt);
  144. break;
  145. }case Native_WM_TOUCH_Event.TOUCH_MOVE:{
  146. // logger.debug("TOUCH_MOVE ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y);
  147. // System.out.println("Contact area X:" + wmTouchEvent.contactSizeX + " Y:" + wmTouchEvent.contactSizeY);
  148. Long cursorID = touchToCursorID.get(wmTouchEvent.id);
  149. if (cursorID != null){
  150. InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID);
  151. if (c != null){
  152. MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_UPDATED, c);
  153. this.enqueueInputEvent(te);
  154. }
  155. }
  156. break;
  157. }case Native_WM_TOUCH_Event.TOUCH_UP:{
  158. // logger.debug("TOUCH_UP ==> ID:" + wmTouchEvent.id + " x:" + wmTouchEvent.x + " y:" + wmTouchEvent.y);
  159. Long cursorID = touchToCursorID.get(wmTouchEvent.id);
  160. if (cursorID != null){
  161. InputCursor c = ActiveCursorPool.getInstance().getActiveCursorByID(cursorID);
  162. if (c != null){
  163. MTWin7TouchInputEvt te = new MTWin7TouchInputEvt(this, wmTouchEvent.x, wmTouchEvent.y, wmTouchEvent.contactSizeX, wmTouchEvent.contactSizeY, MTFingerInputEvt.INPUT_ENDED, c);
  164. this.enqueueInputEvent(te);
  165. }
  166. ActiveCursorPool.getInstance().removeCursor(cursorID);
  167. touchToCursorID.remove(wmTouchEvent.id);
  168. }
  169. break;
  170. }default:
  171. break;
  172. }
  173. }
  174. }
  175. super.pre();
  176. }
  177. private void getNativeWindowHandles(){
  178. if (app.frame == null){
  179. logger.error("applet.frame == null! -> cant set up windows 7 input!");
  180. return;
  181. }
  182. //TODO kind of hacky way of getting the HWND..but there seems to be no real alternative(?)
  183. final String oldTitle = app.frame.getTitle();
  184. final String tmpTitle = "Initializing Native Windows 7 Touch Input " + Math.random();
  185. app.frame.setTitle(tmpTitle);
  186. logger.debug("Temp title: " + tmpTitle);
  187. //FIXME TEST REMOVE
  188. //Window window = SwingUtilities.getWindowAncestor(app);
  189. // AWTUtilities.setWindowOpacity(window, 0.5f); //works!
  190. //Invokelater because of some crash issue
  191. //-> maybe we need to wait a frame until windows is informed of the window name change
  192. SwingUtilities.invokeLater(new Runnable() {
  193. public void run() {
  194. int awtCanvasHandle = 0;
  195. try {
  196. // //TODO also search for window class?
  197. awtCanvasHandle = (int)findWindow(tmpTitle, canvasClassName);
  198. setSunAwtCanvasHandle(awtCanvasHandle);
  199. } catch (Exception e) {
  200. System.err.println(e.getMessage());
  201. }
  202. app.frame.setTitle(oldTitle); //Reset title text
  203. }
  204. });
  205. }
  206. private void setTopWindowHandle(int HWND){
  207. if (HWND > 0){
  208. this.awtFrameHandle = HWND;
  209. logger.debug("-> Found AWT HWND: " + this.awtFrameHandle);
  210. }else{
  211. logger.error("-> Couldnt retrieve the top window handle!");
  212. }
  213. }
  214. private void setSunAwtCanvasHandle(int HWND){
  215. if (HWND > 0){
  216. this.sunAwtCanvasHandle = HWND;
  217. logger.debug("-> Found SunAwtCanvas HWND: " + this.sunAwtCanvasHandle);
  218. //Initialize c++ core (subclass etc)
  219. this.init(this.sunAwtCanvasHandle);
  220. this.initialized = true;
  221. }else{
  222. logger.error("-> Couldnt retrieve the SunAwtCanvas handle!");
  223. }
  224. }
  225. private class Native_WM_TOUCH_Event{
  226. //can be real enums in Java 5.0.
  227. /** The Constant TOUCH_DOWN. */
  228. public static final int TOUCH_DOWN = 0;
  229. /** The Constant TOUCH_MOVE. */
  230. public static final int TOUCH_MOVE = 1;
  231. /** The Constant TOUCH_UP. */
  232. public static final int TOUCH_UP = 2;
  233. /** The type. */
  234. public int type;
  235. /** The id. */
  236. public int id;
  237. /** The x value. */
  238. public int x;
  239. /** The y value. */
  240. public int y;
  241. /** The contact size area X dimension */
  242. public int contactSizeX;
  243. /** The contact size area Y dimension */
  244. public int contactSizeY;
  245. }
  246. }