From 8bd5e66f464bf12b34d1f231873a318fdda4984e Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Thu, 11 Apr 2024 20:16:40 +1000 Subject: [PATCH] Dreamcast: Initial WIP on showing on-screen keyboard when no keyboard connected --- src/Bitmap.c | 41 ++++++++++--------- src/Launcher.c | 2 + src/VirtualKeyboard.h | 91 +++++++++++++++++++++++++++++++++++------- src/Window.h | 2 + src/Window_Dreamcast.c | 20 ++++++++-- 5 files changed, 118 insertions(+), 38 deletions(-) diff --git a/src/Bitmap.c b/src/Bitmap.c index 0d30ffe26..562566647 100644 --- a/src/Bitmap.c +++ b/src/Bitmap.c @@ -303,7 +303,7 @@ static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) { return NULL; } -/* Sets alpha to 0 for any pixels in the bitmap whose RGB is same as col */ +/* Sets alpha to 0 for any pixels in the bitmap whose RGB is same as colorspace */ static void ComputeTransparency(struct Bitmap* bmp, BitmapCol col) { BitmapCol trnsRGB = col & BITMAPCOLOR_RGB_MASK; int x, y, width = bmp->width, height = bmp->height; @@ -324,27 +324,27 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* header variables */ static const cc_uint8 samplesPerPixel[] = { 1, 0, 3, 1, 2, 0, 4 }; - cc_uint8 col, bitsPerSample, bytesPerPixel = 0; + cc_uint8 colorspace, bitsPerSample, bytesPerPixel = 0; Png_RowExpander rowExpander = NULL; cc_uint32 scanlineSize = 0, scanlineBytes = 0; /* palette data */ - BitmapCol trnsCol; + BitmapCol trnsColor; BitmapCol palette[PNG_PALETTE]; cc_uint32 i; /* idat state */ cc_uint32 begY, rowY = 0, endY; cc_uint8 buffer[PNG_PALETTE * 3]; - cc_uint32 bufferIdx, bufferLen; - cc_uint32 read, left; + cc_uint32 read, bufferIdx = 0; + cc_uint32 left, bufferLen = 0; int curY; /* idat decompressor */ struct InflateState inflate; struct Stream compStream, datStream; struct ZLibHeader zlibHeader; - cc_uint8* data; + cc_uint8* data = NULL; bmp->width = 0; bmp->height = 0; bmp->scan0 = NULL; @@ -353,8 +353,8 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { if (res) return res; if (!Png_Detect(tmp, PNG_SIG_SIZE)) return PNG_ERR_INVALID_SIG; - col = 0xFF; /* Unknown colour space */ - trnsCol = BITMAPCOLOR_BLACK; + colorspace = 0xFF; /* Unknown colour space */ + trnsColor = BITMAPCOLOR_BLACK; for (i = 0; i < PNG_PALETTE; i++) { palette[i] = BITMAPCOLOR_BLACK; } Inflate_MakeStream2(&compStream, &inflate, stream); @@ -378,25 +378,24 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { if (bmp->width < 0 || bmp->width > PNG_MAX_DIMS) return PNG_ERR_TOO_WIDE; if (bmp->height < 0 || bmp->height > PNG_MAX_DIMS) return PNG_ERR_TOO_TALL; - bitsPerSample = tmp[8]; col = tmp[9]; + bitsPerSample = tmp[8]; colorspace = tmp[9]; if (bitsPerSample == 16) return PNG_ERR_16BITSAMPLES; - rowExpander = Png_GetExpander(col, bitsPerSample); + rowExpander = Png_GetExpander(colorspace, bitsPerSample); if (!rowExpander) return PNG_ERR_INVALID_COL_BPP; if (tmp[10] != 0) return PNG_ERR_COMP_METHOD; if (tmp[11] != 0) return PNG_ERR_FILTER; if (tmp[12] != 0) return PNG_ERR_INTERLACED; - bytesPerPixel = ((samplesPerPixel[col] * bitsPerSample) + 7) >> 3; - scanlineSize = ((samplesPerPixel[col] * bitsPerSample * bmp->width) + 7) >> 3; + bytesPerPixel = ((samplesPerPixel[colorspace] * bitsPerSample) + 7) >> 3; + scanlineSize = ((samplesPerPixel[colorspace] * bitsPerSample * bmp->width) + 7) >> 3; scanlineBytes = scanlineSize + 1; /* Add 1 byte for filter byte of each scanline */ data = Mem_TryAlloc(bmp->height, max(scanlineBytes, bmp->width * 4)); bmp->scan0 = (BitmapCol*)data; if (!bmp->scan0) return ERR_OUT_OF_MEMORY; - bufferIdx = 0; bufferLen = bmp->height * scanlineBytes; } break; @@ -418,15 +417,15 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* 11.3.2.1 tRNS Transparency */ case PNG_FourCC('t','R','N','S'): { - if (col == PNG_COLOR_GRAYSCALE) { + if (colorspace == PNG_COLOR_GRAYSCALE) { if (dataSize != 2) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); if (res) return res; /* RGB is always two bytes */ - trnsCol = BitmapCol_Make(buffer[1], buffer[1], buffer[1], 0); - } else if (col == PNG_COLOR_INDEXED) { + trnsColor = BitmapCol_Make(buffer[1], buffer[1], buffer[1], 0); + } else if (colorspace == PNG_COLOR_INDEXED) { if (dataSize > PNG_PALETTE) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); @@ -437,14 +436,14 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { palette[i] &= BITMAPCOLOR_RGB_MASK; /* set A to 0 */ palette[i] |= buffer[i] << BITMAPCOLOR_A_SHIFT; } - } else if (col == PNG_COLOR_RGB) { + } else if (colorspace == PNG_COLOR_RGB) { if (dataSize != 6) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); if (res) return res; /* R,G,B are always two bytes */ - trnsCol = BitmapCol_Make(buffer[1], buffer[3], buffer[5], 0); + trnsColor = BitmapCol_Make(buffer[1], buffer[3], buffer[5], 0); } else { return PNG_ERR_TRANS_INVALID; } @@ -488,7 +487,7 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* With the RGBA colourspace, each scanline is (1 + width*4) bytes wide */ /* Therefore once a row has been reconstructed, the prior row can be converted */ /* immediately into the destination colour format */ - if (col == PNG_COLOR_RGB_A) { + if (colorspace == PNG_COLOR_RGB_A) { /* Prior line is no longer needed and can be overwritten now */ rowExpander(bmp->width, palette, &prior[1], Bitmap_GetRow(bmp, rowY - 1)); /* Current line is also no longer needed and can be overwritten now */ @@ -505,14 +504,14 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* Therefore image expansion can only be done after all the rows have been reconstructed, and must */ /* be done backwards to avoid overwriting any source data that has yet to be processed */ /* This is slightly slower, but the majority of images ClassiCube encounters are RGBA anyways */ - if (col != PNG_COLOR_RGB_A) { + if (colorspace != PNG_COLOR_RGB_A) { for (curY = bmp->height - 1; curY >= 0; curY--) { cc_uint8* scanline = &data[curY * scanlineBytes]; rowExpander(bmp->width, palette, &scanline[1], Bitmap_GetRow(bmp, curY)); } } - if (!BitmapCol_A(trnsCol)) ComputeTransparency(bmp, trnsCol); + if (!BitmapCol_A(trnsColor)) ComputeTransparency(bmp, trnsColor); return 0; } break; diff --git a/src/Launcher.c b/src/Launcher.c index 18c939cbc..2b52df964 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -187,6 +187,8 @@ static cc_bool IsShutdown(int key) { } static void OnInputDown(void* obj, int key, cc_bool was) { + if (Window_Main.SoftKeyboardFocus) return; + if (IsShutdown(key)) Launcher_ShouldExit = true; Launcher_Active->KeyDown(Launcher_Active, key, was); } diff --git a/src/VirtualKeyboard.h b/src/VirtualKeyboard.h index 2c96483cf..f329bad23 100644 --- a/src/VirtualKeyboard.h +++ b/src/VirtualKeyboard.h @@ -1,16 +1,22 @@ #include "Core.h" -#include "Drawer2D.h" #include "Funcs.h" +#include "Drawer2D.h" +#include "Event.h" +#include "ExtMath.h" #include "String.h" +#include "Input.h" +#include "Utils.h" +#include "LBackend.h" #include "Window.h" -static cc_bool kb_inited, kb_showing, kb_shift; +static cc_bool kb_inited, kb_showing, kb_shift, kb_needsHook; static struct FontDesc kb_font; static int kb_selected; static const char** kb_table; static float kb_padXAcc, kb_padYAcc; static char kb_buffer[512]; static cc_string kb_str = String_FromArray(kb_buffer); +static void (*KB_MarkDirty)(void); #define KB_CELLS_PER_ROW 13 #define KB_LAST_CELL (KB_CELLS_PER_ROW - 1) @@ -24,15 +30,15 @@ static cc_string kb_str = String_FromArray(kb_buffer); static const char* kb_table_lower[] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\x11 BSP", - "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "(", ")", "& ", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace", + "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "(", ")", "& ", "a", "s", "d", "f", "g", "h", "j", "k", "l", "?", ";", "'", "Enter", "z", "x", "c", "v", "b", "n", "m", ".", ",","\\", "!", "@", "/ ", "Caps", "Shift", "Space" }; static const char* kb_table_upper[] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "< BSP", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace", "Q", "W", "E", "R", "T", "Y", "U", "I", "o", "p", "(", ")", "& ", "A", "S", "D", "F", "G", "H", "J", "K", "L", "?", ";", "'", "Enter", "Z", "X", "C", "V", "B", "N", "M", ".", ",","\\", "!", "@", "/ ", @@ -49,7 +55,7 @@ static void VirtualKeyboard_Init(void) { } static int VirtualKeyboard_Width(void) { - return (KB_CELLS_PER_ROW + 4) * KB_TILE_SIZE; + return (KB_CELLS_PER_ROW + 3) * KB_TILE_SIZE; } static int VirtualKeyboard_Height(void) { @@ -57,6 +63,7 @@ static int VirtualKeyboard_Height(void) { } static void VirtualKeyboard_Close(void); +static void VirtualKeyboard_Hook(void); #define KB_NORMAL_COLOR BitmapCol_Make(0x7F, 0x7F, 0x7F, 0x90) @@ -69,6 +76,7 @@ static void VirtualKeyboard_Draw(struct Context2D* ctx) { int row, cell; int i, x, y, w, h, dx, dy; Drawer2D.Colors['f'] = Drawer2D.Colors['0']; + if (kb_needsHook) VirtualKeyboard_Hook(); for (row = 0, y = 0; row < KB_TOTAL_ROWS; row++) { @@ -103,15 +111,16 @@ static void VirtualKeyboard_Scroll(int delta) { kb_selected += delta; if (kb_selected < 0) kb_selected += KB_TOTAL_CHARS; Math_Clamp(kb_selected, 0, KB_TOTAL_CHARS - 1); + KB_MarkDirty(); } static void VirtualKeyboard_ToggleTable(void) { kb_table = kb_table == kb_table_lower ? kb_table_upper : kb_table_lower; + KB_MarkDirty(); } static void VirtualKeyboard_AppendChar(char c) { String_Append(&kb_str, c); - Platform_Log1("%s", &kb_str); Event_RaiseString(&InputEvents.TextChanged, &kb_str); if (kb_shift) { VirtualKeyboard_ToggleTable(); kb_shift = false; } @@ -125,8 +134,10 @@ static void VirtualKeyboard_ClickSelected(void) { case KB_INDEX(KB_LAST_CELL, 0): if (kb_str.length) kb_str.length--; Event_RaiseString(&InputEvents.TextChanged, &kb_str); + KB_MarkDirty(); break; case KB_INDEX(KB_LAST_CELL, 2): + KB_MarkDirty(); Window_CloseKeyboard(); Input_SetPressed(CCKEY_ENTER); Input_SetReleased(CCKEY_ENTER); @@ -158,8 +169,10 @@ static void VirtualKeyboard_ProcessDown(void* obj, int key, cc_bool was) { VirtualKeyboard_Scroll(-KB_CELLS_PER_ROW); } else if (Input_IsDownButton(key)) { VirtualKeyboard_Scroll(+KB_CELLS_PER_ROW); - } else if (Input_IsEnterButton(key) || key == CCPAD_A) { + } else if (Input_IsEnterButton(key) || key == CCPAD_A) { VirtualKeyboard_ClickSelected(); + } else if (Input_IsEscapeButton(key) || key == CCPAD_B) { + VirtualKeyboard_Close(); } } @@ -174,10 +187,28 @@ static void VirtualKeyboard_PadAxis(void* obj, int axis, float x, float y) { } -static void VirtualKeyboard_Display(struct OpenKeyboardArgs* args) { +static void VirtualKeyboard_MarkDirty2D(void) { + LBackend_Redraw(); +} + +static void VirtualKeyboard_MarkDirty3D(void) { + /* TODO */ +} + +static void VirtualKeyboard_Hook(void) { + /* Don't hook immediately into events, otherwise the initial up/down press that opened */ + /* the virtual keyboard in the first place gets mistakenly processed */ + kb_needsHook = false; + + Event_Register_(&InputEvents.Down, NULL, VirtualKeyboard_ProcessDown); + Event_Register_(&ControllerEvents.AxisUpdate, NULL, VirtualKeyboard_PadAxis); +} + +static void VirtualKeyboard_Open(struct OpenKeyboardArgs* args, cc_bool launcher) { VirtualKeyboard_Close(); VirtualKeyboard_Init(); - kb_showing = true; + kb_showing = true; + kb_needsHook = true; kb_table = kb_table_lower; kb_selected = -1; @@ -187,9 +218,14 @@ static void VirtualKeyboard_Display(struct OpenKeyboardArgs* args) { kb_str.length = 0; if (args) String_AppendConst(&kb_str, args->placeholder); - - Event_Register_(&InputEvents.Down, NULL, VirtualKeyboard_ProcessDown); - Event_Register_(&ControllerEvents.AxisUpdate, NULL, VirtualKeyboard_PadAxis); + + if (launcher) { + KB_MarkDirty = VirtualKeyboard_MarkDirty2D; + } else { + KB_MarkDirty = VirtualKeyboard_MarkDirty3D; + } + + Window_Main.SoftKeyboardFocus = true; } static void VirtualKeyboard_SetText(const cc_string* text) { @@ -197,7 +233,34 @@ static void VirtualKeyboard_SetText(const cc_string* text) { } static void VirtualKeyboard_Close(void) { + if (KB_MarkDirty) KB_MarkDirty(); Event_Unregister_(&InputEvents.Down, NULL, VirtualKeyboard_ProcessDown); Event_Unregister_(&ControllerEvents.AxisUpdate, NULL, VirtualKeyboard_PadAxis); - kb_showing = false; + Window_Main.SoftKeyboardFocus = false; + + KB_MarkDirty = NULL; + kb_showing = false; + kb_needsHook = false; +} + +static void VirtualKeyboard_Display2D(Rect2D* r, struct Bitmap* bmp) { + struct Context2D ctx; + struct Bitmap copy = *bmp; + int x, y; + + /* Mark entire framebuffer as needing to be redrawn */ + r->x = 0; r->width = bmp->width; + r->y = 0; r->height = bmp->height; + + /* Draw virtual keyboard at centre of window bottom */ + y = bmp->height - 1 - VirtualKeyboard_Height(); + y = max(y, 0); + copy.scan0 = Bitmap_GetRow(bmp, y); + + x = (bmp->width - VirtualKeyboard_Width()) / 2; + x = max(x, 0); + copy.scan0 += x; + + Context2D_Wrap(&ctx, ©); + VirtualKeyboard_Draw(&ctx); } \ No newline at end of file diff --git a/src/Window.h b/src/Window.h index d87db6a47..4224c5ced 100644 --- a/src/Window.h +++ b/src/Window.h @@ -84,6 +84,8 @@ struct _WindowData { /* Whether this window is backgrounded / inactivated */ /* (rendering is not performed when window is inactive) */ cc_bool Inactive; + /* Whether input should be ignored due to soft keyboard being open */ + cc_bool SoftKeyboardFocus; }; /* Data for the game/launcher window */ diff --git a/src/Window_Dreamcast.c b/src/Window_Dreamcast.c index 576e72a8e..076c163ea 100644 --- a/src/Window_Dreamcast.c +++ b/src/Window_Dreamcast.c @@ -10,6 +10,7 @@ #include "Bitmap.h" #include "Errors.h" #include "ExtMath.h" +#include "VirtualKeyboard.h" #include static cc_bool launcherMode; cc_bool window_inited; @@ -31,6 +32,7 @@ void Window_Init(void) { Input.Sources = INPUT_SOURCE_GAMEPAD; DisplayInfo.ContentOffsetX = 10; DisplayInfo.ContentOffsetY = 20; + Window_Main.SoftKeyboard = SOFT_KEYBOARD_VIRTUAL; vid_set_mode(DEFAULT_VID_MODE, DEFAULT_PIXEL_MODE); vid_flip(0); @@ -262,6 +264,7 @@ void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) { // https://dcemulation.org/phpBB/viewtopic.php?t=99999 // https://dcemulation.org/phpBB/viewtopic.php?t=43214 vid_waitvbl(); + if (kb_showing) VirtualKeyboard_Display2D(&r, bmp); for (int y = r.y; y < r.y + r.height; y++) { @@ -285,9 +288,20 @@ void Window_FreeFramebuffer(struct Bitmap* bmp) { /*########################################################################################################################* *------------------------------------------------------Soft keyboard------------------------------------------------------* *#########################################################################################################################*/ -void Window_OpenKeyboard(struct OpenKeyboardArgs* args) { /* TODO implement */ } -void Window_SetKeyboardText(const cc_string* text) { } -void Window_CloseKeyboard(void) { /* TODO implement */ } +void Window_OpenKeyboard(struct OpenKeyboardArgs* args) { + if (Input.Sources & INPUT_SOURCE_NORMAL) return; + VirtualKeyboard_Open(args, launcherMode); +} + +void Window_SetKeyboardText(const cc_string* text) { + if (!kb_showing) return; + VirtualKeyboard_SetText(text); +} + +void Window_CloseKeyboard(void) { + if (Input.Sources & INPUT_SOURCE_NORMAL) return; + VirtualKeyboard_Close(); +} /*########################################################################################################################*