Permalink
Cannot retrieve contributors at this time
#SingleInstance force | |
SetTitleMatchMode, 2 ;string anywhere | |
SetKeyDelay, -1 | |
SetBatchLines, -1 | |
Process, Priority,, High | |
SetWorkingDir %A_ScriptDir% | |
helptext = | |
( | |
TwoCamControl | |
********************** | |
Windows frontend for chdkptp with two cameras. | |
********************** | |
version 2018-11-13 | |
by Nod5 | |
https://www.github.com/nod5/TwoCamControl | |
Free Software GPLv3 | |
********************** | |
FEATURES | |
Shoot two cameras at (almost) the same time and quickly. | |
Save each photo directly to the PC. | |
Trigger cameras with a GUI button, keyboard or mouse wheel. | |
Autoname files in sequence 0001L.jpg 0001R.jpg 0002L.jpg ... | |
CONTROLS - BASIC WORKFLOW | |
1. Connect cameras. | |
2. Adjust Zoom and Focus. Preview. | |
3. When the settings are good start a New Project and Shoot. | |
DEPENDENCIES | |
1. Two Canon cameras with CHDK installed. Use the latest development version of CHDK. | |
http://chdk.wikia.com/wiki/CHDK_in_Brief | |
http://chdk.wikia.com/wiki/Downloads | |
2. The cameras must have filewrite support. See FWT column here: | |
http://chdk.wikia.com/wiki/CameraFeatureTable | |
3. LibUSB drivers installed for both cameras on PC. | |
http://chdk.wikia.com/wiki/PTP_Extension#LibUSB_Driver_for_Windows | |
4. chdkptp downloaded and unzipped. | |
https://www.assembla.com/spaces/chdkptp/wiki | |
5. Only if you want to run/build TwoCamControl from source: | |
Install AutoHotkey https://autohotkey.com and save the .ahk with UTF-8 BOM encoding | |
REQUIRED SETUP IN TWOCAMCONTROL | |
- Set workdirectory path | |
- Set path to chdkptp.exe | |
- Set right camera serial | |
OPTIONAL SETTINGS (DEFAULT VALUE) | |
- Auto zoom (0): Auto zoom this number of steps when right/left camera connects. | |
- Wait (0): Wait this number of miliseconds before the camera shoots. Use this to add a delay in a hardware setup where shoots are triggered by a mousewheel that the platen touches on its way down. Set to 0 for no delay. | |
- Mousewheel (off): Mouse wheel down triggers a shoot. | |
- Numpad keys (off): Activate numpad key controls (see below). | |
- Space (off): Space button triggers a shoot. | |
- PC Save (on): Save images directly to PC. Uncheck to save to camera SD cards (not to PC). Save to SD mode uses chdkptp "shoot" command with parameters set by user in the rsint options field. Try parameter raw=1 for raw shoot. | |
- Alt Zoom (off): Use click('zoom_in') method instead of the default set_zoom method. If this is checked then the numbers in the auto zoom settings are interpreted as the number of click zoom actions to auto perform on connect. | |
- rsint options (-cmdwait=600 -tv=1/160): parameters for the rsint command. See file USAGE.TXT in chdkptp for details. Cmdwait is the number of seconds until rsint times out. | |
- extraprocess (blank): Path to some external tool to run at project start. Example: C:\folder\program.exe . TwoCamControl sends the project name as a command line parameter. The project name is a timestamp (YYYYMMDDhhmmss). | |
On Main tab: Click on green text for a single session change of that setting. | |
Reset to default settings: delete TwoCamControl.ini and restart TwoCamControl. | |
CONTROLS - TIPS AND TRICKS | |
- Hold Shift / Control to apply zoom or focus command only to right/left side camera. | |
- Mouse wheel down: Shoot photo. Useful with a wireless mouse. | |
- Space: Shoot photo. | |
- Numpad keys: useful with a wireless numpad | |
- 3 = camera LCD display on/off (save battery) | |
- 4 = connect cameras | |
- 5 = zoom in 5`% | |
- + = zoom in minimal step | |
- - = zoom out minimal step | |
- 6 = focus lock | |
- 7 = new project | |
- 8 = shoot photo | |
- 9 = pause | |
- Numpad 0 1 2 are modifier keys: | |
- 1 = only apply to left camera | |
- 2 = only apply to right camera | |
- 0 = reverse action (zoom out, focus unlock) | |
- Numpad modifier examples: | |
- 1 & 5 = zoom in 5`% left camera | |
- 2 & 6 = focus lock right camera | |
- 0 & 5 = zoom out 5`% | |
- 0 & 6 = focus unlock | |
- 0 & 1 & 5 = zoom out 5`% left camera | |
- Right click on Zoom In / Zoom Out: zoom in/out minimal step. Useful for fine tuning zoom position. | |
- Right click on Preview: menu to show the same preview again. | |
- Right click on Pause: menu to cancel current project but keep cameras connected. | |
- Click on green text for a single session change of that setting. | |
- Right click on Shoot or press NumpadEnter: save a textfile with current page count, for example 0004.txt . Useful as a reminder for post shoot file check. For example when a page was accidentally shot twice. | |
- Close TwoCamControl: also powers off cameras. | |
FEEDBACK | |
GitHub , https://github.com/nod5/TwoCamControl | |
DIY Book Scanner forum , https://forum.diybookscanner.org/viewtopic.php?f=20&t=3082 | |
FAQ | |
Q What if camera serial setup step fails? | |
A Set camera serial manually: plug in and power on camera, open a cmd window in the chdkptp.exe folder, run the command "chdkptp.exe -elist" and look for the serial. It looks something like this: DC3E13D4111234B2A23CC31F2E3AA5 . TwoCamControl uses the serial to identify which camera is which. | |
Q What if the above manual command "chdkptp.exe -elist" returns no serial? | |
A Reinstall LibUSB for that camera. Also double-check that the camera is on, plugged in, and that the cmd window path is in the chdkptp folder. | |
Q How to solve "ERROR: not in continous mode"? | |
A Power on the camera with CHDK on the SD card without connecting it to the PC, press "func" button on the camera, go to the menu item that allows setting continuous mode and turn it on. Power off the camera. Then try TwoCamControl again. | |
Q What if the chkdptp cmd window shows error about filepath or incorrect characters? | |
A Try changing workdirectory path to not include special or non-english characters. Test with only a-z 0-9 space. | |
Q What if chkdptp cmd window still shows error about filepath or incorrect characters? | |
A See notes_on_character_errors.md at https://github.com/nod5/TwoCamControl/blob/master/docs/notes_on_character_errors.md | |
Q Can I use TwoCamControl with only one camera? | |
A Yes. | |
Q What Windows version does TwoCamControl require? | |
A TwoCamControl is only tested in Windows 10 x64. It might work in earlier Windows. | |
Q How does zoom work in TwoCamControl? | |
A In default mode there are two zoom actions. You can zoom in/out a big amount (5 percent of the camera's zoom range) or a minimal amount (2 steps if the camera has a 60+ zoom range, otherwise 1 step). This is done using the chdkptp commands zoom_set and zoom_set_rel. The non-default "Alt Zoom" mode can be enabled in Setup. There TwoCamControl uses the chdkptp command click('zoom_in'). Use the non-default mode if the default mode causes errors or causes distortion in the .jpg files where lines become curved similar to a fish lens effect. Only some cameras have that problem. Read notes_on_zoom_methods.md on GitHub for more. | |
Q What if my question/issue/suggestion is not in this FAQ? | |
A Open an issue at GitHub and describe the problem. Include these details: Your camera model, the version of CHDK, chdkptp and TwoCamControl and Windows version and language. | |
) | |
FileEncoding, UTF-16 | |
;Set UTF-16 before each iniwrite/iniread instance | |
;to enable special characters (non ASCII) in .ini variables | |
;for work directory path, chdkptp path, extraprocess path | |
; | |
;However chdkptp only supports ASCII characters in the rsint command path | |
;For we require ASCII in the work directory edit field, until chdkptp adds unicode support | |
;Background | |
;https://chdk.setepontos.com/index.php?topic=6231.msg138156#msg138156 | |
;https://en.wikipedia.org/wiki/ASCII | |
checkini() | |
FileEncoding, UTF-8 | |
;reset to UTF-8 immediately afterwards, otherwise other .txt file I/O get garbled text | |
checkini() { | |
global | |
ini = %A_scriptdir%\TwoCamControl.ini | |
ifnotexist, %ini% | |
{ | |
chdkptp := FileExist(A_ScriptDir "\chdkptp.exe") ? A_ScriptDir "\chdkptp.exe" : "" | |
ini_txt = | |
( | |
[options] | |
workdir=%A_ScriptDir% | |
chdkptp=%chdkptp% | |
rightcamserial= | |
auto_zoom_right= | |
auto_zoom_left= | |
previous_zooms= | |
wait=0 | |
mousewheelshoot=0 | |
numpadkeys=0 | |
spaceshoot=0 | |
pcsave=1 | |
alt_zoom_method=0 | |
rsintoptions=-cmdwait=600 -tv=1/160 | |
extraprocess= | |
) | |
FileAppend, %ini_txt%, %ini% ;create default ini | |
} | |
setup_items = workdir,chdkptp,setup_button,rightcamserial,auto_zoom_right,auto_zoom_left,previous_zooms,wait,mousewheelshoot,numpadkeys,spaceshoot,pcsave,alt_zoom_method,rsintoptions,extraprocess | |
FileEncoding, UTF-16 | |
Loop, Parse, setup_items,`, | |
IniRead, %A_LoopField%, %ini%, options, %A_LoopField%, %A_space% ;read ini data | |
FileEncoding, UTF-8 | |
if !mousewheelshoot | |
mousewheelshoot = 0 | |
if !numpadkeys | |
numpadkeys = 0 | |
if !spaceshoot | |
spaceshoot = 0 | |
if !pcsave | |
pcsave = 1 | |
if !alt_zoom_method | |
alt_zoom_method = 0 | |
if extraprocess | |
proc = 1 | |
screenstate = 1 | |
if !RegExMatch(wait,"^\d+$") ;not digits | |
wait = 0 | |
previous_array := StrSplit(previous_zooms, "|") | |
} | |
;prepare variables | |
enum := 0000, counter := 1, pau := 0 | |
workdir := SubStr(workdir,0) = "\" ? SubStr(workdir,1,StrLen(workdir)-1) : workdir ;trim end slash | |
workdirfrontslash := StrReplace(workdir, "\","/") ;chdkptp uses frontslashes | |
;hotkey/buttons list, use to first disable all and then selectively enable | |
;- local hotkeys when preparing session | |
;- global hotkeys when shooting | |
all_hotkeys := "*F4,*F5,*F6,F7" | |
all_numpad_hotkeys := "*Numpad4,*Numpad5,*Numpad6,NumpadAdd,NumpadSub,Numpad7" | |
all_global_hotkeys := "F8,F9,WheelDown,Space,Numpad8,Numpad9,NumpadEnter,Numpad3" | |
all_buttons := "bf4,bf5,bf5b,bmenu,bf6,bf6b,bf6c,bf7,bf8,bf9" | |
;Create Gui | |
gui_width := 330 , gui_height := 500 | |
Gui, Add, Tab3, x0 y0 w%gui_width% h%gui_height%, Main|Setup|Help | |
Gui, Color, ffffff ;white | |
;Gui Main tab | |
Gui, font, s12 bold | |
Gui, Add, text,,%A_space% | |
Gui, Add, Button, vbf4 gbf4 Default,(F4) Connect Cameras | |
GuiControl, -default, bf4 | |
Gui, Add, Button, vbf5 gbf5,(F5) Zoom In | |
Gui, Add, Button, vbf5b gbf5b yp x+10,Out | |
Gui, Add, Button, vbmenu gbmenu yp x+20,Menu | |
Gui, Add, Button, vbf6 gbf6 y+6 xm,(F6) Focus Lock | |
Gui, Add, Button, vbf6b gbf6b yp x+8,Unlock | |
Gui, Add, Button, vbf6c gbf6c yp x+8,Preview | |
Gui, Add, text,,%A_space% | |
Gui, Add, text,,%A_space% | |
Gui, Add, Button, vbf7 gbf7 y+0 xm,(F7) New Project | |
Gui, Add, text,,%A_space% | |
Gui, Add, Button, vbf8 gbf8,(F8) Shoot | |
Gui, Add, Button, vbf9 gbf9,(F9) Pause | |
;disable all hotkeys/buttons | |
hotoff(all_hotkeys "," all_numpad_hotkeys) | |
hotoffglobal(all_global_hotkeys) | |
dis(all_buttons) | |
;enable connect cameras | |
ena("bf4") | |
hoton("*F4") | |
if (numpadkeys = 1) | |
hoton("*Numpad4") | |
;single session settings in green text | |
Gui, font, normal cgreen | |
Gui, add, text,vfileenum gfileenum w160,next filename 0001 | |
Gui, add, text,vsleeptime gsleeptime yp+20 w80, wait %wait% | |
Gui, add, text,vproc gproc yp+20 w100,extraprocess | |
Gui, add, text,vscreenstate gscreenstate yp+20 w140, camera LCD on | |
Gui, font, s10 cblack | |
Gui, add, text,vproj yp+20 w300, | |
Gui, font, cgray | |
Gui, Add, text,vapply xm+2 y174 w300,both cameras | |
Gui, Add, GroupBox, w2 h150 y40 x7, | |
;Gui Setup tab | |
Gui, Tab, 2 | |
setup_width := gui_width-15 | |
Gui, font, s10 normal black | |
Gui, Add, text,x8 y33,Read DEPENDENCIES in Help first. | |
Gui, font, s12 bold black | |
Gui, Add, text,yp+25,projects work directory | |
Gui, font, s10 normal | |
Gui, Add, edit, w300 h20 yp+20 vworkdir gworkdir,%workdir% | |
Gui, Add, text,w%setup_width%,Path to folder where each project subfolder is created and photos saved. Example: C:\test\folder | |
Gui, font, s12 bold | |
Gui, Add, text,yp+36,chdkptp path | |
Gui, font, s10 normal | |
Gui, Add, edit, w300 h20 vchdkptp gchdkptp yp+21,%chdkptp% | |
Gui, Add, text,w%setup_width% yp+22,Example: C:\some folder\chdkptp\chdkptp.exe | |
txtser = A special serial number (not printed on the camera). Take the camera you will later place to the right of the book. Plug it in to the PC with USB. Power on the camera. Then click the Setup button. | |
Gui, font, s12 bold | |
Gui, Add, text,,Right cam serial | |
Gui, font, s10 normal | |
Gui, Add, button, yp-4 xp+140 vsetup_button gsetup_button,Setup | |
Gui, Add, edit, xp-140 yp+29 w300 h20 vrightcamserial grightcamserial,%rightcamserial% | |
Gui, Add, text,yp+22 w%setup_width%,%txtser% | |
;auto zoom | |
Gui, font, s10 bold | |
Gui, Add, text,y+30 x16,Auto zoom | |
Gui, font, s10 normal | |
Gui, Add, edit, yp-2 x+9 w28 h20 Limit3 Number vauto_zoom_left gauto_zoom_left,%auto_zoom_left% | |
Gui, Add, text, yp+2 x+4 w60, left cam | |
Gui, Add, edit, yp-2 x+2 w28 h20 Limit3 Number vauto_zoom_right gauto_zoom_right,%auto_zoom_right% | |
Gui, Add, text, yp+2 x+5 w100, right cam | |
;wait = 400 | |
Gui, font, s10 bold | |
Gui, Add, text,x56 yp+21,Wait | |
Gui, font, s10 normal | |
Gui, Add, edit, yp-2 x+9 w45 Limit Number h20 vwait gwait,%wait% | |
Gui, Add, text, yp+2 x+5 w170, before shoot (in ms) | |
;mousewheelshoot | |
Gui, font, s10 bold | |
Gui, Add, text,x3 yp+25,Mousewheel | |
Gui, font, s10 normal | |
Gui, Add, Checkbox, yp x+7 w17 h20 Checked%mousewheelshoot% vmousewheelshoot gmousewheelshoot | |
;numpadkeys | |
Gui, font, s10 bold | |
Gui, Add, text,xp+27 yp,Numpad | |
Gui, font, s10 normal | |
Gui, Add, Checkbox, yp x+7 w17 h20 Checked%numpadkeys% vnumpadkeys gnumpadkeys | |
;spaceshoot | |
Gui, font, s10 bold | |
Gui, Add, text,x44 yp+25,Space | |
Gui, font, s10 normal | |
Gui, Add, Checkbox, yp x+7 w17 h20 Checked%spaceshoot% vspaceshoot gspaceshoot | |
;pcsave | |
Gui, font, s10 bold | |
Gui, Add, text,xp+25 yp,PC Save | |
Gui, font, s10 normal | |
Gui, Add, Checkbox, yp x+7 w15 h20 Checked%pcsave% vpcsave gpcsave | |
;alternative zoom method | |
Gui, font, s10 bold | |
Gui, Add, text,xp+27 yp,Alt Zoom | |
Gui, font, s10 normal | |
Gui, Add, Checkbox, yp x+7 w17 h20 Checked%alt_zoom_method% valt_zoom_method galt_zoom_method | |
;rsintoptions = -cmdwait=600 -tv=1/160 | |
Gui, font, s10 bold | |
Gui, Add, text,x7 yp+27,rsint options | |
Gui, font, s10 normal | |
Gui, Add, edit, yp-2 x+10 w205 h20 vrsintoptions grsintoptions,%rsintoptions% | |
;extraprocess | |
Gui, font, s10 bold | |
Gui, Add, text,x6 yp+23,Extraprocess | |
Gui, font, s10 normal | |
Gui, Add, edit, yp-2 x+4 w205 h20 vextraprocess gextraprocess,%extraprocess% | |
;Gui Help tab | |
Gui, Tab, 3 | |
help_width := gui_width-10 , helph = gui_height-25 | |
Gui, Add, Edit, ReadOnly x5 y22 w%help_width% h%helph% vMyEdit, %helptext% | |
Gui, Tab, 1 ;default tab | |
Gui, Show, w%gui_width% h%gui_height% x0 y0, TwoCamControl | |
WinSet, AlwaysOnTop, On, TwoCamControl | |
SetTimer, modifier_key_check, 50 | |
return | |
;save to ini on editbox/checkbox change | |
alt_zoom_method: | |
workdir: | |
chdkptp: | |
rightcamserial: | |
auto_zoom_right: | |
auto_zoom_left: | |
wait: | |
mousewheelshoot: | |
rsintoptions: | |
numpadkeys: | |
spaceshoot: | |
pcsave: | |
extraprocess: | |
gui,submit,nohide | |
tempkey := a_thislabel ;wait ;label text | |
tempedit := %a_thislabel% ;400 ;value of variable with same name as label | |
FileEncoding, UTF-16 | |
IniWrite, %tempedit%, %ini%, options, %tempkey% | |
FileEncoding, UTF-8 | |
if (a_thislabel = "wait") | |
GuiControl,, sleeptime, wait %tempedit% | |
if (a_thislabel = "workdir") | |
{ | |
;trim end slash | |
workdir := SubStr(workdir,0) = "\" ? SubStr(workdir,1,StrLen(workdir)-1) : workdir | |
;path must be ascii, otherwise chdkptp will save to incorrect folder | |
if !isAscii(workdir) | |
{ | |
ascii_error = `n Error:`nThe work directory path must be ASCII characters.`nTry a path with only A-Z 0-9 Space \ `n `n | |
ToolTip, % ascii_error, % A_CaretX, % A_CaretY | |
;clear ini value | |
FileEncoding, UTF-16 | |
IniWrite, %A_Space% , %ini%, options, %tempkey% | |
FileEncoding, UTF-8 | |
sleep 2500 | |
ToolTip | |
GuiControl,, workdir, | |
} | |
;chdkptp uses frontslashes | |
workdirfrontslash := StrReplace(workdir, "\","/") | |
} | |
return | |
;function: check if string is ASCII | |
;https://en.wikipedia.org/wiki/ASCII | |
isAscii(string){ | |
return RegExMatch(string, "[^[:ascii:]]") = 0 ? 1 : 0 | |
} | |
;use chdkptp to read connected camera serial number | |
setup_button: | |
if !FileExist(chdkptp) or (SubStr(chdkptp, -10) != "chdkptp.exe") | |
msg_and_reload("Error: set chdkptp.exe path first.") | |
FileDelete, %A_ScriptDir%\twocamcontrol_tempcam.txt | |
RunWait %comspec% /c ""%chdkptp%" -elist > "%A_ScriptDir%\twocamcontrol_tempcam.txt"", , Hide | |
FileRead, tempcam, %A_ScriptDir%\twocamcontrol_tempcam.txt | |
FileDelete, %A_ScriptDir%\twocamcontrol_tempcam.txt | |
RegExMatch(tempcam, "s=(\w+)",match) | |
rightcamserial := match1 | |
GuiControl,, rightcamserial, %rightcamserial% | |
return | |
;Preview Shoot preview pictures and show in default image viewer | |
bf6c: | |
FileDelete, %workdir%\TCC_temppreviewL.jpg | |
FileDelete, %workdir%\TCC_temppreviewR.jpg | |
tempoptions := RegExReplace(rsintoptions, "-cmdwait=\d+", "") | |
;enclose path for rs | |
if acti(leftcam) | |
xs("rs '" workdir "/TCC_temppreviewL' " tempoptions,leftcam), sleep(200) | |
if acti(rightcam) | |
xs("rs '" workdir "/TCC_temppreviewR' " tempoptions,rightcam) | |
actiscript() | |
show_preview_again: | |
Loop | |
{ | |
;wait until one picture is saved | |
if FileExist(workdir "\TCC_temppreview*.jpg") | |
break | |
sleep 100 | |
if (a_index = 60) | |
;abort after 6 secs | |
return | |
} | |
LR := FileExist(workdir "\TCC_temppreviewL.jpg") ? "L" : "R" | |
Run %workdir%\TCC_temppreview%LR%.jpg ;preview in default image viewer | |
return | |
;timer: | |
;Hold Control/Shift or Numpad1/2: apply command (zoom, focus, connect) only to L/R camera | |
;Hold Numpad0: reverse numpad5/6 command (zoom out/focus unlock) | |
modifier_key_check: | |
;GetKeyState returns 1 if down, 0 if up | |
ckey := GetKeyState("Control") | |
ckey2 := GetKeyState("Numpad1") | |
skey := GetKeyState("Shift") | |
skey2 := GetKeyState("Numpad2") | |
nkey := GetKeyState("Numpad0") | |
wkey := GetKeyState("Lwin") | |
wkey2 := GetKeyState("Rwin") | |
akey := GetKeyState("Alt") | |
ckey := ckey2 = 1 ? 1 : ckey | |
skey := skey2 = 1 ? 1 : skey | |
wkey := wkey2 = 1 ? 1 : wkey | |
GuiControl,,apply, % skey = ckey ? "both cameras" : skey = 1 ? "right side camera" : "left side camera" | |
return | |
;functions: enable/disable comma separated list of hotkeys/buttons | |
dis(d) { | |
Loop, Parse, d,`, | |
GuiControl, disable, %A_LoopField% | |
} | |
ena(d) { | |
Loop, Parse, d,`, | |
GuiControl, enable, %A_LoopField% | |
} | |
hotoff(h) { | |
Hotkey,IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
Loop, Parse, h,`, | |
Hotkey, %A_LoopField%, Off | |
} | |
hoton(h) { | |
Hotkey, IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
Loop, Parse, h,`, | |
Hotkey, %A_LoopField%, On | |
} | |
hotoffglobal(h) { | |
Hotkey, IfWinActive | |
Loop, Parse, h,`, | |
Hotkey, %A_LoopField%, Off | |
} | |
hotonglobal(h) { | |
Hotkey, IfWinActive | |
Loop, Parse, h,`, | |
Hotkey, %A_LoopField%, On | |
} | |
GuiClose: | |
;if proj has started: exit rsint mode | |
if proj && pcsave | |
{ | |
if acti(leftcam) | |
xs("q",leftcam) | |
if acti(rightcam) | |
xs("q",rightcam) | |
sleep(2000) | |
} | |
;if cameras are connected: shut down cameras | |
if (pid1 or pid2) | |
{ | |
if acti(leftcam) | |
xs("luar shut_down()",leftcam) | |
if acti(rightcam) | |
xs("luar shut_down()",rightcam) | |
sleep(2000) | |
} | |
Process, Close, chdkptp.exe | |
sleep 200 | |
Process, Close, chdkptp.exe | |
sleep 200 | |
loop, 2 | |
WinClose, ahk_exe cmd.exe | |
;clean temp preview images | |
FileDelete, %workdir%\TCC_temppreview*.jpg | |
ExitApp | |
;right click in gui | |
GuiContextMenu: | |
GuiControlGet, PreviewButEnabled, Enabled, bf6c | |
menu, twocam_menu, add | |
Menu, twocam_menu, Delete ;clear old | |
if (proj and A_GuiControl = "bf9") | |
{ | |
;right click button (F9) Pause | |
Menu, twocam_menu, Add,Cancel Project, cancel_project | |
Menu, twocam_menu, Show | |
} | |
else if (proj and !pau and A_GuiControl = "bf8") | |
{ | |
;right click button (F8) Shoot | |
;save blank 0004.txt for troubleshooting | |
Menu, twocam_menu, Add,Save %count_string%.txt, save_txt_count | |
Menu, twocam_menu, Show | |
} | |
else if (PreviewButEnabled and A_GuiControl = "bf6c") | |
{ | |
;right click button Preview | |
Menu, twocam_menu, Add,Show same preview again, show_preview_again | |
Menu, twocam_menu, Show | |
} | |
else if (!proj and A_GuiControl = "bf5") | |
{ | |
;right click Zoom in before project start | |
gosub zoom_in_mini | |
} | |
else if (!proj and A_GuiControl = "bf5b") | |
{ | |
;right click Zoom out before project start | |
gosub zoom_out_mini | |
} | |
return | |
;exit rsint, clear vars, unlock focus, ready cameras | |
cancel_project: | |
if proj | |
{ | |
;enable camera LCD backlight | |
if !screenstate | |
gosub screenstate | |
;exit rsint | |
if acti(leftcam) | |
xs("q",leftcam) | |
if acti(rightcam) | |
xs("q",rightcam) | |
sleep(1000) | |
} | |
enum := 0000, counter := 1, proj := "" | |
gosub bf6b ;focus unlock cameras | |
;no need to update other variables since (F7) New project will set them | |
;disable all hotkeys/buttons | |
hotoff(all_hotkeys "," all_numpad_hotkeys) | |
hotoffglobal(all_global_hotkeys) | |
dis(all_buttons) | |
;enable configuration hotkeys/buttons | |
ena("bf5,bf5b,bmenu,bf6,bf6b,bf6c,bf7") | |
hoton("*F5,*F6,F7") | |
if (numpadkeys = 1) | |
hoton("*Numpad4,*Numpad5,*Numpad6,NumpadAdd,NumpadSub,Numpad7") | |
return | |
;function: activate script main window | |
actiscript() { | |
WinActivate, TwoCamControl ahk_class AutoHotkeyGUI | |
} | |
;function: activate cmd window based on device num (d=0001 or d=00002) in win title | |
;note: y = rightcam/leftcam variable = blank if the camera was not connected | |
acti(y) { | |
If !y or !WinExist("d=" y " ahk_exe cmd.exe") | |
return | |
Loop, 10 | |
{ | |
;try for 1 second (10 times 60+40 ms) | |
WinActivate, d=%y% ahk_exe cmd.exe ;makes six attempts during 60 ms | |
If WinActive("d=" y " ahk_exe cmd.exe") | |
break | |
sleep 40 | |
} | |
If WinActive("d=" y " ahk_exe cmd.exe") | |
Return true | |
} | |
;function: send command x string to camera y's cmd window | |
xs(x,y) { | |
x := unicodify(x) | |
If WinActive("d=" y " ahk_exe cmd.exe") | |
SendInput,%x%{enter} | |
} | |
;function: send command x string as unicode to camera y's cmd window after modifier key release | |
xsif(x,y) { | |
x := unicodify(x) | |
If !WinActive("d=" y " ahk_exe cmd.exe") | |
return | |
;wait for control/shift modifier key release | |
;use for connect/zoom/focus commands where user may press modifier | |
;because sendinput can mess up string to cmd if modifier is down | |
if GetKeyState("Control") | |
KeyWait, Control | |
if GetKeyState("Shift") | |
KeyWait, Shift | |
SendInput,%x%{enter} | |
} | |
;function: convert string to unicode values | |
unicodify(x) { | |
;necessary to prevent sendinput char error in chdkptp interactive CLI with win10 non-eng | |
;for example the ! in !file in get_zoom() will not appear unless unicodify() is used | |
;note: in unicode scripts asc() returns unicode value | |
;note: Setformat can make asc() output hex, for SendInput {U+nnnn} use | |
;old TwoCamControl ansify() method was flawed: sent ascii {ASC nnnn} and only for some chars | |
; https://autohotkey.com/docs/commands/Asc.htm | |
; https://autohotkey.com/docs/commands/SetFormat.htm | |
; https://autohotkey.com/docs/commands/Send.htm | |
;note: special keys like {enter} fail if passed through unicodify() so send them as is | |
SetFormat, IntegerFast, Hex | |
Loop, Parse, x | |
uni .= "{U+" Asc(A_LoopField) "}" | |
SetFormat, IntegerFast, Decimal | |
return uni | |
} | |
;function: link cameras to left/right side based on right camera serial number | |
; note: chdkptp list command returns list of PTP devices in this format (from USAGE.TXT): | |
;<status><num>:<modelname> b=<bus> d=<device> v=<usb vendor> p=<usb pid> s=<serial number> | |
; first identify the right camera using its serial number (previously setup) | |
; next get the enumeration part of the device strings (0001/0002) | |
;later used to correctly activate/send to the right/left camera cmd window | |
checkcams() { | |
global | |
if (pid1 or pid2) | |
;already checked this session, avoid camera error on second -elist run | |
return | |
filedelete, %workdir%\twocamcontrol_tempcam.txt | |
RunWait, %comspec% /c ""%chdkptp%" -elist > "%workdir%\twocamcontrol_tempcam.txt"",,Hide | |
sleep 100 | |
FileReadLine, xline1, %workdir%\twocamcontrol_tempcam.txt, 1 | |
FileReadLine, xline2, %workdir%\twocamcontrol_tempcam.txt, 2 | |
filedelete, %workdir%\twocamcontrol_tempcam.txt | |
if !xline1 | |
;no cameras found | |
return | |
rightcam := InStr(xline1,rightcamserial) ? "0001" | |
: InStr(xline2,rightcamserial) ? "0002" | |
: "" | |
leftcam := xline1 and !InStr(xline1,rightcamserial) ? "0001" | |
: xline2 and !InStr(xline2,rightcamserial) ? "0002" | |
: "" | |
} | |
;CONNECT CAMERAS, ENABLE PHOTO MODE, APPLY AUTO ZOOM | |
#IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
*Numpad4:: | |
*F4:: | |
bf4: | |
if wkey or akey | |
return | |
left_only := ckey and !skey ? 1 : 0 ;connect only left cam | |
right_only := !ckey and skey ? 1 : 0 ;connect only right cam | |
;verify setup workdir, cam serial, chdkptp path | |
if !InStr(FileExist(workdir), "D") | |
msg_and_reload("Error: working directory does not exist.") | |
SplitPath, chdkptp,xname,xdir | |
if !FileExist(chdkptp) or (xname != "chdkptp.exe") | |
msg_and_reload("Error: chdkptp path incorrect.") | |
if !rightcamserial | |
msg_and_reload("Error: rightcamserial setting missing.") | |
;disable setup tab during project | |
loop,parse,setup_items,`, | |
dis(a_loopfield) ;disable setup tab items | |
dis("Setup") ;disable setup tab | |
;check and connect cameras -> sets variables rightcam and leftcam | |
checkcams() | |
if !rightcam and !leftcam ;no cameras detected | |
msg_and_reload("Error: could not detect cameras.") | |
if leftcam and !right_only and !left_connected | |
pid1 := connect_cam(chdkptp, leftcam, gui_width, 38) | |
if rightcam and !left_only and !right_connected | |
pid2 := connect_cam(chdkptp, rightcam, gui_width, 367) | |
sleep(500) | |
;get zoom step range for each camera, returns blank if fail | |
if !right_only and !left_connected | |
if acti(leftcam) and !alt_zoom_method | |
left_zoom_range := get_zoom("left", "return get_zoom_steps()") , sleep(500) | |
if !left_only and !right_connected | |
if acti(rightcam) and !alt_zoom_method | |
right_zoom_range := get_zoom("right", "return get_zoom_steps()") | |
;calculate 5% zoom in steps for each camera, at minimum 1 step | |
;examples: 128 range camera -> 6 steps , 9 range camera -> 1 step | |
zp := 0.05 | |
right_percentage_steps := return_largest( round(right_zoom_range * zp) , 1) | |
left_percentage_steps := return_largest( round(left_zoom_range * zp) , 1) | |
;auto zoom using Setup/ini values | |
if (auto_zoom_left > 0) | |
if !right_only and !left_connected | |
if acti(leftcam) | |
if !alt_zoom_method | |
zoom_set("left", auto_zoom_left) | |
else | |
;use alternative zoom click method | |
Loop, % auto_zoom_left | |
zoom_click("left", "zoom_in"), sleep(300) | |
if (auto_zoom_right > 0) | |
if !left_only and !right_connected | |
if acti(rightcam) | |
if !alt_zoom_method | |
zoom_set("right", auto_zoom_right) | |
else | |
;use alternative zoom click method | |
Loop, % auto_zoom_right | |
zoom_click("right", "zoom_in"), sleep(300) | |
;mark camera as connected | |
;avoids error from chdkptp connecting an already connected camera | |
left_connected := !right_only ? 1 : left_connected | |
right_connected := !left_only ? 1 : right_connected | |
actiscript() | |
;disable all hotkeys/buttons | |
hotoff(all_hotkeys "," all_numpad_hotkeys) | |
hotoffglobal(all_global_hotkeys) | |
dis(all_buttons) | |
;enable configuration hotkeys/buttons | |
ena("bf5,bf5b,bmenu,bf6,bf6b,bf6c,bf7") | |
hoton("*F5,*F6,F7") | |
if (numpadkeys = 1) | |
hoton("*Numpad5,*Numpad6,Numpad7,NumpadAdd,NumpadSub") | |
return | |
;function: connect camera | |
;use enumeration part of the device id string (0001 or 0002) | |
connect_cam(chdkptp, thiscam, xpos, ypos) | |
{ | |
sleep 500 | |
Run, %comspec% /k ""%chdkptp%" -c"-d=%thiscam%" -i -e"luar switch_mode_usb(1)"" ,,,campid | |
sleep 500 | |
Process, Priority, %campid%, High | |
Winwait, %thiscam% ahk_exe cmd.exe,,2 | |
WinMove,%thiscam% ahk_exe cmd.exe,, %xpos%, %ypos%,,332 | |
return campid | |
} | |
;function: get zoom range or current zoom position from a camera | |
; returns from chdkptp's get_zoom_range() or get_zoom() on the camera | |
; returns blank on fail | |
; note: chdkptp file writes to a_ScriptDir | |
get_zoom(which, command) | |
{ | |
FileDelete, % A_ScriptDir "\twocamcontrol_zoom_temp.txt" | |
xsif("!var = con:execwait('" command "')",%which%cam) | |
xsif("!file = io.open('twocamcontrol_zoom_temp.txt', 'w')",%which%cam) | |
xsif("!file:write(tostring(var))",%which%cam) | |
xsif("!file:close()",%which%cam) | |
Loop, 20 | |
{ | |
;wait for file write | |
if FileExist(A_ScriptDir "\twocamcontrol_zoom_temp.txt") | |
break | |
sleep 50 | |
} | |
FileRead, zoom, % A_ScriptDir "\twocamcontrol_zoom_temp.txt" | |
return zoom | |
} | |
;function: return the largest of two inputs | |
return_largest(a, b) | |
{ | |
return a < b ? b : a | |
} | |
;ZOOM | |
#IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
*F5:: | |
*Numpad5:: | |
NumpadSub:: | |
NumpadAdd:: | |
bf5: | |
bf5b: ;out | |
zoom_in_mini: | |
zoom_out_mini: | |
if wkey or akey | |
return | |
;zoom which cameras? | |
left_only := ckey and !skey ? 1 : 0 ;connect only left cam | |
right_only := !ckey and skey ? 1 : 0 ;connect only right cam | |
;zoom in or out? (+ = in, - = out) | |
in_out := InStr("|bf5b|NumpadSub|zoom_out_mini|", "|" A_ThisLabel "|") or (A_ThisLabel = "*Numpad5" and nkey) ? "-" : "+" | |
;zoom big (5%) or small (a minimal step) amount? | |
; 2018-08-26: Notes from test on cameras with 128 zoom step range | |
; Zooming one step (+1) only works once in a row, we must zoom out | |
; or zoom in more for (+1) to work again. | |
; Zooming two steps (+2) always works. | |
; Therefore consider two steps minimal on 128 zoom step range cameras. | |
; 2018-09-24: CHDK cameras vary in zoom step range from 8 to 202. | |
; For now use two steps if zoom step range >60 and otherwise one step. | |
; >60 is a guesstimate after chdkptp forum discussion. | |
minimal_left := left_zoom_range > 60 ? 2 : 1 | |
minimal_right := right_zoom_range > 60 ? 2 : 1 | |
left_zoom_size := InStr("|*F5|*Numpad5|bf5|bf5b|", "|" A_ThisLabel "|") ? left_percentage_steps : minimal_left | |
right_zoom_size := InStr("|*F5|*Numpad5|bf5|bf5b|", "|" A_ThisLabel "|") ? right_percentage_steps : minimal_right | |
;If user enables "Alt Zoom" in Setup then alt_zoom_method=1 and we use the zoom_click method. | |
;Also use zoom_click method if left_zoom_range/right_zoom_range is false because get_zoom failed. | |
sleep(200) | |
if !right_only | |
if left_zoom_range and !alt_zoom_method | |
zoom_steps("left", in_out, left_zoom_size) | |
else | |
zoom_click("left", in_out = "+" ? "zoom_in" : "zoom_out") | |
if !left_only | |
if right_zoom_range and !alt_zoom_method | |
zoom_steps("right", in_out, right_zoom_size) | |
else | |
zoom_click("right", in_out = "+" ? "zoom_in" : "zoom_out") | |
actiscript() | |
return | |
;function: click zoom | |
;used by alternative zoom method | |
zoom_click(which, in_out){ | |
if acti(%which%cam) | |
xsif("luar click('" in_out "')",%which%cam) | |
} | |
;function: zoom X steps | |
zoom_steps(which, in_out, steps){ | |
if acti(%which%cam) | |
xsif("luar set_zoom_rel('" in_out steps "')",%which%cam) | |
} | |
;function: set zoom position | |
zoom_set(which, zoom_position){ | |
if acti(%which%cam) | |
xsif("luar set_zoom('" zoom_position "')",%which%cam) | |
} | |
;zoom menu: options to store current zoom as autozoom or restore zoom from previous projects | |
bmenu: | |
if alt_zoom_method | |
;disable menu when user has chosen the alternative zoom mode | |
return | |
menu, twocam_zoom_menu, add | |
Menu, twocam_zoom_menu, Delete ;clear old | |
Menu, twocam_zoom_menu, Add, Store current zooms as auto zooms, store_to_auto_zoom | |
Menu, twocam_zoom_menu, Add, zoom out completely, zoom_out_completely | |
menu, twocam_previous, add | |
Menu, twocam_previous, Delete ;clear old | |
menu, twocam_previous, add | |
For Key, Val in previous_array | |
Menu, twocam_previous, Add, % Val, apply_previous_zoom | |
;submenu for reuse of zoom from previous projects | |
Menu, twocam_zoom_menu, Add, reuse previous project zooms, :twocam_previous | |
Menu, twocam_zoom_menu, Show | |
return | |
;store current zoom as auto zoom | |
store_to_auto_zoom: | |
sleep(500) | |
if acti(leftcam) | |
auto_zoom_left := get_zoom("left", "return get_zoom()") , sleep(500) | |
if acti(rightcam) | |
auto_zoom_right := get_zoom("right", "return get_zoom()") | |
sleep(200) | |
actiscript() | |
if !isDigit(auto_zoom_left) and !isDigit(auto_zoom_right) | |
{ | |
msgbox, error: cannot read current zoom | |
actiscript() | |
return | |
} | |
FileEncoding, UTF-16 | |
if isDigit(auto_zoom_left) | |
{ | |
GuiControl, , auto_zoom_left , % auto_zoom_left | |
IniWrite, %auto_zoom_left% , %ini%, options, auto_zoom_left | |
} | |
if isDigit(auto_zoom_right) | |
{ | |
GuiControl, , auto_zoom_right, % auto_zoom_right | |
IniWrite, %auto_zoom_right%, %ini%, options, auto_zoom_right | |
} | |
FileEncoding, UTF-8 | |
return | |
;zoom out completely | |
zoom_out_completely: | |
zoom_set("left" , 0) | |
zoom_set("right", 0) | |
actiscript() | |
return | |
;function: apply zoom from previous project | |
;itemName format: YYYYMMDDhhmmss L85 R120 | |
apply_previous_zoom(ItemName, ItemPos) | |
{ | |
RegExMatch(ItemName, "^\d+ L(\d+) " , zoom_left_temp) | |
RegExMatch(ItemName, "^\d+ .* R(\d+)$", zoom_right_temp) | |
if isDigit(zoom_left_temp1) | |
zoom_set("left", zoom_left_temp1) | |
if isDigit(zoom_right_temp1) | |
zoom_set("right", zoom_right_temp1) | |
actiscript() | |
} | |
;FOCUS LOCK | |
#IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
*Numpad6:: | |
*F6:: | |
bf6: | |
bf6b: ;off | |
if wkey or akey | |
return | |
;focus action on which camera? | |
left_only := ckey and !skey ? 1 : 0 ;connect only left cam | |
right_only := !ckey and skey ? 1 : 0 ;connect only right cam | |
;focus lock or unlock? (0 = unlock , 1 = lock) | |
lock_unlock := A_ThisLabel = "bf6b" or (A_ThisLabel = "*Numpad6" and nkey) ? 0 : 1 | |
sleep(500) | |
if !right_only | |
if acti(leftcam) | |
xsif("luar set_aflock(" lock_unlock ")",leftcam) | |
if !left_only | |
if acti(rightcam) | |
xsif("luar set_aflock(" lock_unlock ")",rightcam) | |
actiscript() | |
return | |
;NEW PROJECT + PREPARE RSINT | |
#IfWinActive, TwoCamControl ahk_class AutoHotkeyGUI | |
Numpad7:: | |
F7:: | |
bf7: | |
if wkey or akey | |
return | |
proj := A_now | |
GuiControl,, proj, %workdir%\%proj% | |
FileCreateDir, %workdir%\%proj%\ | |
If !FileExist(workdir "\" proj "\") | |
msg_and_reload("Error: could not create %workdir%\%proj%\ .") | |
;clear temp preview images | |
FileDelete, %workdir%\TCC_temppreview*.jpg | |
;add current zooms to zoom history array and ini | |
;format: YYYYMMDDhhmmss L30 R45|YYYYMMDDhhmmss L20 R20|... | |
if acti(leftcam) | |
zoom_left := get_zoom("left", "return get_zoom()"), sleep(500) | |
if acti(rightcam) | |
zoom_right := get_zoom("right", "return get_zoom()") | |
actiscript() | |
if !isDigit(zoom_left) | |
zoom_left := 0 | |
if !isDigit(zoom_right) | |
zoom_right := 0 | |
previous_array.InsertAt(1, proj " L" zoom_left " R" zoom_right) | |
;keep at most 5 zooms | |
if ( previous_array.MaxIndex() > 5 ) | |
previous_array.Pop() | |
;write to ini | |
previous_zooms := "" | |
For Key, Val in previous_array | |
previous_zooms .= key = previous_array.MaxIndex() ? Val : Val "|" | |
FileEncoding, UTF-16 | |
IniWrite, %previous_zooms% , %ini%, options, previous_zooms | |
FileEncoding, UTF-8 | |
;apply rsintoptions | |
if pcsave | |
{ | |
if acti(leftcam) | |
xsif("rsint " rsintoptions,leftcam), sleep(200) | |
if acti(rightcam) | |
xsif("rsint " rsintoptions,rightcam) | |
actiscript() | |
} | |
;disable all hotkeys/buttons | |
hotoff(all_hotkeys "," all_numpad_hotkeys) | |
hotoffglobal(all_global_hotkeys) | |
dis(all_buttons) | |
;enable project hotkeys/buttons | |
ena("bf8,bf9") | |
hotonglobal("F8,F9") | |
if numpadkeys | |
hotonglobal("Numpad8,Numpad9,NumpadEnter,Numpad3") | |
if mousewheelshoot | |
hotonglobal("WheelDown") | |
if spaceshoot | |
hotonglobal("Space") | |
;run extra process at project start with project name timestamp (YYYYMMDDhhmmss) param | |
if ( proc = 1 and FileExist(extraprocess) ) | |
Run, "%extraprocess%" %proj% | |
return | |
;GREEN TEXT BUTTONS | |
;toggle first file name enumeration for this session | |
fileenum: | |
enum := enum > 9000 ? 0000 : enum + 1000 | |
count_string := SubStr("000000000000" . counter+enum, -3) ;pad 1 to 0001 | |
GuiControl,, fileenum, next filename %count_string% | |
return | |
;toggle sleep time for this session | |
sleeptime: | |
wait += wait = 1500 ? -1500 : 100 | |
GuiControl,, sleeptime, wait %wait% ;temp change, don't save to ini | |
return | |
;toggle extraprocess for this session | |
proc: | |
GuiControl,, proc, % proc ? "only save" : "extraprocess" | |
proc := !proc | |
return | |
;toggle camera LCD display on/off (saves battery) | |
#IfWinActive | |
Numpad3:: | |
screenstate: | |
if !proj | |
return | |
;only toggle after a project is created | |
GuiControl,, screenstate, % screenstate ? "camera LCD off" : "camera LCD on" | |
screenstate := !screenstate | |
if acti(leftcam) | |
xs("exec set_lcd_display(" screenstate ")",leftcam), sleep(200) | |
if acti(rightcam) | |
xs("exec set_lcd_display(" screenstate ")",rightcam) | |
actiscript() | |
return | |
;PAUSE: disable shoot hotkeys, temp change GUI background color | |
#IfWinActive | |
F9:: | |
bf9: | |
Numpad9:: | |
if wkey or akey | |
return | |
if !proj | |
return | |
Gui, 1:Default ;operate on first gui | |
pau := !pau ;pau var starts at 0 | |
col := pau = 1 ? "F6CECE" : "ffffff" ;red or white | |
Gui, Color, %col% | |
toggle := pau ? "off" : "on" | |
;hot%toggle%("F8") | |
hot%toggle%global("F8") | |
if (mousewheelshoot = 1) | |
hot%toggle%global("WheelDown") | |
if (spaceshoot = 1) | |
hot%toggle%global("Space") | |
if (numpadkeys = 1) | |
hot%toggle%global("Numpad8") | |
pmode := pau ? "dis" : "ena" | |
%pmode%("bf8") | |
return | |
;SHOOT | |
#IfWinActive | |
F8:: | |
bf8: | |
Numpad8:: | |
WheelDown:: | |
Space:: | |
if wkey or akey | |
return | |
if (pau = 1) or !proj | |
return | |
pau := 1 | |
counter := !counter ? 1 : counter | |
count_string := SubStr("000000000000" . counter+enum, -3) ;pad 1 to 0001 | |
Gui, Color, silver | |
sleep(wait) | |
if pcsave ;do *not* enclose path in rsint mode | |
{ | |
if acti(leftcam) | |
xs("path " workdirfrontslash "/" proj "/" count_string "R",leftcam), xs("s", leftcam) | |
if acti(rightcam) | |
xs("path " workdirfrontslash "/" proj "/" count_string "L",rightcam), xs("s", rightcam) | |
} | |
if !pcsave | |
{ | |
if acti(leftcam) | |
xs("shoot " rsintoptions,leftcam) | |
if acti(rightcam) | |
xs("shoot " rsintoptions,rightcam) | |
} | |
actiscript() | |
pau := 0 | |
Gui, Color, D3E3D3 ;green | |
counter++ | |
count_string := SubStr("000000000000" . counter+enum, -3) ;pad 1 to 0001 | |
GuiControl,, fileenum, next filename %count_string% | |
Gui, Submit, NoHide | |
return | |
#IfWinActive | |
NumpadEnter:: ;save blank txt, for troubleshooting | |
save_txt_count: | |
fileappend,,%workdir%\%proj%\%count_string%.txt ;0003.txt = count of last saved images | |
return | |
;numpad modifier keys | |
;also prevents sending 012 chars accidentally to cmd window | |
#IfWinActive, ahk_Exe cmd.exe | |
Numpad0:: nkey := 1 | |
Numpad1:: ckey := 1 | |
Numpad2:: skey := 1 | |
return | |
;function: return 1 if input is non-empty and contains only 0123456789 chars | |
isDigit(x){ | |
if (x != "") ;not empty | |
if x is digit ;only 0123456789 chars | |
return 1 | |
} | |
;function: sleep | |
sleep(x) { | |
sleep %x% | |
} | |
;function: msgbox errormessage and reload script | |
msg_and_reload(x) { | |
msgbox, %x% | |
reload | |
} |