diff --git a/.Jules/palette.md b/.Jules/palette.md index 8aed79d..347c6db 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -17,3 +17,7 @@ ## 2026-02-13 - Tactile Feedback in CLI **Learning:** In terminal-based games, users expect immediate visual feedback for their actions. Relying on a periodic "tick" to update the UI creates a laggy feel. Using `poll()` with a dynamic timeout allows the application to remain idle yet wake up instantly to process and render user input. **Action:** Always trigger a UI refresh immediately after processing user input in CLI applications, and use efficient waiting mechanisms (like `poll`) that can be interrupted by input. + +## 2026-10-24 - Pre-start Prompts and Cursor Visibility +**Learning:** For interactive CLI games, automatically starting the game and timer immediately upon executing the binary creates a jarring user experience, as the user is rarely ready to start playing instantly. Additionally, the presence of a blinking cursor during rapid UI updates is distracting and reduces visual polish. Providing an explicit "Press any key to start" action gives the user control, while hiding the cursor during active gameplay (`\033[?25l`) and restoring it upon exit (`\033[?25h`) makes the terminal feel more like a dedicated application than a raw command-line interface. +**Action:** Always require an explicit user action to start a CLI interactive loop and always hide the terminal cursor during active rendering (ensuring it is safely restored on normal exit and via signal handlers). diff --git a/src/main.cpp b/src/main.cpp index 775bf66..5408b15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,8 +25,8 @@ struct termios oldt; void restore_terminal(int signum) { tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // Use write() and _exit() because they are async-signal-safe - const char* msg = "\033[0m\n\nGame interrupted. Terminal settings restored.\n"; - write(STDOUT_FILENO, msg, 52); + const char msg[] = "\033[?25h\033[0m\n\nGame interrupted. Terminal settings restored.\n"; + write(STDOUT_FILENO, msg, sizeof(msg) - 1); _exit(signum); } @@ -51,6 +51,11 @@ int main() { << "Controls:\n " << CLR_CTRL << "[h]" << CLR_RESET << " Toggle Hard Mode (10x Speed!)\n " << CLR_CTRL << "[q]" << CLR_RESET << " Quit Game\n " << CLR_CTRL << "[Any key]" << CLR_RESET << " Click!\n\n"; + std::cout << "Press any key to start..." << std::flush; + read(STDIN_FILENO, &input, 1); + std::cout << "\r \r"; + std::cout << "\033[?25l" << std::flush; + struct pollfd fds[1] = {{STDIN_FILENO, POLLIN, 0}}; auto last_tick = std::chrono::steady_clock::now(); bool updateUI = true; @@ -86,6 +91,7 @@ int main() { } } tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + std::cout << "\033[?25h"; // Restore cursor std::cout << "\n\n" << CLR_SCORE << "Final Score: " << score << CLR_RESET << "\nThanks for playing!\n"; return 0; }