mathiasbynens / dotfiles

:wrench: .files, including ~/.macos — sensible hacker defaults for macOS
https://mths.be/dotfiles
MIT License
30.31k stars 8.73k forks source link

There has to be a way to disable the Caps Lock key programatically #310

Closed kevinSuttle closed 10 years ago

kevinSuttle commented 10 years ago

Right?

kevinSuttle commented 10 years ago

Jackpot? _globalpreferences_c5f654d8-61c6-532d-af3f-ed697f2f7b2b_plist_and__users_kevinsuttle_library_preferences_byhost

kevinSuttle commented 10 years ago

Bam.

defaults read ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist com.apple.keyboard.modifiermapping.1452-610-0 
(
        {
        HIDKeyboardModifierMappingDst = "-1";
        HIDKeyboardModifierMappingSrc = 0;
    }
)
❯ defaults read-type ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist com.apple.keyboard.modifiermapping.1452-610-0
Type is array
kevinSuttle commented 10 years ago

Got it, but it's writing to a string. Not sure of the syntax to write to a number/integer in an array format.

defaults write ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist com.apple.keyboard.modifiermapping.1452-610-0 -array '{"HIDKeyboardModifierMappingDst" = 0; "HIDKeyboardModifierMappingSrc" = -1;}'
pengwynn commented 10 years ago

CAPS is prime home row real estate to waste. I've remapped mine to CTRL+ESC for tmux+vim and haven't looked back. @kevinSuttle seen https://github.com/jasonrudolph/keyboard ?

kevinSuttle commented 10 years ago

@pengwynn Yeah, I know all about it, but my muscle memory just isn't mapped to use the key at all.

I'm just happy that I was finally able to figure out how to do this from the command line. Haven't seen anyone do that yet.

kevinSuttle commented 10 years ago

I can certainly appreciate the time and approach, but I'm just not there yet I don't think. http://stevelosh.com/blog/2012/10/a-modern-space-cadet/

kevinSuttle commented 10 years ago

@mathiasbynens I'm sure you could probably do this with PlistBuddy also.

kevinSuttle commented 10 years ago

Got it.

# Disable the CAPS LOCK key
/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.1452-610-0 array" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add :com.apple.keyboard.modifiermapping.1452-610-0: dict" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.1452-610-0:0:HIDKeyboardModifierMappingDst integer -1" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.1452-610-0:0:HIDKeyboardModifierMappingSrc integer 0" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
kevinSuttle commented 10 years ago

https://github.com/kevinSuttle/OSXDefaults/commit/c36d81fa21d8551645cd9ffbf3a153622b40c10c

mathiasbynens commented 10 years ago

Is the 1452-610-0 the same for all OS X versions / users / environments?

kevinSuttle commented 10 years ago

That's what I'm wondering. Can you backup your file and test, or more safely, just check the value?

oxyc commented 9 years ago

For reference, the value is 1452-626-0 on my 12" macbook (2015). So it does change.

kevinSuttle commented 9 years ago

Who knows?

Tatsh commented 9 years ago

I would think the value is not predictable. Maybe not even model specific really, but it looks like the 1452 portion is always the same. @oxyc how did you find that value for your machine?

I have a few machines I can try: 2013 Mac Pro, Mid-2012 MacBook Pro, Mid-2014 MacBook Pro, unkown-2012 (might be 2013) MacBook Air.

