如何用C语言基于SDL2创建文本框及解决适配疑问
在C语言中基于SDL2实现文本框的两种方案
方案一:手动基于SDL2+SDL_ttf实现文本框
因为大部分SDL GUI库是C++实现的,用纯C的话自己实现轻量文本框更直接,核心是处理输入事件、维护文本状态、渲染文本。
核心步骤与代码示例
依赖准备:确保链接
SDL2和SDL2_ttf库,编译时用gcc your_code.c -o apppkg-config --cflags --libs sdl2 SDL2_ttf``定义文本框状态结构体:
#include <SDL2/SDL.h> #include <SDL2/SDL_ttf.h> #include <string.h> #include <stdlib.h> typedef struct { SDL_Rect rect; // 文本框位置大小 char* content; // 输入内容 size_t max_len; // 最大输入长度 int is_focused; // 是否获得焦点 int cursor_pos; // 光标位置(字符索引) TTF_Font* font; // 渲染字体 SDL_Color text_color; // 文本颜色 SDL_Color bg_color; // 背景颜色 SDL_Color border_color; // 边框颜色 } TextBox;
- 初始化文本框:
TextBox* textbox_init(int x, int y, int w, int h, size_t max_len, const char* font_path, int font_size) { TextBox* tb = malloc(sizeof(TextBox)); tb->rect = (SDL_Rect){x, y, w, h}; tb->content = calloc(max_len + 1, sizeof(char)); tb->max_len = max_len; tb->is_focused = 0; tb->cursor_pos = 0; tb->font = TTF_OpenFont(font_path, font_size); tb->text_color = (SDL_Color){255,255,255,255}; tb->bg_color = (SDL_Color){50,50,50,255}; tb->border_color = (SDL_Color){100,100,100,255}; return tb; }
- 处理输入事件:
void textbox_handle_event(TextBox* tb, SDL_Event* e) { if (e->type == SDL_MOUSEBUTTONDOWN) { // 点击判断是否聚焦 tb->is_focused = SDL_PointInRect(&(SDL_Point){e->button.x, e->button.y}, &tb->rect); } if (!tb->is_focused) return; if (e->type == SDL_KEYDOWN) { switch(e->key.keysym.sym) { case SDLK_BACKSPACE: if (tb->cursor_pos > 0) { memmove(&tb->content[tb->cursor_pos-1], &tb->content[tb->cursor_pos], strlen(tb->content) - tb->cursor_pos + 1); tb->cursor_pos--; } break; case SDLK_LEFT: if (tb->cursor_pos > 0) tb->cursor_pos--; break; case SDLK_RIGHT: if (tb->cursor_pos < strlen(tb->content)) tb->cursor_pos++; break; default: // 处理可打印字符 if (e->key.keysym.sym >= SDLK_SPACE && e->key.keysym.sym <= SDLK_z && strlen(tb->content) < tb->max_len) { char c = e->key.keysym.sym; // 处理大小写(根据Shift键状态) if (!(SDL_GetModState() & KMOD_SHIFT) && c >= 'A' && c <= 'Z') c += 32; else if ((SDL_GetModState() & KMOD_SHIFT) && c >= 'a' && c <= 'z') c -= 32; // 插入字符到光标位置 memmove(&tb->content[tb->cursor_pos+1], &tb->content[tb->cursor_pos], strlen(tb->content) - tb->cursor_pos + 1); tb->content[tb->cursor_pos] = c; tb->cursor_pos++; } break; } } }
- 渲染文本框:
void textbox_render(TextBox* tb, SDL_Renderer* renderer) { // 绘制背景 SDL_SetRenderDrawColor(renderer, tb->bg_color.r, tb->bg_color.g, tb->bg_color.b, tb->bg_color.a); SDL_RenderFillRect(renderer, &tb->rect); // 绘制边框(聚焦时高亮) SDL_Color border = tb->is_focused ? (SDL_Color){200,200,200,255} : tb->border_color; SDL_SetRenderDrawColor(renderer, border.r, border.g, border.b, border.a); SDL_RenderDrawRect(renderer, &tb->rect); // 渲染文本 SDL_Surface* text_surf = TTF_RenderText_Solid(tb->font, tb->content, tb->text_color); SDL_Texture* text_tex = SDL_CreateTextureFromSurface(renderer, text_surf); SDL_Rect text_rect = {tb->rect.x + 5, tb->rect.y + (tb->rect.h - text_surf->h)/2, text_surf->w, text_surf->h}; SDL_RenderCopy(renderer, text_tex, NULL, &text_rect); // 渲染光标(聚焦时) if (tb->is_focused) { // 计算光标位置 char temp[tb->max_len + 1]; strncpy(temp, tb->content, tb->cursor_pos); temp[tb->cursor_pos] = '\0'; SDL_Surface* cursor_surf = TTF_RenderText_Solid(tb->font, temp, tb->text_color); int cursor_x = tb->rect.x + 5 + cursor_surf->w; int cursor_y = tb->rect.y + 5; int cursor_h = tb->rect.h - 10; SDL_SetRenderDrawColor(renderer, 255,255,255,255); SDL_RenderDrawLine(renderer, cursor_x, cursor_y, cursor_x, cursor_y + cursor_h); SDL_FreeSurface(cursor_surf); } // 清理资源 SDL_FreeSurface(text_surf); SDL_DestroyTexture(text_tex); }
- 主循环示例:
int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); TTF_Init(); SDL_Window* window = SDL_CreateWindow("SDL2 TextBox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); TextBox* tb = textbox_init(50, 50, 300, 40, 64, "arial.ttf", 24); int running = 1; SDL_Event e; while(running) { while(SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) running = 0; textbox_handle_event(tb, &e); } SDL_SetRenderDrawColor(renderer, 30,30,30,255); SDL_RenderClear(renderer); textbox_render(tb, renderer); SDL_RenderPresent(renderer); } // 清理 free(tb->content); TTF_CloseFont(tb->font); free(tb); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); TTF_Quit(); SDL_Quit(); return 0; }
方案二:结合GTK与SDL2实现
如果不想手动写文本框逻辑,可以用GTK的GtkEntry控件作为文本输入,同时将SDL的渲染窗口嵌入GTK界面中,两者通过GTK的容器布局结合。
核心步骤与代码示例
依赖准备:链接
SDL2和gtk+-3.0,编译命令:gcc your_code.c -o apppkg-config --cflags --libs sdl2 gtk+-3.0``示例代码:
#include <SDL2/SDL.h> #include <gtk/gtk.h> static SDL_Window* sdl_window; static SDL_Renderer* sdl_renderer; // GTK窗口关闭回调 static void on_window_destroy(GtkWidget* widget, gpointer data) { SDL_DestroyRenderer(sdl_renderer); SDL_DestroyWindow(sdl_window); SDL_Quit(); gtk_main_quit(); } // SDL渲染回调(定时刷新) static gboolean sdl_render_loop(gpointer data) { SDL_SetRenderDrawColor(sdl_renderer, 0, 128, 255, 255); SDL_RenderClear(sdl_renderer); // 这里添加你的SDL渲染逻辑 SDL_RenderPresent(sdl_renderer); return G_SOURCE_CONTINUE; } // 获取GTK控件的原生窗口ID,用于SDL嵌入 static Uint32 get_gtk_window_id(GtkWidget* widget) { #ifdef __linux__ return GDK_WINDOW_XID(gtk_widget_get_window(widget)); #elif _WIN32 return (Uint32)GDK_WINDOW_HWND(gtk_widget_get_window(widget)); #endif } int main(int argc, char* argv[]) { gtk_init(&argc, &argv); SDL_Init(SDL_INIT_VIDEO); // 创建GTK主窗口 GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "GTK + SDL2"); gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL); // 创建垂直布局容器 GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); gtk_container_add(GTK_CONTAINER(window), vbox); // 添加GTK文本框 GtkWidget* entry = gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(entry), 64); gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 10); // 添加SDL嵌入的绘图区域 GtkWidget* sdl_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(vbox), sdl_box, TRUE, TRUE, 10); gtk_widget_set_size_request(sdl_box, 780, 480); // 显示所有控件,获取原生窗口ID gtk_widget_show_all(window); gdk_window_process_all_updates(gdk_get_default_root_window()); Uint32 window_id = get_gtk_window_id(sdl_box); // 创建SDL窗口嵌入到GTK控件中 sdl_window = SDL_CreateWindowFrom((void*)window_id); sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED); // 启动SDL渲染循环 g_timeout_add(16, sdl_render_loop, NULL); gtk_main(); return 0; }
关键说明
- 这种方式下,文本输入完全由GTK的
GtkEntry处理,你可以通过gtk_entry_get_text()获取输入内容,无需自己处理键盘事件。 - SDL负责渲染游戏/图形内容,两者共享同一个窗口界面,交互自然。
内容的提问来源于stack exchange,提问作者dMight
相关产品推荐
相关产品推荐

