Add yaw-vowel

This commit is contained in:
yaw-man 2022-08-19 16:22:04 -03:00
parent bdaa77dcf0
commit 48eb843cfa
9 changed files with 851 additions and 0 deletions

View File

@ -0,0 +1,17 @@
option(USE_WINTAB true)
dpf_add_plugin(yaw-vowel
TARGETS vst2
FILES_DSP
dsp.cpp
FILES_UI
wtutil.cpp
wintab.cpp
ui.cpp)
target_include_directories(yaw-vowel PUBLIC
"."
"../../lib/wintab"
)
target_compile_definitions(yaw-vowel PUBLIC YAW_USE_WINTAB)

View File

@ -0,0 +1,25 @@
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_BRAND "yaw-audio"
#define DISTRHO_PLUGIN_NAME "yaw-vowel"
#define DISTRHO_PLUGIN_URI "https://yaw.man/plugins/yaw-vowel"
#define DISTRHO_PLUGIN_HAS_UI 1
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_NUM_INPUTS 2
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2
#define DISTRHO_UI_USE_NANOVG 1
enum Parameters {
ktpax = 0,
ktpay,
ktpaz,
ktpap,
kParameterButtonA,
kParameterButtonB,
kParameterTime,
kParameterCount
};
#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED

135
src/yaw-vowel/dsp.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "DistrhoPlugin.hpp"
START_NAMESPACE_DISTRHO
class TabPlugin : public Plugin
{
public:
TabPlugin()
: Plugin(kParameterCount, 0, 0),
sampleRate(getSampleRate())
{
// clear all parameters
std::memset(fParameters, 0, sizeof(float) * kParameterCount);
}
protected:
const char* getLabel() const override { return "yaw-tab"; }
const char* getDescription() const override { return "Drawing tablet synth UI"; }
const char* getMaker() const override { return "yaw-audio"; }
const char* getHomePage() const override { return "https://yaw.man/plugins/yaw-tab"; }
const char* getLicense() const override { return "ISC"; }
uint32_t getVersion() const override { return d_version(1, 0, 0); }
int64_t getUniqueId() const override { return d_cconst('y', 'w', 't', 'b'); }
void initParameter(uint32_t index, Parameter& parameter) override
{
parameter.hints = kParameterIsAutomable;
parameter.ranges.def = 0.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
switch (index)
{
case ktpax:
parameter.name = "x";
parameter.symbol = "x";
break;
case ktpay:
parameter.name = "y";
parameter.symbol = "y";
break;
case ktpaz:
parameter.name = "z";
parameter.symbol = "z";
break;
case ktpap:
parameter.name = "p";
parameter.symbol = "p";
break;
case kParameterButtonA:
parameter.name = "Button A";
parameter.symbol = "A";
parameter.hints |= kParameterIsBoolean;
break;
case kParameterButtonB:
parameter.name = "Button B";
parameter.symbol = "B";
parameter.hints |= kParameterIsBoolean;
break;
case kParameterTime:
parameter.name = "t";
parameter.symbol = "t";
parameter.hints = kParameterIsOutput;
break;
}
}
void sampleRateChanged(double newRate) override
{
sampleRate = newRate;
}
float getParameterValue(uint32_t index) const override
{
return fParameters[index];
}
void setParameterValue(uint32_t idx, float val) override
{
fParameters[idx] = val;
switch (idx) {
case ktpax:
period = 0.02f * val * static_cast<float>(sampleRate);
break;
case ktpay:
break;
case ktpaz:
break;
case ktpap:
volume = val;
break;
case kParameterButtonA:
break;
case kParameterButtonB:
break;
}
}
void run(const float** inputs, float** outputs, uint32_t frames) override
{
for (uint32_t i = 0; i < frames; ++i) {
counter++;
if (counter > period) {
outputs[0][i] = outputs[1][i] = volume;
counter = 0;
}
else {
outputs[0][i] = outputs[1][i] = 0.0f;
}
}
parity = !parity;
fParameters[kParameterTime] += parity ? 1 : -1;
}
private:
float fParameters[kParameterCount];
float period = 0.f;
float counter = 0.f;
float volume = 0.f;
double sampleRate;
bool parity = false;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TabPlugin)
};
Plugin* createPlugin()
{
return new TabPlugin();
}
END_NAMESPACE_DISTRHO

