I got hooked on this script and started to noodle with it. Well, I may have gotten carried away. It's still a work in progress. I present it here "as is". I did not change the documentation in the header to match my changes.
Here's what I've done:
- automatically delete the temporary work file
- allow a drawing to be appended after replay when NOT compiled
- changed the playback script to use the drawLine function (this eliminated mouse interference during playback and fixed a problem where the playback would not obey the mouse button UP)
- added a help function to F1 and moved the function of the other keys to F2 thru F5
- added color names (and moved the color order around a bit)
- added a transient tooltip that shows the color and width when changed
Code:
;
;modified by Relayer 06-08-2010
; from:
; http://www.autohotkey.com/forum/viewtopic.php?p=253705#253705
; Shimanov, Metaxal, maximo3491
; If Shimanov agrees:
; GNU General Public License 3.0 or higher <http://www.gnu.org/licenses/gpl-3.0.txt>
; Version 1.6 - Metaxal 2010/06/06
; - creates the batch scripts (DrawOnSCreen and compile) to replay files and compile them
; - script can replay a saved script! (not anymore in paint, but on the screen directly!)
; using the batch file.
; Works even with multiple-screens!
; - keypresses are also logged! (erase screen, widen pen, etc.)
; Version 1.5 - Metaxal 2010/06/05
; - added multiple display support
; TODO:
; - more colors, better UI ?
; - scale mouse move to screen resolution ? (-> still ok if script replayed on a different screen)
; not easy to do
; - command line parameters (initial width, color, etc.)
/*
********************
*** README FIRST ***
********************
** Simple usage **
Just launch this script, draw on the screen with mouse moves and key F1-F4,
and press Escape when you're finished.
F1: change color
F2: erase screen
F3: pen_width-
F4: pen_width+
Notice that 4 files are created the first time you launch DrawOnScreen.ahk:
- DrawOnScreen.bat (see next section)
- compile.bat (to create executables)
- painting.ahk (the saved mouse movements)
- paintingtemp.ahk (temporary work file)
WARNING: From now on, you should must DrawOnScreen.bat instead of DrawOnScreen.ahk
** Replaying mouse-move scripts **
After pressing Escape, the mouse movements are saved to "painting.ahk".
Rename this file to whatever name you want to save it.
To replay a saved file, just drag&drop it onto the batch script "DrawOnScreen.bat".
At the end of the replay, you may need to press Escape to return to Windows.
** Compiling move scripts **
To create a stand-alone executable from a saved move file,
just drag&drop it onto the batch script "compile.bat".
At the end of the replay, it will automatically exit after 1 second.
You can now send this .exe file to a friend!
Beware however that his/her screen resolution may be different from yours.
** Modifying a move script **
To modify the mouse speed, or end the script as soon as it is finished,
check the first and last lines of the generated script.
You can of course make other adjustments if you need.
*/
; added by JB
; Menu, Tray, Icon, %A_ScriptDir%\159.ico
TO := 0
create_batch(bat_file, text) {
if !( A_IsCompiled OR FileExist(bat_file))
FileAppend %text%, %bat_file%
}
compile_bat =
(
:: move to script directory
cd /d `%0\..
copy `%1 paintingtemp.ahk
>> paintingtemp.ahk echo.
>> paintingtemp.ahk echo Sleep 1000
>> paintingtemp.ahk echo ExitApp
"%A_AhkPath%\..\Compiler\Ahk2exe.exe" /in DrawOnScreen.ahk /out "`%~n1.exe"
)
; Create the compiler if does not exists
create_batch("compile.bat", compile_bat)
drawonscreen_bat = ; no interpolation of variables
( %
:: move to script directory
cd /d %0\..
:: does nothing if %1 does not exist:
copy %1 paintingtemp.ahk
start c:\utilities\autohotkey\autohotkey.exe DrawOnScreenJB2.ahk %1
)
if !( A_IsCompiled OR FileExist("DrawOnScreen.bat"))
{
; Create batch launching file if does not exist (and not compiled)
create_batch("DrawOnScreen.bat", drawonscreen_bat)
MsgBox Please now use the DrawOnScreen.bat file
ExitApp
}
color_codes=0x000000,0x0000FF,0xFF0000,0x008000,0x00FFFF,0xC0C0C0,0x808080,0xFFFFFF,0x800000,0x800080,0xFF00FF,0x00FF00,0x808000,0xFFFF00,0x000080,0x008080
color_names=Black,Red,Blue,Green,Yellow,Silver,Gray,White,Navy,Purple,Fuchsia,Lime,Teal,Aqua,Maroon,Olive
max_width = 200 ; max width of the brush
log_movement := !( A_IsCompiled ) && !(%0% > 0) ; 0 for false, 1 for true
append_movement := false
;msgbox, %log_movement%
CoordMode, Mouse, Screen
Process, Exist
pid_this := ErrorLevel
hdc_screen := DllCall( "GetDC", "uint", 0 )
; Get the number of monitors:
SysGet, MonitorCount, MonitorCount
MonitorNum := 1 ; primary monitor
; if multiple displays:
if (MonitorCount > 1) {
Gui, Add, Text,, Select the display:
Gui, Add, Text,, 1
Gui, Add, UpDown, vMonitorNum Range1-%MonitorCount%, 1
Gui, Add, Button, default, OK ; The label ButtonOK (if it exists) will be run when the button is pressed.
Gui, Show
return ; End of auto-execute section. The script is idle until the user does something.
}
ButtonOK:
Gui, Submit
Gui, Destroy
Sleep, 100 ; wait for the GUI to hide
; Get info about the monitor size
SysGet, Screen, Monitor, %MonitorNum%
; -> creates the 4 variables ScreenLeft, ScreenTop, ScreenRight, ScreenBottom
ScreenWidth := ScreenRight - ScreenLeft
ScreenHeight := ScreenBottom - ScreenTop
; Create a working buffer
hdc_buffer := DllCall( "CreateCompatibleDC", "uint", hdc_screen )
hbm_buffer := DllCall( "CreateCompatibleBitmap", "uint", hdc_screen, "int", ScreenWidth, "int", ScreenHeight )
DllCall( "SelectObject", "uint", hdc_buffer, "uint", hbm_buffer )
; Init the buffer with a screenshot
DllCall( "BitBlt", "uint", hdc_buffer, "int", 0, "int", 0, "int", ScreenWidth, "int", ScreenHeight, "uint", hdc_screen, "int", ScreenLeft, "int", ScreenTop, "uint", 0x00CC0020 )
; Create a (completely transparent) window with the size of the display
Gui, +AlwaysOnTop -Caption
Gui, Show, x%ScreenLeft% y%ScreenTop% w%ScreenWidth% h%ScreenHeight%
; Get the hwnd associated with the canvas of the current window
WinGet, hw_canvas, ID, ahk_class AutoHotkeyGUI ahk_pid %pid_this%
; Get the canvas of the created window
hdc_canvas := DllCall( "GetDC", "uint", hw_canvas )
; Begin by drawing the background in the canvas
DllCall( "BitBlt", "uint", hdc_canvas, "int", 0, "int", 0, "int", ScreenWidth, "int", ScreenHeight, "uint", hdc_buffer, "int", 0, "int", 0, "uint", 0x00CC0020 )
; not exactly because colors are reverse in SetPixel...
StringSplit colors, color_codes, `,
StringSplit cnames, color_names, `,
color_index := 1 ;first color to show
color := colors%color_index%
width := 2
x_last := 0
y_last := 0
Pace := 0
SetBatchLines, -1
SetMouseDelay 1.5
left_down := false
right_down := false
/*
http://msdn.microsoft.com/en-us/library/ms645616(VS.85).aspx
wParam:
*/
WM_MOUSEMOVE = 0x0200
/*
; numbers
MK_CONTROL := 0x0008
MK_LBUTTON := 0x0001
MK_MBUTTON := 0x0010
MK_RBUTTON := 0x0002
MK_SHIFT := 0x0004
MK_XBUTTON1 := 0x0020
MK_XBUTTON2 := 0x0040
*/
if log_movement
{
log .= "`n`;change the PaceFactor to speed up or slow down the replay"
log .= "`n`;PaceFactor = 0 is the fastest"
log .= "`nPaceFactor := 1"
}
#Include *i paintingtemp.ahk
;this has been moved below the include to prevent mouse movements from messing with a replay
;it is enabled after so that an append can take place
if !( A_IsCompiled )
OnMessage( WM_MOUSEMOVE, "HandleMessage" )
;; End of auto-executed section
return
HandleMessage( p_w, p_l )
{
global hdc_canvas, hdc_buffer, x_last, y_last, width, color, left_down, right_down, log, log_movement, ScreenLeft, ScreenTop, Pace, append_movement
x := (p_l & 0xFFFF)
y := (p_l >> 16)
lbutton := p_w & 0x0001 ; MK_LBUTTON
rbutton := p_w & 0x0002 ; MK_RBUTTON
if (lbutton)
{
if not left_down
{
SetTimer, Pacer, 10
left_down := true
if !(log_movement)
append_movement := true
}
else
{
drawLine(x_last, y_last, x, y, color, width)
log .= "`nSleep, % " . Pace*10 . "*PaceFactor"
log .= "`ndrawLine(" . x_last . "," . y_last . "," . x . "," . y . "," . color . "," . width . ")"
Pace := 1
}
}
else if (left_down)
left_down := false
if (rbutton)
{
if not right_down
{
SetTimer, Pacer, 10
right_down := true
if !(log_movement)
append_movement := true
}
else
{
drawLine(x_last, y_last, x, y, "ERASE", width)
log .= "`nSleep, % " . Pace*10 . "*PaceFactor"
log .= "`ndrawLine(" . x_last . "," . y_last . "," . x . "," . y . ",""ERASE"" ," . width . ")"
Pace := 1
}
}
else if (right_down)
right_down := false
x_last := x
y_last := y
}
drawLine(x0, y0, x1, y1, color_ini=0, width=1)
{
dx := x1 - x0
dy := y1 - y0
dxy := (Abs(dx) > Abs(dy) ? Abs(dx) : Abs(dy) )
dx := dx / dxy
dy := dy / dxy
Loop %dxy%
{
x0 += dx
y0 += dy
drawCircle(Round(x0),round(y0), color_ini)
}
}
drawCircle(x,y, color_ini) {
global hdc_buffer, hdc_canvas, width, ScreenLeft, ScreenTop, ScreenWidth, ScreenHeight
cRegion := DllCall( "gdi32.dll\CreateRoundRectRgn", "int", x-width , "int", y-width , "int", x+width , "int", y+width, "int", width*2, "int", width*2 )
cBrush := DllCall("gdi32.dll\CreateSolidBrush", "uint", color_ini )
if color_ini=ERASE
{
; from http://msdn.microsoft.com/en-us/library/dd183437%28VS.85%29.aspx
; Select clipping region
DllCall( "SelectClipRgn", "uint", hdc_canvas, "uint", cRegion )
; Transfer (draw) the bitmap into the clipped rectangle.
DllCall( "BitBlt", "uint", hdc_canvas, "int", 0, "int", 0, "int", ScreenWidth, "int", ScreenHeight, "uint", hdc_buffer, "int", 0, "int", 0, "uint", 0x00CC0020 )
; Select the full region back
DllCall( "SelectClipRgn", "uint", hdc_canvas, "uint", 0 )
}
else
DllCall( "gdi32.dll\FillRgn" , "uint", hdc_canvas , "uint", cRegion , "uint", cBrush )
DllCall("gdi32.dll\DeleteObject", "uint", cRegion)
DllCall("gdi32.dll\DeleteObject", "uint", cBrush)
}
rotate_color() {
global
color_index++
if(color_index > 16)
color_index := 1
Gosub, ShowTip
}
erase_canvas() {
global
DllCall( "BitBlt", "uint", hdc_canvas, "int", 0, "int", 0, "int", ScreenWidth, "int", ScreenHeight, "uint", hdc_buffer, "int", 0, "int", 0, "uint", 0x00CC0020 )
}
width_minus() {
global
if width > 1
width--
Gosub, ShowTip
}
width_plus() {
global
if width < %max_width%
width++
Gosub, ShowTip
}
log_function(fun_str) {
global log
log .= "`nSleep 10`n" . fun_str
}
F1::
if (TO = 1)
Goto, nTO
TO := 1
OnMessage( WM_MOUSEMOVE, "" )
Help = Draw on Screen
Help = %Help%`n left = draw
Help = %Help%`n right = erase
Help = %Help%`n Escape = quit
Help = %Help%`n F2 = toggle colors
Help = %Help%`n F3/F4 = dec/inc width
Help = %Help%`n F5 = erases screen
;Help = %Help%`n F1: toggle colors
ToolTip, %Help%
Return
nTO:
TO := 0
Tooltip
if !( A_IsCompiled )
OnMessage( WM_MOUSEMOVE, "HandleMessage" )
Return
ShowTip:
if !( A_IsCompiled )
{
color := colors%color_index%
cname := cnames%color_index%
Tip := cnames%color_index%
ToolTip, %Tip% %width%
SetTimer, EndTip, 700
}
Return
EndTip:
SetTimer, EndTip, Off
ToolTip
Return
F2::
rotate_color()
log_function("rotate_color()")
return
F5::
erase_canvas()
log_function("erase_canvas()")
return
F3::
width_minus()
log_function("width_minus()")
return
F4::
width_plus()
log_function("width_plus()")
return
Pacer:
Pace += 1
Return
Escape::
if ((log_movement || append_movement) && (Pace <> 0))
{
if !(append_movement)
FileDelete, painting.ahk
log .= "`n`;Sleep 1000 `; uncomment these two lines..."
log .= "`n`;ExitApp `; ...to end the script as soon as it is finished"
FileAppend, %log%, painting.ahk
}
FileDelete, paintingtemp.ahk
ExitApp