[Solved] Why sleep freeze all timer !? Is possible add a delay in label?

Get help with using AutoHotkey and its commands and hotkeys
dsewq1LYJ
Posts: 114
Joined: 26 Aug 2014, 23:21

[Solved] Why sleep freeze all timer !? Is possible add a delay in label?

12 Sep 2015, 08:22

Follow is my code:

Code: Select all

F4::
SetTimer,QWE,1
SetTimer,ASD,-1
Exit


QWE:
ToolTip FOO BAR
;~ And the sleep in ASD just pause QWE 2,000 ms...What !?
Exit

ASD:
MsgBox Hello???
Sleep,2000
MsgBox What the hell.
Exit
Code explain my problem, and ... HOW...please help me :(
Last edited by tomoe_uehara on 13 Sep 2015, 11:50, edited 1 time in total.
Reason: Marked as solved
User avatar
evilC
Posts: 4771
Joined: 27 Feb 2014, 12:30

Re: Why sleep freeze all timer !? Is possible add a delay in label?

12 Sep 2015, 12:25

The sleep in ASD is not affecting QWE.
Run this script and observe that the tooltip still changes between 1 and 0, even when ASD is Sleeping, or the message box is being displayed.

Code: Select all

#SingleInstance force
test_var := 0

F4::
ToolTip
SetTimer,QWE,500
SetTimer,ASD,-1
return
 
 
QWE:
test_var := !test_var
ToolTip % "TV: " test_var
return
 
ASD:
MsgBox Hello???
Sleep,2000
MsgBox What the hell.
return
lexikos
Posts: 6878
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Why sleep freeze all timer !? Is possible add a delay in label?

12 Sep 2015, 19:09

See Threads. If thread A interrupts thread B, thread B won't resume until thread A completes. However, that's not happening in this case because QWE finishes before it can be interrupted. It would happen if you added Sleep 1 after the ToolTip, since that would allow ASD to interrupt QWE.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: Why sleep freeze all timer !? Is possible add a delay in label?

13 Sep 2015, 06:25

Lexikos wrote:It would happen if you added Sleep 1 after the ToolTip, since that would allow ASD to interrupt QWE.
Hmm, in my case, the interruption happens about half of the time, even w/o Sleep 1. There is this:
Thread wrote:By default, every newly launched thread is uninterruptible for a Duration of 15 milliseconds or a LineCount of 1000 script lines, whichever comes first. This gives the thread a chance to finish rather than being immediately interrupted by another thread that is waiting to launch (such as a buffered hotkey or a series of timed subroutines that are all due to be run).
but perhaps in some circumstances (slower PC/too much stuff running) 15ms is not enough. With evilC's script the interruption never happened.

There is also the question of why, when the messagebox thread interrupts the tooltip thread, another instance of the tooltip thread is not spawned after 1 ms (so that the tooltip would move again). It looks like it's because after registering a particular timer, there can only be _one_ thread associated with that registered timer at a time. I.e. if that registered timer thread gets interrupted, another instance of that timer thread won't be spawned, even if the timer was supposed to be run every 1ms (won't be spawned until the original instance finishes). Couldn't find the documentation quote on this though.

//edit: so it isn't like the tooltip thread can't be interrupted _at all_ (so that the tooltip stops moving) - it's just that _in that particular example_, the tooltip thread does not interrupt the messagebox thread.

Code: Select all

#Persistent
SetTimer, tooltip, 20
SetTimer, messagebox, -2000
return

tooltip:
	Tooltip, % A_Now
	sleep 4000 ; make sure interruption happens
return

messagebox:
	msgbox hello ; tooltip stops moving
return
Last edited by trismarck on 13 Sep 2015, 07:33, edited 1 time in total.
User avatar
evilC
Posts: 4771
Joined: 27 Feb 2014, 12:30

Re: Why sleep freeze all timer !? Is possible add a delay in label?

13 Sep 2015, 07:02

I have just observed a similar issue in one of my projects.
It was caused by Sleep being used in a thread that had Critical declared.
Note how the MsgBox Hello??? in ASD() does not halt execution of the QWE timer, but the Sleep does.

Code: Select all

#SingleInstance force
test_var := 0
 
F4::
ToolTip
SetTimer,QWE,500
fn := Func("ASD")
SetTimer, % fn,-1
return
 
 
QWE:
test_var := !test_var
ToolTip % "TV: " test_var
return

ASD(){
	Critical
	MsgBox Hello???
	Sleep,2000
	MsgBox What the hell.
}
Interestingly, in my project, the Sleep would not always stop the Timer from firing - it would only happen some of the time. As in, for a given run of the script, it would either always stop the timer or never stop it. Seems to generally happen if the script is not running, then you run it. If the script was already running and I hit F5 in SciTE, it always seemed to block the timer.

Here is the broken version of the code from my project:
The Timer that was getting interrupted was the one declared in _ProcessJHook that called _WatchJoystickPOV.
It was being interrupted by the Sleep in _WaitForJoyUp
To replicate, you will need a joystick with a hat.
Hold any joystick button and notice how the WATCHPOV tooltip stops counting. Run the script multiple times and notice how sometimes it does not halt the WATCHPOV tooltip.

Code: Select all

#SingleInstance force
#Persistent

OutputDebug DBGVIEWCLEAR

mc := new MyClass()
return

class MyClass {
	__New(){
		this.CInputDetector := new CInputDetector(this._ProcessInput.Bind(this), opt)
	}
	
	_ProcessInput(aParams*){

	}
}

;------------------------------------------------------------------------------------------------------------
; Sets up the hooks etc to watch for input
;------------------------------------------------------------------------------------------------------------
class CInputDetector {
	__New(callback, options := 0){
		if (options = 0){
			options := {}
		}
		this.options := options
		this._HooksEnabled := 0
		this._Callback := callback
		
		this.EnableHooks()
	}
	
	EnableHooks(){
		static WH_KEYBOARD_LL := 13, WH_MOUSE_LL := 14
		
		if (this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 1

		; Hook Input
		this._hHookKeybd := this._SetWindowsHookEx(WH_KEYBOARD_LL, RegisterCallback(this._ProcessKHook,"Fast",,&this))
		this._hHookMouse := this._SetWindowsHookEx(WH_MOUSE_LL, RegisterCallback(this._ProcessMHook,"Fast",,&this))
		
		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; watch buttons
				Loop % 32 {
					fn := this._ProcessJHook.Bind(this, joyid, A_Index)
					hotkey, % joyid "Joy" A_Index, % fn
					hotkey, % joyid "Joy" A_Index, On
				}
				; disablejoystick option is useful when debugging, so you do not keep getting interrupted by the settimer.
				; Watch POVs
				if (instr(joyinfo, "p")){
					this._JoysticksWithHats.push(joyid)
				}
			}
		}
		if (this.options.disablejoystickhats != 1){
			fn := this._WatchJoystickPOV.Bind(this)
			SetTimer, % fn, 10
		}
		return 1
	}
	
	DisableHooks(){
		if (!this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 0

		this._UnhookWindowsHookEx(this._hHookKeybd)
		this._UnhookWindowsHookEx(this._hHookMouse)

		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; stop watching buttons
				Loop % 32 {
					hotkey, % joyid "Joy" A_Index, Off
				}
			}
		}
		fn := this._WatchJoystickPOV.Bind(this)
		SetTimer, % fn, Off
		return 1
	}
	
	; Process Joystick button down events
	_ProcessJHook(joyid, btn){
		;ToolTip % "Joy " joyid " Btn " btn
		this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 1, uid: joyid "j" btn})
		fn := this._WaitForJoyUp.Bind(this, joyid, btn)
		SetTimer, % fn, -0
	}
	
	; Emulate up events for joystick buttons
	_WaitForJoyUp(joyid, btn){
		str := joyid "Joy" btn
		while (GetKeyState(str)){
			sleep 10
		}
		this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 0, uid: joyid "j" btn})
	}
	
	; A constantly running timer to emulate "button events" for Joystick POV directions (eg 2JoyPOVU, 2JoyPOVD...)
	_WatchJoystickPOV(){
		ToolTip % "WATCHPOV " A_TickCount
		static pov_states := [-1, -1, -1, -1, -1, -1, -1, -1]
		static pov_strings := ["1JoyPOV", "2JoyPOV", "3JoyPOV", "4JoyPOV", "5JoyPOV", "6JoyPOV" ,"7JoyPOV" ,"8JoyPOV"]
		static pov_direction_map := [[0,0,0,0], [1,0,0,0], [1,1,0,0] , [0,1,0,0], [0,1,1,0], [0,0,1,0], [0,0,1,1], [0,0,0,1], [1,0,0,1]]
		static pov_direction_states := [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]
		Loop % this._JoysticksWithHats.length() {
			joyid := this._JoysticksWithHats[A_Index]
			pov := GetKeyState(pov_strings[joyid])
			if (pov = pov_states[joyid]){
				; do not process stick if nothing changed
				;OutputDebug % "WATCHPOV: Nothing Changed"
				continue
			}
			if (pov = -1){
				state := 1
			} else {
				state := round(pov / 4500) + 2
			}
			
			Loop 4 {
				if (pov_direction_states[joyid, A_Index] != pov_direction_map[state, A_Index]){
					OutputDebug % "_WatchJoystickPOV: Change!"
					this._Callback.({Type: "h", Code: A_Index, joyid: joyid, event: pov_direction_map[state, A_Index], uid: joyid "h" A_Index})
				}
			}
			pov_states[joyid] := pov
			pov_direction_states[joyid] := pov_direction_map[state]
		}
	}
	
	; Process Keyboard Hook messages
	_ProcessKHook(wParam, lParam){
		; KBDLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx
		; KeyboardProc function: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
		static WM_KEYDOWN := 0x100, WM_KEYUP := 0x101, WM_SYSKEYDOWN := 0x104
		Critical
		
		if (this<0){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		
		vk := NumGet(lParam+0, "UInt")
		Extended := NumGet(lParam+0, 8, "UInt") & 1
		sc := (Extended<<8)|NumGet(lParam+0, 4, "UInt")
		sc := sc = 0x136 ? 0x36 : sc
		;key:=GetKeyName(Format("vk{1:x}sc{2:x}", vk,sc))
		event := wParam = WM_SYSKEYDOWN || wParam = WM_KEYDOWN
		
		if ( sc != 541 ){		; ignore non L/R Control. This key never happens except eg with RALT
			block := this._Callback.({ Type: "k", Code: sc, event: event, uid: "k" sc})
			if (block){
				return 1
			}
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)

	}
	
	; Process Mouse Hook messages
	_ProcessMHook(wParam, lParam){
		; MSLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644970(v=vs.85).aspx
		static WM_LBUTTONDOWN := 0x0201, WM_LBUTTONUP := 0x0202 , WM_RBUTTONDOWN := 0x0204, WM_RBUTTONUP := 0x0205, WM_MBUTTONDOWN := 0x0207, WM_MBUTTONUP := 0x0208, WM_MOUSEHWHEEL := 0x20E, WM_MOUSEWHEEL := 0x020A, WM_XBUTTONDOWN := 0x020B, WM_XBUTTONUP := 0x020C
		static button_map := {0x0201: 1, 0x0202: 1 , 0x0204: 2, 0x0205: 2, 0x0207: 3, 0x208: 3}
		static button_event := {0x0201: 1, 0x0202: 0 , 0x0204: 1, 0x0205: 0, 0x0207: 1, 0x208: 0}
		Critical
		
		if (this<0 || wParam = 0x200){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		out := "Mouse: " wParam " "
		
		keyname := ""
		event := 0
		button := 0
		
		;if (IsObject(this._MouseLookup[wParam])){
		if (ObjHasKey(button_map, wParam)){
			; L / R / M  buttons
			button := button_map[wParam]
			event := button_event[wParam]
		} else {
			; Wheel / XButtons
			; Find HiWord of mouseData from Struct
			mouseData := NumGet(lParam+0, 10, "Short")
			
			if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
				; Mouse Wheel - mouseData indicate direction (up/down)
				event := 1	; wheel has no up event, only down
				if (wParam = WM_MOUSEWHEEL){
					if (mouseData > 1){
						button := 6
					} else {
						button := 7
					}
				} else {
					if (mouseData < 1){
						button := 8
					} else {
						button := 9
					}
				}
			} else if (wParam = WM_XBUTTONDOWN || wParam = WM_XBUTTONUP){
				; X Buttons - mouseData indicates Xbutton 1 or Xbutton2
				if (wParam = WM_XBUTTONDOWN){
					event := 1
				} else {
					event := 0
				}
				button := 3 + mouseData
			}
		}

		block := this._Callback.({Type: "m", Code: button, event: event, uid: "m" button})
		if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
			; Mouse wheel does not generate up event, simulate it.
			this._Callback.({Type: "m", Code: button, event: 0, uid: "m" button})
		}
		if (block){
			return 1
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
	}
	
	; ============= HOOK HANDLING =================
	_SetWindowsHookEx(idHook, pfn){
		Return DllCall("SetWindowsHookEx", "Ptr", idHook, "Ptr", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0, "Ptr"), "Uint", 0, "Ptr")
	}
	
	_UnhookWindowsHookEx(idHook){
		Return DllCall("UnhookWindowsHookEx", "Ptr", idHook)
	}

}
	
