/* Copyright 2010 Fictive (Fictive's public key's fingerprint is "44:1a:41:70:b1:22:d4:93:3a:bb:84:62:60:0b:e4:a3") This file is part of Sane Java Tablet. Sane Java Tablet 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. Sane Java Tablet 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 Sane Java Tablet. If not, see <http://www.gnu.org/licenses/>. */ package domain.libs.sjt.garbage; import java.util.ArrayList; import com.sun.jna.Memory; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.Kernel32Util; import domain.libs.sjt.impl.TabletImplementationException; import domain.libs.sjt.misc.ConsoleLogger; public class WindowsImplWinTab { private ConsoleLogger log = ConsoleLogger.newInstanceForCallingClass(); private final Memory sharedBuffer = new Memory(524288); // 512 KiB buffer private final Pointer sharedPointer = sharedBuffer.getPointer(0); // private ArrayList<Pointer> deviceCtxHandles; private Pointer deviceCtxHandle; // private final ArrayList<WinTabDevice> devices; // private WinTabPacketThread winTabPacketThread; // int queueSize; private final WindowsTabletImplementation impl; public WindowsImplWinTab(WindowsTabletImplementation impl) throws TabletImplementationException { this.impl = impl; // windowsWindow = new WindowsWindow(); // deviceCtxHandles = new ArrayList<Pointer>(); final WT_structLOGCONTEXTW wt_tagLogContextW = new WT_structLOGCONTEXTW(sharedBuffer); // ensure wintab implementation is present ensureWinTabIsPresent(); // device detection @SuppressWarnings("unused") int devCount = getDeviceCount(); // for (int devIdx=0; devIdx<devCount; devIdx++) { // int devIdx = 0; // { wt_tagLogContextW.clear(); wt_tagLogContextW.read(); /* * acquire a template of the device-specific * system context (moves system mouse cursor) */ if (WinTab.WTInfoW(WinTabDef.WTI_DEFSYSCTX.int_, 0, sharedBuffer) == 0) { throw new TabletImplementationException( "Unable to acquire device's system context."); } wt_tagLogContextW.read(); /* * fill in the context * Although the documentation says you can not include items in the packet that are not supported by the tablet, * it appears that this is incorrect. * Unsupported items may be included and will be set to zero. * - Courtesy of Wacom. Thank you. */ // personal note: api = stupid by design fillLogContext(0, wt_tagLogContextW); // store values to be requested // String requested = wt_tagLogContextW.toString(); // log.DEBUG("Printing request tagLOGCONTEXTW structure of device #%d:%n%s", devIdx, requested); // if (1==1)return; // // open context with filled in template deviceCtxHandle = WinTab.WTOpenW( impl.getWindowsWindow().getHWND(), wt_tagLogContextW, true); if (deviceCtxHandle == Pointer.NULL) { throw new TabletImplementationException( "Unable to open device #%d.", 0); } // set wintab packet queue size int queueSize; // TODO: queue size was 1000 for (queueSize=100; queueSize>=0; queueSize>>=1) { if (WinTab.WTQueueSizeSet(deviceCtxHandle, queueSize) == true) break; } if (queueSize == 0) { throw new TabletImplementationException( "Failed to set queue size."); } // } // /* // * prepare instance, so call to close() in case of failure doesn't throw // * NullPointerException // */ // winTabPacketThread = new WinTabPacketThread(this); // // int lastErrCode = Native.getLastError(); // if (wt_hCtx.getPointer() == Pointer.NULL) { // close(); // /* // * Kernel32.getLastError() might not be the best as an error code, // * but I can't find any error codes being returned from WinTab. // */ // throw new TabletImplementationException(classId, // "Failed to open wintab context. (" + // Kernel32Util.formatMessageFromLastErrorCode( // lastErrCode) + ")"); // } // // // // // // // start packet receiving thread // winTabPacketThread.start(); } private void ensureWinTabIsPresent() throws TabletImplementationException { try { if (WinTab.WTInfoW(0, 0, Pointer.NULL) == 0) { throw new TabletImplementationException( "Wintab implementation isn't present."); } } catch (UnsatisfiedLinkError e) { throw new TabletImplementationException( e, "Failed to load wintab dll."); } if (WinTab.WTInfoW(WinTabDef.WTI_INTERFACE.int_, WinTabDef.IFC_SPECVERSION.int_, sharedBuffer) == 0) { throw new TabletImplementationException( "Failed to acquire WinTab version."); } byte[] version = sharedBuffer.getByteArray(0, 2); log.info("Found WinTab version %d.%d", version[1], version[0]); if ((version[1] < 1) && (version[0] < 1)) { throw new TabletImplementationException( "Minimum supported WinTab version is 1.1"); } } // method private int getDeviceCount() throws TabletImplementationException { if (WinTab.WTInfoW(WinTabDef.WTI_INTERFACE.int_, WinTabDef.IFC_NDEVICES.int_, sharedBuffer) == 0) { throw new TabletImplementationException( "Unable to acquire device count."); } int devCount = sharedBuffer.getInt(0); if (devCount == 0) { throw new TabletImplementationException( "No devices detected."); } for (int i=0; i<devCount; i++) { if (WinTab.WTInfoW(WinTabDef.WTI_DEVICES.int_, WinTabDef.DVC_NAME.int_, sharedBuffer) == 0) { throw new TabletImplementationException( "Unable to acquire device #%d name.", i+1); } log.info("Device %d of %d detected (%s).", i+1, devCount, sharedBuffer.getString(0, true)); } return devCount; } private void fillLogContext(int devIdx, WT_structLOGCONTEXTW wt_tagLogContextW) throws TabletImplementationException { String contextName = String.format( "STT.%X", Thread.currentThread().getId(), 0,0 ); // turncate contextName if it's longer than LCNAMELEN minus null string terminator int len; if (contextName.length() >= (WinTabDef.LCNAMELEN.int_)) { len = WinTabDef.LCNAMELEN.int_ - 1; } else { len = contextName.length(); } contextName.getChars(0, len, wt_tagLogContextW.lcName, 0); wt_tagLogContextW.lcOptions = WinTabDef.CXO_MESSAGES.int_;// | WinTabDef.CXO_CSRMESSAGES.int_; // handle context overlap according using WTOverlap() // wt_tagLogContextW.lcStatus |= WinTabDef.CXS_ONTOP.int_; wt_tagLogContextW.lcDevice = devIdx; wt_tagLogContextW.lcPktRate = getMaxSupportedPacketRate(devIdx); wt_tagLogContextW.lcPktData = WinTabDef.PK_X.int_ | WinTabDef.PK_Y.int_; // wt_tagLogContextW.lcPktData = // WinTabDef.PK_CONTEXT.int_ | // WinTabDef.PK_STATUS.int_ | // WinTabDef.PK_TIME.int_ | // WinTabDef.PK_CHANGED.int_ | // WinTabDef.PK_SERIAL_NUMBER.int_ | // WinTabDef.PK_CURSOR.int_ | // WinTabDef.PK_BUTTONS.int_ | // WinTabDef.PK_X.int_ | // WinTabDef.PK_Y.int_ | // WinTabDef.PK_Z.int_ | // WinTabDef.PK_NORMAL_PRESSURE.int_ | // WinTabDef.PK_TANGENT_PRESSURE.int_ | // WinTabDef.PK_ORIENTATION.int_ | // WinTabDef.PK_ROTATION.int_; wt_tagLogContextW.lcPktMode = 0; // absolute mode for all wt_tagLogContextW.lcMoveMask = (int) Long.MAX_VALUE; wt_tagLogContextW.lcBtnDnMask = (int) Long.MAX_VALUE; wt_tagLogContextW.lcBtnUpMask = (int) Long.MAX_VALUE; // WT_structAXIS wt_tagAXISx = getAXISx(devIdx); // WT_structAXIS wt_tagAXISy = getAXISy(devIdx); // possible error with memory of axis struct and assigning NativeLong below // wt_tagLogContextW.lcOutOrgX = wt_tagAXISx.axMin; // wt_tagLogContextW.lcOutOrgY = wt_tagAXISy.axMin; // wt_tagLogContextW.lcOutExtX = new NativeLong(wt_tagAXISx.axMax.longValue() - wt_tagAXISx.axMin.longValue()); // wt_tagLogContextW.lcOutExtY = new NativeLong(wt_tagAXISy.axMax.longValue() - wt_tagAXISy.axMin.longValue()); wt_tagLogContextW.write(); } private int getMaxSupportedPacketRate(int devIdx) throws TabletImplementationException { /* * The lcPktRate field is not functional in the non-Wacom Wintab which shipped * with older Wacom tablets. The Wacom Wintab currently doesn't support * lcPktRate either. In both Wintabs, lcPktRate is reported as 100, but the * tablet will actually be sending data at the highest rate possible * for whatever mode in which the tablet is set. * - courtesy of wacom */ Pointer localBuffer = sharedBuffer.share(1000); if (WinTab.WTInfoW(WinTabDef.WTI_DEVICES.int_ + devIdx, WinTabDef.DVC_PKTRATE.int_, localBuffer) == 0) { throw new TabletImplementationException( "Unable to acquire maximum packet rate for device #%d.", devIdx+1); } return localBuffer.getInt(0); } // private WT_structAXIS getAXISx(int devIdx) throws TabletImplementationException { // return getAXIS(devIdx, WinTabDef.DVC_X.int_, 1000); // } // // // private WT_structAXIS getAXISy(int devIdx) throws TabletImplementationException { // return getAXIS(devIdx, WinTabDef.DVC_X.int_, 2000); // } // // // private WT_structAXIS getAXIS(int devIdx, int winTabAxisTag, int bufOffset) throws TabletImplementationException { // /* // * warning! the returned Structure shares memory with sharedBuffer! // */ // WT_structAXIS axisStruct = new WT_structAXIS(sharedBuffer.share(bufOffset)); // if (WinTab.WTInfoW(WinTabDef.WTI_DEVICES.int_ + devIdx, winTabAxisTag, axisStruct.getPointer()) == 0) { // throw new TabletImplementationException( // "Unable to acquire axis struct for device #%d.", // devIdx+1); // } // axisStruct.read(); // return axisStruct; // } public void closeAndCleanup() { log.debug("Close and cleanup."); /* * Kernel32.getLastError() might not be the best as an error code, * but I can't find any error codes being returned from WinTab. */ // for (int i=0; i<deviceCtxHandles.size(); i++) { if (WinTab.WTClose(deviceCtxHandle) != true) { log.error("Failed to close device context handle for device %d (of %d). GetLastError() returned: %s", 1, 1, Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError())); } // } // for } public void enableTablet() { // for (Pointer hCtx : deviceCtxHandles) { // WinTab.WTOverlap(hCtx, true); // } } public void disableTablet() { // for (Pointer hCtx : deviceCtxHandles) { // WinTab.WTOverlap(hCtx, false); // } } public void handleWindowMessage(Pointer hWnd, int wtPacket, Pointer wParam, Pointer lParam) { // WinTab.WTPacketsGet(lParam, 999, sharedBuffer); WinTab.WTPacket(lParam, (int) Pointer.nativeValue(wParam), sharedPointer); } } //new Memory() //int i = CWinTab.WTInfoW(CWinTab.WTI_INTERFACE, CWinTab.IFC_NDEVICES, m); //System.out.println(m.getInt(0)); //final String lastErrorMsg = Kernel32Util.formatMessageFromLastErrorCode( //Kernel32.INSTANCE.GetLastError()); //final String exceptionMessage = String.format( //"Windows tablet implementation failed to load. Kernel32.GetLastError() returned \"%s\"", //lastErrorMsg); //throw new TabletImplementationException(exceptionMessage); //CWinTab.WTClose(hCtx) == 0 //CWinTab.WTInfoW(CWinTab.WTI_DEFCONTEXT, CWinTab.CTX_SYSEXTX, CWinTab.buffer); //System.out.println("'" + CWinTab.buffer.getInt(0) + "'"); //Memory m = new Memory(64); //int i = CWinTab.WTInfoW(CWinTab.WTI_INTERFACE, CWinTab.IFC_WINTABID, m); //System.out.println(m.getString(0, true));// Structure.setAutoSynch(boolean) //User32.INSTANCE.setw //Native.getComponentPointer(c) //WindowsTabletImplementation wti = WindowsTabletImplementation.getInstance(); //wti.numTabletsAvailable(); //IntByReference r;