Xbox One 控制器轉換為 macOS HID 設備的工作原理分析

Xbox One 控制器轉換為 macOS HID 設備的工作原理分析

源代碼在 https://github.com/guilhermearaujo/xboxonecontrollerenabler.git

這個工程的核心功能是將 Xbox One 控制器(macOS 原生不支持的設備)轉換為 macOS 可識別的 HID 設備。這里通過分析代碼,詳細解釋其工作原理、設備描述和報告描述符的實現。

整體架構

該項目由三個主要部分組成:

  1. Xbox 控制器通信層:通過 IOKit 框架與 Xbox One 控制器進行 USB 通信
  2. 虛擬 HID 設備層:使用 VHID 框架創建虛擬 HID 設備
  3. 系統集成層:使用 WirtualJoy 框架將虛擬設備注冊到 macOS 系統

Xbox One 控制器設備描述

Xbox One 控制器的設備描述在代碼中通過 XboxOneButtonMap 結構體定義:

typedef struct {bool sync;bool dummy; // Always 0.bool menu;  // Not entirely sure what these arebool view;  // called on the new controllerbool a;bool b;bool x;bool y;bool dpad_up;bool dpad_down;bool dpad_left;bool dpad_right;bool bumper_left;bool bumper_right;bool stick_left_click;bool stick_right_click;unsigned short trigger_left;unsigned short trigger_right;short stick_left_x;short stick_left_y;short stick_right_x;short stick_right_y;bool home;
} XboxOneButtonMap;

這個結構體映射了 Xbox One 控制器的所有輸入元素,包括:

  • 按鈕(A、B、X、Y、方向鍵、肩鍵等)
  • 搖桿(左右搖桿的 X/Y 坐標)
  • 扳機鍵(左右扳機的模擬值)

控制器通信實現

GAXboxControllerCommunication 類負責與 Xbox One 控制器通信:

  1. 通過 USB 供應商 ID (0x045e) 和產品 ID (0x02d1) 識別 Xbox One 控制器
  2. 使用 IOKit 框架打開設備并配置接口
  3. 初始化控制器并開始輪詢數據
  4. poll 方法中讀取原始數據并解析為 XboxOneButtonMap 結構

關鍵代碼片段:


- (void)poll {while (shouldPoll) {UInt32 numBytes = 20;char dataBuffer[32];returnCode = (*usbInterface)->ReadPipe(usbInterface, 2, dataBuffer, &numBytes);if (numBytes == 18) {Byte b = dataBuffer[4];buttonMap.sync  = (b & (1 << 0)) != 0;buttonMap.dummy = (b & (1 << 1)) != 0;buttonMap.menu  = (b & (1 << 2)) != 0;buttonMap.view  = (b & (1 << 3)) != 0;buttonMap.a = (b & (1 << 4)) != 0;buttonMap.b = (b & (1 << 5)) != 0;buttonMap.x = (b & (1 << 6)) != 0;buttonMap.y = (b & (1 << 7)) != 0;b = dataBuffer[5];buttonMap.dpad_up    = (b & (1 << 0)) != 0;buttonMap.dpad_down  = (b & (1 << 1)) != 0;buttonMap.dpad_left  = (b & (1 << 2)) != 0;buttonMap.dpad_right = (b & (1 << 3)) != 0;buttonMap.bumper_left       = (b & (1 << 4)) != 0;buttonMap.bumper_right      = (b & (1 << 5)) != 0;buttonMap.stick_left_click  = (b & (1 << 6)) != 0;buttonMap.stick_right_click = (b & (1 << 7)) != 0;buttonMap.trigger_left  = (dataBuffer[7] << 8) + (dataBuffer[6] & 0xff);buttonMap.trigger_right = (dataBuffer[9] << 8) + (dataBuffer[8] & 0xff);buttonMap.stick_left_x  = (dataBuffer[11] << 8) + dataBuffer[10];buttonMap.stick_left_y  = (dataBuffer[13] << 8) + dataBuffer[12];buttonMap.stick_right_x = (dataBuffer[15] << 8) + dataBuffer[14];buttonMap.stick_right_y = (dataBuffer[17] << 8) + dataBuffer[16];[delegate controllerDidUpdateData:buttonMap];}else if (numBytes == 6) {buttonMap.home = dataBuffer[4] & 1;[delegate controllerDidUpdateData:buttonMap];}[NSThread sleepForTimeInterval:0.005f];}
}