And the fix: Replace sleep with a SetTimer

Code: Select all

#SingleInstance force
#Persistent

OutputDebug DBGVIEWCLEAR

mc := new MyClass()
return

class MyClass {
	__New(){
		this.CInputDetector := new CInputDetector(this._ProcessInput.Bind(this), opt)
	}
	
	_ProcessInput(aParams*){

	}
}

;------------------------------------------------------------------------------------------------------------
; Sets up the hooks etc to watch for input
;------------------------------------------------------------------------------------------------------------
class CInputDetector {
	__New(callback, options := 0){
		if (options = 0){
			options := {}
		}
		this.options := options
		this._HooksEnabled := 0
		this._Callback := callback
		this._JoyUpFuncs := []
		
		this.EnableHooks()
	}
	
	EnableHooks(){
		static WH_KEYBOARD_LL := 13, WH_MOUSE_LL := 14
		
		if (this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 1

		; Hook Input
		this._hHookKeybd := this._SetWindowsHookEx(WH_KEYBOARD_LL, RegisterCallback(this._ProcessKHook,"Fast",,&this))
		this._hHookMouse := this._SetWindowsHookEx(WH_MOUSE_LL, RegisterCallback(this._ProcessMHook,"Fast",,&this))
		
		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; watch buttons
				Loop % 32 {
					fn := this._ProcessJHook.Bind(this, joyid, A_Index)
					hotkey, % joyid "Joy" A_Index, % fn
					hotkey, % joyid "Joy" A_Index, On
				}
				this._JoyUpFuncs[joyid] := []
				; disablejoystick option is useful when debugging, so you do not keep getting interrupted by the settimer.
				; Watch POVs
				if (instr(joyinfo, "p")){
					this._JoysticksWithHats.push(joyid)
				}
			}
		}
		if (this.options.disablejoystickhats != 1){
			fn := this._WatchJoystickPOV.Bind(this)
			SetTimer, % fn, 10
		}
		return 1
	}
	
	DisableHooks(){
		if (!this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 0

		this._UnhookWindowsHookEx(this._hHookKeybd)
		this._UnhookWindowsHookEx(this._hHookMouse)

		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; stop watching buttons
				Loop % 32 {
					hotkey, % joyid "Joy" A_Index, Off
				}
			}
		}
		fn := this._WatchJoystickPOV.Bind(this)
		SetTimer, % fn, Off
		return 1
	}
	
	; Process Joystick button down events
	_ProcessJHook(joyid, btn){
		;ToolTip % "Joy " joyid " Btn " btn
		this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 1, uid: joyid "j" btn})
		fn := this._WaitForJoyUp.Bind(this, joyid, btn)
		SetTimer, % fn, -0
	}
	
	; Emulate up events for joystick buttons
	_WaitForJoyUp(joyid, btn){
		fn := this.__WaitForJoyUp.Bind(this, joyid, btn)
		this._JoyUpFuncs[joyid, btn] := fn
		SetTimer, % fn, 10
	}
	
	__WaitForJoyUp(joyid, btn){
		str := joyid "Joy" btn
		if (!GetKeyState(str)){
			fn := this._JoyUpFuncs[joyid, btn]
			SetTimer, % fn, Off
			this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 0, uid: joyid "j" btn})
		}
		
	}
	
	; A constantly running timer to emulate "button events" for Joystick POV directions (eg 2JoyPOVU, 2JoyPOVD...)
	_WatchJoystickPOV(){
		ToolTip % "WATCHPOV " A_TickCount
		static pov_states := [-1, -1, -1, -1, -1, -1, -1, -1]
		static pov_strings := ["1JoyPOV", "2JoyPOV", "3JoyPOV", "4JoyPOV", "5JoyPOV", "6JoyPOV" ,"7JoyPOV" ,"8JoyPOV"]
		static pov_direction_map := [[0,0,0,0], [1,0,0,0], [1,1,0,0] , [0,1,0,0], [0,1,1,0], [0,0,1,0], [0,0,1,1], [0,0,0,1], [1,0,0,1]]
		static pov_direction_states := [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]
		Loop % this._JoysticksWithHats.length() {
			joyid := this._JoysticksWithHats[A_Index]
			pov := GetKeyState(pov_strings[joyid])
			if (pov = pov_states[joyid]){
				; do not process stick if nothing changed
				;OutputDebug % "WATCHPOV: Nothing Changed"
				continue
			}
			if (pov = -1){
				state := 1
			} else {
				state := round(pov / 4500) + 2
			}
			
			Loop 4 {
				if (pov_direction_states[joyid, A_Index] != pov_direction_map[state, A_Index]){
					OutputDebug % "_WatchJoystickPOV: Change!"
					this._Callback.({Type: "h", Code: A_Index, joyid: joyid, event: pov_direction_map[state, A_Index], uid: joyid "h" A_Index})
				}
			}
			pov_states[joyid] := pov
			pov_direction_states[joyid] := pov_direction_map[state]
		}
	}
	
	; Process Keyboard Hook messages
	_ProcessKHook(wParam, lParam){
		; KBDLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx
		; KeyboardProc function: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
		static WM_KEYDOWN := 0x100, WM_KEYUP := 0x101, WM_SYSKEYDOWN := 0x104
		Critical
		
		if (this<0){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		
		vk := NumGet(lParam+0, "UInt")
		Extended := NumGet(lParam+0, 8, "UInt") & 1
		sc := (Extended<<8)|NumGet(lParam+0, 4, "UInt")
		sc := sc = 0x136 ? 0x36 : sc
		;key:=GetKeyName(Format("vk{1:x}sc{2:x}", vk,sc))
		event := wParam = WM_SYSKEYDOWN || wParam = WM_KEYDOWN
		
		if ( sc != 541 ){		; ignore non L/R Control. This key never happens except eg with RALT
			block := this._Callback.({ Type: "k", Code: sc, event: event, uid: "k" sc})
			if (block){
				return 1
			}
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)

	}
	
	; Process Mouse Hook messages
	_ProcessMHook(wParam, lParam){
		; MSLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644970(v=vs.85).aspx
		static WM_LBUTTONDOWN := 0x0201, WM_LBUTTONUP := 0x0202 , WM_RBUTTONDOWN := 0x0204, WM_RBUTTONUP := 0x0205, WM_MBUTTONDOWN := 0x0207, WM_MBUTTONUP := 0x0208, WM_MOUSEHWHEEL := 0x20E, WM_MOUSEWHEEL := 0x020A, WM_XBUTTONDOWN := 0x020B, WM_XBUTTONUP := 0x020C
		static button_map := {0x0201: 1, 0x0202: 1 , 0x0204: 2, 0x0205: 2, 0x0207: 3, 0x208: 3}
		static button_event := {0x0201: 1, 0x0202: 0 , 0x0204: 1, 0x0205: 0, 0x0207: 1, 0x208: 0}
		Critical
		
		if (this<0 || wParam = 0x200){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		out := "Mouse: " wParam " "
		
		keyname := ""
		event := 0
		button := 0
		
		;if (IsObject(this._MouseLookup[wParam])){
		if (ObjHasKey(button_map, wParam)){
			; L / R / M  buttons
			button := button_map[wParam]
			event := button_event[wParam]
		} else {
			; Wheel / XButtons
			; Find HiWord of mouseData from Struct
			mouseData := NumGet(lParam+0, 10, "Short")
			
			if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
				; Mouse Wheel - mouseData indicate direction (up/down)
				event := 1	; wheel has no up event, only down
				if (wParam = WM_MOUSEWHEEL){
					if (mouseData > 1){
						button := 6
					} else {
						button := 7
					}
				} else {
					if (mouseData < 1){
						button := 8
					} else {
						button := 9
					}
				}
			} else if (wParam = WM_XBUTTONDOWN || wParam = WM_XBUTTONUP){
				; X Buttons - mouseData indicates Xbutton 1 or Xbutton2
				if (wParam = WM_XBUTTONDOWN){
					event := 1
				} else {
					event := 0
				}
				button := 3 + mouseData
			}
		}

		block := this._Callback.({Type: "m", Code: button, event: event, uid: "m" button})
		if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
			; Mouse wheel does not generate up event, simulate it.
			this._Callback.({Type: "m", Code: button, event: 0, uid: "m" button})
		}
		if (block){
			return 1
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
	}
	
	; ============= HOOK HANDLING =================
	_SetWindowsHookEx(idHook, pfn){
		Return DllCall("SetWindowsHookEx", "Ptr", idHook, "Ptr", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0, "Ptr"), "Uint", 0, "Ptr")
	}
	
	_UnhookWindowsHookEx(idHook){
		Return DllCall("UnhookWindowsHookEx", "Ptr", idHook)
	}
}
Last edited by evilC on 13 Sep 2015, 07:29, edited 5 times in total.
just me
Posts: 7107
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Why sleep freeze all timer !? Is possible add a delay in label?

