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:
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! Smile
Have the user supply the buffer and buffer length like literally every other standard C function.
Thank you, Mateo!

I thought I had tried everything to get around the leak, but I never thought of that. Now all the rest will be easy!
Progress Update

Following Mateo's advice on user-supplied buffers, I have finished a prototype of the textio_LetterInput function.

This function gets string input from the user in both uppercase and lowercase as well as supporting backspacing, character insertion/deletion, and clearing. The [alpha] key switches between the uppercase and lowercase letters and briefly displays an indicator showing the current case. The [del] key acts as a backspace, and the [clear] key deletes all letters behind the cursor. If there are no letters behind the cursor, pressing [clear] will erase any letters in front of the cursor. Pressing [clear] with no letters behind or in front of the cursor exits the function. The function also exits when the user presses [enter], storing the input in the supplied buffer.

After learning how to use Git and Github from commandblockguy's excellent tutorial, I have pushed the latest version of textioc to Github. The function contains a large amount of debugging code which shows how the function manipulates the input buffer in CEmu.

Here is a demonstration of how the function works:


This demo uses the following code:

Code:
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tice.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <keypadc.h>
#include <graphx.h>
#include <debug.h>

#include "textioc.h"

void dialog( uint24_t xPos, uint8_t yPos, uint24_t width, uint8_t height, uint8_t banner_color, const char *title);

void main(void) {
   
   char *user_input;
   const char *pass = "PASSWORD";
   const char *message = "Welcome";
   
   user_input = malloc(9);
   memset(user_input, '\0', 9);
   
   gfx_Begin();
   
   // Draw dialogs
   dialog(50, 80, 220, 80, 30, "Access Requested" );
   
   gfx_PrintStringXY("Please enter your password:", 55, 115);
   gfx_SetColor(0);
   gfx_Rectangle(55, 130, 150, 13);
   
   // Get input
   textio_LetterInput(user_input, 20, 58, 132);
   
   if (strcmp(user_input, pass) == 0) {
      gfx_FillScreen(30);
      gfx_SetTextScale(3, 3);
      gfx_SetTextBGColor(30);
      gfx_SetTextFGColor(255);
      gfx_SetTextTransparentColor(30);
      gfx_PrintStringXY(message, 160 - gfx_GetStringWidth(message) / 2, 80);
   };
   
   while (!os_GetCSC());
   
   free(user_input);
   
   gfx_End();
   
}

void dialog( uint24_t xPos, uint8_t yPos, uint24_t width, uint8_t height, uint8_t banner_color, const char *title) {
   
   gfx_SetColor(181);
   gfx_FillRectangle(xPos + 1, yPos + 1, width, height);
   gfx_SetColor(0);
   gfx_FillRectangle(xPos, yPos, width, height);
   gfx_SetColor(banner_color);
   gfx_FillRectangle(xPos + 1, yPos + 1, width - 2, 15);
   gfx_SetColor(255);
   gfx_FillRectangle(xPos + 1, yPos + 17, width - 2, height - 18);
   gfx_PrintStringXY(title, xPos + 4, yPos + 4);
   
}


I would greatly appreciate any recommendations for improving the function in order to make as fast and efficient as possible. Once this function is complete, I can then modify it to create other input functions, such as numerical input, alpha-numerical input, and other specialized functions.
Progress Update

I discovered this morning that I had improperly implemented the textio_PrintWrappedText function and found a few, subtle bugs in the function that produced some odd effects. I fixed the function this evening and added another check to it that makes the function exit when it has reached the bottom of the screen.

A screenshot of the function, using an excerpt from Stevenson’s Treasure Island (the red line shows the right margin):


The code for the screenshot:

Code:
 #include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tice.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <keypadc.h>
#include <graphx.h>
#include <debug.h>

#include "textioc.h"

void main(void) {
   
   gfx_Begin();
   
   /* void textio_PrintWrappedText(char *text, uint24_t width, uint24_t xPos, uint8_t yPos) */

   textio_PrintWrappedText("Squire Trelawney, Doctor Livesey, and the rest of these gentlemen having asked me to write down the whole particulars about Treasure Island, from the beginning to the end, keeping nothing back but the bearings of the island, and that only because there is still treasure not yet lifted, I take up my pen in the year of grace 17-, and go back to the time when my father kept the \"Admiral Benbow\" Inn, and the brown old seaman, with the saber cut, first took up his lodging under our roof.", 300, 1, 1);
   
   while (!os_GetCSC());   
   gfx_End();
}


