Working with the keyboard
This tutorial will guide you through handling keyboard input on your Classpad calculator using C++. Even if you’re new to C development, we’ll take it step by step.
Step 1: Empty base
Section titled “Step 1: Empty base”Start with the “Application Template”.
Go to your codespace and open the main.cpp
file. Ensure it have the needed imports :
#include <appdef.hpp>#include <sdk/calc/calc.hpp>#include <sdk/os/input.hpp>#include <sdk/os/lcd.hpp>#include <sdk/os/debug.hpp>
Here, we’ll need:
<appdef.hpp>
: Defines app properties like name and version<sdk/calc/calc.hpp>
to manage the calculator environment and graphics<sdk/os/input.hpp>
to manage the key events<sdk/os/lcd.hpp>
to draw on the screen<sdk/os/debug.hpp>
to doDebug_Printf
when debugging your application
Step 2: Set Up Your App Information
Section titled “Step 2: Set Up Your App Information”Add your app’s basic information:
APP_NAME("TouchRectangle")APP_DESCRIPTION("Move a rectangle based on touch and key input.")APP_AUTHOR("Your Name")APP_VERSION("1.0.0")
Step 3: Define Constants and Variables
Section titled “Step 3: Define Constants and Variables”Add constants for colors, rectangle size, and movement:
#define COLOR_BACKGROUND RGB_TO_RGB565(0, 0, 0)#define COLOR_RECTANGLE RGB_TO_RGB565(0x1F, 0, 0)
#define RECT_SIZE 20#define MOVE_STEP 5
extern uint16_t *vram;int lcdWidth, lcdHeight;int rectX, rectY;
These constants define:
COLOR_BACKGROUND
: Black background colorCOLOR_RECTANGLE
: Red rectangle colorRECT_SIZE
: Size of the rectangle in pixelsMOVE_STEP
: How many pixels to move with each key press
Step 4: Create Drawing Functions
Section titled “Step 4: Create Drawing Functions”Create functions to draw the rectangle and update the screen:
void drawRectangle(int x, int y, uint16_t color) { for (int i = x; i < x + RECT_SIZE; ++i) { for (int j = y; j < y + RECT_SIZE; ++j) { if (i >= 0 && i < lcdWidth && j >= 0 && j < lcdHeight) { vram[i + (j + 24) * lcdWidth] = color; } } }}
void draw() { LCD_ClearScreen(); drawRectangle(rectX, rectY, COLOR_RECTANGLE); LCD_Refresh();}
The drawRectangle
function draws a filled rectangle at position (x, y) with the specified color.
The draw
function clears the screen, draws the rectangle, and refreshes the display.
Step 5: Initialize the App
Section titled “Step 5: Initialize the App”Set up the main function with initialization code:
extern "C"void main() { LCD_VRAMBackup(); vram = LCD_GetVRAMAddress(); LCD_GetSize(&lcdWidth, &lcdHeight);
rectX = lcdWidth / 2 - RECT_SIZE / 2; rectY = lcdHeight / 2 - RECT_SIZE / 2;
/* TODO: Add input handling loop here */
LCD_VRAMRestore(); LCD_Refresh();}
This code:
- Backs up the current VRAM content
- Gets the VRAM address and screen dimensions
- Centers the rectangle on the screen
Step 6: Implement Input Handling
Section titled “Step 6: Implement Input Handling”This loop will:
- Draws the current state
- Waits for input events
- Handles touch events by moving the rectangle to the touch position
- Handles key events by moving the rectangle with arrow keys
- Exits when the CLEAR key is pressed
- Keeps the rectangle within screen boundaries
Input Events
Section titled “Input Events”The app uses GetInput()
to wait for events:
EVENT_TOUCH
: Touch screen eventsEVENT_KEY
: Keyboard events
Touch Handling
Section titled “Touch Handling”When the user touches the screen:
TOUCH_DOWN
: Initial touchTOUCH_HOLD_DRAG
: Continued touch with movement
Key Handling
Section titled “Key Handling”The app responds to these keys:
KEYCODE_UP
: Move rectangle upKEYCODE_DOWN
: Move rectangle downKEYCODE_LEFT
: Move rectangle leftKEYCODE_RIGHT
: Move rectangle rightKEYCODE_POWER_CLEAR
: Exit the app
Add the main input handling loop:
struct InputEvent event; bool running = true;
while (running) { draw();
GetInput(&event, 0xFFFFFFFF, 0x10);
switch (event.type) { case EVENT_TOUCH: if (event.data.touch_single.direction == TOUCH_DOWN || event.data.touch_single.direction == TOUCH_HOLD_DRAG) { rectX = event.data.touch_single.p1_x; rectY = event.data.touch_single.p1_y; } break;
case EVENT_KEY: switch (event.data.key.keyCode) { case KEYCODE_UP: rectY -= MOVE_STEP; break; case KEYCODE_RIGHT: rectX += MOVE_STEP; break; case KEYCODE_DOWN: rectY += MOVE_STEP; break; case KEYCODE_LEFT: rectX -= MOVE_STEP; break; case KEYCODE_POWER_CLEAR: running = false; break; } break; }
// Ensure the rectangle stays within the screen boundaries if (rectX < 0) rectX = 0; if (rectY < 0) rectY = 0; if (rectX + RECT_SIZE > lcdWidth) rectX = lcdWidth - RECT_SIZE; if (rectY + RECT_SIZE > lcdHeight) rectY = lcdHeight - RECT_SIZE; }
Complete Example Code
Section titled “Complete Example Code”Here’s the complete code for your TouchRectangle app:
#include <appdef.hpp>#include <sdk/calc/calc.hpp>#include <sdk/os/debug.hpp>#include <sdk/os/input.hpp>#include <sdk/os/lcd.hpp>
APP_NAME("TouchRectangle")APP_DESCRIPTION("Move a rectangle based on touch and key input.")APP_AUTHOR("Your Name")APP_VERSION("1.0.0")
#define COLOR_BACKGROUND RGB_TO_RGB565(0, 0, 0)#define COLOR_RECTANGLE RGB_TO_RGB565(0x1F, 0, 0)
#define RECT_SIZE 20#define MOVE_STEP 5
extern uint16_t *vram;int lcdWidth, lcdHeight;int rectX, rectY;
void drawRectangle(int x, int y, uint16_t color) { for (int i = x; i < x + RECT_SIZE; ++i) { for (int j = y; j < y + RECT_SIZE; ++j) { if (i >= 0 && i < lcdWidth && j >= 0 && j < lcdHeight) { vram[i + (j + 24) * lcdWidth] = color; } } }}
void draw() { LCD_ClearScreen(); drawRectangle(rectX, rectY, COLOR_RECTANGLE); LCD_Refresh();}
extern "C"void main() { LCD_VRAMBackup(); vram = LCD_GetVRAMAddress(); LCD_GetSize(&lcdWidth, &lcdHeight);
rectX = lcdWidth / 2 - RECT_SIZE / 2; rectY = lcdHeight / 2 - RECT_SIZE / 2;
struct InputEvent event;
bool running = true; while (running) { draw();
GetInput(&event, 0xFFFFFFFF, 0x10);
switch (event.type) { case EVENT_TOUCH: if (event.data.touch_single.direction == TOUCH_DOWN || event.data.touch_single.direction == TOUCH_HOLD_DRAG) { rectX = event.data.touch_single.p1_x; rectY = event.data.touch_single.p1_y; } break;
case EVENT_KEY: switch (event.data.key.keyCode) { case KEYCODE_UP: rectY -= MOVE_STEP; break; case KEYCODE_RIGHT: rectX += MOVE_STEP; break; case KEYCODE_DOWN: rectY += MOVE_STEP; break; case KEYCODE_LEFT: rectX -= MOVE_STEP; break; case KEYCODE_POWER_CLEAR: running = false; break; } break; }
// Ensure the rectangle stays within the screen boundaries if (rectX < 0) rectX = 0; if (rectY < 0) rectY = 0; if (rectX + RECT_SIZE > lcdWidth) rectX = lcdWidth - RECT_SIZE; if (rectY + RECT_SIZE > lcdHeight) rectY = lcdHeight - RECT_SIZE; }
LCD_VRAMRestore(); LCD_Refresh();}
Step 7: Build and Run Your App
Section titled “Step 7: Build and Run Your App”- Save your
main.cpp
file - Open the terminal and run
make bin
- Download the generated
.bin
file - Transfer it to your calculator
- Launch it from the Hollyhock-2 Launcher
Next Steps
Section titled “Next Steps”Congratulations! You’ve created an interactive app that responds to both touch and keyboard input.
Now try:
- Adding more shapes that respond to different keys
- Creating a simple game with multiple moving objects
- Implementing more complex touch gestures
- Adding visual feedback for different input types