GUI Script Occasionally Goes Haywire, Takes Over Computer

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
tmbutcher
Posts: 2
Joined: 27 Jul 2021, 10:37

GUI Script Occasionally Goes Haywire, Takes Over Computer

27 Jul 2021, 11:09

I've been working on a script (copied below) for a little while now that will display GUI images of a keyboard overlaid with various special or accented characters depending on the combination pressed. The intention is to create a visual guide for a different script that will actually send those characters. The script is significantly based off the one that DreymaR uses in his Big Bag of Keyboard Tricks. For the most part the script works really well, but occasionally it goes haywire and creates a zillion threads, slowing my computer to the point that it becomes totally unusable and I have to perform a hard reset. When this happens, I almost always also get a bunch of popup boxes saying that the previous instance of the script couldn't be closed, and asking if I want to wait. (Clicking no doesn't really do anything, even if I manage to do it on all of the 100+ boxes that show up.)

I hope I've described the problem sufficiently, but please let me know if anything is confusing or if you have any questions. I can also provide images of anything if it would help, or if you're just curious.

Obviously this is undesired behavior! Ideally, I'd like to find the source of the problem and keep it from happening. However, if there's a way to simply limit the damage that such a problem can cause—for example, by limiting the number of instances that can exist simultaneously—then that would be great also. But I'll readily admit that I haven't ever really wrapped my mind around how GUI scripts work. I never would have been able to get this one going if it wasn't rooted in the work of DreymaR, linked above, and then hacked for my own purposes. Anyway, please let me know if you have any suggestions, and thank you in advance!

(Incidentally, if any of you are thinking about switching to Colemak or some other non-QWERTY keyboard layout, I highly recommend checking out DreymaR's work!)

Here's the 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.
#InstallKeybdHook
#NoTrayIcon ;As it says.

; TODO: fix issue with alt in deadkey mode
; TODO: find a way to make image program know whether or not I want to exit after a dk
; TODO: find a way to toggle the image on or off? (esp. for extend)

; TODO: might want to decouple this more thoroughly from EPKL (since alt = state 6 doesn't really make sense here); could also use more streamlined file paths

global dk
global dkStop
global dkF23Wait := 0

~Esc::Reload
^Esc::ExitApp

~F23::						; Alt/dk images
	Ext := false
	Emo := false
	helpImage()
	Reload

~F22::						; Ext images
	Ext := true
	Emo := false
	helpImage()
	Reload

~F21::						; Emoji images
	Ext := false
	Emo := true
	helpImage(1)
	Reload

#if (!dk && !Ext && !Emo)
~`::dk := "Grave-Sub"
~~::dk := "Tilde"
~1::dk := "DotBelow"
~2::dk := "Hook-Palatal"
~3::dk := "Horn-Tail"
~4::dk := "Currency"
~5::dk := "Greek"
~6::dk := "Circumflex"
~7::dk := "Caron"
~8::dk := "Ogonek-CmBel"
~9::dk := "Breve"
~(::dk := "Macron"
~0::dk := "RingAbov-Lig"
~=::dk := "DblAcute-Sci"
~e::dk := "Acute-Sup"
~u::dk := "Umlaut"
~,::dk := "Cedilla"
~.::dk := "DotAbove"
~/::dk := "Stroke-Bar"
#if

#if dk && !dkF23Wait
~F23 Up::dkF23Wait := 1
#if

#if (dk && dkStop)
~1::
~2::
~3::
~4::
~5::
~6::
~7::
~8::
~9::
~0::
~-::
~=::
~q::
~w::
~f::
~p::
~b::
~j::
~l::
~u::
~y::
~;::
~[::
~]::
~\::
~a::
~r::
~s::
~t::
~g::
~m::
~n::
~e::
~i::
~o::
~'::
~x::
~c::
~d::
~v::
~z::
~k::
~h::
~,::
~.::
~/::
~Esc::
~Enter::
~Tab::
	dkStop := 2
#if

; F21::Msgbox F21 Pressed
; F21 Up::Msgbox F21 Released

helpImage(kb:=2) {
	; static kb
	static imgPath
	static imgBg
	static imgShPath
	static imgSh
	static imgDkPath
	static imgExtPath
	static imgEmoPath
	static imgZoom
	static imgX 						; Keep these for %var% use with the Gui commands
	static imgY
	static imgW
	static imgH
	static initialized  := false
	static state

	if !initialized {
		imgZoom := .667
		SysGet, scrSize, MonitorWorkArea
		if (kb == 2) {		; Space Invader KB
			imgPath := "Layouts\SpaceInvader\SPA-eD_ANS"
			imgBg := "Files\ImgBackground\Bg_SpaceInvader.png"
			imgShPath := ""
			imgDkPath := imgPath . "\DeadkeyImg"
			ImgExtPath := "Files\ImgExtend\ANS-CAWide_Ext"
			ImgEmoPath := "Files\ImgEmoji\ANS-CAWide_Emo_s"
			imgW := 842 * A_ScreenWidth/3840*2 * imgZoom
			imgH := 386 * A_ScreenWidth/3840*2 * imgZoom
		} else {			; Regular KB
			imgPath := "Layouts\Colemak\Cmk-eD_ANS_CurlAngle"
			imgBg := "Files\ImgBackground\Bg_ANS_FingerShui-AWide-eD.png"
			imgShPath := "Files\ImgModStates\GrnBlob"
			imgDkPath := imgPath . "\DeadkeyImg"
			ImgExtPath := "Files\ImgExtend\ANS-CAWide_Ext"
			ImgEmoPath := "Files\ImgEmoji\ANS-CAWide_Emo_s"
			imgW := 812 * A_ScreenWidth/3840*2 * imgZoom
			imgH := 282 * A_ScreenWidth/3840*2 * imgZoom
		}
		imgX := ( A_ScreenWidth - imgW )/2
		imgY := scrSizeBottom - imgH
		initialized  := true
		dk := ""
		dkStop := 0
	}

	state := GetState()
	if (state < 8) 										; If we're drawing Alt/dk images
		imgKy := imgPath . "\state" . state . ".png"
	else if (state < 18) {								; If we're drawing Ext images
		state -= 7										; Drop the Ext multiplier out, because Ext images are 1-2-3
		imgKy := imgExtPath . state . ".png"
		state += 7
	}
	else {												; If we're drawing Emoji images
		state -= 18										; Drop the Emo multiplier out, because Emo images are 0-1
		imgKy := imgEmoPath . state . ".png"
		state += 18
	}
	draw(imgShPath, imgW, imgH, imgX, imgY, imgKy, imgBg, state)
	
	loop {														; Main holding loop
		oldState := state
		state := GetState()

		if (dk) {
			state := 0
			imgKy := imgDkPath . "\" . dk . "_s" . state . ".png"
			draw(imgShPath, imgW, imgH, imgX, imgY, imgKy, imgBg, state)
			dkF23Wait := 1
			break
		}
		if (state = 0) {
			break
		}
		if (state != oldState) {
			if (state < 8) 										; If we're using Alt-combos
				imgKy := imgPath . "\state" . state . ".png"
			else if (state < 18) {
				state -= 7										; Drop the Ext multiplier out, because Ext images are 1-2-3
				imgKy := imgExtPath . state . ".png"
				state += 7
			} else {
				state -= 18										; Drop the Emoji multiplier out, because Emoji images are 0-1
				imgKy := imgEmoPath . state . ".png"
				state += 18
			}

			draw(imgShPath, imgW, imgH, imgX, imgY, imgKy, imgBg, state)
		}
		Sleep 20
	}		

	if (dk)	{												; deadkey holding loop
		oldState := 6										; set manually, because for some reason there's a problem with alt-detection here
		dkStop := 1											; allows regular keys to turn off the image (needed so that alt doesn't have to be held)
		loopCount := 0
		loop {
			state := GetState()
			if (state != oldState) {						; if the state has changed
				if (dkF23Wait = 1 && loopCount < 25)  {		; ignore the first time a state changes (should be releasing alt) if within the first 500ms
					dkF23Wait := 2
					oldState := GetState()
				} else {									; activated each subsequent state change (or any change after 500ms)
					imgKy := imgDkPath . "\" . dk . "_s" . state . ".png"
					draw(imgShPath, imgW, imgH, imgX, imgY, imgKy, imgBg, state)
					oldState := state
				}
				
			}

			if (dkStop = 2) {
				break
			}

			Sleep, 20
			loopCount += 1
		}
	}
	
	dkStop := 0
	dk := ""
	dkF23Wait := 0
	GUI, HI:hide

	Return
}

draw(imgShPath, imgW, imgH, imgX, imgY, imgKy, imgBg, state)
{
	static CtrlKyImg 					; Image control handle
	static CtrlBgImg 					; --"--
	static CtrlShImg 					; --"--
	GUI, HI:New, +AlwaysOnTop -Border -Caption +ToolWindow 			; Create a GUI for the help images
				+LastFound +Owner 			; Owner removes the task bar button?
	GUI, HI:Margin, 0, 0
	GUI, HI:Color, 333333
	WinSet, TransColor, 333333 200		;Transparent, 255
	GUI, HI:Add, Pic, xm +BackgroundTrans vCtrlBgImg AltSubmit 	; Make image controls stored in Help##### variables
	GUI, HI:Add, Pic, xm +BackgroundTrans vCtrlKyImg AltSubmit
	GUI, HI:Add, Pic, xm +BackgroundTrans vCtrlShImg AltSubmit

	imgSh := imgShPath . "\state" . state . ".png"
	GuiControl, HI:, CtrlBgImg, *w%imgW% *h%imgH% %imgBg%
	GuiControl, HI:, CtrlKyImg, *w%imgW% *h%imgH% %imgKy%
	GuiControl, HI:, CtrlShImg, *w%imgW% *h%imgH% %imgSh%
	GUI, HI: Show, x%imgX% y%imgY% AutoSize NA 
	Return
}

GetState()
{
	state := 0

	if (GetKeyState("F22")) {						; If Ext is pressed
		state += 8					
		state += 1 * getKeyState("F20")				; aka Ext + "Shift"
		state += 2 * getKeyState("F19")				; aka Ext + "Alt"
	} else {										; Don't want shift or alt to change the img for Extend
		state += 1 * getKeyState("Shift")				
		state += 2 * getKeyState("Alt")				; Probably will need to change to LAlt if I change F23 back to RAlt
	}

	state += 6 * getKeyState("F23")					; aka RAlt; WIP: For some reason RAlt isn't working properly in the DK loop, but it doesn't really matter until I get multi-DKs running
	state += 18 * GetKeyState("F21")					; aka Emoji


;	state += 2 * getKeyState("Ctrl")
;	state += 6 * getLayInfo( "LayHasAltGr" ) * AltGrIsPressed()
	Return state
}
User avatar
mikeyww
Posts: 26852
Joined: 09 Sep 2014, 18:38

Re: GUI Script Occasionally Goes Haywire, Takes Over Computer

27 Jul 2021, 11:40

I'm not sure, but part of the question is probably, "How do I go about debugging my script?", so a few suggestions are below.

1. Remove #NoTrayIcon until fixed.
2. While testing, pare down the script to essential code; expand later when working.
3. Display results of conditional statements & function calls.
4. If possible, remove some of the loops while you are testing.

If you press a hotkey while one of your loops is active, you might get unexpected results, because the hotkey routine will interrupt the active thread.
tmbutcher
Posts: 2
Joined: 27 Jul 2021, 10:37

Re: GUI Script Occasionally Goes Haywire, Takes Over Computer

28 Jul 2021, 14:10

Oh yes, all of those are good suggestions, which I try to follow as much as possible. Unfortunately, in this particular case debugging is quite difficult, because I am not able to replicate the bug on demand. It occurs naturally every few weeks, but I haven't been able to figure out what the specific trigger is that causes the script to go haywire.
braunbaer
Posts: 478
Joined: 22 Feb 2016, 10:49

Re: GUI Script Occasionally Goes Haywire, Takes Over Computer

28 Jul 2021, 15:59

Send debug output as complete as possible to a logfile. when the crash has happened, you can examine the log. You will probably need several crashes to isolate the source of the error.
User avatar
mikeyww
Posts: 26852
Joined: 09 Sep 2014, 18:38

Re: GUI Script Occasionally Goes Haywire, Takes Over Computer

28 Jul 2021, 17:38

I noticed that your #if (dk && dkStop) block has no terminating Return. This might have no adverse effect, but I have not tested it. It is probably unrelated to your issue, but I thought I would mention it anyway.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Billykid, Chunjee, Giresharu, icyolive, inseption86, Swiftly9767 and 279 guests