admin管理员组

文章数量:1566221

数位板配置通用画笔橡皮快捷键

  • 1. 背景
  • 2. 实现
    • 2.1. 主要思路
    • 2.2. 识别窗口
    • 2.3. 模拟按键
    • 2.4. 模拟点击
    • 2.5. 调试细节
      • 2.5.1. 窗口标题
      • 2.5.2. 按键无效
        • 2.5.2.1. 扫描代码
        • 2.5.2.2. 手形问题
          • 2.5.2.2.1. 释放代码
          • 2.5.2.2.2. 按键顺序
      • 2.5.3 其他问题
    • 2.6. 编译代码
    • 2.7. 设置按键
    • 2.8. 原快捷键
      • 2.8.1. Xodo
      • 2.8.2. DrawBoard
      • 2.8.3. 微软白板
  • 3. 代码
    • 3.1. 画笔
    • 3.2. 橡皮
    • 3.3. 套索
    • 3.4. 选择
    • 3.5. 编译

1. 背景

因为有在PDF上写写画画的需求,所以买了一块高漫M6的数位板,便宜入门能用就行。然后在实际使用过程当中,发现偶尔得在几个不同的软件之间切换,而他们的快捷键不同甚至说根本就没有快捷键。这就很难受了,因为一般沉浸式书写的时候,最讨厌打断节奏了。

在多处调研解决方案时,发现了一个AutoHotkey的操作1。但是我感觉这个好像需要一个软件一直在后台运行,这样才能监测到用户对不同窗口按下的同快捷键。以及我并不熟悉AutoHotkey这个工具,查了下资料发现功能还挺强大的,感觉用在这里有些臃肿和浪费。不过这个方法给我带来启发,我们完全可以单独写脚本来触发功能。

无论是模拟鼠标点击屏幕上的工具,还是模拟键盘来使用快捷键,都能用编程语言实现,可以分别写画笔和橡皮的启动脚本。那么还有一个问题就是,这样的脚本该如何触发呢?诶,高漫M6的驱动里面就有方法。为某个键设置快捷键的时候,可以设定其运行指定的exe文件,这不就可行了嘛

还有一个问题,这里得写一下我常用的软件。那么首先是神中神,OneNote,这个在知乎有很多关于快捷键的讨论2。我看了下发现自己的版本不适用,然后也懒得倒退版本。或者也有适用版本的,但是感觉不够优雅,按键有点复杂。然后是Xodo PDF,也很不错,我是在微软应用商店安装的,支持快捷键。但问题就是写一段时间之后,当笔迹多了之后,会变卡,或者是笔迹漂移什么的。接下来是DrawBoard PDF,也很不错,但我愣是一直没发现它还支持快捷键,还是在上面那个教程1的评论区看到的。还有就是BookxNote,在B站发现的一个读书笔记软件,基本没咋用。再说一个Inkodo,好像是可以压感练书法的,没具体用过。以及神奇的ZoomIt,基本可以在任何地方进行屏幕标注。还有一个是微软自带的白板,Microsoft Whiteboard,感觉跟OneNote差不多,但是白板看上去更自由一些,以及这个快捷键是真难搞。

噢,对了,我这脚本是用C语言写的,然后gcc一通编译。由于本人不擅此道,以及脚本实在太过简单,所以什么版本之类的应该不用在意。

2. 实现

2.1. 主要思路

首先是整体的说法,就,在一个具体的PDF软件当中,当用户需要切换画笔的时候,只需要按下手写笔的下键,然后会触发我们自己写的exe应用程序,来模拟点击工具栏里屏幕画笔的按钮,或者是模拟按下快捷键切到画笔。当然仅仅这样是不够的,我们还要适配不同软件的情况,做到多个软件使用同一个脚本。然后就是单个脚本只做单个功能,运行这个脚本就是切换到画笔,想要切换到橡皮得运行另一个脚本。

这里我们来说一说单个脚本的思路吧,这里以画笔为例。首先我们要检测当前所在的窗口是什么软件,因为这意味着我们使用什么方式去触发画笔,比如说在OneNote需要点击屏幕的特定区域,而Xodo直接按I键就能触发墨水Ink的画笔功能。接着就顺理成章,根据不同的软件,用不同的方式去触发画笔,反正不是点击屏幕就是模拟键盘按压。当然还有一个问题,那就是用鼠标点击之后,鼠标的位置还要返回原位置,也就是得预先记录触发脚本时鼠标的位置。

2.2. 识别窗口

