현재 개인적으로 사용하고 있는 노트북은 Apple M1 Pro 모델이다.
이전에 karabiner를 사용해서 윈도우처럼 한영키를 사용중이었지만, 베타버전의 새로운 기능(iphone 미러링)을 체험해보고 싶다는 생각에 업데이트를 해버렸다. 그러더니 karabiner에서 에러가 발생해서 한영키가 제대로 작동하지 않았었다.
생각보다 해당 이슈가 잦았기 때문에 hammerspoon을 사용해보자 라는 생각으로 끄적여본다..
1. karabiner 삭제
일단 이 프로그램은 "휴지통으로 이동" 을 해서 삭제되지 않고 다른 방식으로 삭제를 해야됬었다.

1-1. karabiner-Elements
일반 카라비너는 해당 프로그램을 실행시켜서 uninstall 방식으로 삭제를 해야했다.

1-2. karabiner-EventViewer
이게 karabiner-elements 를 삭제하고 나니까 켜지지도 않고, 휴지통으로 삭제되지도 않아서 당황스러웠다...
명령어를 입력해서 맥북에서 해당 응용프로그램의 보호를 해제를 하고나서 휴지통으로 지워야했다.
sudo chflags nouchg,noschg /Applications/Karabiner-EventViewer.app
2. hammerspoon 설치
2-1. hammerspoon 다운로드
github 사이트에서 hammerspoon을 다운로드 받는다 (os 버전별로 차이가 있을 수 있음!)
https://github.com/Hammerspoon/hammerspoon/releases/tag/1.0.0
Release 1.0.0 · Hammerspoon/hammerspoon
Anniversary This release comes after 100 releases of 0.9, and just two months shy of Hammerspoon’s 10th birthday. Thank you all for your part in this project, it’s been a lot of fun! Minimum macOS ...
github.com
2-2. hammerspoon 설정 적용
여러가지 설정들이 있지만 "로그인 시 hammerspoon" 등록 외에는 다 종료하였다.
또한 "Enable Accessibility" 설정을 눌러서 시스템에서 허용을 해줘야한다.