I have also added a function called textio_SetInputConfig that customizes the foreground color and the cursor color of the text input function. The updated version of textioc 0.1.00 is available here.

I would greatly appreciate any recommendations for improving textioc. Smile
Many thanks to Jeffitus and beckadamtheinventor for their support and encouragement for textioc!

@beckadam, Thank you so much for your time and effort rewriting textioc in assembly! I really like the new features you’ve added to it! I know very little about ASM, but I hope to learn it quickly in order to help rewrite the library. Very Happy
Captain Calc wrote:
Many thanks to Jeffitus and beckadamtheinventor for their support and encouragement for textioc!

@beckadam, Thank you so much for your time and effort rewriting textioc in assembly! I really like the new features you’ve added to it! I know very little about ASM, but I hope to learn it quickly in order to help rewrite the library. Very Happy


I'm very happy you noticed Smile
Also I'm exited you are receptive to my fork Very Happy

EDIT: finished rewrite, made a pull request.
EDIT2: NOTE on textio_LetterInput: My rewrite of that function does not have cursor scrolling yet.
Hello everyone!

I have written an outline for textioc to help figure out how all of the functions will work and how they should be implemented. If you are interested in helping with the library or have any questions about the outline, please PM me.

If you have any features you think would be useful, please let me know! Smile
Progress Update on TextIOC

As part of my next release of Mental Math CE, I am porting the TextIOC library to eZ80 assembly. After a discussion with DrDnar, the creator of the FontLibC library, I realized that TextIOC would be better implemented as a wrapper for the FontLib library, providing the text wrapping support DrDnar mentioned in his library documentation as well as providing the input function that the previous version supported. This change will mean that TextIOC will no longer support the GraphX font.

I have pulled the C version from Github and pushed the current ASM version to the TextIOC repository. This version is incomplete. I am still slowly rewriting functions from C into assembly.


Debugging an ASM Function

This post is also a request for help. Wink

The problem is in the following function: uint24_t textio_GetLineWidth(const char *line, const char *eol). The line parameter is a pointer to the start of the line, and the eol parameter is a pointer to the End-Of-Line. This is the current, unoptimized version:

Code:
 ;-------------------------------------------------------------
; Includes
;-------------------------------------------------------------
    include 'include/library.inc'
    include 'include/include_library.inc'


;-------------------------------------------------------------
    library 'TEXTIOC', 1
;-------------------------------------------------------------


;-------------------------------------------------------------
; Dependencies
;-------------------------------------------------------------
    include_library 'graphx.asm'
    include_library 'fontlibc.asm'


;-------------------------------------------------------------
; v1 functions
;-------------------------------------------------------------
    export textio_SetTabWidth
    export textio_GetTabWidth
    export textio_GetLineWidth


;-------------------------------------------------------------
; Global variables
;-------------------------------------------------------------
    arg0     := 3
    arg1     := 6
    null     := $00
    tab      := $09




;-------------------------------------------------------------
; Functions
;-------------------------------------------------------------


;-------------------------------------------------------------
textio_SetTabWidth:
; Arguments:
;   arg0 = width
; Returns:
;   None
; Destroys:
;   HL
;   DE

    pop     hl
    pop     de
    push    de
    push    hl
    ld     (_TabWidth), de
    ret

;-------------------------------------------------------------
textio_GetTabWidth:
; Arguments:
;   None
; Returns:
;   Width of tab

    ld       hl, (_TabWidth)
    ret
   

;-------------------------------------------------------------
textio_GetLineWidth:
; Arguments:
;   arg0  =  pointer to line
;   arg1  =  pointer to end of line
; Returns:
;   Width of line

    scf            ; dbg_Debugger()
    sbc     hl,hl
    ld     (hl),2
   
    ld      iy, 0
    add     iy, sp
    ld      hl, (iy + arg0)        ; hl -> line
    ld      bc, (iy + arg1)        ; bc -> eol

    ld      e, 0
    ld      d, 0