44
src/yaw-vowel/tablet.h Normal file
View File

@ -0,0 +1,44 @@
//Interface to supported tablet APIs.
#include <array>
#include <string>
#ifdef YAW_USE_WINTAB
#include <Windows.h>
#include "MSGPACK.H"
#include "wintab.h"
typedef unsigned long ButtonMask;
#endif
struct Packet {
float x;
float y;
float z;
float p;
ButtonMask buttons;
bool operator==(const Packet& p)
{ return
this->x == p.x &&
this->y == p.y &&
this->z == p.z &&
this->p == p.p &&
this->buttons == p.buttons;
};
bool operator!=(const Packet& p) { return !(*this == p); }
};
class Tablet {
public:
Tablet(uintptr_t handle);
~Tablet();
bool GetPacket( Packet &pkt );
bool initialized;
std::string errormsg = "";
Packet ext = { 0 };
private:
#ifdef YAW_USE_WINTAB
void NewContext(HWND hwnd);
HCTX hctx = NULL;
#endif
};

246
src/yaw-vowel/ui.cpp Normal file
View File

@ -0,0 +1,246 @@
#include "ui.h"
#ifdef DEBUG
#include <format>
#include <optional>
#endif
START_NAMESPACE_DISTRHO
ButtonMappingWidget::ButtonMappingWidget(
Widget *parent,
float initialSize,
float initialX,
float initialY,
Parameters associatedParameter,
ButtonEventHandler::Callback *const callback) : x(initialX),
y(initialY),
size(initialSize),
param(associatedParameter),
isClicked(false),
isPenPressed(false),
mask(0),
NanoSubWidget(parent),
ButtonEventHandler(this)
{
setSize(Size<uint>(static_cast<uint>(size), static_cast<uint>(size)));
setAbsolutePos((int)x, (int)y);
ButtonEventHandler::setCallback(callback);
}
void ButtonMappingWidget::onNanoDisplay()
{
beginPath();
strokeColor(200, 200, 200);
fillColor(0.5f, 0.5f, 0.5f, 0.5f * (isClicked + isPenPressed));
roundedRect(0.f, 0.f, size, size, 0.25f * size);
stroke();
fill();
closePath();
}
bool ButtonMappingWidget::onMouse(const MouseEvent &ev)
{
isClicked = ev.press && contains(ev.pos);
return ButtonEventHandler::mouseEvent(ev);
}
static constexpr uint kInitialWidth = 800;
static constexpr uint kInitialHeight = 600;
TabUI::TabUI()
: UI(kInitialWidth, kInitialHeight),
tab(getWindow().getNativeWindowHandle()),
AButtonWidget(this, 75.f, kInitialWidth - 100, kInitialHeight - 100, kParameterButtonA, this),
BButtonWidget(this, 75.f, kInitialWidth - 100, kInitialHeight - 200, kParameterButtonB, this)
{
#ifdef DGL_NO_SHARED_RESOURCES
createFontFromFile("sans", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf");
#else
loadSharedResources();
#endif
if (!tab.initialized)
return;
float tabletAspectRatio = tab.ext.x ? tab.ext.y / tab.ext.x : 1.f;
setGeometryConstraints(400, static_cast<uint>(300 * tabletAspectRatio), true, false);
}
void TabUI::getTabletData()
{
if (!tab.initialized || !tab.GetPacket(pkt))
return;
if (pkt == lastPkt)
return;
if (pkt.x != lastPkt.x)
setParameterValue(ktpax, pkt.x);
if (pkt.y != lastPkt.y)
setParameterValue(ktpay, pkt.y);
if (pkt.z != lastPkt.z)
setParameterValue(ktpaz, pkt.z);
if (pkt.p != lastPkt.p)
setParameterValue(ktpap, pkt.p);
if (pkt.buttons != lastPkt.buttons)
setButtonsValue(pkt.buttons);
lastPkt = pkt;
}
void TabUI::setButtonsValue(unsigned long buttonMask)
{
if (AButtonWidget.matchesMask(buttonMask))
setParameterValue(kParameterButtonA, 1.f);
else
setParameterValue(kParameterButtonA, 0.f);
if (BButtonWidget.matchesMask(buttonMask))
setParameterValue(kParameterButtonB, 1.f);
else
setParameterValue(kParameterButtonB, 0.f);
}
void TabUI::buttonClicked(SubWidget *const widget, int)
{
if (widget == &AButtonWidget)
AButtonWidget.setMask(pkt.buttons);
if (widget == &BButtonWidget)
BButtonWidget.setMask(pkt.buttons);
}
void TabUI::parameterChanged(uint32_t index, float value)
{
if (index != kParameterTime && index < kParameterCount)
{
switch (index)
{
case (ktpax):
pkt.x = value;
case (ktpay):
pkt.y = value;
case (ktpaz):
pkt.z = value;
case (ktpap):
pkt.p = value;
}
return;
}
getTabletData();
repaint();
}
void TabUI::uiIdle()
{
getTabletData();
repaint();
}
bool TabUI::onMouse(const MouseEvent &ev)
{
getTabletData();
repaint();
return false; // Allow event to propagate.
}
void TabUI::onResize(const ResizeEvent &ev)
{
int x = static_cast<int>(ev.size.getWidth());
int y = static_cast<int>(ev.size.getHeight());
AButtonWidget.setAbsolutePos(x - 100, y - 100);
BButtonWidget.setAbsolutePos(x - 100, y - 200);
return UI::onResize(ev);
}
bool TabUI::onScroll(const ScrollEvent &ev)
{
double add;
const uint x = getWidth();
const uint y = getHeight();
add = (ev.delta.getY() > 0) ? 20 : -20;
float tabletAspectRatio;
if (tab.initialized)
tabletAspectRatio = tab.ext.y / tab.ext.x;
else
tabletAspectRatio = 1.f;
setSize(static_cast<uint>(x + add + 0.5), static_cast<uint>(tabletAspectRatio * (x + add + 0.5)));
return true;
}
void TabUI::onNanoDisplay()
{
fontSize(15.0f);
textLineHeight(1.f);
// Report tablet errors.
#ifdef DEBUG
if (!tab.initialized)
{
const std::string err = std::vformat("Tablet not supported:\n{}",
std::make_format_args(tab.errormsg));
beginPath();
fillColor(200, 200, 200);
textBox(0.f, 15.f, 250.f, err.c_str(), nullptr);
closePath();
return;
}
// Numerical feedback.
beginPath();
fillColor(200, 200, 200);
textBox(0.f, 15.f, 250.f,
std::format("x: {:.3f}\ny: {:.3f}\nz: {:.3f}\np: {:.3f}\nb: {}",
pkt.x, pkt.y, pkt.z, pkt.p, pkt.buttons)
.c_str(),
nullptr);
closePath();
#endif
// Pen position and pressure.
drawCircle(pkt.x, pkt.y, pkt.z, pkt.p);
}
void TabUI::drawCircle(float x, float y, float z, float p)
{
static constexpr float circleRadius = 25.f;
x *= getWidth();
y = (1.f - y) * getHeight();
z = 1.f - z;
beginPath();
strokeColor(1.f, 1.f, 1.f, 0.5f);
moveTo(x - z * circleRadius, y);
lineTo(x + z * circleRadius, y);
stroke();
closePath();
beginPath();
strokeColor(1.f, 1.f, 1.f, 0.5f);
moveTo(x, y - z * circleRadius);
lineTo(x, y + z * circleRadius);
stroke();
closePath();
beginPath();
fillColor(1.f, 1.f, 1.f, p);
strokeColor(255, 255, 255, 255);
circle(x, y, circleRadius);
fill();
stroke();
closePath();
beginPath();
strokeColor(1.f, 1.f, 1.f, z);
circle(x, y, z * circleRadius);
stroke();
closePath();
}
UI *createUI()
{
return new TabUI();
}
END_NAMESPACE_DISTRHO

69
src/yaw-vowel/ui.h Normal file
View File

@ -0,0 +1,69 @@
#include "DistrhoUI.hpp"
#include "tablet.h"
#ifdef DEBUG
#include <format>
#include <optional>
#endif
START_NAMESPACE_DISTRHO
class ButtonMappingWidget : public NanoSubWidget,
public ButtonEventHandler
{
public:
explicit ButtonMappingWidget(
Widget *parent,
float initialSize,
float initialX,
float initialY,
Parameters associatedParameter,
ButtonEventHandler::Callback *const callback);
void onNanoDisplay() override;
bool onMouse(const MouseEvent &ev) override;
void setMask(const ButtonMask m) { mask = m; };
bool matchesMask(const ButtonMask m) { return isPenPressed = (m && mask == m); };
bool isClicked;
bool isPenPressed;
private:
float x = 0;
float y = 0;
float size = 0;
const Parameters param;
ButtonMask mask;
};
class TabUI : public UI,
public ButtonEventHandler::Callback
{
public:
explicit TabUI();
void buttonClicked(SubWidget *const widget, int) override;
void parameterChanged(uint32_t index, float value) override;
void uiIdle() override;
bool onMouse(const MouseEvent &ev) override;
bool onScroll(const ScrollEvent &ev) override;
void onResize(const ResizeEvent &ev) override;
void onNanoDisplay() override;
private:
void getTabletData();
void setButtonsValue(unsigned long buttonMask);
void drawCircle(float x, float y, float z, float p);
// Tablet context handler
Tablet tab;
Packet pkt = {0};
Packet lastPkt = {0};
// Button mapping widgets
ButtonMappingWidget AButtonWidget;
ButtonMappingWidget BButtonWidget;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TabUI)
};
END_NAMESPACE_DISTRHO