2-3. 기본 설정파일 등록
~/.hammerspoon/init.lua
local FRemap = require('foundation_remapping')
require('inputsource_aurora') #한/영 구분을 편하게 보기 위한 설정
local remapper = FRemap.new()
remapper:remap('capslock', 'f18')
remapper:register()
local function remap(mods, key, pressFn)
hs.hotkey.bind(mods, key, pressFn, nil, pressFn)
end
local function pressFn(mods, key)
if key == nil then
key = mods
mods = {}
end
return function() hs.eventtap.keyStroke(mods, key, 1000) end
end
~/.hammerspoon/foundation_remapping.lua
-- foundation_remapping.lua
-- https://github.com/hetima/hammerspoon-foundation_remapping
-- reference:
-- Technical Note TN2450 Remapping Keys in macOS 10.12 Sierra)
-- https://developer.apple.com/library/content/technotes/tn2450/
local FOUNDATION_REMAPPING_VERSION = '0.1.1'
local log = hs.logger.new('foundation_remapping', 'debug')
CFundationRemap = {
version = FOUNDATION_REMAPPING_VERSION,
}
-- Never use these values as it is.
-- Bitwise OR with 0x700000000 required. (or simply add 0x700000000)
CFundationRemap.hidkeys = {
[0x00] = 0x04, -- a
[0x0b] = 0x05,
[0x08] = 0x06,
[0x02] = 0x07,
[0x0e] = 0x08,
[0x03] = 0x09,
[0x05] = 0x0a,
[0x04] = 0x0b,
[0x22] = 0x0c,
[0x26] = 0x0d,
[0x28] = 0x0e,
[0x25] = 0x0f,
[0x2e] = 0x10,
[0x2d] = 0x11,
[0x1f] = 0x12,
[0x23] = 0x13,
[0x0c] = 0x14,
[0x0f] = 0x15,
[0x01] = 0x16,
[0x11] = 0x17,
[0x20] = 0x18,
[0x09] = 0x19,
[0x0d] = 0x1a,
[0x07] = 0x1b,
[0x10] = 0x1c,
[0x06] = 0x1d, -- z
[0x12] = 0x1e, -- 1
[0x13] = 0x1f,
[0x14] = 0x20,
[0x15] = 0x21,
[0x17] = 0x22,
[0x16] = 0x23,
[0x1a] = 0x24,
[0x1c] = 0x25,
[0x19] = 0x26,
[0x1d] = 0x27, -- 0
[0x24] = 0x28, -- Return
[0x35] = 0x29, -- esc
[0x33] = 0x2a, -- delete back space
[0x30] = 0x2b, -- tab
[0x31] = 0x2c, -- space
[0x1b] = 0x2d, -- - and _
[0x18] = 0x2e, -- = and +
[0x21] = 0x2f, -- [ and {
[0x1e] = 0x30, -- ] and }
[0x2a] = 0x31, -- \ and |
[0x2a] = 0x32, -- Non-US # and ~
[0x29] = 0x33, -- ; and :
[0x27] = 0x34, -- ' and "
[0x32] = 0x35, -- Grave Accent and Tilde E/J
[0x2b] = 0x36, -- , and "<"
[0x2f] = 0x37, -- . and ">"
[0x2c] = 0x38, -- / and ?
[0x39] = 0x39, -- Caps Lock
[0x7a] = 0x3a, -- F1
[0x78] = 0x3b,
[0x63] = 0x3c,
[0x76] = 0x3d,
[0x60] = 0x3e,
[0x61] = 0x3f,
[0x62] = 0x40,
[0x64] = 0x41,
[0x65] = 0x42,
[0x6d] = 0x43,
[0x67] = 0x44,
[0x6f] = 0x45, -- F12
[0x69] = 0x46, PrintScreen = 0x46, -- Print Screen
[0x6b] = 0x47, ScrollLock = 0x47, -- Scroll Lock
[0x71] = 0x48, Pause = 0x48, -- Pause
[0x72] = 0x49, Insert = 0x49,-- Insert conflict with help
[0x73] = 0x4a, -- Home
[0x74] = 0x4b, -- Page Up
[0x75] = 0x4c, -- Delete Forward
[0x77] = 0x4d, -- End
[0x79] = 0x4e, -- Page Down
[0x7c] = 0x4f, --Right arrow key, raw is 0x3c, virtual ADB is 0x7c
[0x7b] = 0x50, --Left arrow key, raw is 0x3b, virtual ADB is 0x7b
[0x7d] = 0x51, --Down arrow, raw is 0x3d, virtual is 0x7d
[0x7e] = 0x52, --Up arrow key, raw is 0x3e, virtual is 0x7e
[0x47] = 0x53, --Num Lock and Clear
[0x4b] = 0x54, -- Keypad /
[0x43] = 0x55, -- pad *
[0x4e] = 0x56, -- pad -
[0x45] = 0x57, -- pad +
[0x4c] = 0x58, -- pad Enter
[0x53] = 0x59, -- pad 1
[0x54] = 0x5a,
[0x55] = 0x5b,
[0x56] = 0x5c,
[0x57] = 0x5d,
[0x58] = 0x5e,
[0x59] = 0x5f,
[0x5b] = 0x60,
[0x5c] = 0x61,
[0x52] = 0x62, -- pad 0
[0x41] = 0x63, -- pad .
[0x0a] = 0x64, -- \ and | ISO only
[0x6e] = 0x65, Application=0x65,-- Application
[0x7f] = 0x66, --This is the power key, scan code in ADB is 7f 7f, not 7f ff
[0x51] = 0x67, -- pad =
-- [0x69] = 0x68, -- F13 on Andy keyboards conflict with PrintScreen
-- [0x6b] = 0x69, -- F14 on Andy keyboards conflict with ScrollLock
-- [0x71] = 0x6a, -- F15 on Andy keyboards conflict with Pause
[0x6a] = 0x6b, -- F16
[0x40] = 0x6c, -- F17
[0x4f] = 0x6d, -- F18
[0x50] = 0x6e, -- F19
[0x5a] = 0x6f, -- F20
f21=0x70, f22=0x71, f23=0x72, f24=0x73,
Execute=0x74,
-- [0x72] = 0x75, --help conflict with insert
Menu=0x76, Select=0x77, Stop=0x78, Again=0x79, Undo=0x7a, Cut=0x7b, Copy=0x7c, Paste=0x7d, Find=0x7e,
[0x4a] = 0x7f, -- Norsi Mute, or maybe 0x4a
[0x48] = 0x80, -- Norsi volume up, otherwise is 0x48 in ADB
[0x49] = 0x81, -- Norsi volume down
LockCapsLock=0x82, LockNumLocl=0x83, LockScrollLock=0x84,
[0x5f] = 0x85, -- pad , JIS only
-- padEqualSign=0x86
International1=0x87, [0x5e] = 0x87, JISUnderScore = 0x87,-- Ro (JIS) International1 _ ろ
International2=0x88, PCKana=0x88, -- PC Kana|Roma-ji
International3=0x89, [0x5d] = 0x89, -- Yen (JIS) ¥
International4=0x8a, XFER=0x8a, Henkan=0x8a, -- XFER 変換
International5=0x8b, NFER=0x8b, Muhenkan=0x8b,-- NFER 無変換
International6=0x8c, -- ,
International7=0x8d, -- DoubleByte/SingleByte
International8=0x8e, -- undef
International9=0x8f, -- undef
[0x68] = 0x90, -- Kana lang1
[0x66] = 0x91, -- Eisu lang2
lang3=0x92, --Hiragana?
lang4=0x93, --Katakana?
lang5=0x94, --Zenkaku/Hankaku?
lang6=0x95,
lang7=0x96,
lang8=0x97,
lang9=0x98,
[0x3b] = 0xe0, lctrl = 0xe0, lctl = 0xe0,--Left Control. raw is 0x36, virtual is 0x3b
[0x38] = 0xe1, lshift = 0xe1, --Left Shift
[0x3a] = 0xe2, lalt = 0xe2, lopt = 0xe2, --Left option/alt key
[0x37] = 0xe3, lcmd = 0xe3,--Left command key
[0x3e] = 0xe4, rctrl = 0xe4, rctl = 0xe4, --Right Control, use 0x3e virtual
[0x3c] = 0xe5, rshift = 0xe5, --Right Shift, use 0x3c virtual
[0x3d] = 0xe6, ralt = 0xe6, ropt = 0xe6, --Right Option, use 0x3d virtual
[0x36] = 0xe7, rcmd = 0xe7, --Right Command, use 0x36 virtual
-- 全角/半角キーはいくつか該当しそうなのがあるけれど、うちでは [0x32] = 0x35, -- Grave Accent and Tilde と判定される
}
for i, v in pairs(CFundationRemap.hidkeys) do
if type(v) == 'number' then
CFundationRemap.hidkeys[i] = v + 0x700000000
end
end
-- keyCode を数値に統一
local function realKeyCode(v)
if type(v) == 'string' then
v = hs.keycodes.map[v]
end
if type(v) == 'number' then
return v
end
return nil
end
-- keyCode を hidutil で使える値に変換
local function hidKeyCode(keyCode)
local hidCode = nil
if (type(keyCode) == 'number') and (keyCode > 0x700000000) then
return keyCode
end
--hidkeys[string] があるかどうか
if type(keyCode) == 'string' then
hidCode = CFundationRemap.hidkeys[keyCode]
if hidCode ~= nil then
return hidCode
end
end
keyCode = realKeyCode(keyCode)
if keyCode ~= nil then
--数値keyCodeで探す
return CFundationRemap.hidkeys[keyCode]
end
return nil
end
local CFundationRemapImpl = {
remap = function(self, fromKey, toKey)
fromKey = hidKeyCode(fromKey)
toKey = hidKeyCode(toKey)
if fromKey and toKey then
table.insert(self._remaps, {from=fromKey, to=toKey})
end
return self
end,
nullfy = function(self, fromKey)
fromKey = hidKeyCode(fromKey)
if fromKey then
table.insert(self._remaps, {from=fromKey, to=0})
end
return self
end,
-- --filter '{"ProductID":...,"VendorID":...}'
_filterArgument = function(self)
local filter = ''
if self.productID then
filter = '"ProductID":' .. self.productID .. ','
end
if self.vendorID then
filter = filter .. '"VendorID":' .. self.vendorID .. ','
end
local optionName = '--filter'
if os.execute("hidutil property --help | grep -e '--matching'") then
optionName = '--matching'
end
if #filter > 0 then
return ' ' .. optionName .. ' \'{' .. filter .. '}\''
end
return ''
end,
-- /usr/bin/hidutil property --filter '{"ProductID":...,"VendorID":...}' --set '{"UserKeyMapping":[{...},...]}'
command = function(self)
if #self._remaps == 0 then
return nil
end
local filter = self:_filterArgument()
local cmd = '/usr/bin/hidutil property' .. filter .. ' --set \'{"UserKeyMapping":['
for i, v in ipairs(self._remaps) do
if type(v) == 'table' then
cmd = cmd .. '{"HIDKeyboardModifierMappingSrc":' .. v.from .. ',"HIDKeyboardModifierMappingDst":' .. v.to .. '},'
end
end
cmd = cmd .. ']}\''
return cmd
end,
-- /usr/bin/hidutil property --filter '{"ProductID":...,"VendorID":...}' --set '{"UserKeyMapping":[]}'
resetCommand = function(self)
local filter = self:_filterArgument()
local cmd = '/usr/bin/hidutil property' .. filter .. ' --set \'{"UserKeyMapping":[]}\''
return cmd
end,
register = function(self)
local cmd = self:command()
if cmd then
if os.execute(cmd) ~= true then
log.d('error occured while register()')
log.d('command:' .. cmd)
end
end
return self
end,
unregister = function(self)
local cmd = self:resetCommand()
if cmd then
if os.execute(cmd) ~= true then
log.d('error occured while unregister()')
log.d('command:' .. cmd)
end
end
return self
end,
}
CFundationRemap.new = function(opt)
local _self = {
_remaps = {},
vendorID = nil,
productID = nil,
}
setmetatable(_self, {__index = CFundationRemapImpl})
if type(opt) == 'table' then
if type(opt.vendorID) == 'number' then
_self.vendorID = opt.vendorID
end
if type(opt.productID) == 'number' then
_self.productID = opt.productID
end
end
return _self
end
return CFundationRemap
~/.hammerspoon/inputsource_aurora.lua
local boxes = {}
local box_height = 23
local box_alpha = 0.35
local GREEN = hs.drawing.color.osx_green
-- 입력소스 변경 이벤트에 이벤트 리스너를 달아준다
hs.keycodes.inputSourceChanged(function()
local inputSource = {
english = "com.apple.keylayout.ABC",
korean = "com.apple.inputmethod.Korean.2SetKorean",
}
local current = hs.keycodes.currentSourceID()
local language = nil
if current == inputSource.korean then
language = '🇰🇷 한글'
elseif current == inputSource.english then
language = '🇺🇸 영문'
else
language = current
end
-- alert
hs.alert.closeAll()
hs.alert.show(language)
-- boxes
disable_show()
if hs.keycodes.currentSourceID() ~= inputSource.english then
enable_show()
end
end)
function enable_show()
reset_boxes()
hs.fnutils.each(hs.screen.allScreens(), function(scr)
local frame = scr:fullFrame()
-- 상단 박스
local box = newBox()
draw_rectangle(box, frame.x, frame.y, frame.w, box_height, GREEN)
table.insert(boxes, box)
-- 하단 박스
local box2 = newBox()
draw_rectangle(box2, frame.x, frame.y + frame.h - 10, frame.w, box_height, GREEN)
table.insert(boxes, box2)
end)
end
function disable_show()
hs.fnutils.each(boxes, function(box)
if box ~= nil then
box:delete()
end
end)
reset_boxes()
end
function newBox()
return hs.drawing.rectangle(hs.geometry.rect(0,0,0,0))
end
function reset_boxes()
boxes = {}
end
function draw_rectangle(target_draw, x, y, width, height, fill_color)
-- 그릴 영역 크기를 잡는다
target_draw:setSize(hs.geometry.rect(x, y, width, height))
-- 그릴 영역의 위치를 잡는다
target_draw:setTopLeft(hs.geometry.point(x, y))
target_draw:setFillColor(fill_color)
target_draw:setFill(true)
target_draw:setAlpha(box_alpha)
target_draw:setLevel(hs.drawing.windowLevels.overlay)
target_draw:setStroke(false)
target_draw:setBehavior(hs.drawing.windowBehaviors.canJoinAllSpaces)
target_draw:show()
end
위처럼 적용하면 캡스락으로 한/영 전환이 가능하며, 국기, 테두리를 포함하여 잘 나오게 된다!
refs
https://blog.naver.com/tech_garage/222680246367
카라바이너(Karabiner) 맥에서 삭제하는 방법 : 항목이 잠겨있기 때문에 작업을 완료할 수 없습니다
오늘은 카라바이너(Karabiner)를 맥북, 맥미니, 아이맥에서 깔끔하게 제거하는 방법에 대해서 이야기해보...
blog.naver.com
'잡담' 카테고리의 다른 글
| 구글 서치콘솔 + 티스토리 + 개인도메인 (7) | 2024.08.02 |
|---|