.loop:
    ld      a, (hl)
    cp      a, null
    jr      z, .exit

    push    hl
    sbc     hl, bc
    jr      z, .exit

    pop     hl
    ld      a, (hl)
    cp      a, tab
    jr      nz, .addGlyphWidth

    push    hl
    call    textio_GetTabWidth
    call    util.AddHLToDE
    pop     hl
    jr      .nextChar

.addGlyphWidth:
    push    de
    push    hl
    call    fontlib_GetGlyphWidth
    pop     hl
    pop     de
    call    util.AddAToDE

.nextChar:
    inc     hl        ; HL should be pointer to line
    jr      .loop

.exit:
    ex      de, hl
    ret



;-------------------------------------------------------------
; Internal Library Functions
;-------------------------------------------------------------


;-------------------------------------------------------------
util.AddAToDE:
; Inputs:
;   A  = Operand 1
;   HL = Operand 2
; Ouputs:
;   DE = DE + A

    add   a, e
    ld    e, a
    jr    nc, .exit
    inc   d
.exit:
    ret

;-------------------------------------------------------------
util.AddHLToDE:
; Inputs:
;   HL = Operand 1
;   DE = Operand 2
; Outputs:
;   DE = HL + DE

    push  hl
    add   hl, de
    ex    hl, de
    pop   hl
    ret




;-------------------------------------------------------------
; Internal Library Data
;-------------------------------------------------------------

_TabWidth:
    dl     4
_PrintFormat:
    db     0

The main function that calls it:

Code:
 #include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tice.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <graphx.h>
#include <debug.h>
#include "dbg/calcdbg.h"

#include <fileioc.h>
#include <fontlibc.h>
#include "fonts/fonts.h"

#include <textioc.h>


void textio_SetWindow_Outline(unsigned int x, uint8_t y, unsigned int width, uint8_t height) {
   
   gfx_SetColor(224);
   fontlib_SetWindow(x, y, width, height);
   gfx_Rectangle_NoClip(x, y, width + 1, height);
}


void main(void) {
   
    char text[] = {"\tThis is a very long sample string for wrapping around 320 pixels. This is yet another sample string made for text wrapping testing."};
   
   uint24_t width;

    gfx_Begin();
    fontlib_SetFont(test_font, 0);
   
    fontlib_SetLineSpacing(2, 2);
    textio_SetWindow_Outline(0, 0, 220, 240);
    fontlib_SetColors(0x00, 0xFF);
   
   //textio_SetTabWidth(4);
   
   //textio_SetPrintFormat(1);
    //textio_PrintText(text, 5);
   dbg_sprintf(dbgout, "tab = %d\n", textio_GetTabWidth());
   width = textio_GetLineWidth(text, text + 2);
   dbg_sprintf(dbgout, "line width = %d\n", width);
    while (!os_GetCSC());
   
    gfx_End();
}]

The first loop executes correctly, leaving DE as four (the width of the tab). The second loop, however, does not execute correctly as it adds only four to DE when it should add at least six for the “T”. During this loop, the accumulator holds $54 (“T”), so I think the bug lies somewhere around the call to fontlib_GetGlyphWidth. At the end of the third loop, the function exits, an invalid write to flash occurs, and the calculator crashes.

If anyone could show me where the bug lies, I would very much appreciate it!

GUILibCE Update

After a considerable amount of thinking and experimenting, I have concluded that creating a GUI library for the TI-84 Plus CE would not be feasible, given the enormous flexibility it would need to possess and my lack of time to bring the project to fruition. Laying this project aside, I have renamed this forum thread, and it will become the main development thread for the TextIOC library.
To DrDnar:

I believe I found what was giving us all the trouble. While I was getting the second code ready to run, I found that the copy of fontlibc.asm that I had gotten from your repository on Github did not have the fontlib_DrawUInt function that the code needed. Going to the toolchain repository, I found its copy of FontLib that included the function and replaced my copy with it. The code ran perfectly. I think that my using the wrong copy of FontLib was the problem.