81
src/yaw-vowel/wintab.cpp Normal file
View File

@ -0,0 +1,81 @@
#ifdef YAW_USE_WINTAB
#include "tablet.h"
#define PACKETDATA (PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_NORMAL_PRESSURE)
//#define PACKETMODE
#include "pktdef.h"
#include "wtutil.h"
Tablet::Tablet(uintptr_t handle)
{
HWND hwnd = reinterpret_cast<HWND>(handle);
if (!hwnd) { return; }
if (!LoadWintab() || !gpWTInfoA(0, 0, NULL)) {
errormsg = "Wintab not installed.";
return;
}
NewContext(hwnd);
}
Tablet::~Tablet() {
if (hctx) { gpWTClose(hctx); }
UnloadWintab();
}
bool Tablet::GetPacket( Packet& packet ) {
//Serial number of newest packet.
UINT oldest, newest;
//This function returns false when it fails
//which may happen with a full or empty queue.
if (!gpWTQueuePacketsEx(hctx, &oldest, &newest)) {
//Queue may be full, flush it all just in case.
gpWTPacketsGet(hctx, gpWTQueueSizeGet(hctx), nullptr);
return false;
}
//Store newest packet in pkt, flush older packets.
PACKET pkt;
bool newData = gpWTPacket(hctx, newest, &pkt);
if (!newData) return false;
packet.x = static_cast<float>(pkt.pkX) / ext.x;
packet.y = static_cast<float>(pkt.pkY) / ext.y;
packet.z = static_cast<float>(pkt.pkZ) / ext.z;
packet.p = static_cast<float>(pkt.pkNormalPressure) / ext.p;
packet.buttons = pkt.pkButtons;
return true;
}
void Tablet::NewContext(HWND hwnd) {
if (hctx) { gpWTClose(hctx); }
LOGCONTEXT ctx = {};
AXIS TabletX = { 0 };
AXIS TabletY = { 0 };
AXIS TabletZ = { 0 };
AXIS TabletPressure = { 0 };
gpWTInfoA(WTI_DEFCONTEXT, 0, &ctx);
ctx.lcOptions |= CXO_MESSAGES; //TODO: checker çela
ctx.lcPktData = PACKETDATA;
ctx.lcPktMode = 0;
//Tablet extents.
gpWTInfoA(WTI_DEVICES, DVC_X, &TabletX);
gpWTInfoA(WTI_DEVICES, DVC_Y, &TabletY);
gpWTInfoA(WTI_DEVICES, DVC_Z, &TabletZ);
gpWTInfoA(WTI_DEVICES, DVC_NPRESSURE, &TabletPressure);
ext.x = static_cast<float>(TabletX.axMax);
ext.y = static_cast<float>(TabletY.axMax);
ext.z = static_cast<float>(TabletZ.axMax);
ext.p = static_cast<float>(TabletPressure.axMax);
hctx = gpWTOpenA(hwnd, &ctx, TRUE);
if (!hctx)
{
errormsg = "Could not open Wintab context.";
return;
}
initialized = true;
};
#endif

