Skip to content

Commit

Permalink
Various improvements
Browse files Browse the repository at this point in the history
* build.bash
** Add Win32 vs Linux build handling
** Improve GCC command
** Add --clean and --release args

* config.c
** Add logging for each parsed entry

* New: console.h/c
** Attempt(!) to redirect stdout/err/in to a Win32 console

* error_exit.h/c
** Big upgrade(!) to use elipsis

* New: log.h/c

* main.c
** Add a logged counter of number of times a specific shortcut key is hit

* New: min_max.h/c

General:
* Convert all "\r\n" to "\n"
* Add __attribute__((unused)) for all unused local vars and params
* Replace win32_winver.h with win32.h
* Enable and fix many warnings from GCC
  • Loading branch information
kevinarpe committed Mar 10, 2022
1 parent b5ce4b1 commit 781555e
Show file tree
Hide file tree
Showing 20 changed files with 701 additions and 216 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ hello-world/
task_switcher/

send_input/release/send_input-*
send_input/obsolete

144 changes: 139 additions & 5 deletions send_input/build.bash
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,101 @@ set -o pipefail
# Print commands and their arguments as they are executed.
# set -x

TRUE=0
FALSE=1

ARG_CLEAN='--clean'
ARG_RELEASE='--release'

EXECUTABLE='send_input.exe'

GCC_PREFIX='x86_64-w64-mingw32-'

# Ex: 'x86_64-w64-mingw32-gcc'
GCC="$GCC_PREFIX"'gcc'

# Ex: 'x86_64-w64-mingw32-strip'
STRIP="$GCC_PREFIX"'strip'

is_linux()
{
local uname
uname="$(uname)"
[ "Linux" = "$uname" ]
}

echo_exit_status()
{
local exit_status
exit_status="$("$@")"
printf -- '%d' $?
}

IS_LINUX="$(echo_exit_status is_linux)"

main()
{
local this_script_abs_dir_path
this_script_abs_dir_path="$(dirname "$0")"
this_script_abs_dir_path="$(dirname "$(readlink --canonicalize "$0")")"

# Intentional: By default, build debug executable.
local is_release=$FALSE
local is_clean=$FALSE

local arg
for arg in "$@"
do
if [ "-h" = "$arg" ] || [ "--help" = "$arg" ]
then
show_help_then_exit

elif [ "$ARG_CLEAN" = "$arg" ]
then
if [ $FALSE = $is_clean ]
then
is_clean=$TRUE
else
printf -- '\nError: Found multiple %s arguments\n' "$ARG_CLEAN"
show_help_then_exit
fi

elif [ "$ARG_RELEASE" = "$arg" ]
then
if [ $FALSE = $is_release ]
then
is_release=$TRUE
else
printf -- '\nError: Found multiple %s arguments\n' "$ARG_RELEASE"
show_help_then_exit
fi
else
printf -- '\nError: Unknown argument: [%s]\n' "$arg"
show_help_then_exit
fi
done

if [ $TRUE = $is_release ]
then
printf -- '\nRelease build\n'
else
printf -- '\nDebug build\n'
fi

echo_and_run_cmd \
cd "$this_script_abs_dir_path"

if [ $TRUE = $is_clean ]
then
echo_and_run_cmd \
rm -f *.o
fi

echo_and_run_gcc_cmd_if_necessary \
win32.c win32.o

echo_and_run_gcc_cmd_if_necessary \
log.c log.o

echo_and_run_gcc_cmd_if_necessary \
error_exit.c error_exit.o

Expand All @@ -31,15 +118,30 @@ main()
echo_and_run_gcc_cmd_if_necessary \
config.c config.o

echo_and_run_gcc_cmd_if_necessary \
min_max.c min_max.o

echo_and_run_gcc_cmd_if_necessary \
console.c console.o

echo_and_run_gcc_cmd_if_necessary \
main.c main.o

echo_and_run_gcc_cmd \
-o send_input.exe error_exit.o win32_xmalloc.o wstr.o config.o main.o -lgdi32
-o send_input.exe win32.o log.o error_exit.o win32_xmalloc.o wstr.o config.o min_max.o console.o main.o -lgdi32

echo_and_run_cmd \
ls -l send_input.exe

if [ $TRUE = $is_release ]
then
echo_and_run_cmd \
"$STRIP" --verbose send_input.exe

echo_and_run_cmd \
ls -l send_input.exe
fi

echo_and_run_cmd \
cd -

Expand All @@ -49,6 +151,28 @@ main()
printf -- '\n'
}

show_help_then_exit()
{
printf -- '\n'
printf -- 'Usage: %s [%s] [%s] [-h|--help]\n' "$0" "$ARG_CLEAN" "$ARG_RELEASE"
printf -- 'Build executable (debug or release): %s\n' "$EXECUTABLE"
printf -- '\n'
printf -- 'Required Arguments:\n'
printf -- ' None\n'
printf -- '\n'
printf -- 'Optional Arguments:\n'
printf -- ' %s: Remove object files to force full rebuild\n' "$ARG_CLEAN"
printf -- '\n'
printf -- ' %s: Strip final executable\n' "$ARG_RELEASE"
printf -- '\n'
printf -- ' -h or --help: Show this help\n'
printf -- '\n'
printf -- 'Compatibility Notes:\n'
printf -- ' This Bash shell script is compatible with Linux, Cygwin, and MSYS2\n'
printf -- '\n'
exit 1
}

