Emacs
Mac
Windows
AutoHotkey

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


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に割り当てます。