Slider control's A_GuiEvent = 4 is unreliable

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Slider control's A_GuiEvent = 4 is unreliable

27 Aug 2017, 04:42

If a Slider's subroutine takes a while to execute (eg. 100ms) due to it being a complex subroutine, or some other thread in the script interrupting it, A_GuiEvent 4 (the event which triggers on mouse release) becomes unreliable. I believe it's due to the mouse position getting ahead of the thumb position (the bar moved by the user) and so when the mouse is released, the cursor is no longer on top of the thumb, and it only counts as a release if the mouse position = the thumb position at the time of release. Possible solution: trigger A_GuiEvent 4 if mouse position overlaps the entire control's position at time of release rather than just the thumb's position.

Here is a demo. Move the slider and release the left mouse button when the cursor is in front of the thumb. In that case you should not hear the sound play, in which case A_GuiEvent4 failed. On my system (i7-4790k) it only triggers if I don't move the slider too fast. You may need to change the loop length to suit your system. It's a problem for me because I need to execute some lines ONLY on mouse release, otherwise it's too much overhead to have that subroutine executing tens of times (and sometimes it has actually crashed AHK).

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test



Slider1:
DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
counter := 0
loop 1500000
{
counter += 1
}
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
GuiControl ,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
If (A_GuiEvent = 4)
	Soundplay , *64

return
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

27 Aug 2017, 05:49

:arrow: GUI Events, Threads, and Subroutines

Clicking on a control while its g-label is already running from a prior click will have no effect and the event is discarded. To prevent this, use Critical as the subroutine's first line (however, this will also buffer/defer other threads such as the press of a hotkey).
The same is true for all other events triggering a control's g-label.

This isn't an AHK bug, it's wrong usage!
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

27 Aug 2017, 22:13

just me wrote: This isn't an AHK bug, it's wrong usage!
Unfortunately critical doesn't seem to be having any effect (it still misses the event) so I can't agree it's "wrong usage" if there is no "right way" of doing it. I guess it's just a limitation of ahk, which fair enough is not a bug* , but a pretty severe limitation imo (my main script only takes 24ms to complete the subroutine and still misses the event every now and then for some reason). I guess I will have to look into a manual workaround along the lines of checking if the mouse cursor overlaps the slider's position when releasing the mouse button.

*edit: on second thought, if the documentation says Critical should fix the behaviour and it doesn't, then that could count as a "problem with documented functionality" and therefore a bug.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

27 Aug 2017, 22:39

Seem to have an easy solution

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1
OnMessage(0x202,"WM_LBUTTONUP")

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test



Slider1:
DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
counter := 0
loop 1500000
{
counter += 1
}
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
GuiControl ,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
;If (A_GuiEvent = 4)
	;Soundplay , *64

return



WM_LBUTTONUP(){
If (A_GuiControl = "Slider1")
	Soundplay , *64
}
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

28 Aug 2017, 01:02

pneumatic wrote:... my main script only takes 24ms to complete the subroutine ...
24 ms are a quite long time on a i7-4790k CPU.

Would you please post the real code which produces the issue? There might be are other 'workarounds'.

Code: Select all

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
counter := 0
loop 1500000
{
counter += 1
}
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
doesn't make sense.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

28 Aug 2017, 05:21

just me wrote:24 ms are a quite long time on a i7-4790k CPU.
Yes, but remember ahk is single threaded and only uses about ~12.5% CPU max. If a game was CPU bound to 24ms (~40fps) that shouldn't affect reliability of controls.
just me wrote: Would you please post the real code which produces the issue? There might be are other 'workarounds'.
I can't I'm sorry, it's closed source for now.
just me wrote:

Code: Select all

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
counter := 0
loop 1500000
{
counter += 1
}
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
doesn't make sense.
I was just trying to make the subroutine last long enough to produce the issue. I was going to use sleep instead but I found sleep is different in the way it behaves when there are multiple threads sleeping, so I didn't think that was a realistic test. Should I have done something differently?
obeeb
Posts: 140
Joined: 20 Feb 2014, 19:15

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 00:38

Critical on the first line worked for me, are you sure you tried with it on the first line? It takes some time until it processes all the other events and handles the release event.
If it doesn't then it's definitely a bug even according to justme's narrow definition, though if it only happens on your system it might be difficult to reproduce and fix.
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 05:20

obeeb wrote:If it doesn't then it's definitely a bug even according to justme's narrow definition ...
It might be a bug if it could be verified that the control actually sends a WM_H/VSCROLL notification with TB_THUMBPOSITION (4) received and not forwarded to the g-label by AHK although the g-label is Critical.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 06:03

