pqrs-org / Karabiner-Elements

Karabiner-Elements is a powerful utility for keyboard customization on macOS Sierra (10.12) or later.
https://pqrs.org/osx/karabiner/
The Unlicense
18.57k stars 834 forks source link

Feature: Support for dynamic switching of profiles per app #3180

Open Soromeister opened 2 years ago

Soromeister commented 2 years ago

Hi,

Kindly add support for profiles per app. For example, I have 2 profiles set in KE:

It would be nice to have KE switch between these 2 profiles dynamically based on which app is in foreground.

Basically, when I have Microsoft Word in focus, KE should switch the profile to "MS Word". Similarly, when I have iTerm2 in focus, KE should switch to the profile "iTerm2".

andrewtquick commented 1 year ago

+1

AustinCDavis commented 1 month ago

Hi, I don't know if you are still looking for this feature. I just finished making something similar and wanted to share my steps to hopefully help someone else.

WARNING LONG COMMENT

My code functions to do the following:

My sample profile framework is: Generic - "Default" "Test" "Test 2" ThinkorSwim - "ToS - Default" "ToS - Technical Analysis" "ToS - Fundamental Analysis" Microsoft One Note - "MON - Default" "MON - 2" Apple Notes - "Notes - Default"

Step-by-Step Guide

Step 1: Install and Configure Hammerspoon

  1. Download Hammerspoon

  2. Install Hammerspoon:

    • Open the downloaded .dmg file.
    • Drag the Hammerspoon app to your Applications folder.
    • Go to your Applications folder and open Hammerspoon (grant permissions if asked).
  3. Configure Hammerspoon

    • Open Terminal and create the configuration directory:
mkdir -p ~/.hammerspoon
 - Create the configuration file:
touch ~/.hammerspoon/init.lua
  1. Test Hammerspoon
    • Open the Configuration File:
open -e ~/.hammerspoon/init.lua
 - Write a sample script for Hammerspoon
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
    hs.alert.show("Hello World!")
end)
 - Save the file (cmd + s)
 - Reload the configuration by clicking on hammerspoon in the upper right menu bar on your screen and select "reload config"
 - Test Hotkey (cmd+alt+ctrl+W) (should now see "Hello World!" on your screen)

Step 2: Add the Actual Hammerspoon Script Code

  1. Open the init.lua file
open -e ~/.hammerspoon/init.lua
  1. Replace all code with this script for Hammerspoon

NOTE: You may need to change this line of code: (my karabiner_cli location may be different from your) local command = string.format('/Library/Application\ Support/org.pqrs/Karabiner-Elements/bin/karabiner_cli --select-profile "%s"', profile)

[local appProfiles = {
    ["com.azul.zulu.java"] = { "ToS - Default", "ToS - Technical Analysis", "ToS - Fundamental Analysis" },  -- ThinkorSwim
    ["com.microsoft.onenote.mac"] = { "MON - Default", "MON - 2" },  -- Microsoft OneNote
    ["com.apple.Notes"] = { "Notes - Default" },  -- Apple Notes
}

local generalProfiles = { "Default", "Test", "Test 2" }

-- Track the last used profile for each app
local lastUsedProfile = {}
-- Track the last used generic profile
local lastUsedGenericProfile = "Default"
-- Track the current profile to avoid redundant notifications
local currentProfile = ""

-- Function to switch profiles using Karabiner CLI
function switchProfile(profile)
    if profile ~= currentProfile then
        local command = string.format('/Library/Application\\ Support/org.pqrs/Karabiner-Elements/bin/karabiner_cli --select-profile "%s"', profile)
        hs.execute(command)
        -- Add a delay to ensure the profile switch has time to execute before sending the notification
        hs.timer.doAfter(0.1, function()
            hs.notify.new({
                title = "Karabiner Elements",
                informativeText = string.format('"%s" Profile Activated', profile)
            }):send()
        end)
        print("Switched to profile: " .. profile)  -- Debug print statement
        currentProfile = profile
    end
end

-- Switch profiles based on the current application
function switchProfileForApp(bundleID)
    local profiles = appProfiles[bundleID] or generalProfiles
    local profile

    if profiles == generalProfiles then
        profile = lastUsedGenericProfile
    else
        profile = lastUsedProfile[bundleID] or profiles[1]
    end

    if profile then
        switchProfile(profile)
        if profiles == generalProfiles then
            lastUsedGenericProfile = profile
        else
            lastUsedProfile[bundleID] = profile
        end
    end
end

-- Hammerspoon application watcher to handle app switching
hs.application.watcher.new(function(appName, eventType, appObject)
    if eventType == hs.application.watcher.activated then
        local bundleID = appObject:bundleID()
        print("Activated app: " .. bundleID)
        switchProfileForApp(bundleID)
    end
end):start()

-- Cycle through profiles when pressing the "1" key on the mouse
hs.hotkey.bind({}, '1', function()
    local frontmostApp = hs.application.frontmostApplication()
    if frontmostApp then
        local bundleID = frontmostApp:bundleID()
        local profiles = appProfiles[bundleID] or generalProfiles
        local currentProfile = lastUsedProfile[bundleID] or lastUsedGenericProfile
        local index = hs.fnutils.indexOf(profiles, currentProfile)
        if index then
            local nextIndex = (index % #profiles) + 1
            local nextProfile = profiles[nextIndex]
            switchProfile(nextProfile)
            if profiles == generalProfiles then
                lastUsedGenericProfile = nextProfile
            else
                lastUsedProfile[bundleID] = nextProfile
            end
        end
    end
end)](is:issue commenter:your_username)
  1. Save the file (cmd + s)
  2. Reload the configuration by clicking on hammerspoon in the upper right menu bar on your screen and select "reload config"

Step 3: Create JSON Configuration

  1. Navigate to complex modifications folder
cd ~/.config/karabiner/assets/complex_modifications/
  1. Create JSON File
nano app_profile_switcher.json
  1. Add JSON Configuration

{
  "title": "App Profile Switcher",
  "rules": [
{
    "description": "App Profile Switching  using '1' key on mouse keypad that is assigned 'f19'",
    "manipulators": [
        {
            "conditions": [
                {
                    "identifiers": [
                        {
                            "is_keyboard": true,
                            "product_id": 64007,
                            "vendor_id": 9639
                        }
                    ],
                    "type": "device_if"
                }
            ],
            "from": {
                "key_code": "f19",
                "modifiers": {
                    "optional": [
                        "any"
                    ]
                }
            },
            "to_if_alone": [
                {
                    "key_code": "f19"
                }
            ],
            "type": "basic"
        }
    ]
}
  ]
}
  1. Save JSON File (ctrl + o, enter, ctrl + x)

  2. Apply configuration in Karabiner elements to each applicable profile under complex modifications by selecting "add predefined rule" and enabling "Cycle through profiles using the '1' key on the mouse"

Best to you all!