这个貌似也是挺麻烦的,我最早的想法是直接根据窗口去抓进程,去看看属于哪个软件,以此来确定接下来需要触发的快捷键。但是查了下教程3发现好像挺复杂的,然后试了下还没复现成功,于是决定尝试获取窗口标题。找了篇教程4,复现成功了,就决定使用GetWindowText()

但是拿到窗口标题,也还需要进一步地加工。比如说我在OneNote开了个名为draft的笔记本,然后标题栏里面就也会出现这个单词,但是其他笔记本又不叫这个名字,所以直接获取到的窗口标题栏并不能作为标识。



但是好在我们可以观察到,页面标题栏的末尾一般跟的是软件的名称,所以我们需要对这串字符做一下小小的分割。但很遗憾的,C语言是很基础的,所以分割字符串这种工具需要自行造轮子。而我这也不是什么大工程,不需要那么完备的轮子,于是找了个教程5自行删减了一版。

2.3. 模拟按键

这个挺简单的,找个教程6,用keybd_event就行,照着官方文档对应的表7查一查对应。

2.4. 模拟点击

这个就有点意思了,因为坐标需要换算一下。首先我们要先知道自己屏幕的分辨率,然后根据这个分辨率确定画笔按钮所在的区域,选一个落在这个区域的点,使得点击这个坐标点就能触发画笔。关于如何获取坐标点,已经有很多方案了,很多截图工具自带的就有,比如Snipaste或者是QQ自带的截图。总之开启截图后,鼠标拉到右下角,那个坐标基本就是分辨率,我的是1920*1080。然后鼠标放在画笔图标上,这个坐标就是启动点。

关于如何换算,参考这篇教程8里面的细节,以我的分辨率为例,大致的公式是: 65536 ∗ ( x _ l o c + 1 ) / 1920 65536 * (x\_loc + 1)/1920 65536(x_loc+1)/1920 65536 ∗ ( y _ l o c + 1 ) / 1080 65536 * (y\_loc + 1)/1080 65536(y_loc+1)/1080,反正就是把坐标放缩或者说标准化到0-65536这个范围内。由于我的坐标是0开始的,所以加了个1。实际上无所谓,这点偏移量没啥影响。

2.5. 调试细节

2.5.1. 窗口标题

主要的调试工作是在获取窗口名称的时候。首先我们需要找的是,程序获取的窗口名称是啥。然后就像代码里面的,get之后我拿MessageBox打印了一下。毕竟编译的时候去除窗口了,而更为巧妙的调试方式我也不会,所以只好用笨办法了。就像这样:

#include <stdio.h>
#include <windows.h>
#include <string.h> 
// #include <stdlib.h>

int main() {
   
  HWND foreground = GetForegroundWindow(); 
  
  if (foreground) 
  {
    
    char window_title[256]; 
    GetWindowText(foreground, window_title, 256);

    // char window_title[] = "draft - draft - OneNote";

    // char *window_title_origin = strdup(window_title);

    char delim[] = " ";

    char *token_iter = NULL;

    char *token_result = NULL;

    for(
      token_iter = strtok(window_title, delim);
      token_iter != NULL;
      token_iter = strtok(NULL, delim)
    ){
   
      token_result = token_iter;
    }

    // MessageBox(NULL,token_result,NULL, MB_OK );

    // MessageBox(NULL,strcat(strcat(token_result,"\n"),window_title_origin),NULL, MB_OK );

    // trim(token_result);

    char concat_result[32];

    sprintf(concat_result, "TT%sTT", token_result);

    MessageBox(NULL,concat_result,NULL, MB_OK );


  }   
  return 0;
}

把这段代码编译成可执行文件,然后绑定到数位板的快捷按键上面。接着去打开OneNote这样的窗口,点击窗口聚集,然后按一下刚刚绑定的快捷键,看一看这个窗口标题被截取到的关键词是什么,截取到的关键词就是判断条件

还有一个地方是关于分割字符串的,我也试过其他的分割字符,但是都不如空格好使。反正空格分割出来的七零八落的,也能给每个软件一个唯一的标识,反正够用了。

2.5.2. 按键无效

这个问题出现在调试微软白板的时候,明明跟其他软件一样设置好了快捷键,但还是不起作用,这个时候就要仔细地、一步一步地查找问题了。

2.5.2.1. 扫描代码

我首先想到的是,代码里面的判断条件激活了吗?也就是说代码是否执行到了这一部分…因为之前确实有过这样的情况,就是我把窗口标题的单词拼写错了,导致代码没有运行到这一部分。那么话说回来,我在这个判断条件里面埋了个MessageBox,正确触发了,看来没问题。

