Autohotkey 3 clicks = volume mute - autohotkey

In autohotkey im trying to make it so that when I press the left mouse button 3 times with a delay of +/- 10 ms it becomes a volume mute
LButton::
if (?)
{
Send, Volume_Mute
}
else
{
Send, LButton
}
Return

Use A_TickCount to read current time in milliseconds and then calculate the delay between clicks. See Date and Time
ms := A_TickCount
N := 3 ; number of clicks
T := 500 ; max delay between clicks, ms
clicks := 0
~lbutton::
msx := A_TickCount ; get current time
d := msx - ms ; get time past
ms := msx ; remember current time
if (d < T)
clicks += 1
else
clicks := 1
if (clicks >= N)
{
; tooltip %N%-click detected
send {Volume_Mute}
clicks := 0
}
return

Each Autohotkey Script (example.Ahk) that you will run in a loop (running in the background), these loops will repeating in a count off frequence ?...ms (milliseconds)
If you want to use a delay from +- 10ms you will need to change the Timer. (Default = +-250ms)
With the Autohotkey Command (SetTimer) you can change that.
(ps- +-10 ms is very fast i recommend to use a lower Time frequence)
In the Line (SetTimer, CountClicks, 100) you can change(optimize) the number 100. (so that it works fine on your system.)
Note: you can remove the line (msgbox) this is only to show visual how many times you did click.
Try this code:
#NoEnv
#SingleInstance force
;#NoTrayIcon
a1 := -1
b1 := 0
esc::exitapp ;You can click the (esc) key to stop the script.
;if you use ~ it will also use the default function Left-Button-Click.
;and if you Click the Left Mouse Button 3x times, it will Execute Ahk Code Part 3
~LButton::
if(a1 = -1)
{
a1 := 4
#Persistent
SetTimer, CountClicks, 100
}
else
{
a1 := 3
}
return
CountClicks:
if(a1 = 3)
{
b1 := b1 + 1
}
if(a1 = 0)
{
msgbox you did Click <LButton> Key > %b1%x times
if (b1=1)
{
;if Click 1x - Then Execute Ahk Code Part 1
;Here you can put any code for Part 1
}
if (b1=2)
{
;if Click 2x - Then Execute Ahk Code Part 2
;Here you can put any code for Part 2
}
if (b1=3)
{
;if Click 3x - Then Execute Ahk Code Part 3
;Here you can put any code for Part 3
Send {Volume_Mute} ;Send, Volume_Mute
}
if (b1=4)
{
;if Click 4x - Then Execute Ahk Code Part 4
;Here you can put any code for Part 4
}
b1 := 0
SetTimer, CountClicks , off
reload ; restart script
}
a1 := a1 - 1
return
I did test it out on a Windows 10 System and it works.

Related

Call to nonexistent function AHKv2

