Jump to content

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

110.32 Chording Keyboard Input 1.1.4 Send/Run/GoSub


  • Please log in to reply
16 replies to this topic
Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
background/related:
Type with the numpad! by jonny
Chording keyboard: strings sent at key combinations by Laszlo

chording/chorded input: input using simultaneous press of 2 or more keys.

Although i had already used the chording method for my Configurable One-Hand Typing script, i didn't realize the advantage that chording has over hotstrings, until it struck me during reading the above discussion threads. The main advantage is this: chording frees up the key-sequence to be typed normally, which is not possible with hotstrings which auto-replace whether or not the keys are held pressed. Chording is actually a sort of combination between hotkeys and hotstrings, resembling hotstrings by auto-replace (although it could be extended beyond this to perform commands other than Send, as well as use non-alphanumeric keys) and resembling hotkeys by the method of simultaneous-press of keys. The difference is almost cosmetic in that the prefix of hotkeys is usually a traditional modifier-key (Shift/Alt/Ctrl/Win), while in chording there isn't this restriction.

AutoHotkey already allows chording of any two keys, but the ability to maintain the native function becomes tricky and is not immediate to achieve (challenging actually, with the various parameter considerations). And a generalized solution for this has remained elusive, until now.

Usage

^#k toggle chording on/off (starts out Off)
^#k if chording is on, holding the hotkey ~1 sec shows the Chord List

It is easy to configure, with 3 settings:
ch_th = chord threshold (millisec), time to allow chords as normal input; hold the keys pressed this long to transform the chord.
ch_ini = filePathName of the .ini file.
ch_delimiter = the separator used between the chord and its expansion in the settings file. Do not use it within the expanded string.
These may be changed, directly below the hotkey label.

The .ini file is recognized as Chord settings.ini (which you may change) and should be done in the following fashion, by default:
[preferences]

total = 20

