├── .gitignore ├── INSTALL ├── RESOURCES │ ├── LPHK-banner.bmp │ └── LPHK.bmp ├── environment-build.yml ├── environment.yml ├── linux_beta │ ├── build_pyinstall_installed.bash │ └── install_linux.bash ├── requirements.txt └── windows │ ├── build_pyinstall_installed.bat │ ├── build_pyinstall_portable.bat │ ├── build_setup.bat │ ├── install_build_env.bat │ ├── install_conda_windows.bat │ ├── make_release.bat │ └── windows.iss ├── LICENSE ├── LPHK.py ├── README.md ├── README_FILES ├── LPHK_icon_square.png ├── LPHK_update_4.png ├── Old │ ├── LPHK_first_look.png │ ├── LPHK_update_1.png │ ├── LPHK_update_2.png │ └── LPHK_update_3.png ├── Source │ ├── LPHK.ai │ └── LPHK_icon.ai ├── discord.png ├── patreon.png └── spacer.png ├── RUN.bash ├── RUN_with_local_venv.bat ├── VERSION ├── bresenham.py ├── files.py ├── kb.py ├── logger.py ├── lp_colors.py ├── lp_events.py ├── ms.py ├── parse.py ├── resources ├── LPHK-banner.png ├── LPHK.gif ├── LPHK.ico ├── alert.png ├── error.png ├── info.png ├── running.png ├── scare.png ├── scheduled.png ├── script.png └── warning.png ├── scripts.py ├── sound.py ├── system_apis ├── keyboard_unix.py └── keyboard_win.py ├── user_layouts └── examples │ ├── Black_Mesa_HL2_Mod.lpl │ ├── DOOM.lpl │ ├── GIMP_paint.lpl │ ├── Half-Life_2_New_Engine.lpl │ ├── Undertale.lpl │ ├── all_delays_all_day.lpl │ ├── all_delays_all_gay.lpl │ └── default.lpl ├── user_scripts └── examples │ ├── LPHK_project_page.lps │ ├── LPMM_project_page.lps │ ├── chrome_developer_console.lps │ ├── hello_world.lps │ ├── mute.lps │ ├── next_track.lps │ ├── nonblocking_press_and_release.lps │ ├── play_pause.lps │ ├── previous_track.lps │ ├── python_for_template.lps │ ├── sound.lps │ ├── tv.nimaid.com.lps │ ├── volume_down.lps │ ├── volume_up.lps │ ├── windows_show_desktop.lps │ └── youtube.lps ├── user_sounds └── examples │ ├── airhorn.wav │ ├── doublekill.wav │ ├── killingspree.wav │ ├── killtacular.wav │ ├── runningriot.wav │ └── triplekill.wav ├── utils ├── GET_KEYCODES.py ├── LIST_PADS.py ├── RAW_CONNECT.py ├── build.bat ├── compiled │ ├── GET_KEYCODES.exe │ ├── LIST_PADS.exe │ └── RAW_CONNECT.exe └── launchpad_connector.py └── window.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # intellij/PyCharm 10 | .idea/ 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | *.lnk 109 | user_layouts/*.* 110 | __setup__/* 111 | __release__/* 112 | USERPATH 113 | -------------------------------------------------------------------------------- /INSTALL/RESOURCES/LPHK-banner.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/INSTALL/RESOURCES/LPHK-banner.bmp -------------------------------------------------------------------------------- /INSTALL/RESOURCES/LPHK.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/INSTALL/RESOURCES/LPHK.bmp -------------------------------------------------------------------------------- /INSTALL/environment-build.yml: -------------------------------------------------------------------------------- 1 | name: LPHK-build 2 | channels: 3 | - defaults 4 | dependencies: 5 | - python=3.7* 6 | - pip 7 | - tk 8 | - pip: 9 | - git+https://github.com/FMMT666/launchpad.py.git@master 10 | - pillow 11 | - pygame 12 | - pynput 13 | - tkcolorpicker 14 | - https://github.com/pyinstaller/pyinstaller/archive/develop.zip 15 | - py-getch 16 | - pyautogui 17 | -------------------------------------------------------------------------------- /INSTALL/environment.yml: -------------------------------------------------------------------------------- 1 | name: LPHK 2 | channels: 3 | - defaults 4 | dependencies: 5 | - python=3.7* 6 | - pip 7 | - tk 8 | - pip: 9 | - git+https://github.com/FMMT666/launchpad.py.git@master 10 | - pillow 11 | - pygame 12 | - pynput 13 | - tkcolorpicker 14 | - py-getch 15 | - pyautogui 16 | -------------------------------------------------------------------------------- /INSTALL/linux_beta/build_pyinstall_installed.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -i 2 | 3 | SCRIPTPATH=$(readlink -f "$0") 4 | SCRIPTDIR=$(dirname "$SCRIPTPATH") 5 | INSTALLDIR=$SCRIPTDIR/.. 6 | MAINDIR=$INSTALLDIR/.. 7 | 8 | ORIGDIR=$(pwd) 9 | 10 | cd $MAINDIR 11 | 12 | conda activate LPHK-build 13 | pyinstaller \ 14 | --noconfirm \ 15 | --onedir \ 16 | --windowed \ 17 | --hidden-import='PIL._tkinter_finder' \ 18 | --add-data VERSION:. \ 19 | --add-data resources/:resources/ \ 20 | LPHK.py 21 | conda deactivate 22 | 23 | cd $ORIGDIR 24 | -------------------------------------------------------------------------------- /INSTALL/linux_beta/install_linux.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -i 2 | 3 | # This is a beta installer script that is the first step 4 | # in making the installation process painless. All you have 5 | # to do is run this. It will install Miniconda3 if you don't 6 | # have a conda installation, and will ask if you want to install LPHK. 7 | # A shortcut will be created for LPHK in the main LPHK folder. 8 | # If you wish to uninstall, run this after LPHK is installed. You will 9 | # have to remove the shortcuts, and manually delete the files. 10 | 11 | # Please let me know if this does or does not work in the Discord! 12 | 13 | CONDAEXE= 14 | CONDA= 15 | LPHKENVDIR= 16 | DOINSTALLCONDA=0 17 | DOINSTALLLPHK=0 18 | DOUNINSTALLLPHK=0 19 | DOUNINSTALLCONDA=0 20 | 21 | SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 22 | MINICONDADIR=~/miniconda3 23 | 24 | function pause () { 25 | read -rsp $'Press any key to continue...\n' -n1 key 26 | } 27 | 28 | function prompt_yn () { 29 | read -p "Do you wish to continue? (Y/[N]): " -n 1 -r 30 | echo 31 | if [[ $REPLY =~ ^[Yy]$ ]]; then 32 | return 1 33 | else 34 | return 0 35 | fi 36 | } 37 | 38 | function exit_if_error () { 39 | if [ ! $? = 0 ]; then 40 | echo "An error has occured! Exiting..." 41 | pause 42 | exit 1 43 | fi 44 | } 45 | 46 | 47 | 48 | function install_conda () { 49 | CONDAEXE=/tmp/$RANDOM-$RANDOM-$RANDOM-$RANDOM-condainstall.sh 50 | 51 | if [ $(uname -m) == 'x86_64' ]; then 52 | wget 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh' -O $CONDAEXE 53 | else 54 | wget 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86.sh' -O $CONDAEXE 55 | fi; exit_if_error 56 | 57 | sudo chmod +x $CONDAEXE; exit_if_error 58 | sudo $CONDAEXE -b -p $MINICONDADIR; exit_if_error 59 | sudo rm $CONDAEXE 60 | 61 | CONDA=$MINICONDADIR/bin/conda 62 | 63 | $CONDA init; exit_if_error 64 | 65 | source ~/.bashrc; exit_if_error 66 | 67 | conda config --set auto_activate_base False 68 | } 69 | 70 | function uninstall_conda () { 71 | sed -i '/^# >>> conda initialize >>>/,/^# <<< conda initialize << /dev/null 2>&1 && CONDAGOOD=1 || CONDAGOOD=0 90 | if [ $CONDAGOOD = 0 ]; then 91 | echo "No conda found. Install Miniconda3 and LPHK?" 92 | prompt_yn 93 | DOINSTALLCONDA=$? 94 | if [ $DOINSTALLCONDA = 1 ]; then 95 | echo "Installing Miniconda3..." 96 | install_conda 97 | echo "Miniconda3 has been installed!" 98 | else 99 | echo "Not installing Miniconda3, exiting..." 100 | pause 101 | exit 102 | fi 103 | fi 104 | 105 | # If LPHK is already installed, offer to uninstall 106 | LPHKENVDIR=$(cat ~/.conda/environments.txt | grep LPHK) > /dev/null 2>&1 107 | if [ ! -z $LPHKENVDIR ]; then 108 | echo "LPHK already installed! Uninstall LPHK?" 109 | prompt_yn 110 | DOUNINSTALLLPHK=$? 111 | if [ $DOUNINSTALLLPHK = 1 ]; then 112 | if [ -d "$MINICONDADIR" ]; then 113 | echo "Uninstall Miniconda3 as well? (THIS WILL DELETE ALL OTHER CONDA ENVIRONMENTS)" 114 | prompt_yn 115 | DOUNINSTALLCONDA=$? 116 | else 117 | DOUNINSTALLCONDA=0 118 | fi 119 | 120 | echo "Uninstalling LPHK..." 121 | uninstall_LPHK 122 | echo "LPHK uninstalled!" 123 | 124 | if [ $DOUNINSTALLCONDA = 1 ]; then 125 | echo "Uninstalling Miniconda3..." 126 | uninstall_conda 127 | echo "Miniconda3 uninstalled!" 128 | fi 129 | 130 | pause 131 | exit 132 | else 133 | echo "Not uninstalling LPHK, exiting..." 134 | pause 135 | exit 136 | fi 137 | # If LPHK is not installed, offer to install 138 | else 139 | if [ $DOINSTALLCONDA = 1 ]; then 140 | DOINSTALLLPHK=1 141 | else 142 | echo "Install LPHK?" 143 | prompt_yn 144 | DOINSTALLLPHK=$? 145 | fi 146 | if [ $DOINSTALLLPHK = 1 ]; then 147 | echo "Installing LPHK..." 148 | install_LPHK 149 | # Get the new location after install 150 | LPHKENVDIR=$(cat ~/.conda/environments.txt | grep LPHK) > /dev/null 2>&1 151 | echo "LPHK environment set up. Run '[your lphk directory]/run.bash'" 152 | else 153 | echo "Not installing LPHK, exiting..." 154 | pause 155 | exit 156 | fi 157 | fi 158 | -------------------------------------------------------------------------------- /INSTALL/requirements.txt: -------------------------------------------------------------------------------- 1 | MouseInfo==0.1.3 2 | Pillow==9.3.0 3 | py-getch==1.0.1 4 | PyAutoGUI==0.9.50 5 | pygame==2.1.2 6 | PyGetWindow==0.0.8 7 | PyMsgBox==1.0.7 8 | pynput==1.6.8 9 | pyperclip==1.8.0 10 | PyRect==0.1.4 11 | PyScreeze==0.1.26 12 | python-xlib==0.27 13 | python3-xlib==0.15 14 | PyTweening==1.0.3 15 | six==1.14.0 16 | tkcolorpicker==2.1.3 17 | -e git+https://github.com/FMMT666/launchpad.py.git@master#egg=launchpad-py 18 | -------------------------------------------------------------------------------- /INSTALL/windows/build_pyinstall_installed.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set ORIGDIR="%CD%" 6 | 7 | cd %~dp0\..\.. 8 | 9 | echo Building installed EXE... 10 | call conda run -n LPHK-build pyinstaller ^ 11 | --noconfirm ^ 12 | --add-data VERSION;. ^ 13 | --add-data resources\;resources\ ^ 14 | --onedir ^ 15 | --windowed ^ 16 | --icon=resources\LPHK.ico ^ 17 | LPHK.py 18 | if errorlevel 1 goto ERROR 19 | 20 | goto DONE 21 | 22 | 23 | :ERROR 24 | cd %ORIGDIR% 25 | echo Installed EXE build failed! 26 | exit /B 1 27 | 28 | :DONE 29 | cd %ORIGDIR% 30 | echo Installed EXE build done! 31 | exit /B 0 -------------------------------------------------------------------------------- /INSTALL/windows/build_pyinstall_portable.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set ORIGDIR="%CD%" 6 | 7 | cd %~dp0\..\.. 8 | 9 | echo Building portable EXE... 10 | call conda run -n LPHK-build pyinstaller ^ 11 | --noconfirm ^ 12 | --add-data VERSION;. ^ 13 | --add-data resources\;resources\ ^ 14 | --onefile ^ 15 | --windowed ^ 16 | --icon=resources\LPHK.ico ^ 17 | LPHK.py 18 | if errorlevel 1 goto ERROR 19 | 20 | goto DONE 21 | 22 | 23 | :ERROR 24 | cd %ORIGDIR% 25 | echo Portable EXE build failed! 26 | exit /B 1 27 | 28 | :DONE 29 | cd %ORIGDIR% 30 | echo Portable EXE build done! 31 | exit /B 0 -------------------------------------------------------------------------------- /INSTALL/windows/build_setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set ORIGDIR="%CD%" 6 | 7 | set ISCC="C:\Program Files (x86)\Inno Setup 6\ISCC.exe" 8 | if exist %ISCC% goto BUILD 9 | echo Inno Setup Studio 6 not found! (%ISCC%) 10 | echo Download the latest installer at http://files.jrsoftware.org/is/6/ 11 | goto ERROR 12 | 13 | :BUILD 14 | echo Building Installer... 15 | %ISCC% "%~dp0\windows.iss" 16 | if errorlevel 1 goto ERROR 17 | 18 | goto DONE 19 | 20 | 21 | :ERROR 22 | echo Installer build failed! 23 | exit /B 1 24 | 25 | :DONE 26 | echo Installer build done! 27 | exit /B 0 -------------------------------------------------------------------------------- /INSTALL/windows/install_build_env.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set ORIGDIR="%CD%" 6 | 7 | set "LPHKENV=" 8 | set "STARTPATH=" 9 | 10 | set "ENVNAME=LPHK-build" 11 | 12 | where conda >nul 2>nul 13 | if %ERRORLEVEL% NEQ 0 goto CONDAMISSING 14 | 15 | :INSTALLENV 16 | REM TODO: Use environments.txt 17 | FOR /F "tokens=*" %%g IN ('conda env list ^| findstr /R /C:"%ENVNAME%"') do (set LPHKENV="%%g") 18 | if defined LPHKENV goto ALREADYINSTALLED 19 | 20 | echo Installing LPHK build environment... 21 | set STARTPATH="%CD%" 22 | cd "%~dp0" 23 | call conda env create -f ../environment-build.yml 24 | cd %ORIGDIR% 25 | if errorlevel 1 goto INSTALLENVFAIL 26 | 27 | cd %STARTPATH% 28 | echo LPHK build environment installed! 29 | goto END 30 | 31 | :CONDAMISSING 32 | echo Conda is not installed! 33 | goto ERROREND 34 | 35 | :INSTALLENVFAIL 36 | REM TODO: Use environments.txt 37 | rmdir %USERPROFILE%\Miniconda3\envs\%ENVNAME% /s /q 2> nul 38 | goto ERROREND 39 | 40 | :ALREADYINSTALLED 41 | echo LPHK build environment is already installed! 42 | goto END 43 | 44 | :ERROREND 45 | echo The LPHK build environment could not be installed! 46 | exit /B 1 47 | 48 | :END 49 | exit /B 0 50 | -------------------------------------------------------------------------------- /INSTALL/windows/install_conda_windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set ORIGDIR="%CD%" 6 | 7 | set MINICONDAPATH=%USERPROFILE%\Miniconda3 8 | set CONDAEXE=%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-condainstall.exe 9 | set "OS=" 10 | set "MCLINK=" 11 | 12 | where conda >nul 2>nul 13 | if %ERRORLEVEL% EQU 0 goto CONDAFOUND 14 | 15 | :INSTALLCONDA 16 | reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OS=32BIT || set OS=64BIT 17 | if %OS%==32BIT set MCLINK=https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86.exe 18 | if %OS%==64BIT set MCLINK=https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe 19 | 20 | echo Downloading Miniconda3 (This will take while, please wait)... 21 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%MCLINK%', '%CONDAEXE%')" >nul 2>nul 22 | if errorlevel 1 goto CONDAERROR 23 | 24 | echo Installing Miniconda3 (This will also take a while, please wait)... 25 | start /wait /min "Installing Miniconda3..." "%CONDAEXE%" /InstallationType=JustMe /S /D="%MINICONDAPATH%" 26 | del "%CONDAEXE%" 27 | if not exist "%MINICONDAPATH%\" (goto CONDAERROR) 28 | 29 | "%MINICONDAPATH%\Scripts\conda.exe" init 30 | if errorlevel 1 goto CONDAERROR 31 | 32 | echo Miniconda3 has been installed! 33 | goto END 34 | 35 | :CONDAERROR 36 | echo Miniconda3 install failed! 37 | exit /B 1 38 | 39 | :CONDAFOUND 40 | echo Conda is already installed! 41 | goto END 42 | 43 | :END 44 | exit /B 0 -------------------------------------------------------------------------------- /INSTALL/windows/make_release.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM DO NOT USE THIS SCRIPT. It is for creating new releases. 4 | 5 | set RELEASEDIRNAME=__release__ 6 | 7 | set ORIGDIR="%CD%" 8 | 9 | set INSTALLWINDIR=%~dp0 10 | set INSTALLDIR=%INSTALLWINDIR%\.. 11 | set LPHKDIR=%INSTALLDIR%\.. 12 | 13 | set RELEASEDIR=%LPHKDIR%\%RELEASEDIRNAME% 14 | set SETUPDIR=%LPHKDIR%\__setup__ 15 | set DISTDIR=%LPHKDIR%\dist 16 | set BUILDDIR=%LPHKDIR%\build 17 | 18 | set /p VERSION=<"%LPHKDIR%\VERSION" 19 | if [%VERSION%] == [] goto ERROR 20 | 21 | set PORTABLENAME=LPHK_portable_win_%VERSION% 22 | set PORTABLEDIR=%RELEASEDIR%\LPHK 23 | 24 | set EXENAME=LPHK.exe 25 | 26 | 27 | call cmd /c "%INSTALLWINDIR%\install_conda_windows.bat" 28 | if errorlevel 1 goto ERROR 29 | 30 | 31 | :CONDAFOUND 32 | call cmd /c "%INSTALLWINDIR%\install_build_env.bat" 33 | if errorlevel 1 goto ERROR 34 | 35 | echo Cleaning up before making release... 36 | del /f /s /q "%RELEASEDIR%" 1>nul 2>&1 37 | rmdir /s /q "%RELEASEDIR%" 1>nul 2>&1 38 | 39 | del /f /s /q "%SETUPDIR%" 1>nul 2>&1 40 | rmdir /s /q "%SETUPDIR%" 1>nul 2>&1 41 | 42 | del /f /s /q "%DISTDIR%" 1>nul 2>&1 43 | rmdir /s /q "%DISTDIR%" 1>nul 2>&1 44 | 45 | del /f /s /q "%BUILDDIR%" 1>nul 2>&1 46 | rmdir /s /q "%BUILDDIR%" 1>nul 2>&1 47 | 48 | echo Making release version %VERSION% now... 49 | call cmd /c "%INSTALLWINDIR%\build_pyinstall_installed.bat" 50 | if errorlevel 1 goto ERROR 51 | 52 | call cmd /c "%INSTALLWINDIR%\build_setup.bat" 53 | if errorlevel 1 goto ERROR 54 | 55 | echo Moving setup to release... 56 | rename "%SETUPDIR%" "%RELEASEDIRNAME%" 1>nul 2>&1 57 | if errorlevel 1 goto ERROR 58 | 59 | call cmd /c "%INSTALLWINDIR%\build_pyinstall_portable.bat" 60 | if errorlevel 1 goto ERROR 61 | 62 | echo Moving needed portable files to folder... 63 | mkdir "%PORTABLEDIR%" 1>nul 2>&1 64 | xcopy "%DISTDIR%\%EXENAME%" "%PORTABLEDIR%" 1>nul 2>&1 65 | if not exist "%PORTABLEDIR%\%EXENAME%" goto ERROR 66 | 67 | mkdir "%PORTABLEDIR%\user_layouts\examples" 1>nul 2>&1 68 | xcopy /s "%LPHKDIR%\user_layouts\examples" "%PORTABLEDIR%\user_layouts\examples" 1>nul 2>&1 69 | if not exist "%PORTABLEDIR%\user_layouts\examples" goto ERROR 70 | 71 | mkdir "%PORTABLEDIR%\user_scripts\examples" 1>nul 2>&1 72 | xcopy /s "%LPHKDIR%\user_scripts\examples" "%PORTABLEDIR%\user_scripts\examples" 1>nul 2>&1 73 | if not exist "%PORTABLEDIR%\user_scripts\examples" goto ERROR 74 | 75 | mkdir "%PORTABLEDIR%\user_sounds\examples" 1>nul 2>&1 76 | xcopy /s "%LPHKDIR%\user_sounds\examples" "%PORTABLEDIR%\user_sounds\examples" 1>nul 2>&1 77 | if not exist "%PORTABLEDIR%\user_sounds\examples" goto ERROR 78 | 79 | if errorlevel 1 goto ERROR 80 | 81 | echo Zipping portable version... 82 | powershell -Command Compress-Archive "%PORTABLEDIR%" "%RELEASEDIR%\%PORTABLENAME%.zip" 83 | if errorlevel 1 goto ERROR 84 | 85 | echo Cleaning up... 86 | del /f /s /q "%PORTABLEDIR%" 1>nul 2>&1 87 | rmdir /s /q "%PORTABLEDIR%" 1>nul 2>&1 88 | 89 | del /f /s /q "%DISTDIR%" 1>nul 2>&1 90 | rmdir /s /q "%DISTDIR%" 1>nul 2>&1 91 | 92 | del /f /s /q "%BUILDDIR%" 1>nul 2>&1 93 | rmdir /s /q "%BUILDDIR%" 1>nul 2>&1 94 | if errorlevel 1 goto ERROR 95 | 96 | goto DONE 97 | 98 | 99 | :ERROR 100 | echo Create release failed! 101 | exit /B 1 102 | 103 | :DONE 104 | echo Create release done! 105 | exit /B 0 -------------------------------------------------------------------------------- /INSTALL/windows/windows.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define FileHandle 5 | #define FileLine 6 | #define MyAppVersion 7 | #sub ProcessFileLine 8 | #define FileLine = FileRead(FileHandle) 9 | #define public MyAppVersion = FileLine 10 | #pragma message "Version: " + MyAppVersion 11 | #endsub 12 | #for {FileHandle = FileOpen("..\..\VERSION"); \ 13 | FileHandle && !FileEof(FileHandle); ""} \ 14 | ProcessFileLine 15 | #if FileHandle 16 | #expr FileClose(FileHandle) 17 | #endif 18 | 19 | #define MyAppName "LPHK" 20 | #define MyAppPublisher "Ella Jameson (nimaid)" 21 | #define MyAppURL "https://github.com/nimaid/LPHK" 22 | #define MyAppExeName "LPHK.exe" 23 | 24 | [Setup] 25 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 26 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 27 | AppId={{A6E030E7-C7D0-4EA7-BBD9-4AD52745451B} 28 | AppName={#MyAppName} 29 | AppVersion={#MyAppVersion} 30 | AppPublisher={#MyAppPublisher} 31 | AppPublisherURL={#MyAppURL} 32 | AppSupportURL={#MyAppURL} 33 | AppUpdatesURL={#MyAppURL} 34 | DefaultDirName={autopf}\{#MyAppName} 35 | DisableProgramGroupPage=auto 36 | DisableWelcomePage=no 37 | PrivilegesRequired=admin 38 | PrivilegesRequiredOverridesAllowed=dialog 39 | OutputDir=..\..\__setup__ 40 | OutputBaseFilename=LPHK_setup_{#MyAppVersion} 41 | SetupIconFile=..\..\resources\LPHK.ico 42 | UninstallDisplayIcon={app}\{#MyAppExeName} 43 | Compression=lzma 44 | SolidCompression=yes 45 | WizardImageFile=..\..\INSTALL\RESOURCES\LPHK-banner.bmp 46 | WizardSmallImageFile=..\..\INSTALL\RESOURCES\LPHK.bmp 47 | WizardStyle=modern 48 | 49 | [Languages] 50 | Name: "english"; MessagesFile: "compiler:Default.isl" 51 | 52 | [Tasks] 53 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 54 | 55 | [Files] 56 | Source: "..\..\dist\LPHK\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs 57 | Source: "..\..\user_layouts\examples\*"; DestDir: "{code:GetUserDir}\user_layouts\examples"; Flags: ignoreversion recursesubdirs createallsubdirs 58 | Source: "..\..\user_scripts\examples\*"; DestDir: "{code:GetUserDir}\user_scripts\examples"; Flags: ignoreversion recursesubdirs createallsubdirs 59 | Source: "..\..\user_sounds\examples\*"; DestDir: "{code:GetUserDir}\user_sounds\examples"; Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: SetLphkUserPath 60 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 61 | 62 | [Icons] 63 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 64 | Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 65 | 66 | [Messages] 67 | WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing.%n%nPlease be advised this program is still in beta. Kindly report any issues on the Discord server or GitHub. 68 | 69 | [Code] 70 | var UserDirPage: TInputDirWizardPage; 71 | 72 | procedure InitializeWizard(); 73 | begin 74 | UserDirPage := CreateInputDirPage(wpSelectDir, 75 | 'Select User Folder', 'Where should the User Folder be installed?', 76 | 'Setup will install layouts, scripts, sounds, and logs into the following folder.' + #13#10#13#10#13#10 + 77 | 'To continue, click Next. If you would like to select a different folder, click Browse.', 78 | False, ''); 79 | UserDirPage.Add(''); 80 | UserDirPage.Values[0] := ExpandConstant('{userdocs}\LPHK'); 81 | end; 82 | 83 | function GetUserDir(Param: String): String; 84 | begin 85 | Result := UserDirPage.Values[0]; 86 | end; 87 | 88 | procedure SetLphkUserPath(); 89 | begin 90 | SaveStringToFile(ExpandConstant('{app}\USERPATH'), ExpandConstant('{code:GetUserDir}'), False); 91 | end; 92 | 93 | [Run] 94 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: shellexec postinstall skipifsilent 95 | 96 | [UninstallDelete] 97 | Type: filesandordirs; Name: "{app}" 98 | Type: files; Name: "{code:GetUserDir}\*.log" 99 | -------------------------------------------------------------------------------- /LPHK.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from datetime import datetime 4 | 5 | print("\n!!!!!!!! DO NOT CLOSE THIS WINDOW WITHOUT SAVING !!!!!!!!\n") 6 | 7 | LOG_TITLE = "LPHK.log" 8 | 9 | # Get platform information 10 | PLATFORMS = [{"search_string": "win", "name_string": "windows"}, 11 | {"search_string": "linux", "name_string": "linux"}, 12 | {"search_string": "darwin", "name_string": "macintosh"}] 13 | PLATFORM = None 14 | for plat in PLATFORMS: 15 | if sys.platform.startswith(plat["search_string"]): 16 | PLATFORM = plat["name_string"] 17 | break 18 | if PLATFORM is None: 19 | PLATFORM = "other" 20 | 21 | # Test if this is a PyInstaller executable or a .py file 22 | if getattr(sys, 'frozen', False): 23 | IS_EXE = True 24 | PROG_FILE = sys.executable 25 | PROG_PATH = os.path.dirname(PROG_FILE) 26 | PATH = sys._MEIPASS 27 | else: 28 | IS_EXE = False 29 | PROG_FILE = os.path.realpath(__file__) 30 | PROG_PATH = os.path.dirname(PROG_FILE) 31 | PATH = PROG_PATH 32 | 33 | 34 | # Test if there is a user folder specified 35 | def get_first_textfile_line(file_path): 36 | with open(file_path, "r") as f: 37 | file_lines = f.readlines() 38 | first_line = file_lines[0] 39 | return first_line.strip() 40 | 41 | 42 | USERPATH_FILE = os.path.join(PATH, "USERPATH") 43 | if os.path.exists(USERPATH_FILE): 44 | IS_PORTABLE = False 45 | USER_PATH = get_first_textfile_line(USERPATH_FILE) 46 | os.makedirs(USER_PATH, exist_ok=True) 47 | else: 48 | IS_PORTABLE = True 49 | USER_PATH = PROG_PATH 50 | 51 | # Get program version 52 | VERSION = get_first_textfile_line(os.path.join(PATH, "VERSION")) 53 | 54 | # Setup dual logging/printing 55 | LOG_PATH = os.path.join(USER_PATH, LOG_TITLE) 56 | 57 | import logger 58 | 59 | logger.start(LOG_PATH) 60 | 61 | 62 | # Start printing output 63 | def datetime_str(): 64 | now = datetime.now() 65 | return now.strftime("%d/%m/%Y %H:%M:%S") 66 | 67 | 68 | print("---------------- BEGIN LOG", datetime_str(), "----------------") 69 | print("LPHK - LaunchPad HotKey - A Novation Launchpad Macro Scripting System") 70 | print("Version:", VERSION) 71 | print("Platform:", PLATFORM.capitalize()) 72 | print("Is compiled executable:", IS_EXE) 73 | print("Is portable:", IS_PORTABLE) 74 | print("Operating path:", PATH) 75 | print("User path:", USER_PATH) 76 | print("Program file path:", PROG_PATH) 77 | print("Program file:", PROG_FILE) 78 | print("Log file (this file):", LOG_PATH, end="\n\n") 79 | 80 | # Try to import launchpad.py 81 | try: 82 | import launchpad_py as launchpad 83 | except ImportError: 84 | try: 85 | import launchpad 86 | except ImportError: 87 | sys.exit("[LPHK] Error loading launchpad.py") 88 | print("") 89 | 90 | import lp_events, scripts, files, sound, window 91 | from utils import launchpad_connector 92 | 93 | lp = launchpad.Launchpad() 94 | 95 | EXIT_ON_WINDOW_CLOSE = True 96 | 97 | 98 | def init(): 99 | global EXIT_ON_WINDOW_CLOSE 100 | if len(sys.argv) > 1: 101 | if ("--debug" in sys.argv) or ("-d" in sys.argv): 102 | EXIT_ON_WINDOW_CLOSE = False 103 | print("[LPHK] Debugging mode active! Will not shut down on window close.") 104 | print("[LPHK] Run shutdown() to manually close the program correctly.") 105 | 106 | else: 107 | print("[LPHK] Invalid argument: " + sys.argv[1] + ". Ignoring...") 108 | 109 | files.init(USER_PATH) 110 | sound.init(USER_PATH) 111 | 112 | 113 | def shutdown(): 114 | if lp_events.timer is not None: 115 | lp_events.timer.cancel() 116 | scripts.to_run = [] 117 | for x in range(9): 118 | for y in range(9): 119 | if scripts.threads[x][y] is not None: 120 | scripts.threads[x][y].kill.set() 121 | if window.lp_connected: 122 | scripts.unbind_all() 123 | lp_events.timer.cancel() 124 | launchpad_connector.disconnect(lp) 125 | window.lp_connected = False 126 | logger.stop() 127 | if window.restart: 128 | if IS_EXE: 129 | os.startfile(sys.argv[0]) 130 | else: 131 | os.execv(sys.executable, ["\"" + sys.executable + "\""] + sys.argv) 132 | sys.exit("[LPHK] Shutting down...") 133 | 134 | 135 | def main(): 136 | init() 137 | window.init(lp, launchpad, PATH, PROG_PATH, USER_PATH, VERSION, PLATFORM) 138 | if EXIT_ON_WINDOW_CLOSE: 139 | shutdown() 140 | 141 | 142 | main() 143 | -------------------------------------------------------------------------------- /README_FILES/LPHK_icon_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/LPHK_icon_square.png -------------------------------------------------------------------------------- /README_FILES/LPHK_update_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/LPHK_update_4.png -------------------------------------------------------------------------------- /README_FILES/Old/LPHK_first_look.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Old/LPHK_first_look.png -------------------------------------------------------------------------------- /README_FILES/Old/LPHK_update_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Old/LPHK_update_1.png -------------------------------------------------------------------------------- /README_FILES/Old/LPHK_update_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Old/LPHK_update_2.png -------------------------------------------------------------------------------- /README_FILES/Old/LPHK_update_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Old/LPHK_update_3.png -------------------------------------------------------------------------------- /README_FILES/Source/LPHK.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Source/LPHK.ai -------------------------------------------------------------------------------- /README_FILES/Source/LPHK_icon.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/Source/LPHK_icon.ai -------------------------------------------------------------------------------- /README_FILES/discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/discord.png -------------------------------------------------------------------------------- /README_FILES/patreon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/patreon.png -------------------------------------------------------------------------------- /README_FILES/spacer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/README_FILES/spacer.png -------------------------------------------------------------------------------- /RUN.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -i 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 3 | python $DIR/LPHK.py 4 | conda deactivate 5 | -------------------------------------------------------------------------------- /RUN_with_local_venv.bat: -------------------------------------------------------------------------------- 1 | .\venv\Scripts\activate && python LPHK.py 2 | PAUSE -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.1 -------------------------------------------------------------------------------- /bresenham.py: -------------------------------------------------------------------------------- 1 | #https://github.com/encukou/bresenham 2 | 3 | """ 4 | Copyright © 2016 Petr Viktorin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | """ 24 | 25 | """Implementation of Bresenham's line drawing algorithm 26 | 27 | See en.wikipedia.org/wiki/Bresenham's_line_algorithm 28 | """ 29 | 30 | 31 | def bresenham(x0, y0, x1, y1): 32 | """Yield integer coordinates on the line from (x0, y0) to (x1, y1). 33 | 34 | Input coordinates should be integers. 35 | 36 | The result will contain both the start and the end point. 37 | """ 38 | dx = x1 - x0 39 | dy = y1 - y0 40 | 41 | xsign = 1 if dx > 0 else -1 42 | ysign = 1 if dy > 0 else -1 43 | 44 | dx = abs(dx) 45 | dy = abs(dy) 46 | 47 | if dx > dy: 48 | xx, xy, yx, yy = xsign, 0, 0, ysign 49 | else: 50 | dx, dy = dy, dx 51 | xx, xy, yx, yy = 0, ysign, xsign, 0 52 | 53 | D = 2*dy - dx 54 | y = 0 55 | 56 | for x in range(dx + 1): 57 | yield x0 + x*xx + y*yx, y0 + x*xy + y*yy 58 | if D >= 0: 59 | y += 1 60 | D -= 2*dx 61 | D += 2*dy 62 | -------------------------------------------------------------------------------- /files.py: -------------------------------------------------------------------------------- 1 | import lp_colors, scripts 2 | from time import sleep 3 | import os, json, platform, subprocess 4 | 5 | LAYOUT_DIR = "user_layouts" 6 | SCRIPT_DIR = "user_scripts" 7 | 8 | FILE_VERSION = "0.1.1" 9 | 10 | LAYOUT_EXT = ".lpl" 11 | SCRIPT_EXT = ".lps" 12 | 13 | LEGACY_LAYOUT_EXT = ".LPHKlayout" 14 | LEGACY_SCRIPT_EXT = ".LPHKscript" 15 | 16 | USER_PATH = None 17 | LAYOUT_PATH = None 18 | SCRIPT_PATH = None 19 | 20 | import window 21 | 22 | curr_layout = None 23 | in_error = False 24 | layout_changed_since_load = False 25 | 26 | def init(user_path_in): 27 | global USER_PATH 28 | global LAYOUT_PATH 29 | global SCRIPT_PATH 30 | USER_PATH = user_path_in 31 | LAYOUT_PATH = os.path.join(USER_PATH, LAYOUT_DIR) 32 | SCRIPT_PATH = os.path.join(USER_PATH, SCRIPT_DIR) 33 | 34 | def save_layout(layout, name, printing=True): 35 | with open(name, "w") as f: 36 | json.dump(layout, f, indent=2, sort_keys=True) 37 | if printing: 38 | print("[files] Saved layout as " + name) 39 | 40 | def load_layout_json(name, printing=True): 41 | with open(name, "r") as f: 42 | layout = json.load(f) 43 | if printing: 44 | print("[files] Loaded layout " + name) 45 | return layout 46 | 47 | def load_layout_legacy(name, printing=True): 48 | layout = dict() 49 | layout["version"] = "LEGACY" 50 | 51 | layout["buttons"] = [] 52 | with open(name, "r") as f: 53 | l = f.readlines() 54 | 55 | for x in range(9): 56 | layout["buttons"].append([]) 57 | line = l[x][:-1].split(":LPHK_BUTTON_SEP:") 58 | for y in range(9): 59 | info = line[y].split(":LPHK_ENTRY_SEP:") 60 | 61 | color = None 62 | if not info[0].isdigit(): 63 | split = info[0].split(",") 64 | color = [int(x) for x in split[:3]] 65 | else: 66 | color = lp_colors.code_to_RGB(int(info[0])) 67 | script_text = info[1].replace(":LPHK_NEWLINE_REP:", "\n") 68 | 69 | layout["buttons"][-1].append({"color": color, "text": script_text}) 70 | if printing: 71 | print("[files] Loaded legacy layout " + name) 72 | return layout 73 | 74 | def load_layout(name, popups=True, save_converted=True, printing=True): 75 | basename_list = os.path.basename(name).split(os.path.extsep) 76 | ext = basename_list[-1] 77 | title = os.path.extsep.join(basename_list[:-1]) 78 | 79 | if "." + ext == LEGACY_LAYOUT_EXT: 80 | # TODO: Error checking on resultant JSON 81 | layout = load_layout_legacy(name, printing=printing) 82 | 83 | 84 | if save_converted: 85 | name = os.path.dirname(name) + os.path.sep + title + LAYOUT_EXT 86 | if popups: 87 | window.app.popup(window.app, "Legacy layout loaded...", window.app.info_image, "The layout is in the legacy .LPHKlayout format. It will be\nconverted to the new .lpl format, and will be saved as such.", "OK") 88 | else: 89 | if printing: 90 | print("[files] The layout is in the legacy .LPHKlayout format. It will be converted to the new .lpl format, and will be saved as such.") 91 | save_layout(layout, name) 92 | else: 93 | # TODO: Error checking on loaded JSON 94 | try: 95 | layout = load_layout_json(name, printing=printing) 96 | except json.decoder.JSONDecodeError: 97 | if popups: 98 | window.app.popup(window.app, "Error loading file!", window.app.info_image, "The layout is not in valid JSON format (the new .lpl extention).\n\nIf this was renamed from a .LPHKlayout file, please change\nthe extention back to .LPHKlayout and try loading again.", "OK") 99 | raise 100 | 101 | return layout 102 | 103 | def save_lp_to_layout(name): 104 | layout = dict() 105 | layout["version"] = FILE_VERSION 106 | 107 | layout["buttons"] = [] 108 | for x in range(9): 109 | layout["buttons"].append([]) 110 | for y in range(9): 111 | color = lp_colors.curr_colors[x][y] 112 | script_text = scripts.text[x][y] 113 | 114 | layout["buttons"][-1].append({"color": color, "text": script_text}) 115 | 116 | save_layout(layout=layout, name=name) 117 | 118 | def load_layout_to_lp(name, popups=True, save_converted=True, preload=None): 119 | global curr_layout 120 | global in_error 121 | global layout_changed_since_load 122 | 123 | converted_to_rg = False 124 | 125 | scripts.unbind_all() 126 | window.app.draw_canvas() 127 | 128 | if preload == None: 129 | layout = load_layout(name, popups=popups, save_converted=save_converted) 130 | else: 131 | layout = preload 132 | 133 | for x in range(9): 134 | for y in range(9): 135 | button = layout["buttons"][x][y] 136 | color = button["color"] 137 | script_text = button["text"] 138 | 139 | if window.lp_mode == "Mk1": 140 | if color[2] != 0: 141 | color = lp_colors.RGB_to_RG(color) 142 | converted_to_rg = True 143 | 144 | if script_text != "": 145 | script_validation = None 146 | try: 147 | script_validation = scripts.validate_script(script_text) 148 | except: 149 | new_layout_func = lambda: window.app.unbind_lp(prompt_save = False) 150 | if popups: 151 | window.app.popup(window.app, "Script Validation Error", window.app.error_image, "Fatal error while attempting to validate script.\nPlease see LPHK.log for more information.", "OK", end_command = new_layout_func) 152 | else: 153 | print("[files] Fatal error while attempting to validate script.\nPlease see LPHK.log for more information.") 154 | raise 155 | if script_validation != True: 156 | lp_colors.update_all() 157 | window.app.draw_canvas() 158 | in_error = True 159 | window.app.save_script(window.app, x, y, script_text, open_editor = True, color = color) 160 | in_error = False 161 | else: 162 | scripts.bind(x, y, script_text, color) 163 | else: 164 | lp_colors.setXY(x, y, color) 165 | lp_colors.update_all() 166 | window.app.draw_canvas() 167 | 168 | curr_layout = name 169 | if converted_to_rg: 170 | if popups: 171 | window.app.popup(window.app, "Layout converted to Classic/Mini/S...", window.app.info_image, "The colors in this layout have been converted to be\ncompatable with the Launchpad Classic/Mini/S.\n\nChanges have not yet been saved to the file.", "OK") 172 | else: 173 | print("[files] The colors in this layout have been converted to be compatable with the Launchpad Classic/Mini/S. Changes have not yet been saved to the file.") 174 | layout_changed_since_load = True 175 | else: 176 | layout_changed_since_load = False 177 | 178 | def import_script(name): 179 | with open(name, "r") as f: 180 | text = f.read() 181 | print("[files] Imported script as " + name) 182 | return text 183 | 184 | def export_script(name, script): 185 | with open(name, "w+") as f: 186 | f.write(script) 187 | print("[files] Exported script as " + name) 188 | 189 | def strip_lines(text): 190 | return "\n".join([line.strip() for line in text.split("\n")]) 191 | 192 | def open_file_folder(path): 193 | try: 194 | if platform.system() == "Windows": 195 | os.startfile(path) 196 | elif platform.system() == "Darwin": 197 | subprocess.Popen(["open", path]) 198 | else: 199 | subprocess.Popen(["xdg-open", path]) 200 | except: 201 | print("[files] Could not open file or folder " + path) 202 | -------------------------------------------------------------------------------- /kb.py: -------------------------------------------------------------------------------- 1 | import ms 2 | import sys 3 | 4 | if sys.platform == 'win32': 5 | import system_apis.keyboard_win as keyboard_api 6 | else: 7 | import system_apis.keyboard_unix as keyboard_api 8 | 9 | pressed = set() 10 | 11 | 12 | def sp(name): 13 | if "mouse_" in name: 14 | return name 15 | 16 | return keyboard_api.sp(name) 17 | 18 | 19 | def press(key): 20 | pressed.add(key) 21 | if type(key) == str: 22 | if "mouse_" in key: 23 | ms.press(key[6:]) 24 | return 25 | keyboard_api.press(key) 26 | 27 | 28 | def release(key): 29 | pressed.discard(key) 30 | if type(key) == str: 31 | if "mouse_" in key: 32 | ms.release(key[6:]) 33 | return 34 | keyboard_api.release(key) 35 | 36 | 37 | def release_all(): 38 | for key in pressed.copy(): 39 | release(key) 40 | 41 | 42 | def tap(key): 43 | if type(key) == str: 44 | if "mouse_" in key: 45 | ms.click(key[6:]) 46 | return 47 | press(key) 48 | release(key) 49 | 50 | 51 | def write(string): 52 | keyboard_api.write(string) 53 | -------------------------------------------------------------------------------- /logger.py: -------------------------------------------------------------------------------- 1 | # Logger module by Ella J. (nimaid) 2 | # 3 | # This module provides basic logging of STDOUT and STDERR to a text file 4 | # Usage: 5 | # 6 | # import logger 7 | # logger.start("/path/to/my/log.txt") 8 | # ... 9 | # logger.stop() 10 | 11 | import sys 12 | 13 | _log = None 14 | 15 | class _Logger: 16 | def __init__(self, file_path): 17 | self.path = file_path 18 | self._file = open(self.path, "w") 19 | self._stdout_logger = self._LoggerStdout(self._file) 20 | self._stderr_logger = self._LoggerStderr(self._file) 21 | 22 | def __del__(self): 23 | self._stdout_logger.__del__() 24 | self._stderr_logger.__del__() 25 | self._file.close() 26 | 27 | class _LoggerStdout: 28 | def __init__(self, file_in): 29 | self._file = file_in 30 | self._stdout = sys.stdout 31 | sys.stdout = self 32 | def __del__(self): 33 | sys.stdout = self._stdout 34 | def write(self, data): 35 | self._stdout.write(data) 36 | self._file.write(data) 37 | self._file.flush() 38 | def flush(self): 39 | self._file.flush() 40 | 41 | class _LoggerStderr: 42 | def __init__(self, file_in): 43 | self._file = file_in 44 | self._stderr = sys.stderr 45 | sys.stderr = self 46 | def __del__(self): 47 | sys.stderr = self._stderr 48 | def write(self, data): 49 | self._stderr.write(data) 50 | self._file.write(data) 51 | self._file.flush() 52 | def flush(self): 53 | self._file.flush() 54 | 55 | def start(file_path): 56 | global _log 57 | if _log != None: 58 | raise FileExistsError("A log is already running: " + _log.path) 59 | else: 60 | _log = _Logger(file_path) 61 | 62 | def stop(): 63 | global _log 64 | if _log == None: 65 | raise FileNotFoundError("No log is currently running.") 66 | else: 67 | _log.__del__() 68 | _log = None -------------------------------------------------------------------------------- /lp_colors.py: -------------------------------------------------------------------------------- 1 | curr_colors = [[[0, 0, 0] for y in range(9)] for x in range(9)] 2 | color_modes = [["solid" for y in range(9)] for x in range(9)] 3 | 4 | import lp_events, scripts, window 5 | import colorsys 6 | 7 | lp_object = None 8 | 9 | 10 | def init(lp_object_in): 11 | global lp_object 12 | lp_object = lp_object_in 13 | 14 | 15 | def code_to_RGB(code): 16 | # Used to convert old layouts to the new format only 17 | RGB = {0: "#000", 18 | 1: "#555", 19 | 2: "#aaa", 20 | 3: "#fff", 21 | 5: "#f00", 22 | 6: "#a00", 23 | 7: "#500", 24 | 9: "#f90", 25 | 10: "#a60", 26 | 11: "#530", 27 | 21: "#0f0", 28 | 22: "#0a0", 29 | 23: "#050", 30 | 13: "#ff0", 31 | 14: "#aa0", 32 | 15: "#550", 33 | 29: "#0ff", 34 | 30: "#0aa", 35 | 31: "#055", 36 | 37: "#0bf", 37 | 38: "#08a", 38 | 39: "#045", 39 | 45: "#00f", 40 | 46: "#00a", 41 | 47: "#005", 42 | 53: "#f0f", 43 | 54: "#a0a", 44 | 55: "#505", 45 | 48: "#96f", 46 | 49: "#64a", 47 | 50: "#325"} 48 | rgb = [] 49 | for c in range(3): 50 | val = RGB[code][c + 1] 51 | rgb.append(int(val + val, 16)) 52 | return rgb 53 | 54 | 55 | def RGB_to_RG(rgb): 56 | if rgb[2] != 0: 57 | color = list(colorsys.rgb_to_hsv(rgb[0], rgb[1], rgb[2])) 58 | color[0] = color[0] * (106 / 330) 59 | color = list(colorsys.hsv_to_rgb(color[0], color[1], color[2])) 60 | color = [round(x) for x in color] 61 | for x in range(2): 62 | color[x] = round(color[x]) 63 | return color[:2] + [0] 64 | else: 65 | return rgb 66 | 67 | 68 | def setXY(x, y, color): 69 | curr_colors[x][y] = color 70 | 71 | 72 | def getXY(x, y): 73 | return curr_colors[x][y] 74 | 75 | 76 | def list_RGB_to_string(color): 77 | color_texts = [hex(c)[2:] for c in color] 78 | color_string = "#" 79 | for c in color_texts: 80 | if len(c) < 2: 81 | color_string += "0" 82 | color_string += c 83 | return color_string 84 | 85 | 86 | def getXY_RGB(x, y): 87 | color = getXY(x, y) 88 | color_string = list_RGB_to_string(color) 89 | return color_string 90 | 91 | 92 | def luminance(r, g, b): 93 | return ((0.299 * r) + (0.587 * g) + (0.114 * b)) / 255.0 94 | 95 | 96 | def updateXY(x, y): 97 | if window.lp_connected: 98 | if (x, y) != (8, 0): 99 | is_running = False 100 | if scripts.threads[x][y] is not None: 101 | if scripts.threads[x][y].is_alive(): 102 | is_running = True 103 | 104 | is_func_key = ((y == 0) or (x == 8)) 105 | 106 | # print("Update colors for (" + str(x) + ", " + str(y) + "), is_running = " + str(is_running)) 107 | 108 | if is_running: 109 | set_color = scripts.COLOR_PRIMED 110 | color_modes[x][y] = "flash" 111 | elif (x, y) in [l[1:] for l in scripts.to_run]: 112 | if is_func_key: 113 | set_color = scripts.COLOR_FUNC_KEYS_PRIMED 114 | else: 115 | set_color = scripts.COLOR_PRIMED 116 | color_modes[x][y] = "pulse" 117 | else: 118 | set_color = curr_colors[x][y] 119 | color_modes[x][y] = "solid" 120 | 121 | if window.lp_mode == "Mk1": 122 | if type(set_color) is int: 123 | set_color = code_to_RGB(set_color) 124 | lp_object.LedCtrlXY(x, y, set_color[0] // 64, set_color[1] // 64) 125 | else: 126 | if (color_modes[x][y] == "solid") or is_func_key: 127 | # pulse and flash only work on main grid 128 | if type(set_color) is list: 129 | lp_object.LedCtrlXYByRGB(x, y, [c // 4 for c in set_color]) 130 | else: 131 | lp_object.LedCtrlXYByCode(x, y, set_color) 132 | elif color_modes[x][y] == "pulse": 133 | lp_object.LedCtrlPulseXYByCode(x, y, set_color) 134 | elif color_modes[x][y] == "flash": 135 | lp_object.LedCtrlXYByCode(x, y, 0) 136 | lp_object.LedCtrlFlashXYByCode(x, y, set_color) 137 | else: 138 | if type(set_color) is list: 139 | lp_object.LedCtrlXYByRGB(x, y, [c // 4 for c in set_color]) 140 | else: 141 | lp_object.LedCtrlXYByCode(x, y, set_color) 142 | else: 143 | print("[lp_colors] (" + str(x) + ", " + str(y) + ") Launchpad is disconnected, cannot update.") 144 | 145 | 146 | def update_all(): 147 | if window.lp_connected: 148 | for x in range(9): 149 | for y in range(9): 150 | updateXY(x, y) 151 | else: 152 | print("[lp_colors] Launchpad is disconnected, cannot update.") 153 | 154 | 155 | def raw_clear(): 156 | for x in range(9): 157 | for y in range(9): 158 | if window.lp_mode == "Mk1": 159 | lp_object.LedCtrlXY(x, y, 0, 0) 160 | else: 161 | lp_object.LedCtrlXYByCode(x, y, 0) 162 | -------------------------------------------------------------------------------- /lp_events.py: -------------------------------------------------------------------------------- 1 | import copy, threading, time 2 | import lp_colors 3 | 4 | RUN_DELAY = 0.005 # 0.005 == 200 FPS 5 | 6 | 7 | def unbound_press(x, y): 8 | print("[lp_events] (" + str(x) + ", " + str(y) + ") Unbound button...") 9 | 10 | 11 | press_funcs = [[unbound_press for y in range(9)] for x in range(9)] 12 | pressed = [[False for y in range(9)] for x in range(9)] 13 | 14 | timer = None 15 | 16 | 17 | def init(lp_object): 18 | global timer 19 | global press_funcs 20 | timer = threading.Timer(RUN_DELAY, run, (lp_object,)) 21 | 22 | 23 | def run(lp_object): 24 | global timer 25 | while True: 26 | event = lp_object.ButtonStateXY() 27 | if event: 28 | x = event[0] 29 | y = event[1] 30 | try: 31 | if event[2] == 0: 32 | pressed[x][y] = False 33 | else: 34 | pressed[x][y] = True 35 | press_funcs[x][y](x, y) 36 | lp_colors.updateXY(x, y) 37 | except IndexError: 38 | pass 39 | else: 40 | break 41 | init(lp_object) 42 | timer.start() 43 | 44 | 45 | def start(lp_object): 46 | lp_colors.init(lp_object) 47 | init(lp_object) 48 | run(lp_object) 49 | lp_colors.update_all() 50 | 51 | 52 | def bind_func_with_colors(x, y, func, off_color): 53 | global press_funcs 54 | press_funcs[x][y] = func 55 | lp_colors.setXY(x, y, off_color) 56 | 57 | 58 | def unbind(x, y): 59 | global press_funcs 60 | press_funcs[x][y] = unbound_press 61 | lp_colors.setXY(x, y, [0, 0, 0]) 62 | lp_colors.updateXY(x, y) 63 | 64 | 65 | def unbind_all(): 66 | global press_funcs 67 | press_funcs = [[unbound_press for y in range(9)] for x in range(9)] 68 | for x in range(9): 69 | for y in range(9): 70 | lp_colors.setXY(x, y, [0, 0, 0]) 71 | lp_colors.raw_clear() 72 | -------------------------------------------------------------------------------- /ms.py: -------------------------------------------------------------------------------- 1 | from pynput.mouse import Controller, Button 2 | from bresenham import bresenham 3 | 4 | controller = Controller() 5 | buttons = ["left", "middle", "right"] 6 | 7 | 8 | def get_pos(): 9 | return controller.position 10 | 11 | 12 | def set_pos(x, y): 13 | global controller 14 | controller.position = (x, y) 15 | 16 | 17 | def move_to_pos(x, y): 18 | controller.move(x, y) 19 | 20 | 21 | def click(button="left", clicks=1): 22 | _check_button(button) 23 | controller.click(Button[button], clicks) 24 | 25 | 26 | def press(button="left"): 27 | _check_button(button) 28 | controller.press(Button[button]) 29 | 30 | 31 | def release(button="left"): 32 | _check_button(button) 33 | controller.release(Button[button]) 34 | 35 | 36 | def scroll(x, y): 37 | controller.scroll(x, y) 38 | 39 | 40 | def line_coords(x1, y1, x2, y2): 41 | return list(bresenham(x1, y1, x2, y2)) 42 | 43 | 44 | def _check_button(btn): 45 | if btn not in buttons: 46 | raise ValueError('The mouse button specified is not valid') 47 | 48 | -------------------------------------------------------------------------------- /parse.py: -------------------------------------------------------------------------------- 1 | # CURRENTLY UNUSED 2 | import parser, types 3 | 4 | variables = dict() 5 | 6 | def set_var(var_string, val): 7 | global variables 8 | try: 9 | try: 10 | temp = int(val) 11 | except: 12 | temp = float(val) 13 | variables[var_string] = val 14 | except: 15 | if isinstance(val, str): 16 | value = variables[val] 17 | variables[var_string] = value 18 | 19 | def get_var(var_string): 20 | try: 21 | return variables[var_string] 22 | except: 23 | return None 24 | 25 | def eval_string_and_vars(eq_string, vars_in): 26 | for var in vars_in: 27 | eq_string = eq_string.replace(var, str(vars_in[var])) 28 | eq = parser.expr(eq_string).compile() 29 | return eval(eq) 30 | 31 | def eval_string(eq_string): 32 | return eval_string_and_vars(eq_string, variables) 33 | 34 | def set_var_eval_string(var_string, eq_string): 35 | global variables 36 | value = eval_string(eq_string) 37 | variables[var_string] = str(value) 38 | 39 | -------------------------------------------------------------------------------- /resources/LPHK-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/LPHK-banner.png -------------------------------------------------------------------------------- /resources/LPHK.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/LPHK.gif -------------------------------------------------------------------------------- /resources/LPHK.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/LPHK.ico -------------------------------------------------------------------------------- /resources/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/alert.png -------------------------------------------------------------------------------- /resources/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/error.png -------------------------------------------------------------------------------- /resources/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/info.png -------------------------------------------------------------------------------- /resources/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/running.png -------------------------------------------------------------------------------- /resources/scare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/scare.png -------------------------------------------------------------------------------- /resources/scheduled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/scheduled.png -------------------------------------------------------------------------------- /resources/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/script.png -------------------------------------------------------------------------------- /resources/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/resources/warning.png -------------------------------------------------------------------------------- /sound.py: -------------------------------------------------------------------------------- 1 | import pygame, os 2 | from time import sleep 3 | 4 | PATH = None 5 | SOUND_PATH = "/user_sounds/" 6 | 7 | import pygame 8 | 9 | m = pygame.mixer 10 | 11 | def init(path_in): 12 | global PATH 13 | PATH = path_in 14 | 15 | m.pre_init(44100, -16, 2, 2048) 16 | m.init() 17 | 18 | def full_name(filename): 19 | name = PATH + SOUND_PATH + filename 20 | if PATH.find('\\') > 0: 21 | name = name.replace('/', '\\') 22 | return name 23 | 24 | def is_valid(filename): 25 | final_name = full_name(filename) 26 | try: 27 | sound = m.Sound(final_name) 28 | return True 29 | except: 30 | return False 31 | 32 | def play(filename, volume=100.0): 33 | final_name = full_name(filename) 34 | try: 35 | sound = m.Sound(final_name) 36 | vol = float(volume / 100.0) 37 | sound.set_volume(vol) 38 | sound.play() 39 | except: 40 | print("[sound] Could not play sound " + final_name) 41 | 42 | 43 | def stop(): 44 | try: 45 | m.stop() 46 | except: 47 | print("Could not stop sounds") 48 | 49 | 50 | def fadeout(delay): 51 | try: 52 | m.fadeout(delay) 53 | except: 54 | print("Could not fade out sound") 55 | -------------------------------------------------------------------------------- /system_apis/keyboard_unix.py: -------------------------------------------------------------------------------- 1 | from pynput import keyboard 2 | from pynput.keyboard import KeyCode 3 | # from pynput.keyboard import Controller as KeyboardController 4 | import pyautogui 5 | from pyautogui import KEY_NAMES as pyautogui_keys 6 | 7 | # keyboard_controller = KeyboardController() 8 | 9 | media_key_map = { 10 | "vol_up": "media_volume_up", 11 | "vol_down": "media_volume_down", 12 | "mute": "media_volume_mute", 13 | "play_pause": "media_play_pause", 14 | "prev_track": "media_previous", 15 | "next_track": "media_next", 16 | "mouse_left": "left", 17 | "mouse_middle": "middle", 18 | "mouse_right": "right", 19 | "num0": 0x60, 20 | "num1": 0x61, 21 | "num2": 0x62, 22 | "num3": 0x63, 23 | "num4": 0x64, 24 | "num5": 0x65, 25 | "num6": 0x66, 26 | "num7": 0x67, 27 | "num8": 0x68, 28 | "num9": 0x69, 29 | } 30 | 31 | media_key_map_pyautogui = { 32 | "alt": "alt", 33 | "alt_gr": "altright", 34 | "shift_r": "shiftright", 35 | "scroll_lock": "scrolllock", 36 | "print_screen": "printscreen", 37 | "page_up": "pgup", 38 | "page_down": "pgdn", 39 | "num_lock": "numlock", 40 | "vol_up": "volumeup", 41 | "vol_down": "volumedown", 42 | "mute": "volumemute", 43 | "play_pause": "playpause", 44 | "prev_track": "prevtrack", 45 | "next_track": "nexttrack", 46 | "mouse_left": "left", 47 | "mouse_middle": "middle", 48 | "mouse_right": "right" 49 | } 50 | 51 | 52 | def sp(name): 53 | return _sp_pyautogui(name) 54 | 55 | 56 | def _sp_pyautogui(name): 57 | if name in media_key_map_pyautogui: 58 | name = media_key_map_pyautogui[name] 59 | 60 | if name in pyautogui_keys: 61 | return name 62 | 63 | return None 64 | 65 | 66 | def _sp_pynput(name): 67 | # This is safe because we know the names in the pynput lib 68 | if name in media_key_map: 69 | name = media_key_map[name] 70 | 71 | try: 72 | return keyboard.Key[name] 73 | except KeyError: 74 | try: 75 | return KeyCode.from_char(name) 76 | except KeyError: 77 | return None 78 | 79 | 80 | def press(key): 81 | # keyboard_controller.press(key) 82 | pyautogui.keyDown(key) 83 | 84 | 85 | def release(key): 86 | # keyboard_controller.release(key) 87 | pyautogui.keyUp(key) 88 | 89 | 90 | def write(string): 91 | # keyboard_controller.type(string) 92 | pyautogui.write(string) 93 | -------------------------------------------------------------------------------- /system_apis/keyboard_win.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from ctypes import wintypes 3 | import pyautogui 4 | 5 | # code in this file comes from https://gist.github.com/Aniruddha-Tapas/1627257344780e5429b10bc92eb2f52a 6 | 7 | user32 = ctypes.WinDLL('user32', use_last_error=True) 8 | 9 | INPUT_MOUSE = 0 10 | INPUT_KEYBOARD = 1 11 | INPUT_HARDWARE = 2 12 | 13 | KEYEVENTF_EXTENDEDKEY = 0x0001 14 | KEYEVENTF_KEYUP = 0x0002 15 | KEYEVENTF_UNICODE = 0x0004 16 | KEYEVENTF_SCANCODE = 0x0008 17 | 18 | MAPVK_VK_TO_VSC = 0 19 | 20 | # C struct definitions 21 | 22 | wintypes.ULONG_PTR = wintypes.WPARAM 23 | 24 | 25 | class MOUSEINPUT(ctypes.Structure): 26 | _fields_ = (("dx", wintypes.LONG), 27 | ("dy", wintypes.LONG), 28 | ("mouseData", wintypes.DWORD), 29 | ("dwFlags", wintypes.DWORD), 30 | ("time", wintypes.DWORD), 31 | ("dwExtraInfo", wintypes.ULONG_PTR)) 32 | 33 | 34 | class KEYBDINPUT(ctypes.Structure): 35 | _fields_ = (("wVk", wintypes.WORD), 36 | ("wScan", wintypes.WORD), 37 | ("dwFlags", wintypes.DWORD), 38 | ("time", wintypes.DWORD), 39 | ("dwExtraInfo", wintypes.ULONG_PTR)) 40 | 41 | def __init__(self, *args, **kwds): 42 | super(KEYBDINPUT, self).__init__(*args, **kwds) 43 | # some programs use the scan code even if KEYEVENTF_SCANCODE 44 | # isn't set in dwFflags, so attempt to map the correct code. 45 | if not self.dwFlags & KEYEVENTF_UNICODE: 46 | self.wScan = user32.MapVirtualKeyExW(self.wVk, 47 | MAPVK_VK_TO_VSC, 0) 48 | 49 | 50 | class HARDWAREINPUT(ctypes.Structure): 51 | _fields_ = (("uMsg", wintypes.DWORD), 52 | ("wParamL", wintypes.WORD), 53 | ("wParamH", wintypes.WORD)) 54 | 55 | 56 | class INPUT(ctypes.Structure): 57 | class _INPUT(ctypes.Union): 58 | _fields_ = (("ki", KEYBDINPUT), 59 | ("mi", MOUSEINPUT), 60 | ("hi", HARDWAREINPUT)) 61 | 62 | _anonymous_ = ("_input",) 63 | _fields_ = (("type", wintypes.DWORD), 64 | ("_input", _INPUT)) 65 | 66 | 67 | LPINPUT = ctypes.POINTER(INPUT) 68 | 69 | 70 | # Functions 71 | 72 | def press_key(hexKeyCode): 73 | x = INPUT(type=INPUT_KEYBOARD, 74 | ki=KEYBDINPUT(wVk=hexKeyCode)) 75 | user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) 76 | 77 | 78 | def release_key(hexKeyCode): 79 | x = INPUT(type=INPUT_KEYBOARD, 80 | ki=KEYBDINPUT(wVk=hexKeyCode, 81 | dwFlags=KEYEVENTF_KEYUP)) 82 | user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) 83 | 84 | 85 | # List of all codes for keys: 86 | # https://msdn.microsoft.com/en-us/library/dd375731 87 | key_map = { 88 | "alt": 0x12, 89 | "alt_gr": 0x12, # TODO: find a way to send two keys 90 | 91 | "apps": 0x5D, 92 | 93 | "backspace": 0x08, 94 | "caps_lock": 0x14, 95 | 96 | # cmd and win keys are the same 97 | "cmd": 0x5B, 98 | "win": 0x5B, 99 | "win_r": 0x5C, 100 | 101 | "ctrl": 0x11, 102 | "ctrl_l": 0xA2, 103 | "ctrl_r": 0xA3, 104 | 105 | "delete": 0x2E, 106 | 107 | "down": 0x28, 108 | "end": 0x23, 109 | "enter": 0x0D, 110 | "esc": 0x1B, 111 | 112 | "f1": 0x70, 113 | "f2": 0x71, 114 | "f3": 0x72, 115 | "f4": 0x73, 116 | "f5": 0x74, 117 | "f6": 0x75, 118 | "f7": 0x76, 119 | "f8": 0x77, 120 | "f9": 0x78, 121 | "f10": 0x79, 122 | "f11": 0x7A, 123 | "f12": 0x7B, 124 | "f13": 0x7C, 125 | "f14": 0x7D, 126 | "f15": 0x7E, 127 | "f16": 0x7F, 128 | "f17": 0x80, 129 | "f18": 0x81, 130 | "f19": 0x82, 131 | "f20": 0x83, 132 | "f21": 0x84, 133 | "f22": 0x85, 134 | "f23": 0x86, 135 | "f24": 0x87, 136 | 137 | "home": 0x24, 138 | "insert": 0x2D, 139 | "left": 0x25, 140 | 141 | "menu": 0xA4, # left menu key 142 | "menu_r": 0xA5, 143 | 144 | "mute": 0xAD, 145 | "next_track": 0xB0, 146 | "num_lock": 0x90, 147 | "page_down": 0x22, 148 | "page_up": 0x21, 149 | "pause": 0x13, 150 | "play_pause": 0xB3, 151 | "prev_track": 0xB1, 152 | "print_screen": 0x2C, 153 | "right": 0x27, 154 | "scroll_lock": 0x91, 155 | 156 | "shift": 0x10, 157 | "shift_l": 0xA0, 158 | "shift_r": 0xA1, 159 | 160 | "space": 0x20, 161 | "tab": 0x09, 162 | "up": 0x26, 163 | "vol_down": 0xAE, 164 | "vol_up": 0xAF, 165 | 166 | "0": 0x30, 167 | "1": 0x31, 168 | "2": 0x32, 169 | "3": 0x33, 170 | "4": 0x34, 171 | "5": 0x35, 172 | "6": 0x36, 173 | "7": 0x37, 174 | "8": 0x38, 175 | "9": 0x39, 176 | 177 | "num0": 0x60, 178 | "num1": 0x61, 179 | "num2": 0x62, 180 | "num3": 0x63, 181 | "num4": 0x64, 182 | "num5": 0x65, 183 | "num6": 0x66, 184 | "num7": 0x67, 185 | "num8": 0x68, 186 | "num9": 0x69, 187 | 188 | "a": 0x41, 189 | "b": 0x42, 190 | "c": 0x43, 191 | "d": 0x44, 192 | "e": 0x45, 193 | "f": 0x46, 194 | "g": 0x47, 195 | "h": 0x48, 196 | "i": 0x49, 197 | "j": 0x4A, 198 | "k": 0x4B, 199 | "l": 0x4C, 200 | "m": 0x4D, 201 | "n": 0x4E, 202 | "o": 0x4F, 203 | "p": 0x50, 204 | "q": 0x51, 205 | "r": 0x52, 206 | "s": 0x53, 207 | "t": 0x54, 208 | "u": 0x55, 209 | "v": 0x56, 210 | "w": 0x57, 211 | "x": 0x58, 212 | "y": 0x59, 213 | "z": 0x5A, 214 | 215 | "+": 0x6B, 216 | "-": 0xBD, 217 | "=": 0xBB, 218 | ".": 0xBE, 219 | ",": 0xBC, 220 | "*": 0x6A, 221 | "]": 0xDD, 222 | "[": 0xDB, 223 | ";": 0xBA, 224 | "'": 0xDE, 225 | "\\": 0xDC, 226 | "`": 0xC0, 227 | 228 | # very special keys 229 | "clear": 0x0C, 230 | } 231 | 232 | 233 | def sp(name): 234 | if name in key_map: 235 | return key_map[name] 236 | 237 | return None 238 | 239 | 240 | def press(key): 241 | press_key(key) 242 | 243 | 244 | def release(key): 245 | release_key(key) 246 | 247 | 248 | def write(string): 249 | pyautogui.write(string) 250 | -------------------------------------------------------------------------------- /user_layouts/examples/Black_Mesa_HL2_Mod.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 255, 7 | 255, 8 | 255 9 | ], 10 | "text": "@SIMPLE vol_up" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 149, 23 | 255, 24 | 0 25 | ], 26 | "text": "@SIMPLE esc" 27 | }, 28 | { 29 | "color": [ 30 | 174, 31 | 0, 32 | 255 33 | ], 34 | "text": "@SIMPLE `" 35 | }, 36 | { 37 | "color": [ 38 | 85, 39 | 0, 40 | 0 41 | ], 42 | "text": "@SIMPLE 1" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 0, 48 | 0 49 | ], 50 | "text": "" 51 | }, 52 | { 53 | "color": [ 54 | 0, 55 | 0, 56 | 0 57 | ], 58 | "text": "" 59 | }, 60 | { 61 | "color": [ 62 | 116, 63 | 51, 64 | 255 65 | ], 66 | "text": "@SIMPLE shift" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 255, 72 | 0 73 | ], 74 | "text": "@SIMPLE ctrl" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "@SIMPLE vol_down" 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 0 91 | ], 92 | "text": "" 93 | }, 94 | { 95 | "color": [ 96 | 255, 97 | 0, 98 | 0 99 | ], 100 | "text": "@SIMPLE f6" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 255, 106 | 0 107 | ], 108 | "text": "@SIMPLE f9" 109 | }, 110 | { 111 | "color": [ 112 | 85, 113 | 51, 114 | 0 115 | ], 116 | "text": "@SIMPLE 2" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 187, 122 | 255 123 | ], 124 | "text": "@SIMPLE q" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 0, 130 | 255 131 | ], 132 | "text": "@SIMPLE a" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 0 157 | ], 158 | "text": "" 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 0 165 | ], 166 | "text": "" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 0 173 | ], 174 | "text": "" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 0 181 | ], 182 | "text": "" 183 | }, 184 | { 185 | "color": [ 186 | 85, 187 | 85, 188 | 0 189 | ], 190 | "text": "@SIMPLE 3" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 255 197 | ], 198 | "text": "@SIMPLE w" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 255 205 | ], 206 | "text": "@SIMPLE s" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 0 213 | ], 214 | "text": "" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "text": "" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 0 239 | ], 240 | "text": "" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 0 247 | ], 248 | "text": "" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 0, 254 | 0 255 | ], 256 | "text": "" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 85, 262 | 0 263 | ], 264 | "text": "@SIMPLE 4" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 187, 270 | 255 271 | ], 272 | "text": "@SIMPLE e" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 0, 278 | 255 279 | ], 280 | "text": "@SIMPLE d" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 0 287 | ], 288 | "text": "" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 0 295 | ], 296 | "text": "" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 0 305 | ], 306 | "text": "" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 0 321 | ], 322 | "text": "" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 0 329 | ], 330 | "text": "" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 85 337 | ], 338 | "text": "@SIMPLE 5" 339 | }, 340 | { 341 | "color": [ 342 | 136, 343 | 255, 344 | 0 345 | ], 346 | "text": "@SIMPLE r" 347 | }, 348 | { 349 | "color": [ 350 | 0, 351 | 187, 352 | 255 353 | ], 354 | "text": "@SIMPLE f" 355 | }, 356 | { 357 | "color": [ 358 | 141, 359 | 70, 360 | 20 361 | ], 362 | "text": "@ASYNC\n- Wallclimbing Script\n\nPRESS w\n\nLABEL start\nTAP space\nTAP e\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start\n\nRELEASE w" 363 | }, 364 | { 365 | "color": [ 366 | 255, 367 | 0, 368 | 255 369 | ], 370 | "text": "@ASYNC\n- Auto-ABH Script\n\nTAP space\nPRESS ctrl\n\nLABEL start\nTAP space\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start\n\nRELEASE ctrl" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 0, 385 | 0, 386 | 0 387 | ], 388 | "text": "" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 0, 394 | 0 395 | ], 396 | "text": "" 397 | }, 398 | { 399 | "color": [ 400 | 0, 401 | 0, 402 | 0 403 | ], 404 | "text": "" 405 | }, 406 | { 407 | "color": [ 408 | 51, 409 | 34, 410 | 85 411 | ], 412 | "text": "@SIMPLE 6" 413 | }, 414 | { 415 | "color": [ 416 | 255, 417 | 166, 418 | 0 419 | ], 420 | "text": "@SIMPLE z" 421 | }, 422 | { 423 | "color": [ 424 | 137, 425 | 210, 426 | 238 427 | ], 428 | "text": "@SIMPLE enter" 429 | }, 430 | { 431 | "color": [ 432 | 255, 433 | 87, 434 | 255 435 | ], 436 | "text": "@SIMPLE space" 437 | }, 438 | { 439 | "color": [ 440 | 255, 441 | 255, 442 | 0 443 | ], 444 | "text": "@ASYNC\n- Jump Script\n\nLABEL start\nTAP space\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 0, 459 | 0, 460 | 0 461 | ], 462 | "text": "" 463 | }, 464 | { 465 | "color": [ 466 | 0, 467 | 0, 468 | 0 469 | ], 470 | "text": "" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 0, 476 | 0 477 | ], 478 | "text": "" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 0 485 | ], 486 | "text": "" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 0 493 | ], 494 | "text": "" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 0, 500 | 0 501 | ], 502 | "text": "" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 0 509 | ], 510 | "text": "" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 0 517 | ], 518 | "text": "" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 128, 525 | 0, 526 | 255 527 | ], 528 | "text": "OPEN C:\\Users\\ellag\\Desktop\\Black Mesa.url" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 0 543 | ], 544 | "text": "" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 0 551 | ], 552 | "text": "" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 0, 558 | 0 559 | ], 560 | "text": "" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 0, 566 | 0 567 | ], 568 | "text": "" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 0 575 | ], 576 | "text": "" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 0 583 | ], 584 | "text": "" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 0 591 | ], 592 | "text": "" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 255, 608 | 0 609 | ], 610 | "text": "- Enables cl_showpos, and ABH ;)\n- Source Pause Tool must be installed\n\nTAP `\nDELAY 0.1\n\nSTRING plugin_load spt\nTAP enter\nDELAY 5\n\nSTRING y_spt_additional_jumpboost 1\nTAP enter\nDELAY 0.1\n\nSTRING cl_showpos 1\nTAP enter\nDELAY 0.1\n\nSTRING y_spt_pause 0\nTAP enter\nDELAY 0.1\n\nTAP `" 611 | }, 612 | { 613 | "color": [ 614 | 255, 615 | 255, 616 | 0 617 | ], 618 | "text": "- Enables reticle\nTAP `\nDELAY 0.1\n\nSTRING hud_draw_fixed_reticle 1\nTAP enter\nDELAY 0.1\n\nSTRING hud_draw_active_reticle 1\nTAP enter\nDELAY 0.1\n\nTAP `" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 0 641 | ], 642 | "text": "" 643 | }, 644 | { 645 | "color": [ 646 | 0, 647 | 0, 648 | 0 649 | ], 650 | "text": "" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 0 665 | ], 666 | "text": "" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/DOOM.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 255, 7 | 255, 8 | 255 9 | ], 10 | "text": "@SIMPLE vol_up" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 0, 23 | 0, 24 | 0 25 | ], 26 | "text": "" 27 | }, 28 | { 29 | "color": [ 30 | 85, 31 | 0, 32 | 0 33 | ], 34 | "text": "@SIMPLE 1" 35 | }, 36 | { 37 | "color": [ 38 | 0, 39 | 0, 40 | 0 41 | ], 42 | "text": "" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 187, 48 | 255 49 | ], 50 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS left\nWAIT_UNPRESSED\nRELEASE left" 51 | }, 52 | { 53 | "color": [ 54 | 153, 55 | 102, 56 | 255 57 | ], 58 | "text": "@SIMPLE space" 59 | }, 60 | { 61 | "color": [ 62 | 0, 63 | 0, 64 | 0 65 | ], 66 | "text": "" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 0, 72 | 0 73 | ], 74 | "text": "" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "@SIMPLE vol_down" 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 0 91 | ], 92 | "text": "" 93 | }, 94 | { 95 | "color": [ 96 | 0, 97 | 0, 98 | 0 99 | ], 100 | "text": "" 101 | }, 102 | { 103 | "color": [ 104 | 85, 105 | 51, 106 | 0 107 | ], 108 | "text": "@SIMPLE 2" 109 | }, 110 | { 111 | "color": [ 112 | 0, 113 | 187, 114 | 255 115 | ], 116 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS left\nWAIT_UNPRESSED\nRELEASE left" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 255 123 | ], 124 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS alt\nPRESS left\nWAIT_UNPRESSED\nRELEASE left\nRELEASE alt" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 187, 130 | 255 131 | ], 132 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS left\nWAIT_UNPRESSED\nRELEASE left" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 0 157 | ], 158 | "text": "" 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 0 165 | ], 166 | "text": "" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 0 173 | ], 174 | "text": "" 175 | }, 176 | { 177 | "color": [ 178 | 85, 179 | 85, 180 | 0 181 | ], 182 | "text": "@SIMPLE 3" 183 | }, 184 | { 185 | "color": [ 186 | 0, 187 | 0, 188 | 255 189 | ], 190 | "text": "@SIMPLE up" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 255 197 | ], 198 | "text": "@SIMPLE down" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 0 205 | ], 206 | "text": "" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 0 213 | ], 214 | "text": "" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "text": "" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 0 239 | ], 240 | "text": "" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 0 247 | ], 248 | "text": "" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 85, 254 | 0 255 | ], 256 | "text": "@SIMPLE 4" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 187, 262 | 255 263 | ], 264 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS right\nWAIT_UNPRESSED\nRELEASE right" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 0, 270 | 255 271 | ], 272 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS alt\nPRESS right\nWAIT_UNPRESSED\nRELEASE right\nRELEASE alt" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 187, 278 | 255 279 | ], 280 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS right\nWAIT_UNPRESSED\nRELEASE right" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 0 287 | ], 288 | "text": "" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 0 295 | ], 296 | "text": "" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 0 305 | ], 306 | "text": "" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 0 321 | ], 322 | "text": "" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 85 329 | ], 330 | "text": "@SIMPLE 5" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 0 337 | ], 338 | "text": "" 339 | }, 340 | { 341 | "color": [ 342 | 0, 343 | 187, 344 | 255 345 | ], 346 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS right\nWAIT_UNPRESSED\nRELEASE right" 347 | }, 348 | { 349 | "color": [ 350 | 153, 351 | 102, 352 | 255 353 | ], 354 | "text": "@SIMPLE space" 355 | }, 356 | { 357 | "color": [ 358 | 0, 359 | 255, 360 | 0 361 | ], 362 | "text": "@SIMPLE ctrl" 363 | }, 364 | { 365 | "color": [ 366 | 0, 367 | 0, 368 | 0 369 | ], 370 | "text": "" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 0, 385 | 0, 386 | 0 387 | ], 388 | "text": "" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 0, 394 | 0 395 | ], 396 | "text": "" 397 | }, 398 | { 399 | "color": [ 400 | 51, 401 | 34, 402 | 85 403 | ], 404 | "text": "@SIMPLE 6" 405 | }, 406 | { 407 | "color": [ 408 | 0, 409 | 85, 410 | 0 411 | ], 412 | "text": "@SIMPLE y" 413 | }, 414 | { 415 | "color": [ 416 | 85, 417 | 0, 418 | 0 419 | ], 420 | "text": "@SIMPLE n" 421 | }, 422 | { 423 | "color": [ 424 | 255, 425 | 0, 426 | 0 427 | ], 428 | "text": "@ASYNC\nPRESS esc\nWAIT_UNPRESSED\nRELEASE esc" 429 | }, 430 | { 431 | "color": [ 432 | 255, 433 | 255, 434 | 0 435 | ], 436 | "text": "@SIMPLE enter" 437 | }, 438 | { 439 | "color": [ 440 | 255, 441 | 153, 442 | 0 443 | ], 444 | "text": "@SIMPLE backspace" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 0, 459 | 0, 460 | 0 461 | ], 462 | "text": "" 463 | }, 464 | { 465 | "color": [ 466 | 0, 467 | 0, 468 | 0 469 | ], 470 | "text": "" 471 | }, 472 | { 473 | "color": [ 474 | 85, 475 | 0, 476 | 85 477 | ], 478 | "text": "@SIMPLE 7" 479 | }, 480 | { 481 | "color": [ 482 | 170, 483 | 170, 484 | 0 485 | ], 486 | "text": "@SIMPLE f" 487 | }, 488 | { 489 | "color": [ 490 | 170, 491 | 0, 492 | 0 493 | ], 494 | "text": "@SIMPLE -" 495 | }, 496 | { 497 | "color": [ 498 | 170, 499 | 102, 500 | 0 501 | ], 502 | "text": "@SIMPLE c" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 255, 508 | 255 509 | ], 510 | "text": "@SIMPLE shift" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 187, 516 | 255 517 | ], 518 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS left\nWAIT_UNPRESSED\nRELEASE left" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 85, 525 | 85, 526 | 85 527 | ], 528 | "text": "WEB_NEW https://playclassic.games/game/play-doom-online/play/" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 0 543 | ], 544 | "text": "" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 0 551 | ], 552 | "text": "" 553 | }, 554 | { 555 | "color": [ 556 | 102, 557 | 68, 558 | 170 559 | ], 560 | "text": "@SIMPLE tab" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 170, 566 | 0 567 | ], 568 | "text": "@ASYNC\nPRESS +\nWAIT_UNPRESSED\nRELEASE +" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 170 575 | ], 576 | "text": "@SIMPLE m" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 187, 582 | 255 583 | ], 584 | "text": "-These scripts don't run as @ASYNC because we only want one turn/strafe macro running at a time\nPRESS right\nWAIT_UNPRESSED\nRELEASE right" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 255, 590 | 255 591 | ], 592 | "text": "@SIMPLE shift" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 0, 608 | 0 609 | ], 610 | "text": "" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 0 617 | ], 618 | "text": "" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 0 641 | ], 642 | "text": "" 643 | }, 644 | { 645 | "color": [ 646 | 255, 647 | 255, 648 | 255 649 | ], 650 | "text": "@SIMPLE mute" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 0 665 | ], 666 | "text": "" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/GIMP_paint.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 0, 7 | 0, 8 | 0 9 | ], 10 | "text": "" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 0, 23 | 0, 24 | 255 25 | ], 26 | "text": "M_MOVE -1 0" 27 | }, 28 | { 29 | "color": [ 30 | 0, 31 | 0, 32 | 0 33 | ], 34 | "text": "" 35 | }, 36 | { 37 | "color": [ 38 | 0, 39 | 0, 40 | 0 41 | ], 42 | "text": "" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 0, 48 | 0 49 | ], 50 | "text": "" 51 | }, 52 | { 53 | "color": [ 54 | 0, 55 | 0, 56 | 0 57 | ], 58 | "text": "" 59 | }, 60 | { 61 | "color": [ 62 | 0, 63 | 0, 64 | 0 65 | ], 66 | "text": "" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 0, 72 | 0 73 | ], 74 | "text": "" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 0, 81 | 0, 82 | 0 83 | ], 84 | "text": "" 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 255 91 | ], 92 | "text": "M_MOVE 0 -1" 93 | }, 94 | { 95 | "color": [ 96 | 0, 97 | 0, 98 | 0 99 | ], 100 | "text": "" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 0, 106 | 255 107 | ], 108 | "text": "M_MOVE 0 1" 109 | }, 110 | { 111 | "color": [ 112 | 0, 113 | 0, 114 | 0 115 | ], 116 | "text": "" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 0 123 | ], 124 | "text": "" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 0, 130 | 0 131 | ], 132 | "text": "" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 0 157 | ], 158 | "text": "" 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 0 165 | ], 166 | "text": "" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 255 173 | ], 174 | "text": "M_MOVE 1 0" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 0 181 | ], 182 | "text": "" 183 | }, 184 | { 185 | "color": [ 186 | 0, 187 | 0, 188 | 0 189 | ], 190 | "text": "" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 0 197 | ], 198 | "text": "" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 0 205 | ], 206 | "text": "" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 0 213 | ], 214 | "text": "" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "text": "" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 255, 238 | 255 239 | ], 240 | "text": "@SIMPLE ]" 241 | }, 242 | { 243 | "color": [ 244 | 153, 245 | 102, 246 | 255 247 | ], 248 | "text": "@SIMPLE [" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 0, 254 | 0 255 | ], 256 | "text": "" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 0, 262 | 0 263 | ], 264 | "text": "" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 0, 270 | 255 271 | ], 272 | "text": "M_LINE_MOVE -30 -15 1\nWAIT_UNPRESSED" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 0, 278 | 0 279 | ], 280 | "text": "" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 255 287 | ], 288 | "text": "M_LINE_MOVE -30 15 1\nWAIT_UNPRESSED" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 0 295 | ], 296 | "text": "" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 0 305 | ], 306 | "text": "" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 0 321 | ], 322 | "text": "" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 0 329 | ], 330 | "text": "" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 255 337 | ], 338 | "text": "M_LINE_MOVE -15 -30 1\nWAIT_UNPRESSED" 339 | }, 340 | { 341 | "color": [ 342 | 0, 343 | 0, 344 | 255 345 | ], 346 | "text": "M_LINE_MOVE -30 -30 1\nWAIT_UNPRESSED" 347 | }, 348 | { 349 | "color": [ 350 | 0, 351 | 0, 352 | 255 353 | ], 354 | "text": "M_LINE_MOVE -30 0 1\nWAIT_UNPRESSED" 355 | }, 356 | { 357 | "color": [ 358 | 0, 359 | 0, 360 | 255 361 | ], 362 | "text": "M_LINE_MOVE -30 30 1\nWAIT_UNPRESSED" 363 | }, 364 | { 365 | "color": [ 366 | 0, 367 | 0, 368 | 255 369 | ], 370 | "text": "M_LINE_MOVE -15 30 1\nWAIT_UNPRESSED" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 153, 385 | 102, 386 | 255 387 | ], 388 | "text": "PRESS shift\nTAP e\nRELEASE shift\nTAP ] 10" 389 | }, 390 | { 391 | "color": [ 392 | 255, 393 | 255, 394 | 0 395 | ], 396 | "text": "PRESS ctrl\nTAP a\nRELEASE ctrl" 397 | }, 398 | { 399 | "color": [ 400 | 255, 401 | 153, 402 | 0 403 | ], 404 | "text": "PRESS ctrl\nTAP z\nRELEASE ctrl" 405 | }, 406 | { 407 | "color": [ 408 | 0, 409 | 0, 410 | 0 411 | ], 412 | "text": "" 413 | }, 414 | { 415 | "color": [ 416 | 0, 417 | 0, 418 | 255 419 | ], 420 | "text": "M_LINE_MOVE 0 -30 1\nWAIT_UNPRESSED" 421 | }, 422 | { 423 | "color": [ 424 | 0, 425 | 255, 426 | 0 427 | ], 428 | "text": "@SIMPLE mouse_left" 429 | }, 430 | { 431 | "color": [ 432 | 0, 433 | 0, 434 | 255 435 | ], 436 | "text": "M_LINE_MOVE 0 30 1\nWAIT_UNPRESSED" 437 | }, 438 | { 439 | "color": [ 440 | 0, 441 | 0, 442 | 0 443 | ], 444 | "text": "" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 255, 459 | 255, 460 | 255 461 | ], 462 | "text": "TAP n\nTAP [ 10" 463 | }, 464 | { 465 | "color": [ 466 | 255, 467 | 0, 468 | 0 469 | ], 470 | "text": "TAP delete" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 187, 476 | 255 477 | ], 478 | "text": "PRESS ctrl\nTAP y\nRELEASE ctrl" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 255 485 | ], 486 | "text": "M_LINE_MOVE 15 -30 1\nWAIT_UNPRESSED" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 255 493 | ], 494 | "text": "M_LINE_MOVE 30 -30 1\nWAIT_UNPRESSED" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 0, 500 | 255 501 | ], 502 | "text": "M_LINE_MOVE 30 0 1\nWAIT_UNPRESSED" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 255 509 | ], 510 | "text": "M_LINE_MOVE 30 30 1\nWAIT_UNPRESSED" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 255 517 | ], 518 | "text": "M_LINE_MOVE 15 30 1\nWAIT_UNPRESSED" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 0, 525 | 0, 526 | 0 527 | ], 528 | "text": "" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 0 543 | ], 544 | "text": "" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 0 551 | ], 552 | "text": "" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 0, 558 | 0 559 | ], 560 | "text": "" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 0, 566 | 255 567 | ], 568 | "text": "M_LINE_MOVE 30 -15 1\nWAIT_UNPRESSED" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 0 575 | ], 576 | "text": "" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 255 583 | ], 584 | "text": "M_LINE_MOVE 30 15 1\nWAIT_UNPRESSED" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 0 591 | ], 592 | "text": "" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 0, 608 | 0 609 | ], 610 | "text": "" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 0 617 | ], 618 | "text": "" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 0 641 | ], 642 | "text": "" 643 | }, 644 | { 645 | "color": [ 646 | 0, 647 | 0, 648 | 0 649 | ], 650 | "text": "" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 0 665 | ], 666 | "text": "" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/Half-Life_2_New_Engine.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 255, 7 | 255, 8 | 255 9 | ], 10 | "text": "@SIMPLE vol_up" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 149, 23 | 255, 24 | 0 25 | ], 26 | "text": "@SIMPLE esc" 27 | }, 28 | { 29 | "color": [ 30 | 174, 31 | 0, 32 | 255 33 | ], 34 | "text": "@SIMPLE `" 35 | }, 36 | { 37 | "color": [ 38 | 85, 39 | 0, 40 | 0 41 | ], 42 | "text": "@SIMPLE 1" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 0, 48 | 0 49 | ], 50 | "text": "" 51 | }, 52 | { 53 | "color": [ 54 | 0, 55 | 0, 56 | 0 57 | ], 58 | "text": "" 59 | }, 60 | { 61 | "color": [ 62 | 116, 63 | 51, 64 | 255 65 | ], 66 | "text": "@SIMPLE shift" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 255, 72 | 0 73 | ], 74 | "text": "@SIMPLE ctrl" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "@SIMPLE vol_down" 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 0 91 | ], 92 | "text": "" 93 | }, 94 | { 95 | "color": [ 96 | 255, 97 | 0, 98 | 0 99 | ], 100 | "text": "@SIMPLE f6" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 255, 106 | 0 107 | ], 108 | "text": "@SIMPLE f9" 109 | }, 110 | { 111 | "color": [ 112 | 85, 113 | 51, 114 | 0 115 | ], 116 | "text": "@SIMPLE 2" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 187, 122 | 255 123 | ], 124 | "text": "@SIMPLE q" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 0, 130 | 255 131 | ], 132 | "text": "@SIMPLE a" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 0 157 | ], 158 | "text": "" 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 0 165 | ], 166 | "text": "" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 0 173 | ], 174 | "text": "" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 0 181 | ], 182 | "text": "" 183 | }, 184 | { 185 | "color": [ 186 | 85, 187 | 85, 188 | 0 189 | ], 190 | "text": "@SIMPLE 3" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 255 197 | ], 198 | "text": "@SIMPLE w" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 255 205 | ], 206 | "text": "@SIMPLE s" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 0 213 | ], 214 | "text": "" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "text": "" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 0 239 | ], 240 | "text": "" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 0 247 | ], 248 | "text": "" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 0, 254 | 0 255 | ], 256 | "text": "" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 85, 262 | 0 263 | ], 264 | "text": "@SIMPLE 4" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 187, 270 | 255 271 | ], 272 | "text": "@SIMPLE e" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 0, 278 | 255 279 | ], 280 | "text": "@SIMPLE d" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 0 287 | ], 288 | "text": "" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 0 295 | ], 296 | "text": "" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 0 305 | ], 306 | "text": "" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 0 321 | ], 322 | "text": "" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 0 329 | ], 330 | "text": "" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 85 337 | ], 338 | "text": "@SIMPLE 5" 339 | }, 340 | { 341 | "color": [ 342 | 136, 343 | 255, 344 | 0 345 | ], 346 | "text": "@SIMPLE r" 347 | }, 348 | { 349 | "color": [ 350 | 0, 351 | 187, 352 | 255 353 | ], 354 | "text": "@SIMPLE f" 355 | }, 356 | { 357 | "color": [ 358 | 141, 359 | 70, 360 | 20 361 | ], 362 | "text": "@ASYNC\n- Wallclimbing Script\n\nPRESS w\n\nLABEL start\nTAP space\nTAP e\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start\n\nRELEASE w" 363 | }, 364 | { 365 | "color": [ 366 | 255, 367 | 0, 368 | 255 369 | ], 370 | "text": "@ASYNC\n- Auto-ABH Script\n\nTAP space\nPRESS ctrl\n\nLABEL start\nTAP space\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start\n\nRELEASE ctrl" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 0, 385 | 0, 386 | 0 387 | ], 388 | "text": "" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 0, 394 | 0 395 | ], 396 | "text": "" 397 | }, 398 | { 399 | "color": [ 400 | 0, 401 | 0, 402 | 0 403 | ], 404 | "text": "" 405 | }, 406 | { 407 | "color": [ 408 | 51, 409 | 34, 410 | 85 411 | ], 412 | "text": "@SIMPLE 6" 413 | }, 414 | { 415 | "color": [ 416 | 255, 417 | 166, 418 | 0 419 | ], 420 | "text": "@SIMPLE z" 421 | }, 422 | { 423 | "color": [ 424 | 137, 425 | 210, 426 | 238 427 | ], 428 | "text": "@SIMPLE enter" 429 | }, 430 | { 431 | "color": [ 432 | 255, 433 | 87, 434 | 255 435 | ], 436 | "text": "@SIMPLE space" 437 | }, 438 | { 439 | "color": [ 440 | 255, 441 | 255, 442 | 0 443 | ], 444 | "text": "@ASYNC\n- Jump Script\n\nLABEL start\nTAP space\nDELAY 0.07\nIF_PRESSED_GOTO_LABEL start" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 0, 459 | 0, 460 | 0 461 | ], 462 | "text": "" 463 | }, 464 | { 465 | "color": [ 466 | 0, 467 | 0, 468 | 0 469 | ], 470 | "text": "" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 0, 476 | 0 477 | ], 478 | "text": "" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 0 485 | ], 486 | "text": "" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 0 493 | ], 494 | "text": "" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 0, 500 | 0 501 | ], 502 | "text": "" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 0 509 | ], 510 | "text": "" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 0 517 | ], 518 | "text": "" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 255, 525 | 166, 526 | 0 527 | ], 528 | "text": "OPEN C:\\Program Files (x86)\\Steam\\steamapps\\common\\Half-Life 2\\hl2.exe" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 0 543 | ], 544 | "text": "" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 0 551 | ], 552 | "text": "" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 0, 558 | 0 559 | ], 560 | "text": "" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 0, 566 | 0 567 | ], 568 | "text": "" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 0 575 | ], 576 | "text": "" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 0 583 | ], 584 | "text": "" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 0 591 | ], 592 | "text": "" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 255, 608 | 0 609 | ], 610 | "text": "- Enables cl_showpos\n\nTAP `\nDELAY 0.1\n\nSTRING cl_showpos 1\nTAP enter\nDELAY 0.1\n\nTAP `" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 0 617 | ], 618 | "text": "" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 0 641 | ], 642 | "text": "" 643 | }, 644 | { 645 | "color": [ 646 | 0, 647 | 0, 648 | 0 649 | ], 650 | "text": "" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 187, 664 | 255 665 | ], 666 | "text": "- Crazy bonkers physics gun button.\n\nTAP `\nDELAY 0.1\n\nSTRING sv_cheats 1\nTAP enter\nDELAY 0.1\n\nSTRING give weapon_physcannon\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_mega_enabled 1\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_mega_tracelength 10000\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_mega_pullforce 100000\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_maxmass 10000\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_minforce 10000\nTAP enter\nDELAY 0.1\n\nSTRING physcannon_maxforce 10000\nTAP enter\nDELAY 0.1\n\nTAP `" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/Undertale.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 255, 7 | 255, 8 | 255 9 | ], 10 | "text": "@SIMPLE vol_up" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 255, 23 | 0, 24 | 0 25 | ], 26 | "text": "-" 27 | }, 28 | { 29 | "color": [ 30 | 255, 31 | 0, 32 | 0 33 | ], 34 | "text": "-" 35 | }, 36 | { 37 | "color": [ 38 | 255, 39 | 0, 40 | 0 41 | ], 42 | "text": "-" 43 | }, 44 | { 45 | "color": [ 46 | 255, 47 | 0, 48 | 0 49 | ], 50 | "text": "-" 51 | }, 52 | { 53 | "color": [ 54 | 255, 55 | 255, 56 | 0 57 | ], 58 | "text": "@SIMPLE enter" 59 | }, 60 | { 61 | "color": [ 62 | 0, 63 | 0, 64 | 0 65 | ], 66 | "text": "" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 0, 72 | 0 73 | ], 74 | "text": "" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "@SIMPLE vol_down" 85 | }, 86 | { 87 | "color": [ 88 | 255, 89 | 0, 90 | 0 91 | ], 92 | "text": "-" 93 | }, 94 | { 95 | "color": [ 96 | 255, 97 | 0, 98 | 0 99 | ], 100 | "text": "-" 101 | }, 102 | { 103 | "color": [ 104 | 255, 105 | 0, 106 | 0 107 | ], 108 | "text": "-" 109 | }, 110 | { 111 | "color": [ 112 | 255, 113 | 0, 114 | 0 115 | ], 116 | "text": "-" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 255 123 | ], 124 | "text": "@SIMPLE left" 125 | }, 126 | { 127 | "color": [ 128 | 255, 129 | 0, 130 | 0 131 | ], 132 | "text": "-" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 0 157 | ], 158 | "text": "" 159 | }, 160 | { 161 | "color": [ 162 | 255, 163 | 0, 164 | 0 165 | ], 166 | "text": "-" 167 | }, 168 | { 169 | "color": [ 170 | 255, 171 | 0, 172 | 0 173 | ], 174 | "text": "-" 175 | }, 176 | { 177 | "color": [ 178 | 255, 179 | 0, 180 | 0 181 | ], 182 | "text": "-" 183 | }, 184 | { 185 | "color": [ 186 | 0, 187 | 0, 188 | 255 189 | ], 190 | "text": "@SIMPLE up" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 255 197 | ], 198 | "text": "@SIMPLE down" 199 | }, 200 | { 201 | "color": [ 202 | 255, 203 | 0, 204 | 0 205 | ], 206 | "text": "-" 207 | }, 208 | { 209 | "color": [ 210 | 255, 211 | 0, 212 | 0 213 | ], 214 | "text": "-" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "text": "" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 0 239 | ], 240 | "text": "" 241 | }, 242 | { 243 | "color": [ 244 | 255, 245 | 0, 246 | 0 247 | ], 248 | "text": "-" 249 | }, 250 | { 251 | "color": [ 252 | 255, 253 | 0, 254 | 0 255 | ], 256 | "text": "-" 257 | }, 258 | { 259 | "color": [ 260 | 255, 261 | 0, 262 | 0 263 | ], 264 | "text": "-" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 0, 270 | 255 271 | ], 272 | "text": "@SIMPLE right" 273 | }, 274 | { 275 | "color": [ 276 | 255, 277 | 0, 278 | 0 279 | ], 280 | "text": "-" 281 | }, 282 | { 283 | "color": [ 284 | 255, 285 | 0, 286 | 0 287 | ], 288 | "text": "-" 289 | }, 290 | { 291 | "color": [ 292 | 255, 293 | 0, 294 | 0 295 | ], 296 | "text": "-" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 247, 304 | 255 305 | ], 306 | "text": "@SIMPLE f4" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 255, 319 | 0, 320 | 0 321 | ], 322 | "text": "-" 323 | }, 324 | { 325 | "color": [ 326 | 255, 327 | 0, 328 | 0 329 | ], 330 | "text": "-" 331 | }, 332 | { 333 | "color": [ 334 | 255, 335 | 0, 336 | 0 337 | ], 338 | "text": "-" 339 | }, 340 | { 341 | "color": [ 342 | 255, 343 | 0, 344 | 0 345 | ], 346 | "text": "-" 347 | }, 348 | { 349 | "color": [ 350 | 255, 351 | 255, 352 | 0 353 | ], 354 | "text": "@SIMPLE enter" 355 | }, 356 | { 357 | "color": [ 358 | 255, 359 | 106, 360 | 0 361 | ], 362 | "text": "@SIMPLE x" 363 | }, 364 | { 365 | "color": [ 366 | 255, 367 | 0, 368 | 0 369 | ], 370 | "text": "-" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 255, 385 | 0, 386 | 0 387 | ], 388 | "text": "-" 389 | }, 390 | { 391 | "color": [ 392 | 255, 393 | 0, 394 | 0 395 | ], 396 | "text": "-" 397 | }, 398 | { 399 | "color": [ 400 | 255, 401 | 0, 402 | 0 403 | ], 404 | "text": "-" 405 | }, 406 | { 407 | "color": [ 408 | 255, 409 | 0, 410 | 0 411 | ], 412 | "text": "-" 413 | }, 414 | { 415 | "color": [ 416 | 255, 417 | 0, 418 | 0 419 | ], 420 | "text": "-" 421 | }, 422 | { 423 | "color": [ 424 | 255, 425 | 0, 426 | 255 427 | ], 428 | "text": "@SIMPLE c" 429 | }, 430 | { 431 | "color": [ 432 | 255, 433 | 0, 434 | 0 435 | ], 436 | "text": "-" 437 | }, 438 | { 439 | "color": [ 440 | 0, 441 | 0, 442 | 0 443 | ], 444 | "text": "" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 255, 459 | 0, 460 | 0 461 | ], 462 | "text": "-" 463 | }, 464 | { 465 | "color": [ 466 | 255, 467 | 0, 468 | 0 469 | ], 470 | "text": "-" 471 | }, 472 | { 473 | "color": [ 474 | 255, 475 | 0, 476 | 0 477 | ], 478 | "text": "-" 479 | }, 480 | { 481 | "color": [ 482 | 255, 483 | 0, 484 | 0 485 | ], 486 | "text": "-" 487 | }, 488 | { 489 | "color": [ 490 | 255, 491 | 0, 492 | 0 493 | ], 494 | "text": "-" 495 | }, 496 | { 497 | "color": [ 498 | 255, 499 | 0, 500 | 0 501 | ], 502 | "text": "-" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 0 509 | ], 510 | "text": "" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 0 517 | ], 518 | "text": "" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 255, 525 | 0, 526 | 60 527 | ], 528 | "text": "@SIMPLE esc" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 255, 541 | 0, 542 | 0 543 | ], 544 | "text": "-" 545 | }, 546 | { 547 | "color": [ 548 | 255, 549 | 0, 550 | 0 551 | ], 552 | "text": "-" 553 | }, 554 | { 555 | "color": [ 556 | 255, 557 | 0, 558 | 0 559 | ], 560 | "text": "-" 561 | }, 562 | { 563 | "color": [ 564 | 255, 565 | 0, 566 | 0 567 | ], 568 | "text": "-" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 0 575 | ], 576 | "text": "" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 0 583 | ], 584 | "text": "" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 0 591 | ], 592 | "text": "" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 0, 608 | 0 609 | ], 610 | "text": "" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 0 617 | ], 618 | "text": "" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 0 641 | ], 642 | "text": "" 643 | }, 644 | { 645 | "color": [ 646 | 255, 647 | 255, 648 | 255 649 | ], 650 | "text": "@SIMPLE mute" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 0 665 | ], 666 | "text": "" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/all_delays_all_day.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 0, 7 | 255, 8 | 0 9 | ], 10 | "text": "@LOAD_LAYOUT examples/all_delays_all_gay.lpl" 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 255 17 | ], 18 | "text": "DELAY 2" 19 | }, 20 | { 21 | "color": [ 22 | 0, 23 | 0, 24 | 255 25 | ], 26 | "text": "DELAY 2" 27 | }, 28 | { 29 | "color": [ 30 | 0, 31 | 0, 32 | 255 33 | ], 34 | "text": "DELAY 2" 35 | }, 36 | { 37 | "color": [ 38 | 0, 39 | 0, 40 | 255 41 | ], 42 | "text": "DELAY 2" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 0, 48 | 255 49 | ], 50 | "text": "DELAY 2" 51 | }, 52 | { 53 | "color": [ 54 | 0, 55 | 0, 56 | 255 57 | ], 58 | "text": "DELAY 2" 59 | }, 60 | { 61 | "color": [ 62 | 0, 63 | 0, 64 | 255 65 | ], 66 | "text": "DELAY 2" 67 | }, 68 | { 69 | "color": [ 70 | 0, 71 | 0, 72 | 255 73 | ], 74 | "text": "DELAY 2" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 0, 81 | 0, 82 | 255 83 | ], 84 | "text": "DELAY 2" 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 255 91 | ], 92 | "text": "DELAY 2" 93 | }, 94 | { 95 | "color": [ 96 | 0, 97 | 0, 98 | 255 99 | ], 100 | "text": "DELAY 2" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 0, 106 | 255 107 | ], 108 | "text": "DELAY 2" 109 | }, 110 | { 111 | "color": [ 112 | 0, 113 | 0, 114 | 255 115 | ], 116 | "text": "DELAY 2" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 255 123 | ], 124 | "text": "DELAY 2" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 0, 130 | 255 131 | ], 132 | "text": "DELAY 2" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 255 139 | ], 140 | "text": "DELAY 2" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 255 147 | ], 148 | "text": "DELAY 2" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 255 157 | ], 158 | "text": "DELAY 2" 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 255 165 | ], 166 | "text": "DELAY 2" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 255 173 | ], 174 | "text": "DELAY 2" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 255 181 | ], 182 | "text": "DELAY 2" 183 | }, 184 | { 185 | "color": [ 186 | 0, 187 | 0, 188 | 255 189 | ], 190 | "text": "DELAY 2" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 255 197 | ], 198 | "text": "DELAY 2" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 255 205 | ], 206 | "text": "DELAY 2" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 255 213 | ], 214 | "text": "DELAY 2" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 255 221 | ], 222 | "text": "DELAY 2" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 255 231 | ], 232 | "text": "DELAY 2" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 255 239 | ], 240 | "text": "DELAY 2" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 255 247 | ], 248 | "text": "DELAY 2" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 0, 254 | 255 255 | ], 256 | "text": "DELAY 2" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 0, 262 | 255 263 | ], 264 | "text": "DELAY 2" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 0, 270 | 255 271 | ], 272 | "text": "DELAY 2" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 0, 278 | 255 279 | ], 280 | "text": "DELAY 2" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 255 287 | ], 288 | "text": "DELAY 2" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 255 295 | ], 296 | "text": "DELAY 2" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 255 305 | ], 306 | "text": "DELAY 2" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 255 313 | ], 314 | "text": "DELAY 2" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 255 321 | ], 322 | "text": "DELAY 2" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 255 329 | ], 330 | "text": "DELAY 2" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 255 337 | ], 338 | "text": "DELAY 2" 339 | }, 340 | { 341 | "color": [ 342 | 0, 343 | 0, 344 | 255 345 | ], 346 | "text": "DELAY 2" 347 | }, 348 | { 349 | "color": [ 350 | 0, 351 | 0, 352 | 255 353 | ], 354 | "text": "DELAY 2" 355 | }, 356 | { 357 | "color": [ 358 | 0, 359 | 0, 360 | 255 361 | ], 362 | "text": "DELAY 2" 363 | }, 364 | { 365 | "color": [ 366 | 0, 367 | 0, 368 | 255 369 | ], 370 | "text": "DELAY 2" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 255 379 | ], 380 | "text": "DELAY 2" 381 | }, 382 | { 383 | "color": [ 384 | 0, 385 | 0, 386 | 255 387 | ], 388 | "text": "DELAY 2" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 0, 394 | 255 395 | ], 396 | "text": "DELAY 2" 397 | }, 398 | { 399 | "color": [ 400 | 0, 401 | 0, 402 | 255 403 | ], 404 | "text": "DELAY 2" 405 | }, 406 | { 407 | "color": [ 408 | 0, 409 | 0, 410 | 255 411 | ], 412 | "text": "DELAY 2" 413 | }, 414 | { 415 | "color": [ 416 | 0, 417 | 0, 418 | 255 419 | ], 420 | "text": "DELAY 2" 421 | }, 422 | { 423 | "color": [ 424 | 0, 425 | 0, 426 | 255 427 | ], 428 | "text": "DELAY 2" 429 | }, 430 | { 431 | "color": [ 432 | 0, 433 | 0, 434 | 255 435 | ], 436 | "text": "DELAY 2" 437 | }, 438 | { 439 | "color": [ 440 | 0, 441 | 0, 442 | 255 443 | ], 444 | "text": "DELAY 2" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 255 453 | ], 454 | "text": "DELAY 2" 455 | }, 456 | { 457 | "color": [ 458 | 0, 459 | 0, 460 | 255 461 | ], 462 | "text": "DELAY 2" 463 | }, 464 | { 465 | "color": [ 466 | 0, 467 | 0, 468 | 255 469 | ], 470 | "text": "DELAY 2" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 0, 476 | 255 477 | ], 478 | "text": "DELAY 2" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 255 485 | ], 486 | "text": "DELAY 2" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 255 493 | ], 494 | "text": "DELAY 2" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 0, 500 | 255 501 | ], 502 | "text": "DELAY 2" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 255 509 | ], 510 | "text": "DELAY 2" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 255 517 | ], 518 | "text": "DELAY 2" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 0, 525 | 0, 526 | 255 527 | ], 528 | "text": "DELAY 2" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 255 535 | ], 536 | "text": "DELAY 2" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 255 543 | ], 544 | "text": "DELAY 2" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 255 551 | ], 552 | "text": "DELAY 2" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 0, 558 | 255 559 | ], 560 | "text": "DELAY 2" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 0, 566 | 255 567 | ], 568 | "text": "DELAY 2" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 255 575 | ], 576 | "text": "DELAY 2" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 255 583 | ], 584 | "text": "DELAY 2" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 255 591 | ], 592 | "text": "DELAY 2" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 0, 607 | 0, 608 | 255 609 | ], 610 | "text": "DELAY 2" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 255 617 | ], 618 | "text": "DELAY 2" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 255 625 | ], 626 | "text": "DELAY 2" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 255 633 | ], 634 | "text": "DELAY 2" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 255 641 | ], 642 | "text": "DELAY 2" 643 | }, 644 | { 645 | "color": [ 646 | 0, 647 | 0, 648 | 255 649 | ], 650 | "text": "DELAY 2" 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 255 657 | ], 658 | "text": "DELAY 2" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 255 665 | ], 666 | "text": "DELAY 2" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/all_delays_all_gay.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 0, 7 | 255, 8 | 0 9 | ], 10 | "text": "@LOAD_LAYOUT examples/all_delays_all_day.lpl" 11 | }, 12 | { 13 | "color": [ 14 | 255, 15 | 0, 16 | 0 17 | ], 18 | "text": "DELAY 1" 19 | }, 20 | { 21 | "color": [ 22 | 255, 23 | 153, 24 | 0 25 | ], 26 | "text": "DELAY 1" 27 | }, 28 | { 29 | "color": [ 30 | 255, 31 | 255, 32 | 0 33 | ], 34 | "text": "DELAY 1" 35 | }, 36 | { 37 | "color": [ 38 | 0, 39 | 255, 40 | 0 41 | ], 42 | "text": "DELAY 1" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 255, 48 | 0 49 | ], 50 | "text": "DELAY 1" 51 | }, 52 | { 53 | "color": [ 54 | 255, 55 | 255, 56 | 0 57 | ], 58 | "text": "DELAY 1" 59 | }, 60 | { 61 | "color": [ 62 | 255, 63 | 153, 64 | 0 65 | ], 66 | "text": "DELAY 1" 67 | }, 68 | { 69 | "color": [ 70 | 255, 71 | 0, 72 | 0 73 | ], 74 | "text": "DELAY 1" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "DELAY 1" 85 | }, 86 | { 87 | "color": [ 88 | 255, 89 | 153, 90 | 0 91 | ], 92 | "text": "DELAY 1" 93 | }, 94 | { 95 | "color": [ 96 | 255, 97 | 255, 98 | 0 99 | ], 100 | "text": "DELAY 1" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 255, 106 | 0 107 | ], 108 | "text": "DELAY 1" 109 | }, 110 | { 111 | "color": [ 112 | 0, 113 | 0, 114 | 255 115 | ], 116 | "text": "DELAY 1" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 255 123 | ], 124 | "text": "DELAY 1" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 255, 130 | 0 131 | ], 132 | "text": "DELAY 1" 133 | }, 134 | { 135 | "color": [ 136 | 255, 137 | 255, 138 | 0 139 | ], 140 | "text": "DELAY 1" 141 | }, 142 | { 143 | "color": [ 144 | 255, 145 | 153, 146 | 0 147 | ], 148 | "text": "DELAY 1" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 255, 155 | 255, 156 | 255 157 | ], 158 | "text": "DELAY 1" 159 | }, 160 | { 161 | "color": [ 162 | 255, 163 | 255, 164 | 0 165 | ], 166 | "text": "DELAY 1" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 255, 172 | 0 173 | ], 174 | "text": "DELAY 1" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 255 181 | ], 182 | "text": "DELAY 1" 183 | }, 184 | { 185 | "color": [ 186 | 153, 187 | 102, 188 | 255 189 | ], 190 | "text": "DELAY 1" 191 | }, 192 | { 193 | "color": [ 194 | 153, 195 | 102, 196 | 255 197 | ], 198 | "text": "DELAY 1" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 255 205 | ], 206 | "text": "DELAY 1" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 255, 212 | 0 213 | ], 214 | "text": "DELAY 1" 215 | }, 216 | { 217 | "color": [ 218 | 255, 219 | 255, 220 | 0 221 | ], 222 | "text": "DELAY 1" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 255, 229 | 255, 230 | 255 231 | ], 232 | "text": "DELAY 1" 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 255, 238 | 0 239 | ], 240 | "text": "DELAY 1" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 255 247 | ], 248 | "text": "DELAY 1" 249 | }, 250 | { 251 | "color": [ 252 | 153, 253 | 102, 254 | 255 255 | ], 256 | "text": "DELAY 1" 257 | }, 258 | { 259 | "color": [ 260 | 255, 261 | 0, 262 | 255 263 | ], 264 | "text": "DELAY 1" 265 | }, 266 | { 267 | "color": [ 268 | 255, 269 | 0, 270 | 255 271 | ], 272 | "text": "DELAY 1" 273 | }, 274 | { 275 | "color": [ 276 | 153, 277 | 102, 278 | 255 279 | ], 280 | "text": "DELAY 1" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 255 287 | ], 288 | "text": "DELAY 1" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 255, 294 | 0 295 | ], 296 | "text": "DELAY 1" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 255, 303 | 255, 304 | 255 305 | ], 306 | "text": "DELAY 1" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 255, 312 | 0 313 | ], 314 | "text": "DELAY 1" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 255 321 | ], 322 | "text": "DELAY 1" 323 | }, 324 | { 325 | "color": [ 326 | 153, 327 | 102, 328 | 255 329 | ], 330 | "text": "DELAY 1" 331 | }, 332 | { 333 | "color": [ 334 | 255, 335 | 0, 336 | 255 337 | ], 338 | "text": "DELAY 1" 339 | }, 340 | { 341 | "color": [ 342 | 255, 343 | 0, 344 | 255 345 | ], 346 | "text": "DELAY 1" 347 | }, 348 | { 349 | "color": [ 350 | 153, 351 | 102, 352 | 255 353 | ], 354 | "text": "DELAY 1" 355 | }, 356 | { 357 | "color": [ 358 | 0, 359 | 0, 360 | 255 361 | ], 362 | "text": "DELAY 1" 363 | }, 364 | { 365 | "color": [ 366 | 0, 367 | 255, 368 | 0 369 | ], 370 | "text": "DELAY 1" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 255, 377 | 255, 378 | 255 379 | ], 380 | "text": "DELAY 1" 381 | }, 382 | { 383 | "color": [ 384 | 255, 385 | 255, 386 | 0 387 | ], 388 | "text": "DELAY 1" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 255, 394 | 0 395 | ], 396 | "text": "DELAY 1" 397 | }, 398 | { 399 | "color": [ 400 | 0, 401 | 0, 402 | 255 403 | ], 404 | "text": "DELAY 1" 405 | }, 406 | { 407 | "color": [ 408 | 153, 409 | 102, 410 | 255 411 | ], 412 | "text": "DELAY 1" 413 | }, 414 | { 415 | "color": [ 416 | 153, 417 | 102, 418 | 255 419 | ], 420 | "text": "DELAY 1" 421 | }, 422 | { 423 | "color": [ 424 | 0, 425 | 0, 426 | 255 427 | ], 428 | "text": "DELAY 1" 429 | }, 430 | { 431 | "color": [ 432 | 0, 433 | 255, 434 | 0 435 | ], 436 | "text": "DELAY 1" 437 | }, 438 | { 439 | "color": [ 440 | 255, 441 | 255, 442 | 0 443 | ], 444 | "text": "DELAY 1" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 255, 451 | 255, 452 | 255 453 | ], 454 | "text": "DELAY 1" 455 | }, 456 | { 457 | "color": [ 458 | 255, 459 | 153, 460 | 0 461 | ], 462 | "text": "DELAY 1" 463 | }, 464 | { 465 | "color": [ 466 | 255, 467 | 255, 468 | 0 469 | ], 470 | "text": "DELAY 1" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 255, 476 | 0 477 | ], 478 | "text": "DELAY 1" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 255 485 | ], 486 | "text": "DELAY 1" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 255 493 | ], 494 | "text": "DELAY 1" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 255, 500 | 0 501 | ], 502 | "text": "DELAY 1" 503 | }, 504 | { 505 | "color": [ 506 | 255, 507 | 255, 508 | 0 509 | ], 510 | "text": "DELAY 1" 511 | }, 512 | { 513 | "color": [ 514 | 255, 515 | 153, 516 | 0 517 | ], 518 | "text": "DELAY 1" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 255, 525 | 255, 526 | 255 527 | ], 528 | "text": "DELAY 1" 529 | }, 530 | { 531 | "color": [ 532 | 255, 533 | 0, 534 | 0 535 | ], 536 | "text": "DELAY 1" 537 | }, 538 | { 539 | "color": [ 540 | 255, 541 | 153, 542 | 0 543 | ], 544 | "text": "DELAY 1" 545 | }, 546 | { 547 | "color": [ 548 | 255, 549 | 255, 550 | 0 551 | ], 552 | "text": "DELAY 1" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 255, 558 | 0 559 | ], 560 | "text": "DELAY 1" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 255, 566 | 0 567 | ], 568 | "text": "DELAY 1" 569 | }, 570 | { 571 | "color": [ 572 | 255, 573 | 255, 574 | 0 575 | ], 576 | "text": "DELAY 1" 577 | }, 578 | { 579 | "color": [ 580 | 255, 581 | 153, 582 | 0 583 | ], 584 | "text": "DELAY 1" 585 | }, 586 | { 587 | "color": [ 588 | 255, 589 | 0, 590 | 0 591 | ], 592 | "text": "DELAY 1" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 255, 607 | 255, 608 | 255 609 | ], 610 | "text": "DELAY 1" 611 | }, 612 | { 613 | "color": [ 614 | 255, 615 | 255, 616 | 255 617 | ], 618 | "text": "DELAY 1" 619 | }, 620 | { 621 | "color": [ 622 | 255, 623 | 255, 624 | 255 625 | ], 626 | "text": "DELAY 1" 627 | }, 628 | { 629 | "color": [ 630 | 255, 631 | 255, 632 | 255 633 | ], 634 | "text": "DELAY 1" 635 | }, 636 | { 637 | "color": [ 638 | 255, 639 | 255, 640 | 255 641 | ], 642 | "text": "DELAY 1" 643 | }, 644 | { 645 | "color": [ 646 | 255, 647 | 255, 648 | 255 649 | ], 650 | "text": "DELAY 1" 651 | }, 652 | { 653 | "color": [ 654 | 255, 655 | 255, 656 | 255 657 | ], 658 | "text": "DELAY 1" 659 | }, 660 | { 661 | "color": [ 662 | 255, 663 | 255, 664 | 255 665 | ], 666 | "text": "DELAY 1" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_layouts/examples/default.lpl: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": [ 3 | [ 4 | { 5 | "color": [ 6 | 255, 7 | 255, 8 | 255 9 | ], 10 | "text": "@SIMPLE vol_up\n- Volume up key." 11 | }, 12 | { 13 | "color": [ 14 | 0, 15 | 0, 16 | 0 17 | ], 18 | "text": "" 19 | }, 20 | { 21 | "color": [ 22 | 0, 23 | 0, 24 | 0 25 | ], 26 | "text": "" 27 | }, 28 | { 29 | "color": [ 30 | 255, 31 | 0, 32 | 255 33 | ], 34 | "text": "- Example of: scroling mouse, wait until unpressed\n\nM_SCROLL -100\nWAIT_UNPRESSED\nM_SCROLL 100" 35 | }, 36 | { 37 | "color": [ 38 | 0, 39 | 0, 40 | 255 41 | ], 42 | "text": "- Example of: storing mouse, recalling mouse in a line, moving mouse in a line\n\nM_STORE\nM_LINE_SET 100 200 1 5\nM_LINE_MOVE 100 100 10\nM_LINE_MOVE 100 -100 10\nM_LINE_MOVE -100 -100 10\nM_LINE_MOVE -100 100 10\nM_RECALL_LINE 1 5" 43 | }, 44 | { 45 | "color": [ 46 | 0, 47 | 255, 48 | 0 49 | ], 50 | "text": "- Example of: opening websites, delays, tapping\n\nWEB_NEW tv.nimaid.com\nDELAY 2\nTAP f11" 51 | }, 52 | { 53 | "color": [ 54 | 255, 55 | 255, 56 | 0 57 | ], 58 | "text": "@ASYNC\n- Example of: async header, playing sounds, unconditional looping, delays\n- Tap a running script to kill it\n\nLABEL start\n\nSOUND examples/doublekill.wav 50\nDELAY 0.8\nSOUND examples/doublekill.wav 50\nDELAY 0.8\n\nSOUND examples/triplekill.wav 50\nDELAY 0.8\nSOUND examples/triplekill.wav 50\nDELAY 0.8\n\nSOUND examples/killingspree.wav 50\nDELAY 0.8\nSOUND examples/killingspree.wav 50\nDELAY 0.8\n\nSOUND examples/killtacular.wav 50\nDELAY 0.8\n\nSOUND examples/runningriot.wav 50\nDELAY 0.8\n\nGOTO_LABEL start" 59 | }, 60 | { 61 | "color": [ 62 | 255, 63 | 166, 64 | 0 65 | ], 66 | "text": "- Example of: conditional repeating, delays, typing, tapping\n\nLABEL loop\nSTRING I love to loop and repeat myself forever into the void.\nTAP enter\nDELAY 1\nIF_PRESSED_REPEAT_LABEL loop 2" 67 | }, 68 | { 69 | "color": [ 70 | 255, 71 | 0, 72 | 0 73 | ], 74 | "text": "- Example of: conditional looping, delays, typing, tapping\n\nLABEL loop\nSTRING I love to loop\nTAP space\nDELAY 1\nIF_UNPRESSED_GOTO_LABEL end\nSTRING and repeat myself\nTAP space\nDELAY 1\nIF_UNPRESSED_GOTO_LABEL end\nSTRING forever into\nTAP space\nDELAY 1\nIF_UNPRESSED_GOTO_LABEL end\nSTRING the void.\nTAP enter\nDELAY 1\nIF_PRESSED_GOTO_LABEL loop\n\nLABEL end" 75 | } 76 | ], 77 | [ 78 | { 79 | "color": [ 80 | 255, 81 | 255, 82 | 255 83 | ], 84 | "text": "@SIMPLE vol_down\n- Volume down key." 85 | }, 86 | { 87 | "color": [ 88 | 0, 89 | 0, 90 | 0 91 | ], 92 | "text": "" 93 | }, 94 | { 95 | "color": [ 96 | 0, 97 | 0, 98 | 0 99 | ], 100 | "text": "" 101 | }, 102 | { 103 | "color": [ 104 | 0, 105 | 0, 106 | 0 107 | ], 108 | "text": "" 109 | }, 110 | { 111 | "color": [ 112 | 0, 113 | 0, 114 | 0 115 | ], 116 | "text": "" 117 | }, 118 | { 119 | "color": [ 120 | 0, 121 | 0, 122 | 0 123 | ], 124 | "text": "" 125 | }, 126 | { 127 | "color": [ 128 | 0, 129 | 0, 130 | 0 131 | ], 132 | "text": "" 133 | }, 134 | { 135 | "color": [ 136 | 0, 137 | 0, 138 | 0 139 | ], 140 | "text": "" 141 | }, 142 | { 143 | "color": [ 144 | 0, 145 | 0, 146 | 0 147 | ], 148 | "text": "" 149 | } 150 | ], 151 | [ 152 | { 153 | "color": [ 154 | 0, 155 | 0, 156 | 255 157 | ], 158 | "text": "@SIMPLE prev_track\n- Previous track key." 159 | }, 160 | { 161 | "color": [ 162 | 0, 163 | 0, 164 | 0 165 | ], 166 | "text": "" 167 | }, 168 | { 169 | "color": [ 170 | 0, 171 | 0, 172 | 0 173 | ], 174 | "text": "" 175 | }, 176 | { 177 | "color": [ 178 | 0, 179 | 0, 180 | 0 181 | ], 182 | "text": "" 183 | }, 184 | { 185 | "color": [ 186 | 0, 187 | 0, 188 | 0 189 | ], 190 | "text": "" 191 | }, 192 | { 193 | "color": [ 194 | 0, 195 | 0, 196 | 0 197 | ], 198 | "text": "" 199 | }, 200 | { 201 | "color": [ 202 | 0, 203 | 0, 204 | 0 205 | ], 206 | "text": "" 207 | }, 208 | { 209 | "color": [ 210 | 0, 211 | 0, 212 | 0 213 | ], 214 | "text": "" 215 | }, 216 | { 217 | "color": [ 218 | 0, 219 | 0, 220 | 0 221 | ], 222 | "text": "" 223 | } 224 | ], 225 | [ 226 | { 227 | "color": [ 228 | 0, 229 | 0, 230 | 255 231 | ], 232 | "text": "@SIMPLE next_track\n- Next track key." 233 | }, 234 | { 235 | "color": [ 236 | 0, 237 | 0, 238 | 0 239 | ], 240 | "text": "" 241 | }, 242 | { 243 | "color": [ 244 | 0, 245 | 0, 246 | 0 247 | ], 248 | "text": "" 249 | }, 250 | { 251 | "color": [ 252 | 0, 253 | 0, 254 | 0 255 | ], 256 | "text": "" 257 | }, 258 | { 259 | "color": [ 260 | 0, 261 | 0, 262 | 0 263 | ], 264 | "text": "" 265 | }, 266 | { 267 | "color": [ 268 | 0, 269 | 0, 270 | 0 271 | ], 272 | "text": "" 273 | }, 274 | { 275 | "color": [ 276 | 0, 277 | 0, 278 | 0 279 | ], 280 | "text": "" 281 | }, 282 | { 283 | "color": [ 284 | 0, 285 | 0, 286 | 0 287 | ], 288 | "text": "" 289 | }, 290 | { 291 | "color": [ 292 | 0, 293 | 0, 294 | 0 295 | ], 296 | "text": "" 297 | } 298 | ], 299 | [ 300 | { 301 | "color": [ 302 | 0, 303 | 0, 304 | 0 305 | ], 306 | "text": "" 307 | }, 308 | { 309 | "color": [ 310 | 0, 311 | 0, 312 | 0 313 | ], 314 | "text": "" 315 | }, 316 | { 317 | "color": [ 318 | 0, 319 | 0, 320 | 0 321 | ], 322 | "text": "" 323 | }, 324 | { 325 | "color": [ 326 | 0, 327 | 0, 328 | 0 329 | ], 330 | "text": "" 331 | }, 332 | { 333 | "color": [ 334 | 0, 335 | 0, 336 | 0 337 | ], 338 | "text": "" 339 | }, 340 | { 341 | "color": [ 342 | 0, 343 | 0, 344 | 0 345 | ], 346 | "text": "" 347 | }, 348 | { 349 | "color": [ 350 | 0, 351 | 0, 352 | 0 353 | ], 354 | "text": "" 355 | }, 356 | { 357 | "color": [ 358 | 0, 359 | 0, 360 | 0 361 | ], 362 | "text": "" 363 | }, 364 | { 365 | "color": [ 366 | 0, 367 | 0, 368 | 0 369 | ], 370 | "text": "" 371 | } 372 | ], 373 | [ 374 | { 375 | "color": [ 376 | 0, 377 | 0, 378 | 0 379 | ], 380 | "text": "" 381 | }, 382 | { 383 | "color": [ 384 | 0, 385 | 0, 386 | 0 387 | ], 388 | "text": "" 389 | }, 390 | { 391 | "color": [ 392 | 0, 393 | 0, 394 | 0 395 | ], 396 | "text": "" 397 | }, 398 | { 399 | "color": [ 400 | 0, 401 | 0, 402 | 0 403 | ], 404 | "text": "" 405 | }, 406 | { 407 | "color": [ 408 | 0, 409 | 0, 410 | 0 411 | ], 412 | "text": "" 413 | }, 414 | { 415 | "color": [ 416 | 0, 417 | 0, 418 | 0 419 | ], 420 | "text": "" 421 | }, 422 | { 423 | "color": [ 424 | 0, 425 | 0, 426 | 0 427 | ], 428 | "text": "" 429 | }, 430 | { 431 | "color": [ 432 | 0, 433 | 0, 434 | 0 435 | ], 436 | "text": "" 437 | }, 438 | { 439 | "color": [ 440 | 0, 441 | 0, 442 | 0 443 | ], 444 | "text": "" 445 | } 446 | ], 447 | [ 448 | { 449 | "color": [ 450 | 0, 451 | 0, 452 | 0 453 | ], 454 | "text": "" 455 | }, 456 | { 457 | "color": [ 458 | 0, 459 | 0, 460 | 0 461 | ], 462 | "text": "" 463 | }, 464 | { 465 | "color": [ 466 | 0, 467 | 0, 468 | 0 469 | ], 470 | "text": "" 471 | }, 472 | { 473 | "color": [ 474 | 0, 475 | 0, 476 | 0 477 | ], 478 | "text": "" 479 | }, 480 | { 481 | "color": [ 482 | 0, 483 | 0, 484 | 0 485 | ], 486 | "text": "" 487 | }, 488 | { 489 | "color": [ 490 | 0, 491 | 0, 492 | 0 493 | ], 494 | "text": "" 495 | }, 496 | { 497 | "color": [ 498 | 0, 499 | 0, 500 | 0 501 | ], 502 | "text": "" 503 | }, 504 | { 505 | "color": [ 506 | 0, 507 | 0, 508 | 0 509 | ], 510 | "text": "" 511 | }, 512 | { 513 | "color": [ 514 | 0, 515 | 0, 516 | 0 517 | ], 518 | "text": "" 519 | } 520 | ], 521 | [ 522 | { 523 | "color": [ 524 | 0, 525 | 0, 526 | 0 527 | ], 528 | "text": "" 529 | }, 530 | { 531 | "color": [ 532 | 0, 533 | 0, 534 | 0 535 | ], 536 | "text": "" 537 | }, 538 | { 539 | "color": [ 540 | 0, 541 | 0, 542 | 0 543 | ], 544 | "text": "" 545 | }, 546 | { 547 | "color": [ 548 | 0, 549 | 0, 550 | 0 551 | ], 552 | "text": "" 553 | }, 554 | { 555 | "color": [ 556 | 0, 557 | 0, 558 | 0 559 | ], 560 | "text": "" 561 | }, 562 | { 563 | "color": [ 564 | 0, 565 | 0, 566 | 0 567 | ], 568 | "text": "" 569 | }, 570 | { 571 | "color": [ 572 | 0, 573 | 0, 574 | 0 575 | ], 576 | "text": "" 577 | }, 578 | { 579 | "color": [ 580 | 0, 581 | 0, 582 | 0 583 | ], 584 | "text": "" 585 | }, 586 | { 587 | "color": [ 588 | 0, 589 | 0, 590 | 0 591 | ], 592 | "text": "" 593 | } 594 | ], 595 | [ 596 | { 597 | "color": [ 598 | 0, 599 | 0, 600 | 0 601 | ], 602 | "text": "" 603 | }, 604 | { 605 | "color": [ 606 | 255, 607 | 0, 608 | 0 609 | ], 610 | "text": "@ASYNC\n- Open the project page w/ readme.\nWEB https://github.com/nimaid/LPHK" 611 | }, 612 | { 613 | "color": [ 614 | 0, 615 | 0, 616 | 0 617 | ], 618 | "text": "" 619 | }, 620 | { 621 | "color": [ 622 | 0, 623 | 0, 624 | 0 625 | ], 626 | "text": "" 627 | }, 628 | { 629 | "color": [ 630 | 0, 631 | 0, 632 | 0 633 | ], 634 | "text": "" 635 | }, 636 | { 637 | "color": [ 638 | 0, 639 | 0, 640 | 255 641 | ], 642 | "text": "@SIMPLE play_pause\n- Play/pause key." 643 | }, 644 | { 645 | "color": [ 646 | 255, 647 | 255, 648 | 255 649 | ], 650 | "text": "@SIMPLE mute\n- Mute key." 651 | }, 652 | { 653 | "color": [ 654 | 0, 655 | 0, 656 | 0 657 | ], 658 | "text": "" 659 | }, 660 | { 661 | "color": [ 662 | 0, 663 | 0, 664 | 0 665 | ], 666 | "text": "" 667 | } 668 | ] 669 | ], 670 | "version": "0.1.1" 671 | } -------------------------------------------------------------------------------- /user_scripts/examples/LPHK_project_page.lps: -------------------------------------------------------------------------------- 1 | WEB https://github.com/nimaid/LPHK 2 | -------------------------------------------------------------------------------- /user_scripts/examples/LPMM_project_page.lps: -------------------------------------------------------------------------------- 1 | WEB https://github.com/nimaid/LPMM 2 | -------------------------------------------------------------------------------- /user_scripts/examples/chrome_developer_console.lps: -------------------------------------------------------------------------------- 1 | PRESS ctrl 2 | PRESS shift 3 | TAP j 4 | RELEASE shift 5 | RELEASE ctrl 6 | 7 | -------------------------------------------------------------------------------- /user_scripts/examples/hello_world.lps: -------------------------------------------------------------------------------- 1 | STRING Hello, world! 2 | -------------------------------------------------------------------------------- /user_scripts/examples/mute.lps: -------------------------------------------------------------------------------- 1 | TAP mute 2 | -------------------------------------------------------------------------------- /user_scripts/examples/next_track.lps: -------------------------------------------------------------------------------- 1 | TAP next_track 2 | -------------------------------------------------------------------------------- /user_scripts/examples/nonblocking_press_and_release.lps: -------------------------------------------------------------------------------- 1 | @ASYNC 2 | PRESS y 3 | WAIT_UNPRESSED 4 | RELEASE y 5 | -------------------------------------------------------------------------------- /user_scripts/examples/play_pause.lps: -------------------------------------------------------------------------------- 1 | TAP play_pause 2 | -------------------------------------------------------------------------------- /user_scripts/examples/previous_track.lps: -------------------------------------------------------------------------------- 1 | TAP prev_track 2 | -------------------------------------------------------------------------------- /user_scripts/examples/python_for_template.lps: -------------------------------------------------------------------------------- 1 | STRING for x in range(): 2 | TAP enter 3 | TAP tab 4 | -------------------------------------------------------------------------------- /user_scripts/examples/sound.lps: -------------------------------------------------------------------------------- 1 | SOUND examples/440_1s.wav 2 | -------------------------------------------------------------------------------- /user_scripts/examples/tv.nimaid.com.lps: -------------------------------------------------------------------------------- 1 | WEB_NEW tv.nimaid.com 2 | DELAY 2 3 | TAP f11 4 | -------------------------------------------------------------------------------- /user_scripts/examples/volume_down.lps: -------------------------------------------------------------------------------- 1 | TAP vol_down 2 | -------------------------------------------------------------------------------- /user_scripts/examples/volume_up.lps: -------------------------------------------------------------------------------- 1 | TAP vol_up 2 | -------------------------------------------------------------------------------- /user_scripts/examples/windows_show_desktop.lps: -------------------------------------------------------------------------------- 1 | @ASYNC 2 | - This shows the desktop by clicking the "Minimize All" button on the far right of the taskbar. 3 | M_STORE 4 | M_MOVE 99999999 99999999 5 | TAP mouse_left 6 | M_RECALL 7 | -------------------------------------------------------------------------------- /user_scripts/examples/youtube.lps: -------------------------------------------------------------------------------- 1 | WEB youtube.com 2 | -------------------------------------------------------------------------------- /user_sounds/examples/airhorn.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/airhorn.wav -------------------------------------------------------------------------------- /user_sounds/examples/doublekill.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/doublekill.wav -------------------------------------------------------------------------------- /user_sounds/examples/killingspree.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/killingspree.wav -------------------------------------------------------------------------------- /user_sounds/examples/killtacular.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/killtacular.wav -------------------------------------------------------------------------------- /user_sounds/examples/runningriot.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/runningriot.wav -------------------------------------------------------------------------------- /user_sounds/examples/triplekill.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/user_sounds/examples/triplekill.wav -------------------------------------------------------------------------------- /utils/GET_KEYCODES.py: -------------------------------------------------------------------------------- 1 | import pyautogui 2 | from pynput import keyboard 3 | from time import sleep 4 | 5 | 6 | def press_callback(key): 7 | # print("Name: '" + event.name + "', Scan Code: '" + str(keyboard.key_to_scan_codes(event.name)[0]) + "'") 8 | print('{0} pressed'.format(key)) 9 | 10 | if hasattr(key, 'vk'): 11 | print('vk code {0} pressed'.format(key.vk)) 12 | 13 | 14 | def release_callback(key): 15 | print('{0} released'.format(key)) 16 | 17 | 18 | listener = keyboard.Listener( 19 | on_press=press_callback, 20 | on_release=release_callback) 21 | listener.start() 22 | 23 | print("Reading keys. Press CTRL-C to exit...") 24 | 25 | 26 | while True: 27 | sleep(60) 28 | -------------------------------------------------------------------------------- /utils/LIST_PADS.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | try: 4 | import launchpad_py as launchpad 5 | except ImportError: 6 | try: 7 | import launchpad 8 | except ImportError: 9 | sys.exit("[LPHK] Error loading launchpad.py") 10 | 11 | from getch import pause 12 | 13 | lp = launchpad.Launchpad() 14 | 15 | print("\nListing all MIDI devices...") 16 | lp.ListAll() 17 | 18 | pause("\nPress any key to exit...") 19 | -------------------------------------------------------------------------------- /utils/RAW_CONNECT.py: -------------------------------------------------------------------------------- 1 | # Shush pycharm 2 | # noinspection PyUnresolvedReferences 3 | import launchpad_connector as lpcon 4 | 5 | from getch import pause 6 | 7 | MK2_NAME = "Launchpad MK2" 8 | MK3MINI_NAME = "LPMiniMK3" 9 | PRO_NAME = "Launchpad Pro" 10 | CTRL_XL_NAME = "control xl" 11 | LAUNCHKEY_NAME = "launchkey" 12 | DICER_NAME = "dicer" 13 | 14 | print("\nTrying to connect to launchpad...") 15 | 16 | launchpad = lpcon.get_launchpad() 17 | 18 | if launchpad is -1: 19 | print('Unsupported device detected!') 20 | elif launchpad is None: 21 | print('Launchpad appears to be unplugged!') 22 | else: 23 | name = lpcon.get_display_name(launchpad) 24 | if lpcon.connect(launchpad): 25 | print(f'Connected to {name}! Yay!') 26 | else: 27 | print(f'{name} detected, but connection failed!') 28 | 29 | 30 | pause("\nPress any key to exit...") 31 | lpcon.disconnect(launchpad) 32 | -------------------------------------------------------------------------------- /utils/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set ORIGDIR="%CD%" 4 | set SCRIPTDIR=%~dp0 5 | set LPHKDIR=%SCRIPTDIR%\.. 6 | set ICONDIR=%LPHKDIR%\resources 7 | set ICON=%ICONDIR%\LPHK.ico 8 | 9 | echo Building LIST_PADS... 10 | call conda run -n LPHK-build pyinstaller ^ 11 | --noconfirm ^ 12 | --onefile ^ 13 | --icon=%ICON% ^ 14 | LIST_PADS.py 15 | echo Built LIST_PADS! 16 | 17 | echo Building RAW_CONNECT... 18 | call conda run -n LPHK-build pyinstaller ^ 19 | --noconfirm ^ 20 | --onefile ^ 21 | --icon=%ICON% ^ 22 | RAW_CONNECT.py 23 | echo Built RAW_CONNECT! 24 | 25 | echo Building GET_KEYCODES... 26 | call conda run -n LPHK-build pyinstaller ^ 27 | --noconfirm ^ 28 | --onefile ^ 29 | --icon=%ICON% ^ 30 | GET_KEYCODES.py 31 | echo Built GET_KEYCODES! 32 | 33 | pause -------------------------------------------------------------------------------- /utils/compiled/GET_KEYCODES.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/utils/compiled/GET_KEYCODES.exe -------------------------------------------------------------------------------- /utils/compiled/LIST_PADS.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/utils/compiled/LIST_PADS.exe -------------------------------------------------------------------------------- /utils/compiled/RAW_CONNECT.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimaid/LPHK/c52f180f2fb4c6adcad5426a81f1125b73fe45d3/utils/compiled/RAW_CONNECT.exe -------------------------------------------------------------------------------- /utils/launchpad_connector.py: -------------------------------------------------------------------------------- 1 | import launchpad_py as launchpad 2 | 3 | MK2_NAME = "Launchpad MK2" 4 | # MK3MINI_NAME = "LPMiniMK3" 5 | MK3MINI_NAME = "minimk3" 6 | PRO_NAME = "Launchpad Pro" 7 | LPX_NAME = "lpx" 8 | CTRL_XL_NAME = "control xl" 9 | LAUNCHKEY_NAME = "launchkey" 10 | DICER_NAME = "dicer" 11 | 12 | PAD_MODES = { 13 | launchpad.Launchpad: "Mk1", 14 | launchpad.LaunchpadMk2: "Mk2", 15 | launchpad.LaunchpadMiniMk3: "Mk3", 16 | launchpad.LaunchpadPro: "Pro", 17 | launchpad.LaunchpadLPX: "Mk3" 18 | } 19 | PAD_TEXT = { 20 | launchpad.Launchpad: "Classic/Mini/S", 21 | launchpad.LaunchpadMk2: "MkII", 22 | launchpad.LaunchpadMiniMk3: "Mk3", 23 | launchpad.LaunchpadPro: "Pro (BETA)", 24 | launchpad.LaunchpadLPX: "LPX" 25 | } 26 | 27 | 28 | def get_launchpad(): 29 | lp = launchpad.Launchpad() 30 | 31 | if lp.Check(0, MK2_NAME): 32 | return launchpad.LaunchpadMk2() 33 | # the MK3 has two midi devices, we need the second one 34 | if lp.Check(1, MK3MINI_NAME): 35 | return launchpad.LaunchpadMiniMk3() 36 | if lp.Check(0, PRO_NAME): 37 | return launchpad.LaunchpadPro() 38 | if lp.Check(1, LPX_NAME): 39 | return launchpad.LaunchpadLPX() 40 | 41 | # unsupported pads 42 | if lp.Check(0, CTRL_XL_NAME) or lp.Check(0, LAUNCHKEY_NAME) or lp.Check(0, DICER_NAME): 43 | return -1 44 | 45 | if lp.Check(): 46 | return lp 47 | 48 | return None 49 | 50 | 51 | def get_mode(pad): 52 | cls = type(pad) 53 | 54 | if cls not in PAD_MODES: 55 | return None 56 | 57 | return PAD_MODES[cls] 58 | 59 | 60 | def get_display_name(pad): 61 | cls = type(pad) 62 | 63 | if cls not in PAD_TEXT: 64 | return "Unsupported" 65 | 66 | return PAD_TEXT[cls] 67 | 68 | 69 | def connect(pad): 70 | mode = get_mode(pad) 71 | 72 | if mode == "Mk3": 73 | return pad.Open(1) 74 | 75 | return pad.Open() 76 | 77 | 78 | def disconnect(pad): 79 | pad.Close() 80 | -------------------------------------------------------------------------------- /window.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.filedialog, tkinter.scrolledtext, tkinter.messagebox, tkcolorpicker 3 | from PIL import ImageTk, Image 4 | import os, sys 5 | from functools import partial 6 | import webbrowser 7 | 8 | import scripts, files, lp_colors, lp_events 9 | from utils import launchpad_connector as lpcon 10 | 11 | BUTTON_SIZE = 40 12 | HS_SIZE = 200 13 | V_WIDTH = 50 14 | STAT_ACTIVE_COLOR = "#080" 15 | STAT_INACTIVE_COLOR = "#444" 16 | SELECT_COLOR = "#f00" 17 | DEFAULT_COLOR = [0, 0, 255] 18 | MK1_DEFAULT_COLOR = [0, 255, 0] 19 | INDICATOR_BPM = 480 20 | BUTTON_FONT = ("helvetica", 11, "bold") 21 | 22 | PATH = None 23 | PROG_PATH = None 24 | USER_PATH = None 25 | 26 | VERSION = None 27 | 28 | PLATFORM = None 29 | 30 | MAIN_ICON = None 31 | 32 | launchpad = None 33 | 34 | root = None 35 | app = None 36 | root_destroyed = None 37 | restart = False 38 | lp_object = None 39 | 40 | 41 | load_layout_filetypes = [('LPHK layout files', [files.LAYOUT_EXT, files.LEGACY_LAYOUT_EXT])] 42 | load_script_filetypes = [('LPHK script files', [files.SCRIPT_EXT, files.LEGACY_SCRIPT_EXT])] 43 | 44 | save_layout_filetypes = [('LPHK layout files', [files.LAYOUT_EXT])] 45 | save_script_filetypes = [('LPHK script files', [files.SCRIPT_EXT])] 46 | 47 | lp_connected = False 48 | lp_mode = None 49 | colors_to_set = [[DEFAULT_COLOR for y in range(9)] for x in range(9)] 50 | 51 | 52 | def init(lp_object_in, launchpad_in, path_in, prog_path_in, user_path_in, version_in, platform_in): 53 | global lp_object 54 | global launchpad 55 | global PATH 56 | global PROG_PATH 57 | global USER_PATH 58 | global VERSION 59 | global PLATFORM 60 | global MAIN_ICON 61 | lp_object = lp_object_in 62 | launchpad = launchpad_in 63 | PATH = path_in 64 | PROG_PATH = prog_path_in 65 | USER_PATH = user_path_in 66 | VERSION = version_in 67 | PLATFORM = platform_in 68 | 69 | if PLATFORM == "windows": 70 | MAIN_ICON = os.path.join(PATH, "resources", "LPHK.ico") 71 | else: 72 | MAIN_ICON = os.path.join(PATH, "resources", "LPHK.gif") 73 | 74 | make() 75 | 76 | 77 | class Main_Window(tk.Frame): 78 | def __init__(self, master=None): 79 | tk.Frame.__init__(self, master) 80 | self.master = master 81 | self.init_window() 82 | 83 | self.about_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/LPHK-banner.png")) 84 | self.info_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/info.png")) 85 | self.warning_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/warning.png")) 86 | self.error_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/error.png")) 87 | self.alert_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/alert.png")) 88 | self.scare_image = ImageTk.PhotoImage(Image.open(PATH + "/resources/scare.png")) 89 | self.grid_drawn = False 90 | self.grid_rects = [[None for y in range(9)] for x in range(9)] 91 | self.button_mode = "edit" 92 | self.last_clicked = None 93 | self.outline_box = None 94 | 95 | def init_window(self): 96 | global root 97 | 98 | self.master.title("LPHK - Novation Launchpad Macro Scripting System") 99 | self.pack(fill="both", expand=1) 100 | 101 | self.m = tk.Menu(self.master) 102 | self.master.config(menu=self.m) 103 | 104 | self.m_Launchpad = tk.Menu(self.m, tearoff=False) 105 | self.m_Launchpad.add_command(label="Redetect (Restart)", command=self.redetect_lp) 106 | self.m.add_cascade(label="Launchpad", menu=self.m_Launchpad) 107 | 108 | self.m_Layout = tk.Menu(self.m, tearoff=False) 109 | self.m_Layout.add_command(label="New Layout", command=self.unbind_lp) 110 | self.m_Layout.add_command(label="Load Layout", command=self.load_layout) 111 | self.m_Layout.add_command(label="Save Layout", command=self.save_layout) 112 | self.m_Layout.add_command(label="Save Layout As...", command=self.save_layout_as) 113 | self.m.add_cascade(label="Layout", menu=self.m_Layout) 114 | 115 | self.disable_menu("Layout") 116 | 117 | self.m_Help = tk.Menu(self.m, tearoff=False) 118 | open_readme = lambda: webbrowser.open("https://github.com/nimaid/LPHK#lphk-launchpad-hotkey") 119 | self.m_Help.add_command(label="Open README...", command=open_readme) 120 | open_scripting = lambda: webbrowser.open("https://github.com/nimaid/LPHK#what-is-lphkscript-table-of-contents") 121 | self.m_Help.add_command(label="Scripting Help...", command=open_scripting) 122 | open_user_folder = lambda: files.open_file_folder(USER_PATH) 123 | self.m_Help.add_command(label="User Folder...", command=open_user_folder) 124 | open_prog_folder = lambda: files.open_file_folder(PROG_PATH) 125 | self.m_Help.add_command(label="Program Folder...", command=open_prog_folder) 126 | display_info = lambda: self.popup(self, "About LPHK", self.about_image, "A Novation Launchpad Macro Scripting System\nMade by Ella Jameson (nimaid)\n\nVersion: " + VERSION + "\nFile format version: " + files.FILE_VERSION, "Done") 127 | self.m_Help.add_command(label="About LPHK", command=display_info) 128 | self.m.add_cascade(label="Help", menu=self.m_Help) 129 | 130 | c_gap = int(BUTTON_SIZE // 4) 131 | 132 | c_size = (BUTTON_SIZE * 9) + (c_gap * 9) 133 | self.c = tk.Canvas(self, width=c_size, height=c_size) 134 | self.c.bind("", self.click) 135 | self.c.grid(row=0, column=0, padx=round(c_gap/2), pady=round(c_gap/2)) 136 | 137 | self.stat = tk.Label(self, text="No Launchpad Connected", bg=STAT_INACTIVE_COLOR, fg="#fff") 138 | self.stat.grid(row=1, column=0, sticky=tk.EW) 139 | self.stat.config(font=("Courier", BUTTON_SIZE // 3, "bold")) 140 | 141 | def raise_above_all(self): 142 | self.master.attributes('-topmost', 1) 143 | self.master.attributes('-topmost', 0) 144 | 145 | def enable_menu(self, name): 146 | self.m.entryconfig(name, state="normal") 147 | 148 | def disable_menu(self, name): 149 | self.m.entryconfig(name, state="disabled") 150 | 151 | def connect_dummy(self): 152 | # WIP 153 | global lp_connected 154 | global lp_mode 155 | global lp_object 156 | 157 | lp_connected = True 158 | lp_mode = "Dummy" 159 | self.draw_canvas() 160 | self.enable_menu("Layout") 161 | 162 | def connect_lp(self): 163 | global lp_connected 164 | global lp_mode 165 | global lp_object 166 | 167 | lp = lpcon.get_launchpad() 168 | 169 | if lp == -1: 170 | self.popup(self, "Connect to Unsupported Device", self.error_image, 171 | """The device you are attempting to use is not currently supported by LPHK, 172 | and there are no plans to add support for it. 173 | Please voice your feature requests on the Discord or on GitHub.""", 174 | "OK") 175 | 176 | if lp is None: 177 | self.popup_choice(self, "No Launchpad Detected...", self.error_image, 178 | """Could not detect any connected Launchpads! 179 | Disconnect and reconnect your USB cable, 180 | then click 'Redetect Now'.""", 181 | [["Ignore", None], ["Redetect Now", self.redetect_lp]] 182 | ) 183 | return 184 | 185 | if lpcon.connect(lp): 186 | lp_connected = True 187 | lp_object = lp 188 | lp_mode = lpcon.get_mode(lp) 189 | 190 | if lp_mode == "Pro": 191 | self.popup(self, "Connect to Launchpad Pro", self.error_image, 192 | """This is a BETA feature! The Pro is not fully supported yet,as the bottom and left rows are not mappable currently. 193 | I (nimaid) do not have a Launchpad Pro to test with, so let me know if this does or does not work on the Discord! (https://discord.gg/mDCzB8X) 194 | You must first put your Launchpad Pro in Live (Session) mode. To do this, press and holde the 'Setup' key, press the green pad in the 195 | upper left corner, then release the 'Setup' key. Please only continue once this step is completed.""", 196 | "I am in Live mode.") 197 | 198 | lp_object.ButtonFlush() 199 | 200 | # special case? 201 | if lp_mode != "Mk1": 202 | lp_object.LedCtrlBpm(INDICATOR_BPM) 203 | 204 | lp_events.start(lp_object) 205 | self.draw_canvas() 206 | self.enable_menu("Layout") 207 | self.stat["text"] = f"Connected to {lpcon.get_display_name(lp)}" 208 | self.stat["bg"] = STAT_ACTIVE_COLOR 209 | # search for a startup layout and load it 210 | if os.path.isfile(os.path.join(files.LAYOUT_PATH, "startup.lpl")): 211 | files.load_layout_to_lp(os.path.join(files.LAYOUT_PATH, "startup.lpl")) 212 | 213 | def disconnect_lp(self): 214 | global lp_connected 215 | try: 216 | scripts.unbind_all() 217 | lp_events.timer.cancel() 218 | lpcon.disconnect(lp_object) 219 | except: 220 | self.redetect_lp() 221 | lp_connected = False 222 | 223 | self.clear_canvas() 224 | 225 | self.disable_menu("Layout") 226 | 227 | self.stat["text"] = "No Launchpad Connected" 228 | self.stat["bg"] = STAT_INACTIVE_COLOR 229 | 230 | def redetect_lp(self): 231 | global restart 232 | restart = True 233 | close() 234 | 235 | def unbind_lp(self, prompt_save=True): 236 | if prompt_save: 237 | self.modified_layout_save_prompt() 238 | scripts.unbind_all() 239 | files.curr_layout = None 240 | self.draw_canvas() 241 | 242 | def load_layout(self): 243 | self.modified_layout_save_prompt() 244 | name = tk.filedialog.askopenfilename(parent=app, 245 | initialdir=files.LAYOUT_PATH, 246 | title="Load layout", 247 | filetypes=load_layout_filetypes) 248 | if name: 249 | files.load_layout_to_lp(name) 250 | 251 | def save_layout_as(self): 252 | name = tk.filedialog.asksaveasfilename(parent=app, 253 | initialdir=files.LAYOUT_PATH, 254 | title="Save layout as...", 255 | filetypes=save_layout_filetypes) 256 | if name: 257 | if files.LAYOUT_EXT not in name: 258 | name += files.LAYOUT_EXT 259 | files.save_lp_to_layout(name) 260 | files.load_layout_to_lp(name) 261 | 262 | def save_layout(self): 263 | if files.curr_layout == None: 264 | self.save_layout_as() 265 | else: 266 | files.save_lp_to_layout(files.curr_layout) 267 | files.load_layout_to_lp(files.curr_layout) 268 | 269 | def click(self, event): 270 | gap = int(BUTTON_SIZE // 4) 271 | 272 | 273 | column = min(8, int(event.x // (BUTTON_SIZE + gap))) 274 | row = min(8, int(event.y // (BUTTON_SIZE + gap))) 275 | 276 | if self.grid_drawn: 277 | if(column, row) == (8, 0): 278 | #mode change 279 | self.last_clicked = None 280 | if self.button_mode == "edit": 281 | self.button_mode = "move" 282 | elif self.button_mode == "move": 283 | self.button_mode = "swap" 284 | elif self.button_mode == "swap": 285 | self.button_mode = "copy" 286 | else: 287 | self.button_mode = "edit" 288 | self.draw_canvas() 289 | else: 290 | if self.button_mode == "edit": 291 | self.last_clicked = (column, row) 292 | self.draw_canvas() 293 | self.script_entry_window(column, row) 294 | self.last_clicked = None 295 | else: 296 | if self.last_clicked == None: 297 | self.last_clicked = (column, row) 298 | else: 299 | move_func = partial(scripts.move, self.last_clicked[0], self.last_clicked[1], column, row) 300 | swap_func = partial(scripts.swap, self.last_clicked[0], self.last_clicked[1], column, row) 301 | copy_func = partial(scripts.copy, self.last_clicked[0], self.last_clicked[1], column, row) 302 | 303 | if self.button_mode == "move": 304 | if scripts.is_bound(column, row) and ((self.last_clicked) != (column, row)): 305 | self.popup_choice(self, "Button Already Bound", self.warning_image, "You are attempting to move a button to an already\nbound button. What would you like to do?", [["Overwrite", move_func], ["Swap", swap_func], ["Cancel", None]]) 306 | else: 307 | move_func() 308 | elif self.button_mode == "copy": 309 | if scripts.is_bound(column, row) and ((self.last_clicked) != (column, row)): 310 | self.popup_choice(self, "Button Already Bound", self.warning_image, "You are attempting to copy a button to an already\nbound button. What would you like to do?", [["Overwrite", copy_func], ["Swap", swap_func], ["Cancel", None]]) 311 | else: 312 | copy_func() 313 | elif self.button_mode == "swap": 314 | swap_func() 315 | self.last_clicked = None 316 | self.draw_canvas() 317 | 318 | def draw_button(self, column, row, color="#000000", shape="square"): 319 | gap = int(BUTTON_SIZE // 4) 320 | 321 | x_start = round((BUTTON_SIZE * column) + (gap * column) + (gap / 2)) 322 | y_start = round((BUTTON_SIZE * row) + (gap * row) + (gap / 2)) 323 | x_end = x_start + BUTTON_SIZE 324 | y_end = y_start + BUTTON_SIZE 325 | 326 | if shape == "square": 327 | return self.c.create_rectangle(x_start, y_start, x_end, y_end, fill=color, outline="") 328 | elif shape == "circle": 329 | shrink = BUTTON_SIZE / 10 330 | return self.c.create_oval(x_start + shrink, y_start + shrink, x_end - shrink, y_end - shrink, fill=color, outline="") 331 | 332 | def draw_canvas(self): 333 | if self.last_clicked != None: 334 | if self.outline_box == None: 335 | gap = int(BUTTON_SIZE // 4) 336 | 337 | x_start = round((BUTTON_SIZE * self.last_clicked[0]) + (gap * self.last_clicked[0])) 338 | y_start = round((BUTTON_SIZE * self.last_clicked[1]) + (gap * self.last_clicked[1])) 339 | x_end = round(x_start + BUTTON_SIZE + gap) 340 | y_end = round(y_start + BUTTON_SIZE + gap) 341 | 342 | if (self.last_clicked[1] == 0) or (self.last_clicked[0] == 8): 343 | self.outline_box = self.c.create_oval(x_start + (gap // 2), y_start + (gap // 2), x_end - (gap // 2), y_end - (gap // 2), fill=SELECT_COLOR, outline="") 344 | else: 345 | self.outline_box = self.c.create_rectangle(x_start, y_start, x_end, y_end, fill=SELECT_COLOR, outline="") 346 | self.c.tag_lower(self.outline_box) 347 | else: 348 | if self.outline_box != None: 349 | self.c.delete(self.outline_box) 350 | self.outline_box = None 351 | 352 | if self.grid_drawn: 353 | for x in range(8): 354 | y = 0 355 | self.c.itemconfig(self.grid_rects[x][y], fill=lp_colors.getXY_RGB(x, y)) 356 | 357 | for y in range(1, 9): 358 | x = 8 359 | self.c.itemconfig(self.grid_rects[x][y], fill=lp_colors.getXY_RGB(x, y)) 360 | 361 | for x in range(8): 362 | for y in range(1, 9): 363 | self.c.itemconfig(self.grid_rects[x][y], fill=lp_colors.getXY_RGB(x, y)) 364 | 365 | self.c.itemconfig(self.grid_rects[8][0], text=self.button_mode.capitalize()) 366 | else: 367 | for x in range(8): 368 | y = 0 369 | self.grid_rects[x][y] = self.draw_button(x, y, color=lp_colors.getXY_RGB(x, y), shape="circle") 370 | 371 | for y in range(1, 9): 372 | x = 8 373 | self.grid_rects[x][y] = self.draw_button(x, y, color=lp_colors.getXY_RGB(x, y), shape="circle") 374 | 375 | for x in range(8): 376 | for y in range(1, 9): 377 | self.grid_rects[x][y] = self.draw_button(x, y, color=lp_colors.getXY_RGB(x, y)) 378 | 379 | gap = int(BUTTON_SIZE // 4) 380 | text_x = round((BUTTON_SIZE * 8) + (gap * 8) + (BUTTON_SIZE / 2) + (gap / 2)) 381 | text_y = round((BUTTON_SIZE / 2) + (gap / 2)) 382 | self.grid_rects[8][0] = self.c.create_text(text_x, text_y, text=self.button_mode.capitalize(), font=("Courier", BUTTON_SIZE // 3, "bold")) 383 | 384 | self.grid_drawn = True 385 | 386 | def clear_canvas(self): 387 | self.c.delete("all") 388 | self.grid_rects = [[None for y in range(9)] for x in range(9)] 389 | self.grid_drawn = False 390 | 391 | def script_entry_window(self, x, y, text_override=None, color_override=None): 392 | global color_to_set 393 | 394 | w = tk.Toplevel(self) 395 | w.winfo_toplevel().title("Editing Script for Button (" + str(x) + ", " + str(y) + ")") 396 | w.resizable(False, False) 397 | 398 | if MAIN_ICON != None: 399 | if os.path.splitext(MAIN_ICON)[1].lower() == ".gif": 400 | dummy = None 401 | #w.call('wm', 'iconphoto', w._w, tk.PhotoImage(file=MAIN_ICON)) 402 | else: 403 | w.iconbitmap(MAIN_ICON) 404 | 405 | def validate_func(): 406 | nonlocal x, y, t 407 | 408 | text_string = t.get(1.0, tk.END) 409 | try: 410 | script_validate = scripts.validate_script(text_string) 411 | except: 412 | #self.save_script(w, x, y, text_string) # This will fail and throw a popup error 413 | self.popup(w, "Script Validation Error", self.error_image, "Fatal error while attempting to validate script.\nPlease see LPHK.log for more information.", "OK") 414 | raise 415 | if script_validate != True and files.in_error: 416 | self.save_script(w, x, y, text_string) 417 | else: 418 | w.destroy() 419 | w.protocol("WM_DELETE_WINDOW", validate_func) 420 | 421 | e_m = tk.Menu(w) 422 | w.config(menu=e_m) 423 | 424 | e_m_Script = tk.Menu(e_m, tearoff=False) 425 | 426 | t = tk.scrolledtext.ScrolledText(w) 427 | t.grid(column=0, row=0, rowspan=3, padx=10, pady=10) 428 | 429 | if text_override == None: 430 | t.insert(tk.INSERT, scripts.text[x][y]) 431 | else: 432 | t.insert(tk.INSERT, text_override) 433 | t.bind("<>", self.custom_paste) 434 | t.bind("", self.select_all) 435 | 436 | import_script_func = lambda: self.import_script(t, w) 437 | e_m_Script.add_command(label="Import script", command=import_script_func) 438 | export_script_func = lambda: self.export_script(t, w) 439 | e_m_Script.add_command(label="Export script", command=export_script_func) 440 | e_m.add_cascade(label="Script", menu=e_m_Script) 441 | 442 | if color_override == None: 443 | colors_to_set[x][y] = lp_colors.getXY(x, y) 444 | else: 445 | colors_to_set[x][y] = color_override 446 | 447 | if type(colors_to_set[x][y]) == int: 448 | colors_to_set[x][y] = lp_colors.code_to_RGB(colors_to_set[x][y]) 449 | 450 | if all(c < 4 for c in colors_to_set[x][y]): 451 | if lp_mode == "Mk1": 452 | colors_to_set[x][y] = MK1_DEFAULT_COLOR 453 | else: 454 | colors_to_set[x][y] = DEFAULT_COLOR 455 | 456 | ask_color_func = lambda: self.ask_color(w, color_button, x, y, colors_to_set[x][y]) 457 | color_button = tk.Button(w, text="Select Color", command=ask_color_func) 458 | color_button.grid(column=1, row=0, padx=(0, 10), pady=(10, 50), sticky="nesw") 459 | color_button.config(font=BUTTON_FONT) 460 | start_color_str = lp_colors.list_RGB_to_string(colors_to_set[x][y]) 461 | self.button_color_with_text_update(color_button, start_color_str) 462 | 463 | save_script_func = lambda: self.save_script(w, x, y, t.get(1.0, tk.END)) 464 | save_button = tk.Button(w, text="Bind Button (" + str(x) + ", " + str(y) + ")", command=save_script_func) 465 | save_button.grid(column=1, row=1, padx=(0,10), sticky="nesw") 466 | save_button.config(font=BUTTON_FONT) 467 | save_button.config(bg="#c3d9C3") 468 | 469 | unbind_func = lambda: self.unbind_destroy(x, y, w) 470 | unbind_button = tk.Button(w, text="Unbind Button (" + str(x) + ", " + str(y) + ")", command=unbind_func) 471 | unbind_button.grid(column=1, row=2, padx=(0,10), pady=10, sticky="nesw") 472 | unbind_button.config(font=BUTTON_FONT) 473 | unbind_button.config(bg="#d9c3c3") 474 | 475 | w.wait_visibility() 476 | w.grab_set() 477 | t.focus_set() 478 | w.wait_window() 479 | 480 | def classic_askcolor(self, color=(255, 0, 0), title="Color Chooser"): 481 | w = tk.Toplevel(self) 482 | w.winfo_toplevel().title(title) 483 | w.resizable(False, False) 484 | if MAIN_ICON != None: 485 | if os.path.splitext(MAIN_ICON)[1].lower() == ".gif": 486 | dummy = None 487 | #w.call('wm', 'iconphoto', popup._w, tk.PhotoImage(file=MAIN_ICON)) 488 | else: 489 | w.iconbitmap(MAIN_ICON) 490 | 491 | w.protocol("WM_DELETE_WINDOW", w.destroy) 492 | 493 | color = "" 494 | 495 | def return_color(col): 496 | nonlocal color 497 | color = col 498 | w.destroy() 499 | 500 | button_frame = tk.Frame(w) 501 | button_frame.grid(padx=(10, 0), pady=(10, 0)) 502 | 503 | def make_grid_button(column, row, color_hex, func=None, size=100): 504 | nonlocal w 505 | f = tk.Frame(button_frame, width=size, height=size) 506 | 507 | b = tk.Button(f, command=func) 508 | 509 | f.rowconfigure(0, weight = 1) 510 | f.columnconfigure(0, weight = 1) 511 | f.grid_propagate(0) 512 | 513 | f.grid(column=column, row=row) 514 | b.grid(padx=(0,10), pady=(0,10), sticky="nesw") 515 | b.config(bg=color_hex) 516 | 517 | def make_color_button(button_color, column, row, size=100): 518 | button_color_hex = "#%02x%02x%02x" % button_color 519 | 520 | b_func = lambda: return_color(button_color) 521 | make_grid_button(column, row, button_color_hex, b_func, size) 522 | 523 | for c in range(4): 524 | for r in range(4): 525 | if not (c == 0 and r == 3): 526 | red = int(c * (255 / 3)) 527 | green = int((3 - r) * (255 / 3)) 528 | 529 | make_color_button((red, green, 0), c, r, size=75) 530 | 531 | w.wait_visibility() 532 | w.grab_set() 533 | w.wait_window() 534 | 535 | if color: 536 | hex = "#%02x%02x%02x" % color 537 | return color, hex 538 | else: 539 | return None, None 540 | 541 | def ask_color(self, window, button, x, y, default_color): 542 | global colors_to_set 543 | 544 | if lp_mode == "Mk1": 545 | color = self.classic_askcolor(color=tuple(default_color), title="Select Color for Button (" + str(x) + ", " + str(y) + ")") 546 | else: 547 | color = tkcolorpicker.askcolor(color=tuple(default_color), parent=window, title="Select Color for Button (" + str(x) + ", " + str(y) + ")") 548 | if color[0] != None: 549 | color_to_set = [int(min(255, max(0, c))) for c in color[0]] 550 | if all(c < 4 for c in color_to_set): 551 | rerun = lambda: self.ask_color(window, button, x, y, default_color) 552 | self.popup(window, "Invalid Color", self.warning_image, "That color is too dark to see.", "OK", rerun) 553 | else: 554 | colors_to_set[x][y] = color_to_set 555 | self.button_color_with_text_update(button, color[1]) 556 | 557 | def button_color_with_text_update(self, button, color): 558 | button.configure(bg=color, activebackground=color) 559 | color_rgb = [] 560 | for c in range(3): 561 | start_index = c * 2 562 | val = color[start_index + 1:start_index + 3] 563 | color_rgb.append(int(val, 16)) 564 | luminance = lp_colors.luminance(color_rgb[0], color_rgb[1], color_rgb[2]) 565 | if luminance > 0.5: 566 | button.configure(fg="black", activeforeground="black") 567 | else: 568 | button.configure(fg="white", activeforeground="white") 569 | 570 | def custom_paste(self, event): 571 | try: 572 | event.widget.delete("sel.first", "sel.last") 573 | except: 574 | pass 575 | event.widget.insert("insert", event.widget.clipboard_get()) 576 | return "break" 577 | 578 | def select_all(self, event): 579 | event.widget.tag_add(tk.SEL, "1.0", tk.END) 580 | event.widget.mark_set(tk.INSERT, "1.0") 581 | event.widget.see(tk.INSERT) 582 | return "break" 583 | 584 | def unbind_destroy(self, x, y, window): 585 | scripts.unbind(x, y) 586 | self.draw_canvas() 587 | window.destroy() 588 | 589 | def save_script(self, window, x, y, script_text, open_editor = False, color=None): 590 | global colors_to_set 591 | 592 | script_text = script_text.strip() 593 | 594 | def open_editor_func(): 595 | nonlocal x, y 596 | if open_editor: 597 | self.script_entry_window(x, y, script_text, color) 598 | try: 599 | script_validate = scripts.validate_script(script_text) 600 | except: 601 | self.popup(window, "Script Validation Error", self.error_image, "Fatal error while attempting to validate script.\nPlease see LPHK.log for more information.", "OK", end_command = open_editor_func) 602 | raise 603 | if script_validate == True: 604 | if script_text != "": 605 | script_text = files.strip_lines(script_text) 606 | scripts.bind(x, y, script_text, colors_to_set[x][y]) 607 | self.draw_canvas() 608 | lp_colors.updateXY(x, y) 609 | window.destroy() 610 | else: 611 | self.popup(window, "No Script Entered", self.info_image, "Please enter a script to bind.", "OK", end_command = open_editor_func) 612 | else: 613 | self.popup(window, "(" + str(x) + ", " + str(y) + ") Syntax Error", self.error_image, "Error in line: " + script_validate[1] + "\n" + script_validate[0], "OK", end_command = open_editor_func) 614 | 615 | def import_script(self, textbox, window): 616 | name = tk.filedialog.askopenfilename(parent=window, 617 | initialdir=files.SCRIPT_PATH, 618 | title="Import script", 619 | filetypes=load_script_filetypes) 620 | if name: 621 | text = files.import_script(name) 622 | text = files.strip_lines(text) 623 | textbox.delete("1.0", tk.END) 624 | textbox.insert(tk.INSERT, text) 625 | 626 | def export_script(self, textbox, window): 627 | name = tk.filedialog.asksaveasfilename(parent=window, 628 | initialdir=files.SCRIPT_PATH, 629 | title="Export script", 630 | filetypes=save_script_filetypes) 631 | if name: 632 | if files.SCRIPT_EXT not in name: 633 | name += files.SCRIPT_EXT 634 | text = textbox.get("1.0", tk.END) 635 | text = files.strip_lines(text) 636 | files.export_script(name, text) 637 | 638 | def popup(self, window, title, image, text, button_text, end_command=None): 639 | popup = tk.Toplevel(window) 640 | popup.resizable(False, False) 641 | if MAIN_ICON != None: 642 | if os.path.splitext(MAIN_ICON)[1].lower() == ".gif": 643 | dummy = None 644 | #popup.call('wm', 'iconphoto', popup._w, tk.PhotoImage(file=MAIN_ICON)) 645 | else: 646 | popup.iconbitmap(MAIN_ICON) 647 | popup.wm_title(title) 648 | popup.tkraise(window) 649 | 650 | def run_end(): 651 | popup.destroy() 652 | if end_command != None: 653 | end_command() 654 | 655 | picture_label = tk.Label(popup, image=image) 656 | picture_label.photo = image 657 | picture_label.grid(column=0, row=0, rowspan=2, padx=10, pady=10) 658 | tk.Label(popup, text=text, justify=tk.CENTER).grid(column=1, row=0, padx=10, pady=10) 659 | tk.Button(popup, text=button_text, command=run_end).grid(column=1, row=1, padx=10, pady=10) 660 | popup.wait_visibility() 661 | popup.grab_set() 662 | popup.wait_window() 663 | 664 | def popup_choice(self, window, title, image, text, choices): 665 | popup = tk.Toplevel(window) 666 | popup.resizable(False, False) 667 | if MAIN_ICON != None: 668 | if os.path.splitext(MAIN_ICON)[1].lower() == ".gif": 669 | dummy = None 670 | #popup.call('wm', 'iconphoto', popup._w, tk.PhotoImage(file=MAIN_ICON)) 671 | else: 672 | popup.iconbitmap(MAIN_ICON) 673 | popup.wm_title(title) 674 | popup.tkraise(window) 675 | 676 | def run_end(func): 677 | popup.destroy() 678 | if func != None: 679 | func() 680 | 681 | picture_label = tk.Label(popup, image=image) 682 | picture_label.photo = image 683 | picture_label.grid(column=0, row=0, rowspan=2, padx=10, pady=10) 684 | tk.Label(popup, text=text, justify=tk.CENTER).grid(column=1, row=0, columnspan=len(choices), padx=10, pady=10) 685 | for idx, choice in enumerate(choices): 686 | run_end_func = partial(run_end, choice[1]) 687 | tk.Button(popup, text=choice[0], command=run_end_func).grid(column=1 + idx, row=1, padx=10, pady=10) 688 | popup.wait_visibility() 689 | popup.grab_set() 690 | popup.wait_window() 691 | 692 | def modified_layout_save_prompt(self): 693 | if files.layout_changed_since_load == True: 694 | layout_empty = True 695 | for x_texts in scripts.text: 696 | for text in x_texts: 697 | if text != "": 698 | layout_empty = False 699 | break 700 | 701 | if not layout_empty: 702 | self.popup_choice(self, "Save Changes?", self.warning_image, "You have made changes to this layout.\nWould you like to save this layout before exiting?", [["Save", self.save_layout], ["Save As...", self.save_layout_as], ["Discard", None]]) 703 | 704 | def make(): 705 | global root 706 | global app 707 | global root_destroyed 708 | global redetect_before_start 709 | root = tk.Tk() 710 | root_destroyed = False 711 | root.protocol("WM_DELETE_WINDOW", close) 712 | root.resizable(False, False) 713 | if MAIN_ICON != None: 714 | if os.path.splitext(MAIN_ICON)[1].lower() == ".gif": 715 | root.call('wm', 'iconphoto', root._w, tk.PhotoImage(file=MAIN_ICON)) 716 | else: 717 | root.iconbitmap(MAIN_ICON) 718 | app = Main_Window(root) 719 | app.raise_above_all() 720 | app.after(100, app.connect_lp) 721 | app.mainloop() 722 | 723 | 724 | def close(): 725 | global root_destroyed, launchpad 726 | app.modified_layout_save_prompt() 727 | app.disconnect_lp() 728 | 729 | if not root_destroyed: 730 | root.destroy() 731 | root_destroyed = True 732 | --------------------------------------------------------------------------------