Mouse switch part 3

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Mouse switch part 3

14 Oct 2020, 05:09

@mikeyww

Sorry, but I am stuck again.

Trying to elimatinate all the other stuff, I have a basic code that should switch mouse buttons on pressing cntrl-j:
it doesn't seem to do anything (except display the message).

Can you help me again? What am I missing?

Code: Select all

^j::
{
SplashTextOn, 300, 100, title, Mouse switch!
sleep, 5000
SplashTextOff
LButton::RButton
RButton::LButton
}


User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Mouse switch part 3

14 Oct 2020, 05:54

Use the Hotkey command instead of the :: method of assigning a hotkey to assign one dynamically. You will not be able to do a remap but you will identify a subroutine where you can send the opposite mouse button.

Note also that braces { } are not what define a hotkey subroutine. The end is identified with a return like this:

Code: Select all

^j::
	SplashTextOn, 300, 100, title, Mouse switch!
	sleep, 5000
	SplashTextOff
	Hotkey...
	Hotkey...
return
User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

14 Oct 2020, 05:58

You cannot define a hotkey within a hotkey in this manner.

Here is one way to do a toggle. Hotkey is another way. I suggest having a look at that chapter, including example #1.

Code: Select all

^j::SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
LButton::Send % toggle ? "{RButton}" : "{LButton}"
RButton::Send % toggle ? "{LButton}" : "{RButton}"
If you need the remap, here is another way.

Code: Select all

^j::SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20

#If toggle
LButton::RButton
RButton::LButton
#If
#If is a convenient way to define conditional hotkeys and hotstrings.

Looks like @boiler and I were replying at the same time, so you now have three possible methods here!
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

14 Oct 2020, 08:23

Thanks again. I try so hard getting it work,
This is what I thought should work, but it isnt:

