AutoHotkeyでMac USキーボード完全互換を目指す

#この記事の目的
Windows環境 + MacのUSキーボードを使って、
あたかもMac + USキーボードでKarabinerを使っているような環境を構築する。

方法は、以下のステップで構成する
1,左右のwindowsキーを副作用の無い他のキーに変更する
2,AutoHotkeyを使って、キーコンビネーションを任意の機能に割り当てる
3,IMEの設定で、commandキー単体を押しをIME ON とIME OFFに割り当てる

##キーの割り当て変更
まずchangeKeyなどのレジストリのキーボードマップをいじるツールを使って、commandキー(windowsではwinキーと認識される)を副作用の無い他のキーに割り当てる。副作用とは、キーを押したことで何かアクションが起こることを意味します。あとで、AutoHotkeyでコンビネーションショートカットを機能に割り当てますが、その時にキーの元の機能を完全には無効にできないため。

自分の場合は、左のwinキーをPause、右のwinキーをscroll lockに割り当てました。
(後で気づいたんですが、scroll lockの機能は、エクセルなどでカーソルキーで隣のセルに移動するか、スクロールするかを切り替えるので、完全に副作用なしとは行きませんでした。誰か他の副作用の無いキーがあったら教えてください。)

changeKeyで見ると以下のようになります。
(左のAltとctrlは自分の趣味です。ここでは必須では無いです)


changeKey

