Bounty: 100
This is, kind of, my first programming project. It’s a small project to complete first year’s university programming course.
The program allows the user to record his mouse/keyboard/cursor activities and then replay it. It’s a mini-bot.
I am unsure whether I will continue developing this project, or abandon C and go learn some OOP or web. Either way, I would really love to know what kind of mistakes I made.
Particularly: do you see something that hurts your eyes, some bad practices, terrible naming, some unreadable code?
Short video demonstration: https://streamable.com/7qcb3
Project’s code: https://github.com/Wenox/WinAuto
The menu.c
file was written in a rush, so you’re likely to find the ugliest code in there. I am mostly interested about menu.c, smooth_cursor.c, replay.c, recording.c
files.
I’ve got a small review and this code is vulnerable:
printf("Save recording as (i.e: myrecording.txt):n");
char file_name[64];
scanf("%s", file_name);
(I will probably replace scanf
with fgets
combined with sscanf
).
Other than that, now that I am looking at my code I probably could have used typedef
on the struct
. Heard that it’s a bad practice, though.
I am not sure if I should remove the large, ugly comments from the .h
files or not.
The program is launched from main
like this:
int main(int argc, char **argv)
{
struct f_queue *headptr = NULL;
struct f_queue *tailptr = NULL;
if (!h_switch_invoked(argc, argv))
init_menu(headptr, tailptr, 0, 0);
else
init_menu(headptr, tailptr, 7, 0);
return 0;
}
Here is menu.c
file that I am particularly interested in. I’ve written it in a rush and never wrote “menu” before. So I came up with an idea to make it recursive, with helping enum
:
bool h_switch_invoked(int argc, char **argv)
{
if (argc > 1)
if (0 == strcmp(argv[1], "-h"))
return true;
return false;
}
/** Enum containing various menu flags used to determine which <b>printf</b> should be displayed to the user, based on earlier program behaviour. */
enum menu_flags { ///< start of definition
NO_ERRORS, ///< default
ERROR_NO_TXT_SUFFIX, ///< when user forgot to input the .txt postfix
ERROR_READING_FILE, ///< when file was corrupted, does not exist or cannot be opened
SAVED_HOTKEY, ///< when the hotkey has been successfully saved
SAVED_FILE, ///< when the file saved successfully
STOPPED_PLAYBACK, ///< when the recording playback successfully ended
STOPPED_SCREENSAVER, ///< when the screensaver has been successfully stopped
HELP_SWITCH ///< when program was ran with '-h' switch
};
void draw_menu(const int flag_id)
{
system("cls");
switch (flag_id) {
case 0:
printf("WinAuton");
break;
case 1:
printf("ERROR: File name must end with .txt suffixnn");
break;
case 2:
printf("ERROR: No such file or file is corruptednn");
break;
case 3:
printf("Hotkey set successfullynn");
break;
case 4:
printf("Recording saved successfullynn");
break;
case 5:
printf("Playback finished or interruptednn");
break;
case 6:
printf("Welcome backnn");
break;
case 7:
print_help();
break;
default: // do nothing
break;
}
printf("Press 1 to set global hotkey (DEFAULT HOTKEY: F5)n");
printf("Press 2 to create new recordingn");
printf("Press 3 to play recordingn");
printf("Press 4 to start screensavern");
printf("Press 5 to exitn");
}
int get_menu_choice(void)
{
int choice = 0;
while (choice < 1 || choice > 5)
if (1 != scanf("%d", &choice))
fseek(stdin, 0, SEEK_END);
return choice;
}
int get_hotkey(void)
{
printf("Set hotkey: n");
int hotkey = 0;
while (hotkey == 0 ||
hotkey == KEY_RETURN ||
hotkey == KEY_LMB ||
hotkey == KEY_RMB ||
hotkey == KEY_F5) {
hotkey = get_keystroke();
}
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
return hotkey;
}
bool str_ends_with(const char *source, const char *suffix)
{
int source_len = strlen(source);
int suffix_len = strlen(suffix);
return (source_len >= suffix_len) && (0 == strcmp(source + (source_len - suffix_len), suffix));
}
int get_cycles_num(void)
{
printf("How many playing cycles? (>5 to play infinitely, default 1):n");
int cycles_num = 1;
if (1 != scanf("%d", &cycles_num) || cycles_num <= 0) {
fseek(stdin, 0, SEEK_END);
get_cycles_num();
}
return cycles_num;
}
void exec_play_recording(struct f_queue *head, struct f_queue *tail, const int cycles_num, const int hotkey_id)
{
printf("Playing recording...n");
printf("Press your hotkey to stopn");
if (cycles_num > 5) {
make_queue_cyclic(head, tail);
play_recording(tail, hotkey_id);
unmake_queue_cyclic(head, tail);
return;
}
for (int i = 0; i < cycles_num; i++)
play_recording(tail, hotkey_id);
}
void init_menu(struct f_queue *head, struct f_queue *tail, const int flag_id, const int hotkey_id); ///< cyclic dependency
void chosen_recording(struct f_queue *head, struct f_queue *tail, const int hotkey_id)
{
printf("Save recording as (i.e: myrecording.txt):n");
char file_name[64];
scanf("%s", file_name);
if (str_ends_with(file_name, ".txt")) {
record(&head, &tail, 10, hotkey_id);
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
trim_list(&head);
save_recording(tail, file_name);
free_recording(&head, &tail);
init_menu(head, tail, SAVED_FILE, hotkey_id);
}
else
init_menu(head, tail, ERROR_NO_TXT_SUFFIX, hotkey_id);
}
void chosen_playback(struct f_queue *head, struct f_queue *tail, const int hotkey_id)
{
printf("Type in file name of your recording (i.e: myfile.txt):n");
char file_name[64];
scanf("%s", file_name);
if (load_recording(&head, &tail, file_name)) {
int cycles_num = get_cycles_num();
exec_play_recording(head, tail, cycles_num, hotkey_id);
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
free_recording(&head, &tail);
init_menu(head, tail, STOPPED_PLAYBACK, hotkey_id);
}
else { // error when reading file
free_recording(&head, &tail);
init_menu(head, tail, ERROR_READING_FILE, hotkey_id);
}
}
void init_menu(struct f_queue *head, struct f_queue *tail, const int flag_id, const int hotkey_id)
{
draw_menu(flag_id);
int choice = get_menu_choice();
static int hotkey = KEY_F5; /// default hotkey
switch(choice) {
case 1:
hotkey = get_hotkey();
init_menu(head, tail, SAVED_HOTKEY, hotkey);
break;
case 2:
chosen_recording(head, tail, hotkey);
break;
case 3:
chosen_playback(head, tail, hotkey);
break;
case 4:
exec_screen_saver(hotkey);
init_menu(head, tail, STOPPED_SCREENSAVER, hotkey);
break;
case 5:
return;
default: // do nothing
break;
}
}
Here’s recording “engine” from recording.c
:
void record(struct f_queue **head, struct f_queue **tail, const int sleep_dur, const int hotkey_id)
{
int key_buff[2] = {-1, -1}; ///< buffer for curr and prev pressed key
POINT cursor_buff[2] = {{-1, -1}, {-1, -1}}; ///< buffer for curr and prev cursor position
printf("RECORDING...n[press your hotkey to stop]n");
while(key_buff[1] != hotkey_id) { ///< stop recording when 'hotkey' is pressed
add_cursor(head, tail, cursor_buff);
add_keystroke(head, tail, key_buff);
add_sleep(head, tail, sleep_dur);
}
}
and replay “engine” from replay.c
:
void play_recording(struct f_queue *tail, const int hotkey_id)
{
while (tail) {
if (check_key(hotkey_id))
return;
if (tail->f_type == _GETCURSOR)
SetCursorPos(tail->f_args[0], tail->f_args[1]); ///< Simulates cursor's position
else if (tail->f_type == _GETKEY)
send_input(tail->f_args[0]); ///< Simulates keystroke
else if (tail->f_type == _SLEEP)
Sleep(tail->f_args[0]); ///< Simulates waiting interval in between keystrokes and/or cursor's movements
tail = tail->prev;
}
}
I am in need of all kinds of criticism, and for that I am grateful. Thanks.
Get this bounty!!!