日前跟孝宇老師聊天,老師表示想要有一個按下八個點位 (FDSJKLA;) 能夠打出點字字形的程式,於是我建議用 AutoIt 腳本來實作,這是目前的成果,張貼於此與各位分享。 請點此下載執行檔,使用前請先點此連到 Code2000 字形下載頁面,從 Download 連結下載 code2000.zip 解開並且安裝在電腦上。 Windows 7 只要在 .ttf 檔案的塊選功能表按「安裝」,XP 必須依照 Microsoft 官方說明安裝字形。 打開執行檔後,就能開始打點字,也可以如一般編輯一樣使用倒退、Delete, Enter. 以下建議請留意: (1) 導盲鼠的用戶請關閉導盲鼠的點字鍵盤,因為打字牽涉到 7, 8 點,屬於本程式處理範圍但會引起導盲鼠點字鍵盤失效。 (2) NVDA 用戶請關閉 pcKbBrl (PC Keyboard Braille Input) 附加元件的點字輸入模式,因目前測試結果,若有開此模式則本程式無法接收輸入。 文中剩下的部份會介紹程式碼,以及我當初寫下它的想法,敬請對 AutoIt 有興趣的讀者不吝指教! 下面程式碼的說明假設讀者已經知道: 1. 位元層級的運算,如 AND, OR, NOT 等,這些知識可在高中電腦課程學到 2. 基本的 AutoIt 語法,如變數宣告 (Global, Local), 陣列宣告 (Dim) 以及定義常數 (Const), 標頭檔案的引用 (#include) 3. 如何制定用戶定義函式 (UDF, User-Defined Function) 4. 建立與管理 AutoIt 圖形化介面 (GUI) 的視窗和控制項 5. AutoIt GUI 的消息迴圈 (Message Loop), 包含 While 迴圈和 Switch 或者 Select 處理不同來源的消息 腳本放在全文結束之後。這個腳本分成主要部分與三個函式 (EditText, GetBRL, _User32_VkKeyScanEx). 1. 主要部份:細分為標頭引用、創建 GUI, 設置字形、設置熱鍵、消息迴圈等五大段落。 1.1 標頭引用 程式碼中列出四個標頭檔案,簡述如下: (1) GUIConstants.au3 囊括了所有跟 GUI 有關的常數,如控制項的樣式、消息的種類,程式碼有許多處都要用到它們。 (2) Misc.au3 程式碼中需要從它引入 _IsPressed 函式,且它間接引用 FontConstants.au3, 包含與字形 (font) 相關的常數項目,設置字形時會用到裡面的常數。 (3) WinAPIEx.au3 間接引入許多 Windows API 的標頭,如這段程式碼會間接用到 WinAPILocale.au3 和 WinAPISys.au3 (4) WinAPIvkeysConstants.au3 定義 Windows API 的 Virtual Key Code 常數,用這些常數可以向系統查詢某個按鍵的資訊 1.2 創建 GUI 程式中只有一個視窗,窗內僅一個編輯區控制項。視窗大小 @DesktopWidth, @DesktopHeight 表示開啟時就是全螢幕,編輯區的大小故意選用與它一樣以填滿整個視窗。編輯區樣式有四個參 數: (1) $ES_AUTOHSCROLL 讓編輯區不會自動換行,必須手動按下 Enter 換行 (2) $ES_AUTOVSCROLL 讓編輯區不因為螢幕顯示的行數用完而阻止輸入 (3) $ES_MULTILINE 指示此編輯區為多行編輯 (4) $ES_READONLY 使編輯區變成唯讀,不接受任何會改變其內容的輸入,包含打字與倒退鍵、Enter, Delete 等 細心的讀者將覺得奇怪,阻止了編輯區的編輯功能,為何要 $ES_AUTOHSCROLL 跟 $ES_AUTOVSCROLL 約束換行與 Enter 的關係,以及不因為行數用完就阻止輸入? 這個程式主要目的是能進行八點打字的輸入,送往螢幕顯示點字,因此必須把使用者按下的輸入經過一定處理才送至編輯區。 平常時候編輯區是禁止輸入的,但當程式偵測到合法的點字被輸入時就要送往編輯區當做使用者輸入,這時候兩個樣式就有效果了。 1.3 設置字形 程式中以 GUICtrlSetFont 設置編輯區內的字形。要顯示在編輯區控制項內的「點字」其實指的是 Unicode 中配給點字符號的 256 個編號,範圍在 0x2800~0x28FF. Unicode 裡的字元通常一個字形不會全數支援,因此要特別安裝支援「點字」的字形來正確顯示編輯區的內容。Code2000 顯示出來的點字外觀為八點,凸起的點位全黑,其他點位為空心圓。 若想知道更多支援點字的字形,可參見Font Support for Unicode Block 'Braille Patterns'以及Braille Unicode Fonts, 後者有提供某些字形的載點(不好意思,沒找到好的中文網頁,只好貼英文的上來)。 至於其他的參數,大小設置 150 以方便弱視者看,粗細與特效方面都只採用正常設定。 註:目前程式沒有把字形寫成讓使用者自行選擇,只會去抓 Code2000. 1.4 設置熱鍵 之前提到,此程式為讓使用者以八點方式輸入,但是某些正常的編輯所需功能仍要支援,也就是倒退鍵、Enter 及 Delete. 編輯區已經不處理這些按鍵,因此程式要負責接收它們並且轉送指令至編輯區。 自己定義編輯區的視窗程序 (Windows Procedure) 也可以達到阻止一般輸入只允許倒退鍵、Enter 和 Delete, 但是這種方法比較複雜。 程式想自行處理某些按鍵時,AutoIt 提供 HotKeySet 跟 GUISetAccelerators 二種方法,前者將按鍵綁定一個用戶定義的處理程序上,觀念直覺但會全局性偵測,就是即使 AutoIt 的視窗 不是使用者正在操作的視窗,也會收到熱鍵指令,後者只在使用者正操作 AutoIt 視窗時偵測熱鍵,每個按鍵必須配合一個控制項 ID, 通常是按鈕或者選單上的項目,然而城市中只有一個編 輯區控制項,因此必須有技巧地使用。 GUI 控制項中有一個種類叫 Dummy, 它不需要顯示在螢幕上,但是因為它也是控制項,所以可以用來註冊 GUISetAccelerators. 程式中就針對三個按鍵產生三個 Dummy 控制項,當按鍵被按 下則消息迴圈會接收到 Dummy 控制項的 ID, 認出按鍵進行處理。 1.5 消息迴圈 這段程式中的消息迴圈只偵測使用者輸入按鍵與 GUI 關閉指令。當 AutoIt 視窗為操作中 (active) 時,消息迴圈將一直用 GetBRL 函式來檢查是否有點字輸入,如果有則利用 EditText 函 式轉送消息到編輯區,不過如果點字與空白憶起按下時,目前程式會用 MsgBox 打印一些資訊,其中包含 GetBRL 實際回傳的結果,做為除錯測試之用。 程式中用 And 位元運算來決定是否跟空白一起按下,詳細請參見 GetBRL 函式介紹的最後。 Switch 中即轉送前述三個按鍵到編輯區,以及偵測到 GUI 關閉指令時跳出去結束整個程序。 EditText 指令的參數有出現 $VK_BACK 與 $VK_DELETE, 它們是按鍵的 Virtual Key Code. 鍵盤上每個按鍵都有獨一無二的 Virtual Key Code, 用以跟系統溝通時存取該按鍵有關的資訊, 如當前有沒有被壓下。其他細節請鍵 EditText 的說明。 2. EditText 函式 這個函式負責把正確的使用者輸入轉送到編輯區。 由於編輯區一般狀況設置為唯讀,函式中必須先移除它的 $ES_READONLY 樣式,送出編輯指令後再恢復它。 一般情形下,按下鍵盤會產生 WM_KEYDOWN 消息,如果按下的鍵可以打出字或控制字元如 AutoIt 裡的 @CR 跟 @LF, 就會再產生 $WM_CHAR 消息,兩個消息先後送到編輯區。 但是現在編輯區完全拒絕這些消息,改由腳本送消息至編輯區。 腳本中對編輯區送出點字 Unicode, 倒退、刪除和 Enter 鍵。 (1) 倒退鍵會產生整數值 8 的控制字元,恰好與它的 Virtual Key Code 一致,所以直接把 $VK_BACK 當成 $WM_CHAR 消息送出。 (2) Enter 目前使用整數值 10 的 @LF 當 $WM_CHAR 送出,但我實際以 subclass 方式自訂 callback 函式去抓取送來的消息,其實 Enter 顯示的是整數值 13 的 @CR, 可是腳本要送 @LF 給編輯區才有效果。 (3) 刪除鍵因為不會打字出來,所以只有 WM_KEYDOWN 消息,配合它的 Virtual Key Code 做成刪除鍵的指令。 (4) 點字 Unicode 其實都不是鍵盤上的按鍵,但還是可以利用 $WM_CHAR 消息,告訴它按下點字 Unicode 字元了,編輯區就會乖乖的把這個字打上去。 EditText 開始時先從編輯區的 ID 取得 Handle, 然後取出控制項的樣式。 每個視窗和控制項都有位置、大小、樣式、視窗程序等,可從 _WinAPI_GetWindowLong 拿到指定的資料項目,$GWL_STYLE 即指定「樣式」,拿出編輯區控制項的樣式。 再用位元 Not 及 And 運算,從樣式中去除唯讀,用 GUICtrlSetStyle 設定到編輯區裡。 用 GUICtrlSendMsg 把剛才描述的按鍵消息送入編輯區後,回覆它原本唯讀的樣式。 3. GetBRL 函式 這個函式負責掃描鍵盤當前的狀態,以確定是否有點字被輸入了。 因為每次檢查鍵盤狀態可以立刻知道各個按鍵現在是壓下或拿起,所以很直覺地能夠在某次掃描到點字鍵被按下就知道有點字輸入了。 但是實際的點字機,如果打字者一直壓住鍵盤不全部釋放,然後又去壓下剛才沒按過的點,點字機的游標不會前進,最後完全釋放鍵盤讓游標前進後所打出的點字,是剛才有被按過所有點位 組合起來。以下把八點與空白合稱「點字鍵」,因為都在點字機上。 因此 GetBRL 要跟點字機一樣,能一直累積使用者按下的點位,直到所有點位被釋放的那一刻才認定最終的點字輸入為何。 GetBRL 是一個函式,它裡面設的一般變數只會在執行它的其間存在,執行過就沒了,可是 GetBRL 要讓消息迴圈呼叫然後說出現在是否有點字輸入,它不能自己卡住執行直到點字打好才出來 ,否則視窗程式其他消息就都無法獲得處理。 為了使 GetBRL 會一直記住使用者已經按下那些點字鍵,全域 (Global) 或函式中的靜態 (Static) 變數都可以達到需求。 全域變數寫在函式外,有效的範圍是第一次出現並給值以後到程式碼最尾端,可把 GetBRL 想成一個指令一直去修改全域變數的值,可是因為其他和 GetBRL 無關的程式碼也有機會修改全域 變數,會增加管理的困難。 靜態變數寫在函式內,函式第一次執行給它值以後,它會把該值留到函式下次執行,然後又把下次執行期間給的值保留到下下次,如此下去,然而函式以外的程式碼無法存取它,它完全屬於 該函式掌管。 GetBRL 只要讓消息迴圈知道當前有沒有點字輸入就好,對消息迴圈來說鍵盤模擬的點字機目前有哪些鍵按著不放並不重要,所以使用靜態變數能達到目標又好管理。 GetBRL 函式開始時先宣告九個常數,是要監控九個點字鍵的 Virtual Key Code, 其中代表第八點的分號透過 _User32_VkKeyScanEx 決定 Virtual Key Code 值,詳情請見該函式的解釋,其 他點和空白鍵都用 WinAPIvkeysConstants.au3 定義的常數填寫。 再來就是靜態變數 $bkey, 把它想成好幾個位元,挑出九個位元分別表示點字八點和空白是否被按過,從右邊數來第一個位元對應點字的第一點,第二個位元對應第二點,類推到第八點,第 九個位元就代表空白。 設計成這樣的原因,是點字 Unicode 其實也如此分配,在 0x2800~0x28FF 中可變的是最後八個位元,也從右邊數來第一到第八位元對應一致八點,則 GetBRL 傳回的結果能做最少運算就直 接利用。 接著在一個迴圈用 _IsPressed 掃過九個按鍵,_IsPressed 可以偵測當前這個按鍵是否被按下,偵測到是的話就把 $bkey 裡對應的位元設成 1. 另外還準備一個變數 $state, 若過程中有任何點字鍵還沒被放開,迴圈結束時它就不是 0. 掃描結束後,兩個情形當成沒有抓到點字,一為 $bkey 沒有任何位元為 1 表示不九前也沒有按住點字鍵,二為 $state 不是 0 的話表示使用者還沒放開所有點字鍵,現在無法決定會打出怎 樣點字,傳回 Null 表示 GetBRL 沒有抓到確定的點字輸入。 接著把 $bkey 的值交給 $state, $bkey 自己歸零,表示拿到點字輸入了,點字機的按鍵彈回原位。 因為 $state 原本的值已經用來判斷過了,下面程式碼中不關心 $state 可以直接拿來替代 $bkey 存點字輸入來進一步處理。 最後,如果發現剛才只有空白被按過,傳回答案就是代表空白的 0; 有別的鍵跟空白一起則 $state 原封不動傳回去給消息迴圈處理。 消息迴圈裡,就要算出代表空白的位元是否為 1, 來決定空白有沒有跟其他點一起按下。 代表空白的位元是第九個位元,可以把輸出結果與「只有第九個位元是 1 的常數」做位元 And, 要是輸出結果的第九位元也是 1, 計算結果就不會是 0, 否則計算結果一定為 0. 而計算結果 0 表示沒按下空白,就可以直接轉成真假值來決定是否按下空白了。 4. _User32_VkKeyScanEx 函式 這是一個用戶自定義函式,內容主要是一個 DllCall 從 user32.dll 調用 VkKeyScanEx 函式。 GetBRL 函式呼叫 _User32_VkKeyScanEx 的時候,傳入參數 Asc(";") 和系統預設語系的 ID, VkKeyScanEx 可以根據給入的字元 ASCII 碼及語系 ID, 求出該按鍵當前的 Virtual Key Code. Virtual Key Code 中有些值並未被指定給特定按鍵,鍵盤上也有些按鍵沒有對應的 Virtual Key Code, 每個語系的鍵盤配置可能給予不同的 Code. 因此,要這麼麻煩,過問系統到底分號是哪個 Code, 才不會被電腦上的語系綁住。 不過,如果使用 subclass 的方法,就能直接在自訂的 callback 函式中偵測 $WM_CHAR 事件並抓取分號的輸入,不管哪個語言。 PS: _User32_VkKeyScanEx 函式是參考自這個 AutoIt 討論區網頁的 #15 樓,這是一個國外 AutoIt 的討論區,許多亂七八糟的問題老外都討論過了,像這篇就是有人鍵盤不是英文語系,他要偵測鍵入時要先取得 Virtual Key Code... 如何從非英文的字元去拿到該 Code.
|
本站公告:〔您越需要我們,我們就越有創意〕 | 本站說明書:〔發現故鄉還有改進的地方,請來信告訴原丁們〕 |
觀察應用學習點數 :〔咱的故鄉有您的參與,會使我們有更大的發揮空間,展現更豐富精彩的學習畫面〕 | 〔期待藉由無障礙網頁設計,能讓視障小朋友更愛看書、更愛寫作且更愛學習〕:盲用電腦「心得分享」 |