虛擬 HID 設備實現

VHIDDevice 類負責創建虛擬 HID 設備,它通過組合 VHIDButtonCollectionVHIDPointerCollection 來管理按鈕和指針(搖桿)狀態:

- (id)initWithType:(VHIDDeviceType)typepointerCount:(NSUInteger)pointerCountbuttonCount:(NSUInteger)buttonCountisRelative:(BOOL)isRelative
{self = [super init];m_Type      = type;m_Buttons   = [[VHIDButtonCollection alloc] initWithButtonCount:buttonCount];m_Pointers  = [[VHIDPointerCollection alloc] initWithPointerCount:pointerCountisRelative:isRelative];// ... 初始化狀態數據m_Descriptor = [[self createDescriptor] retain];return self;
}

HID 報告描述符生成

VHIDDevice 類的 createDescriptor 方法負責生成 HID 報告描述符,這是關鍵部分:

- (NSData*)createDescriptor
{BOOL             isMouse        = (m_Type == VHIDDeviceTypeMouse);NSData          *buttonsHID     = [m_Buttons descriptor];NSData          *pointersHID    = [m_Pointers descriptor];NSMutableData   *result         = [NSMutableData dataWithLength:[buttonsHID length] +[pointersHID length] +((isMouse)?(HIDDescriptorMouseAdditionalBytes):(HIDDescriptorJoystickAdditionalBytes))];unsigned char   *data           = [result mutableBytes];unsigned char    usage          = ((isMouse)?(0x02):(0x05));*data = 0x05; data++; *data = 0x01; data++;      // USAGE_PAGE (Generic Desktop)*data = 0x09; data++; *data = usage; data++;     // USAGE (Mouse/Game Pad)*data = 0xA1; data++; *data = 0x01; data++;      // COLLECTION (Application)// ... 添加按鈕和指針描述符*data = 0xC0; data++; // END_COLLECTION*data = 0xC0; data++; // END_COLLECTIONreturn result;
}

這個方法創建了一個標準的 HID 報告描述符,定義了設備類型(游戲手柄)、按鈕和指針(搖桿)的布局。

系統集成

WJoyDeviceWJoyDeviceImpl 類負責將虛擬 HID 設備注冊到 macOS 系統:

  1. 加載內核驅動程序
  2. 創建與驅動程序的連接
  3. 設置設備屬性(產品名稱、供應商 ID、產品 ID 等)
  4. 啟用設備并更新 HID 狀態

關鍵代碼:

- (id)initWithHIDDescriptor:(NSData*)HIDDescriptor properties:(NSDictionary*)properties
{// ... 初始化代碼m_Impl = [[WJoyDeviceImpl alloc] init];// 設置設備屬性if(productString != nil)[m_Impl setDeviceProductString:productString];// ... 設置其他屬性// 啟用設備if(![m_Impl enable:HIDDescriptor]){[self release];return nil;}return self;
}

數據流轉換過程

整個數據流轉換過程如下:

  1. GAXboxControllerCommunication 從 Xbox One 控制器讀取原始 USB 數據
  2. 數據被解析為 XboxOneButtonMap 結構體
  3. GAXboxController 處理這些數據并提供高級訪問方法
  4. GAMainViewController 將控制器數據映射到虛擬 HID 設備:
- (void)updateVHID:(GAXboxController *)controller {[_VHID setButton:0 pressed:[controller A]];[_VHID setButton:1 pressed:[controller B]];// ... 設置其他按鈕NSPoint point = NSZeroPoint;point.x = [controller leftAnalogX];point.y = [controller leftAnalogY];[_VHID setPointer:0 position:point];// ... 設置其他指針
}
  1. VHIDDevice 更新其內部狀態并通知代理
  2. WJoyDevice 將更新后的 HID 狀態發送到系統

