From 474dce4da92f1015d3d8ec5452d74b50442eb24d Mon Sep 17 00:00:00 2001 From: yaw-man Date: Thu, 18 Aug 2022 16:25:37 -0300 Subject: [PATCH] Copy interface for Shepard tone synth. --- src/CMakeLists.txt | 3 +- src/yaw-shepard/CMakeLists.txt | 22 +++ src/yaw-shepard/DistrhoPluginInfo.h | 32 ++++ src/yaw-shepard/dsp.cpp | 135 +++++++++++++++ src/yaw-shepard/rtstylus.cpp | 23 +++ src/yaw-shepard/tablet.h | 44 +++++ src/yaw-shepard/ui.cpp | 246 ++++++++++++++++++++++++++++ src/yaw-shepard/ui.h | 69 ++++++++ src/yaw-shepard/wintab.cpp | 81 +++++++++ src/yaw-shepard/wtutil.cpp | 143 ++++++++++++++++ src/yaw-shepard/wtutil.h | 91 ++++++++++ 11 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 src/yaw-shepard/CMakeLists.txt create mode 100644 src/yaw-shepard/DistrhoPluginInfo.h create mode 100644 src/yaw-shepard/dsp.cpp create mode 100644 src/yaw-shepard/rtstylus.cpp create mode 100644 src/yaw-shepard/tablet.h create mode 100644 src/yaw-shepard/ui.cpp create mode 100644 src/yaw-shepard/ui.h create mode 100644 src/yaw-shepard/wintab.cpp create mode 100644 src/yaw-shepard/wtutil.cpp create mode 100644 src/yaw-shepard/wtutil.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e192e7b..504e1df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,2 +1,3 @@ cmake_minimum_required(VERSION 3.10) -add_subdirectory(yaw-tab) \ No newline at end of file +add_subdirectory(yaw-tab) +add_subdirectory(yaw-shepard) \ No newline at end of file diff --git a/src/yaw-shepard/CMakeLists.txt b/src/yaw-shepard/CMakeLists.txt new file mode 100644 index 0000000..ae967e6 --- /dev/null +++ b/src/yaw-shepard/CMakeLists.txt @@ -0,0 +1,22 @@ +dpf_add_plugin(yaw-tab + TARGETS vst2 + FILES_DSP + dsp.cpp + FILES_UI + wtutil.cpp + wintab.cpp + rtstylus.cpp + ui.cpp) + +target_include_directories(yaw-tab PUBLIC + "." + "../../lib/wintab" +) +target_compile_definitions(yaw-tab PUBLIC YAW_USE_WINTAB) + +set_target_properties(yaw-tab PROPERTIES VS_DOTNET_REFERENCES "System") +set_target_properties(yaw-tab PROPERTIES COMPILE_FLAGS "/clr") +set_target_properties(yaw-tab PROPERTIES COMPILE_FLAGS "/Zc:twoPhase-") + +# default compiler flags from CMAKE conflict with managed code compliation, probably a better way of doing this. + diff --git a/src/yaw-shepard/DistrhoPluginInfo.h b/src/yaw-shepard/DistrhoPluginInfo.h new file mode 100644 index 0000000..ffdcd0e --- /dev/null +++ b/src/yaw-shepard/DistrhoPluginInfo.h @@ -0,0 +1,32 @@ +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_BRAND "yaw-audio" +#define DISTRHO_PLUGIN_NAME "yaw-tab" +#define DISTRHO_PLUGIN_URI "https://yaw.man/plugins/yaw-tab" + +#define DISTRHO_PLUGIN_HAS_UI 1 +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 +#define DISTRHO_PLUGIN_NUM_INPUTS 0 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 +#define DISTRHO_UI_USE_NANOVG 1 + +// only checking if supported, not actually used +#define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 1 + +#ifdef __MOD_DEVICES__ +#define DISTRHO_PLUGIN_USES_MODGUI 1 +#endif + +enum Parameters { + ktpax = 0, + ktpay, + ktpaz, + ktpap, + kParameterButtonA, + kParameterButtonB, + kParameterTime, + kParameterCount +}; + +#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/src/yaw-shepard/dsp.cpp b/src/yaw-shepard/dsp.cpp new file mode 100644 index 0000000..0091087 --- /dev/null +++ b/src/yaw-shepard/dsp.cpp @@ -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(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 diff --git a/src/yaw-shepard/rtstylus.cpp b/src/yaw-shepard/rtstylus.cpp new file mode 100644 index 0000000..b093fbd --- /dev/null +++ b/src/yaw-shepard/rtstylus.cpp @@ -0,0 +1,23 @@ +#ifdef YAW_USE_OTD +// text_write.cpp +// compile with: /clr +using namespace System; +using namespace System::IO; + +int func() +{ + String^ fileName = "textfile.txt"; + + StreamWriter^ sw = gcnew StreamWriter(fileName); + sw->WriteLine("A text file is born!"); + sw->Write("You can use WriteLine"); + sw->WriteLine("...or just Write"); + sw->WriteLine("and do {0} output too.", "formatted"); + sw->WriteLine("You can also send non-text objects:"); + sw->WriteLine(DateTime::Now); + sw->Close(); + Console::WriteLine("a new file ('{0}') has been written", fileName); + + return 0; +} +#endif \ No newline at end of file diff --git a/src/yaw-shepard/tablet.h b/src/yaw-shepard/tablet.h new file mode 100644 index 0000000..3258e1e --- /dev/null +++ b/src/yaw-shepard/tablet.h @@ -0,0 +1,44 @@ +//Interface to supported tablet APIs. +#include +#include + +#ifdef YAW_USE_WINTAB +#include +#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 +}; \ No newline at end of file diff --git a/src/yaw-shepard/ui.cpp b/src/yaw-shepard/ui.cpp new file mode 100644 index 0000000..ee84d3d --- /dev/null +++ b/src/yaw-shepard/ui.cpp @@ -0,0 +1,246 @@ +#include "ui.h" +#ifdef DEBUG +#include +#include +#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(static_cast(size), static_cast(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(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(ev.size.getWidth()); + int y = static_cast(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(x + add + 0.5), static_cast(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 diff --git a/src/yaw-shepard/ui.h b/src/yaw-shepard/ui.h new file mode 100644 index 0000000..f532fb1 --- /dev/null +++ b/src/yaw-shepard/ui.h @@ -0,0 +1,69 @@ +#include "DistrhoUI.hpp" +#include "tablet.h" +#ifdef DEBUG +#include +#include +#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 \ No newline at end of file diff --git a/src/yaw-shepard/wintab.cpp b/src/yaw-shepard/wintab.cpp new file mode 100644 index 0000000..d1ff0b2 --- /dev/null +++ b/src/yaw-shepard/wintab.cpp @@ -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(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(pkt.pkX) / ext.x; + packet.y = static_cast(pkt.pkY) / ext.y; + packet.z = static_cast(pkt.pkZ) / ext.z; + packet.p = static_cast(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(TabletX.axMax); + ext.y = static_cast(TabletY.axMax); + ext.z = static_cast(TabletZ.axMax); + ext.p = static_cast(TabletPressure.axMax); + + hctx = gpWTOpenA(hwnd, &ctx, TRUE); + + if (!hctx) + { + errormsg = "Could not open Wintab context."; + return; + } + initialized = true; +}; +#endif \ No newline at end of file diff --git a/src/yaw-shepard/wtutil.cpp b/src/yaw-shepard/wtutil.cpp new file mode 100644 index 0000000..ac921f1 --- /dev/null +++ b/src/yaw-shepard/wtutil.cpp @@ -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 +#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); +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/src/yaw-shepard/wtutil.h b/src/yaw-shepard/wtutil.h new file mode 100644 index 0000000..190d0aa --- /dev/null +++ b/src/yaw-shepard/wtutil.h @@ -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 +#include +#include +#include + +#include // 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); + +////////////////////////////////////////////////////////////////////////////// +