obeeb wrote:Critical on the first line worked for me, are you sure you tried with it on the first line?
Yes, on the first line of the subroutine, and then Critical, Off at the last line (if you are not doing critical , off at the end then yes it fixes the issue but the whole script becomes critical which is no good, plus too many of the slider's subroutines get buffered to the point where the sound effect plays some 1.5 seconds after mouse release - not acceptable for my use). If you are using critical, Off at the end and its working fine for you then it could be something specific to my system, or maybe you are not releasing the mouse button while the slider is in motion?

The workaround I posted is no good either, because the WM_LBUTTONUP executes before any GUI control is updated with its new state. For example with an unchecked box, upon clicking it, it will go to WM_LBUTTONUP while the checkbox is still in an unchecked state with seemingly no way of correcting it, i.e putting a Gui,Submit,NoHide or GuiControlGet as the first line inside WM_LBUTTONUP still only gets the control's associated variables before it has even responded to the click. Looking into a possible solution , maybe making the control's gosub critical or Thread, Interrupt, but neither seem to be capable of guaranteeing it. Any ideas?

edit: putting a WM_LBUTTONUP() inside the control's gsub works, but then it results in WM_LBUTTONUP() executing twice every time that control is clicked, which is weird and patchy and I don't like it :problem:

Also tried

Code: Select all

WM_LBUTTONUP(){
 Pause, On, 0
 Sleep 100   ;give the underlying GUI thread a chance to update the control state (not its associated variable, just it's state so that any future Gui,Submits or GuiControlGets retrieve the value after the click release, rather than its state before the release
 Pause, Off
 ...
 }


but didn't work; execution doesn't even get past the Pause, Off. No idea what is actually happening to the thread.


edit3: this seems to work

Code: Select all

WM_LBUTTONUP(){

global AGuiControl := A_GuiControl   ;because A_GuiControl becomes blank when LButtonUp timer fires 10ms from now.  For some reason static doesn't work - why?  
SetTimer , LButtonUp , 10
return

	LButtonUp:
	Gui, GuiName: Submit, NoHide   ;must use GuiName: from now on for all Gui related commands because it's a different thread
	
	If (AGuiControl = ControlVariableName)
            soundplay , *64 
            
	SetTimer, LButtonUp, Off
	AGuiControl := ""  ;just to be safe
	return
	
}
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 07:50

For what reason do you use AltSubmit at all? Obviously, your g-label isn't able to process all incoming notifications in adequate time.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 08:01

just me wrote:For what reason do you use AltSubmit at all?
Because I don't need to be submitting every single variable in the entire GUI.
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 08:25

I don't understand how AltSubmit specified for a Slider control is related to 'submitting every single variable in the entire GUI'.
Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 08:42

The label slider1 is getting called often while dragging, is that desired? (You can tell because the time updates while dragging the slider around).

If you only need to run your subroutine once after the slider has been moved, just check for the Event first, and return if it isn't a mouse release...

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test
Return


Slider1:
If (A_GuiEvent != 4)
	Return
DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
counter := 0
loop 1500000
{
counter += 1
}
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
GuiControl ,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
Soundplay , *64
Return
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 08:52

just me wrote:I don't understand how AltSubmit specified for a Slider control is related to 'submitting every single variable in the entire GUI'.
Nevermind I was remembering the wrong bit in the help file. My sliders make adjustments to an xgraph which the user needs to see in real time not just on release. I guess this kind of real time feedback is putting a bit of strain on ahk. I am satisfied with the workaround, although I have noticed the slider position is still not quite perfectly in sync with the state of the subroutine if I move the mouse really really fast and release the button. But it's good enough.
Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 10:18

pneumatic wrote: My sliders make adjustments to an xgraph which the user needs to see in real time not just on release
An alternate option for you, since the issue is caused because only one thread of the g-label can be running, the event #4 never fires if the mouse is moving too fast. We can use SetTimer to work around this by watching the slider separately until the mouse is released. This way the g-label itself returns very quickly and nothing should be missed, and you can still react to where the slider is positioned.

If you are happy with just playing a sound whenever the LButton is released, then just ignore this suggestion.

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, Main:New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test
Return


Slider1:
IF (A_GuiEvent = 5 AND !AlreadyWatching) {
	SetTimer, watchSlider, 1
	AlreadyWatching := True
}
IF (A_GuiEvent = 4) {
	SetTimer, watchSlider, -1 ; -1 instead of Off to have it update on the final position.
	AlreadyWatching := False
	Soundplay , *64
}
Return

watchSlider:
Gui, Main:Default
GuiControlGet, sliderVal,, Slider1
IF (sliderVal != prevVal) {
	DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
	counter := 0
	loop 1500000
	{
	counter += 1
	}
	DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
	GuiControl,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
	prevVal := sliderVal
	ToolTip, %sliderVal%  ; Just an example, to show how to use the current value of the slider, without needing to submit the GUI.
}
Return
Note: In my example I named the Gui Main obviously change it to what ever yours is called wherever it is used.

Edit: Added check to see if slider changed before running the long process.
just me
Posts: 9453
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 11:20

Hi Nightwolf85,

i've been working on a timer solution, too. I think the timer has to be called in more cases to update the XGraph properly:

Code: Select all

Slider1:
If (A_GuiEvent = 4) || A_GuiEvent = "Normal") {
	SetTimer, watchSlider, -1 ; -1 instead of Off to have it update on the final position.
	AlreadyWatching := False
	Soundplay , *64
}
Else If !(AlreadyWatching) {
	SetTimer, watchSlider, 1
	AlreadyWatching := True
}
Return
Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

29 Aug 2017, 11:33

just me wrote:i've been working on a timer solution, too. I think the timer has to be called in more cases to update the XGraph properly:
You are probably right, I just used the dragging as it was being discussed earlier in the post, good suggested change though.

Edit: Actually you probably only want the timer running while being dragged, but do want it to run once for all other cases. Otherwise arrow keys would start the timer then never stop it? or does it always end with a normal?

So Maybe order it this way?
**Removed**

Edit2: It will always end with a normal, I just tested it out. so it doesn't really matter I guess.
Last edited by Nightwolf85 on 30 Aug 2017, 09:41, edited 1 time in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

30 Aug 2017, 06:47

I think grabbing A_GuiControl on WM_LBUTTONUP() is just a fluke and in no way guarantees A_GuiControl will receive the correct value. i.e I see no reason why WM_LBUTTONUP() is guaranteed to be part of the thread that was launched by the control (which is required for A_GuiControl to receive the control's assoc. var). I found it doesn't work for example when clicking on a list item in a dropdownlist, I guess because WM_LBUTTONUP() is firing either before or after the dropdownlist's subroutine thread.
Last edited by pneumatic on 30 Aug 2017, 09:10, edited 1 time in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

30 Aug 2017, 09:05

Apparently OnMessage functions will interrupt everything including critical threads, so if the slider's subroutine should not be interrupted mid way through then there is no way of preventing that. I guess it could be handled by using a variable at the end of the sub to signify that the subroutine has finished and a new one can start. Now I see why "Clicking on a control while its g-label is already running from a prior click will have no effect and the event is discarded" is important :lol:

edit: seems to work , but requires a timer again (messy :facepalm: )

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1
OnMessage(0x202,"WM_LBUTTONUP")

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test


Slider1:

	Critical
	DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
	counter := 0
	loop 2000000
		counter += 1
	DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
	GuiControl ,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
	global A_GuiLastSubroutine := "Slider1"
	Critical , Off

return


WM_LBUTTONUP(){

	SetTimer , WM_LBUTTONUP, 1	
	return
	
	WM_LBUTTONUP:
	global A_GuiLastSubroutine

	If ( A_GuiLastSubroutine = "Slider1"){
		soundplay , *64
		A_GuiLastSubroutine := ""
		
	}
		
	SetTimer , WM_LBUTTONUP, Off
	return
	
}
Last edited by pneumatic on 30 Aug 2017, 11:05, edited 5 times in total.
Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

30 Aug 2017, 09:37

What was wrong with:

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
1ms := (CounterAfter - CounterBefore) / 1000

Gui, Main:New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSlider1
Gui, Add, Text, vText w250 x300 yp+50
Gui, Show, w740 h100 Center, Slider Test
Return

Slider1:
If (A_GuiEvent = 4 || A_GuiEvent = "Normal") {
	SetTimer, watchSlider, -1 ; -1 instead of Off to have it update on the final position.
	AlreadyWatching := False
	Soundplay , *64
}
Else If !(AlreadyWatching) {
	SetTimer, watchSlider, 1
	AlreadyWatching := True
}
Return

watchSlider:
Gui, Main:Default
GuiControlGet, sliderVal,, Slider1
IF (sliderVal != prevVal) {
	DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
	counter := 0
	loop 1500000
	{
	counter += 1
	}
	DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
	GuiControl,, Text, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
	prevVal := sliderVal
	ToolTip, %sliderVal%  ; Just an example, to show how to use the current value of the slider, without needing to submit the GUI.
}
Return

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: doodles333, vysmaty and 238 guests