What the *(*(&* am I missing?

Code: Select all

^j::
SplashTextOn, 300, 100, title, Mouse switch!
sleep, 5000
SplashTextOff
toggle := !toggle
return

#If toggle
LButton::RButton
RButton::LButton
#If
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

14 Oct 2020, 08:27

mikeyww wrote:
14 Oct 2020, 05:58

If you need the remap, here is another way.

Code: Select all

^j::SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20

#If toggle
LButton::RButton
RButton::LButton
#If
#If is a convenient way to define conditional hotkeys and hotstrings.

Looks like @boiler and I were replying at the same time, so you now have three possible methods here!
@mikeyww I literally copied above code and run it and press ctrl-J, but it doesn't switches mouse button on my machine
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

14 Oct 2020, 08:36

mikeyww wrote:
14 Oct 2020, 05:58
You cannot define a hotkey within a hotkey in this manner.

Here is one way to do a toggle. Hotkey is another way. I suggest having a look at that chapter, including example #1.

Code: Select all

^j::SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
LButton::Send % toggle ? "{RButton}" : "{LButton}"
RButton::Send % toggle ? "{LButton}" : "{RButton}"
If you need the remap, here is another way.
@mikeyww
I copied above code and this switches the mouse button,
could you explain what the ? 1500 : 100 , 20 mean??

Also, I noticed that when the mouse button is changed with above code, multiple line selection/dragging with new assigned mouse buttons does not work.
Is this known/expected?

For my project I would also need multiple line selection and dragging etc with switched mouse buttons to work.
Hopefully this is possible with Autohotkey?
User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

14 Oct 2020, 08:47

Your first reply with the toggle + remap does work. Remember that your splash text is quite long (5 seconds), and the toggle occurs only after that period ends, according to your script. Remember, too, that if you mistakenly press the hotkey twice in a row, you will get the original assignment and not the toggled one.

"?:" is called a ternary operator. It simply uses the two punctuation marks to separate the three "If", "then", and "else" elements in shorthand. It is explained in the documentation.
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

14 Oct 2020, 10:53

thanks again @mikeyww

@mikeyww , @boiler
can you check on your machines that if mouse buttons are switched then multiple line selection and dragging (with new assigned primary mouse button) does not work? you can test this in a notepad session or try to move a window by dragging the window title and see if the window moves.

Could also be my mistake/wrong coding.

Need clarification on this. Could you please check your machines as well?

I have used this code:

Code: Select all

^j::SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
LButton::Send % toggle ? "{RButton}" : "{LButton}"
RButton::Send % toggle ? "{LButton}" : "{RButton}"
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Mouse switch part 3

14 Oct 2020, 11:27

That version of the code won't allow dragging because it's only sending a click, not a click down then later a click up when it's released. It could be made to do that, but you should just use the remap version instead (modified to show a SplashText as well):

Code: Select all

^j::
	SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
	SplashTextOn, 300, 100, title, % "Mouse switch " (toggle ? "ON" : "OFF") "!"
	SetTimer, SplashOff, -2000
return

SplashOff:
	SplashTextOff
return

#If toggle
LButton::RButton
RButton::LButton
#If

Here's what it would look like to make the Send version allow for dragging:

Code: Select all

^j::
	SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
	SplashTextOn, 300, 100, title, % "Mouse switch " (toggle ? "ON" : "OFF") "!"
	SetTimer, SplashOff, -2000
return

LButton::
	Send % toggle ? "{RButton down}" : "{LButton down}"
	KeyWait, LButton
	Send, % toggle ? "{RButton up}" : "{LButton up}"
return

RButton::
	Send % toggle ? "{LButton down}" : "{RButton down}"
	KeyWait, RButton
	Send, % toggle ? "{LButton up}" : "{RButton up}"
return
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

15 Oct 2020, 04:48

@boiler

I am sorry, I have copied above code, like this:

Code: Select all

^j::
	SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
	SplashTextOn, 300, 100, title, % "Mouse switch " (toggle ? "ON" : "OFF") "!"
	SetTimer, SplashOff, -2000
return

LButton::
	Send % toggle ? "{RButton down}" : "{LButton down}"
	KeyWait, LButton
	Send, % toggle ? "{RButton up}" : "{LButton up}"
return

RButton::
	Send % toggle ? "{LButton down}" : "{RButton down}"
	KeyWait, RButton
	Send, % toggle ? "{LButton up}" : "{RButton up}"
return

If I run it, I get an error:

---------------------------
ttz.ahk
---------------------------
Error: Target label does not exist.

Line#
003: SendMode,Input
004: SetWorkingDir,%A_ScriptDir%
011: Return
012: SoundBeep,(toggle := !toggle) ? 1500 : 1000,20
013: SplashTextOn,300,100,title,"Mouse switch " (toggle ? "ON" : "OFF") "!"
---> 014: SetTimer,SplashOff,-2000
015: Return
018: Send,toggle ? "{RButton down}" : "{LButton down}"
019: KeyWait,LButton
020: Send,toggle ? "{RButton up}" : "{LButton up}"
021: Return
024: Send,toggle ? "{LButton down}" : "{RButton down}"
025: KeyWait,RButton

The program will exit.
---------------------------
OK
---------------------------

Also, I have to say (I am sorry to), but above code has a lot of lines I simply don't understand. You see, I am trying to understand the code what it is actually doing. In stead of just copying/pasting it. I try to understand as well. This is no critism to you coding, but I think you're coding has a compact style, and for me my knowledge of Autohotkey commands is too limited to understand your code.
Would be helpfull to me and go through the code lines of above code and explain exactly line by line what it is doing???
For example:
SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20

seems to be combination commanding a soundbeep and also toggling a switch, in one line.
For me, as a beginner in Autohotkey thats hard to learn.

Next question:
why do you use -2000 as a parameter for SetTimer?
according to the syntax this is a priority value but why -2000?

What exactly means the line:
Send,toggle ? "{RButton up}" : "{LButton up}"

this send commando uses to send a keystroke/mousebutton but why use a ? ternary operator in that line?
User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

15 Oct 2020, 06:28

@boiler recommended the other script rather than this one, but this was informative to understand how to preserve mouse drag.

If you read the linked page that I posted about ternary operators, it explains how that operator works. The documentation for AHK is very clear and provides many examples. It is worth a look.

The toggle code is just a shorthand to save three lines. It could also be rewritten as follows.

Code: Select all

toggle := !toggle
If (toggle = True)
 SoundBeep, 1500, 20
Else SoundBeep, 1000, 20
SetTimer explains how the timer parameters work. A negative frequency runs the timer once.
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Mouse switch part 3

15 Oct 2020, 06:31

mgroen wrote:
15 Oct 2020, 04:48
I have copied above code, like this:
...
If I run it, I get an error:
...
Sorry, I left out the part subroutine where it turns off the SplashText in that one. It should be:

Code: Select all

^j::
	SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
	SplashTextOn, 300, 100, title, % "Mouse switch " (toggle ? "ON" : "OFF") "!"
	SetTimer, SplashOff, -2000
return

SplashOff:
	SplashTextOff
return

LButton::
	Send % toggle ? "{RButton down}" : "{LButton down}"
	KeyWait, LButton
	Send, % toggle ? "{RButton up}" : "{LButton up}"
return

RButton::
	Send % toggle ? "{LButton down}" : "{RButton down}"
	KeyWait, RButton
	Send, % toggle ? "{LButton up}" : "{RButton up}"
return


mgroen wrote:
15 Oct 2020, 04:48
SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20

seems to be combination commanding a soundbeep and also toggling a switch, in one line.
The ternary operator (see Operators in Expressions) is shorthand for if-else. It is affecting only the first SoundBeep parameter since that's where it appears, and it is like if it said this:

Code: Select all

if (toggle := !toggle) ; assigns the opposite of toggle's current value to itself (1 if it is 0, 0 if it is 1) and that becomes the result of the condition
	SoundBeep, 1500, 20
else
	SoundBeep, 1000, 20


mgroen wrote:
15 Oct 2020, 04:48
why do you use -2000 as a parameter for SetTimer?
according to the syntax this is a priority value but why -2000?
It's not a priority value. You're looking at the wrong parameter. That's the third parameter which wasn't used. This is the Period parameter (the second one). A negative value says to perform the code at the label once, and wait that number of milliseconds. So in 2000 ms, it will perform the subroutine that turns off the SplashText one time. This is better than using Sleep, 2000 because the rest of the code isn't waiting for the Sleep to finish.

mgroen wrote:
15 Oct 2020, 04:48
What exactly means the line:
Send,toggle ? "{RButton up}" : "{LButton up}"

this send commando uses to send a keystroke/mousebutton but why use a ? ternary operator in that line?
It is done so it will send the either RButton or LButton depending on what the value of toggle is (1 or 0), which is whether you want the buttons switched or not. It is the same as this:

Code: Select all

if toggle ; true if toggle is 1, false if toggle is 0
	Send, {RButton up}
else
	Send, {LButton up}
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

16 Oct 2020, 06:09

Once again, thank you both.

Well..I have given it all a thought yesterday and this morning.
Situation is as follows:
I have copied @boiler s code and this works.
Here is the code:

Code: Select all

^j::
	SoundBeep, (toggle := !toggle) ? 1500 : 1000, 20
	SplashTextOn, 300, 100, title, % "Mouse switch " (toggle ? "ON" : "OFF") "!"
	SetTimer, SplashOff, -2000
return

SplashOff:
	SplashTextOff
return

LButton::
	Send % toggle ? "{RButton down}" : "{LButton down}"
	KeyWait, LButton
	Send, % toggle ? "{RButton up}" : "{LButton up}"
return

RButton::
	Send % toggle ? "{LButton down}" : "{RButton down}"
	KeyWait, RButton
	Send, % toggle ? "{LButton up}" : "{RButton up}"
return
Now, what I want to do, is to rewrite this in code thats understandable for me. So, I know this code is functionally correct because it does what I expect it to do, but I have some caps in understanding the lines. Also, I want to implement above functionality in other code I have produced already.

So, I will rewrite above above code in my own style.

Now I will let you know what I have written and post it here for your comments.
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

19 Oct 2020, 07:19

Hi @boiler , @mikeyww (and others),

like said earlier I am in the progress of writing a mouse switch applicatoin.
Unfortunately, it's harder then I thought :(

Well, I have tried to produce code I understand myself and its working. Unfortunately, the latter is not the case :(

Code follows below and the end of the message.

The major thing is my mind gets stuck in the combination of toggles and sending right/mouse buttons in very short/combined statements. So for my code understandeness, I want to have this split up as much as possible. So, I redesigned the code snippets you helped me with so far, but then I get errors.

My second question is, how to define a variable with 2 values, being non numeric. Explaination: in stead of using (probably binary toggle value), I would like define a variable (or maybe better said: a constante with 2 defined values), with the name: switch_value, and possible values: left_hand and right_hand. Is that even possible with Autohotkey and also how to use this in if statements?

At the moment, current code fails to run because of the error:
---------------------------
focus_9.ahk
---------------------------
Error at line 53.

Line Text: LButton
Error: Duplicate hotkey.

The program will exit.
---------------------------
OK
---------------------------

I think this error is because of the redefition of LButton. Is there a way to prevent/fix this error by putting it in a subroutine, and call this subroutine by using parameters? If tried but I get stuck :(

Also, could you evaluate if the definition and usage of switch_value with 2 possible values is correctly implemented?

Thanks again!
ps: my goals is to have working code that I can understand and explain to someone else.

Code: Select all

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

Pauze_interval := 30000
sleep_duur_left := 3000
sleep_duur_right := 4000
switch_value := left_hand, right_hand

cursor_left = D:\mouse\ARROW_RED_LEFT.cur
cursor_right = D:\mouse\ARROW_GREEN_RIGHT.cur

loop {

	if switch_value = left_hand
	{
	SplashTextOn, 300, 100, Mouse switch, switch mouse to LEFT hand
	Sleep, 5000
	SplashTextOff

	; Code voor muiscursor switch:
	SetSystemCursor(cursor_left)

	; set left button press to right button function:
	LButton::
	Send % "{RButton down}" 
	KeyWait, LButton
	Send, % "{RButton up}"
	return
	
	; set right button press to left button function:
	RButton::
	Send % "{LButton down}" 
	KeyWait, RButton
	Send, % "{LButton up}"
	return
	
	sleep, sleep_duur_left

	}

	if switch_value = right_hand
	{
	SplashTextOn, 300, 100, Mouse switch, switch mouse to RIGHT hand
	Sleep, 5000
	SplashTextOff

	; code voor muiscursor switch:
	SetSystemCursor(cursor_right)

	; set left button press to left button function:
	LButton::
	Send % "{LButton down}" 
	KeyWait, LButton
	Send, % "{LButton up}"
	return
	
	; set right button press to right button function:
	RButton::
	Send % "{RButton down}" 
	KeyWait, RButton
	Send, % "{RButton up}"
	return	

	sleep, sleep_duur_right

	}

switch_value := !switch_value
Sleep, Pauze_interval
} 

; functiedefinities:

SetSystemCursor(file) {
 CursorHandle := DllCall("LoadCursorFromFile", Str, file)
 Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
 Loop, Parse, Cursors, `,
 {
  DllCall( "SetSystemCursor", Uint, CursorHandle, Int, A_Loopfield )
 }
}




User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

19 Oct 2020, 07:32

You're on the right track! Hotkeys are not things that you define in your running code. They are "static" and so stand alone outside routines like this, though you can have context-sensitive hotkeys with #If or Hotkey.

The good news is that this is easy to fix. Delete your button hotkey definitions. Replace them with the following.

To swap mouse buttons:

Code: Select all

DllCall("SwapMouseButton", "uInt", True)
To resume default mouse buttons:

Code: Select all

DllCall("SwapMouseButton", "uInt", False)
These functions will do the entire swap for you.

Regarding toggles: one convenient approach is to use True and False, without quotation marks. True is the same as 1; False is the same as zero. This helps with readability of the code.

It may help you to examine examples of other working code on the forum here, regarding the hotkeys. They are defined only once (maximum), or once per context. An example of a context is having a particular window that is active. If you want to turn a hotkey on and off, you would use an #If directive or a Hotkey command. The #If directive is convenient and efficient. It precedes a hotkey definition and simply defines any condition that makes that hotkey active.
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

19 Oct 2020, 08:11

Back again,

I managed to re-write my code. I included a function to switch mouse buttons:

Here is the fresh code:

Code: Select all

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

Pauze_interval := 30000
sleep_duur_left := 3000
sleep_duur_right := 4000
switch_value := left_hand, right_hand

cursor_left = D:\mouse\ARROW_RED_LEFT.cur
cursor_right = D:\mouse\ARROW_GREEN_RIGHT.cur

loop {

	if switch_value = left_hand
	{
	SplashTextOn, 300, 100, Mouse switch, switch mouse to LEFT hand
	Sleep, 5000
	SplashTextOff

	; Code voor muiscursor switch:
	SetSystemCursor(cursor_left)

	buttontoggle(left_hand)
	
	sleep, sleep_duur_left

	}

	if switch_value = right_hand
	{
	SplashTextOn, 300, 100, Mouse switch, switch mouse to RIGHT hand
	Sleep, 5000
	SplashTextOff

	; code voor muiscursor switch:
	SetSystemCursor(cursor_right)

	buttontoggle(right_hand)

	sleep, sleep_duur_right

	}

switch_value := !switch_value
Sleep, Pauze_interval
} 

; function_definitions:

SetSystemCursor(file) {
 CursorHandle := DllCall("LoadCursorFromFile", Str, file)
 Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
 Loop, Parse, Cursors, `,
 {
  DllCall( "SetSystemCursor", Uint, CursorHandle, Int, A_Loopfield )
 }
}

button_toggle(togglevalue){
LButton::
	if (togglevalue = left_hand)
	{
	Send % "{RButton down}"
	KeyWait, LButton
	Send, % "{RButton up}"
	}
	if (togglevalue = right_hand)
	{
	Send % "{LButton down}"
	KeyWait, LButton
	Send, % "{LButton up}"
	}

RButton::
	if (togglevalue = left_hand)
	{
	Send % "{LButton down}"
	KeyWait, RButton
	Send, % "{LButton up}"
	}
	if (togglevalue = right_hand)
	{
	Send % "{RButton down}"
	KeyWait, RButton
	Send, % "{RButton up}"
	}
}

Unfortunately, it doesn't run :( :(

When running, I get the error:

---------------------------
focus_10.ahk
---------------------------
Error at line 62.

Line Text: LButton::
Error: Hotkeys/hotstrings are not allowed inside functions.

The program will exit.
---------------------------
OK
---------------------------

Too bad :(:(

Could I create a feature request to support hotkeys inside functions??
User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

19 Oct 2020, 08:19

As I mentioned, you cannot define a hotkey in running code such as within a function (unless you want to use Hotkey). You can use Hotkey to accomplish what you want.

It looks like I am not helping you, so I am signing off. Best wishes for success.
mgroen
Posts: 97
Joined: 13 Jul 2018, 02:22

Re: Mouse switch part 3

19 Oct 2020, 08:23

mikeyww wrote:
19 Oct 2020, 08:19
As I mentioned, you cannot define a hotkey in running code such as within a function (unless you want to use Hotkey).

It looks like I am not helping you, so I am signing off. Best wishes for success.
No, please, @mikeyww you did help me a lot!
but its just that your code is too compact/complicated for me so I need to rewrite it to my own code.

What bothers me more is that my programming style does not really with Autohotkey specifications... is this something to overcome?

Edit:
so you suggest me to use Hotkey function, right? I will take a look at it later.. Thanks!
User avatar
mikeyww
Posts: 26600
Joined: 09 Sep 2014, 18:38

Re: Mouse switch part 3

19 Oct 2020, 08:34

A lot of people get stuck in the same way you do, because hotkey definitions depart from more common "linear" kinds of coding. You could think of the hotkey definition as a sort of function that is called, "in place", whenever you press the hotkey. Like a function, you locate this anywhere in your script (below the auto-execute section at the top), and the script will find it when needed. Since you wouldn't duplicate a function definition or put a function in the middle of your running code (there may be exceptions), you can similarly avoid doing that with a hotkey definition. In case it helps you with getting the habit down, force all of the hotkey definitions to reside at the bottom of your script for a while.

Hotkey is easy to use. You just label a subroutine that you want the hotkey to run. You then issue Hotkey, key, label, On, or Hotkey, key, Off for default operation. The documentation provides more examples and options. In most situations, you don't actually need the Hotkey command and can simply use a #If directive instead (often a better choice). In some situations, a directive does not meet the need.

For mouse button swap, you are probably better off using the DLL function that I provided. It saves some headaches getting drag to work properly.
Last edited by mikeyww on 19 Oct 2020, 08:39, edited 1 time in total.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: jaka1 and 140 guests