143
src/yaw-vowel/wtutil.cpp Normal file
View File

@ -0,0 +1,143 @@
///////////////////////////////////////////////////////////////////////////////
//
// DESCRIPTION
// Some general-purpose functions for the WinTab demos.
//
// COPYRIGHT
// Copyright (c) 2014-2020 Wacom Co., Ltd.
// All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
#include <string>
#include "wtutil.h"
//////////////////////////////////////////////////////////////////////////////
HINSTANCE ghWintab = nullptr;
WTINFOA gpWTInfoA = nullptr;
WTOPENA gpWTOpenA = nullptr;
WTGETA gpWTGetA = nullptr;
WTSETA gpWTSetA = nullptr;
WTCLOSE gpWTClose = nullptr;
WTPACKET gpWTPacket = nullptr;
WTENABLE gpWTEnable = nullptr;
WTOVERLAP gpWTOverlap = nullptr;
WTSAVE gpWTSave = nullptr;
WTCONFIG gpWTConfig = nullptr;
WTRESTORE gpWTRestore = nullptr;
WTEXTSET gpWTExtSet = nullptr;
WTEXTGET gpWTExtGet = nullptr;
WTQUEUESIZESET gpWTQueueSizeSet = nullptr;
WTDATAPEEK gpWTDataPeek = nullptr;
WTPACKETSGET gpWTPacketsGet = nullptr;
WTMGROPEN gpWTMgrOpen = nullptr;
WTMGRCLOSE gpWTMgrClose = nullptr;
WTMGRDEFCONTEXT gpWTMgrDefContext = nullptr;
WTMGRDEFCONTEXTEX gpWTMgrDefContextEx = nullptr;
// TODO - add more wintab32 function pointers as needed
WTQPACKETSEX gpWTQueuePacketsEx = nullptr;
WTQSIZEGET gpWTQueueSizeGet = nullptr;
//////////////////////////////////////////////////////////////////////////////
// Purpose
// Find wintab32.dll and load it.
// Find the exported functions we need from it.
//
// Returns
// TRUE on success.
// FALSE on failure.
//
BOOL LoadWintab(void)
{
// load the wintab32 dll
ghWintab = LoadLibraryA("Wintab32.dll");
if (!ghWintab)
{
const DWORD err = GetLastError();
ShowError("Could not load Wintab32.dll: " + std::to_string(err));
return FALSE;
}
// Explicitly find the exported Wintab functions in which we are interested.
// We are using the ASCII, not unicode versions (where applicable).
gpWTOpenA = (WTOPENA)GetProcAddress(ghWintab, "WTOpenA");
gpWTInfoA = (WTINFOA)GetProcAddress(ghWintab, "WTInfoA");
gpWTGetA = (WTGETA)GetProcAddress(ghWintab, "WTGetA");
gpWTSetA = (WTSETA)GetProcAddress(ghWintab, "WTSetA");
gpWTPacket = (WTPACKET)GetProcAddress(ghWintab, "WTPacket");
gpWTClose = (WTCLOSE)GetProcAddress(ghWintab, "WTClose");
gpWTEnable = (WTENABLE)GetProcAddress(ghWintab, "WTEnable");
gpWTOverlap = (WTOVERLAP)GetProcAddress(ghWintab, "WTOverlap");
gpWTSave = (WTSAVE)GetProcAddress(ghWintab, "WTSave");
gpWTConfig = (WTCONFIG)GetProcAddress(ghWintab, "WTConfig");
gpWTRestore = (WTRESTORE)GetProcAddress(ghWintab, "WTRestore");
gpWTExtSet = (WTEXTSET)GetProcAddress(ghWintab, "WTExtSet");
gpWTExtGet = (WTEXTGET)GetProcAddress(ghWintab, "WTExtGet");
gpWTQueueSizeSet = (WTQUEUESIZESET)GetProcAddress(ghWintab, "WTQueueSizeSet");
gpWTDataPeek = (WTDATAPEEK)GetProcAddress(ghWintab, "WTDataPeek");
gpWTPacketsGet = (WTPACKETSGET)GetProcAddress(ghWintab, "WTPacketsGet");
gpWTMgrOpen = (WTMGROPEN)GetProcAddress(ghWintab, "WTMgrOpen");
gpWTMgrClose = (WTMGRCLOSE)GetProcAddress(ghWintab, "WTMgrClose");
gpWTMgrDefContext = (WTMGRDEFCONTEXT)GetProcAddress(ghWintab, "WTMgrDefContext");
gpWTMgrDefContextEx = (WTMGRDEFCONTEXTEX)GetProcAddress(ghWintab, "WTMgrDefContextEx");
gpWTQueuePacketsEx = (WTQPACKETSEX)GetProcAddress(ghWintab, "WTQueuePacketsEx");
gpWTQueueSizeGet = (WTQSIZEGET)GetProcAddress(ghWintab, "WTQueueSizeGet");
// TODO - don't forget to NULL out pointers in UnloadWintab().
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// Purpose
// Uninitializes use of wintab32.dll
//
// Returns
// Nothing.
//
void UnloadWintab(void)
{
if (ghWintab)
{
FreeLibrary(ghWintab);
ghWintab = nullptr;
}
gpWTOpenA = nullptr;
gpWTClose = nullptr;
gpWTInfoA = nullptr;
gpWTPacket = nullptr;
gpWTEnable = nullptr;
gpWTOverlap = nullptr;
gpWTSave = nullptr;
gpWTConfig = nullptr;
gpWTGetA = nullptr;
gpWTSetA = nullptr;
gpWTRestore = nullptr;
gpWTExtSet = nullptr;
gpWTExtGet = nullptr;
gpWTQueueSizeSet = nullptr;
gpWTDataPeek = nullptr;
gpWTPacketsGet = nullptr;
gpWTMgrOpen = nullptr;
gpWTMgrClose = nullptr;
gpWTMgrDefContext = nullptr;
gpWTMgrDefContextEx = nullptr;
gpWTQueuePacketsEx = nullptr;
gpWTQueueSizeGet = nullptr;
}
//////////////////////////////////////////////////////////////////////////////
// Purpose
// Display error to user.
//
void ShowError(const std::string &pszErrorMessage_I)
{
//TODO: post error message to buffer in Tablet class
//which in turn can post to TabUI
//MessageBoxA(NULL, pszErrorMessage_I.c_str(), "Error", MB_OK | MB_ICONERROR);
}
//////////////////////////////////////////////////////////////////////////////

91
src/yaw-vowel/wtutil.h Normal file
View File

@ -0,0 +1,91 @@
///////////////////////////////////////////////////////////////////////////////
//
// DESCRIPTION
// Defines for the general-purpose functions for the WinTab demos.
//
// COPYRIGHT
// Copyright (c) 2014-2020 Wacom Co., Ltd.
// All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <wintab.h> // NOTE: get from wactab header package
//////////////////////////////////////////////////////////////////////////////
// Ignore warnings about using unsafe string functions.
#pragma warning( disable : 4996 )
//////////////////////////////////////////////////////////////////////////////
// Function pointers to Wintab functions exported from wintab32.dll.
using WTINFOA = UINT (API*)(UINT, UINT, LPVOID);
using WTOPENA = HCTX (API*)(HWND, LPLOGCONTEXTA, BOOL);
using WTGETA = BOOL (API*)(HCTX, LPLOGCONTEXT);
using WTSETA = BOOL (API*)(HCTX, LPLOGCONTEXT);
using WTCLOSE = BOOL (API*)(HCTX);
using WTENABLE = BOOL (API*)(HCTX, BOOL);
using WTPACKET = BOOL (API*)(HCTX, UINT, LPVOID);
using WTOVERLAP = BOOL (API*)(HCTX, BOOL);
using WTSAVE = BOOL (API*)(HCTX, LPVOID);
using WTCONFIG = BOOL (API*)(HCTX, HWND);
using WTRESTORE = HCTX (API*)(HWND, LPVOID, BOOL);
using WTEXTSET = BOOL (API*)(HCTX, UINT, LPVOID);
using WTEXTGET = BOOL (API*)(HCTX, UINT, LPVOID);
using WTQUEUESIZESET = BOOL (API*)(HCTX, int);
using WTDATAPEEK = int (API*)(HCTX, UINT, UINT, int, LPVOID, LPINT);
using WTPACKETSGET = int (API*)(HCTX, int, LPVOID);
using WTMGROPEN = HMGR (API*)(HWND, UINT);
using WTMGRCLOSE = BOOL (API*)(HMGR);
using WTMGRDEFCONTEXT = HCTX (API*)(HMGR, BOOL);
using WTMGRDEFCONTEXTEX = HCTX (API*)(HMGR, UINT, BOOL);
// TODO - add more wintab32 function defs as needed
using WTQPACKETSEX = BOOL(API*)(HCTX, UINT FAR*, UINT FAR*);
using WTQSIZEGET = int (API*)(HCTX);
//////////////////////////////////////////////////////////////////////////////
// Loaded Wintab32 API functions.
extern HINSTANCE ghWintab;
extern WTINFOA gpWTInfoA;
extern WTOPENA gpWTOpenA;
extern WTGETA gpWTGetA;
extern WTSETA gpWTSetA;
extern WTCLOSE gpWTClose;
extern WTPACKET gpWTPacket;
extern WTENABLE gpWTEnable;
extern WTOVERLAP gpWTOverlap;
extern WTSAVE gpWTSave;
extern WTCONFIG gpWTConfig;
extern WTRESTORE gpWTRestore;
extern WTEXTSET gpWTExtSet;
extern WTEXTGET gpWTExtGet;
extern WTQUEUESIZESET gpWTQueueSizeSet;
extern WTDATAPEEK gpWTDataPeek;
extern WTPACKETSGET gpWTPacketsGet;
extern WTMGROPEN gpWTMgrOpen;
extern WTMGRCLOSE gpWTMgrClose;
extern WTMGRDEFCONTEXT gpWTMgrDefContext;
extern WTMGRDEFCONTEXTEX gpWTMgrDefContextEx;
// TODO - add more wintab32 function pointers as needed
extern WTQPACKETSEX gpWTQueuePacketsEx;
extern WTQSIZEGET gpWTQueueSizeGet;
//////////////////////////////////////////////////////////////////////////////
BOOL LoadWintab(void);
void UnloadWintab(void);
void ShowError(const std::string &pszErrorMessage_I);
//////////////////////////////////////////////////////////////////////////////