TextIOC Version 1.0 Release


Today I released the first version of TextIOC as part of the LLVM branch of the C toolchain. The library will not officially be available until the next toolchain release, but in the meantime, it is available for download at its Github repository.

Here are several screenshots taken from some of the toolchain examples:


Timed Input



Click here to view the demo's source code.

This example demonstrates the textio_TimedInput function. The time displayed is in seconds. Each IDS allocates 3 bytes for a timer, so each timer has a theoretical maximum capacity of 16,777,216 seconds, or about 4,660 hours. Plenty of leeway for any project that you might have. Wink

To implement this timer, TextIOC uses the third calculator timer, leaveing the first two timers free for progam use.


Program/Appvar Name Input



Click here to view the demo's source code.

This screenshot shows how the textio_SetPrgmNameFlag and the textio_SetThetaCharacter functions work. Each IDS has a byte allocated for special attributes. These attributes include whether or not an IDS is timed, is locked, set for program/appvar input, or if its data buffer is full. When the prgm_name flag is set, textio_Input will not accept a numeral as the first input character and locks the key that moves the cursor left, executing the backspace instead. The textio_SetThetaCharacter function determines which codepoint in the current font will act as the theta character. In the example above, I set the theta character value to 255. Thus, instead of the TI-OS value for theta (0x5b or 91; "[" in ASCII), the input function inserts 255 in the program/appvar name. TextIOC also offers two functions, textio_ConvertProgramAppvarName_TIOS and textio_ConvertProgramAppvarName_TextIOC, to convert the name into a TI-OS compatible format from a TextIOC compatible format and vice versa.


Advanced Input



Click here to view the demo's source code.

This demonstration shows how the IDS_Lock flag works as well as how to switch between different input fields.


TextIOC provides four keymaps of its own: uppercase letters, lowercase letters, numerals, and a special program/appvar name keymap. However, it also supports custom keymaps using the special string format given with the os_GetCSC function in the tice.h documentation.

The library also supports cursor customization, offering such functions as textio_SetCursorDimensions, textio_SetCursorColor, and textio_SetCursorBlinkRate.


As always, any feedback is greatly appreciated! Thanks again to everyone who helped make this project possible! Very Happy
@Adriweb:

After looking at some of the other autotext.json files in the toolchain examples, I believe I have figured out what they do: they load their associated demo into CEmu and test it. The first three sections: "transfer_files:", "target:", and "sequence:" also seem to make sense. What I don't understand is what kind of "sequence:" I am supposed to use for the TextIOC demos and how the "description:" blocks work.

I fixed part of the Travis build by modifying the call to GetCSC, but I think it is still failing because I don't have the autotest files in place.

Thank you for your help! Smile

EDIT: My apologies for double posting.
Well actually it fails because of another ti.xxxx call as you can see in the travis log, but I see you fixed it already Razz

Regarding the Autotest, well, you can copy/paste an existing json file from another project, but yeah you seem to have it already figured out for some parts. The description parts, well, you can actually just modify the expected CRCs values by getting them from CEmu Smile There is an autotester window/doc that allows you to get them.
So basically go step by step in CEmu, with your example programs, then for each step worthy of testing, get the crc/hash in CEmu and write that in the autotest file after the sequence of action (keypresses generally) to reach that step.
Another way of getting the CRCs is to just write in the JSON fake/wrong values at the expected steps and wait for the autotest log to give you the difference between actual and expected values, then put in the json file the actual values. But if you do that, make sure to (re)play the autotest within CEmu to make sure that it actually runs succesfully and everything is displayed and done as you want it.

An official documentation of the format is available here: https://github.com/CE-Programming/CEmu/blob/master/tests/autotester/json_file_documentation.txt
That said, the source code is the best documentation, as always Very Happy

__

Regardless, this is impressive work, your PR is big, with documented code, examples, and soon tests, great job!

That said, it would have been nice to have a few brainstorming sessions with the usual toolchain/libs contributors in order to think more about the architecture side of things, as Mateo already has comments about it Razz
Thanks, Adriweb! Very Happy

I have added the autotests and modified the makefiles for each TextIOC example, using the format I found in the other toolchain examples, including the -Oz flag for LLVM. Travis now builds perfectly, but AppVeyor does not for some reason.

Adriweb wrote:
That said, it would have been nice to have a few brainstorming sessions with the usual toolchain/libs contributors in order to think more about the architecture side of things, as Mateo already has comments about it Razz

