- TextIOC: A library for advanced text I/O
- 23 Feb 2020 03:47:40 pm
- Last edited by Captain Calc on 29 May 2020 03:30:56 pm; edited 1 time in total
Hello, everyone!
For the past two weeks I have been working on a pair of libraries, textioc.h and guilibce.h, for the TI-84 Plus CE. textioc provides some useful functions such as alpha/numerical input and printing auto-wrapped text. guilibce is a loose collection of functions for drawing GUI elements such as containers, buttons, and scroll bars.
This is the current list of functions for textioc:
Code:
And this the list of functions I have so far for guilibce:
Code:
I am currently working out the underlying design of guilibce, deciding how the elements will work, how they can be modified, whether there will be a GUI stack like DCSLibs, and so on. My goal is to make guilibce as flexible and intutive as possible.
Both the text-wrapping function and the PrintTruncatedStringXY function in textio have been implemented and debugged. This weekend, I have almost completed the LetterInput function which allows the user to enter uppercase and lowercase letters. By changing the function slightly, I believe I can easily create a function that will take only numbers and another function that will take both numbers and letters.
However, while I was working on the last part of the LetterInput function I found that I hadn't freed the pointer to the buffer that holds the user input, creating a potential memory leak. Below is the code for the function with the proper calls to free() but which does not return the user input.
Code:
My first solution was to allocate memory for another pointer that was exactly as long as the string, copy only the string characters from buffer_ptr, and return that pointer. However, I think this can also cause a memory leak unless the programmer does the following:
Code:
However, I would like the function to be self-contained and not require the programmer to do any extra memory freeing.
Any help would be very much appreciated!
For the past two weeks I have been working on a pair of libraries, textioc.h and guilibce.h, for the TI-84 Plus CE. textioc provides some useful functions such as alpha/numerical input and printing auto-wrapped text. guilibce is a loose collection of functions for drawing GUI elements such as containers, buttons, and scroll bars.
This is the current list of functions for textioc:
Code:
void textio_PrintWrappedText(char text[], uint24_t width, uint8_t xPos, uint8_t yPos);
/* maxWidth is in pixels */
void textio_PrintTruncatedStringXY(char string[], uint24_t max_width, uint24_t xPos, uint8_t yPos);
uint24_t textio_NumericalInput(uint8_t xPos, uint8_t yPos, uint8_t magnitude_limit);
char *textio_LetterInput(uint24_t xPos, uint8_t yPos, size_t buffer_size);
And this the list of functions I have so far for guilibce:
Code:
/* GUI Element Config Functions
// ----------------------------------
void gui_SetTitledContainerConfig(outlineColor, bodyColor, bannerColor, titleColor [,shadowColor]);
void gui_SetContainerConfig(outlineColor, bodyColor, [,shadowColor]);
void gui_SetMenuConfig(outlineColor, bodyColor, selectionColor, itemFGColor, subMenuArrowSprite, [,shadowColor]);
void gui_SetBalloonContainerConfig(outlineColor, bodyColor, [,shadowColor]);
void gui_SetRadioButtonConfig(onStateSprite, offStateSprite, labelFGColor, labelBGColor);
void gui_SetCheckBoxConfig(onStateSprite, offStateSprite, labelFGColor, labelBGColor);
// Possible implementations:
void gui_SetScrollBarConfig(scrollButtonSprite);
void gui_SetRadioButtonLockedConfig(onStateSprite, offStateSprite, labelFGColor, labelBGColor);
void gui_SetCheckBoxLockedConfig(onStateSprite, offStateSprite, labelFGColor, labelBGColor);
*/
/* GUI Element Functions
// -----------------------------
void gui_TitledContainerFullScreen(title);
void gui_TitledContainer(xPos, yPos, width, height);
void gui_Container(xPos, yPos, width, height);
// item#Type: 1 = normal; 2 = subMenu; 3 = dividerAbove; 4 = subMenuAndDividerAbove
void gui_Menu(xPos, yPos, item1, item1Type, ...);
// arrowDirection: 1 = up; 2 = down; 3 = left; 4 = right
void gui_BalloonContainer(xPos, yPos, width, height, arrowDirection);
void gui_RadioButton(xPos, yPos, label, state);
void gui_CheckBox(xPos, yPos, label, state);
// Possible implementations:
void gui_ScrollBarHorizontal(xPos, yPos, height, initialPos, startPos, rangePos);
void gui_ScrollBarVertical(xPos, yPos, width, initialPos, startPos, rangePos);
void gui_RadioButtonLocked(xPos, yPos, label, state);
void gui_CheckBoxLocked(xPos, yPos, label, state);
// Advanced GUI Element Functions
// -------------------------------------------
void gui_BalloonContainerAutoAdj(xPos, yPos, width, height, arrowDirection);
I am currently working out the underlying design of guilibce, deciding how the elements will work, how they can be modified, whether there will be a GUI stack like DCSLibs, and so on. My goal is to make guilibce as flexible and intutive as possible.
Both the text-wrapping function and the PrintTruncatedStringXY function in textio have been implemented and debugged. This weekend, I have almost completed the LetterInput function which allows the user to enter uppercase and lowercase letters. By changing the function slightly, I believe I can easily create a function that will take only numbers and another function that will take both numbers and letters.
However, while I was working on the last part of the LetterInput function I found that I hadn't freed the pointer to the buffer that holds the user input, creating a potential memory leak. Below is the code for the function with the proper calls to free() but which does not return the user input.
Code:
char* textio_LetterInput(uint24_t xPos, uint8_t yPos, size_t buffer_size) {
char *buffer_ptr; // Buffer for user input
char *letter_ptr; // Holds current char
bool caps_on = true;
uint8_t text_BG_color;
uint8_t text_FG_color = 0x00;
uint24_t cursor_xPos = xPos + 1;
uint24_t i; // Counter variable
bool cursor_active = false;
uint8_t key = '\0';
const char *lowercase_letters = "\0\0\0\0\0\0\0\0\0\0\0wrmh\0\0\0\0vqlg\0\0\0zupkfc\0\0ytojeb\0\0xsnida\0\0\0\0\0\0\0\0";
const char *uppercase_letters = "\0\0\0\0\0\0\0\0\0\0\0WRMH\0\0\0\0VQLG\0\0\0ZUPKFC\0\0YTOJEB\0\0XSNIDA\0\0\0\0\0\0\0\0";
// Debugging variables
uint8_t j;
char *pLetter;
buffer_ptr = malloc(buffer_size + 1);
if (!buffer_ptr)
return NULL;
memset(buffer_ptr, '\0', buffer_size + 1);
letter_ptr = buffer_ptr;
gfx_SetTextScale(1,1);
gfx_SetTextFGColor(text_FG_color);
for (;;) {
gfx_SetTextXY(xPos, yPos);
gfx_PrintString(buffer_ptr);
cursor_active = false;
if (letter_ptr < buffer_ptr + buffer_size)
cursor_active = true;
dbg_sprintf(dbgout, "cursor_active = %d\n", cursor_active);
cursor_xPos = xPos + 1;
if (letter_ptr > buffer_ptr)
cursor_xPos += gfx_GetStringWidth(buffer_ptr);
text_BG_color = gfx_GetPixel(cursor_xPos, yPos);
gfx_SetTextBGColor(text_BG_color);
gfx_SetTextTransparentColor(text_BG_color);
// Debugging
dbg_sprintf(dbgout, "letter_ptr = %x\nbuffer_ptr = %x\n", letter_ptr, buffer_ptr);
dbg_sprintf(dbgout, "buffer =");
pLetter = buffer_ptr;
for (j = 0; j < buffer_size; j++)
dbg_sprintf(dbgout, " %x", *pLetter++);
dbg_sprintf(dbgout, "\t|");
pLetter = buffer_ptr;
for (j = 0; j < buffer_size; j++)
dbg_sprintf(dbgout," %c ", *pLetter++);
dbg_sprintf(dbgout, "|\n");
// Wait for input and display cursor
do {
if (cursor_active) {
gfx_SetColor(0x00);
gfx_Line(cursor_xPos, yPos - 1, cursor_xPos, yPos + 8);
};
i = 0;
do {
key = os_GetCSC();
i++;
} while (!key && i < 6000);
if (cursor_active) {
gfx_SetColor(text_BG_color);
gfx_Line(cursor_xPos, yPos - 1, cursor_xPos, yPos + 8);
};
while (!key && i < 12000) {
key = os_GetCSC();
i++;
};
} while (!key);
if (key == sk_Clear && letter_ptr == buffer_ptr)
return NULL;
if (key == sk_Enter) {
// Debugging
dbg_sprintf(dbgout, "buffer =");
pLetter = buffer_ptr;
for (j = 0; j < buffer_size; j++)
dbg_sprintf(dbgout, " %x", *pLetter++);
dbg_sprintf(dbgout, "\t|");
pLetter = buffer_ptr;
for (j = 0; j < buffer_size; j++)
dbg_sprintf(dbgout," %c ", *pLetter++);
dbg_sprintf(dbgout, "|\n");
// Return only the chars entered and remove
// all trailing null chars
if (letter_ptr > buffer_ptr) {
// string should contain the final string input plus the null terminator
free(buffer_ptr);
return string;
};
dbg_sprintf(dbgout,"letter_ptr is equal to buffer_ptr");
free(buffer_ptr);
return NULL;
};
if (key == sk_Clear) {
while (letter_ptr > buffer_ptr)
*--letter_ptr = '\0';
gfx_SetColor(text_BG_color);
gfx_FillRectangle(xPos, yPos, cursor_xPos, 8);
};
if ((key == sk_Del) && letter_ptr > buffer_ptr) {
letter_ptr--;
// Debugging
dbg_sprintf(dbgout, "*letter_ptr = %c\n&letter_ptr = %x\n", *letter_ptr, &letter_ptr);
dbg_sprintf(dbgout, "Deleted char\n");
// Erase char
gfx_SetColor(text_BG_color);
gfx_FillRectangle(cursor_xPos - gfx_GetCharWidth(*letter_ptr) - 1, yPos, gfx_GetCharWidth(*letter_ptr), 8);
*letter_ptr = '\0';
} else {
// Debugging
dbg_sprintf(dbgout, "Cannot delete char\n");
};
if (key == sk_Alpha) {
gfx_SetTextBGColor(0x00);
gfx_SetTextFGColor(0xFF);
gfx_SetTextTransparentColor(0x00);
gfx_SetColor(0x00);
if (caps_on) {
caps_on = false;
gfx_FillRectangle(cursor_xPos, yPos - 1, 9, 10);
gfx_PrintStringXY("a", cursor_xPos + 1, yPos);
} else {
caps_on = true;
gfx_FillRectangle(cursor_xPos, yPos - 1, 9, 10);
gfx_PrintStringXY("A", cursor_xPos + 1, yPos + 1);
};
delay(400);
gfx_SetColor(text_BG_color);
gfx_FillRectangle(cursor_xPos, yPos - 1, 9, 10);
gfx_SetTextFGColor(text_FG_color);
};
if (cursor_active && (uppercase_letters[key] || lowercase_letters[key])) {
if (caps_on) {
*letter_ptr = uppercase_letters[key];
dbg_sprintf(dbgout, "Added uppercase char\n");
} else {
*letter_ptr = lowercase_letters[key];
dbg_sprintf(dbgout, "Added lowercase char\n");
};
// Debugging
dbg_sprintf(dbgout, "*letter_ptr = %c\n&letter_ptr = %x\n", *letter_ptr, &letter_ptr);
letter_ptr++;
} else {
// Debugging
dbg_sprintf(dbgout, "No char added\n");
};
delay(100);
};
}
My first solution was to allocate memory for another pointer that was exactly as long as the string, copy only the string characters from buffer_ptr, and return that pointer. However, I think this can also cause a memory leak unless the programmer does the following:
Code:
char* user_input;
user_input = textio_LetterInput(50, 50, 10);
/* Do something with user_input... */
free(user_input);
However, I would like the function to be self-contained and not require the programmer to do any extra memory freeing.
Any help would be very much appreciated!