13 Sep 2015, 07:16

Critical wrote:A critical thread becomes interruptible when a MsgBox or other dialog is displayed. However, unlike Thread Interrupt, the thread becomes critical again after the user dismisses the dialog.
...
The command Thread NoTimers is similar to Critical except that it only prevents interruptions from timers.
Edit: The script in the OP doesn't freeze QWE here.
User avatar
evilC
Posts: 4771
Joined: 27 Feb 2014, 12:30

Re: Why sleep freeze all timer !? Is possible add a delay in label?

13 Sep 2015, 07:40

Ah. It appears that the intermittent behavior was because of SCiTE.

Run the following code in debug mode in SCiTE, and notice how A_Critical is 0 (Even if there are no breakpoints). When not in debug mode, A_Critical is 16

Edit: Nope. Not totally to do with SCiTe it seems. If I double click the script, A_Critical is typically 0. If I right-click the icon and "Reload Script", A_Critical is generally 16 (But not always).

Edit 2: Solved. It seems that as I was using "Fast" mode for RegisterCallback, the callback was happening on the main thread, so when the hook processing code declared critical, it did so for the main thread. Simple fix was to remove Fast mode for the callback.
The intermittent behavior was due to the hooks - if you happened to move the mouse while launching the thread, it called the mouse hook, which made the main thread critical.