On a side note, I noticed that the copy from your repo had ‘library FONTLIBC, 1’ in its header. Although I do not know what the syntax means (and would be happy to learn), I suspect that line could have made some subtle collisions occur with TextIOC which uses the same number.

The invalid write to flash still occurs when I run my function, but when I’m using the debugger and disassembler, I can recognize the FontLib code executing when I call fontlib_GetGlyphWidth. I know that my function is still sub-par (e.g. realizing I was passing a pointer instead of a char to fontlib_GetGlyphWidth in the above code Rolling Eyes ), but I feel confident that I can carry on and get this off the ground.

Thanks for all your help, I really appreciate it! Very Happy
To DrDnar:

While I was working on one of the TextIOC functions, I found that fontlib_GetStringWidth modified the stack in a strange way. The function that I was porting was textio_GetLinePtr, and the fontlib call is on line 290. This is the C code I was using as a reference to port the function:

Code:
 char *textio_GetLinePtr(const char *text, uint8_t line_num) {
   
   uint8_t curr_line_num;
   unsigned int line_width;
   
   char *curr_word;
   char *curr_line;
   
   fontlib_SetFirstPrintableCodePoint(0x20);
   fontlib_SetAlternateStopCode(' ');
   curr_line_num = 0;
   line_width = 0;
   curr_word = text;
   curr_line = curr_word;
   
   for (;;) {
      
      START:
      if (*fontlib_GetLastCharacterRead() == '\0')
         return fontlib_GetLastCharacterRead();
      
      if (curr_line_num == line_num)
         return curr_line;
      
      line_width += fontlib_GetStringWidth(curr_word);
      if (*fontlib_GetLastCharacterRead() == ' ')
         line_width += fontlib_GetGlyphWidth(' ');
      
      if (*fontlib_GetLastCharacterRead() == '\t') {
         line_width += textio_GetTabWidth();
      };
      
      if (line_width > fontlib_GetWindowWidth()) {
         curr_line_num++;
         line_width = 0;
         if (textio_print_config.format == RIGHT_MARGIN_FLUSH)
            curr_word--;
         curr_line = curr_word;
         goto START;
      };
      
      if (*fontlib_GetLastCharacterRead() == '\n') {
         curr_line_num++;
         line_width = 0;
         curr_line = fontlib_GetLastCharacterRead() + 1;
      };
      
      curr_word = fontlib_GetLastCharacterRead() + 1;
   };
}

Register HL holds a pointer to the current word being processed, register DE holds the current line width, and register BC holds a pointer to the start of the current line. Before the function call, the stack looks like this:

Code:
Address       Value         Register
---------     ------        ---------
D1A797        D1A7A9        HL
D1A79A        000000        DE
D1A79D        D1A7A9        BC


After the function call, the stack changes:

Code:
Address       Value         Register
---------     ------        ---------
D1A797        FFFFFF        HL
D1A79A        000000        DE
D1A79D        D1A7A9        BC


I couldn’t tell if this was intended behavior or not, but since it destroyed the value that was supposed to be popped into HL, the function froze the calculator when it executed. I then tried pushing HL twice before the function call, making the stack look like this:

Code:
Address       Value         Register
---------     ------        ---------
D1A794        D1A7A9        HL
D1A797        D1A7A9        HL
D1A79A        000000        DE
D1A79D        D1A7A9        BC


Stack state after the call:

Code:
Address       Value         Register
---------     ------        ---------
D1A794        FFFFFF        HL
D1A797        D1A7A9        HL
D1A79A        000000        DE
D1A79D        D1A7A9        BC


After the call, I popped the stack into HL twice to recover the proper value. The function worked after that ( still not returning the correct line pointer, yet Wink ).

I didn’t know if this was a bug or not, but I thought you should know about it.

EDIT: After a discussion with DrDnar, I learned that C assembly functions are not required to preserve passed arguments.
Progress Update

The textio_PrintText function is nearing completion. All that is left is the debugging of the print_centered and print_right_margin_flush flags. I am hoping to post a screenshot of the finished function soon. Smile

By bitter experience, I have found that to have a rock-solid outline of how a given function will work is indispensable and have written up an outline for the next major function, the textio_Input function. The file is hosted on my Google Drive in a PDF format. Any feedback on the design would be most appreciated!

