=========. -========. :========- .========- ========= =========. -========. :========- .========- ========= =========. -========. :========- .==================== :@@@@=@@@@+ @@@@%*@@@% @@@@++@@@@ #@@@+=%@@@ =@@@@%*@@@::@@@*==%@@+ @@@@==@@@% @@@++++@@@ #@@@+=%@@@ =@@@+=%@@@::@@@#=#@@@+ @@@*-+@@@% @@@@%+@@@@ #@@@@@@@@@@@@@@@@@@@@: :@@@: .@@@+ @@@- @@@% @@@::. @@@ #@@+-..@@@ =@@@* @@@::@@@ .:%@@+ @@@ ::+@@% @@@=- -@@@ #@@- : @@@ =@@- = %@@::@@% + *@@+ @@@ = %@@% @@@@*#@@@@ #@@@@@++@+@++*#%@@@@@: :@@@:@-%@@+ @@@%- @@@% @@@@= +@@@ #@@@#..@@@ =@@= . %@@::@@@.- =@@+ @@@ . :@@% @@@@::@@@@ #@@= ..@@@ =@@* . *@@::@@+ @ =@@+ @@@ + +@@% @@@@@@@@@@ #@@@@@..-.-:=..#@@@@@: :@@@@@@@@@+ @@@@- @@@% @@@. .:@@@ #@@= . @@@ =@@*=. #@@::@@% . *@@+ @@@.. -@@% @@@@ %@@@@ #@@- . @@@ =@@# . @@@::@@@ . %@@+ @@@ +. @@% @@@@@@@@@@ #@@@@@%%%@%@@%@%@@@@@: :@@@@@@@@@+ @@@@@@@@@% @@@@@@@@@@ #@@@@@@@@@ =@@@@@@@@@::@@@@@@@@@+ @@@@@@@@@% @@@@@@@@@@ #@@@@@@@@@ =@@@@@@@@@::@@@@%@@@@+ @@@@@%@@@% @@@@@@@@@@ #@@@@@@@@@@@@@@@@@@@@: ......... ......... ......... ......... ......... ......... ......... ......... ......... ......... ......... ......... ......... .................... %@@@@@@@@@@@@@# :@@@@@@@@%. %@@@@@@@@: #@@@@@@@@= *@@@@@@@@# =@@@@@@@@# :@@@@@@@@%. %@@@@@@@@: #@@@@@@@@= *@@@@@@@@# =@@@@@@@@# :@@@%@%@@%. %@@@@@@@@: #@@@@@@@@@@@@@@: :@@@@@@@@@@@@@@@ =@@=...+@@::@:##.+@.@+ @@#...:@@% @@#...:@@@ #@@....+@@ =@@=...=@@::@@-=@+:@@+ @@@@:*@@@% @@@-..-@@@ #@@:..:@@@ =@@#:*:%@@::@@@@=@@@@+ @@@@@@@@@@@@@@@: :@@@@==#-*-=@@@@ =@# %@* @@::@.:. = @+ @@+ ==*@@% @@+ ++ *@@ #@@@= @@@@ =@@@%. %@@::@@.:@- @@+ @@@@ -@@@% @@: @@..@@ #@@ -# -@@ =@@ =@..@@::@@*: :+@@+ @@-=-+=:+-*:-@@: :@@@@*+:--:-@@@@ =@# *=- @@::@+ % =@+ @@+ ==*@@% @@+ - :@@@ #@@@= @@@@ =@@* :@@@@::@@..@- @@+ @@@@ -@@@% @@. @@..@@ #@@ :-@@@ =@@ =@..@@::@@%# *%@@+ @@:---+-#.+::@@: :@@@@@@@@@@@@@@@ =@@*... %@::@@.:@- @@+ @@* ...@@% @@* @% +@@ #@@@+.@@@@ =@@. ..=@@::@@*...+@@+ @@@@.+@@@% @@@....%@@ #@@.+@@@@@ =@@. - =@@::@@@@@@@@@+ @@@@@@@@@@@@@@@: .@@@@@@@@@@@@@@@ -@@@@@@@@@:.@@@@@@@@@= @@@@@@@@@# @@@@@@@@@@ *@@@@@@@@@ -@@@@@@@@@:.@@@@@@@@@= @@@@@@@@@# @@@@@@@@@@ *@@@@@@@@@ -@@@#*%@@@:.@@@@@@@@@= @@@@@@@@@@@@@@@: ............... ...... ...... ...... ...... ...... ...... ...... ...... ...... ...... ...... ...... -@@@@@@@@@@@@: .@@@@@@@@@@@@@@@@@@ *@@@@@@@@@ =@@@@@@@@@:.@@@@@@@@@+ @@@@@@@@@# @@@@@@@@@@ *@@@@@@@@@ =@@@@@@@@@:.@@@@@@@@@+ @@@@@@@@@# @@@%++%@@@ *@@@+*#@@@ =@@@@@@@@@::@@@@@@@@@@@@: :@@@@@@@@@@@@@@@@@@ #@@@. %@@@ =@@- . *@@::@@. . =@@+ @@@ ..:@@% @@%... #@@ #@@ #@.-@@ =@@@@* @@@::@@::# :@@+ @@@ #@@@@% @@@=. -@@@ #@@@:.#@@@ =@@@-- %@@::@@@@@@@@@@@@: :@@@@-=#.=..-.%@@@@ #@@= : @@@ =@@- .:#@@::@@.:@+ @@+ @@@ ..+@@% @@:.@=:+@@ #@@ .. -@@ =@@@@* @@@::@@. :@@@+ @@@ #@@@@% @@- #% :@@ #@@% -@@@ =@@= . +@@::@@@@@@@@@@@@: :@@@@*=++=+@+=%@@@@ #@% .. =@@ =@@:-# :@@::@@..#: @@+ @@@ #@@@@% @@= *= .@@ #@@ #@ -@@ =@@-:= @@@::@@. # -@@+ @@@ +##@@% @@..@@. @@ #@@ . #@@ =@@- +@@::@@@@@@@@@@@@: :@@@@@@@@@@@@@@@@@@ #@*=@@*-@@ =@@%::-@@@::@@+::=@@@+ @@@:%@@@@% @@@*::-%@@ #@@:%@=*@@ =@@%-:#@@@::@@++@#:%@+ @@@-::-@@% @@%....#@@ #@+ ##:.@@ =@@*+=%@@@::@@@@@@@@@@@@: #################+ -########* .######### #########: *########= +########+ -########* .######### #########: *########= +########+ -########* .######### .############: :::::::::::: ::::::::: .:::::::: .::::::::. ::::::::. ::::::::: ::::::::: .:::::::: .::::::::. ::::::::. ::::::::: ::::::::: .:::::::::::::::::::::::::::: :@@@@@@@@@@@@::@@@@@@@@@+ @@#%@@#@@% @@@#@@#@@@ #@@@%*@@@@ =@@#@@@#@@::@@%##%@@@+ @@%%@@#@@% @@%#@@#%@@ #@@@@@@@@@ =@@@@@@@@@::@@@@@@@@@+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@: :@%*%@##***#@::@@@#-.+@@+ @@* *. @@% @@@ .. @@@ #@@ .: +@@ =@@ :@..@@::@@. : .@@+ @@: =# @@% @@. =+ @@ #@@@%#@@@@ =@@@@@@@@@::@@@%**@@@+ @@@@@@@@@@*##%##*#*%@@@@@@@@@: :@-::=::.#:%@::@@* .+@@@+ @@@* .@@@% @@@@ @@@@ #@* @@@@@@ =@@* * %@@::@@. . :@@+ @@: : @@% @@.. . @@ #@@@*.@@@@ =@@@%.@@@@::@@@%-=@@@+ @@@@@@@@@@::.:..=@:@@@@@@@@@@: :@#+#@###@#@@::@@@@%-+@@+ @@@@ =@@@% @@% :: %@@ #@@ .- =@@ =@@@. :@@@::@@. -. @@+ @@:.% @@% @@.- - @@ #@@@%@@@@@ =@@@@@@@@@::@@@@@@@@@+ @@@@@@@@@@*##%##@@#@@@@@@@@@@: :@@@@@@@@@@@@::@@@@@@@@@+ @@@@#%@@@% @@%#@@#%@@ #@@@#*%@@@ =@@@@*@@@@::@@#**#@@@+ @@%#@@*@@% @@#%%#%#@@ #@@@@@@@@@ =@@@@@@@@@::@@@@@@@@@+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@: :----------: :-------- :--------. .--------: --------: --------: :-------- :--------. .--------: --------: --------: :-------- :---------------------------: ************ ************ ************ ******************************************************************** ************ ************ ************ ************ :@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@: :@@=+=+==+%@@::@@@=++=*-%@@::@@*+*==+=*@@::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@::@@*+*==+=*@@::@@@=++=*-%@@::@@=+=+==+%@@::@@@@=*=*@@@@: :@*-++*..:=%@::@@*-::=%:@@@::@@-==..:=-@@::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@::@@-==..:=-@@::@@*-::=%:@@@::@*-++*..:=%@::@@@@.#. @@@@: :@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@::@@@@@@@@@@@@: .@@@@@@@@@@@@:.@@@@@@@@@@@@:.@@@@@@@@@@@@:.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:.@@@@@@@@@@@@:.@@@@@@@@@@@@:.@@@@@@@@@@@@:.@@@@@@@@@@@@: :::::::::::: :::::::::::: :::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::: :::::::::::: :::::::::::: ::::::::::::
macOS switches input sources per app and per window — never per keyboard. InputPilot watches which keyboard produced the last key press and switches the layout automatically, whether that is the built-in board or an external one. Map each keyboard once, then forget it exists.
macOS 13+ · signed & notarized · Apache-2.0 · v1.0.1 · brew install --cask lucagerlich/tap/inputpilot
Nothing. You type on the other keyboard and the language is already right. The debug log is the only place it shows its work — useful when reporting a bug, and safe to share.
Three steps, once.
Type on each keyboard so InputPilot learns it — built-in, external, USB or Bluetooth. Devices are remembered and recognised again after unplugging, reconnecting, or moving to another port.
Assign an input source per keyboard in Settings. Add per-device or global fallbacks for anything unmapped.
A 400 ms debounce and a 1.5 s cooldown keep it from flapping. Modifier-only presses are ignored, so ⌘-Tab never changes your layout.
InputPilot asks for Input Monitoring — the permission that lets an app see keyboard events. That is a lot to ask for, so here is exactly what it does with it.
It reads which keyboard sent a key press, not what you typed.
InputPilot is not sandboxed — it talks to IOKit's HID layer directly, which is the most reliable way to tell one physical keyboard from another. App Store apps must be sandboxed, which constrains both HID access and input-source switching. That is not an absolute barrier — OctoKeys does per-keyboard switching on the App Store — but outside the store there is no review cycle, the app stays free, and you can read every line of the thing you are granting keyboard access to. It is signed with a Developer ID certificate and notarized by Apple, so it opens without warnings.
Some Macs do not populate that list automatically. Add it by hand: System Settings → Privacy & Security → Input Monitoring → + → pick InputPilot in Applications, then switch it on.
macOS applies Input Monitoring on the next launch. Quit InputPilot and open it again.
Yes. Keyboards are identified by vendor, product, transport and name, so a Bluetooth keyboard is recognised again after reconnecting.
That is the case it was written for. Map the external board to German and the built-in one to U.S. once. From then on the layout is right the moment you start typing on either — no shortcut, no menu, nothing to remember.
Input Source Pro and similar tools switch per app or per window. autokbisw switches per keyboard from the command line, with no interface. OctoKeys switches per keyboard on the App Store by remembering the last source you used with each one. InputPilot switches per keyboard too, but you assign the input source explicitly rather than relying on what was used last, it is open source so you can audit what it does with the permission, and it is free.
Nothing. Free and open source under the Apache 2.0 licence.
A free, open source keyboard layout switcher for macOS 13 or newer. About a minute to set up.