1 = yt.Yours truly,
2 = oh.on the other hand
3 = iet.in the event that
4 = wo.without
5 = tpi.the patient is
6 = tpw.the patient was
7 = et.exceptional typing speed
8 = YT.yuletide
9 = OH.ornaments and heart-warming gatherings
10 = IET.It's an excellent time of year
11 = 135.153
12 = 123.1234567890
13 = a1.abcdefghijklmnopqrstuvwxyz0123456789
14 = ahk.AutoHotkey is an excellent program
15 = mcl.my chord list
16 = xc.extend chords to actions other than Send
17 = ct.Christmas time
18 = ny.New Year's
19 = mc.Merry Christmas
20 = ty.thank You
This is just for example. The total should be accurately hard-coded, and there is no limit to the number of entries (i've not yet tested for the practical-speed limit-range). A period is used as the delimiter (this can be changed using the ch_delimiter setting, below the hotkey label). Place the .ini file in the same folder as the script.


;------------------------------------------------
; Chording Keyboard Input 1.1.4
; Copyright (c) 2005 by Decarlo L.


SetKeyDelay 0
return

; this is necessary or the Keywait function does not execute properly (perhaps a function-loading bug. Win2000)
*#!x::
;Thread, Priority, 2147483647
msgbox,,, exiting Ahk, .5
EXITAPP


^#k::              ; change to preference

ch_th = 100	; threshold/window of time (millisec) to allow chords as normal input
; keep the keys pressed for this long to transform the chord
ch_ini = %A_ScriptDir%\Chord settings.ini	; filePathName of .ini file
ch_delimiter = .     ; separates chord abbreviation from expansion in settings file.  do not use it within the expanded string.  change to preference

ch_k := StringR(A_ThisHotkey, 1)
if chording_On
{
	Loop 14		; if hotkey is held for ~1 sec, show the chord list
	{
		sleep 30
		if !GetKeyState(ch_k, "p")
			BREAK
		if A_Index = 14
		{
			Gui, 30: Destroy
			Gui, 30: +AlwaysOnTop +VScroll
			Gui, 30: Font, 0x183CE7		; blue
			Gui, 30: Color, 0xD6D3D6  	; light grey
			Gui, 30: Add, Text, ReadOnly, %ch_index%
			Gui, 30: Show, AutoSize, Chord List
			Keywait % ch_k
			RETURN
		}
	}
	Process, Priority,, Normal   ; optional, normal priority when chording is off
	Keywait, %ch_k%
	SetTimer, ChordInfo, off
	SplashText("chording off")
	Tooltip,,,,17
}
else
{
	Process, Priority,, High   ; increase responsiveness during higher cpu load
	ch_str =
	ch_index =
	StringCaseSense on
	IniRead ch_#chords, %ch_ini%, preferences, total
	Loop % ch_#chords
		IniRead ch_%A_Index%, %ch_ini%, preferences, %A_Index%
	Loop % ch_#chords
	{
		A_Index1 := A_Index
		Loop Parse, ch_%A_Index%, %ch_delimiter%
		{
			if A_Index = 1
				ch_%A_Index1% = %A_LoopField%
			else
			{
				ch_%A_Index1%_ = %A_LoopField%
				ch_index := ch_index . ch_%A_Index1% . "`t" . ch_%A_Index1%_ . "`r`n"
			}
		}
	}
	SplashText("chording ON", 1000)
	SetTimer, Chord, 1
	;SetTimer, ChordInfo, 200
}
chording_On := !chording_On
return


SplashText(title="", timeout="", width="", height="", text="")   ; default timeout 700
{
	SplashTextOn, %Width%, %Height%, %Title%, %Text%
	if timeout =
		sleep 700
	else if timeout = 0	; leave on until replaced or turned off later
		RETURN
	else
		sleep %timeout%
	SplashTextOff
	return
}

StringR(in, charsToKeep, charsToTrim="", trimTrailingWhitespace="")
{
	StringRight, out, in, %charsToKeep%
	if charsToTrim
		StringTrimRight, out, out, %charsToTrim%
	return out
}

In( a, b, eachChar =0 )   ; If a in b list.	For b, use varName or "var1 [, var2, ...]"
{
	if eachChar
	{
		Loop, Parse, b
			if a = %A_LoopField%
				Return 1
	}
	else
	Loop, Parse, b, `,, %A_Space%%A_Tab%
		if a = %A_LoopField%
			Return 1
}

GetKS(in, eachChar =0)
{
	global ch_str

	if eachChar
	{
		Loop, Parse, in
			if not GetKeyState(A_LoopField, "p")
				RETURN
		return 1
	}
	else
	{
		Loop, Parse, in, `,, %A_Space%%A_Tab%
			if not GetKeyState(A_LoopField, "p")
				RETURN
		return 1
	}
}