報告提交流程

  1. VHIDDevice.m 中的狀態更新和報告生成
    當按鈕或指針狀態發生變化時,VHIDDevice會生成新的狀態報告:

    - (void)setButton:(NSUInteger)buttonIndex pressed:(BOOL)pressed {// ... 檢查按鈕狀態是否變化[m_Buttons setButton:buttonIndex pressed:pressed];if(m_Delegate != nil)[m_Delegate VHIDDevice:self stateChanged:[self state]];
    }
    
    - (NSData*)state {unsigned char *data = [m_State mutableBytes];NSData *buttonState = [m_Buttons state];NSData *pointerState = [m_Pointers state];// 合并按鈕和指針狀態到一個完整的HID報告if(buttonState != nil) {memcpy(data, [buttonState bytes], [buttonState length]);}if(pointerState != nil) {memcpy(data + [buttonState length], [pointerState bytes], [pointerState length]);}return [[m_State retain] autorelease];
    }
    
  2. VHIDButtonCollection.m 和 VHIDPointerCollection.m
    這兩個類負責維護按鈕和指針的狀態,并生成對應的HID報告部分:

    在VHIDButtonCollection中:

    - (void)setButton:(NSUInteger)buttonIndex pressed:(BOOL)pressed {// ... 檢查按鈕索引NSUInteger buttonByte = buttonIndex / 8;NSUInteger buttonBit = buttonIndex % 8;unsigned char *data = (unsigned char*)[m_State mutableBytes];// 設置對應位的按鈕狀態if(pressed)data[buttonByte] |= buttonMasks[buttonBit];elsedata[buttonByte] &= ~(buttonMasks[buttonBit]);
    }
    

    在VHIDPointerCollection中:

    - (void)setPointer:(NSUInteger)pointerIndex position:(NSPoint)position {// ... 檢查指針索引char *data = (char*)[m_State mutableBytes] + pointerIndex * HIDStatePointerSize;// 設置X和Y坐標值*data = [VHIDPointerCollection clipCoordinateTo:position.x];*(data + 1) = -[VHIDPointerCollection clipCoordinateTo:position.y];
    }
    
  3. GAMainViewController.m 中的代理方法
    當VHIDDevice狀態變化時,通過代理方法將狀態傳遞給WJoyDevice:

    - (void)VHIDDevice:(VHIDDevice *)device stateChanged:(NSData *)state {[_virtualDevice updateHIDState:state];
    }
    
  4. WJoyDevice.m 中的更新方法
    最后,WJoyDevice將HID狀態報告提交給系統:

    - (BOOL)updateHIDState:(NSData*)HIDState {return [m_Impl updateState:HIDState];
    }
    

總結

這個工程通過以下步驟將 Xbox One 控制器轉換為 macOS 可識別的 HID 設備:

  1. 使用 IOKit 框架與 Xbox One 控制器通信,讀取原始輸入數據
  2. 將這些數據解析為結構化的按鈕和搖桿狀態
  3. 創建一個虛擬 HID 設備,生成標準的 HID 報告描述符
  4. 將控制器狀態映射到虛擬 HID 設備狀態
  5. 通過內核驅動程序將虛擬設備注冊到系統

這種方法允許 macOS 將 Xbox One 控制器識別為標準游戲手柄,從而在不需要官方驅動的情況下實現兼容性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87391.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87391.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87391.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Notepad++ 復制宏、編輯宏的方法

Notepad具有宏的功能&#xff0c;能夠記錄當下所有操作&#xff0c;后續只需要一鍵就可以重復執行&#xff0c;大大減少工作量。 比如我需要把很多文件里面的字符完成替換&#xff0c;那我只需要把替換的過程錄制成宏&#xff0c;后續打開文件就可以一鍵替換了。 但是Notepad的…

Oracle:報錯jdbc:oracle:thin:@IP地址:端口:實例名, errorCode 28001, state 99999

