Closed hirakujira closed 6 years ago
this is helpful - THANKS
This is great, thanks!
That said, Karabiner had an option to not remap Apple keyboards without the need to run a script. It would be great to have this as an option in Karabiner-Elements.
Karabiner Elements monitors the JSON config file for changes so it's reliable to just swap the JSON between alternate configurations rather than killing the process. This also allows you to have some keys redefined for Mac with different keys defined for PC.
Below is a script I wrote to quickly switch between a Mac and PC keyboard Karabiner Elements JSON file. It assumes you have the following files in ~/.karabiner.d/configuration
:
mackeyboard.json pckeyboard.json
The script will toggle the two JSON files for you. It has zero error handling at the moment but should be reliable for most use cases.
You can run with -h
for help.
#!/usr/bin/env bash
declare -i verboseMode=1
usage="Usage: $0 [-h|q|v]\n
-h\tHelp
-q\tQuiet mode
-v\tVerbose mode"
while getopts "hqv" opt;
do case ${opt} in
h ) echo -e ${usage}
exit 1;;
q ) verboseMode=0;;
v ) verboseMode=1;;
esac
done
shift $(($OPTIND - 1))
if [[ -f ~/.karabiner.d/configuration/.pckeyboard.json ]]; then # switch to Mac
command cp -f ~/.karabiner.d/configuration/mackeyboard.json ~/.karabiner.d/configuration/karabiner.json
command mv -f ~/.karabiner.d/configuration/.pckeyboard.json ~/.karabiner.d/configuration/pckeyboard.json
command mv -f ~/.karabiner.d/configuration/mackeyboard.json ~/.karabiner.d/configuration/.mackeyboard.json
if [[ -n verboseMode ]]; then echo "Mac keyboard active in Karabiner Elements!"; fi
elif [[ -f ~/.karabiner.d/configuration/.mackeyboard.json ]]; then # switch to PC
command cp -f ~/.karabiner.d/configuration/pckeyboard.json ~/.karabiner.d/configuration/karabiner.json
command mv -f ~/.karabiner.d/configuration/pckeyboard.json ~/.karabiner.d/configuration/.pckeyboard.json
command mv -f ~/.karabiner.d/configuration/.mackeyboard.json ~/.karabiner.d/configuration/mackeyboard.json
if [[ -n verboseMode ]]; then echo "PC keyboard active in Karabiner Elements!"; fi
else # unknown state - default to Mac
command cp -f ~/.karabiner.d/configuration/mackeyboard.json ~/.karabiner.d/configuration/karabiner.json
command mv -f ~/.karabiner.d/configuration/mackeyboard.json ~/.karabiner.d/configuration/.mackeyboard.json
echo "Current state unknown - defaulting to Mac keyboard active in Karabiner Elements!"
fi
Here's an Alfred workflow that cycles through multiple profiles defined in a single karabiner.json file:
https://gist.github.com/zxaos/59cd3e967c72f576738997e46ddacb74
So you could set up karabiner.json like:
{"profiles": [
{
"selected": true,
"simple_modifications": {
"caps_lock": "f18",
"escape": "caps_lock"
},
"name": "internal"
},
{
"selected": false,
"simple_modifications": {
"left_option": "left_command",
"right_command": "right_option",
},
"name": "external"
}]}
Then you can simply toggle the "selected" key to switch which one is active. The benefit here is that you aren't limited to only two profiles, you can just keep adding them and cycling through as necessary.
Or, if you prefer the underlying python script:
from os import path
import sys
import json
targetfile = path.expanduser('~/.karabiner.d/configuration/karabiner.json')
with open(targetfile) as settings_file:
settings = json.load(settings_file)
active_index = next(index for (index, profile) in enumerate(settings['profiles']) if profile["selected"] == True)
# disable current active profile
settings['profiles'][active_index]['selected'] = False;
# set new active profile
next_profile = (active_index + 1) % len(settings['profiles'])
settings['profiles'][next_profile]['selected'] = True;
with open(targetfile, 'w') as settings_file:
json.dump(settings, settings_file, indent=4, separators=(',', ': '))
sys.stdout.write(settings['profiles'][next_profile]['name'])
I've created a simple Mac app that enables Karabiner Elements when Apple Thunderbolt display is connected (my keyboard is hooked to it) : https://github.com/AndrianBdn/KarabinerElementsToggler
@AndrianBdn: Thanks for the nice idea of looking for a network adapter in order to determine which profile to use. I used this idea for creating a shell script that basically does the same job:
#!/bin/bash
karabiner_dir="$HOME/.karabiner.d/configuration"
karabiner_conf="$karabiner_dir/karabiner.json"
karabiner_conf_enabled="$karabiner_conf.enabled"
karabiner_conf_disabled="$karabiner_conf.disabled"
mac_address="02:01:02:03:04:05"
files_match() {
[ `md5 -q "$1"` = `md5 -q "$2"` ]
}
while true; do
if ifconfig | grep -qi "ether $mac_address"; then
if ! files_match "$karabiner_conf_enabled" "$karabiner_conf"; then
cp "$karabiner_conf_enabled" "$karabiner_conf"
osascript -e "display notification \"Karabiner has been enabled\" with title \"Karabiner Elements\"" 2>/dev/null
fi
else
if ! files_match "$karabiner_conf_disabled" "$karabiner_conf"; then
cp "$karabiner_conf_disabled" "$karabiner_conf"
osascript -e "display notification \"Karabiner has been disabled\" with title \"Karabiner Elements\"" 2>/dev/null
fi
fi
sleep 1
done
When this script is saved as toggle-karabiner.sh
it can be started in the background by running
./toggle-karabiner.sh &
disown %1
One could adapt this code to look for a certain USB keyboard being connected by parsing the output of ioreg -p IOUSB -l -w 0
, but this would be a bit more complicated because the vendor ID and the product ID are not on the same line.
@smarsching Thanks for your bash script!
I changed it a bit to:
I think the productID is not very possible to conflict if you use external keyboard that manufactured by 3rd party. So it's not perfect, but enough for me now.
#!/bin/bash karabiner_dir="$HOME/.karabiner.d/configuration" karabiner_conf="$karabiner_dir/karabiner.json" productID='610' #productID should be conveted to decimal function conf_enabled() { if grep -q 'true' "$karabiner_conf"; then return 1 else return 0 fi } while true; do if ioreg -p IOUSB -l -w 0 | grep 'idProduct' | grep -qi $productID; then if conf_enabled; then sed -i '' 's/false/true/g' "$karabiner_conf" osascript -e "display notification \"Karabiner has been enabled\" with title \"Karabiner Elements\"" 2>/dev/null fi else if ! conf_enabled; then sed -i '' 's/true/false/g' "$karabiner_conf" osascript -e "display notification \"Karabiner has been disabled\" with title \"Karabiner Elements\"" 2>/dev/null fi fi sleep 5 done
Thanks for the script ideas! Here's an improvement to the check for productId
:
if ioreg -p IOUSB -l -w 0 | grep -qi "\"idProduct\" = $productId$"; then
This eliminates the extra grep invocation and also ensures that the entire productId matches to the end of the line. Previously the script would match a subset of the ID string.
FYI, there seems to be progress on adding support for matching certain devices directly into Karabiner Elements. The code for parsing the corresponding configuration structures is already there, and there also seems to be some work on using this information at runtime (e.g. 3db15dc549ca04ffad1760244fb89824c782d039 added some code for this). Of course I do not know how complete this code is and when it will be usable, but it looks like things are progressing quickly.
As of version 0.90.57 devices can be ignored by specifying them in the configuration file (can be done via the preferences GUI).
This is not just simpler than any of the workarounds discussed in this issue, but also has the advantage that it will actually work while the device for which the remappings should be active is connected.
As this issue seems to be resolved now, I think it should be closed.
Doesn't resolve all of the use cases.
I don't want to disable all of the remapping when using a specific device, I want to use different ones. Turning them all off doesn't help me at all, I still need to use the switching scripts.
Nice workarounds all you mention here, but in the future I would like to have a third select box in the ui to select in which device should be applied each modification (ie all, internal, usb). I imagine this is a big change but it would be awesome. PS: thanks for all your work.
Just checking in on this, like @smarsching said there is 3db15dc which looks like it might have some device specific options.
I tried @zxaos 's workflow but it didn't work for. I also noticed there is a devices array in the config file now as shown below:
{
"profiles": [
{
"devices": [
{
"identifiers": {
"is_keyboard": true,
"is_pointing_device": false,
"product_id": 1957,
"vendor_id": 1118
},
"ignore": false,
"keyboard_type": 0
},
{
"identifiers": {
"is_keyboard": true,
"is_pointing_device": false,
"product_id": 601,
"vendor_id": 1452
},
"ignore": false,
"keyboard_type": 0
}
],
"fn_function_keys": {
"f1": "vk_consumer_brightness_down",
"f10": "mute",
"f11": "volume_down",
"f12": "volume_up",
"f2": "vk_consumer_brightness_up",
"f3": "vk_mission_control",
"f4": "vk_launchpad",
"f5": "vk_consumer_illumination_down",
"f6": "vk_consumer_illumination_up",
"f7": "vk_consumer_previous",
"f8": "vk_consumer_play",
"f9": "vk_consumer_next"
},
"name": "Default profile",
"selected": true,
"simple_modifications": {
"caps_lock": "left_control"
}
}
]
}
I tried putting some settings inside the array, but it didn't work:
{
"profiles": [
{
"devices": [
{
"identifiers": {
"is_keyboard": true,
"is_pointing_device": false,
"product_id": 1957,
"vendor_id": 1118
},
"ignore": false,
"keyboard_type": 0,
"name": "Microsoft keyboard",
"selected": true,
"simple_modifications": {
"caps_lock": "left_control",
"left_command": "left_option"
}
},
{
"identifiers": {
"is_keyboard": true,
"is_pointing_device": false,
"product_id": 601,
"vendor_id": 1452
},
"ignore": false,
"keyboard_type": 0
}
],
.
.
.
@zxaos is there anything you recommend I try to figure out why the workflow isn't working for me?
@mbigras The script should toggle between objects in the "profiles" array - for your first configuration there only has the one profile, and your second configuration is missing a "selected": false
key for the script to toggle.
There's an example of an updated configuration with device stanzas below. Make sure each profile has a selected
key and a name
key.
For debugging, you could also download the python script directly and run it from the command line so you can more easily step through it, but try just adding the missing keys first 😄
{
"profiles": [
{
"devices": [
{
"identifiers": {
"is_keyboard": true,
"is_pointing_device": false,
"product_id": 627,
"vendor_id": 1452
},
"ignore": false,
"keyboard_type": 0
}
],
"fn_function_keys": {
"f1": "vk_consumer_brightness_down",
"f10": "mute",
"f11": "volume_down",
"f12": "volume_up",
"f2": "vk_consumer_brightness_up",
"f3": "vk_mission_control",
"f4": "vk_launchpad",
"f5": "vk_consumer_illumination_down",
"f6": "vk_consumer_illumination_up",
"f7": "vk_consumer_previous",
"f8": "vk_consumer_play",
"f9": "vk_consumer_next"
},
"name": "internal",
"selected": true,
"simple_modifications": {
"caps_lock": "f18",
"escape": "caps_lock"
}
},
{
"fn_function_keys": {
"f1": "vk_consumer_brightness_down",
"f10": "mute",
"f11": "volume_down",
"f12": "volume_up",
"f2": "vk_consumer_brightness_up",
"f3": "vk_mission_control",
"f4": "vk_launchpad",
"f5": "vk_consumer_illumination_down",
"f6": "vk_consumer_illumination_up",
"f7": "vk_consumer_previous",
"f8": "vk_consumer_play",
"f9": "vk_consumer_next"
},
"name": "external",
"selected": false,
"simple_modifications": {
"caps_lock": "f18",
"escape": "caps_lock",
"left_command": "left_option",
"left_option": "left_command",
"right_command": "right_option",
"right_option": "right_command"
}
}
]
}
Thank you @zxaos! working keymap is here https://gist.github.com/mbigras/6661dd08e6ac0f7c4556ce5a72355bfb
Thanks to all here for a good workaround! I think I have a clever addition to it that I'd like to share...
I'm using Python script by @zxaos pretty much as-is. But instead of running it manually, I have added it as a shortcut using BetterTouchTool. It has a nice new feature of being able to bind to "Key Sequence" which I'm using to map a triple-keystroke of my most often mistaken key.
Here is the full use case: I strike a key while on the wrong profile. I get angry 😠But I can immediately do a rather satisfying triple keystroke on that same key - that triggers BetterTouchTool to execute the Python script which in turn switches the profile and I can continue typing. As an added bonus, I can set BetterTouchTool to also send a backspace to delete the offending character, and also to type the desired character in its place.
Granted, In my case I only need to swap one pair of keys between devices, so your mileage may vary if that is not the case for you.
Another thing: BetterTouchTool can't directly execute a script, but it can run Apple Script, and it is fairly simple to execute scripts from there (plus show a notification, totally optional):
set activeProfile to do shell script "python ~/.karabiner_switch.py"
display notification "Activated: " & activeProfile
And the beauty of it all is that once the desired profile has been activated in this way, repeating the angry triple keystroke on the same key will not trigger any more profile changes (i.e. won't switch back to the wrong profile). Unless on another device, of course, which is good.
BTT config:
I found that KE 0.90.75 only updates the profile when the ~/.config/karabiner/karabiner.json
file is changed; not the ~/.karabiner.d/configuration/karabiner.json
file.
@cwsmith Yeah, that's mentioned in the changelog that pops up when you upgrade! 😄
You'll need to update the paths in any scripts you use.
Hey guys, I am regularly using an Apple keyboard and a standard USB one, oftenly switching. As I struggled to switch between the two layouts, I've created an Alfred Workflow to help with this.
I just saw @zxaos answer with an Alfred Workflow as well, but I've already created my own so I guess I can share it. The difference is that mine doesn't simply cycle through your profiles, but gives you a full list in a visually appealing way and then you can choose one you want to switch to.
So if you're using Alfred, have a look at Karabiner Elements Profile Switcher, or download directly from Packal directory.
Hope this helps some of you out there. Here's a screenshot:
I have got a nice workaround i think:
(Assume i have two profile: Debug & Default )
Write a script (eg. named Debug-profile.sh):
gsed -i '/"name": "Debug"/!b;n;c"selected": true,' ~/.config/karabiner/karabiner.json; gsed -i '/"name": "Default"/!b;n;c"selected": false,' ~/.config/karabiner/karabiner.json
and a script (eg. named Default-profile.sh):
gsed -i '/"name": "Debug"/!b;n;c"selected": false,' ~/.config/karabiner/karabiner.json; gsed -i '/"name": "Default"/!b;n;c"selected": true,' ~/.config/karabiner/karabiner.json
(if you are using linux, just use sed, if your are using mac, just install gsed(gun-sed) via brew install gsed
and if you want switch to one profile ,just run the debug-profile.sh or default-profile.sh, you can surely add them in your .bashrc and make alias.
I have made some enhancements to KBE to remove the pain of switching profiles for 2+ keyboards by supporting per-device configuration. Please take a look at https://github.com/starsy/Karabiner-Elements if you are interested in. The release/download link is here: https://github.com/starsy/Karabiner-Elements/releases/tag/v1.0_merged . Pull request is here: https://github.com/tekezo/Karabiner-Elements/pull/752. It works pretty stably on my MBPR, I think it worths a try. Hope it can solve your problems too. Please don't hesitate to let me know if any issues or questions.
@starsy I tried to use your fork but the dropdowns aren't populated
@jlippold maybe you were using the old release, please try the latest one and let me know: https://github.com/starsy/Karabiner-Elements/releases/tag/v1.1
I just wanna say: for me the original Python script by @zxaos works with current Karabiner elements on a new Mac:
@starsy This is exactly what I need, however I require the complex modifications to enable me to remap caps lock to a hyper key modifier (all modifier keys).
My specific use case is that I need to remap keys on an external keyboard, remap caps lock on the MBP keyboard, and most importantly prevent the external remap from affecting the MBP keyboard.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This is not a issue... just want to share this workaround but the Wiki is not available yet.
For me, I have 2 keyboards: Apple Internal and a PC keyboard. So I need to switch
Option
andCommand
when I use the PC keyboard, but not in the internal keyboard.Since Karabiner-Elements haven't support to set profile for specific devices yet, the workaround is swapping the profile automatically. :)
So, use any text editor, and modify the following shell script, then save as
karabinerOff.command
Then save the following script as
karabinerOn.command
So you can click the .command file to move the profile and restart Karabiner daemon automatically. For me, I use TextExpander, which supports shell script, so I just set the commands as a snippet, which is much easier.