Download: https://drive.google.com/file/d/1w4Qr_WomntPjzGtLdwdJViTFaofxt6yI/view?usp=sharing
About the GUI not being feasible on the CE- are you familiar with the GUI library available for the TI-83+ using DCS7? I used it for a utility and a game and really liked it.
Yes, I am familiar with the Doors GUI library. To create a similar library for the CE is definitely feasible.

While I was developing GUILibCE, I looked at several games and programs for the CE and thought about how each user interface would be implemented using a single GUI library. After awhile the sheer variability between each user interface made what I at first deemed a simple task into a massive project. In order for the programmer to customize at will each GUI element in the library, it would have to be very flexible and would require a level of expertise I do not possess. A brief research session on several computer widget toolkits, including OpenGL and Qt, finally convinced me that my vision had outgrown my skill, and I decided to drop the project in favor of TextIOC.

After I laid the project to rest, however, I did have another idea that may be useful if anyone would like to take it up: a computer-based GUI builder for the CE. It would use an interface like Microsoft Paint, supplying functions like rectangles, circles, and so on. The programmer would select the type of GUI element he wanted to create (e.g. a window) and then draw it. The GUI builder would keep track of each shape drawn, supply the necessary variables for width, height, x-position, and y-position, and translate the shapes into C code using the GraphX functions. Once the programmer was satisfied, the program would output the completed function (e.g. draw_window()), and the programmer could then incorporate it into his program or add it to his own personal GUI library.

The advantage of this kind of program is that it would allow the programmer to see what the finished element would look like in real-time, rather than going back-and-forth from source code to calculator trying to make it look right.
Progress Update

Here is the demo!


The pause when each "Printing..." message shows is a deliberate delay in the code. The actual print time for each screen is less than one second.

I have also updated the Github repo. If anyone has any bug reports, code recommendations, or suggestions, I would be more than happy to hear them! Very Happy
Captain Calc wrote:
Yes, I am familiar with the Doors GUI library. To create a similar library for the CE is definitely feasible.

While I was developing GUILibCE, I looked at several games and programs for the CE and thought about how each user interface would be implemented using a single GUI library. After awhile the sheer variability between each user interface made what I at first deemed a simple task into a massive project. In order for the programmer to customize at will each GUI element in the library, it would have to be very flexible and would require a level of expertise I do not possess. A brief research session on several computer widget toolkits, including OpenGL and Qt, finally convinced me that my vision had outgrown my skill, and I decided to drop the project in favor of TextIOC.

After I laid the project to rest, however, I did have another idea that may be useful if anyone would like to take it up: a computer-based GUI builder for the CE. It would use an interface like Microsoft Paint, supplying functions like rectangles, circles, and so on. The programmer would select the type of GUI element he wanted to create (e.g. a window) and then draw it. The GUI builder would keep track of each shape drawn, supply the necessary variables for width, height, x-position, and y-position, and translate the shapes into C code using the GraphX functions. Once the programmer was satisfied, the program would output the completed function (e.g. draw_window()), and the programmer could then incorporate it into his program or add it to his own personal GUI library.

The advantage of this kind of program is that it would allow the programmer to see what the finished element would look like in real-time, rather than going back-and-forth from source code to calculator trying to make it look right.



This is what I’ve been searching for. Captain Calc do you have a discord channel? Please pm me. I’m new here and interested in contributing the TI-84 community. I’m more proficient in java but I will do my best working with c.
Progress Update

After some refinement to my original design of the text input functions for TextIOC, I am starting the implementation phase. The current design of the input functions centers on an Input Data Structure (IDS) which holds the input buffer (data buffer) as well as additional information that instructs the main input routine, textio_Input() on how to handle the buffer.

The IDS contains the following data:

Code:
+-------------+------+--------------------+---------------+---------------+------+--------------+----------------------+
| data buffer | null | visible_buffer_ptr | curr_char_ptr | original xPos | yPos | current xPos | visible buffer width |
+-------------+------+--------------------+---------------+---------------+------+--------------+----------------------+