Code: Select all

#SingleInstance force
#Persistent

OutputDebug DBGVIEWCLEAR

mc := new MyClass()
return

class MyClass {
	__New(){
		this.CInputDetector := new CInputDetector(this._ProcessInput.Bind(this), opt)
	}
	
	_ProcessInput(aParams*){

	}
}

;------------------------------------------------------------------------------------------------------------
; Sets up the hooks etc to watch for input
;------------------------------------------------------------------------------------------------------------
class CInputDetector {
	__New(callback, options := 0){
		if (options = 0){
			options := {}
		}
		this.options := options
		this._HooksEnabled := 0
		this._Callback := callback
		
		this.EnableHooks()
	}
	
	EnableHooks(){
		static WH_KEYBOARD_LL := 13, WH_MOUSE_LL := 14
		
		if (this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 1

		; Hook Input
		this._hHookKeybd := this._SetWindowsHookEx(WH_KEYBOARD_LL, RegisterCallback(this._ProcessKHook,"Fast",,&this))
		this._hHookMouse := this._SetWindowsHookEx(WH_MOUSE_LL, RegisterCallback(this._ProcessMHook,"Fast",,&this))
		
		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; watch buttons
				Loop % 32 {
					fn := this._ProcessJHook.Bind(this, joyid, A_Index)
					hotkey, % joyid "Joy" A_Index, % fn
					hotkey, % joyid "Joy" A_Index, On
				}
				; disablejoystick option is useful when debugging, so you do not keep getting interrupted by the settimer.
				; Watch POVs
				if (instr(joyinfo, "p")){
					this._JoysticksWithHats.push(joyid)
				}
			}
		}
		if (this.options.disablejoystickhats != 1){
			fn := this._WatchJoystickPOV.Bind(this)
			SetTimer, % fn, 10
		}
		return 1
	}
	
	DisableHooks(){
		if (!this._HooksEnabled){
			return 1
		}
		
		this._HooksEnabled := 0

		this._UnhookWindowsHookEx(this._hHookKeybd)
		this._UnhookWindowsHookEx(this._hHookMouse)

		this._JoysticksWithHats := []
		Loop 8 {
			joyid := A_Index
			joyinfo := GetKeyState(joyid "JoyInfo")
			if (joyinfo){
				; stop watching buttons
				Loop % 32 {
					hotkey, % joyid "Joy" A_Index, Off
				}
			}
		}
		fn := this._WatchJoystickPOV.Bind(this)
		SetTimer, % fn, Off
		return 1
	}
	
	; Process Joystick button down events
	_ProcessJHook(joyid, btn){
		;ToolTip % "Joy " joyid " Btn " btn
		this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 1, uid: joyid "j" btn})
		fn := this._WaitForJoyUp.Bind(this, joyid, btn)
		SetTimer, % fn, -0
	}
	
	; Emulate up events for joystick buttons
	_WaitForJoyUp(joyid, btn){
		str := joyid "Joy" btn
		while (GetKeyState(str)){
			sleep 10
		}
		this._Callback.({Type: "j", Code: btn, joyid: joyid, event: 0, uid: joyid "j" btn})
	}
	
	; A constantly running timer to emulate "button events" for Joystick POV directions (eg 2JoyPOVU, 2JoyPOVD...)
	_WatchJoystickPOV(){
		ToolTip % "WATCHPOV " A_TickCount " Critical: " A_IsCritical
		static pov_states := [-1, -1, -1, -1, -1, -1, -1, -1]
		static pov_strings := ["1JoyPOV", "2JoyPOV", "3JoyPOV", "4JoyPOV", "5JoyPOV", "6JoyPOV" ,"7JoyPOV" ,"8JoyPOV"]
		static pov_direction_map := [[0,0,0,0], [1,0,0,0], [1,1,0,0] , [0,1,0,0], [0,1,1,0], [0,0,1,0], [0,0,1,1], [0,0,0,1], [1,0,0,1]]
		static pov_direction_states := [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]
		Loop % this._JoysticksWithHats.length() {
			joyid := this._JoysticksWithHats[A_Index]
			pov := GetKeyState(pov_strings[joyid])
			if (pov = pov_states[joyid]){
				; do not process stick if nothing changed
				;OutputDebug % "WATCHPOV: Nothing Changed"
				continue
			}
			if (pov = -1){
				state := 1
			} else {
				state := round(pov / 4500) + 2
			}
			
			Loop 4 {
				if (pov_direction_states[joyid, A_Index] != pov_direction_map[state, A_Index]){
					OutputDebug % "_WatchJoystickPOV: Change!"
					this._Callback.({Type: "h", Code: A_Index, joyid: joyid, event: pov_direction_map[state, A_Index], uid: joyid "h" A_Index})
				}
			}
			pov_states[joyid] := pov
			pov_direction_states[joyid] := pov_direction_map[state]
		}
	}
	
	; Process Keyboard Hook messages
	_ProcessKHook(wParam, lParam){
		; KBDLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx
		; KeyboardProc function: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
		static WM_KEYDOWN := 0x100, WM_KEYUP := 0x101, WM_SYSKEYDOWN := 0x104
		Critical
		
		if (this<0){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		
		vk := NumGet(lParam+0, "UInt")
		Extended := NumGet(lParam+0, 8, "UInt") & 1
		sc := (Extended<<8)|NumGet(lParam+0, 4, "UInt")
		sc := sc = 0x136 ? 0x36 : sc
		;key:=GetKeyName(Format("vk{1:x}sc{2:x}", vk,sc))
		event := wParam = WM_SYSKEYDOWN || wParam = WM_KEYDOWN
		
		if ( sc != 541 ){		; ignore non L/R Control. This key never happens except eg with RALT
			block := this._Callback.({ Type: "k", Code: sc, event: event, uid: "k" sc})
			if (block){
				return 1
			}
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookKeybd, "int", this, "Uint", wParam, "Uint", lParam)

	}
	
	; Process Mouse Hook messages
	_ProcessMHook(wParam, lParam){
		; MSLLHOOKSTRUCT structure: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644970(v=vs.85).aspx
		static WM_LBUTTONDOWN := 0x0201, WM_LBUTTONUP := 0x0202 , WM_RBUTTONDOWN := 0x0204, WM_RBUTTONUP := 0x0205, WM_MBUTTONDOWN := 0x0207, WM_MBUTTONUP := 0x0208, WM_MOUSEHWHEEL := 0x20E, WM_MOUSEWHEEL := 0x020A, WM_XBUTTONDOWN := 0x020B, WM_XBUTTONUP := 0x020C
		static button_map := {0x0201: 1, 0x0202: 1 , 0x0204: 2, 0x0205: 2, 0x0207: 3, 0x208: 3}
		static button_event := {0x0201: 1, 0x0202: 0 , 0x0204: 1, 0x0205: 0, 0x0207: 1, 0x208: 0}
		Critical
		
		if (this<0 || wParam = 0x200){
			Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
		}
		this:=Object(A_EventInfo)
		out := "Mouse: " wParam " "
		
		keyname := ""
		event := 0
		button := 0
		
		;if (IsObject(this._MouseLookup[wParam])){
		if (ObjHasKey(button_map, wParam)){
			; L / R / M  buttons
			button := button_map[wParam]
			event := button_event[wParam]
		} else {
			; Wheel / XButtons
			; Find HiWord of mouseData from Struct
			mouseData := NumGet(lParam+0, 10, "Short")
			
			if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
				; Mouse Wheel - mouseData indicate direction (up/down)
				event := 1	; wheel has no up event, only down
				if (wParam = WM_MOUSEWHEEL){
					if (mouseData > 1){
						button := 6
					} else {
						button := 7
					}
				} else {
					if (mouseData < 1){
						button := 8
					} else {
						button := 9
					}
				}
			} else if (wParam = WM_XBUTTONDOWN || wParam = WM_XBUTTONUP){
				; X Buttons - mouseData indicates Xbutton 1 or Xbutton2
				if (wParam = WM_XBUTTONDOWN){
					event := 1
				} else {
					event := 0
				}
				button := 3 + mouseData
			}
		}

		block := this._Callback.({Type: "m", Code: button, event: event, uid: "m" button})
		if (wParam = WM_MOUSEHWHEEL || wParam = WM_MOUSEWHEEL){
			; Mouse wheel does not generate up event, simulate it.
			this._Callback.({Type: "m", Code: button, event: 0, uid: "m" button})
		}
		if (block){
			return 1
		}
		Return DllCall("CallNextHookEx", "Uint", Object(A_EventInfo)._hHookMouse, "int", this, "Uint", wParam, "Uint", lParam)
	}
	
	; ============= HOOK HANDLING =================
	_SetWindowsHookEx(idHook, pfn){
		Return DllCall("SetWindowsHookEx", "Ptr", idHook, "Ptr", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0, "Ptr"), "Uint", 0, "Ptr")
	}
	
	_UnhookWindowsHookEx(idHook){
		Return DllCall("UnhookWindowsHookEx", "Ptr", idHook)
	}

}
	
dsewq1LYJ
Posts: 114
Joined: 26 Aug 2014, 23:21

Re: Why sleep freeze all timer !? Is possible add a delay in label?

13 Sep 2015, 09:30

I did realized I misunderstanded about "Threads"
Just thinking it's all unlimited threads and totally works parallelly. (Lol...I am pretty stupid...)

Anyway ! Deeply thanks for all replies.
I've learned a lot today!

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], DonaldKenneth, HooptyDoopty and 320 guests