/* 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;