接下来看看按键,我把keybd_event模拟按键换了,改成win+s,也就是唤醒搜索框,这个也成功了,在白板里面能正确触发。

看起来,就是说,模拟白板快捷键的时候出问题了,比如说画笔快捷键alt+w+1,那就以此为关键词搜索吧

在搜索引擎上,确实找到了keybd_event失效的问题,详见这个9。大概就是说,需要改一下参数,增加一个扫描代码

于是继续查找教程,找到这个10

按照这个里面的表格,更改了第二个参数,画笔、橡皮、套索都可以了,但是手形工具还不行

2.5.2.2. 手形问题

继续以模拟按键失效为关键词查找教程,找到这个11

按照教程,我们得用Spy++,直接去GitHub吧,是这个12

然后还需要找一个使用教程,是这个13

这个教程貌似不适用于我这里的情况,所以贴一下我处理的方法吧:

首先,解压下载的包,里面貌似包括了好几个版本,我选了spy14.00.25420。点进这个文件夹之后,由于电脑是64位,所以我直接启动了spyxx_amd64

关于Spy++捕捉日志的设置,如下:

首先去找日志部分,先点sqy再点log那个

打开日志设置,默认的就是窗口设置,勾选监测所有窗口,然后去设置捕捉的消息类型

先清除所有选项,然后选监测所有键盘消息。教程里面只选了WM_HOTKEY,但是我发现只选这个捕捉不到快捷键,最后记得点确定

现在主窗口就会显示所有的键盘消息了,理论上此时主窗口是空白。但如果你敲了键盘的话,是会有一些其他日志的,右键清除即可

2.5.2.2.1. 释放代码

接着切换到白板窗口,先按键盘上面的alt+s,然后再按数位板快捷键,也就是运行我们写好的代码编译出来的可执行文件。首先需要停止记录日志,要不然再按一下别的按键,会污染日志,操作如下:

理论上来说,我这里捕捉到的是10条日志,上面那张图片多了一条,是捕捉到了我在截屏的时候,win+shift+s的那个s,我也不知道为什么就只捕捉到一个s。那么下面这张图片是我再一次捕捉到的日志,为什么这里只有10条而没有截屏呢?是因为我停止了记录日志,然后按F1呼叫Snipaste的全屏快捷键,截下来这张日志图片:


由于我们先按的键盘,后运行的可执行程序,所有从这里我们可以看到的是,前五行代表键盘消息,后五行代表虚拟键盘的消息。一个很显然的问题是,s键在这里释放的时候,物理键盘的扫描代码是0x1F,跟按下的时候一样,并不是教程里面的0x9F,那我们改一下看看吧

2.5.2.2.2. 按键顺序

很遗憾的是,改完释放代码,还是不行,那只能再抓一下日志了。步骤如上,日志如下:

很神奇,我看了下大概是前面的前缀不同,一个是WM_SYSKEYIP,另一个是WM_KEYUP;然后就是,后面的有个参数不同。

查了下,后面那个参数大概意味着,alt键有没有被按下

那这两行日志的区别就是,按下按键之后,松开按键的时候,先松开s再松开alt有效,否则无效

我感觉有点扯,于是用实体键盘模拟了一下,先松开alt是真不行

其实在第一次捕捉日志的时候,这个点就被我注意到了。当时觉得没啥问题,没想到这么神奇

于是我们调换一下松开按键的顺序,问题解决了

2.5.3 其他问题

以及一些其他问题就是,我之前有一次死活都触发不了手写,最后发现是因为zoomit没启动,这真是,太神奇了。

不过这好像在OneNote当中也有不好用的时候,我有时切换笔的按两次。不过这好像不是bug,而是OneNote自带的什么机制,反正就是切换工具时也有相关规则。无所谓,不咋影响使用,暂时不用管。

啊,对,高漫驱动里面那个,Windows Ink一般可以关掉,貌似没啥用。在使用Zoomit的时候,开启这个会使得鼠标指针消失,也就是说在屏幕上看不到代表鼠标的点,这在做批注的时候十分难受。

2.6. 编译代码

我先直接编译了代码,发现默认图标挺丑的,于是就寻思能不能放个自定义的,然后找了篇教程14。大概过程就是,首先你需要一个ico图标文件,可以去这里15输入PNG什么的进行转换。至于PNG,反正只是自己用,应该不会涉及什么版权之类的,直接百度搜画笔图标什么的。

编译图标其实还挺烦的,首先要有个rc文件,比如pen.rc

本文标签: 画笔快捷键橡皮数位