echo_cmd()
{
echo
Expand All @@ -63,10 +187,18 @@ echo_and_run_cmd()

echo_and_run_gcc_cmd()
{
local arg_arr=()
if [ $TRUE = $IS_LINUX ]
then
# Normally, 'winegcc' defines this auto-magically.
arg_arr+=(-D__WINE__)
fi
# Ref: https://stackoverflow.com/a/62488988/257299
echo_and_run_cmd \
x86_64-w64-mingw32-gcc \
"$GCC" \
"${arg_arr[@]}" \
-g3 -ggdb3 \
-Wall -Wextra -Wshadow \
-Werror \
-Wl,-subsystem,windows \
-municode \
Expand All @@ -77,17 +209,19 @@ echo_and_run_gcc_cmd_if_necessary()
{
# Ex: 'error_exit.c'
local input_file_path="$1"; shift

# Ex: 'error_exit.o'
local output_file_path="$1"; shift

# Remaining args stored in "$@"

# $ help test -> "FILE1 -nt FILE2: True if file1 is newer than file2 (according to modification date)."
if [ "$input_file_path" -nt "$output_file_path" ]
then
echo_and_run_gcc_cmd \
-c -o "$output_file_path" "$input_file_path" "$@"
# Remaining args stored in "$@"
else
printf -- '\n%s: Not updated\n' "$input_file_path"
printf -- '\n%s: Not updated (do not re-compile)\n' "$input_file_path"
fi
}

Expand Down
86 changes: 31 additions & 55 deletions send_input/config.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#include "config.h"
#include "error_exit.h"
#include "xmalloc.h"
#include "log.h"
#include <assert.h> // required for assert()
#include <windows.h>
#include <stdio.h>

static void ConfigEntryDynArr_AssertValid(_In_ const struct ConfigEntryDynArr *lpDynArr)
{
assert(NULL != lpDynArr);
assert(lpDynArr->ulCapacity >= 0);
// Intentional: If ulCapacity is more that *half* of SIZE_MAX, there is probably an unsigned wrap bug.
assert(lpDynArr->ulCapacity <= SIZE_MAX / 2U);
assert(lpDynArr->ulSize <= lpDynArr->ulCapacity);
}

Expand Down Expand Up @@ -47,7 +48,7 @@ static void ConfigAssertValid(_In_ const struct ConfigEntryDynArr *lpDynArr)
{
if (0 == lpDynArr->ulSize)
{
ErrorExit(L"Zero config entries found!");
ErrorExit("Zero config entries found!");
}

for (size_t i = 0; i < lpDynArr->ulSize; ++i)
Expand All @@ -59,10 +60,7 @@ static void ConfigAssertValid(_In_ const struct ConfigEntryDynArr *lpDynArr)
if (lpEntry->shortcutKey.eModifiers == lpEntry2->shortcutKey.eModifiers
&& lpEntry->shortcutKey.dwVkCode == lpEntry2->shortcutKey.dwVkCode)
{
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config entries #%zd and #%zd have the same shortcut key\r\n", (1 + i), (1 + j));

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config entries #%zd and #%zd have the same shortcut key\n", (1 + i), (1 + j));
}
}
}
Expand Down Expand Up @@ -122,13 +120,9 @@ void ConfigParseLine(_In_ const size_t ulLineIndex,

if (1 == tokenWStrArr.ulSize)
{
// Ref: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-s-snprintf-s-l-snwprintf-s-snwprintf-s-l
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Failed to find delim [%ls]\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpDelimWCharArr, lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Failed to find delim [%ls]\n"
"Line: %ls\n",
(1 + ulLineIndex), lpDelimWCharArr, lpLineWStr->lpWCharArr);
}

// Ex: L"Ctrl+Shift+Alt+0x70|username" -> L"Ctrl+Shift+Alt+0x70"
Expand All @@ -143,21 +137,15 @@ void ConfigParseLine(_In_ const size_t ulLineIndex,

if (0 == lpShortcutKeyWStr->ulSize)
{
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Left side shortcut key is empty\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Left side shortcut key is empty\n"
"Line: %ls\n",
(1 + ulLineIndex), lpLineWStr->lpWCharArr);
}
else if (0 == lpSendKeysWStr->ulSize)
{
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Right side send keys text is empty\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Right side send keys text is empty\n"
"Line: %ls\n",
(1 + ulLineIndex), lpLineWStr->lpWCharArr);
}

ConfigParseShortcutKey(lpShortcutKeyWStr, ulLineIndex, lpLineWStr, &(lpConfigEntry->shortcutKey));
Expand All @@ -168,6 +156,8 @@ void ConfigParseLine(_In_ const size_t ulLineIndex,
WStrCopyWStr(&(lpConfigEntry->sendKeysWStr), lpSendKeysWStr);

WStrArrFree(&tokenWStrArr);

LogF(stdout, "Parsed config line #%d: [%ls]", (1 + ulLineIndex), lpLineWStr->lpWCharArr);
}

