잡담

맥북 키보드 설정

jjuni_96 2024. 9. 30. 09:49
728x90

현재 개인적으로 사용하고 있는 노트북은 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

 

728x90
반응형
LIST