Looking at ~/Library/Preferences/ByHost/.GlobalPreferences/*.plist and I actually have two. I wonder if one if left over from upgrading to Yosemite.

kevinSuttle commented 9 years ago

Seems like you can regex it to 1452-(3 integers)-0. Interesting.

Tatsh commented 9 years ago

I changed the Modifier key for Caps Lock in Settings and did not get a change in the ~/Library/Preferences/ByHost/.GlobalPreferences/*.plist path. There is only one on this machine (a never-upgraded Mac Pro).

The domain/default pair of (/Users/tatsh/Library/Preferences/ByHost/.GlobalPreferences.4598E8F9-CC91-5ACE-980E-03EDA8EAE871.plist, com.apple.keyboard) does not exist
kevinSuttle commented 9 years ago

Did that file exist before you ran the command? Also, did you use the proper data type? https://github.com/kevinSuttle/OSXDefaults/commit/c36d81fa21d8551645cd9ffbf3a153622b40c10c

Tatsh commented 9 years ago

The file did not exist before I changed the setting in Settings.

I did not run the PlistBuddy commands because I was/am assuming that the key will appear if I change the setting via GUI. And because I do not know if that code is the right one for this system. I am trying to find out the correct code (the 1452- identifier).

mathiasbynens commented 9 years ago

I wonder if the same thing can be done using a custom .keylayout file.

oxyc commented 9 years ago

Found a way. Do you want to include it in your repo @mathiasbynens? I can send a PR

VENDORID="$(ioreg -n IOHIDKeyboard -r | awk '$2 == "\"VendorID\"" { print $4 }')"
PRODUCTID="$(ioreg -n IOHIDKeyboard -r | awk '$2 == "\"ProductID\"" { print $4 }')"

/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${VENDORID}-${PRODUCTID}-0 array" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add :com.apple.keyboard.modifiermapping.${VENDORID}-${PRODUCTID}-0: dict" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${VENDORID}-${PRODUCTID}-0:0:HIDKeyboardModifierMappingDst integer -1" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
/usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${VENDORID}-${PRODUCTID}-0:0:HIDKeyboardModifierMappingSrc integer 0" ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist
Tatsh commented 9 years ago

So those two values are vendor ID product ID. Good to know.

@oxyc Maybe delete entries before adding new ones? This is just in case the old entries are not necessary anymore.

Also I do not see a case where you handle if two keyboards appear under IOHIDKeyboard with ioreg. This could easily be the case where someone uses an external USB or Bluetooth keyboard with an external monitor on a MacBook, and therefore there are now two. I am assuming in that case your ioreg filtering would get back two or more lines instead of one.

Tatsh commented 9 years ago

In my .osx I am hard-coding to keyboards I have already instead of doing a dynamic check. Das keyboard model S is 1241-8211.

oxyc commented 9 years ago

Yes, as I don't use multiple keyboards I left it as this. @mathiasbynens hasn't really expressed interest so I thought to ask before making a more robust version.

Tatsh commented 9 years ago

Also just to note, I am pretty sure that upgrades or updates in general could cause a new plist to be generated in that path. Even on my Mac Pro that came with Yosemite I have two files there. If you just use the expansion ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist then you have two or more and PlistBuddy complains.

This is what I have due to the aforementioned. I know it probably does not matter to || continue on failure of the first attempt to write (and possibly this could mean other things), but I do not think the case of having an empty array will be very common. Could || continue on each line (but maybe not the lines that change the values).

###############################################################################
# Disable caps lock                                                           #
###############################################################################
# Delete old entries first
# Normally these do not have an entry for com.apple.keyboard.modifiermapping.*
# Sometimes there is more than one (maybe caused by upgrade?)
old_ifs="$IFS"
export IFS=$'\n'
for plist in ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist; do
    keys=$(/usr/libexec/PlistBuddy -c 'Print' "$plist" | egrep 'com\.apple\.keyboard\.modifiermapping.[0-9]' | awk '{ print $1 }')
    for key in "$keys"; do
        /usr/libexec/PlistBuddy -c "Delete ${key}" "$plist" >/dev/null 2>&1
    done
done
export IFS="$old_ifs"

# Keyboards, VID-PID; get from ioreg -n IOHIDKeyboard -r (USB only?)
das="1241-8211"
mbp2012="1452-610"

for pair in $das $mbp2012; do
    for plist in ~/Library/Preferences/ByHost/.GlobalPreferences.*.plist; do
        # If first one fails, then the entry is assumed to exist
        /usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${pair}-0 array" "$plist" >/dev/null 2>&1 || continue
        /usr/libexec/PlistBuddy -c "Add :com.apple.keyboard.modifiermapping.${pair}-0: dict" "$plist"
        /usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${pair}-0:0:HIDKeyboardModifierMappingDst integer -1" "$plist"
        /usr/libexec/PlistBuddy -c "Add com.apple.keyboard.modifiermapping.${pair}-0:0:HIDKeyboardModifierMappingSrc integer 0" "$plist"
    done
done
unset das mbp2012
martinlindhe commented 7 years ago

Hi, found this while attempting the same. Sorry for resurrecting an old issue, but this method using PlistBuddy no longer works on macOS 10.10+ because they started caching the plist preferences. the "defaults" command has been updated to work with the new caching system, but the PlistBuddy tool sadly, has not. However as mentioned earlier in this issue, it does not seem to be possible to instruct defaults into creating the proper structure. One last resort is using a python script, that speaks with the proprer API's, looks like this:

not updated to solve this particular issue, just a copy&paste:

#!/usr/bin/python
import CoreFoundation 

ManagedPlugInPolicies = {

# Always Allow Sharepoint plugin
    "com.microsoft.sharepoint.browserplugin": { 
        "PlugInFirstVisitPolicy": "PlugInPolicyAllowNoSecurityRestrictions", 
    }} 

CoreFoundation.CFPreferencesSetAppValue("ManagedPlugInPolicies", ManagedPlugInPolicies,  "com.apple.Safari") 
CoreFoundation.CFPreferencesAppSynchronize("com.apple.Safari")

Source: https://macmule.com/2014/02/07/mavericks-preference-caching/

I also investigated using AppleScript to reconfigure the settings, but doing so in an automated manner blocks you at execution error: System Events got an error: osascript is not allowed assistive access. which is even more hazzle to configure than just disabling caps in the settings. One can come around this by manipulating sqlite database, but the table needed was recently changed (a column added) so it is not a "stable" solution either. Also you must add access for the terminal app you are invoking the osascript command from. For reference, using iTerm:

find app bundle id:

/usr/libexec/PlistBuddy -c 'Print CFBundleIdentifier' /Applications/iTerm.app/Contents/Info.plist
com.googlecode.iterm2

give it access:

sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES('kTCCServiceAccessibility','com.googlecode.iterm2',0,1,1,NULL,0);" 

I am now dusting of my python since it seems to be the last possible solution.

martinlindhe commented 7 years ago

Update: I ragequit. While the python solution worked and created a integer 0, the -1 in this snippet turned into a string. Same issue as with 'defaults' command.

Here is what I came up with, if someone wants to take this deeper. Use system python interpreter to find CoreFoundation

/usr/bin/python -c '

import CoreFoundation

POL = [{
    "HIDKeyboardModifierMappingSrc": 0,
    "HIDKeyboardModifierMappingDst": -1,
}]

CoreFoundation.CFPreferencesSetAppValue("modifiermapping.1452-567-0", POL, "com.apple.keyboard")
CoreFoundation.CFPreferencesAppSynchronize("com.apple.keyboard")
'

and here is an osascript:

osascript -e '
tell application "System Preferences"
    reveal anchor "keyboardTab" of pane "com.apple.preference.keyboard"
end tell    
tell application "System Events" to tell window 1 of process "System Preferences"
    click button "Modifier Keys…" of tab group 1
    tell sheet 1
        tell pop up button 1
            click
            click menu item "No Action" of menu 1
        end tell
        click button "OK"
    end tell
end tell    
quit application "System Preferences"
'
sj26 commented 7 years ago

defaults with plist syntax won't respect the integer type, it will only save values as strings. But you can use the XML syntax. Here's what I'm using now:

VENDORID="$(ioreg -n IOHIDKeyboard -r | awk '$2 == "\"VendorID\"" { print $4 }')"
PRODUCTID="$(ioreg -n IOHIDKeyboard -r | awk '$2 == "\"ProductID\"" { print $4 }')"
defaults -currentHost write -globalDomain \
  "com.apple.keyboard.modifiermapping.${VENDORID}-${PRODUCTID}-0" \
  "<array><dict><key>HIDKeyboardModifierMappingSrc</key><integer>0</integer><key>HIDKeyboardModifierMappingDst</key><integer>-1</integer></dict></array>"
kevinSuttle commented 7 years ago

Sierra has changed this, and you can now change the behavior of ESC in system preferences, so it should be in there somewhere.