報錯原因是oracle密碼過期&#xff0c;根本解決辦法是讓密碼不再過期&#xff0c;永久有效。具體操作記錄一下。 cmd命令行輸入&#xff1a; sqlplus / as sysdba修改Oracle密碼期限為無限&#xff1a; SQL> ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;SQL&…

Apipost 簽約中原消費金融:共建企業級 API 全鏈路協作平臺,推動接口管理與測試智能化升級

隨著企業數字化轉型的不斷深化&#xff0c;API 正在從技術細節演變為業務協作的核心樞紐。特別是在金融行業&#xff0c;微服務架構、系統聯動、合規要求等多重因素交織下&#xff0c;接口數量激增、管理復雜度提升、質量保障難度加大。近日&#xff0c;Apipost 與中原消費金融…

AntV L7 之LarkMap 地圖

一、安裝$ npm install -S antv/l7 antv/larkmap # or $ yarn add antv/l7 antv/larkmap二、引入包import type { LarkMapProps, LineLayerProps } from antv/larkmap; import { LarkMap, LineLayer, Marker } from antv/larkmap;三、config配置const layerOptions:Omit<Lin…

客戶案例 | 某新能源車企依托Atlassian工具鏈+龍智定制開發服務,打造符合ASPICE標準的研發管理體系

客戶案例 ASPICE標準已成為衡量整車廠及供應商研發能力的重要標尺。某知名車企在其重點項目研發過程中&#xff0c;面臨著ASPICE 4.0評估認證的挑戰——項目團隊缺乏體系經驗、流程規范和數字化支撐工具。 為幫助該客戶團隊順利通過ASPICE認證并提升研發合規性&#xff0c;At…

stm32的USART使用DMA配置成循環模式時發送和接收有著本質區別

stm32的USART使用DMA配置成循環模式時發送和接收有著本質區別&#xff0c;不要被網上誤導了。發送數據時會不停的發送數據&#xff0c;而接收只有有數據時才會接收&#xff0c;沒有數據時就會掛起等待。 一、觸發機制的差異? ?發送方向&#xff08;TX&#xff09;——狀態驅…

銀河麒麟系統上利用WPS的SDK進行WORD的二次開發

目錄 1.下載安裝包 2.安裝WPS 3.獲取示例代碼 4.編譯示例代碼 5.完整示例代碼 相關鏈接 1.下載安裝包 去wps的官網 https://www.wps.cn/ 下載linux版本。 下載的安裝包名稱為&#xff1a;wps-office_12.8.2.21176.AK.preload.sw_amd64.deb, 官網有介紹適用于Ubuntu、麒麟…

人工智能之數學基礎:如何判斷正定矩陣和負定矩陣?

本文重點 正定矩陣和負定矩陣是線性代數中的重要概念,在優化理論、數值分析、統計學等領域有廣泛應用。 正定矩陣(負定矩陣) 如上所示,我們可以看到滿足上面的性質的時候,我們可以認為矩陣A稱為正定矩陣(負定矩陣) 舉例: 半正定(半負定) 如果≥或者≤的時候,我們認為矩…

匯編基礎介紹——ARMv8指令集(四)

一、CMP 指令 CMP 指令用來比較兩個數的大小。在 A64 指令集的實現中&#xff0c;CMP 指令內部調用 SUBS 指令來實現。 1.1、使用立即數的 CMP 指令 使用立即數的 CMP 指令的格式如下。 CMP <Xn|SP>, #<imm>{, <shift>} 上述指令等同于如下指令。 SUBS …

深入剖析 Electron 性能瓶頸及優化策略

Electron 是一個流行的跨平臺桌面應用開發框架&#xff0c;基于 Chromium 和 Node.js&#xff0c;使得開發者可以使用 Web 技術&#xff08;HTML、CSS、JavaScript&#xff09;構建跨平臺的桌面應用。許多知名應用如 VS Code、Slack、Discord 和 Figma 都采用了 Electron。然而…

Qt的前端和后端過于耦合(0/7)

最近在寫一個軟件&#xff0c;這個軟件稍微復雜一些&#xff0c;界面大概需要十幾個&#xff0c;后端也是要開多線程讀各種傳感器數據。然后鼠鼠我呀就發現一個致命的問題&#xff0c;那就是前端要求的控件太多了&#xff0c;點一下就需要通知后端&#xff0c;即調用后端的函數…

