2021-11-29 07:25:07 +00:00
// TODO:
// - IN PROGRESS new message window -- needs to blank out messages, then needs fixes on new mac end
// - get new messages in other chats and display some sort of alert
2022-01-03 07:36:23 +00:00
// - need timeout on serial messages in case the computer at the other end dies (prevent hard reset) -- probably possible in coprocessorjs library
2021-11-29 07:25:07 +00:00
// - delete doesnt work right (leaves characters at end of string)
2021-12-05 06:27:50 +00:00
# define WINDOW_WIDTH 510
# define WINDOW_HEIGHT 302
# define NK_ZERO_COMMAND_MEMORY
# define NK_INCLUDE_FIXED_TYPES
# define NK_INCLUDE_STANDARD_IO
// #define NK_INCLUDE_STANDARD_VARARGS
# define NK_INCLUDE_DEFAULT_ALLOCATOR
# define NK_IMPLEMENTATION
# define NK_QUICKDRAW_IMPLEMENTATION
// #define NK_BUTTON_TRIGGER_ON_RELEASE
# define NK_MEMSET memset
# define NK_MEMCPY memcpy
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
void aFailed ( char * file , int line ) {
MoveTo ( 10 , 10 ) ;
char * textoutput ;
sprintf ( textoutput , " %s:%d " , file , line ) ;
writeSerialPortDebug ( boutRefNum , " assertion failure " ) ;
writeSerialPortDebug ( boutRefNum , textoutput ) ;
// hold the program - we want to be able to read the text! assuming anything after the assert would be a crash
while ( true ) { }
}
# define NK_ASSERT(e) \
if ( ! ( e ) ) \
aFailed ( __FILE__ , __LINE__ )
# include <Types.h>
# include "nuklear.h"
# include "nuklear_quickdraw.h"
2022-01-03 06:00:22 +00:00
# include "coprocessorjs.h"
2021-11-28 07:48:59 +00:00
2021-12-05 06:27:50 +00:00
# define MAX_CHAT_MESSAGES 16
2021-11-27 07:45:53 +00:00
Boolean firstOrMouseMove = true ;
2021-11-28 07:48:59 +00:00
Boolean gotMouseEvent = false ;
2021-11-27 07:45:53 +00:00
char activeChat [ 64 ] ;
2021-12-05 06:27:50 +00:00
char activeChatMessages [ MAX_CHAT_MESSAGES ] [ 2048 ] ; // this should match to MAX_ROWS in index.js
2021-11-27 07:45:53 +00:00
char box_input_buffer [ 2048 ] ;
2021-11-28 07:48:59 +00:00
char chatFriendlyNames [ 16 ] [ 64 ] ;
2021-11-27 07:45:53 +00:00
char ip_input_buffer [ 255 ] ;
2021-11-28 07:48:59 +00:00
char jsFunctionResponse [ 102400 ] ; // Matches MAX_RECEIVE_SIZE
2021-11-27 07:45:53 +00:00
char new_message_input_buffer [ 255 ] ;
2021-11-28 07:48:59 +00:00
int activeMessageCounter = 0 ;
int chatFriendlyNamesCounter = 0 ;
int coprocessorLoaded = 0 ;
int drawChatsOneMoreTime = 2 ; // this is how many 'iterations' of the UI that we need to see every element for
int forceRedraw = 2 ; // this is how many 'iterations' of the UI that we need to see every element for
int haveRun = 0 ;
int ipAddressSet = 0 ;
int mouse_x ;
int mouse_y ;
int sendNewChat = 0 ;
2021-11-27 07:45:53 +00:00
short box_input_len ;
2021-11-28 07:48:59 +00:00
short box_len ;
2021-11-27 07:45:53 +00:00
short new_message_input_buffer_len ;
static short ip_input_buffer_len ; // TODO: setting a length here will make the default `http://...` work, but doesn't work right -- maybe due to perf work in nuklear
2021-11-28 07:48:59 +00:00
struct nk_rect chats_window_size ;
struct nk_rect graphql_input_window_size ;
struct nk_rect message_input_window_size ;
struct nk_rect messages_window_size ;
2021-12-20 07:54:47 +00:00
struct nk_context * ctx ;
void refreshNuklearApp ( Boolean blankInput ) ;
2021-11-27 07:45:53 +00:00
void getMessagesFromjsFunctionResponse ( ) {
2021-12-05 06:27:50 +00:00
for ( int i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
memset ( & activeChatMessages [ i ] , ' \0 ' , 2048 ) ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
activeMessageCounter = 0 ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// writeSerialPortDebug(boutRefNum, "BEGIN");
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// writeSerialPortDebug(boutRefNum, jsFunctionResponse);
char * token = ( char * ) strtokm ( jsFunctionResponse , " ENDLASTMESSAGE " ) ;
// loop through the string to extract all other tokens
while ( token ! = NULL ) {
// writeSerialPortDebug(boutRefNum, "LOAD VALUE TO TOKEN");
// writeSerialPortDebug(boutRefNum, token);
sprintf ( activeChatMessages [ activeMessageCounter ] , " %s " , token ) ;
// writeSerialPortDebug(boutRefNum, activeChatMessages[activeMessageCounter]);
// writeSerialPortDebug(boutRefNum, "DONE! LOAD VALUE TO TOKEN");
token = ( char * ) strtokm ( NULL , " ENDLASTMESSAGE " ) ;
activeMessageCounter + + ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
2021-11-28 07:48:59 +00:00
2021-11-27 07:45:53 +00:00
// function to send messages in chat
void sendMessage ( ) {
2021-11-28 07:48:59 +00:00
char output [ 2048 ] ;
2022-01-03 07:36:23 +00:00
sprintf ( output , " %s&&&%.*s " , activeChat , box_input_len , box_input_buffer ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
memset ( & box_input_buffer , ' \0 ' , 2048 ) ;
sprintf ( box_input_buffer , " " ) ;
box_input_len = 0 ;
2021-12-05 06:27:50 +00:00
refreshNuklearApp ( 1 ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
callFunctionOnCoprocessor ( " sendMessage " , output , jsFunctionResponse ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
getMessagesFromjsFunctionResponse ( ) ;
2021-11-27 07:45:53 +00:00
2021-12-05 06:27:50 +00:00
forceRedraw = 2 ;
2021-11-28 07:48:59 +00:00
firstOrMouseMove = true ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
void sendIPAddressToCoprocessor ( ) {
2021-11-28 07:48:59 +00:00
char output [ 2048 ] ;
2022-01-03 07:36:23 +00:00
sprintf ( output , " %.*s " , ip_input_buffer_len , ip_input_buffer ) ;
2021-11-27 07:45:53 +00:00
writeSerialPortDebug ( boutRefNum , output ) ;
2021-11-28 07:48:59 +00:00
callFunctionOnCoprocessor ( " setIPAddress " , output , jsFunctionResponse ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
2021-12-05 06:27:50 +00:00
2021-11-27 07:45:53 +00:00
// set up function to get messages in current chat
// limit to recent messages
// figure out pagination?? button on the top that says "get previous chats"?
void getMessages ( char * thread , int page ) {
2021-11-28 07:48:59 +00:00
char output [ 62 ] ;
sprintf ( output , " %s&&&%d " , thread , page ) ;
2021-11-27 07:45:53 +00:00
// writeSerialPortDebug(boutRefNum, output);
2021-11-28 07:48:59 +00:00
callFunctionOnCoprocessor ( " getMessages " , output , jsFunctionResponse ) ;
2021-11-27 07:45:53 +00:00
// writeSerialPortDebug(boutRefNum, jsFunctionResponse);
getMessagesFromjsFunctionResponse ( ) ;
2021-11-28 07:48:59 +00:00
forceRedraw = 3 ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
void getHasNewMessagesInChat ( char * thread ) {
2021-11-28 07:48:59 +00:00
char output [ 62 ] ;
sprintf ( output , " %s " , thread ) ;
2021-11-27 07:45:53 +00:00
// writeSerialPortDebug(boutRefNum, output);
2021-11-28 07:48:59 +00:00
callFunctionOnCoprocessor ( " hasNewMessagesInChat " , output , jsFunctionResponse ) ;
2021-11-27 07:45:53 +00:00
// writeSerialPortDebug(boutRefNum, jsFunctionResponse);
if ( ! strcmp ( jsFunctionResponse , " true " ) ) {
2021-11-28 07:48:59 +00:00
// writeSerialPortDebug(boutRefNum, "update current chat");
SysBeep ( 1 ) ;
getMessages ( thread , 0 ) ;
// force redraw
firstOrMouseMove = true ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
2021-12-20 07:54:47 +00:00
2021-11-27 07:45:53 +00:00
// set up function to get available chat (fill buttons on right hand side)
// run it on some interval? make sure user is not typing!!!
void getChats ( ) {
2021-11-28 07:48:59 +00:00
if ( haveRun ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
haveRun = 1 ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
callFunctionOnCoprocessor ( " getChats " , " " , jsFunctionResponse ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
char * token = ( char * ) strtokm ( jsFunctionResponse , " , " ) ;
// loop through the string to extract all other tokens
while ( token ! = NULL ) {
sprintf ( chatFriendlyNames [ chatFriendlyNamesCounter + + ] , " %s " , token ) ;
token = ( char * ) strtokm ( NULL , " , " ) ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
2021-11-27 07:45:53 +00:00
}
Boolean chatWindowCollision ;
Boolean messageWindowCollision ;
Boolean checkCollision ( struct nk_rect window ) {
2021-11-28 07:48:59 +00:00
// writeSerialPortDebug(boutRefNum, "checkCollision!");
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// Boolean testout = (window.x < mouse_x &&
// window.x + window.w > mouse_x &&
// window.y < mouse_y &&
// window.y + window.h > mouse_y);
// char str[255];
// sprintf(str, "what %d", testout);
// writeSerialPortDebug(boutRefNum, str);
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// if truthy return, mouse is over window!
return ( window . x < mouse_x & &
window . x + window . w > mouse_x & &
window . y < mouse_y & &
window . y + window . h > mouse_y ) ;
2021-11-27 07:45:53 +00:00
}
2021-11-29 07:25:07 +00:00
// UI setup and event handling goes here
2021-11-28 07:48:59 +00:00
static void nuklearApp ( struct nk_context * ctx ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// prompt the user for the graphql instance
if ( ! coprocessorLoaded ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
if ( nk_begin_titled ( ctx , " Loading coprocessor services " , " Loading coprocessor services " , graphql_input_window_size , NK_WINDOW_TITLE | NK_WINDOW_BORDER ) ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 20 , 1 ) ;
{
nk_layout_row_push ( ctx , 200 ) ;
nk_label_wrap ( ctx , " Please wait " ) ;
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_end ( ctx ) ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// prompt the user for the graphql instance
if ( ! ipAddressSet ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
if ( nk_begin_titled ( ctx , " Enter iMessage GraphQL Server " , " Enter iMessage GraphQL Server " , graphql_input_window_size , NK_WINDOW_TITLE | NK_WINDOW_BORDER ) ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 20 , 1 ) ;
{
nk_layout_row_push ( ctx , 200 ) ;
nk_label_wrap ( ctx , " ex: http://127.0.0.1 " ) ;
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 30 , 2 ) ;
{
2021-11-27 07:45:53 +00:00
2021-12-05 06:27:50 +00:00
nk_layout_row_push ( ctx , WINDOW_WIDTH / 2 - 100 ) ;
2021-11-28 07:48:59 +00:00
nk_edit_string ( ctx , NK_EDIT_SIMPLE , ip_input_buffer , & ip_input_buffer_len , 255 , nk_filter_default ) ;
2021-12-05 06:27:50 +00:00
nk_layout_row_push ( ctx , 55 ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
if ( nk_button_label ( ctx , " save " ) ) {
ipAddressSet = 1 ;
forceRedraw = 2 ;
sendIPAddressToCoprocessor ( ) ;
}
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_end ( ctx ) ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// prompt the user for new chat
if ( sendNewChat ) {
2021-11-27 07:45:53 +00:00
2022-01-03 07:36:23 +00:00
if ( nk_begin_titled ( ctx , " Enter New Message Recipient " , " Enter New Message Recipient " , nk_rect ( 50 , WINDOW_HEIGHT / 4 , WINDOW_WIDTH - 100 , 140 ) , NK_WINDOW_TITLE | NK_WINDOW_BORDER ) ) {
2021-11-27 07:45:53 +00:00
2022-01-03 07:36:23 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 30 , 1 ) ;
2021-11-28 07:48:59 +00:00
{
2022-01-03 07:36:23 +00:00
nk_layout_row_push ( ctx , WINDOW_WIDTH - 120 ) ;
nk_label ( ctx , " this is known to be a bit finnicky, " , NK_TEXT_ALIGN_LEFT ) ;
nk_layout_row_push ( ctx , WINDOW_WIDTH - 120 ) ;
nk_label ( ctx , " input exact phone number " , NK_TEXT_ALIGN_LEFT ) ;
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2022-01-03 07:36:23 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 30 , 2 ) ;
{
nk_layout_row_push ( ctx , WINDOW_WIDTH / 2 ) ;
2021-11-28 07:48:59 +00:00
nk_edit_string ( ctx , NK_EDIT_SIMPLE , new_message_input_buffer , & new_message_input_buffer_len , 2048 , nk_filter_default ) ;
2022-01-03 07:36:23 +00:00
nk_layout_row_push ( ctx , 100 ) ;
2021-12-05 06:27:50 +00:00
2021-11-28 07:48:59 +00:00
if ( nk_button_label ( ctx , " open chat " ) ) {
sendNewChat = 0 ;
forceRedraw = 2 ;
2021-11-27 07:45:53 +00:00
2022-01-03 07:36:23 +00:00
sprintf ( activeChat , " %.*s " , new_message_input_buffer_len , new_message_input_buffer ) ;
for ( int i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
memset ( & activeChatMessages [ i ] , ' \0 ' , 2048 ) ;
}
2021-11-28 07:48:59 +00:00
}
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_end ( ctx ) ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
chatWindowCollision = checkCollision ( chats_window_size ) ;
2021-11-27 07:45:53 +00:00
if ( ( chatWindowCollision | | forceRedraw | | drawChatsOneMoreTime ) & & nk_begin ( ctx , " Chats " , chats_window_size , NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR ) ) {
2021-11-28 07:48:59 +00:00
if ( chatWindowCollision ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
drawChatsOneMoreTime = 2 ;
}
2021-11-27 07:45:53 +00:00
getChats ( ) ;
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 25 , 1 ) ;
{
for ( int i = 0 ; i < chatFriendlyNamesCounter ; i + + ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// only display the first 8 chats, create new chat if you need someone not in your list
if ( i > 9 ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
continue ;
}
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_layout_row_push ( ctx , 169 ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
if ( nk_button_label ( ctx , chatFriendlyNames [ i ] ) ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
sprintf ( activeChat , " %s " , chatFriendlyNames [ i ] ) ;
getMessages ( activeChat , 0 ) ;
}
}
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
}
if ( nk_begin ( ctx , " Message Input " , message_input_window_size , NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR ) ) {
2021-12-05 06:27:50 +00:00
// bottom text input
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 28 , 1 ) ;
{
nk_layout_row_push ( ctx , 320 ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
short edit_return_value = nk_edit_string ( ctx , NK_EDIT_FIELD | NK_EDIT_SIG_ENTER , box_input_buffer , & box_input_len , 2048 , nk_filter_default ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
// this is the enter key, obviously
if ( edit_return_value = = 17 ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
sendMessage ( ) ;
}
}
nk_layout_row_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
nk_end ( ctx ) ;
}
2021-11-27 07:45:53 +00:00
if ( ( forceRedraw | | drawChatsOneMoreTime ) & & nk_begin_titled ( ctx , " Message " , activeChat , messages_window_size , NK_WINDOW_BORDER | NK_WINDOW_TITLE | NK_WINDOW_NO_SCROLLBAR ) ) {
2021-11-28 07:48:59 +00:00
nk_layout_row_begin ( ctx , NK_STATIC , 12 , 1 ) ;
{
2021-11-29 07:25:07 +00:00
2021-11-28 07:48:59 +00:00
for ( int i = 0 ; i < activeMessageCounter ; i + + ) {
2021-11-29 07:25:07 +00:00
2021-11-28 07:48:59 +00:00
nk_layout_row_push ( ctx , 305 ) ;
nk_label ( ctx , activeChatMessages [ i ] , NK_TEXT_ALIGN_LEFT ) ;
}
}
nk_layout_row_end ( ctx ) ;
nk_end ( ctx ) ;
2021-11-27 07:45:53 +00:00
}
2021-11-28 07:48:59 +00:00
if ( forceRedraw ) {
2022-01-03 07:36:23 +00:00
forceRedraw = 0 ;
2021-11-28 07:48:59 +00:00
}
if ( drawChatsOneMoreTime ) {
2022-01-03 07:36:23 +00:00
drawChatsOneMoreTime = 0 ;
2021-11-28 07:48:59 +00:00
}
}
2021-12-05 06:27:50 +00:00
void refreshNuklearApp ( Boolean blankInput ) {
nk_input_begin ( ctx ) ;
if ( blankInput ) {
nk_input_key ( ctx , NK_KEY_DEL , 1 ) ;
nk_input_key ( ctx , NK_KEY_DEL , 0 ) ;
}
nk_input_end ( ctx ) ;
nuklearApp ( ctx ) ;
nk_quickdraw_render ( FrontWindow ( ) , ctx ) ;
nk_clear ( ctx ) ;
}
2021-11-28 07:48:59 +00:00
struct nk_context * initializeNuklearApp ( ) {
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
sprintf ( activeChat , " no active chat " ) ;
2021-11-29 07:25:07 +00:00
2021-12-05 06:27:50 +00:00
graphql_input_window_size = nk_rect ( WINDOW_WIDTH / 2 - 118 , 80 , 234 , 100 ) ;
2021-11-28 07:48:59 +00:00
chats_window_size = nk_rect ( 0 , 0 , 180 , WINDOW_HEIGHT ) ;
messages_window_size = nk_rect ( 180 , 0 , 330 , WINDOW_HEIGHT - 36 ) ;
message_input_window_size = nk_rect ( 180 , WINDOW_HEIGHT - 36 , 330 , 36 ) ;
2021-11-29 07:25:07 +00:00
2021-12-05 06:27:50 +00:00
ctx = nk_quickdraw_init ( WINDOW_WIDTH , WINDOW_HEIGHT ) ;
refreshNuklearApp ( false ) ;
2021-11-27 07:45:53 +00:00
2021-11-29 07:25:07 +00:00
sprintf ( ip_input_buffer , " http:// " ) ; // doesn't work due to bug, see variable definition
2021-11-27 07:45:53 +00:00
2021-11-28 07:48:59 +00:00
return ctx ;
2021-11-27 07:45:53 +00:00
}