KeyWait(str, hotkey =0)		; default is string, not hotkey label
; this accepts strings and hotkey labels.  do not use commas; spaces/tabs optional
{
	if hotkey	; parse hotkey label
	{
		StringReplace, str, str, ^, Ctrl`,
		StringReplace, str, str, #, LWin`,RWin`,
		StringReplace, str, str, !, Alt`,
		StringReplace, str, str, +, Shift`,
		StringReplace, str, str, %A_Space%&%A_Space%, `,
		if StringR(str, 2) = ",,"
		{
			StringTrimRight, str, str, 1
			str := str . "`,"
		}
		Loop, Parse, str, `,, %A_Space%%A_Tab%
			Keywait % A_LoopField
	}
	else
		Loop, Parse, str,, %A_Space%%A_Tab%
			Keywait % A_LoopField
}


Chord:
SetKeyDelay 0
StringCaseSense on
keys = abcdefghijklmnopqrstuvwxyz0123456789`,./;'[]\-=``
keys1 = %keys%
keys2 = ABCDEFGHIJKLMNOPQRSTUVWXYZ)!@#$`%^&*(<>?:"{}|_+~
Loop
{
	sleep 10
	if !chording_On
	{
		SetTimer, Chord, off
		Keywait %ch_k%
		EXIT
	}
	if GetKeyState("Space", "p") OR GetKeyState("Enter", "p")
	 OR GetKeyState("bs", "p") OR GetKeyState("Esc", "p")
	{
		ch_str =
		ch_Len = 0
		keys = %keys1%
	}
	else if ch_Len > 5
		CONTINUE
	if GetKeyState("Shift", "p")
		keys = %keys2%
	Loop, Parse, keys
	{
		if GetKeyState(A_LoopField, "p") AND !In(A_LoopField, ch_str, 1)
		{
			ch_str = %ch_str%%A_LoopField%
			ch_Len ++	; for backspacing & performance reasons
			ch_Time = %A_TickCount%		; for threshold calculation
			sleep 10
		}
	}
	if (ch_Len > 1) & (A_TickCount - ch_Time > ch_th) AND GetKS(ch_str, 1)
		Loop %ch_#chords%
		{
			ifEqual, ch_%A_Index%, % ch_str
			{
						if !ch_%A_Index%_
							GoSub ch_%ch_str%
						else
						{
							BlockInput on
							ch_ := ch_%A_Index%_
							send {bs %ch_Len%}%ch_%
	
							Keywait(ch_str)
							Loop Parse, ch_str	; handle BlockInput side-effect
								if GetKeyState(A_LoopField)	; retrieve logical state
									send {%A_LoopField% up}
							BlockInput off
						}
				BREAK
			}
		}
}
return

ChordInfo:
Tooltip in: %ch_str%,, ,17
return

30GuiClose:
30GuiEscape:
Gui, 30:Destroy
return

;-------------------------------------------------

I'll add Shifted counterparts later, probably after Christmas [EDIT].

There are two known issues. One seems to be when the first two letters are pressed within 20 millisec of each other, which may result in the script seeing the first two keys in switched position and not acknowledging the chord; however, this is relatively rare. This can be worked around by removing the sequentialness requirement, but that would introduce a few other, more unwanted side-effects. i may resolve this in the future but it is currently not a priority. The other issue is when a word/string begins with two consecutive identical strings, like pompom, or tyty. If you have ty chorded to Thanksgiving, year-round, and you type tyty, it may sometimes auto-replace on the second instance even without simultaneous pressing.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

Chordful
  • Guests
  • Last active:
  • Joined: --
Interesting version of chording and quite different from Laszlo's. Sometimes it doesn't take and I get a bunch of ytytytyt repeats instead of Yours truly. It happens more often when you repeat the yt or don't have a space between the words. But sometimes it'll just repeat ytytyt even after a put a space after the prior word. And of course because you need to hold the chord down for awhile, if it doesn't type out Yours truly it reallly repeats the ytytyt for awhile. However this style of coding doesn't seem to have the skipping problem when simply typing without bringing chording into play.

If I want to also remap some of the keys, where would I embed something like a::1, s::2, d::3, f::4 etc ?

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004

Interesting version of chording and quite different from Laszlo's.

Thanks.

Sometimes it doesn't take and I get a bunch of ytytytyt repeats instead of Yours truly.

I've never observed this (Win2000), but i believe this has something to do with BlockInput. From the Helpfile:

If BlockInput becomes active while you're holding down keys, it might cause those keys to become "stuck down".

I've added handling for this, so there should be a noticeable improvement.

And of course because you need to hold the chord down for awhile,[...]

You can change the ch_th setting (below the hotkey label) from 150 to 70 or something else. This threshold prevents undesirable auto-replacement when the chord is a string common to another word. Experiment with the setting that gives the fastest chord output, yet still prevents unwanted auto-replace. This is dependent on one's typing speed. You could even mimic hotstrings by setting ch_th = 0, in which case the effect is instantaneous.

this style of coding doesn't seem to have the skipping problem when simply typing without bringing chording into play.

The approach i've taken is to leave normal typing as-is, and monitor when a chord is being pressed. In the script by Laszlo, chord-recognition is achieved thru A_ThisHotkey tied in with every alphanumeric key, and outputting accordingly to the keyboard state and timing. The unfortunate side-effect of that is to crowd out or make unpredictable other hotkeys. Also, since that script uses #UseHook, any hooks of other simultaneously running script(s) are effectively overridden once Laszlo's script takes effect. That is one reason why i wrote my version, other than for the challenge and practice.

If I want to also remap some of the keys, where would I embed something like a::1, s::2, d::3, f::4 etc ?

Anywhere after the auto-execute section (and outside another code block). The fast answer is: after the first Return.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
v1.1.2 reliability for chord expansion is now greatly improved
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
; v1.1.3 latest changes

;

; added ability to differentiate Shifted and unShifted characters

; further improved responsiveness for chord expansion; it is now reliable

1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

Harrie
  • Members
  • 41 posts
  • Last active: Dec 13 2007 04:25 PM
  • Joined: 04 Aug 2005
Decarlo, congratulations! This is superb. I mean it. Yes, it has solved the regular typing difficulties. The hot key is great to turn it on and off if you need to, and the ability to see your list by holding the hot key down is just fantastic!

I have mine set to 0. Instantaneous is for me. On the other hand, I like the way you can wait a tiny second between two keys and have it work. Although, I just got "without" when I was typing work! Maybe I should not go with the 0 setting after all.

p.o. won't work with hitting po, that must be because the period key is a delimeter?

Also, is there a way to have an automatic space after each expansion? Putting {Space} works, so it's no big deal.

Yes, AutoHotkey is an excellent program! (one second sentence)! Great work! Oh, I do have to change that sequence for without! :wink:

It is really, really great. I'll try using it all day tomorrow. You and Laszlo can challenge yourselves to your heart's content so far as I'm concerned

Many thanks.

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004

Decarlo, congratulations! This is superb.

Thank you, much appreciated.

p.o. won't work with hitting po, that must be because the period key is a delimeter?

That's correct, for version 1.1.3. The newer version 1.1.4 allows you to change the delimiter to anything you wish, but dont use it within any chord's expanded string. Use the third setting, ch_delimiter, below the hotkey label.

Also, is there a way to have an automatic space after each expansion? Putting {Space} works, so it's no big deal.

Yes, about 20 lines from the bottom of the code, replace:
send {bs %ch_Len%}%ch_%
with:
send {bs %ch_Len%}%ch_%%A_Space%

Great work! Oh, I do have to change that sequence for without!

Lol. looks like the 0 setting for auto-replace caught you on that one. Glad you find it useful; i've improved it to handle subroutines now, so it can be used for sets of commands, not just Send.

---

; v1.1.4

; added ch_delimiter setting
; added ability to Run/execute subroutines

In order to use a subroutine assigned to the chord, leave the string empty after the delimiter in the settings file, and label the subroutine ch_. For example, to use a subroutine for the chord p12, use ch_p12 as the subroutine label.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

chordful
  • Guests
  • Last active:
  • Joined: --
This method is intriguing. I don't seem to have the misfiring of keys as I do with Laszlo's method. I tried Laszlo's latest version but it still seems to have misfiring when doing normal typing.

However, I can't figure how to include the numeric pad numbers as a variable. For example pressing 123 gets me the sent 1234567890 keystrokes, but pressing the 123 using the numeric pad doesn't work.

I tried entering Numpad1, Numpad2, Numpad3 enclosed with either {}, "",() ;
at the end of line: keys = abcdefghijklmnopqrstuvwxyz0123456789`,./;'[]\-=``
and also in the Chord settings.ini but it doesn't seem to recognize it.

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004

pressing 123 gets me the sent 1234567890 keystrokes, but pressing the 123 using the numeric pad doesn't work.

This is because the use of the GetKS() function is hard-coded to read from the keys list, either each char in the string, or elements delimited with a comma (and spaces/tabs optional). It is currently set for (visible) single-characters. Numpad1,Numpad2, etc. are each 7 in length.

To allow for the NumPad, in the "Chord:" subroutine (towards the bottom of the script) make the following 2 adjustments:
1) Replace:
keys = abcdefghijklmnopqrstuvwxyz0123456789`,./;'[]\-=``
with:
keys = a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
, 0,1,2,3,4,5,6,7,8,9,`,,.,/,;,',[,],\,-,=,``
, Numpad1,Numpad2,Numpad3,Numpad4,Numpad5
, Numpad6,Numpad7,Numpad8,Numpad9,Numpad0
2) Replace, near the middle of the "Chord:" subroutine
if (ch_Len > 1) & (A_TickCount - ch_Time > ch_th) AND GetKS(ch_str, 1)
with
if (ch_Len > 1) & (A_TickCount - ch_Time > ch_th) AND GetKS(ch_str)

After this modification, i'm not sure if comma can be used as part of a chord. Hope this helps, and thanks for testing and using it.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

chordful
  • Guests
  • Last active:
  • Joined: --
Made the first change of adding comma delimeters and the NumpadX keylists for the line code keys = abc... Doesn't seem to be a problem there.

But once I change the code GetKS(ch_str, 1) to GetKS(ch_str) for the "Chord:" subroutine, the subroutine doesn't kick in. None of the chord setting preferences kicks in.

Also not sure how to enter Numpad1, Numpad2, Numpad3 into the chord setting preferences. Something like
{Numpad1}{Numpad2}{Numpad3}.1234567890 ??

For your reference I also tried using space as delimiters for keys =, but the strange result is that the chording only works at the beginning of a line in Notepad, after that chording doesn't work until I press return for the next line.

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
@chordful: i'm working on this and will try to post the solution for non-single-character-designation keys within a few days.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
Here is a version which should work with the numpad:
Chording Keyboard Input 1.1.4b

In your browser, you can Save As a .ahk file.

Note:
1) In the settings file, the following format should be used for any chords which contain at least one numpad keystroke:
; For numpad1 + numpad2 + numpad3, use:
1 = Numpad1|Numpad2|Numpad3|.1234567890

; spaces can be used before and/or after the "|" (pipe) symbol.
; A "|" must follow the last keystroke desgination before the abbreviation-expansion delimiter.

; The format for non-numpad chords remains the same:
1 = 123.1234567890

; mixed types are allowed:
1= Numpad1 | 2 | Numpad3 | .1234567890
2) For performance reasons, the max keystroke length allowed for a chord in v1.1.4b = 4. i may remove this limitation, but speed may be noticeably impacted.

Please post if you have any problems with it. Since i dont have a numpad on my laptop, i didnt test it (with the numlock/simulation key).
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

fsiefken
  • Members
  • 4 posts
  • Last active: Aug 04 2006 10:38 AM
  • Joined: 24 Jul 2006

Here is a version which should work with the numpad:
Chording Keyboard Input 1.1.4b


I get this popup message when enabling chording with the hotkey ^#k:

This variable or function contains a illegal character. The current thread will exit.
Specifically: Numpad1|Numpad3|
Line#
064: StringReplace,ch_%A_Index%,%ch_delimiter%

My settings.ini is:

[preferences]

total = 3

1 = eu.153
2 = ao.1234567890
3 = Numpad1|Numpad3|.ppp

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

This variable or function contains a illegal character. The current thread will exit.
Specifically: Numpad1|Numpad3|
Line#
064: StringReplace,ch_%A_Index%,%ch_delimiter%

That's strange, I don't see this line in either source code...
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

fsiefken
  • Members
  • 4 posts
  • Last active: Aug 04 2006 10:38 AM
  • Joined: 24 Jul 2006

That's strange, I don't see this line in either source code...


Sorry I typed over the popup window and made a mistake. In either case it refers to line 64 in the numpad chording variant:

if A_Index = 1
StringReplace, ch_%A_Index1%, %A_LoopField%, %A_Space%,, All
else

Perhaps Autohotkey doesn't detect a keypress from the numeric pad.