macOS上的應(yīng)用軟件如何接收諾為翻頁筆的APP通道數(shù)據(jù)
諾為翻頁筆對電腦來說,就是一個(gè)和鼠標(biāo)、鍵盤一樣的HID設(shè)備(Human Interface Device)。其所用的技術(shù),和無線鍵盤、無線鼠標(biāo)是一樣的。
目前,如果想在電腦上獲得翻頁筆的按鍵信息,或者說想用翻頁筆來控制電腦上的軟件,有兩種方法。一種是將翻頁筆的按鍵功能自定義為電腦中的應(yīng)用軟件的快捷鍵,應(yīng)用軟件通過獲得HID通道傳來的快捷鍵,來觸發(fā)相應(yīng)的功能。這種自定義方法,請參見諾為官方網(wǎng)站上的Norwii Presenter軟件的說明書,有比較詳細(xì)的介紹。另一種是先自定義翻頁筆按鍵的APP通道的數(shù)據(jù),應(yīng)用軟件通過獲取APP通道的數(shù)據(jù)來接受控制。本文講述的是Windows上的應(yīng)用軟件如何自定義、讀取諾為翻頁筆的按鍵的APP通道數(shù)據(jù),主要是程序?qū)崿F(xiàn)。
可以把按鍵的短按、長按、雙擊自定義為APP通道的數(shù)據(jù)。本文所述的短按是按鍵抬起時(shí)觸發(fā),只要沒有超過長按計(jì)時(shí)器的按鍵抬起,都是短按。長按是持續(xù)按住按鍵超過長按計(jì)時(shí)器時(shí)觸發(fā)。長按計(jì)時(shí)器一般為1秒,不同的產(chǎn)品或按鍵可能會有差異。
一、諾為設(shè)備的設(shè)置和信息
1. 在Norwii Presenter軟件的【功能自定義】頁面,點(diǎn)擊軟件界面上顯示的翻頁筆按鍵圖標(biāo)或這個(gè)圖標(biāo)對應(yīng)的藍(lán)色文字,會打開按鍵自定義窗口。翻頁筆的按鍵模式有3種或者4種,翻頁筆的當(dāng)前按鍵模式通過同時(shí)長按翻頁筆的上、下翻頁鍵進(jìn)行切換,下圖中有4種模式。用戶可以選擇按鍵模式中的一種進(jìn)行自定義,自定義的結(jié)果也只在這種模式下有效。
2. 點(diǎn)擊“短按功能”處“下拉框箭頭”-“APP通道”進(jìn)入APP通道窗口,在編輯框輸入十進(jìn)制數(shù)字1~255的一個(gè)數(shù)值作為APP通道的值,點(diǎn)擊“確定”后,在上個(gè)窗口點(diǎn)擊“保存”。
3. 如何確定將翻頁筆按鍵的值自定義為APP通道的數(shù)據(jù)是成功的?
在【對碼】頁面,短按已經(jīng)把短按功能自定義為“APP通道”的按鍵,窗口右下方白色框中出現(xiàn)“APP通道+數(shù)字”,如“APP通道 1”,說明翻頁筆按鍵的APP通道的自定義設(shè)置成功了。按照這種方法,也可能對按鍵的長按功能進(jìn)行自定義。
4. 如何獲取諾為翻頁筆的VID和PID。
在【關(guān)于】頁面,找到顯示“固件信息”的行,右側(cè)顯示諾為翻頁筆的VID和PID,這兩個(gè)值在USB HID通信中會使用到。頁面顯示的這兩個(gè)值是省略了前綴0x的16進(jìn)制數(shù)值,在程序中使用的時(shí)候記得加上前綴0x,下圖中VID是16進(jìn)制數(shù)值0x3243,PID是16進(jìn)制數(shù)值 0x0341,諾為翻頁筆的VID都是相同的,不同型號的產(chǎn)品的PID是不同的,同一型號的產(chǎn)品的PID一般是相同的,但同一型號的產(chǎn)品、不同的主控芯片也會有不同的PID。
二、USB HID通信
1. Frameworks:IOKit.framework
2. 導(dǎo)入頭文件
#import <IOKit/hid/IOHIDLib.h>
3. 初始化IOHIDManager
IOHIDManagerRef managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
4. 進(jìn)行配對設(shè)置,可以過濾其他USB設(shè)備
1) 無配對設(shè)備
IOHIDManagerSetDeviceMatching(managerRef, NULL);
2)單類設(shè)備配對(其中“pid”和“vid”分別替換為Presenter軟件中查到的值)
NSMutableDictionary* dict= [NSMutableDictionary dictionary];
[dict setValue:[NSNumber numberWithLong:pid] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];
[dict setValue:[NSNumber numberWithLong:vid] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];
IOHIDManagerSetDeviceMatching(managerRef, (__bridge CFMutableDictionaryRef)dict);
3)多種設(shè)備配對設(shè)置
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:dict];
IOHIDManagerSetDeviceMatchingMultiple(managerRef, (__bridge CFMutableArrayRef)arr);
5. 注冊插拔設(shè)備的callback
IOHIDManagerRegisterDeviceMatchingCallback(managerRef, &HandleDeviceMatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(managerRef, &HandleDeviceRemovalCallback, NULL);
6. 加入RunLoop
IOHIDManagerScheduleWithRunLoop(managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
7. 打開IOHIDManager
IOReturn IOReturn = IOHIDManagerOpen(managerRef, kIOHIDOptionsTypeNone);
if(kIOReturnSuccess != IOReturn) puts("IOHIDManagerOpen failed.");
8. 實(shí)現(xiàn)插拔callback
void Handle_DeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
CFTypeRef ref;
int32_t nVendorID, nProductID;
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nVendorID);
}
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nProductID);
}
NSLog(@"insert hid, vid is:%d, pid is:%d",nVendorID, nProductID);
CFRelease(ref);
}
void Handle_DeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
CFTypeRef ref;
int32_t nVendorID, nProductID;
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nVendorID);
}
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nProductID);
}
NSLog(@"remove hid, vid is:%d, pid is:%d",nVendorID, nProductID);
CFRelease(ref);
}
9. 插入設(shè)備獲取到IOHIDDeviceRef inIOHIDDeviceRef后,打開IOHIDDeviceRef
IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef,options);
if (ret != kIOReturnSuccess)
{
printf("Open device failed! ");return;
}
10. 注冊接收數(shù)據(jù)方法,即可接收數(shù)據(jù)
cpp
unsigned char InputBuffer[64];
IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), MyInputCallback, NULL);
11. 接收數(shù)據(jù)callback,當(dāng)操作定義為“APP通道”數(shù)據(jù)的按鍵會收到設(shè)備發(fā)送來的數(shù)據(jù)
int appPath = 0;
void MyInputCallback(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength)
{
memcpy(ReceiveData,report,sizeof(ReceiveData));
if (report[0] == 0x03 && report[1] == 0x31 && report[2] == 0x00 &&
report[3] == 0x00 && report[4] == 0x02) {
appPath = report[5];
}
}
12. 向USB設(shè)備發(fā)送指令
IOReturn ret = IOHIDDeviceSetReport(inIOHIDDeviceRef, kIOHIDReportTypeOutput, 0, (uint8_t*)outbuffer, size);
if (ret != kIOReturnSuccess) {
PRINTMSG("發(fā)送hid數(shù)據(jù)失敗,error number: %x ",ret);
return ret;
}
13. 斷開設(shè)備
IOReturn ret = IOHIDDeviceClose(inIOHIDDeviceRef,0L);
if (ret == kIOReturnSuccess) {
NSLog(@"斷開成功");
}
14. 可能出現(xiàn)的問題
1)出現(xiàn)如下報(bào)錯(cuò):
TCC deny IOHIDDeviceOpen
Open device failed!
需要在“啟動臺 - 系統(tǒng)設(shè)置 – 隱私與安全性”中賦予“輸入監(jiān)控”權(quán)限
2)賦予權(quán)限后依然不能打開hid時(shí),需要在Xcode中,勾選“USB”選項(xiàng)來允許應(yīng)用程序訪問USB設(shè)備。以下是在Xcode中勾選“USB”選項(xiàng)的步驟:
打開Xcode。
打開你的項(xiàng)目(PROJECT)。
在項(xiàng)目導(dǎo)航面板中,選擇你的目標(biāo)(TARGETS)。
點(diǎn)擊“Signing & Capabilities”標(biāo)簽頁。
如果“App Sandbox”沒有被添加到你的項(xiàng)目中,你需要點(diǎn)擊“+ Capability”按鈕來添加它。
在“App Sandbox”中,找到“Hardware”部分。
勾選“USB”選項(xiàng)。
請注意,從Xcode 13開始,蘋果引入了新的App Sandbox特性,這可能會影響你如何配置App Sandbox。如果你使用的是Xcode 13或更高版本,并且找不到“USB”選項(xiàng),可能是因?yàn)檫@個(gè)選項(xiàng)已經(jīng)被移動或者更改了。在這種情況下,你可能需要查看蘋果的官方文檔或者Xcode的更新日志來獲取最新的配置方法。