碰一碰發視頻源碼搭建定制化開發:支持OEM

在移動互聯網與物聯網深度融合的當下&#xff0c;“碰一碰發視頻” 作為一種創新的信息交互方式&#xff0c;正逐漸應用于營銷推廣、產品展示、社交互動等多個領域。其核心在于通過近場通信技術&#xff08;如 NFC、藍牙&#xff09;實現設備間的快速連接&#xff0c;無需復雜操…

機器學習文本特征提取:CountVectorizer與TfidfVectorizer詳解

一、文本特征提取概述 在自然語言處理&#xff08;NLP&#xff09;和文本挖掘任務中&#xff0c;文本特征提取是將原始文本數據轉換為機器學習模型可以理解的數值特征的關鍵步驟。scikit-learn提供了兩種常用的文本特征提取方法&#xff1a;CountVectorizer&#xff08;詞頻統…

【PHP】.Hyperf 框架-collection 集合數據(內置函數歸納-實用版)

&#x1f4cc; Article::query()->where(article_id, 6)->select()->first()?? 進行數據結果的循環&#xff0c;遍歷 1.each() 方法遍歷集合中的項目并將每個項目傳遞給閉包&#xff0c;進行處理數據 Article::query()->get()->each(function ($item) {// 可…

巨獸的陰影:大型語言模型的挑戰與倫理深淵

當GPT-4這樣的龐然大物能夠流暢對話、撰寫詩歌、編寫代碼、解析圖像&#xff0c;甚至在某些測試中媲美人類專家時&#xff0c;大型語言模型&#xff08;LLM&#xff09;仿佛成為了無所不能的“智能神諭”。然而&#xff0c;在這令人目眩的成就之下&#xff0c;潛藏著復雜而嚴峻…

vue根據鏈接生成二維碼 qrcode

vue根據鏈接生成二維碼 qrcode js 需求&#xff1a;后端返回一個完整鏈接&#xff0c;前端根據鏈接生成一個二維碼 1、安裝qrcode插件 npm install qrcode2、引入qrcode,并且使用完整代碼 <template> <div><img :src"qrcodeData" class"qrcode…

C# 事件(源代碼組件概覽)

源代碼組件概覽 需要在事件中使用的代碼有5部分&#xff0c;如圖15-4所示&#xff0c;后文會依次進行介紹。這些組件如下 所示 委托類型聲明事件和事件處理程序必須有共同的簽名和返回類型&#xff0c;它們通過委托類型 進行描述。事件處理程序聲明訂閱者類中會在事件觸發時執…

音視頻會議服務搭建(設計方案-數據庫sql)-02

前言 銜接上篇文章&#xff0c;這篇是相關的表結構sql語句記錄 EchoMeet 會議系統數據庫表結構設計 &#x1f4cb; 設計概述 本文檔定義了EchoMeet音視頻會議系統的完整數據庫表結構&#xff0c;采用微服務架構設計&#xff0c;支持高并發、可擴展的會議場景。 &#x1f3af…

MCPA2APPT 智能化演示文稿系統:A2A、MCP、ADK 三大架構全流程自動化

&#x1f680; 項目名稱 MCPA2APPT / MultiAgentPPT —— 一站式 A2A MCP ADK 多智能體并發 PPT 生成解決方案 MCPA2APPT 是一款開源 AI PPT 創作神器&#xff0c;基于 A2A&#xff08;Ask-to-Answer&#xff09;、MCP&#xff08;Multi-agent Control Protocol&#xff09;和…

pyinstall打包mysql-connector-python后運行報錯的問題!

簡單的測試代碼 # main.py import mysql.connectorDB_HOSTlocalhost DB_PORT3306 DB_NAMElover DB_USERroot DB_PASSWORDxxxx# 連接數據庫 connection mysql.connector.connect(hostDB_HOST,portDB_PORT,databaseDB_NAME,userDB_USER,passwordDB_PASSWORD)if connection.is_c…