diff --git a/includes/tstring.h b/includes/tstring.h new file mode 100644 index 0000000..6177710 --- /dev/null +++ b/includes/tstring.h @@ -0,0 +1,24 @@ +#ifndef TSTRING_H +#define TSTRING_H + +#ifndef DEFAULT_TSTRING_BYTE_BUFFER +#define DEFAULT_TSTRING_BYTE_BUFFER 32 +#endif + +typedef struct tstring TString; + +TString* TStringCreate(void); +void TStringTruncate(TString* string); +void TStringAppendText(const char* text, TString* string); +void TStringGetGlyph(int index, char buffer[8], const TString* string); +void TStringFree(TString* string); +void TStringPrintDetails(const TString* string, int statsOnly); +void TStringInsertText(const char* text, int index, TString* string); +void TStringPop(char buffer[8], TString* string); +void TStringRemoveRange(int startIndex, int count, TString* string); +void TStringGetStringRange(const TString* string, unsigned int startIndex, unsigned int count, unsigned int bufferSize, char buffer[]); +char* TStringGetString(const TString* string); +int TStringLength(const TString* string); + + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index 00fb375..863e7a9 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,15 @@ +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include "../includes/tstring.h" int Exit = 0; int XRel = 0, YRel = 0; @@ -10,18 +18,25 @@ int XOrg = 0, YOrg = 0; typedef struct { SDL_Window *window; SDL_Renderer *renderer; - unsigned short DefaultFontSize; + TTF_Font* DefaultFont; } Client; Client app; -const int SCREEN_WIDTH = 1280; -const int SCREEN_HEIGHT = 720; +const int SCREEN_WIDTH = 100; +const int SCREEN_HEIGHT = 600; +Uint32 KeyPress; +int x = 0; +int y = 0; +SDL_Rect FontSize; +TString* TextBuffer; void HandleInput(void); +void NewLine(void); void Cleanup(void); int main(int argc, char** argv){ memset(&app, 0, sizeof(app)); + TextBuffer = TStringCreate(); atexit(Cleanup); @@ -41,6 +56,16 @@ int main(int argc, char** argv){ return -1; } + TTF_Init(); + + app.DefaultFont = TTF_OpenFont("/usr/share/fonts/TTF/Hack-Regular.ttf", 26); + + TTF_SizeUTF8(app.DefaultFont, "A", &FontSize.w, &FontSize.h); + + int chars = SCREEN_WIDTH / FontSize.w; + + printf("Chars per line: %d\n", chars); + while(!Exit) { SDL_Delay(16); @@ -50,13 +75,57 @@ int main(int argc, char** argv){ SDL_RenderClear(app.renderer); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + + FontSize.x = FontSize.w * x; + + if (FontSize.x + FontSize.w > SCREEN_WIDTH){ + x = 0; + FontSize.x = 0; + + NewLine(); + } + + SDL_Color white = {255, 255, 255}; + + for(int i = 0; i < TStringLength(TextBuffer); i++) { + SDL_Rect Message_rect; //create a rect + Message_rect.x = i % chars * FontSize.w; //controls the rect's x coordinate + Message_rect.y = i / chars * FontSize.h; // controls the rect's y coordinte + Message_rect.w = FontSize.w; // controls the width of the rect + Message_rect.h = FontSize.h; // controls the height of the rect + + char buffer[8]; + TStringGetGlyph(i, buffer, TextBuffer); + SDL_Surface* sur = TTF_RenderText_Solid(app.DefaultFont, buffer, white); + SDL_Texture* Message = SDL_CreateTextureFromSurface(app.renderer, sur); + + SDL_RenderCopy(app.renderer, Message, NULL, &Message_rect); + + SDL_FreeSurface(sur); + SDL_DestroyTexture(Message); + } + + FontSize.y = y * FontSize.h; + + SDL_RenderDrawRect(app.renderer, &FontSize); + SDL_RenderPresent(app.renderer); } } +void NewLine(void) { + x = 0; + + if (FontSize.h * (y + 2) > SCREEN_HEIGHT) return; + + y++; +} + void Cleanup(void) { SDL_DestroyWindow(app.window); SDL_DestroyRenderer(app.renderer); + TTF_CloseFont(app.DefaultFont); TTF_Quit(); SDL_Quit(); } @@ -68,26 +137,24 @@ void HandleInput(void) { case SDL_QUIT: Exit = 1; break; - // case SDL_MOUSEBUTTONDOWN: - // if (event.button.button == SDL_BUTTON_RIGHT) Exit = 1; - // MouseDown = 1; - // break; - // case SDL_MOUSEBUTTONUP: - // MouseDown = 0; - // break; - // case SDL_MOUSEMOTION: - // //if (MainMenu->HitCheck(event.motion.x, event.motion.y, &MainMenu->Area)) { - // if (MouseDown){ - // XRel += event.motion.xrel; - // YRel += event.motion.yrel; - // } - // break; - // case SDL_MOUSEWHEEL: - // if (event.wheel.y > 0 && ZoomLevel < 2) ZoomLevel += .05; - // else if (event.wheel.y < 0 && ZoomLevel > 0.25) ZoomLevel -= .05; - // break; + case SDL_KEYUP: + if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE) + { + if (x == 0 && y > 0) + { + x = SCREEN_WIDTH / FontSize.w - 1; + y--; + } + else if (x > 0) x--; + + TStringPop(NULL, TextBuffer); + + printf("String: '%s'\n", TStringGetString(TextBuffer)); + } + break; case SDL_TEXTINPUT: - case SDL_KEYDOWN: + x++; + TStringAppendText(event.text.text, TextBuffer); default: break; } diff --git a/src/tstring.c b/src/tstring.c new file mode 100644 index 0000000..370534d --- /dev/null +++ b/src/tstring.c @@ -0,0 +1,328 @@ +#include "../includes/tstring.h" +#include +#include +#include +#include + +void TStringCopyGlyph(const char* glyph, int length, TString* string); +int ResizeTStringCapacity(int newTextLength, TString* string); +int GetGlyphByteWidth(char glyph); + +struct tstring { + char* Characters; + int Length; + int ByteCount; + int Capacity; +}; + +TString* TStringCreate(void) { + TString* string = malloc(sizeof(TString)); + + if (!string) { + fprintf(stderr, "Failed to malloc space for TString. %s.\n", strerror(errno)); + + return NULL; + } + + string->Characters = calloc(DEFAULT_TSTRING_BYTE_BUFFER, sizeof(char)); + + if (!string->Characters) { + fprintf(stderr, "Failed to malloc space for TString character array. %s.\n", strerror(errno)); + + free(string); + + return NULL; + } + + string->Capacity = DEFAULT_TSTRING_BYTE_BUFFER; + string->ByteCount = 0; + string->Length = 0; + + return string; +} + +void TStringTruncate(TString* string) { + if (!string) return; + + char* newBuffer = calloc(DEFAULT_TSTRING_BYTE_BUFFER, sizeof(char)); + + if (!newBuffer) { + fprintf(stderr, "Failed to calloc space during TString truncation. %s\n", strerror(errno)); + return; + } + + free(string->Characters); + + string->Characters = newBuffer; + string->Capacity = DEFAULT_TSTRING_BYTE_BUFFER; + string->ByteCount = 0; + string->Length = 0; +} + +int TStringLength(const TString* string) { + if (!string) return -1; + + return string->Length; +} + +int ResizeTStringCapacity(int additionalLength, TString* string) { + if (!string) return 0; + if (string->ByteCount + additionalLength < string->Capacity) return 1; + + unsigned int newCapacity = string->Capacity; + + while (additionalLength > newCapacity) newCapacity = string->Capacity * 2; + + char* buffer = realloc(string->Characters, string->Capacity * 2); + + if (!buffer) { + fprintf(stderr, "Failed to realloc space for text insertion. %s.\n", strerror(errno)); + + return 0; + } + + string->Characters = buffer; + string->Capacity *= 2; + + return 1; +} + +void TStringPop(char buffer[8], TString* string) { + if (!string) return; + if (string->ByteCount == 0) return; + + for (int i = string->ByteCount - 1; i >= 0; i--) { + unsigned int width = GetGlyphByteWidth(string->Characters[i]); + + if (width > 0) { + //If we've been provided a buffer, then populate it with the glyph we're removing. + if (buffer) { + memset(buffer, '\0', 8); + memcpy(buffer, &string->Characters[i], width); + } + + memset(&string->Characters[i], '\0', width); + string->Length--; + string->ByteCount -= width; + break; + } + } +} + +void TStringRemoveRange(int startIndex, int count, TString* string) { + if (!string) return; + if (startIndex < 0 || startIndex > string->Length) return; + if (count <= 0 || count > string->Length - startIndex) return; + + char buffer[256] = { 0 }; + unsigned int startByte = 0; + unsigned int glyphCount = 0; + + while(glyphCount != startIndex) { + startByte += GetGlyphByteWidth(string->Characters[startByte]); + + glyphCount++; + } + + unsigned int endByte = startByte; + + for (glyphCount = 0; glyphCount < count; glyphCount++) endByte += GetGlyphByteWidth(string->Characters[endByte]); + + unsigned int fullBuffers = (string->ByteCount - endByte) / sizeof buffer; + unsigned int remainder = (string->ByteCount - endByte) - fullBuffers * sizeof buffer; + + for (int i = 0; i < fullBuffers; i++) { + memcpy(buffer, &string->Characters[endByte + i * sizeof buffer], sizeof buffer); + memcpy(&string->Characters[startByte + sizeof buffer * i], buffer, sizeof buffer); + } + + if (remainder > 0) { + memset(buffer, 0, sizeof buffer); + memcpy(buffer, &string->Characters[string->ByteCount - remainder], remainder); + memcpy(&string->Characters[startByte + fullBuffers * sizeof buffer], buffer, remainder); + } + + string->Length -= glyphCount; + string->ByteCount -= endByte - startByte; + + memset(&string->Characters[string->ByteCount], '\0', string->Capacity - string->ByteCount); +} + +void TStringInsertText(const char* text, int index, TString* string) { + if (!text || !string) return; + if (index < 0 || index > string->Length) index = string->Length; + + unsigned int insertionTextLength = strlen(text); + unsigned int startByte = 0; + unsigned int endByte = string->ByteCount + insertionTextLength; + + if (!ResizeTStringCapacity(insertionTextLength, string)) return; + + char buffer[256] = { 0 }; + + for (int glyphCount = 0; glyphCount != index; glyphCount++) startByte += GetGlyphByteWidth(string->Characters[startByte]); + for (unsigned int offset = 0; offset < insertionTextLength; string->Length++) offset += GetGlyphByteWidth(text[offset]); + + unsigned int fullBuffers = (string->ByteCount - startByte) / sizeof buffer; + unsigned int remainder = (string->ByteCount - startByte) - fullBuffers * sizeof buffer; + + for (int i = fullBuffers; i > 0; i--) { + memcpy(buffer, &string->Characters[string->ByteCount - i * sizeof buffer], sizeof buffer); + memcpy(&string->Characters[endByte - sizeof buffer * i], buffer, sizeof buffer); + } + + if (remainder > 0) { + memcpy(buffer, &string->Characters[startByte], remainder); + memcpy(&string->Characters[insertionTextLength + startByte], buffer, remainder); + } + + memcpy(&string->Characters[startByte], text, insertionTextLength); + + string->ByteCount += insertionTextLength; +} + +void TStringAppendText(const char* text, TString* string) { + if (!text || !string) return; + + int totalLength = strlen(text); + int glyphLength = GetGlyphByteWidth(text[0]); + + //If the two lengths are the same then that means we have a single character. + //Simply append it to the list and return. + if (totalLength == glyphLength) { + TStringCopyGlyph(text, glyphLength, string); + + return; + } + + //If we've made it here, than that means we have a full on string of characters. + for(int i = 0; i < totalLength;) { + TStringCopyGlyph(&text[i], glyphLength, string); + + i += glyphLength; + glyphLength = GetGlyphByteWidth(text[i]); + } +} + +char* TStringGetString(const TString* string) { + if (!string) return NULL; + + char* text = calloc(string->ByteCount + 1, sizeof(char)); + + if (!text) { + fprintf(stderr, "Failed to calloc memory for TString text. %s.\n", strerror(errno)); + + return NULL; + } + + memcpy(text, string->Characters, string->ByteCount); + + return text; +} + +void TStringGetStringRange(const TString* string, unsigned int startIndex, unsigned int count, unsigned int bufferSize, char buffer[]) { + if (!string || !buffer) return; + if (count > string->Length) return; + + unsigned int glyphCount = 0, startByte = 0; + + while (glyphCount != startIndex) { + startByte += GetGlyphByteWidth(string->Characters[startByte]); + + glyphCount++; + } + + unsigned int endByte = startByte; + + for(glyphCount = 0; glyphCount < count && endByte <= string->ByteCount && endByte - startByte < bufferSize; glyphCount++) { + int width = GetGlyphByteWidth(string->Characters[endByte]); + + if (width + endByte - startByte < bufferSize) endByte += width; + else break; + } + + memset(buffer, 0, bufferSize); + memcpy(buffer, &string->Characters[startByte], endByte - startByte); +} + +void TStringGetGlyph(int index, char buffer[8], const TString* string) { + if (!buffer || !string) return; + if (index > TStringLength(string)) return; + + memset(buffer, '\0', sizeof *buffer); + + int glyphCount = 0; + + for (int i = 0; i < string->ByteCount;) { + int glyphWidth = GetGlyphByteWidth(string->Characters[i]); + + if (glyphCount == index) { + memcpy(buffer, &string->Characters[i], glyphWidth); + return; + } + + glyphCount++; + i += glyphWidth; + } +} + +void TStringFree(TString* string) { + if (!string) return; + + if (string->Characters) free(string->Characters); + + free(string); +} + +int GetGlyphByteWidth(char glyph) { + //ANDing 0x80 (1000 0000) should result in 0 if this code point is 1 byte long. + if ((glyph & 0x80) == 0) return 1; + + //ANDing 0xE0 (1110 0000) will result in 0xC0 (1100 0000) if this code point is 2 bytes long. + if ((glyph & 0xE0) == 0xC0) return 2; + + //ANDing 0xF0 (1111 0000) will result in 0xE0 (1110 0000) if this code point is 3 bytes long. + if ((glyph & 0xF0) == 0xE0) return 3; + + //ANDing 0xE0 (1111 1000) will result in 0xF0 (1111 0000) if this code point is 4 bytes long. + if ((glyph & 0xF8) == 0xF0) return 4; + + //Else we either have a continuation point (pattern: 10XX XXXX) or its just a junk value + //as far as we're concerned. + return 0; +} + +void TStringCopyGlyph(const char* glyph, int length, TString* string) { + if (!glyph || !string) return; + if (length < 1) return; + + if (!ResizeTStringCapacity(length, string)) return; + + memcpy(&string->Characters[string->ByteCount], glyph, length); + + string->ByteCount += length; + string->Length++; +} + +void TStringPrintDetails(const TString* string, int statsOnly) { + if (!string) return; + + printf("Stats: Length %d; Size %d byte(s); Capacity: %d bytes\n", string->Length, string->ByteCount, string->Capacity); + + if (statsOnly) return; + + printf("Stored String:\n"); + + if (string->Characters) { + char* text = TStringGetString(string); + printf("%s\n", text); + free(text); + } + + printf("\nBytes:\n"); + for(int i = 0; i < string->Capacity; i++) { + if (i != 0 && i % 80 == 0) printf("\n"); + printf("%02X ", string->Characters[i] & 0xFF); + } + printf("\n"); +} \ No newline at end of file