That sounds good. Can you recommend a time when all of the contributors could meet? Will this conversation take place on SAX, or IRC?
We're generally all there on the #ez80-dev IRC channel, since many years ago Razz
(and #cemu-dev for CEmu specific stuff, but that's less the concern here)

And yep, I saw you added the autotests, that's good Smile

Regarding AppVeyor failing, yes that's normal, it wasn't updated to work with LLVM yet. And it might never be if we switch to GitHub actions anyway.
I know we briefly talked, but ideally there should be a structure that contains pointers to functions that implement the different mechanisms to instrument the library. A pointer to a character drawing function, a pointer to get a character's width, etc. Then the user would pass this structure to a textio function which would read the structure and modify it's internal calls to use the ones provided by the user.
Additionally, you can have a define in your header file that provides a struct initializer that contains a set of default routines that can be used. This way you don't have any direct dependencies, but the user is able to choose the routines and can still use some reasonable set of defaults with little code.
I did some work on integrating GraphX support for TextIOC this afternoon and have gotten a semi-working input function that uses the GraphX font.

To make the library compatible with the GraphX text functions, I added this structure and function to the library's header file:

Code:
typedef struct {
   void (*set_cursor_position)(int, int);
   int (*get_cursor_x)(void);
   int (*get_cursor_y)(void);
   void (*draw_char)(char);
   unsigned int (*get_char_width)(char);
   unsigned int (*get_string_width_l)(char *, size_t);
} textio_library_routines_t;

/* Function for setting up the library to use external text functions. */
void textio_SetLibraryRoutines(textio_library_routines_t *ptr);


I have had a difficult time with structures since learning C, so this might not be the best way of implementing multi-function compatibility.

In the C file, I create a function that passes the GraphX text functions to the library:

Code:
void setup_textio(void) {

   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   ptr->set_cursor_position = &gfx_SetTextXY;
   ptr->get_cursor_x = &gfx_GetTextX;
   ptr->get_cursor_y = &gfx_GetTextY;
   ptr->draw_char = &gfx_PrintChar;
   ptr->get_char_width = &gfx_GetCharWidth;
   ptr->get_string_width_l = &textio_gfx_GetStringWidthL;

   textio_SetLibraryRoutines(ptr);
   free(ptr);
   return;
}


The textio_gfx_GetStringWidthL is an internal library function that I created as a surrogate for FontLib's function of the same name. Wink

I created several three-byte pointer slots in the library's internal data to hold pointers to the external text functions and removed all of the direct calls to the FontLib functions and replaced them with calls to general util.* functions which handle the pointers to the external functions. For example, instead of calling fontlib_DrawGlyph, the library calls util.DrawChar which pushes the argument to the stack, gets the external function pointer from the library's internal data, and jumps to that location.

I found during the testing that I will need a variable to tell TextIOC from which library (GraphX or FontLibC) the text functions are coming from because some of the functions do not have the same parameters or return value types, which means different registers are used.

I also found that I will need to separate the cursor's y-position from that of its parent IDS so that the programmer can choose how much of the cursor should project above the top of the font.

Do you see anything in this approach that could be improved?
Version 2.0 Release

I am happy to announce the release of the second version of TextIOC. This version supports both FontLibC and GraphX fonts for both text input and output using two new functions, textio_SetSourceLibrary and textio_SetSourceLibraryRoutines. These functions supply TextIOC with pointers to certain basic text functions such as setting and getting the text coordinates, printing characters, and so on.

The following example shows how to specify the source library that TextIOC will use.

Code:

// To setup GraphX as the source library, call this function before using any TextIOC function.

void setup_gfx_textio(void) {

   /* Allocate the pointer structure. */
   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   /* Tell TextIOC that it will be using GraphX. */
   textio_SetSourceLibrary(TEXTIO_SET_GRAPHX_AS_SRC_LIB);

   /* Set the struct's pointers to the requisite GraphX functions. */
   ptr->set_cursor_position = &gfx_SetTextXY;
   ptr->get_cursor_x = &gfx_GetTextX;
   ptr->get_cursor_y = &gfx_GetTextY;
   ptr->draw_char = &gfx_PrintChar;
   ptr->get_char_width = &gfx_GetCharWidth;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   /* Free the structure memory. */
   free(ptr);
   
   return;
}

// To setup FontLib as the source library, call this function before calling any TextIOC functions.

void setup_fontlib_textio(void) {

   /* Allocate the pointer structure. */
   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   /* Tell TextIOC that it will be using FontLibC. */
   textio_SetSourceLibrary(TEXTIO_SET_FONTLIBC_AS_SRC_LIB);

   /* Set the struct pointers to the necessary FontLib functions. */
   ptr->set_cursor_position = &fontlib_SetCursorPosition;
   ptr->get_cursor_x = &fontlib_GetCursorX;
   ptr->get_cursor_y = &fontlib_GetCursorY;
   ptr->draw_char = &fontlib_DrawGlyph;
   ptr->get_char_width = &fontlib_GetGlyphWidth;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   /* Free the structure memory. */
   free(ptr);
   
   return;
}


And that is all you need to setup TextIOC!

As part of the abstraction process, I made a text window for textio_PrintText that operates separately from the FontLib text window. In doing so I had to recreate some of DrDnar's functions for the TextIOC text window, so this window has its own newline character, line spacing, and so on. Separating the two windows, however, also means that you can have both a FontLib and a TextIOC text window operating at the same time on the same screen, instead of only be able to use one.

Late in the abstracting, I found a problem with how the library handled drawing the theta character. With a custom FontLib font, you could create a theta glyph at some inconsequential offset, like 0 or 255, tell TextIOC was where it was located, and it would draw the character. With GraphX, however, it would be difficult to directly edit the library font itself, and then, when TextIOC was finished, it would have to edit the library font again to erase the character. To get around this problem, I added textio_SetDrawThetaCharFunction, which takes a pointer to a programmer-defined function that draws the theta character.

I failed to mention in the first release post that you can also specify custom keybindings for the input function that activate the backspace function, the clear function, and the cursor movement functions.

You can see the updated code examples at the Github repository.
Nice work Smile There shouldn't be a textio_SetSourceLibrary function though Wink

Ideally there should be a default #define that sets up the structure for you if you are using graphx or fontlib, which you can then pass to textio_SetLibraryRoutines. Something like this:


Code:
void setup_fontlib_textio(void) {

   /* Set the structure pointer. */
   textio_library_routines_t ptr = TEXTIO_FONTLIB_ROUTINES;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   return;
}


The user can modify the pointers as they see fit:


Code:
void setup_fontlib_textio(void) {

   /* Set the structure pointer. */
   textio_library_routines_t ptr =
   {
     .set_cursor_position = custom_func0,
     .get_cursor_x = custom_func1;
     .get_cursor_y = custom_func2;
     .draw_char = custom_func3;
     .get_char_width = custom_func4;
   }

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   return;
}


You'll probably have to abstract things a bit more in your library to not depend on the particular argument locations. Anywho.
  
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 2 of 2
» All times are GMT - 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