Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Using an arbitrary key as a modifier, without sacrificing it


  • Please log in to reply
7 replies to this topic
lilleyt
  • Members
  • 16 posts
  • Last active: Jun 23 2010 05:33 PM
  • Joined: 24 Aug 2008
For a long time I've been trying to map an arbitrary key to be a modifier so I can switch my keyboard to (slightly-modified) vim-like controls. I've wanted the functionality to be enabled while I hold the key, like a regular modifier key which is different than vim's modal approach. And of course, I want to be able to use it anywhere in Windows, not just in vim.

While my mapping isn't final, I wanted to share the technique, since it works better than any other I've tried, and I've tried a bunch. Can't tell you how many times I've looked through the forums and came close but been unsatisfied in one way or another.

I'm using the space bar as my modifier, which has its advantages and disadvantages. The point of the technique isn't the key you choose as the modifier, however, but how it behaves.

Here's what I wanted:


[*:ww137etl] Works for (just about) any key
[*:ww137etl] If the key is pressed without modifying another key, it sends the regular keystroke
[*:ww137etl] If the key modifies another key, the regular keystroke is not sent
[*:ww137etl] The modifier doesn't interfere with any other modifiers also pressed
[*:ww137etl] Reliable and stable
[*:ww137etl] The key remapping syntax can be used to define the effect of the modifier
This last one was a tough one to get, because many of the other techniques rely on conditionals within each hotkey, which ends up being a ton of code.

I picked up on the Autohotkey_L #If directive used in some of the other posts for doing this. I also relied on some of the techniques mentioned in the hotkey page of the documentation.

Here's a sample that uses the space bar to turn i, j, k and l into arrow keys:

;
; AutoHotkey Version: 1.x
; Language:       English
; Platform:       Win9x/NT
; Author:         A.N.Other <[email protected]>
;
; Script Function:
;	Template script (you can customize this template by editing "ShellNew\Template.ahk" in your Windows folder)
;

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

Space & F1::Return ; Mentioned in the hotkeys docs for UP
*Space::Send {Blind}{Space} ; Send it explicitly when no other key is pressed before letting go, including any modifiers being held

#If GetKeyState("Space", "p") ; Autohotkey_L directive for enabling following mappings when key is physically down

j::Left
l::Right
i::Up
k::Down

#If

This seems to work for me better than other solutions I've tried, and has the benefit of being short and sweet. Hopefully it's useful to someone else as well.

To substitute any other key as the modifier, just choose that key and put it where the four references to space are instead.

The drawback to this method (or any other I've seen) is that you lose the autorepeat function for the key in question. This is a sacrifice I was readily willing to make.

For space in particular, that might be an issue for some, but it's mitigated for me by the fact that I use programming editors that allow using multiple spaces when you hit tab, so I don't need the ability to hold down the space bar to generate lots of spaces. YMMV.

Ted

  • Guests
  • Last active:
  • Joined: --
If you use
Space & j::Left
you don't loose the repeat keystroke afaik and you can skip the #IF as well :?:

lilleyt
  • Members
  • 16 posts
  • Last active: Jun 23 2010 05:33 PM
  • Joined: 24 Aug 2008
I had tried something like that before, and the big drawback was that your mapping only deals with sending Left when you hit j, but not sending Shift-Left when hitting Shift-j.

The *key::Send {Blind}key syntax may handle all of the modified versions of the keys, but the documentation for Blind sounds a bit mushy, and I don't really trust it to accomplish the job. The reason I don't trust it is because I've read how remapping works, and it relies on * and Blind, but it also has more to it than just that.

Since I'm looking precisely for that remapping effect, it seems wiser to me to use a method which allows me to take advantage of all of the behind-the-scenes work that is baked into the remapping syntax rather than trying to recreate it myself, just so I can avoid using #If GetKeyState.

The final script is also more readable as it stands, since the key mapping syntax is so compact and you don't need to preface every line with "Space &". Concern over form takes a back seat to functionality of course, but fortunately the functionality of this syntax seems completely acceptable so far.

It would be interesting to see how the two syntaxes compare in actuality though. If I have time, I'll give your method a try.

  • Guests
  • Last active:
  • Joined: --
~Space & j::

       If GetKeyState("Shift") = 0

               Send {Left}

       Else

               Send +{Left}

Return
:?:

lilleyt
  • Members
  • 16 posts
  • Last active: Jun 23 2010 05:33 PM
  • Joined: 24 Aug 2008
I was just using Shift as an example. The requirement in the list is to handle *all* modifier keys as well as all modifier combinations. This quickly becomes intractable in that syntax.

  • Guests
  • Last active:
  • Joined: --
If you say so. I disagree, why should you care how succinct the script is? But it's your party :D

  • Guests
  • Last active:
  • Joined: --
Well, it's an interesting calculation.

Let's take Shift, Ctrl and Alt.

I want the key by itself: 1
Now I want each of the modifiers plus the key: that's 3
Plus I want each combination of two modifiers: that's 3
Plus I want each combination of three modifiers: that's 1

So I have to sit down and write 7 conditional cases for each key I want to remap, which takes at the least 14 lines. If I remap half of the keyboard, that's 91 lines. Versus 13 lines my way.

And I get nothing for the 78 extra lines. Now why would I want to do that?

Tahir Hassan
  • Members
  • 17 posts
  • Last active: Dec 17 2012 02:58 PM
  • Joined: 17 Nov 2007

I am all for succinctness as managing a larger script is harder icon_confused.gif

 

I wrote the following method which handles all of that:

 

SendInput_Preserve_Modifiers(input)
{
    SendInputString = %input%
    if (GetKeyState("Shift") = 1)
    {
        SendInputString = {Shift Down}%SendInputString%{Shift Up}
    }
    
    if (GetKeyState("Control") = 1)
    {
        SendInputString = {Control Down}%SendInputString%{Control Up}
    }
    
    if (GetKeyState("Alt") = 1)
    {
        SendInputString = {Alt Down}%SendInputString%
    }

    SendInput, %SendInputString%
}

 

Using my code, the example provided by "Guests" would become:

 

~Space & j::
    SendInput_Preserve_Modifiers("{Left}")
Return