void ConfigParseShortcutKey(_In_ const struct WStr *lpShortcutKeyWStr, // Ex: L"Ctrl+Shift+Alt+0x70"
Expand Down Expand Up @@ -210,24 +200,17 @@ void ConfigParseShortcutKey(_In_ const struct WStr *lpShortcutKeyWStr, // Ex:
// Note: Prefix '0x' and '0X' are automatically ignored. Also, hex chars may be upper or lowercase.
if (iFieldCount != swscanf(lpVkCodeWStr->lpWCharArr, L"%x", &dwVkCode))
{
// Ref: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-s-snprintf-s-l-snwprintf-s-snwprintf-s-l
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Failed to parse virtual key code [%ls]\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpVkCodeWStr->lpWCharArr, lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Failed to parse virtual key code [%ls]\n"
"Line: %ls\n",
(1 + ulLineIndex), lpVkCodeWStr->lpWCharArr, lpLineWStr->lpWCharArr);
}

// Ref: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
if (dwVkCode < 0x01 || dwVkCode > 0xFE)
{
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Invalid virtual key code [%ls]->%d: Min: 0x01 (1), Max: 0xFE (254)\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpVkCodeWStr->lpWCharArr, dwVkCode, lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Invalid virtual key code [%ls]->%d: Min: 0x01 (1), Max: 0xFE (254)\n"
"Line: %ls\n",
(1 + ulLineIndex), lpVkCodeWStr->lpWCharArr, dwVkCode, lpLineWStr->lpWCharArr);
}

WStrArrFree(&tokenWStrArr);
Expand All @@ -248,12 +231,9 @@ static BOOL ConfigParseModifier0(_In_ const wchar_t *lpTokenWCharAr
{
if (0 != (eModifier & *peModifiers))
{
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Multiple %ls modifiers are not allowed: [%ls]\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpTokenWCharArr, lpShortcutKeyWStr->lpWCharArr, lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Multiple %ls modifiers are not allowed: [%ls]\n"
"Line: %ls\n",
(1 + ulLineIndex), lpTokenWCharArr, lpShortcutKeyWStr->lpWCharArr, lpLineWStr->lpWCharArr);
}
*peModifiers |= eModifier;
return TRUE;
Expand All @@ -274,15 +254,15 @@ void ConfigParseModifier(_In_ const struct WStr *lpTokenWStr, // Ex: L

if (0 == _wcsicmp(L"Shift", lpTokenWStr->lpWCharArr))
{
ErrorExit(L"Shortcut key modifier 'Shift' is not supported. Please use 'LShift' or 'RShift'.");
ErrorExit("Shortcut key modifier 'Shift' is not supported. Please use 'LShift' or 'RShift'.");
}
if (0 == _wcsicmp(L"Ctrl", lpTokenWStr->lpWCharArr))
{
ErrorExit(L"Shortcut key modifier 'Ctrl' is not supported. Please use 'LCtrl' or 'RCtrl'.");
ErrorExit("Shortcut key modifier 'Ctrl' is not supported. Please use 'LCtrl' or 'RCtrl'.");
}
if (0 == _wcsicmp(L"Alt", lpTokenWStr->lpWCharArr))
{
ErrorExit(L"Shortcut key modifier 'Alt' is not supported. Please use 'LAlt' or 'RAlt'.");
ErrorExit("Shortcut key modifier 'Alt' is not supported. Please use 'LAlt' or 'RAlt'.");
}

if (!ConfigParseModifier0(L"LShift", SHIFT_LEFT , lpTokenWStr, lpShortcutKeyWStr, ulLineIndex, lpLineWStr, peModifiers)
Expand All @@ -292,13 +272,9 @@ void ConfigParseModifier(_In_ const struct WStr *lpTokenWStr, // Ex: L
&& !ConfigParseModifier0(L"LAlt" , ALT_LEFT , lpTokenWStr, lpShortcutKeyWStr, ulLineIndex, lpLineWStr, peModifiers)
&& !ConfigParseModifier0(L"RAlt" , ALT_RIGHT , lpTokenWStr, lpShortcutKeyWStr, ulLineIndex, lpLineWStr, peModifiers))
{
// Ref: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-s-snprintf-s-l-snwprintf-s-snwprintf-s-l
_snwprintf_s(g_lpErrorMsgBuffer, g_ulErrorMsgBufferSize, g_ulErrorMsgBufferSize,
L"Config file: Line #%zd: Unknown modifier: [%ls]\r\n"
L"Line: %ls\r\n",
(1 + ulLineIndex), lpTokenWStr->lpWCharArr, lpLineWStr->lpWCharArr);

ErrorExit(g_lpErrorMsgBuffer);
ErrorExitF("Config file: Line #%zd: Unknown modifier: [%ls]\n"
"Line: %ls\n",
(1 + ulLineIndex), lpTokenWStr->lpWCharArr, lpLineWStr->lpWCharArr);
}
}

Expand Down
Loading

0 comments on commit 781555e

Please sign in to comment.