Warning: Undefined array key "HTTP_ACCEPT_LANGUAGE" in /var/www/vhosts/bilgigunlugum.net/httpdocs/index.php on line 43
SDL Game Programming

Unreal Engine Oyun Programlama sayfalarımız yayında...

Ana sayfa > Oyun programlama > SDL3 programming > Text input

Text input

In this section, we will examine a program that uses the SDL3 and SDL3_ttf libraries to create a simple text input interface. This program accepts text input from the user, displays it on the screen, and controls the visual state of the text input box.

The program works as follows:

  • When the user presses the C key, a text input window with a blue background opens in the lower-left corner of the main window.
  • The characters entered are written to the input box and the main window until the ENTER key is pressed.
  • Pressing the BACKSPACE key deletes the last entered character.
  • When the user presses the ENTER key, the text input ends and the text input window closes.

Variable declarations and purposes

Global variables that can be accessed by multiple functions at the beginning of the code defined.

  • WINDOW_WIDTH, WINDOW_HEIGHT: Constants that determine the window size.
  • TEXT_BUFFER_SIZE: Constant that determines the maximum size of the text input buffer.
  • is_running: A flag variable that controls the program's main loop. As long as it is true, the loop continues to run.
  • window, renderer: Pointers to the window and renderer, the core components of SDL.
  • font: A pointer to the loaded font.
  • input_text: A character array that stores the text entered by the user.
  • text_length: An integer that holds the length of the current text in the input_text array.
  • text_input_active: A flag variable that indicates whether the text input mode is active. It takes the value 1 (active) or 0 (inactive).
  • rect_input: An SDL_Rect structure that specifies the position and size of the text input box on the screen.

Functions' operating logic

  • init_window(): Initializes the SDL video system and creates a window and a renderer with the specified dimensions. SDL_CreateWindowAndRenderer() creates the window and rendering engine with a single function call. Returns true on success and false on error.
  • init_vars(): Initializes variables at program startup. This program does not perform any operations.
  • load_img(): Loads the image file "assets/images/water.png" using the SDL_image library and creates the textures (texture_background and texture_foreground). This forms the visual basis of the animation.
  • process_event(): Listens for keyboard and window events. This function is triggered when the user presses a key or closes the window.
  • SDL_EVENT_KEY_DOWN (Keypress):
    • Pressing the SDLK_C key, if text input mode is not active (text_input_active == 0), starts text input. The SDL_StartTextInput() function activates the virtual keyboard and defines the text input box (rect_input).
    • Pressing the SDLK_RETURN or SDLK_KP_ENTER keys terminates text input mode. The SDL_StopTextInput() function stops text entry.
    • When the SDLK_BACKSPACE key is pressed, it deletes the last character of the entered text.
    • When the SDLK_ESCAPE key is pressed, it sets the is_running variable to false, terminating the program.
  • SDL_EVENT_TEXT_INPUT (Text Input): If text input mode is active, it appends each character entered by the user to the input_text array.
  • SDL_EVENT_QUIT (Close Window): When the close button on the window is pressed, it sets the is_running variable to false.
  • update_screen(): Updates the state of objects on the screen. This program does not contain any operations.
  • draw_screen(): Draws the screen based on the updated data.
    • Sets the drawing color to black with SDL_SetRenderDrawColor().
    • Clears the window with the specified color (black) with SDL_RenderClear().
    • If text_input_active is active, draws an orange border around the text input box and a blue box inside it.
    • If input_text contains text (text_length > 0):
      • Draws the input_text text onto a surface with TTF_RenderText_Blended(). This makes the text ready for drawing.
      • Converts the surface to a texture with SDL_CreateTextureFromSurface(). Textures are ideal for fast drawing.
      • The first call to SDL_RenderTexture() renders the text into the text input box, and the second call renders it to the upper-left corner of the main window.
      • Limits the width and height when the text doesn't fit.
    • Finally, it renders all drawings made with SDL_RenderPresent() visible on the screen.
  • destroy_window(): Releases all allocated resources when the program terminates. The textures, window, and drawing tool are destroyed, respectively, and the SDL library is terminated.

Code flow

  • The main function creates a window and a renderer with init_window().
  • Initial values are assigned to variables with init_vars(). In this program, they are empty.
  • The font is loaded with load_media(), which is called arial.ttf.
  • A while loop begins and continues as long as is_running is true.
  • Within the loop, the process_event(), update_screen(), and draw_screen() functions are called, respectively.
    • process_event(): User input is processed.
    • update_screen(): Variable values are updated.
    • draw_screen(): The screen is redrawn according to the new state.
  • When the user presses the ESC key or closes the window, is_running becomes false and the loop terminates.
  • After the loop is completed, destroy_window() is called to free the resources and terminate the program.

The main.c file content will be as follows:


#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define TEXT_BUFFER_SIZE 400

// Global variables
int is_running = false;        // Variable that controls the execution of the main loop of the program
SDL_Window *window = NULL;     // Main window variable
SDL_Renderer *renderer = NULL; // Renderer variable

TTF_Font *font = NULL;
char input_text[TEXT_BUFFER_SIZE] = "";
int text_length = 0;
int text_input_active = 0;
SDL_Rect rect_input = { 20, 400, 500, 40 };

// Function prototypes
int init_window(void);         // Create window and renderer
void init_vars(void);          // Initialize variables
void load_media(void);         // Load media
void process_event(void);      // Process events
void update_screen();          // Update values
void draw_screen(void);        // Draw screen
void destroy_window(void);     // Destroy window