![image.png](https://qiita-image-store.s3.amazonaws.com/0/211497/250d31bd-e261-59eb-11de-f3d7a666b078.png)


##AutoHotkey
AutoHotkeyで割り当てを変更したPauseキーと任意のキーコンビネーションに機能を割り当てます。
ここでは、キー単体を押した時の設定は行いません。
あくまでもコンビネーションの割り当てのみです。


```AutoHotkey.ahk
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
SetTitleMatchMode,2
#InstallKeybdHook
#UseHook

#ifWinActive Lightshot.exe

enter::
    winGetTitle, window_title, ahk_exe Lightshot.exe
    if inStr(window_title, "save")
        msgbox, "LWin+4"
;        send {enter}
    else send ^{s}
    return

#ifWinActive

;LControl::LAlt
vkFF::Send, {vk1D}  ;左Windowsキー to 無変換キー
ScrollLock::Send, {vk1C}  ;右Windowsキー to 変換キー
vkFF & Space::Send, ^{Esc}
vkFF & Enter::Send, ^{Enter}
vkFF & n::Send, ^{n}
vkFF & x::Send, ^{x}
vkFF & c::Send, ^{c}
vkFF & v::Send, ^{v}
vkFF & s::Send, ^{s}
vkFF & o::Send, ^{o}
vkFF & p::Send, ^{p}
vkFF & a::Send, ^{a}
vkFF & f::Send, ^{f}
vkFF & l::Send, ^{l}
vkFF & w::Send, ^{w}
vkFF & t::Send, ^{t}
vkFF & g::Send, ^{g}
vkFF & z::Send, ^{z}

Shift & Backspace::Send, {Del}

vkFF & Tab::AltTab

RAlt & Up::Send, {PgUp}
RAlt & Down::Send, {PgDn}
RAlt & Left::Send, {Home}
RAlt & Right::Send, {End}



vkFF & 4::
    If GetKeyState("Shift", "P")
        send, {PrintScreen}
    Else
        msgbox, "LWin+4"
    Return

vkFF & 3::
    If GetKeyState("Shift", "P")
        send, #{s}
    Else
        msgbox, "LWin+4"
    Return


;;
;; An autohotkey script that provides emacs-like keybinding on Windows

; The following line is a contribution of NTEmacs wiki http://www49.atwiki.jp/ntemacs/pages/20.html
SetKeyDelay 0

; turns to be 1 when ctrl-x is pressed
is_pre_x = 0
; turns to be 1 when ctrl-space is pressed
is_pre_spc = 0

; Applications you want to disable emacs-like keybindings
; (Please comment out applications you don't use)
is_target()
{
  IfWinActive,ahk_class VirtualConsoleClass
    Return 1
  IfWinActive,ahk_class ConsoleWindowClass ; Cygwin
    Return 1
  IfWinActive,ahk_class MEADOW ; Meadow
    Return 1
  IfWinActive,ahk_class cygwin/x X rl-xterm-XTerm-0
    Return 1
  IfWinActive,ahk_class MozillaUIWindowClass ; keysnail on Firefox
    Return 1
  ; Avoid VMwareUnity with AutoHotkey
  IfWinActive,ahk_class VMwareUnityHostWndClass
    Return 1
  IfWinActive,ahk_class Vim ; GVIM
    Return 1
;  IfWinActive,ahk_class SWT_Window0 ; Eclipse
;    Return 1
;   IfWinActive,ahk_class Xming X
;     Return 1
;   IfWinActive,ahk_class SunAwtFrame
;     Return 1
;   IfWinActive,ahk_class Emacs ; NTEmacs
;     Return 1  
;   IfWinActive,ahk_class XEmacs ; XEmacs on Cygwin
;     Return 1
  Return 0
}
tab_window()
{
  Send !{Tab}
  global is_pre_spc = 0
  Return
}
delete_char()
{
  Send {Del}
  global is_pre_spc = 0
  Return
}
delete_backward_char()
{
  Send {BS}
  global is_pre_spc = 0
  Return
}
kill_line()
{
  Send {ShiftDown}{END}{SHIFTUP}
  Sleep 50 ;[ms] this value depends on your environment
  Send ^x
  global is_pre_spc = 0
  Return
}
open_line()
{
  Send {END}{Enter}{Up}
  global is_pre_spc = 0
  Return
}
quit()
{
  Send {ESC}
  global is_pre_spc = 0
  Return
}
newline()
{
  Send {Enter}
  global is_pre_spc = 0
  Return
}
indent_for_tab_command()
{
  Send {Tab}
  global is_pre_spc = 0
  Return
}
newline_and_indent()
{
  Send {Enter}{Tab}
  global is_pre_spc = 0
  Return
}
isearch_forward()
{
  Send ^f
  global is_pre_spc = 0
  Return
}
isearch_backward()
{
  Send ^f
  global is_pre_spc = 0
  Return
}
kill_region()
{
  Send ^x
  global is_pre_spc = 0
  Return
}
kill_ring_save()
{
  Send ^c
  global is_pre_spc = 0
  Return
}
yank()
{
  Send ^v
  global is_pre_spc = 0
  Return
}
undo()
{
  Send ^z
  global is_pre_spc = 0
  Return
}
find_file()
{
  Send ^o
  global is_pre_x = 0
  Return
}
save_buffer()
{
  Send, ^s
  global is_pre_x = 0
  Return
}
kill_emacs()
{
  Send !{F4}
  global is_pre_x = 0
  Return
}

move_beginning_of_line()
{
  global
  if is_pre_spc
    Send +{HOME}
  Else
    Send {HOME}
  Return
}
move_end_of_line()
{
  global
  if is_pre_spc
    Send +{END}
  Else
    Send {END}
  Return
}
previous_line()
{
  global
  if is_pre_spc
    Send +{Up}
  Else
    Send {Up}
  Return
}
next_line()
{
  global
  if is_pre_spc
    Send +{Down}
  Else
    Send {Down}
  Return
}
forward_char()
{
  global
  if is_pre_spc
    Send +{Right}
  Else
    Send {Right}
  Return
}
backward_char()
{
  global
  if is_pre_spc
    Send +{Left}
  Else
    Send {Left}
  Return
}
scroll_up()
{
  global
  if is_pre_spc
    Send +{PgUp}
  Else
    Send {PgUp}
  Return
}
scroll_down()
{
  global
  if is_pre_spc
    Send +{PgDn}
  Else
    Send {PgDn}
  Return
}



;vkFF & Tab::
;  If is_target()
;    Send %A_ThisHotkey%
;  Else
;    tab_window()
;  Return 

^f::
  If is_target()
    Send %A_ThisHotkey%
  Else
    forward_char()
  Return  
^d::
  If is_target()
    Send %A_ThisHotkey%
  Else
    delete_char()
  Return
^h::
  If is_target()
    Send %A_ThisHotkey%
  Else
    delete_backward_char()
  Return
^w::
  If is_target()
    Send %A_ThisHotkey%
  Else
    kill_region()
  Return
^g::
  If is_target()
    Send %A_ThisHotkey%
  Else
    quit()
  Return
^m::
  If is_target()
    Send %A_ThisHotkey%
  Else
    newline()
  Return
!s::
  If is_target()
    Send %A_ThisHotkey%
  Else
    isearch_forward()
  Return
^s::
  If is_target()
    Send %A_ThisHotkey%
  Else
    isearch_forward()
  Return
^@::
  Suspend, Toggle
  Return
^a::
  If is_target()
    Send %A_ThisHotkey%
  Else
    move_beginning_of_line()
  Return
^e::
  If is_target()
    Send %A_ThisHotkey%
  Else
    move_end_of_line()
  Return
^p::
  If is_target()
    Send %A_ThisHotkey%
  Else
    previous_line()
  Return
^n::
  If is_target()
    Send %A_ThisHotkey%
  Else
    next_line()
  Return
^b::
  If is_target()
    Send %A_ThisHotkey%
  Else
    backward_char()
  Return
```


##IMEの設定
あとは、IMEの設定でキー単体押した時をIME ON とIME OFFに割り当てます。