/* 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.winimpl;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import domain.libs.sjt.misc.ConsoleLogger;
import domain.libs.sjt.winimpl.wintab.defs.AXIS;
import domain.libs.sjt.winimpl.wintab.defs.LOGCONTEXTW;
import domain.libs.sjt.winimpl.wintab.defs.PACKET;
import domain.libs.sjt.winimpl.wintab.defs.Wintab32;
import domain.libs.sjt.winimpl.wintab.defs.Wintab32Direct;
public class TabletWinImplWintabFuncs {
private final ConsoleLogger log = ConsoleLogger.newInstanceForCallingClass();
private final TabletWinImpl impl;
WintabDevice wtDevices[];
Memory pktBuffer;
PACKET pkts[];
int maxPktsInBuf;
public TabletWinImplWintabFuncs(TabletWinImpl tabletWinImpl) {
this.impl = tabletWinImpl;
}
void ensureWintabIsPresent() throws TabletWinImplException {
try {
if (Wintab32.INSTANCE.WTInfoW(0, 0, Pointer.NULL) == 0) {
throw new TabletWinImplException(
"Wintab implementation isn't present.");
}
}
catch (UnsatisfiedLinkError e) {
throw new TabletWinImplException(e,
"Failed to load Wintab dll.");
}
short version[] = new short[1];
if (Wintab32.INSTANCE.WTInfoW(
(int) Wintab32.WTI_INTERFACE,
(int) Wintab32.IFC_SPECVERSION,
version) == 0
) {
throw new TabletWinImplException(
"Failed to acquire Wintab version.");
}
int version1 = (version[0] & 0xFF00) >> 8;
int version2 = version[0] & 0xFF;
log.info("Found Wintab version %d.%d",
version1,
version2);
if ((version1 < 1) && (version2 < 1)) {
throw new TabletWinImplException(
"Minimum supported Wintab version is 1.1");
}
} // method
void initTablets() throws TabletWinImplException {
final int bufSize = 1024*20; // 100 KiB
final int pktSize = new PACKET().size();
maxPktsInBuf = bufSize / pktSize;
pktBuffer = new Memory(bufSize);
pkts = new PACKET[maxPktsInBuf];
for (int i=0; i<maxPktsInBuf; i++) {
pkts[i] = new PACKET(pktBuffer.share(pktSize*i));
}
int devCount = getDeviceCount();
wtDevices = new WintabDevice[devCount];
for (int devIdx=0; devIdx<devCount; devIdx++) {
wtDevices[devIdx] = new WintabDevice();
initTabletDevice(devIdx);
}
}
int getDeviceCount() throws TabletWinImplException {
int devCount[] = new int[1];
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_INTERFACE, (int) Wintab32.IFC_NDEVICES, devCount) == 0) {
throw new TabletWinImplException(
"Failed to acquire device count.");
}
if (devCount[0] == 0) {
throw new TabletWinImplException(
"No devices detected.");
}
Memory devName = new Memory(Native.WCHAR_SIZE*40);
for (int devIdx=0; devIdx<devCount[0]; devIdx++) {
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_DEVICES + devIdx, (int) Wintab32.DVC_NAME, devName) == 0) {
throw new TabletWinImplException(
"Failed to acquire device #%d name.",
devIdx+1);
}
log.info("Device %d of %d detected (%s).",
devIdx+1,
devCount[0],
devName.getString(0, true));
}
return devCount[0];
}
void initTabletDevice(int devIdx) throws TabletWinImplException {
LOGCONTEXTW lc = new LOGCONTEXTW();
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_DDCTXS + devIdx, 0, lc) == 0) {
throw new TabletWinImplException(
"Failed to acquire device's digitizing context.");
}
String wt_name = String.format(
"WT%d%X",
devIdx,
Pointer.nativeValue(impl.winFuncs.hInstance));
wt_name.getChars(
0,
(int) (wt_name.length() > Wintab32.LCNAMELEN ? Wintab32.LCNAMELEN : wt_name.length()),
lc.lcName,
0);
lc.lcOptions |= Wintab32.CXO_MESSAGES | Wintab32.CXO_CSRMESSAGES;
lc.lcStatus |= Wintab32.CXS_ONTOP;
lc.lcLocks |= Wintab32.CXL_INSIZE | Wintab32.CXL_INASPECT;
lc.lcDevice = devIdx;
lc.lcPktRate = getMaxSupportedPacketRate(0);
lc.lcPktData = (int) (
Wintab32.PK_CONTEXT |
Wintab32.PK_STATUS |
Wintab32.PK_TIME |
Wintab32.PK_CHANGED |
Wintab32.PK_SERIAL_NUMBER |
Wintab32.PK_CURSOR |
Wintab32.PK_BUTTONS |
Wintab32.PK_X |
Wintab32.PK_Y |
Wintab32.PK_Z |
Wintab32.PK_NORMAL_PRESSURE |
Wintab32.PK_TANGENT_PRESSURE |
Wintab32.PK_ORIENTATION |
Wintab32.PK_ROTATION
);
lc.lcPktMode = 0; // return all values as absolute (or set tablet to absolute mode?)
lc.lcMoveMask = (int) Long.MAX_VALUE;
lc.lcBtnUpMask = (int) Long.MAX_VALUE;
lc.lcBtnDnMask = (int) Long.MAX_VALUE;
AXIS xAxis = new AXIS();
AXIS yAxis = new AXIS();
AXIS zAxis = new AXIS();
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_DEVICES + devIdx, (int) Wintab32.DVC_X, xAxis) == 0) {
throw new TabletWinImplException("WTInfoW() for DVC_X failed.");
}
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_DEVICES + devIdx, (int) Wintab32.DVC_Y, yAxis) == 0) {
throw new TabletWinImplException("WTInfoW() for DVC_Y failed.");
}
if (Wintab32.INSTANCE.WTInfoW((int) Wintab32.WTI_DEVICES + devIdx, (int) Wintab32.DVC_Z, zAxis) == 0) {
throw new TabletWinImplException("WTInfoW() for DVC_Z failed.");
}
// x
lc.lcInOrgX = lc.lcOutOrgX = 0;
lc.lcInExtX = lc.lcOutExtX = xAxis.axMax;
// y
lc.lcInOrgY = lc.lcOutOrgY = 0;
lc.lcInExtY = lc.lcOutExtY = yAxis.axMax;
// z
lc.lcInOrgZ = lc.lcOutOrgZ = 0;
lc.lcInExtZ = lc.lcOutExtZ = zAxis.axMax;
wtDevices[devIdx].xAxis = xAxis;
wtDevices[devIdx].yAxis = yAxis;
wtDevices[devIdx].zAxis = zAxis;
log.debug(
"Device %d) Xmin:%d, Xmax:%d, Xres:%d, Xunits:%d",
devIdx+1,
xAxis.axMin,
xAxis.axMax,
xAxis.axResolution,
xAxis.axUnits
);
log.debug(
"Device %d) Ymin:%d, Ymax:%d, Yres:%d, Yunits:%d",
devIdx+1,
yAxis.axMin,
yAxis.axMax,
yAxis.axResolution,
yAxis.axUnits
);
log.debug(
"Device %d) Zmin:%d, Zmax:%d, Zres:%d, Zunits:%d",
devIdx+1,
zAxis.axMin,
zAxis.axMax,
zAxis.axResolution,
zAxis.axUnits
);
Pointer hCtx = Wintab32.INSTANCE.WTOpenW(impl.winFuncs.hWnd, lc, 0);
if (hCtx == Pointer.NULL) {
throw new TabletWinImplException(
"Failed to open tablet device %d.",
devIdx);
} // if
// set wintab packet queue size
int queueSize = 0;
for (queueSize=500; queueSize>=0; queueSize>>=1) {
if (Wintab32.INSTANCE.WTQueueSizeSet(hCtx, queueSize) != 0) break;
}
if (queueSize == 0) {
throw new TabletWinImplException(
"Failed to set queue size on tablet #%d.",
devIdx);
}
wtDevices[devIdx].hCtx = hCtx;
}
int getMaxSupportedPacketRate(int devIdx) throws TabletWinImplException {
/*
* 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.
* - from of wacom dev site
*/
int pktRate[] = new int[1];
if (Wintab32.INSTANCE.WTInfoW(
(int) (Wintab32.WTI_DEVICES + devIdx),
(int) Wintab32.DVC_PKTRATE,
pktRate) == 0
) {
throw new TabletWinImplException(
"Failed to acquire maximum packet rate for device #%d.",
devIdx+1);
}
return pktRate[0];
}
void enableTablet(int devIdx) {
if (Wintab32.INSTANCE.WTEnable(wtDevices[devIdx].hCtx, 1) == 0) {
new TabletWinImplException(
"Failed to enable tablet #%d.",
devIdx+1
).printStackTrace();
impl.winFuncs.sendWMDESTROY();
return;
}
if (Wintab32.INSTANCE.WTOverlap(wtDevices[devIdx].hCtx, 1) == 0) {
new TabletWinImplException(
"Failed to enable tablet #%d (bring context to top).",
devIdx+1
).printStackTrace();
impl.winFuncs.sendWMDESTROY();
return;
}
}
void disableTablet(int devIdx) {
if (Wintab32.INSTANCE.WTOverlap(wtDevices[devIdx].hCtx, 0) == 0) {
new TabletWinImplException(
"Failed to disable tablet #%d (push context to bottom).",
devIdx+1
).printStackTrace();
impl.winFuncs.sendWMDESTROY();
return;
}
if (Wintab32.INSTANCE.WTEnable(wtDevices[devIdx].hCtx, 0) == 0) {
new TabletWinImplException(
"Failed to disable tablet #%d.",
devIdx+1
).printStackTrace();
impl.winFuncs.sendWMDESTROY();
return;
}
}
void tabletCleanup() {
log.debug("tabletCleanup()");
for (int i=0; i<wtDevices.length; i++) wtCloseTablet(i);
}
void wtCloseTablet(int devIdx) {
if (Wintab32.INSTANCE.WTClose(wtDevices[devIdx].hCtx) == 0) {
new TabletWinImplException(
"Failed cleanup (close tablet #%d).",
devIdx+1
).printStackTrace();
} // if
}
int pktNum, wtDevIdx; boolean wtDevIdxFound;
void handleWTPACKET(Pointer hCtx, long pktSerial) {
final int pktsReceived = Wintab32Direct.WTPacketsGet(hCtx, maxPktsInBuf, pktBuffer);
if (pktsReceived == 0) return;
wtDevIdxFound = false;
for (wtDevIdx=0; wtDevIdx<wtDevices.length; wtDevIdx++) {
if (Pointer.nativeValue(hCtx) == Pointer.nativeValue(wtDevices[wtDevIdx].hCtx)) {
wtDevIdxFound = true;
break;
}
} // for
if (!wtDevIdxFound) {
log.error("WT_PACKET received but no matching initialize device found. Packet's hCtx is %d", Pointer.nativeValue(hCtx));
}
for (pktNum=0; pktNum<pktsReceived; pktNum++) {
pkts[pktNum].read();
impl.handlePacket(wtDevIdx, pkts[pktNum]);
} // for
} // method
}