You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

508 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QMessageBox>
#include <QPainter>
#include <QMouseEvent>
#ifdef Q_OS_WIN32
#include <Windows.h>
#include <windowsx.h>
#include <tchar.h>
#include "wintab/WINTAB.H"
#include "wintab/Utils.h"
#include "Dbt.h"
#include "devguid.h"
#endif
#include "signpenpainter.h"
//#include <chrono>
using namespace std;
//inline long long vk_timestamp()
//{
// return chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
//}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pImage = nullptr;
m_pPainter = nullptr;//后台绘图器
m_wintab_handle = 0;
m_mouse_pressed = false;
isUsedMouse = false;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
isUsedInk = false;
ui->checkBox_ink->setVisible(false);
#else
ui->checkBox_ink->setVisible(false);
#endif
ui->plainTextEdit->setVisible(true);
ResetBufferImage();
//动态加载Wintab库
if(!LoadWintab())
{
QMessageBox::warning(nullptr,"Load Wintab","加载Wintab32.dll失败");
}
else
{
gpWTInfoW(0, 0, NULL);
// AXIS axis{0};
AXIS axis;
// //获取x范围
// gpWTInfoW(WTI_DEVICES,DVC_X,&axis);
// m_tablet_ctx.max_x = axis.axMax;
// //获取y范围
// memset(&axis,0,sizeof(AXIS));
// gpWTInfoW(WTI_DEVICES,DVC_Y,&axis);
// m_tablet_ctx.max_y = axis.axMax;
//获取压力范围
memset(&axis,0,sizeof(AXIS));
gpWTInfoW(WTI_DEVICES,DVC_NPRESSURE,&axis);
m_tablet_ctx.max_p = axis.axMax;
// LOGCONTEXTW wintab_ctx{};
LOGCONTEXTW wintab_ctx;
gpWTInfoW(WTI_DEFCONTEXT, 0, &wintab_ctx);
//关心的数据项x y 压力
wintab_ctx.lcPktData = PK_X |PK_Y |PK_NORMAL_PRESSURE;
wintab_ctx.lcOptions = CXO_MESSAGES | CXO_CSRMESSAGES;
//设置Wintab的输出坐标范围 = 数位板设备的逻辑范围
//默认情况下lcOutExtX lcOutExtY 为系统桌面大小
//wintab_ctx.lcOutExtX = wintab_ctx.lcInExtX;
// wintab_ctx.lcOutExtY =wintab_ctx.lcInExtY*-1;
wintab_ctx.lcOutExtY *=-1;
//Wintab返回的系统桌面区域wintab_ctx.lcSysExtX wintab_ctx.lcSysExtY 受系统显示缩放影响
//同时受调用进程的DPI感知特性影响
m_tablet_ctx.sys_x = wintab_ctx.lcSysOrgX;
m_tablet_ctx.sys_y = wintab_ctx.lcSysOrgY;
m_tablet_ctx.sys_w = wintab_ctx.lcSysExtX;
m_tablet_ctx.sys_h = wintab_ctx.lcSysExtY;
//lcInExtX lcInExtY 为设备的输入最大范围
m_tablet_ctx.max_x = wintab_ctx.lcOutExtX;
m_tablet_ctx.max_y = qAbs(wintab_ctx.lcOutExtY);
HWND hwnd = (HWND)this->winId();
m_wintab_handle = (unsigned int)gpWTOpenW((HWND)this->winId(),&wintab_ctx,TRUE);
}
m_SignPenpainter = new SignPenPainter;
m_SignPenpainter->SetPainterRect(this->rect());
penRadius = 1.7;
connect(m_SignPenpainter,SIGNAL(NeedsDisplay(QRect)),this,SLOT(NeedToUpdate(QRect)));
// connect(m_SignPenpainter,SIGNAL(log(QString)),this,SLOT(log(QString)));
}
MainWindow::~MainWindow()
{
delete ui;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
if(QString(eventType)=="windows_generic_MSG")
{
PMSG msg = static_cast<PMSG>(message);
switch(msg->message)
{
case WT_CTXOPEN:
{
qDebug()<<"Tablet wintab open."<<endl;
}break;
case WT_CTXCLOSE:
{
qDebug()<<"Tablet wintab close."<<endl;
}break;
case WT_PROXIMITY:
{
qDebug()<<"Tablet pen proximity event="<<(msg->lParam&0x01?"enter":"leave")<<endl;
}break;
case WT_PACKET:
{
//qDebug()<<"Tablet wintab packet."<<endl;
unsigned char buf[256]{0};
HCTX hctx = (HCTX)msg->lParam;
DWORD num = msg->wParam;
if(gpWTPacket(hctx,num,buf))
{
unsigned x = *(unsigned*)&buf[0];
unsigned y = *(unsigned*)&buf[4];
unsigned p = *(unsigned*)&buf[8];
// qDebug()<<"x:"<<x<<" y:"<<y<<" pressure:"<<p<<endl;
//
TabletDataPacket pkt;
pkt.x = x;
pkt.y = y;
pkt.p = p;
DrawSign_Wintab(pkt);
}
}break;
case WM_POINTERUPDATE:
{
//获取系统指针ID
UINT32 pointerId = GET_POINTERID_WPARAM(msg->wParam);
//获取系统指针设备类型
POINTER_INPUT_TYPE pointerType = PT_POINTER;
if(GetPointerType(pointerId,&pointerType))
{
//判断当前系统指针是否是为笔设备
if(pointerType==PT_PEN)
{
POINTER_PEN_INFO ppi{};
//获取与数字笔相关的信息
if(GetPointerPenInfo(pointerId,&ppi))
{
//获取桌面坐标和笔压力
int x = GET_X_LPARAM(msg->lParam);
int y = GET_Y_LPARAM(msg->lParam);
int pressure = ppi.pressure*8; //统一转化为8192级压力
\
//通常绘图软件会使用Wintab/Windows Ink其中一种进行绘图取决于系统环境支持和绘图软件设计
//Windows Ink的返回坐标与鼠标的全局坐标一致不关心屏幕摆放位置只要光标能到
//**注意:手写板/手写屏在未安装驱动的情况下,光标默认到不了扩展屏幕,通常是由驱动控制到达扩展屏幕或通过系统设置映射到扩展屏。
DrawSign_Ink(x,y,pressure);
//qDebug()<<"ink x:"<<x<<" y:"<<y << " pressure:"<<pressure<<endl;
}
}
}
}break;
}
}
return false;
}
#else
bool MainWindow::winEvent(MSG *message, long *result)
{
// if(QString(eventType)=="windows_generic_MSG")
// {
PMSG msg = static_cast<PMSG>(message);
switch(msg->message)
{
case WT_CTXOPEN:
{
qDebug()<<"Tablet wintab open."<<endl;
}break;
case WT_CTXCLOSE:
{
qDebug()<<"Tablet wintab close."<<endl;
}break;
case WT_PROXIMITY:
{
qDebug()<<"Tablet pen proximity event="<<(msg->lParam&0x01?"enter":"leave")<<endl;
}break;
case WT_PACKET:
{
qDebug()<<"Tablet wintab packet."<<endl;
unsigned char buf[256] = {0};
HCTX hctx = (HCTX)msg->lParam;
DWORD num = msg->wParam;
if(gpWTPacket(hctx,num,buf))
{
unsigned x = *(unsigned*)&buf[0];
unsigned y = *(unsigned*)&buf[4];
unsigned p = *(unsigned*)&buf[8];
// qDebug()<<"x:"<<x<<" y:"<<y<<" pressure:"<<p<<endl;
//
TabletDataPacket pkt;
pkt.x = x;
pkt.y = y;
pkt.p = p;
DrawSign_Wintab(pkt);
}
}break;
// case WM_POINTERUPDATE:
// {
// //获取系统指针ID
// UINT32 pointerId = GET_POINTERID_WPARAM(msg->wParam);
// //获取系统指针设备类型
// POINTER_INPUT_TYPE pointerType = PT_POINTER;
// if(GetPointerType(pointerId,&pointerType))
// {
// //判断当前系统指针是否是为笔设备
// if(pointerType==PT_PEN)
// {
// POINTER_PEN_INFO ppi{};
// //获取与数字笔相关的信息
// if(GetPointerPenInfo(pointerId,&ppi))
// {
// //获取桌面坐标和笔压力
// int x = GET_X_LPARAM(msg->lParam);
// int y = GET_Y_LPARAM(msg->lParam);
// int pressure = ppi.pressure*8; //统一转化为8192级压力
// \
// //通常绘图软件会使用Wintab/Windows Ink其中一种进行绘图取决于系统环境支持和绘图软件设计
// //Windows Ink的返回坐标与鼠标的全局坐标一致不关心屏幕摆放位置只要光标能到
// //**注意:手写板/手写屏在未安装驱动的情况下,光标默认到不了扩展屏幕,通常是由驱动控制到达扩展屏幕或通过系统设置映射到扩展屏。
// // DrawSign_Ink(x,y,pressure);
// //qDebug()<<"ink x:"<<x<<" y:"<<y << " pressure:"<<pressure<<endl;
// }
// }
// }
// }break;
}
// }
return false;
}
#endif
void MainWindow::on_pushButton_clicked()
{
m_SignPenpainter->ClearDraw();
ui->plainTextEdit->clear();
update();
}
void MainWindow::NeedToUpdate(QRect rect)
{
// this->update(QRect(0,0,120,120));
this->update(rect);
}
void MainWindow::paintEvent(QPaintEvent *event)
{
// QPainter painter(this);
// painter.setRenderHints(QPainter::SmoothPixmapTransform
// |QPainter::TextAntialiasing |QPainter::HighQualityAntialiasing);
// if(m_pImage)
// painter.drawImage(rect(),*m_pImage,m_pImage->rect());
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
m_SignPenpainter->DrawRect(&painter,this->rect());
QWidget::paintEvent(event);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
ResetBufferImage();
QMainWindow::resizeEvent(event);
m_SignPenpainter->SetPainterRect(this->rect());
m_SignPenpainter->SetPenType(1);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
// qDebug() << __FUNCTION__ << event->pos();
if(!isUsedMouse)
return;
QPoint pt1 = event->pos();
QPoint p2 = mapToGlobal(pt1);
m_mouse_pressed = false;
m_SignPenpainter->SetStressSwitch(false);
m_SignPenpainter->MousePressed(event->pos(),penRadius * 1.3,10,10);
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
// qDebug() << __FUNCTION__ << event->pos();
if(!isUsedMouse)
return;
QString msg = QString("use ink. pos: %1 %2").arg(event->pos().x()).arg(event->pos().y());
ui->plainTextEdit->appendPlainText(msg);
ui->plainTextEdit->appendPlainText("use mouse.");
m_SignPenpainter->SetStressSwitch(false);
m_SignPenpainter->MouseMoved(event->pos(),penRadius * 1.3,10,10);
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
// qDebug() << __FUNCTION__ << event->pos();
if(!isUsedMouse)
return;
m_mouse_pressed = true;
m_SignPenpainter->MouseReleased(event->pos(),penRadius);
m_SignPenpainter->SetStressSwitch(false);
}
void MainWindow::ResetBufferImage()
{
int w = width();
int h = height();
if(w<0 || h<0)
return;
if(m_pPainter!=nullptr)
{
delete m_pPainter;
}
if(m_pImage!=nullptr)
{
delete m_pImage;
}
//重新创建新的后台缓冲图像对象
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
m_pImage = new QImage(w,h,QImage::Format_RGBA8888);
#else
m_pImage = new QImage(w,h,QImage::Format_ARGB32_Premultiplied);
#endif
m_pImage->fill(Qt::transparent);
//重新创建新的绘图器
m_pPainter = new QPainter(m_pImage);
m_pPainter->setRenderHints(QPainter::SmoothPixmapTransform
|QPainter::TextAntialiasing |QPainter::HighQualityAntialiasing);
}
void MainWindow::DrawSign_Base(int x, int y, int p,bool is_ink)
{
if(isUsedMouse)
return;
if(m_pImage==nullptr )
return;
if(m_tablet_ctx.max_x==0 || m_tablet_ctx.sys_w==0)
return ;
static QPoint last_pt(0,0);
static unsigned last_pressure = 0;
static bool down = false;
//接触时第一个点只保存不画
if(last_pressure==0 && p>0)
{
down = true;
last_pressure = p;
last_pt = QPoint(x,y);
MousePressed(last_pt,p,m_tablet_ctx.max_p,true);
return;
}
//离开时
if(last_pressure>0 && p==0)
{
last_pressure = 0;
MouseReleased(last_pt);
//抬笔时强制更新
update();
down = false;
return;
}
if(p==0)
{
return;
}
int ps = 6; //笔宽
int pw = (int)(ps*(p*1.0/m_tablet_ctx.max_p)+0.5);
QPen pen(is_ink?Qt::blue:Qt::red,pw,Qt::SolidLine);
pen.setCapStyle(Qt::RoundCap);
m_pPainter->setPen(pen);
QPoint cur_pt(x,y);
// m_pPainter->drawLine(last_pt, cur_pt);
MouseMoved(cur_pt,p,m_tablet_ctx.max_p,true);
//todo 刷新机制
// static long long t1 = vk_timestamp();
// long long t2 = vk_timestamp();
// if(t2-t1>33)
// {
// //FPS=30
// update();
// t1 = t2;
// }
update();
last_pressure = p;
//保存当前点
last_pt = cur_pt;
}
void MainWindow::DrawSign_Wintab(const TabletDataPacket &pkt)
{
if(isUsedMouse)
return;
qDebug() << __FUNCTION__ << "wintab.";
ui->plainTextEdit->appendPlainText("wintab.");
//计算Wintab坐标--->全局桌面屏幕坐标
//当扩展屏幕摆放在左边时系统起始坐标为负加上系统原始点得到正确的桌面坐标dx dy
double dx = (pkt.x*1.0/m_tablet_ctx.max_x)*m_tablet_ctx.sys_w+m_tablet_ctx.sys_x;
double dy = (pkt.y*1.0/m_tablet_ctx.max_y)*m_tablet_ctx.sys_h+m_tablet_ctx.sys_y;
QPoint wpt = mapFromGlobal(QPoint(dx,dy));
DrawSign_Base(wpt.x(),wpt.y(),pkt.p,false);
qDebug()<< __FUNCTION__ << "pkt.x:"<<pkt.x<<" pkt.y"<<pkt.y<<" dx:"<<dx<<"dy:"<<dy<<"wx:"<<wpt.x()<<" wy:"<<wpt.y()<<endl;
}
void MainWindow::DrawSign_Ink(int ink_x, int ink_y, int ink_p)
{
if(isUsedMouse)
return;
qDebug() << __FUNCTION__ << "ink.";
QString msg = QString("use ink. pos: %1 %2 press: %3").arg(ink_x).arg(ink_y).arg(ink_p);
ui->plainTextEdit->appendPlainText(msg);
QPoint pt = mapFromGlobal(QPoint(ink_x,ink_y));
DrawSign_Base(pt.x(),pt.y(),ink_p,true);
}
void MainWindow::MousePressed(QPointF currentPoint, double force, double maxForce,bool isStressSwitch)
{
m_SignPenpainter->SetStressSwitch(isStressSwitch);
m_SignPenpainter->MousePressed(currentPoint,penRadius * 1.3,force,maxForce);
}
void MainWindow::MouseMoved(QPointF currentPoint, double force, double maxForce,bool isStressSwitch)
{
m_SignPenpainter->SetStressSwitch(isStressSwitch);
m_SignPenpainter->MouseMoved(currentPoint,penRadius * 1.3,force,maxForce);
}
void MainWindow::MouseReleased(QPointF currentPoint)
{
m_SignPenpainter->MouseReleased(currentPoint,penRadius);
m_SignPenpainter->SetStressSwitch(false);
}
void MainWindow::on_checkBox_stateChanged(int arg1)
{
if(ui->checkBox->isChecked())
isUsedMouse = true;
else
isUsedMouse = false;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
isUsedInk = ui->checkBox_ink->isChecked();
#endif
}