int main(int argc, char* argv[])
{
    // Create window and renderer
    is_running = init_window();

    // Initialize variables
    init_vars();

    // Load media files
    load_media();

   // Main loop
    while (is_running) {
       process_event();     // Processing SDL events (Here keyboard inputs).
       update_screen();     // Updating variables
       draw_screen();       // Drawing objects on the window (Rendering)
    }

    // Destroy renderer and SDL window
    destroy_window();

    return 0;
}

// Create window and renderer
int init_window(void)
{
    // Initialize the SDL, Image and TTF libraries.
    if (SDL_Init(SDL_INIT_VIDEO) == false) {
        SDL_Log("SDL init error: %s\n", SDL_GetError());
        return false;
    }
    if (TTF_Init() == false) {
        SDL_Log("TTF init error: %s", SDL_GetError());
        return false;
    }

    // Create a window and a 2D rendering context for the window.
    if (!SDL_CreateWindowAndRenderer("SDL3 window", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
        SDL_Log("SDL_CreateWindowAndRenderer() error: %s", SDL_GetError());
        return false;
    }

    return true;
}

// Initialization function that runs only once at the beginning of the program
void init_vars(void)
{

}

// Load image to texture
void load_media(void)
{
    // Loading a font
    font = TTF_OpenFont("arial.ttf", 24);
    if (font == NULL) {
        SDL_Log("Font loading error: %s", SDL_GetError());
    }
}

// Function to control SDL events and process keyboard inputs
void process_event(void)
{
    SDL_Event event;

    // Creating a loop to process user inputs
    while (SDL_PollEvent(&event)) {
       switch (event.type) {

          case SDL_EVENT_KEY_DOWN: // Indicate that a key was pressed.
               switch (event.key.key) {
                  case SDLK_C:
                       if (!text_input_active) {
                           SDL_Log("Text entry started.");
                           text_input_active = 1;
                           input_text[0] = '\0';
                           SDL_SetTextInputArea(window, &rect_input, 5);
                           SDL_StartTextInput(window); // Start text input
                       }
                       break;
                  case SDLK_RETURN:
                  case SDLK_KP_ENTER:
                       if (text_input_active) {
                           SDL_Log("Text entry terminated.");
                           text_input_active = 0;
                           SDL_StopTextInput(window); // Stop text input
                       }
                       break;
                  case SDLK_BACKSPACE:
                       if (text_input_active && text_length > 0) {
                           text_length--;
                           input_text[text_length] = '\0';
                       }
                       break;
                  case SDLK_ESCAPE:
                       is_running = false;
                       break;
               }
               break;

          case SDL_EVENT_TEXT_INPUT:
               if (text_input_active) {
                   if (text_length + strlen(event.text.text) < TEXT_BUFFER_SIZE) {
                       strcat(input_text, event.text.text);
                       text_length += strlen(event.text.text);
                   }
               }
               break;

          case SDL_EVENT_QUIT: // Logout action by the user (x button at the top right of the window)
               is_running = false; // Terminates the execution of the program main loop.
               break;
       }
    }
}

// Updates objects in the main window
void update_screen(void)
{

}

// Render function used to draw game objects in the main window
void draw_screen(void)
{
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer); // Clear the current rendering target

    if (text_input_active) {
        SDL_FRect f_rect_frame = { (float)rect_input.x-1, (float)rect_input.y-1, (float)rect_input.w+2, (float)rect_input.h+2 };
        SDL_SetRenderDrawColor(renderer, 255, 170, 0, 255); // Frame color
        SDL_RenderRect(renderer, &f_rect_frame);

        SDL_FRect f_rect_input = { (float)rect_input.x, (float)rect_input.y, (float)rect_input.w, (float)rect_input.h };
        SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); // Text input rect background color
        SDL_RenderFillRect(renderer, &f_rect_input);
    }

    if (text_length > 0) {
        SDL_Color textColor = { 255, 255, 255, 255 }; // Text color
        SDL_Surface* texture_surface = TTF_RenderText_Blended(font, input_text, strlen(input_text), textColor);

        if (texture_surface) {
            SDL_Texture* texture_text = SDL_CreateTextureFromSurface(renderer, texture_surface);

            if (texture_text) {
                SDL_FRect renderQuad;

                // Writing text into the text input field
                if (text_input_active) {
                    int padding = 5;
                    renderQuad.x = (float)rect_input.x + padding;
                    renderQuad.y = (float)rect_input.y + padding;
                    renderQuad.w = (float)texture_surface->w;
                    renderQuad.h = (float)texture_surface->h;
                    if (renderQuad.w > rect_input.w - 2 * padding) {
                        renderQuad.w = (float)(rect_input.w - 2 * padding);
                    }
                    if (renderQuad.h > rect_input.h - 2 * padding) {
                        renderQuad.h = (float)(rect_input.h - 2 * padding);
                    }
                    SDL_RenderTexture(renderer, texture_text, NULL, &renderQuad);
                }

                // Writing text to the main window
                renderQuad.x = 20.00;
                renderQuad.y = 20.00;
                renderQuad.w = (float) texture_surface->w;
                renderQuad.h = (float) texture_surface->h;
                SDL_RenderTexture(renderer, texture_text, NULL, &renderQuad);

                SDL_DestroyTexture(texture_text);
            }
            SDL_DestroySurface(texture_surface);
        }
    }

    // Present the rendered content to the screen
    SDL_RenderPresent(renderer);
}

// Destroy Renderer and SDL window, exit from SDL3
void destroy_window(void)
{
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}