diff --git a/test/testime.c b/test/testime.c index b9bf090b03..8727c720dd 100644 --- a/test/testime.c +++ b/test/testime.c @@ -42,21 +42,32 @@ #define CURSOR_BLINK_INTERVAL_MS 500 +typedef struct +{ + SDL_Window *window; + SDL_Renderer *renderer; + int rendererID; + SDL_FRect textRect; + SDL_FRect markedRect; + char text[MAX_TEXT_LENGTH]; + char markedText[MAX_TEXT_LENGTH]; + int cursor; + int cursor_length; + SDL_bool cursor_visible; + Uint64 last_cursor_change; + char **candidates; + int num_candidates; + int selected_candidate; + SDL_bool horizontal_candidates; +} WindowState; + static SDLTest_CommonState *state; -static SDL_FRect textRect, markedRect; -static SDL_Color lineColor = { 0, 0, 0, 255 }; -static SDL_Color backColor = { 255, 255, 255, 255 }; -static SDL_Color textColor = { 0, 0, 0, 255 }; -static char text[MAX_TEXT_LENGTH], markedText[MAX_TEXT_LENGTH]; -static int cursor = 0; -static int cursor_length = 0; -static SDL_bool cursor_visible; -static Uint64 last_cursor_change; +static WindowState *windowstate; +static const SDL_Color lineColor = { 0, 0, 0, 255 }; +static const SDL_Color backColor = { 255, 255, 255, 255 }; +static const SDL_Color textColor = { 0, 0, 0, 255 }; static SDL_BlendMode highlight_mode; -static char **candidates; -static int num_candidates; -static int selected_candidate; -static SDL_bool horizontal_candidates; + #ifdef HAVE_SDL_TTF static TTF_Font *font; #else @@ -455,81 +466,88 @@ static Uint32 utf8_decode(const char *p, size_t len) return codepoint; } -static void InitInput(void) +static WindowState *GetWindowStateForWindowID(SDL_WindowID windowID) { int i; - - /* Prepare a rect for text input */ - textRect.x = textRect.y = 100.0f; - textRect.w = DEFAULT_WINDOW_WIDTH - 2 * textRect.x; - textRect.h = 50.0f; - - text[0] = 0; - markedRect = textRect; - markedText[0] = 0; + SDL_Window *window = SDL_GetWindowFromID(windowID); for (i = 0; i < state->num_windows; ++i) { - SDL_StartTextInput(state->windows[i]); + if (windowstate[i].window == window) { + return &windowstate[i]; + } } + return NULL; +} + +static void InitInput(WindowState *ctx) +{ + /* Prepare a rect for text input */ + ctx->textRect.x = ctx->textRect.y = 100.0f; + ctx->textRect.w = DEFAULT_WINDOW_WIDTH - 2 * ctx->textRect.x; + ctx->textRect.h = 50.0f; + ctx->markedRect = ctx->textRect; + + SDL_StartTextInput(ctx->window); } -static void ClearCandidates(void) +static void ClearCandidates(WindowState *ctx) { int i; - for (i = 0; i < num_candidates; ++i) { - SDL_free(candidates[i]); + for (i = 0; i < ctx->num_candidates; ++i) { + SDL_free(ctx->candidates[i]); } - SDL_free(candidates); - candidates = NULL; - num_candidates = 0; + SDL_free(ctx->candidates); + ctx->candidates = NULL; + ctx->num_candidates = 0; } -static void SaveCandidates(SDL_Event *event) +static void SaveCandidates(WindowState *ctx, SDL_Event *event) { int i; - ClearCandidates(); + ClearCandidates(ctx); - num_candidates = event->edit_candidates.num_candidates; - if (num_candidates > 0) { - candidates = (char **)SDL_malloc(num_candidates * sizeof(*candidates)); - if (!candidates) { - num_candidates = 0; + ctx->num_candidates = event->edit_candidates.num_candidates; + if (ctx->num_candidates > 0) { + ctx->candidates = (char **)SDL_malloc(ctx->num_candidates * sizeof(*ctx->candidates)); + if (!ctx->candidates) { + ctx->num_candidates = 0; return; } - for (i = 0; i < num_candidates; ++i) { - candidates[i] = SDL_strdup(event->edit_candidates.candidates[i]); + for (i = 0; i < ctx->num_candidates; ++i) { + ctx->candidates[i] = SDL_strdup(event->edit_candidates.candidates[i]); } - selected_candidate = event->edit_candidates.selected_candidate; - horizontal_candidates = event->edit_candidates.horizontal; + ctx->selected_candidate = event->edit_candidates.selected_candidate; + ctx->horizontal_candidates = event->edit_candidates.horizontal; } } -static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) +static void DrawCandidates(WindowState *ctx, SDL_FRect *cursorRect) { - SDL_Renderer *renderer = state->renderers[rendererID]; + SDL_Renderer *renderer = ctx->renderer; + int rendererID = ctx->rendererID; int i; int output_w = 0, output_h = 0; float w = 0.0f, h = 0.0f; SDL_FRect candidatesRect, dstRect, underlineRect; - if (num_candidates == 0) { + if (ctx->num_candidates == 0) { return; } /* Calculate the size of the candidate list */ - for (i = 0; i < num_candidates; ++i) { - if (!candidates[i]) { + for (i = 0; i < ctx->num_candidates; ++i) { + if (!ctx->candidates[i]) { continue; } #ifdef HAVE_SDL_TTF /* FIXME */ #else - if (horizontal_candidates) { - const char *utext = candidates[i]; + if (ctx->horizontal_candidates) { + const char *utext = ctx->candidates[i]; Uint32 codepoint; size_t len; float advance = 0.0f; @@ -544,7 +562,7 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) w += advance; h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; } else { - const char *utext = candidates[i]; + const char *utext = ctx->candidates[i]; Uint32 codepoint; size_t len; float advance = 0.0f; @@ -584,8 +602,8 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) /* Draw the candidates */ dstRect.x = candidatesRect.x + 3.0f; dstRect.y = candidatesRect.y + 3.0f; - for (i = 0; i < num_candidates; ++i) { - if (!candidates[i]) { + for (i = 0; i < ctx->num_candidates; ++i) { + if (!ctx->candidates[i]) { continue; } @@ -595,8 +613,8 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) dstRect.w = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; dstRect.h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; - if (horizontal_candidates) { - const char *utext = candidates[i]; + if (ctx->horizontal_candidates) { + const char *utext = ctx->candidates[i]; Uint32 codepoint; size_t len; float start; @@ -611,7 +629,7 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) utext += len; } - if (i == selected_candidate) { + if (i == ctx->selected_candidate) { underlineRect.x = start; underlineRect.y = dstRect.y + dstRect.h - 2; underlineRect.h = 2; @@ -621,7 +639,7 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) SDL_RenderFillRect(renderer, &underlineRect); } } else { - const char *utext = candidates[i]; + const char *utext = ctx->candidates[i]; Uint32 codepoint; size_t len; float start; @@ -634,7 +652,7 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) utext += len; } - if (i == selected_candidate) { + if (i == ctx->selected_candidate) { underlineRect.x = start; underlineRect.y = dstRect.y + dstRect.h - 2; underlineRect.h = 2; @@ -653,22 +671,28 @@ static void DrawCandidates(int rendererID, SDL_FRect *cursorRect) } } -static void UpdateTextInputArea(SDL_Window *window, const SDL_FRect *cursorRect) +static void UpdateTextInputArea(WindowState *ctx, const SDL_FRect *cursorRect) { SDL_Rect rect; - int cursor_offset = (int)(cursorRect->x - textRect.x); + int cursor_offset = (int)(cursorRect->x - ctx->textRect.x); - rect.x = (int)textRect.x; - rect.y = (int)textRect.y; - rect.w = (int)textRect.w; - rect.h = (int)textRect.h; - SDL_SetTextInputArea(window, &rect, cursor_offset); + rect.x = (int)ctx->textRect.x; + rect.y = (int)ctx->textRect.y; + rect.w = (int)ctx->textRect.w; + rect.h = (int)ctx->textRect.h; + SDL_SetTextInputArea(ctx->window, &rect, cursor_offset); } static void CleanupVideo(void) { - SDL_StopTextInput(state->windows[0]); - ClearCandidates(); + int i; + + for (i = 0; i < state->num_windows; ++i) { + WindowState *ctx = &windowstate[i]; + + SDL_StopTextInput(ctx->window); + ClearCandidates(ctx); + } #ifdef HAVE_SDL_TTF TTF_CloseFont(font); TTF_Quit(); @@ -677,27 +701,28 @@ static void CleanupVideo(void) #endif } -static void RedrawWindow(int rendererID) +static void RedrawWindow(WindowState *ctx) { - SDL_Renderer *renderer = state->renderers[rendererID]; + SDL_Renderer *renderer = ctx->renderer; + int rendererID = ctx->rendererID; SDL_FRect drawnTextRect, cursorRect, underlineRect; SDL_SetRenderDrawColor(renderer, backColor.r, backColor.g, backColor.b, backColor.a); - SDL_RenderFillRect(renderer, &textRect); + SDL_RenderFillRect(renderer, &ctx->textRect); /* Initialize the drawn text rectangle for the cursor */ - drawnTextRect.x = textRect.x; - drawnTextRect.y = textRect.y + (textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; + drawnTextRect.x = ctx->textRect.x; + drawnTextRect.y = ctx->textRect.y + (ctx->textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; drawnTextRect.w = 0.0f; drawnTextRect.h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; - if (*text) { + if (ctx->text[0]) { #ifdef HAVE_SDL_TTF - SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, text, textColor); + SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, ctx->text, textColor); SDL_Texture *texture; /* Vertically center text */ - drawnTextRect.y = textRect.y + (textRect.h - textSur->h) / 2; + drawnTextRect.y = ctx->textRect.y + (ctx->textRect.h - textSur->h) / 2; drawnTextRect.w = textSur->w; drawnTextRect.h = textSur->h; @@ -707,13 +732,13 @@ static void RedrawWindow(int rendererID) SDL_RenderTexture(renderer, texture, NULL, &drawnTextRect); SDL_DestroyTexture(texture); #else - char *utext = text; + char *utext = ctx->text; Uint32 codepoint; size_t len; SDL_FRect dstrect; - dstrect.x = textRect.x; - dstrect.y = textRect.y + (textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; + dstrect.x = ctx->textRect.x; + dstrect.y = ctx->textRect.y + (ctx->textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; dstrect.w = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; dstrect.h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; drawnTextRect.y = dstrect.y; @@ -729,14 +754,14 @@ static void RedrawWindow(int rendererID) } /* The marked text rectangle is the text area that hasn't been filled by committed text */ - markedRect.x = textRect.x + drawnTextRect.w; - markedRect.w = textRect.w - drawnTextRect.w; - if (markedRect.w < 0) { + ctx->markedRect.x = ctx->textRect.x + drawnTextRect.w; + ctx->markedRect.w = ctx->textRect.w - drawnTextRect.w; + if (ctx->markedRect.w < 0) { /* Stop text input because we cannot hold any more characters */ - SDL_StopTextInput(state->windows[0]); + SDL_StopTextInput(ctx->window); return; } else { - SDL_StartTextInput(state->windows[0]); + SDL_StartTextInput(ctx->window); } /* Update the drawn text rectangle for composition text, after the committed text */ @@ -748,26 +773,26 @@ static void RedrawWindow(int rendererID) cursorRect.w = 2; cursorRect.h = drawnTextRect.h; - if (markedText[0]) { + if (ctx->markedText[0]) { #ifdef HAVE_SDL_TTF SDL_Surface *textSur; SDL_Texture *texture; - if (cursor) { - char *p = utf8_advance(markedText, cursor); + if (ctx->cursor) { + char *p = utf8_advance(ctx->markedText, ctx->cursor); char c = 0; if (!p) { - p = &markedText[SDL_strlen(markedText)]; + p = &ctx->markedText[SDL_strlen(ctx->markedText)]; } c = *p; *p = 0; - TTF_SizeUTF8(font, markedText, &drawnTextRect.w, NULL); + TTF_SizeUTF8(font, ctx->markedText, &drawnTextRect.w, NULL); cursorRect.x += drawnTextRect.w; *p = c; } - textSur = TTF_RenderUTF8_Blended(font, markedText, textColor); + textSur = TTF_RenderUTF8_Blended(font, ctx->markedText, textColor); /* Vertically center text */ - drawnTextRect.y = textRect.y + (textRect.h - textSur->h) / 2; + drawnTextRect.y = ctx->textRect.y + (ctx->textRect.h - textSur->h) / 2; drawnTextRect.w = textSur->w; drawnTextRect.h = textSur->h; @@ -777,19 +802,19 @@ static void RedrawWindow(int rendererID) SDL_RenderTexture(renderer, texture, NULL, &drawnTextRect); SDL_DestroyTexture(texture); - if (cursor_length > 0) { + if (ctx->cursor_length > 0) { /* FIXME: Need to measure text extents */ - cursorRect.w = cursor_length * UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; + cursorRect.w = ctx->cursor_length * UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; } #else int i = 0; - char *utext = markedText; + char *utext = ctx->markedText; Uint32 codepoint; size_t len; SDL_FRect dstrect; dstrect.x = drawnTextRect.x; - dstrect.y = textRect.y + (textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; + dstrect.y = ctx->textRect.y + (ctx->textRect.h - UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE) / 2; dstrect.w = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; dstrect.h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; drawnTextRect.y = dstrect.y; @@ -799,22 +824,22 @@ static void RedrawWindow(int rendererID) float advance = unifont_draw_glyph(codepoint, rendererID, &dstrect) * UNIFONT_DRAW_SCALE; dstrect.x += advance; drawnTextRect.w += advance; - if (i < cursor) { + if (i < ctx->cursor) { cursorRect.x += advance; } i++; utext += len; } - if (cursor_length > 0) { - cursorRect.w = cursor_length * UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; + if (ctx->cursor_length > 0) { + cursorRect.w = ctx->cursor_length * UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; } #endif cursorRect.y = drawnTextRect.y; cursorRect.h = drawnTextRect.h; - underlineRect = markedRect; + underlineRect = ctx->markedRect; underlineRect.y = drawnTextRect.y + drawnTextRect.h - 2; underlineRect.h = 2; underlineRect.w = drawnTextRect.w; @@ -825,26 +850,26 @@ static void RedrawWindow(int rendererID) /* Draw the cursor */ Uint64 now = SDL_GetTicks(); - if ((now - last_cursor_change) >= CURSOR_BLINK_INTERVAL_MS) { - cursor_visible = !cursor_visible; - last_cursor_change = now; + if ((now - ctx->last_cursor_change) >= CURSOR_BLINK_INTERVAL_MS) { + ctx->cursor_visible = !ctx->cursor_visible; + ctx->last_cursor_change = now; } - if (cursor_length > 0) { + if (ctx->cursor_length > 0) { /* We'll show a highlight */ SDL_SetRenderDrawBlendMode(renderer, highlight_mode); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderFillRect(renderer, &cursorRect); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); - } else if (cursor_visible) { + } else if (ctx->cursor_visible) { SDL_SetRenderDrawColor(renderer, lineColor.r, lineColor.g, lineColor.b, lineColor.a); SDL_RenderFillRect(renderer, &cursorRect); } /* Draw the candidates */ - DrawCandidates(rendererID, &cursorRect); + DrawCandidates(ctx, &cursorRect); /* Update the area used to draw composition UI */ - UpdateTextInputArea(state->windows[0], &cursorRect); + UpdateTextInputArea(ctx, &cursorRect); } static void Redraw(void) @@ -858,8 +883,7 @@ static void Redraw(void) SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); - /* Sending in the window id to let the font renderers know which one we're working with. */ - RedrawWindow(i); + RedrawWindow(&windowstate[i]); SDL_RenderPresent(renderer); } @@ -920,6 +944,12 @@ int main(int argc, char *argv[]) return 2; } + windowstate = (WindowState *)SDL_calloc(state->num_windows, sizeof(*windowstate)); + if (!windowstate) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate window state: %s\n", SDL_GetError()); + return -1; + } + fontname = GetResourceFilename(fontname, DEFAULT_FONT); #ifdef HAVE_SDL_TTF @@ -939,10 +969,18 @@ int main(int argc, char *argv[]) SDL_Log("Using font: %s\n", fontname); - InitInput(); - /* Create the windows and initialize the renderers */ + /* Initialize window state */ for (i = 0; i < state->num_windows; ++i) { + WindowState *ctx = &windowstate[i]; + SDL_Window *window = state->windows[i]; SDL_Renderer *renderer = state->renderers[i]; + + ctx->window = window; + ctx->renderer = renderer; + ctx->rendererID = i; + + InitInput(ctx); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF); SDL_RenderClear(renderer); @@ -961,33 +999,38 @@ int main(int argc, char *argv[]) while (SDL_PollEvent(&event)) { SDLTest_CommonEvent(state, &event, &done); switch (event.type) { - case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_DOWN: { + WindowState *ctx = GetWindowStateForWindowID(event.key.windowID); + if (!ctx) { + break; + } + switch (event.key.key) { case SDLK_RETURN: - text[0] = 0x00; + ctx->text[0] = 0x00; break; case SDLK_BACKSPACE: /* Only delete text if not in editing mode. */ - if (!markedText[0]) { - size_t textlen = SDL_strlen(text); + if (!ctx->markedText[0]) { + size_t textlen = SDL_strlen(ctx->text); do { if (textlen == 0) { break; } - if (!(text[textlen - 1] & 0x80)) { + if (!(ctx->text[textlen - 1] & 0x80)) { /* One byte */ - text[textlen - 1] = 0x00; + ctx->text[textlen - 1] = 0x00; break; } - if ((text[textlen - 1] & 0xC0) == 0x80) { + if ((ctx->text[textlen - 1] & 0xC0) == 0x80) { /* Byte from the multibyte sequence */ - text[textlen - 1] = 0x00; + ctx->text[textlen - 1] = 0x00; textlen--; } - if ((text[textlen - 1] & 0xC0) == 0xC0) { + if ((ctx->text[textlen - 1] & 0xC0) == 0xC0) { /* First byte of multibyte sequence */ - text[textlen - 1] = 0x00; + ctx->text[textlen - 1] = 0x00; break; } } while (1); @@ -1007,44 +1050,59 @@ int main(int argc, char *argv[]) SDL_static_cast(Uint32, event.key.key), SDL_GetKeyName(event.key.key)); break; + } + case SDL_EVENT_TEXT_INPUT: { + WindowState *ctx = GetWindowStateForWindowID(event.text.windowID); + if (!ctx) { + break; + } - case SDL_EVENT_TEXT_INPUT: - if (event.text.text[0] == '\0' || event.text.text[0] == '\n' || markedRect.w < 0) { + if (event.text.text[0] == '\0' || event.text.text[0] == '\n' || ctx->markedRect.w < 0) { break; } SDL_Log("Keyboard: text input \"%s\"\n", event.text.text); - if (SDL_strlen(text) + SDL_strlen(event.text.text) < sizeof(text)) { - SDL_strlcat(text, event.text.text, sizeof(text)); + if (SDL_strlen(ctx->text) + SDL_strlen(event.text.text) < sizeof(ctx->text)) { + SDL_strlcat(ctx->text, event.text.text, sizeof(ctx->text)); } - SDL_Log("text inputted: %s\n", text); + SDL_Log("text inputted: %s\n", ctx->text); /* After text inputted, we can clear up markedText because it */ /* is committed */ - markedText[0] = 0; + ctx->markedText[0] = 0; break; + } + case SDL_EVENT_TEXT_EDITING: { + WindowState *ctx = GetWindowStateForWindowID(event.edit.windowID); + if (!ctx) { + break; + } - case SDL_EVENT_TEXT_EDITING: SDL_Log("text editing \"%s\", selected range (%" SDL_PRIs32 ", %" SDL_PRIs32 ")\n", event.edit.text, event.edit.start, event.edit.length); - SDL_strlcpy(markedText, event.edit.text, sizeof(markedText)); - cursor = event.edit.start; - cursor_length = event.edit.length; + SDL_strlcpy(ctx->markedText, event.edit.text, sizeof(ctx->markedText)); + ctx->cursor = event.edit.start; + ctx->cursor_length = event.edit.length; break; + } + case SDL_EVENT_TEXT_EDITING_CANDIDATES: { + WindowState *ctx = GetWindowStateForWindowID(event.edit.windowID); + if (!ctx) { + break; + } - case SDL_EVENT_TEXT_EDITING_CANDIDATES: SDL_Log("text candidates:\n"); for (i = 0; i < event.edit_candidates.num_candidates; ++i) { SDL_Log("%c%s\n", i == event.edit_candidates.selected_candidate ? '>' : ' ', event.edit_candidates.candidates[i]); } - ClearCandidates(); - SaveCandidates(&event); + ClearCandidates(ctx); + SaveCandidates(ctx, &event); break; - + } default: break; }