I am trying to create a simple InputBox pop-up that asks for an hour:minute combo, and then starts a countdown timer. When the timer reaches zero, it activates a window and then sends input into that window. I have followed the documentation and am still met with the same error over and over Call to nonexistent function. My script used to be far more complicated, but in an effort to troubleshoot, it has gotten progressively simpler and simpler ... and now it basically mirrors the documentation -- and yet, I still get this error. Fresh eyes on this would be greatly appreciated!
:*:obstimer::
;; Show the input box
USER_INPUT := InputBox("This is the prompt","This is the title",W200 H300 T30,"").value
;; Split the input time into hours and minutes
TimeArray := StrSplit(USER_INPUT, ":")
hours := TimeArray[1] ; Get the hours part
minutes := TimeArray[2] ; Get the minutes part
;; Convert the hours and minutes to seconds
loop_timer = (hours * 3600) + (minutes * 60)
;; Set a timer that will trigger the press_backtick function every 1000 milliseconds (1 second)
SetTimer, press_backtick, 1000
return
press_backtick:
loop_timer-- ; Decrement the loop timer by 1
if (loop_timer <= 0) { ; If the loop timer is less than or equal to 0
WinActivate, ahk_exe obs64.exe ; Activate the obs64.exe window
ControlSend, , {U+0060}, ahk_exe obs64.exe ; Send the backtick character ` to the obs64.exe window
SetTimer, press_backtick, off ; Turn off the timer
}
return
Your syntax is so far from being valid AHKv2 syntax, that the AHK script launcher deduces your script as being AHKv1, and therefore you get the error for a non existent function. (A function InputBox doesn't exist in AHKv1, it's a legacy command there)
Here's the script in AHKv2 syntax, there are loads of changes, and you also had some errors which wouldn't have worked even in v1.
I'd recommend you to go through v2 changes before trying to write v2 code (assuming you have a v1 background). Or if this is your first time writing AHK, be sure to look at v2 examples and documentations.
:*:obstimer::
{
global loop_timer
USER_INPUT := InputBox("This is the prompt", "This is the title", "W200 H300 T30", "").value
TimeArray := StrSplit(USER_INPUT, ":")
hours := TimeArray[1]
minutes := TimeArray[2]
loop_timer := (hours * 3600) + (minutes * 60)
SetTimer(press_backtick, 1000)
}
press_backtick()
{
global loop_timer
loop_timer-- ; Decrement the loop timer by 1
if (loop_timer <= 0) {
WinActivate("ahk_exe obs64.exe")
ControlSend("{U+0060}", , "ahk_exe obs64.exe")
SetTimer(press_backtick, 0)
}
}

Script in AutoHotkey to Press Windows button and Left Arrow simultaneously

I want to write a script in autohotkey so that every time I open my dictionary application on PC, keys Windows+LeftArrow being pressed at the same time and as the result, it snaps the windows on the left side of monitor.
I tried this:
#IfWinActive Oxford Advanced Learner's Dictionary
Send, #{Left}
return
Also this one:
#IfWinActive Oxford Advanced Learner's Dictionary
Send, {LWinDown}{Left}{LWinup}
return
But for either of them noting happened when I opened the application.
EDIT:
As suggested by #Charlie Armstrong the real question is: How do I make a block of code run every time I start a certain program? So #IfWinActive might not be useful for.
One way is periodically check if new process/window is created and to check if that is a process/window we want to interact with.
This first example is based on COM notifications when a process has been created/destroyed.
; help for question: https://stackoverflow.com/q/66394326/883015
; by joedf (16:04 2021/02/28)
MyWatchedWindowTitle := "Oxford Advanced Learner's Dictionary"
NewProcess_CheckInterval := 1 ; in seconds
SetTitleMatchMode, 2 ;this might not be needed, makes the check for "contains" instead of "same" winTitle
hWnds := []
gosub, initialize_NewProcessNotification
return
; Called when a new process is detected
On_NewProcess(proc) {
global hWnds
global MyWatchedWindowTitle
; get the window handle, if possible
if (hwnd:=WinExist("ahk_pid " proc.ProcessID)) {
WinGetTitle, wTitle, ahk_id %hwnd%
; check if there is a visible window
if (wTitle)
{
; if so, check if it's a window we want to interact with
if (InStr(wTitle,MyWatchedWindowTitle))
{
; check if we've interacted with this specific window before
if (!ArrayContains(hWnds, hwnd)) {
; we havent, so we do something with it
hWnds.push(hwnd) ; keep in memory that we have interacted with this window ID before.
DoSomething(hwnd) ; the keys we want to send to it
}
}
}
}
}
DoSomething(hwnd) {
; size and move window to the left
SysGet, MonitorWorkArea, MonitorWorkArea
posY := 0
posX := 0
width := A_ScreenWidth // 2
height := MonitorWorkAreaBottom
WinMove, ahk_id %hwnd% ,,%posX%,%posY%,%width%,%height%
; multi-montitor support, more examples, and more complete snapping functions can be found here:
; https://gist.github.com/AWMooreCO/1ef708055a11862ca9dc
}
ArrayContains(haystack, needle) {
for k, v in haystack
{
if (v == needle)
return true
}
return false
}
initialize_NewProcessNotification:
;////////////////////////////// New Process notificaton ////////////////////////
; from Lexikos' example
; https://autohotkey.com/board/topic/56984-new-process-notifier/#entry358038
; Get WMI service object.
winmgmts := ComObjGet("winmgmts:")
; Create sink objects for receiving event noficiations.
ComObjConnect(createSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessCreate_")
ComObjConnect(deleteSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessDelete_")
; Set event polling interval, in seconds.
interval := NewProcess_CheckInterval
; Register for process creation notifications:
winmgmts.ExecNotificationQueryAsync(createSink
, "Select * from __InstanceCreationEvent"
. " within " interval
. " where TargetInstance isa 'Win32_Process'")
; Register for process deletion notifications:
winmgmts.ExecNotificationQueryAsync(deleteSink
, "Select * from __InstanceDeletionEvent"
. " within " interval
. " where TargetInstance isa 'Win32_Process'")
; Don't exit automatically.
#Persistent
return
; Called when a new process is detected:
ProcessCreate_OnObjectReady(obj) {
proc := obj.TargetInstance
/*
TrayTip New Process Detected, % "
(LTrim
ID:`t" proc.ProcessID "
Parent:`t" proc.ParentProcessID "
Name:`t" proc.Name "
Path:`t" proc.ExecutablePath "
Command line (requires XP or later):
" proc.CommandLine
)
*/
On_NewProcess(proc)
}
; Called when a process terminates:
ProcessDelete_OnObjectReady(prm) {
/*
obj := COM_DispGetParam(prm, 0, 9)
proc := COM_Invoke(obj, "TargetInstance")
COM_Release(obj)
TrayTip Process Terminated, % "
(LTrim
ID:`t" COM_Invoke(proc, "Handle") "
Name:`t" COM_Invoke(proc, "Name")
)
COM_Release(proc)
*/
}
This second example, which is perhaps a bit simpler, checks periodically for new windows that match the searched WinTitle.
; help for question: https://stackoverflow.com/q/66394326/883015
; by joedf (16:17 2021/02/28)
#Persistent
MyWatchedWindowTitle := "Oxford Advanced Learner's Dictionary"
SetTitleMatchMode, 2 ;this might not be needed, makes the check for "contains" instead of "same" winTitle
SetTimer, checkForNewWindow, 1000 ;ms
hWnds := []
return
checkForNewWindow() {
global hWnds
global MyWatchedWindowTitle
; first check if there is at least one window that matches our winTitle
if (hwnd:=WinExist(MyWatchedWindowTitle)) {
; get all window matches
WinGet, wArray, List , %MyWatchedWindowTitle%
; loop through all windows that matched
loop % wArray
{
hWnd := wArray%A_Index%
; check if we've interacted with this specific window before
if (!ArrayContains(hWnds, hwnd)) {
; we havent, so we do something with it
hWnds.push(hwnd) ; keep in memory that we have interacted with this window ID before.
DoSomething(hwnd) ; the keys we want to send to it
}
}
}
}
DoSomething(hwnd) {
; size and move window to the left
SysGet, MonitorWorkArea, MonitorWorkArea
posY := 0
posX := 0
width := A_ScreenWidth // 2
height := MonitorWorkAreaBottom
WinMove, ahk_id %hwnd% ,,%posX%,%posY%,%width%,%height%
; multi-montitor support, more examples, and more complete snapping functions can be found here:
; https://gist.github.com/AWMooreCO/1ef708055a11862ca9dc
}
ArrayContains(haystack, needle) {
for k, v in haystack
{
if (v == needle)
return true
}
return false
}
I think your biggest issue is that AHK doesn't seem to work well for snapping windows (according to my quick research and testing). What does work well, though, is WinMove.
I assume you're launching the program from a shortcut icon, but I would suggest using a keyboard shortcut that launches the program and then positions the window from the script. Here is some sample code that opens Notepad2.exe, waits for 200 milliseconds, and then moves the window and resizes it:
^+!n:: ; Control+Shift+Alt+N to Open Notepad
Run C:\Program Files\Notepad2\Notepad2.exe
sleep, 200
WinMove, Notepad2,, 10, 20, 800, 600
return

while loop not executing correctly when toggled on/off

Goals:
Run ahk script so that a window stays active. When the user clicks off the window it immediately becomes active again.
This is so an overlay (considered its own window) can be used in a game and that if the overlay is clicked on by accident the game window will become the active window again.
I would also like this to be able to be turned on and off during game play so that the user can alt+tab if necessary.
Problems:
I'm testing my code implementation, so far i have it set up to make a blank notepad file become the active window and stay active.
The problem is the toggle (ctrl+alt+J). I can toggle the code it off just fine but when i toggle it on the window doesn't become active again.
Code:
stop := 0
; 0 = off, 1 = on
while (stop = 0)
{
IfWinNotActive, Untitled - Notepad
{
WinActivate, Untitled - Notepad
}
}
return
^!j::
stop := !stop
if (stop = 0){
MsgBox, stop is off.
}
else{
MsgBox, stop is on.
}
return
The reason it doesn't work after you toggle it off is that While only runs until is evaluates to false. Even if what it would evaluate later becomes true again, it won't restart.
Here's what you can do to make your current code work:
stop := 0
; 0 = off, 1 = on
labelWinCheck: ; label for GoSub to restart while-loop
while (stop = 0)
{
IfWinNotActive, Untitled - Notepad
{
WinActivate, Untitled - Notepad
}
Sleep , 250 ; added sleep (250 ms) so CPU isn't running full blast
}
return
^!j::
stop := !stop
if (stop = 0){
MsgBox, stop is off.
} else {
MsgBox, stop is on.
}
GoSub , labelWinCheck ; restarts while-loop
return
There are a couple of different ways that I would look at to achieve your goal.
Easy: use SetTimer instead of While.
stop := 0
SetTimer , labelWinCheck , 250 ; Repeats every 250 ms
labelWinCheck:
If !WinActive( "Untitled - Notepad" )
WinActivate , Untitled - Notepad
Return
^!j::
SetTimer , labelWinCheck , % ( stop := !stop ) ? "off" : "on"
MsgBox , % "stop is " . ( stop ? "on" : "off" )
Return
Advanced: us OnMessage() to monitor WinActivate events. I don't have a working example of this as that would take a bit of research for me, but here is a link for a solution I made to monitor keyboard events, Log multiple Keys with AutoHotkey. The links at the bottom may especially prove useful.

pixelsearch and click right pixel

IF NOT A_IsAdmin ; Runs script as Admin.
{
Run *RunAs "%A_ScriptFullPath%"
ExitApp
}
#MaxThreadsPerHotkey, 2
CoordMode, Pixel, Screen
#singleInstance, Force
toggle = 0
upperLeftX := 750
upperLeftY := 400
lowerRightX := 850
lowerRightY := 500
F8:: ; press F8 to toggle the loop on/off.
SoundBeep
Toggle := !Toggle
While Toggle
{ ;-- Begin of loop.
PixelSearch, X, Y,%upperLeftX%, %upperLeftY%, %lowerRightX%, %lowerRightY%, 0x000000, 0, Fast RGB
IF ErrorLevel = 1 ; IF NOTFound.
{
sleep, 100
}
IF ErrorLevel = 0 ; IF Found.
{
MouseClick, left
sleep, 300
}
} ;-- End of Loop.
return
F8 starts loop and this code checks specific pixel in rectangle and sends left click.
It works with [MouseClick, left, %X%, %Y%].But I want to know how can I use dllcall mouse event to click on specific pixel.
for example
DllCall("mouse_event",uint,1,int,%X%,int,%Y%,uint,0,int,0)
But its not working
I doubt that you actually want to do this via DLL calls. mouse_event doesn't even take coordinates, but values between 0-65535.
If you want to be able to click any pixel on the screen make sure you set it to be relative to the screen: CoordMode, Mouse, Screen
Then use ControlClick/PostMessage/SendMessage if you don't want to affect your mouse pointer by that click. Or use MouseClick/Click. Or MouseMove+Send, {LButton}.

Temporarily Pause Autofire loop on holding button

I wrote a script that sends autofire left clicks and can be triggered on and off. The script works. However, the problem is that holding the right mouse button does not work properly anymore because the left click keeps getting sent. So I want to change the script that it gets temporarily paused while I hold down the right mouse button.
How would I go about doing this? Here is my current code:
#MaxThreadsPerHotkey 3
#z::
#MaxThreadsPerHotkey 1
if keep_winz_running = y
{
keep_winz_running = n
return
}
; Otherwise:
keep_winz_running = y
Loop
{
GetKeyState, rbut, Rbutton
If rbut, = U
{
Loop,
{
MouseClick, left
Sleep, 50 ;This means the script will wait 1.5 secs
if keep_winz_running = n ; The user signaled the loop to stop.
break ; break out of the loop
}
Timers are the best!
sendToggle := false
#z::
if(!sendToggle) {
sendToggle := true
SetTimer, SendClick, 100
} else {
sendToggle := false
SetTimer, SendClick, Off
}
return
#If sendToggle
RButton::
SetTimer, SendClick, Off
KeyWait, RButton
SetTimer, SendClick, 100
return
SendClick:
Click
return
I find the send interval of 50 ms awfully fast, especially since you won't be able to actually reach 50 ms without reducing SetBatchLines and SetKeyDelay. If it really needs to be that fast, consider changing them.