|- ?? bytes --|1 byte|------ 3 bytes -----|--- 3 bytes ---|--- 3 bytes ---|1 byte|--- 3 bytes --|------- 3 bytes ------|

  • The number of characters the programmer desires determines the length of the data buffer.

  • The visible_buffer_ptr points to the first character of the string that the user sees in the input box. This pointer is incremented when the text is scrolled, and the length of the string must never exceed the visible buffer width. The visible buffer width is like the width of the input box, whether or not an input box is actually drawn around the input.


Code:
data buffer contents - |abcdefghijklmno..|
visible input        -       |hijklmno |
                              ^
                              visible_buffer_ptr

  • The curr_char_ptr points to the character just in front of the cursor.

  • The original xPos is the cursor's starting x-position on the screen.

  • The current xPos is the cursor's current x-position on the screen.



When the programmer creates a new input routine, he first creates the IDS, supplying it with all of the data given above. Once the IDS is in place, he can then call the TextIOC input function. The input function has the following syntax:

Code:
kb_key_t textio_Input(uint24_t *IDS)


(I have not yet decided what key routine to use, so kb_key_t is a placeholder.)

When textio_Input() starts, it retrieves the cursor data and the buffer data from the IDS. Using the latter, it draws the visible portion of the data buffer. Using the former, it draws the cursor at the correct location. The input function handles swapping different character sets and manipulating the data buffer. The input function only handles one keypress before exiting, returning the keycode. This allows the programmer to take additional action on the keypress such as terminating the input routine if an escape key was pressed, updating a list of words if the input routine is a search function, and so on. When it exits, the input function saves the state of the buffer and cursor to the IDS which allows it to pick up exactly where it left off. This behavior is useful if the user can switch between multiple input boxes.


Question: Allocating RAM in an assembly function (solved)

While implementing the textio_CreateIDS function, I found I needed a method to allocate memory in assembly. From a discussion on the SAX chat with DrDnar, I learned that the GraphX library uses a macro to access the C malloc() routine. I found the following code in one of the sprite macros but wasn't sure if it was the right code. I would also appreciate an explanation of how the macro accesses the malloc() routine.

Code:
gfx_sprite_t *gfx_AllocSprite(uint8_t width,
                              uint8_t height,
                              void *(*malloc_routine)(size_t));

During the same chat session, fghsgh suggested that I could use an appvar to store the IDS.

EDIT: After studying graphx.h and its source code, I figured out how to access the malloc() function and modified it to suit TextIOC.


Conclusion

My goal at present is to make a solid IDS creation function and slowly build the rest of the input functions on it.

If anyone has any questions on the very condensed outline I gave above, please post them and I will be happy to answer them! Smile
Progress Update

The IDS design and implementation is nearing completion. If no unusually large debugging spells ensue in the last half dozen TextIOC functions, I am hoping to release a beta version of the complete library next week.

Since my last post, I have increased the amount of information the IDS can hold, including such features as cursor dimensions, keymaps, and input locks. To clarify my previous post, each IDS holds the data about one input field, or input box. Thus, you could have more than one input field on the same screen with different cursor dimensions, different font color, and different keymaps, just like computer input fields. The input lock allows the programmer to prevent the user from entering input in certain fields while allowing input in others.

Request For Comments

Before I proceed with the library's development, I wanted to know if anyone would like a different method for indicating which keymap the user is currently using. In the C version of TextIOC, when the [alpha] key was pressed, the input function would switch keymaps and briefly display a symbol, called the keymap indicator, just ahead of the cursor (see the screenshot below). In Doors CS, this indicator was displayed in the upper right-hand corner of the screen, and, in some programs, the indicator is constantly shown near the cursor, but not directly next to it, such as at the far right of an input box.



Would anyone prefer that TextIOC allow the programmer to decide how the keymap indicator should look?
Make it customizable as you can.

I look forward to using these functions in an upcoming C Project Smile .


Code:
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);
I am sorry to disappoint you, but I am no longer working on GUILibCE, so the functions you mentioned will not be available, at least in a LibLoad library.

I can make the keymap indicator completely customizable by making the input function call a C function, written by the programmer, that draws the indicator. I will add another function that will tell the programmer which keymap is currently in use so he can use that to determine how to draw the indicator.

Thank you for your input on TextIOC!
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement