2024-12-01 19:35:24 +00:00
//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2020-03-23 14:48:49 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
2024-05-29 16:36:57 +00:00
# include "cwTest.h"
2020-03-23 14:48:49 +00:00
# include "cwMem.h"
2024-11-30 17:18:18 +00:00
# include "cwObject.h"
2022-12-13 21:53:10 +00:00
# include "cwTime.h"
2021-01-20 18:10:56 +00:00
# include "cwFileSys.h"
2020-03-23 14:48:49 +00:00
# include "cwThread.h"
2020-10-31 18:17:43 +00:00
# include "cwObject.h"
2020-03-23 14:48:49 +00:00
# include "cwWebSock.h"
# include "cwWebSockSvr.h"
# include "cwText.h"
# include "cwNumericConvert.h"
2020-04-21 18:59:55 +00:00
# include "cwUi.h"
2024-05-11 16:10:21 +00:00
# define UI_CLICKABLE_LABEL "clickable"
# define UI_SELECT_LABEL "select"
# define UI_VISIBLE_LABEL "visible"
# define UI_ENABLE_LABEL "enable"
# define UI_ORDER_LABEL "order"
# define UI_SCROLL_TOP_LABEL "scroll_top"
2020-03-23 14:48:49 +00:00
namespace cw
{
namespace ui
{
2021-11-06 02:21:25 +00:00
typedef struct ele_type_propery_str
{
const char * label ;
bool is_div_fl ;
} ele_type_property_t ;
ele_type_property_t eleTypePropertyA [ ] =
{
{ " div " , true } ,
{ " panel " , true } ,
{ " row " , true } ,
{ " col " , true } ,
{ " label " , false } ,
{ " button " , false } ,
{ " check " , false } ,
{ " select " , false } ,
{ " option " , false } ,
2021-12-27 21:48:58 +00:00
{ " str_disp " , false } ,
2021-11-06 02:21:25 +00:00
{ " string " , false } ,
{ " numb_disp " , false } ,
{ " number " , false } ,
{ " progress " , false } ,
{ " log " , false } ,
2024-10-12 19:18:05 +00:00
{ " vlist " , false } ,
{ " hlist " , false } ,
2021-11-06 02:21:25 +00:00
{ nullptr , false } ,
} ;
2020-04-26 13:19:19 +00:00
2020-03-31 17:10:45 +00:00
typedef struct appIdMapRecd_str
{
struct appIdMapRecd_str * link ;
unsigned parentAppId ;
unsigned appId ;
2020-04-06 23:17:04 +00:00
char * eleName ;
2020-03-31 17:10:45 +00:00
} appIdMapRecd_t ;
2024-02-18 13:49:24 +00:00
2020-03-31 17:10:45 +00:00
2020-03-23 14:48:49 +00:00
typedef struct ele_str
{
2021-11-06 02:21:25 +00:00
struct ele_str * phys_parent ; // pointer to actual parent ele - or nullptr if this ele is the root ui ele
struct ele_str * logical_parent ; // pointer to the nearest ancestor that has a valid appId - this is useful to skip over unnamed containers like rows and columns
2021-11-14 16:54:52 +00:00
unsigned uuId ; // UI unique id - automatically generated and unique among all elements that are part of this ui_t object.
unsigned appId ; // application assigned id - application assigned id
unsigned chanId ; //
char * eleName ; // javascript id
object_t * attr ; // attribute dictionary object
void * blob ; // blob[ blobByteN ] user data
unsigned blobByteN ; //
bool destroyFl ; // used by the deleteElement() algorithm
2020-03-23 14:48:49 +00:00
} ele_t ;
2024-02-18 13:49:24 +00:00
const unsigned hashN = 0xffff ;
2020-03-23 14:48:49 +00:00
2024-02-18 13:49:24 +00:00
typedef struct bucket_str
{
ele_t * ele ;
struct bucket_str * link ;
} bucket_t ;
2020-03-23 14:48:49 +00:00
typedef struct ui_str
{
2023-05-20 01:59:35 +00:00
unsigned eleAllocN ; // size of eleA[]
unsigned eleN ; // count of ele's in use
2024-02-18 13:49:24 +00:00
ele_t * * eleA ; // eleA[ eleAllocN ]
2023-05-20 01:59:35 +00:00
uiCallback_t uiCbFunc ; // app. cb func
void * uiCbArg ; // app. cb func arg.
sendCallback_t sendCbFunc ;
void * sendCbArg ;
appIdMapRecd_t * appIdMap ; // map of application parent/child/js id's
char * buf ; // buf[bufN] output message formatting buffer
unsigned bufN ; //
char * recvBuf ;
unsigned recvBufN ;
unsigned recvBufIdx ;
unsigned recvShiftN ;
object_t * uiRsrc ;
unsigned * sessA ; // sessA[ sessN ] array of wsSessId's
unsigned sessN ;
unsigned sessAllocN ;
2023-12-03 16:18:02 +00:00
2023-05-20 01:59:35 +00:00
bool msgCacheEnableFl ;
unsigned msgCacheSessId ;
char * msgCache ;
unsigned msgCacheAllocN ;
unsigned msgCacheDataN ;
unsigned msgCacheN ;
unsigned msgCacheMsgN ;
2022-12-13 21:53:10 +00:00
2023-12-03 16:18:02 +00:00
unsigned sentMsgN ;
unsigned recvMsgN ;
2024-03-26 02:17:40 +00:00
bucket_t hashA [ hashN + 1 ] ;
2024-02-18 13:49:24 +00:00
2020-03-23 14:48:49 +00:00
} ui_t ;
ui_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , ui_t > ( h ) ; }
2024-02-18 13:49:24 +00:00
unsigned short _gen_hash_index ( unsigned parentUuId , unsigned appId )
{
assert ( parentUuId ! = kInvalidId & & appId ! = kInvalidId ) ;
unsigned hc = parentUuId + cwSwap32 ( appId ) ;
2024-03-26 02:17:40 +00:00
unsigned short hash_idx = ( unsigned short ) ( ( ( hc & 0xffff0000 ) > > 16 ) + ( hc & 0x0000ffff ) ) ;
assert ( hash_idx < = hashN ) ;
return hash_idx ;
2024-02-18 13:49:24 +00:00
}
void _store_ele_in_hash_table ( ui_t * p , ele_t * e )
{
2024-10-19 16:36:12 +00:00
unsigned parentUuId = e - > logical_parent = = nullptr ? kInvalidId : e - > logical_parent - > uuId ;
2024-02-18 13:49:24 +00:00
if ( parentUuId = = kInvalidId | | e - > appId = = kInvalidId )
return ;
unsigned short hash_idx = _gen_hash_index ( parentUuId , e - > appId ) ;
2024-10-19 16:36:12 +00:00
// if the first bucket is empty ...
2024-02-18 13:49:24 +00:00
if ( p - > hashA [ hash_idx ] . ele = = nullptr )
2024-10-19 16:36:12 +00:00
p - > hashA [ hash_idx ] . ele = e ; // ... then fill it
2024-02-18 13:49:24 +00:00
else
{
2024-10-19 16:36:12 +00:00
// otherwise look for an empty bucket
bucket_t * b = p - > hashA [ hash_idx ] . link ;
for ( ; b ! = nullptr ; b = b - > link )
if ( b - > ele = = nullptr )
{
b - > ele = e ; // an empty bucket was found - fill it
return ;
}
// create a new bucket
b = mem : : allocZ < bucket_t > ( ) ;
// and insert it as the second bucket
2024-02-18 13:49:24 +00:00
b - > link = p - > hashA [ hash_idx ] . link ;
b - > ele = e ;
p - > hashA [ hash_idx ] . link = b ;
}
}
unsigned _find_ele_in_hash_table ( ui_t * p , unsigned parentUuId , unsigned appId , unsigned chanId )
{
if ( parentUuId ! = kInvalidId & & appId ! = kInvalidId )
{
unsigned hash_idx = _gen_hash_index ( parentUuId , appId ) ;
bucket_t * b = p - > hashA + hash_idx ;
for ( ; b ! = nullptr ; b = b - > link )
2024-03-28 23:47:29 +00:00
if ( b - > ele ! = nullptr & & b - > ele - > logical_parent ! = nullptr & & b - > ele - > appId = = appId & & b - > ele - > logical_parent - > uuId = = parentUuId & & ( chanId = = kInvalidId | | b - > ele - > chanId = = chanId ) )
2024-02-18 13:49:24 +00:00
return b - > ele - > uuId ;
}
2024-10-19 16:36:12 +00:00
return kInvalidId ;
}
void _remove_ele_from_hash_table_0 ( ui_t * p , const ele_t * e )
{
// Note: hashA[] has hashN+1 elements
for ( unsigned i = 0 ; i < = hashN ; + + i )
{
bucket_t * b = p - > hashA + i ;
for ( ; b ! = nullptr ; b = b - > link )
if ( b - > ele = = e )
{
b - > ele = nullptr ;
break ;
}
}
}
void _remove_ele_from_hash_table ( ui_t * p , const ele_t * e )
{
if ( e = = nullptr )
return ;
unsigned parentUuId = e - > logical_parent = = nullptr ? kInvalidId : e - > logical_parent - > uuId ;
unsigned appId = e - > appId ;
2024-02-18 13:49:24 +00:00
2024-10-19 16:36:12 +00:00
if ( parentUuId = = kInvalidId | | appId = = kInvalidId )
{
//_remove_ele_from_hash_table_0(p,e);
return ;
}
unsigned hash_idx = _gen_hash_index ( parentUuId , appId ) ;
bucket_t * b = p - > hashA + hash_idx ;
// locate the bucket to delete
for ( ; b ! = nullptr ; b = b - > link )
{
if ( b - > ele = = e )
{
b - > ele = nullptr ;
return ;
}
}
_remove_ele_from_hash_table_0 ( p , e ) ;
2024-02-18 13:49:24 +00:00
}
2024-10-19 16:36:12 +00:00
2024-02-18 13:49:24 +00:00
2020-03-31 17:10:45 +00:00
void _print_eles ( ui_t * p )
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr )
{
ele_t * e = p - > eleA [ i ] ;
printf ( " %15s u:%i : u:%i a:%i %s \n " , e - > phys_parent = = nullptr ? " <null> " : e - > phys_parent - > eleName , e - > phys_parent = = nullptr ? - 1 : e - > phys_parent - > uuId , e - > uuId , e - > appId , e - > eleName ) ;
}
}
// Return true if 'parent' is an ancestor of 'ele' (or 'ele' is a child of 'parent')
bool _is_child_of ( const ele_t * parent , const ele_t * ele )
{
if ( ele = = nullptr )
return false ;
if ( ele - > phys_parent = = parent | | ele - > logical_parent = = parent )
return true ;
// go up the tree - are 'ele' parents children of 'parent'?
return _is_child_of ( parent , ele - > phys_parent ) | | _is_child_of ( parent , ele - > logical_parent ) ;
}
2024-10-19 16:36:12 +00:00
void _destroy_element ( ui_t * p , ele_t * e )
2021-11-14 16:54:52 +00:00
{
if ( e = = nullptr )
return ;
2024-10-19 16:36:12 +00:00
2021-11-14 16:54:52 +00:00
if ( e - > attr ! = nullptr )
e - > attr - > free ( ) ;
2024-10-19 16:36:12 +00:00
2021-11-14 16:54:52 +00:00
mem : : release ( e - > eleName ) ;
mem : : release ( e - > blob ) ;
mem : : release ( e ) ;
2020-03-31 17:10:45 +00:00
}
2020-03-23 14:48:49 +00:00
rc_t _destroy ( ui_t * p )
{
rc_t rc = kOkRC ;
2021-11-14 16:54:52 +00:00
// free each element
2022-12-13 21:53:10 +00:00
if ( p - > eleA ! = nullptr )
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2024-10-19 16:36:12 +00:00
{
_destroy_element ( p , p - > eleA [ i ] ) ;
p - > eleA [ i ] = nullptr ;
}
2021-11-14 16:54:52 +00:00
2020-03-31 17:10:45 +00:00
appIdMapRecd_t * m = p - > appIdMap ;
while ( m ! = nullptr )
{
appIdMapRecd_t * m0 = m - > link ;
2020-04-06 23:17:04 +00:00
mem : : release ( m - > eleName ) ;
2020-03-31 17:10:45 +00:00
mem : : release ( m ) ;
m = m0 ;
}
2024-03-26 02:17:40 +00:00
// Note: hashA[] has hashN+1 elements
for ( unsigned i = 0 ; i < = hashN ; + + i )
{
bucket_t * b = p - > hashA [ i ] . link ;
while ( b ! = nullptr )
{
bucket_t * b0 = b - > link ;
mem : : release ( b ) ;
b = b0 ;
}
}
2020-04-26 13:19:19 +00:00
mem : : release ( p - > sessA ) ;
2020-03-23 14:48:49 +00:00
mem : : release ( p - > eleA ) ;
2021-01-20 18:10:56 +00:00
mem : : release ( p - > buf ) ;
2022-05-14 14:16:09 +00:00
mem : : release ( p - > recvBuf ) ;
2022-12-13 21:53:10 +00:00
if ( p - > uiRsrc ! = nullptr )
p - > uiRsrc - > free ( ) ;
2021-11-02 01:46:59 +00:00
2020-03-23 14:48:49 +00:00
mem : : release ( p ) ;
return rc ;
}
2021-11-06 02:21:25 +00:00
ele_type_property_t * _labelToEleTypeProperty ( const char * label )
{
for ( unsigned i = 0 ; eleTypePropertyA [ i ] . label ! = nullptr ; + + i )
if ( textIsEqual ( eleTypePropertyA [ i ] . label , label ) )
return eleTypePropertyA + i ;
return nullptr ;
}
bool _isEleTypeDiv ( const char * label )
{
ele_type_property_t * etp ;
if ( ( etp = _labelToEleTypeProperty ( label ) ) = = nullptr )
return false ;
return etp - > is_div_fl ;
}
bool _isEleTypeLabel ( const char * label )
{ return _labelToEleTypeProperty ( label ) ! = nullptr ; }
2020-04-06 23:17:04 +00:00
appIdMapRecd_t * _findAppIdMap ( ui_t * p , unsigned parentAppId , const char * eleName )
2020-03-31 17:10:45 +00:00
{
appIdMapRecd_t * m = p - > appIdMap ;
for ( ; m ! = nullptr ; m = m - > link )
2020-04-06 23:17:04 +00:00
if ( m - > parentAppId = = parentAppId & & textCompare ( eleName , m - > eleName ) = = 0 )
2020-03-31 17:10:45 +00:00
return m ;
return nullptr ;
}
appIdMapRecd_t * _findAppIdMap ( ui_t * p , unsigned parentAppId , unsigned appId )
{
appIdMapRecd_t * m = p - > appIdMap ;
for ( ; m ! = nullptr ; m = m - > link )
if ( m - > parentAppId = = parentAppId & & m - > appId = = appId )
return m ;
return nullptr ;
}
2020-04-06 23:17:04 +00:00
rc_t _allocAppIdMap ( ui_t * p , unsigned parentAppId , unsigned appId , const char * eleName )
2020-03-31 17:10:45 +00:00
{
rc_t rc = kOkRC ;
2020-04-06 23:17:04 +00:00
// The 'eleName' must be valid (or there is no reason to create the map.
// (since it will ultimately be used to locate the appId give the parentAppId and eleName)
if ( eleName = = nullptr | | strlen ( eleName ) = = 0 )
return cwLogError ( kInvalidIdRC , " Registered parent/child app id's must have a valid 'eleName'. " ) ;
2020-03-31 17:10:45 +00:00
// verify that the parent/child pair is unique
if ( _findAppIdMap ( p , parentAppId , appId ) ! = nullptr )
2020-04-06 23:17:04 +00:00
return cwLogError ( kDuplicateRC , " An attempt was made to register a duplicate parent/child appid pair. parentId:%i appId:%i eleName:'%s'. " , parentAppId , appId , cwStringNullGuard ( eleName ) ) ;
2020-03-31 17:10:45 +00:00
// verify that the parent/js pair is unique
2020-04-06 23:17:04 +00:00
if ( _findAppIdMap ( p , parentAppId , eleName ) ! = nullptr )
return cwLogError ( kDuplicateRC , " An attempt was made to register a duplicate parent app id/js id pair. parentId:%i appId:%i eleName:'%s'. " , parentAppId , appId , cwStringNullGuard ( eleName ) ) ;
2020-04-24 20:35:45 +00:00
2020-03-31 17:10:45 +00:00
// allocate and link in a new appId map record
appIdMapRecd_t * m = mem : : allocZ < appIdMapRecd_t > ( ) ;
m - > parentAppId = parentAppId ;
m - > appId = appId ;
2020-04-23 17:53:30 +00:00
m - > eleName = eleName = = nullptr ? nullptr : mem : : duplStr ( eleName ) ;
2020-03-31 17:10:45 +00:00
m - > link = p - > appIdMap ;
p - > appIdMap = m ;
return rc ;
}
2020-04-27 12:14:26 +00:00
rc_t _registerAppIdMap ( ui_t * p , const appIdMap_t * map , unsigned mapN )
{
rc_t rc = kOkRC ;
if ( map ! = nullptr )
for ( unsigned i = 0 ; i < mapN ; + + i )
if ( ( rc = _allocAppIdMap ( p , map [ i ] . parentAppId , map [ i ] . appId , map [ i ] . eleName ) ) ! = kOkRC )
return rc ;
return rc ;
}
2020-03-31 17:10:45 +00:00
// Given a uuId return a pointer to the associated element.
2020-03-23 14:48:49 +00:00
ele_t * _uuIdToEle ( ui_t * p , unsigned uuId , bool errorFl = true )
{
if ( uuId > = p - > eleN )
{
cwLogError ( kInvalidIdRC , " The element uuid:%i is not valid. " , uuId ) ;
return nullptr ;
}
return p - > eleA [ uuId ] ;
}
2020-04-06 23:17:04 +00:00
ele_t * _eleNameToEle ( ui_t * p , const char * eleName , bool errorFl = true )
2020-03-31 17:10:45 +00:00
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr )
if ( textCompare ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
return p - > eleA [ i ] ;
2020-04-06 23:17:04 +00:00
if ( errorFl )
cwLogError ( kInvalidIdRC , " The element with eleName:%s not found. " , cwStringNullGuard ( eleName ) ) ;
return nullptr ;
}
2021-11-06 02:21:25 +00:00
unsigned _findElementUuId ( ui_t * p , unsigned parentUuId , const char * eleName , unsigned chanId = kInvalidId )
2020-03-23 14:48:49 +00:00
{
2020-03-31 17:10:45 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > logical_parent ! = nullptr ) // skip the root
2021-11-06 02:21:25 +00:00
{
if ( ( parentUuId = = kInvalidId | | p - > eleA [ i ] - > logical_parent - > uuId = = parentUuId ) & &
( chanId = = kInvalidId | | p - > eleA [ i ] - > chanId = = chanId ) & &
( textCompare ( p - > eleA [ i ] - > eleName , eleName ) = = 0 ) )
{
return p - > eleA [ i ] - > uuId ;
}
}
2020-03-31 17:10:45 +00:00
return kInvalidId ;
2020-03-23 14:48:49 +00:00
}
2021-11-06 02:21:25 +00:00
unsigned _findElementUuId ( ui_t * p , unsigned parentUuId , unsigned appId , unsigned chanId = kInvalidId )
2021-01-22 14:46:10 +00:00
{
2021-11-06 02:21:25 +00:00
if ( appId = = kRootAppId )
return kRootUuId ;
2024-01-06 13:44:53 +00:00
2024-02-18 13:49:24 +00:00
// try looking up the result in the hash table
unsigned uuid ;
if ( ( uuid = _find_ele_in_hash_table ( p , parentUuId , appId , chanId ) ) ! = kInvalidId )
return uuid ;
// if the result could not be found in the hash table (possibly because parentUuId is set to the wildcard i.e. kInvalidId)
// then do an exhaustive search
2021-01-22 14:46:10 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2024-01-06 13:44:53 +00:00
if ( p - > eleA [ i ] ! = nullptr
& & p - > eleA [ i ] - > appId = = appId
& & ( parentUuId = = kInvalidId | | ( p - > eleA [ i ] - > logical_parent ! = nullptr & & p - > eleA [ i ] - > logical_parent - > uuId = = parentUuId ) )
& & ( chanId = = kInvalidId | | p - > eleA [ i ] - > chanId = = chanId ) )
2021-11-06 02:21:25 +00:00
{
2024-02-18 13:49:24 +00:00
2024-01-06 13:44:53 +00:00
return p - > eleA [ i ] - > uuId ;
2021-11-06 02:21:25 +00:00
}
2024-01-06 13:44:53 +00:00
2021-01-22 14:46:10 +00:00
return kInvalidId ;
}
2020-04-06 23:17:04 +00:00
const char * _findEleEleName ( ui_t * p , unsigned uuId )
2020-03-23 14:48:49 +00:00
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > uuId = = uuId )
2020-04-06 23:17:04 +00:00
return p - > eleA [ i ] - > eleName ;
2020-03-23 14:48:49 +00:00
return nullptr ;
}
2023-12-03 16:18:02 +00:00
rc_t _send_callback ( ui_t * p , unsigned wsSessId , const void * msg , unsigned msgByteN )
{
p - > sentMsgN + = 1 ;
rc_t rc = p - > sendCbFunc ( p - > sendCbArg , wsSessId , msg , msgByteN ) ;
return rc ;
}
2023-05-20 01:59:35 +00:00
rc_t _cache_flush ( ui_t * p )
{
rc_t rc = kOkRC ;
if ( p - > msgCacheMsgN > 0 )
{
assert ( p - > msgCacheN < = p - > msgCacheDataN ) ;
p - > msgCache [ p - > msgCacheN + 0 ] = ' ] ' ;
p - > msgCache [ p - > msgCacheN + 1 ] = ' } ' ;
p - > msgCache [ p - > msgCacheN + 2 ] = 0 ;
2023-12-03 16:18:02 +00:00
rc = _send_callback ( p , p - > msgCacheSessId , p - > msgCache , strlen ( p - > msgCache ) ) ;
2023-05-20 01:59:35 +00:00
p - > msgCacheN = snprintf ( p - > msgCache , p - > msgCacheAllocN , " { \" op \" : \" cache \" , \" array \" : [ " ) ;
p - > msgCacheSessId = kInvalidId ;
p - > msgCacheMsgN = 0 ;
}
return rc ;
}
rc_t _cache_send ( ui_t * p , unsigned wsSessId , const char * msg , unsigned msgByteN )
{
rc_t rc = kOkRC ;
unsigned msgByteCommaN = msgByteN + 1 ;
// if the cache buffer has not yet been allocated
if ( p - > msgCache = = nullptr & & p - > msgCacheAllocN > 0 )
{
p - > msgCacheDataN = p - > msgCacheAllocN - 3 ; // "]}/0" three char's must be reserved to terminate the cache message (See _cache_flush().)
p - > msgCache = mem : : allocZ < char > ( p - > msgCacheAllocN ) ;
p - > msgCacheSessId = kInvalidId ;
p - > msgCacheN = snprintf ( p - > msgCache , p - > msgCacheDataN , " { \" op \" : \" cache \" , \" array \" : [ " ) ;
p - > msgCacheMsgN = 0 ;
}
if ( wsSessId ! = p - > msgCacheSessId | | p - > msgCacheN + msgByteCommaN > p - > msgCacheDataN | | msgByteN > p - > msgCacheDataN )
rc = _cache_flush ( p ) ;
if ( msgByteN > p - > msgCacheDataN )
2023-12-03 16:18:02 +00:00
rc = _send_callback ( p , wsSessId , msg , msgByteN ) ;
2023-05-20 01:59:35 +00:00
else
{
assert ( p - > msgCacheN + msgByteCommaN < = p - > msgCacheDataN ) ;
// if this isn't the first msg is the buffer then prepend a ','
if ( p - > msgCacheMsgN ! = 0 )
strncat ( p - > msgCache , " , " , 2 ) ;
else
msgByteCommaN - = 1 ; // otherwise the msgByteCommaN is the same as msgByteN
strncat ( p - > msgCache , msg , msgByteN ) ;
p - > msgCacheN + = msgByteCommaN ;
p - > msgCacheSessId = wsSessId ;
p - > msgCacheMsgN + = 1 ;
}
return rc ;
}
rc_t _send_or_cache ( ui_t * p , unsigned sessId , const char * msg , unsigned msgByteCnt )
{
rc_t rc = kOkRC ;
if ( p - > msgCacheEnableFl )
rc = _cache_send ( p , sessId , msg , msgByteCnt ) ;
else
2023-12-03 16:18:02 +00:00
rc = _send_callback ( p , sessId , msg , msgByteCnt ) ;
2023-05-20 01:59:35 +00:00
return rc ;
}
2020-03-31 17:10:45 +00:00
rc_t _websockSend ( ui_t * p , unsigned wsSessId , const char * msg )
{
2020-04-07 20:24:34 +00:00
rc_t rc = kOkRC ;
if ( p - > sendCbFunc ! = nullptr )
{
unsigned msgByteN = msg = = nullptr ? 0 : strlen ( msg ) ;
2021-01-22 14:46:10 +00:00
if ( wsSessId ! = kInvalidId )
2023-05-20 01:59:35 +00:00
rc = _send_or_cache ( p , wsSessId , msg , msgByteN ) ;
2021-01-22 14:46:10 +00:00
else
2022-12-13 21:53:10 +00:00
{
for ( unsigned i = 0 ; i < p - > sessN ; + + i )
2023-05-20 01:59:35 +00:00
rc = _send_or_cache ( p , p - > sessA [ i ] , msg , msgByteN ) ;
}
2020-04-07 20:24:34 +00:00
}
return rc ;
2020-03-31 17:10:45 +00:00
}
2021-11-03 15:03:30 +00:00
// terminating condition for format_attributes()
void _create_attributes ( ele_t * e )
{ }
template < typename T , typename . . . ARGS >
void _create_attributes ( ele_t * e , const char * label , T value , ARGS & & . . . args )
{
e - > attr - > insert_pair ( label , value ) ;
_create_attributes ( e , std : : forward < ARGS > ( args ) . . . ) ;
}
bool _has_attribute ( ele_t * e , const char * label )
{ return e - > attr - > find_child ( label ) ! = nullptr ; }
2021-11-14 16:54:52 +00:00
template < typename T >
rc_t _set_attribute ( ele_t * e , const char * label , const T & value )
2021-11-03 15:03:30 +00:00
{
object_t * pair_value ;
if ( ( pair_value = e - > attr - > find ( label ) ) = = nullptr )
_create_attributes ( e , label , value ) ;
else
pair_value - > set_value ( value ) ;
2021-11-14 16:54:52 +00:00
return kOkRC ;
2021-11-03 15:03:30 +00:00
}
2021-11-14 16:54:52 +00:00
template < typename T >
rc_t _get_attribute ( ele_t * e , const char * label , T & valueRef )
{
rc_t rc = kOkRC ;
const object_t * pair_value ;
if ( ( pair_value = e - > attr - > find ( label ) ) = = nullptr )
{
rc = cwLogError ( kInvalidIdRC , " Unable to locate the UI element attribute '%s' on uuid:%i. " , cwStringNullGuard ( label ) , e - > uuId ) ;
goto errLabel ;
}
rc = pair_value - > value ( valueRef ) ;
errLabel :
return rc ;
}
2021-11-03 15:03:30 +00:00
2021-11-02 01:46:59 +00:00
// Convert the ele_t 'attr' object into the attributes for a JSON message.
unsigned _format_attributes ( char * buf , unsigned n , unsigned i , ele_t * ele )
{
assert ( ele - > attr ! = nullptr ) ;
for ( unsigned j = 0 ; j < ele - > attr - > child_count ( ) & & i < n ; + + j )
{
object_t * ch = ele - > attr - > child_ele ( j ) ;
i + = toText ( buf + i , n - i , " , \" " ) ;
i + = toText ( buf + i , n - i , ch - > pair_label ( ) ) ;
i + = toText ( buf + i , n - i , " \" : " ) ;
i + = ch - > pair_value ( ) - > to_string ( buf + i , n - i ) ;
}
return i ;
}
rc_t _transmitOneEle ( ui_t * p , unsigned wsSessId , ele_t * ele )
{
rc_t rc = kOkRC ;
assert ( ele ! = nullptr ) ;
2021-11-06 02:21:25 +00:00
assert ( ele - > phys_parent ! = nullptr ) ;
2021-11-02 01:46:59 +00:00
unsigned i = snprintf ( p - > buf , p - > bufN ,
" { \" op \" : \" create \" , \" parentUuId \" : \" %i \" , \" eleName \" : \" %s \" , \" appId \" : \" %i \" , \" uuId \" :%i " ,
2021-11-06 02:21:25 +00:00
ele - > phys_parent - > uuId ,
2021-11-02 01:46:59 +00:00
ele - > eleName = = nullptr ? " " : ele - > eleName ,
ele - > appId ,
ele - > uuId ) ;
// add the UI specific attributes
i + = _format_attributes ( p - > buf + i , p - > bufN - i , 0 , ele ) ;
// terminate the message
i + = toText ( p - > buf + i , p - > bufN - i , " } " ) ;
if ( i > = p - > bufN )
return cwLogError ( kBufTooSmallRC , " The UI message formatting buffer is too small. (size:%i bytes) " , p->bufN) ;
//printf("%s\n",p->buf);
// send the message
rc = _websockSend ( p , wsSessId , p - > buf ) ;
return rc ;
}
rc_t _transmitTree ( ui_t * p , unsigned wsSessId , ele_t * ele )
{
rc_t rc ;
2022-12-13 21:53:10 +00:00
2021-11-02 01:46:59 +00:00
// transmit the parent (unelss 'ele' is the root
if ( ele - > uuId = = kRootUuId | | ( rc = _transmitOneEle ( p , wsSessId , ele ) ) = = kOkRC )
{
// Transmit each of the children to the remote UI's.
// Note that this requires going through all the nodes and picking out the ones whose
// parent uuid matches the current ele's uuid
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > uuId ! = kRootUuId & & p - > eleA [ i ] - > uuId ! = ele - > uuId & & p - > eleA [ i ] - > phys_parent - > uuId = = ele - > uuId )
2021-11-02 01:46:59 +00:00
if ( ( rc = _transmitTree ( p , wsSessId , p - > eleA [ i ] ) ) ! = kOkRC )
break ;
}
return rc ;
}
2021-11-14 16:54:52 +00:00
unsigned _find_and_available_element_slot ( ui_t * p )
{
2024-02-18 13:49:24 +00:00
if ( p - > eleN < p - > eleAllocN & & p - > eleA [ p - > eleN ] = = nullptr )
return p - > eleN ;
2021-11-14 16:54:52 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] = = nullptr )
return i ;
return p - > eleN ;
}
2021-11-02 01:46:59 +00:00
2021-11-01 14:11:27 +00:00
// Create the base element record. The attributes mut be filled in by the calling function.
// Note that if 'appId' is kInvalidId then this function will attempt to lookup the appId in p->appIdMap[].
2021-11-03 15:03:30 +00:00
ele_t * _createBaseEle ( ui_t * p , ele_t * parent , unsigned appId , unsigned chanId , const char * eleName , const char * eleTypeStr = nullptr , const char * eleClass = nullptr , const char * eleTitle = nullptr )
2020-04-24 20:35:45 +00:00
{
ele_t * e = mem : : allocZ < ele_t > ( ) ;
2021-11-06 02:21:25 +00:00
ele_t * logical_parent = nullptr ;
2021-11-04 13:33:42 +00:00
2021-11-06 02:21:25 +00:00
// Go up the tree looking for the first parent with a valid appId.
// The logical parent is the first ancestor element that has a valid 'appId'.
2021-11-04 13:33:42 +00:00
// This is useful to cover the situation where the parent is an unamed div (e.g. row, col, panel)
// and the appIdMap gives the panel name as the parent.
2020-04-28 20:37:36 +00:00
2021-11-04 13:33:42 +00:00
if ( parent ! = nullptr )
{
2021-11-06 02:21:25 +00:00
for ( ele_t * par = parent ; par ! = nullptr ; par = par - > phys_parent )
2021-11-04 13:33:42 +00:00
if ( par - > appId ! = kInvalidId )
{
2021-11-06 02:21:25 +00:00
logical_parent = par ;
2021-11-04 13:33:42 +00:00
break ;
}
}
2021-11-06 02:21:25 +00:00
assert ( appId = = kRootAppId | | logical_parent ! = nullptr ) ; // because the root always has a valid appid
2021-11-04 13:33:42 +00:00
2021-11-06 02:21:25 +00:00
e - > phys_parent = parent ;
e - > logical_parent = logical_parent ;
e - > appId = appId ;
e - > chanId = chanId ;
e - > eleName = eleName = = nullptr ? nullptr : mem : : duplStr ( eleName ) ;
e - > attr = newDictObject ( ) ;
2021-11-01 14:11:27 +00:00
if ( eleTypeStr ! = nullptr )
2021-11-02 01:46:59 +00:00
e - > attr - > insert_pair ( " type " , eleTypeStr ) ;
2021-11-01 14:11:27 +00:00
if ( eleClass ! = nullptr )
2021-11-02 01:46:59 +00:00
e - > attr - > insert_pair ( " className " , eleClass ) ;
2020-04-26 21:27:29 +00:00
2021-11-01 14:11:27 +00:00
if ( eleTitle ! = nullptr )
2021-11-02 01:46:59 +00:00
e - > attr - > insert_pair ( " title " , eleTitle ) ;
2021-11-01 14:11:27 +00:00
2021-11-04 13:33:42 +00:00
// elements default to visible and enabled
e - > attr - > insert_pair ( " visible " , true ) ;
e - > attr - > insert_pair ( " enable " , true ) ;
2021-11-14 16:54:52 +00:00
// locate the next available element in p->eleA[]
unsigned avail_ele_idx = _find_and_available_element_slot ( p ) ;
// if there are no available slots
if ( avail_ele_idx = = p - > eleAllocN )
2020-04-24 20:35:45 +00:00
{
2024-01-06 13:44:53 +00:00
p - > eleAllocN * = 2 ;
2020-04-24 20:35:45 +00:00
p - > eleA = mem : : resizeZ < ele_t * > ( p - > eleA , p - > eleAllocN ) ;
}
2021-11-14 16:54:52 +00:00
assert ( avail_ele_idx < = p - > eleN & & p - > eleAllocN > avail_ele_idx ) ;
// assign the new element to a slot in p->eleA[]
p - > eleA [ avail_ele_idx ] = e ;
// the ele uuid is the same as it's index in p->eleA[]
e - > uuId = avail_ele_idx ;
// track the count of elements in p->eleA[]
if ( avail_ele_idx = = p - > eleN )
p - > eleN + = 1 ;
2020-04-24 20:35:45 +00:00
// if the given appId was not valid ...
2024-10-12 19:18:05 +00:00
if ( appId = = kInvalidId & & parent ! = nullptr & & eleName ! = nullptr )
2020-04-24 20:35:45 +00:00
{
appIdMapRecd_t * m ;
2021-11-04 13:33:42 +00:00
2020-04-24 20:35:45 +00:00
// ... then try to look it up from the appIdMap.
2021-11-06 02:21:25 +00:00
if ( ( m = _findAppIdMap ( p , e - > logical_parent - > appId , eleName ) ) ! = nullptr )
2020-04-24 20:35:45 +00:00
e - > appId = m - > appId ;
}
2020-04-26 21:27:29 +00:00
2024-02-18 13:49:24 +00:00
_store_ele_in_hash_table ( p , e ) ;
2020-04-30 19:10:54 +00:00
//printf("uuid:%i appId:%i par-uuid:%i %s\n", e->uuId,e->appId,e->parent==nullptr ? -1 : e->parent->uuId, cwStringNullGuard(e->eleName));
2020-04-26 21:27:29 +00:00
2020-04-24 20:35:45 +00:00
return e ;
}
2021-11-01 14:11:27 +00:00
template < typename . . . ARGS >
2021-11-03 15:03:30 +00:00
rc_t _createOneEle ( ui_t * p , unsigned & uuIdRef , const char * eleTypeStr , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , ARGS & & . . . args )
2021-11-01 14:11:27 +00:00
{
rc_t rc = kOkRC ;
ele_t * newEle = nullptr ;
ele_t * parentEle = nullptr ;
uuIdRef = kInvalidId ;
if ( parentUuId = = kInvalidId )
parentUuId = kRootUuId ;
// get the parent element
if ( ( parentEle = _uuIdToEle ( p , parentUuId ) ) = = nullptr )
return cwLogError ( kInvalidArgRC , " Unable to locate the parent element (id:%i) . " , parentUuId ) ;
// create the base element
2021-11-03 15:03:30 +00:00
newEle = _createBaseEle ( p , parentEle , appId , chanId , eleName , eleTypeStr , clas , title ) ;
2021-11-01 14:11:27 +00:00
// create the attributes
2021-11-03 15:03:30 +00:00
_create_attributes ( newEle , std : : forward < ARGS > ( args ) . . . ) ;
2021-11-01 14:11:27 +00:00
uuIdRef = newEle - > uuId ;
2021-11-02 01:46:59 +00:00
rc = _transmitOneEle ( p , wsSessId , newEle ) ;
2021-11-01 14:11:27 +00:00
return rc ;
}
2021-11-03 15:03:30 +00:00
rc_t _createElementsFromChildList ( ui_t * p , const object_t * po , unsigned wsSessId , ele_t * parentEle , unsigned chanId ) ;
2021-11-02 01:46:59 +00:00
2021-11-03 15:03:30 +00:00
rc_t _createEleFromRsrsc ( ui_t * p , ele_t * parentEle , const char * eleType , unsigned chanId , const object_t * srcObj , unsigned wsSessId )
2020-03-25 15:35:37 +00:00
{
2020-04-27 16:31:10 +00:00
rc_t rc = kOkRC ;
object_t * co = nullptr ;
ele_t * ele = nullptr ;
char * eleName = nullptr ;
object_t * o = srcObj - > duplicate ( ) ; // duplicate the rsrc object so that we can modify it.
bool divAliasFl = false ;
2020-03-25 15:35:37 +00:00
2020-04-06 23:17:04 +00:00
if ( ! o - > is_dict ( ) )
return cwLogError ( kSyntaxErrorRC , " All ui element resource records must be dictionaries. " ) ;
2020-04-24 20:35:45 +00:00
// if this object has a 'children' list then unlink it and save it for later
2021-11-03 15:03:30 +00:00
if ( ( co = o - > find ( " children " , kOptionalFl ) ) ! = nullptr )
2020-03-25 15:35:37 +00:00
{
2020-04-06 23:17:04 +00:00
co = co - > parent ;
co - > unlink ( ) ;
2020-03-31 17:10:45 +00:00
}
2021-11-06 02:21:25 +00:00
divAliasFl = _isEleTypeDiv ( eleType ) ;
2020-04-28 12:40:53 +00:00
2020-04-06 23:17:04 +00:00
// get the ui ele name
2021-11-03 15:03:30 +00:00
if ( ( rc = o - > get ( " name " , eleName , cw : : kOptionalFl ) ) ! = kOkRC )
2020-03-31 17:10:45 +00:00
{
2020-04-28 21:39:18 +00:00
// div's and titles don't need a 'name'
2024-02-08 15:55:48 +00:00
if ( rc = = kEleNotFoundRC & & ( divAliasFl | | textCompare ( eleType , " label " ) = = 0 ) )
2020-04-28 12:40:53 +00:00
rc = kOkRC ;
else
{
2022-12-13 21:53:10 +00:00
rc = cwLogError ( rc , " The UI element 'name' could not be read. " ) ;
2020-04-28 12:40:53 +00:00
goto errLabel ;
}
2020-03-31 17:10:45 +00:00
}
2021-11-02 01:46:59 +00:00
2020-04-06 23:17:04 +00:00
// get or create the ele record to associate with this ele
2021-11-03 15:03:30 +00:00
if ( ( ele = _createBaseEle ( p , parentEle , kInvalidId , chanId , eleName , eleType , nullptr , nullptr ) ) = = nullptr )
2020-04-06 23:17:04 +00:00
{
rc = cwLogError ( kOpFailRC , " The local element '%s' could not be created. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
2021-11-06 02:21:25 +00:00
else
2020-03-31 17:10:45 +00:00
{
2021-11-03 15:03:30 +00:00
unsigned childN = o - > child_count ( ) ;
unsigned child_idx = 0 ;
2021-11-02 01:46:59 +00:00
// transfer the attributes of this resource object to ele->attr
2021-11-03 15:03:30 +00:00
for ( unsigned i = 0 ; i < childN ; + + i )
2020-03-31 17:10:45 +00:00
{
2021-11-03 15:03:30 +00:00
object_t * child = o - > child_ele ( child_idx ) ;
2021-11-02 01:46:59 +00:00
const char * pair_label = child - > pair_label ( ) ;
2021-11-03 15:03:30 +00:00
//if( textCompare(eleType,"list")==0 )
// printf("%i list: %s %i\n",i,pair_label,o->child_count());
2021-11-06 02:21:25 +00:00
// skip the 'name' attribute any child notes that refer to child elements
if ( textIsEqual ( pair_label , " name " ) | | _isEleTypeLabel ( pair_label ) )
child_idx + = 1 ;
else
2021-11-02 01:46:59 +00:00
{
child - > unlink ( ) ;
ele - > attr - > append_child ( child ) ;
2021-11-03 15:03:30 +00:00
}
2020-03-31 17:10:45 +00:00
}
2020-04-06 23:17:04 +00:00
}
2021-11-02 01:46:59 +00:00
_transmitOneEle ( p , wsSessId , ele ) ;
2020-04-27 16:31:10 +00:00
2020-04-06 23:17:04 +00:00
// if this element has a list of children then create them here
2020-04-27 16:31:10 +00:00
if ( co ! = nullptr | | divAliasFl )
2020-04-24 20:35:45 +00:00
{
// Note that 'div's need not have an explicit 'children' node.
// Any child node of a 'div' with a dictionary as a value is a child control.
const object_t * childL = co ! = nullptr ? co - > pair_value ( ) : srcObj ;
2021-11-03 15:03:30 +00:00
rc = _createElementsFromChildList ( p , childL , wsSessId , ele , chanId ) ;
2020-04-24 20:35:45 +00:00
}
2020-04-06 23:17:04 +00:00
errLabel :
if ( co ! = nullptr )
co - > free ( ) ;
2020-04-21 18:56:42 +00:00
if ( o ! = nullptr )
o - > free ( ) ;
2020-04-06 23:17:04 +00:00
return rc ;
}
2021-11-02 01:46:59 +00:00
2024-10-12 19:18:05 +00:00
// 'po' is an object dictionary where each pair in the dictionary has
2020-04-06 23:17:04 +00:00
// the form: 'eleType':{ <object> }
2021-11-03 15:03:30 +00:00
rc_t _createElementsFromChildList ( ui_t * p , const object_t * po , unsigned wsSessId , ele_t * parentEle , unsigned chanId )
2020-04-06 23:17:04 +00:00
{
rc_t rc = kOkRC ;
if ( ! po - > is_dict ( ) )
return cwLogError ( kSyntaxErrorRC , " All UI resource elements must be containers. " ) ;
unsigned childN = po - > child_count ( ) ;
for ( unsigned i = 0 ; i < childN ; + + i )
{
2020-04-21 18:56:42 +00:00
const object_t * o = po - > child_ele ( i ) ;
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
if ( ! o - > is_pair ( ) )
return cwLogError ( kSyntaxErrorRC , " All object dictionary children must be pairs. " ) ;
2020-04-21 18:56:42 +00:00
// skip pairs whose value is not a dict
if ( o - > pair_value ( ) - > is_dict ( ) )
2021-11-02 01:46:59 +00:00
{
2021-11-03 15:03:30 +00:00
if ( ( rc = _createEleFromRsrsc ( p , parentEle , o - > pair_label ( ) , chanId , o - > pair_value ( ) , wsSessId ) ) ! = kOkRC )
2020-04-21 18:56:42 +00:00
return rc ;
2021-11-02 01:46:59 +00:00
}
2020-04-06 23:17:04 +00:00
}
return rc ;
}
2020-03-31 17:10:45 +00:00
2024-10-12 19:18:05 +00:00
// This functions assumes that parentUuId is valid or the cfg object 'o' contains a field named: 'parent'
2021-11-02 01:46:59 +00:00
// which contains the element name of the parent node.
2021-11-03 15:03:30 +00:00
rc_t _createFromObj ( ui_t * p , const object_t * o , unsigned wsSessId , unsigned parentUuId , unsigned chanId )
2020-04-06 23:17:04 +00:00
{
2021-01-20 18:10:56 +00:00
rc_t rc = kOkRC ;
const object_t * po = nullptr ;
ele_t * parentEle = nullptr ;
char * eleName = nullptr ;
2020-04-06 23:17:04 +00:00
2021-11-02 01:46:59 +00:00
if ( parentUuId = = kInvalidId )
{
// locate the the 'parent' ele name value object
2021-11-03 15:03:30 +00:00
if ( ( po = o - > find ( " parent " , kOptionalFl ) ) = = nullptr )
2021-11-02 01:46:59 +00:00
return cwLogError ( kSyntaxErrorRC , " UI resources must have a root 'parent' value. " ) ;
2020-04-06 23:17:04 +00:00
2021-11-02 01:46:59 +00:00
// get the parent element name
if ( ( rc = po - > value ( eleName ) ) ! = kOkRC )
return cwLogError ( kOpFailRC , " The root 'parent' value could not be accessed. " ) ;
2020-04-06 23:17:04 +00:00
2021-11-02 01:46:59 +00:00
// find the parent element
parentEle = _eleNameToEle ( p , eleName ) ;
}
else
{
// find the parent element
parentEle = _uuIdToEle ( p , parentUuId ) ;
}
if ( parentEle = = nullptr )
2020-04-06 23:17:04 +00:00
return cwLogError ( kSyntaxErrorRC , " A parent UI element named '%s' could not be found. " , cwStringNullGuard ( eleName ) ) ;
2021-11-02 01:46:59 +00:00
2020-04-06 23:17:04 +00:00
2021-11-03 15:03:30 +00:00
rc = _createElementsFromChildList ( p , o , wsSessId , parentEle , chanId ) ;
2020-04-06 23:17:04 +00:00
2020-03-25 15:35:37 +00:00
return rc ;
}
2020-04-06 23:17:04 +00:00
2021-11-02 01:46:59 +00:00
2020-04-07 20:24:34 +00:00
// value message format: 'value' <uuid> <value_data_type> ':' <value>
2020-03-23 14:48:49 +00:00
ele_t * _parse_value_msg ( ui_t * p , value_t & valueRef , const char * msg )
{
rc_t rc = kOkRC ;
char argType = 0 ;
ele_t * ele = nullptr ;
unsigned eleUuId = kInvalidId ;
valueRef . tid = kInvalidTId ;
if ( msg = = nullptr )
{
2021-11-03 15:03:30 +00:00
cwLogWarning ( " Empty 'value' message received from UI. " ) ;
2020-03-23 14:48:49 +00:00
return nullptr ;
}
// locate the colon prior to the value
const char * s = strchr ( msg , ' : ' ) ;
if ( s = = nullptr | | sscanf ( msg , " value %i %c " , & eleUuId , & argType ) ! = 2 )
{
2021-11-03 15:03:30 +00:00
cwLogError ( kSyntaxErrorRC , " Invalid 'value' message from UI: '%s'. " , msg ) ;
2020-03-23 14:48:49 +00:00
goto errLabel ;
}
// advance s past the colon
s + = 1 ;
// parse the argument
switch ( argType )
{
case ' b ' :
if ( ( rc = string_to_number < bool > ( s , valueRef . u . b ) ) = = kOkRC )
valueRef . tid = kBoolTId ;
break ;
case ' i ' :
if ( ( rc = string_to_number < int > ( s , valueRef . u . i ) ) = = kOkRC )
valueRef . tid = kIntTId ;
break ;
case ' u ' :
if ( ( rc = string_to_number < unsigned > ( s , valueRef . u . u ) ) = = kOkRC )
valueRef . tid = kUIntTId ;
break ;
case ' f ' :
if ( ( rc = string_to_number < float > ( s , valueRef . u . f ) ) = = kOkRC )
valueRef . tid = kFloatTId ;
break ;
case ' d ' :
if ( ( rc = string_to_number < double > ( s , valueRef . u . d ) ) = = kOkRC )
valueRef . tid = kDoubleTId ;
break ;
case ' s ' :
2021-01-31 16:07:29 +00:00
if ( ( valueRef . u . s = nextNonWhiteChar ( s ) ) ! = nullptr )
2020-03-23 14:48:49 +00:00
valueRef . tid = kStringTId ;
break ;
default :
rc = cwLogError ( kInvalidIdRC , " Unknown value type '%c' in message from UI. " , argType ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
errLabel :
return ele ;
}
2020-03-31 17:10:45 +00:00
2021-12-30 02:43:56 +00:00
ele_t * _parse_corrupt_msg ( ui_t * p , const char * msg )
{
ele_t * ele = nullptr ;
unsigned eleUuId = kInvalidId ;
if ( msg = = nullptr )
{
cwLogWarning ( " Empty 'corrupt' message received from UI. " ) ;
return nullptr ;
}
//
if ( sscanf ( msg , " corrupt %i " , & eleUuId ) ! = 1 )
{
cwLogError ( kSyntaxErrorRC , " Invalid 'corrupt' message from UI: '%s'. " , msg ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
errLabel :
return ele ;
}
2021-11-03 15:03:30 +00:00
ele_t * _parse_click_msg ( ui_t * p , const char * msg )
{
ele_t * ele = nullptr ;
unsigned eleUuId = kInvalidId ;
if ( msg = = nullptr )
{
cwLogWarning ( " Empty click message received from UI. " ) ;
return nullptr ;
}
//
if ( sscanf ( msg , " click %i " , & eleUuId ) ! = 1 )
{
cwLogError ( kSyntaxErrorRC , " Invalid 'click' message from UI: '%s'. " , msg ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
errLabel :
return ele ;
}
2021-11-14 16:54:52 +00:00
ele_t * _parse_select_msg ( ui_t * p , value_t & valueRef , const char * msg )
{
ele_t * ele = nullptr ;
unsigned eleUuId = kInvalidId ;
unsigned selectFl = 0 ;
if ( msg = = nullptr )
{
cwLogWarning ( " Empty select message received from UI. " ) ;
return nullptr ;
}
//
if ( sscanf ( msg , " select %i %i " , & eleUuId , & selectFl ) ! = 2 )
{
cwLogError ( kSyntaxErrorRC , " Invalid 'select' message from UI: '%s'. " , msg ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
valueRef . u . b = selectFl ? true : false ;
errLabel :
return ele ;
}
2021-11-03 15:03:30 +00:00
2020-04-24 14:20:25 +00:00
ele_t * _parse_echo_msg ( ui_t * p , const char * msg )
{
unsigned eleUuId = kInvalidId ;
ele_t * ele = nullptr ;
if ( sscanf ( msg , " echo %i " , & eleUuId ) ! = 1 )
{
cwLogError ( kSyntaxErrorRC , " Invalid message from UI: '%s'. " , msg ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
errLabel :
return ele ;
}
2020-03-31 17:10:45 +00:00
opId_t _labelToOpId ( const char * label )
{
typedef struct
{
opId_t id ;
const char * label ;
} map_t ;
map_t mapA [ ] =
2020-04-07 20:24:34 +00:00
{
{ kConnectOpId , " connect " } ,
{ kInitOpId , " init " } ,
{ kValueOpId , " value " } ,
2021-12-30 02:43:56 +00:00
{ kCorruptOpId , " corrupt " } ,
2021-11-03 15:03:30 +00:00
{ kClickOpId , " click " } ,
2021-11-14 16:54:52 +00:00
{ kSelectOpId , " select " } ,
2020-04-24 14:20:25 +00:00
{ kEchoOpId , " echo " } ,
2020-04-26 13:19:19 +00:00
{ kIdleOpId , " idle " } ,
2020-04-07 20:24:34 +00:00
{ kDisconnectOpId , " disconnect " } ,
{ kInvalidOpId , " <invalid> " } ,
} ;
2020-03-31 17:10:45 +00:00
for ( unsigned i = 0 ; mapA [ i ] . id ! = kInvalidOpId ; + + i )
if ( textCompare ( label , mapA [ i ] . label , strlen ( mapA [ i ] . label ) ) = = 0 )
return mapA [ i ] . id ;
return kInvalidOpId ;
2020-03-23 14:48:49 +00:00
}
2020-04-24 14:20:25 +00:00
template < typename T >
2021-11-03 15:03:30 +00:00
rc_t _sendValue ( ui_t * p , unsigned wsSessId , unsigned uuId , const char * vFmt , const T & value , const char * opStr = " value " , int vbufN = 32 )
2020-04-24 14:20:25 +00:00
{
rc_t rc = kOkRC ;
if ( p - > sendCbFunc ! = nullptr )
{
2021-11-03 15:03:30 +00:00
const char * mFmt = " { \" op \" : \" %s \" , \" uuId \" :%i, \" value \" :%s } " ;
2022-03-20 14:24:36 +00:00
const int mbufN = 1024 ;
2020-04-24 14:20:25 +00:00
char vbuf [ vbufN ] ;
char mbuf [ mbufN ] ;
if ( snprintf ( vbuf , vbufN , vFmt , value ) > = vbufN - 1 )
return cwLogError ( kBufTooSmallRC , " The value msg buffer is too small. " ) ;
2021-11-03 15:03:30 +00:00
if ( snprintf ( mbuf , mbufN , mFmt , opStr , uuId , vbuf ) > = mbufN - 1 )
2020-04-24 14:20:25 +00:00
return cwLogError ( kBufTooSmallRC , " The msg buffer is too small. " ) ;
2021-01-22 14:46:10 +00:00
_websockSend ( p , wsSessId , mbuf ) ;
2020-04-24 14:20:25 +00:00
}
return rc ;
}
2022-12-13 21:53:10 +00:00
rc_t _sendValue ( ui_t * p , unsigned wsSessId , unsigned uuId , const value_t & value )
{
rc_t rc = kOkRC ;
switch ( value . tid )
{
case kBoolTId :
rc = _sendValue < int > ( p , wsSessId , uuId , " %i " , value . u . b ? 1 : 0 ) ;
break ;
case kIntTId :
rc = _sendValue < int > ( p , wsSessId , uuId , " %i " , value . u . i ) ;
break ;
case kUIntTId :
rc = _sendValue < unsigned > ( p , wsSessId , uuId , " %i " , value . u . u ) ;
break ;
case kFloatTId :
rc = _sendValue < float > ( p , wsSessId , uuId , " %f " , value . u . f ) ;
break ;
case kDoubleTId :
rc = _sendValue < double > ( p , wsSessId , uuId , " %f " , value . u . d ) ;
break ;
case kStringTId :
rc = _sendValue < const char * > ( p , kInvalidId , uuId , " \" %s \" " , value . u . s , " value " , strlen ( value . u . s ) + 10 ) ;
break ;
default :
assert ( 0 ) ;
}
return rc ;
}
void _reflect_value_msg ( ui_t * p , unsigned wsSessId , ele_t * ele , const value_t & value )
{
// send a 'value' message to all sessions except 'wsSessId'.
for ( unsigned i = 0 ; i < p - > sessN ; + + i )
if ( p - > sessA [ i ] ! = wsSessId )
_sendValue ( p , p - > sessA [ i ] , ele - > uuId , value ) ;
}
2021-11-02 01:46:59 +00:00
rc_t _onNewRemoteUi ( ui_t * p , unsigned wsSessId )
2021-01-20 18:10:56 +00:00
{
rc_t rc = kOkRC ;
2021-11-02 01:46:59 +00:00
ele_t * rootEle ;
if ( ( rootEle = _uuIdToEle ( p , kRootUuId ) ) = = nullptr )
{
rc = cwLogError ( kInvalidStateRC , " Unable to locate the UI root element. " ) ;
goto errLabel ;
}
_transmitTree ( p , wsSessId , rootEle ) ;
errLabel :
2021-01-20 18:10:56 +00:00
return rc ;
}
2021-11-03 15:03:30 +00:00
2021-11-14 16:54:52 +00:00
template < typename T >
rc_t _setPropertyValue ( handle_t h , const char * propertyStr , unsigned uuId , const T & value )
2021-11-03 15:03:30 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
ele_t * ele = nullptr ;
2021-11-14 16:54:52 +00:00
const char * mFmt = " { \" op \" : \" set \" , \" type \" : \" %s \" , \" uuId \" :%i, \" value \" :%i } " ;
2021-11-03 15:03:30 +00:00
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
2021-11-14 16:54:52 +00:00
if ( snprintf ( mbuf , mbufN , mFmt , propertyStr , uuId , value ) > = mbufN - 1 )
2021-11-03 15:03:30 +00:00
{
rc = cwLogError ( kBufTooSmallRC , " The msg buffer is too small. " ) ;
goto errLabel ;
}
if ( ( ele = _uuIdToEle ( p , uuId ) ) = = nullptr )
{
rc = kInvalidIdRC ;
goto errLabel ;
}
2021-11-14 16:54:52 +00:00
if ( ( rc = _set_attribute ( ele , propertyStr , value ) ) ! = kOkRC )
2021-11-03 15:03:30 +00:00
{
cwLogError ( rc , " Property assignment failed. " ) ;
goto errLabel ;
}
if ( ( rc = _websockSend ( p , kInvalidId , mbuf ) ) ! = kOkRC )
{
cwLogError ( rc , " '%s' msg transmit failed. " , propertyStr ) ;
goto errLabel ;
}
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Set '%s' failed. " , propertyStr ) ;
return rc ;
}
2021-11-14 16:54:52 +00:00
rc_t _setPropertyFlag ( handle_t h , const char * propertyStr , unsigned uuId , bool enableFl )
{
return _setPropertyValue ( h , propertyStr , uuId , enableFl ? 1 : 0 ) ;
}
2022-09-10 14:18:52 +00:00
rc_t _copy_msg_to_recv_buffer ( ui_t * p , const void * msg , unsigned msgByteN )
{
2022-09-10 14:58:30 +00:00
if ( msg = = nullptr | | msgByteN = = 0 )
return kOkRC ;
2022-09-10 14:18:52 +00:00
if ( p - > recvBufIdx + msgByteN > p - > recvBufN )
2022-09-10 14:58:30 +00:00
return cwLogError ( kBufTooSmallRC , " The UI input buffer (%i) is too small . " , p->recvBufN) ;
2022-09-10 14:18:52 +00:00
2022-09-10 14:58:30 +00:00
memcpy ( p - > recvBuf + p - > recvBufIdx , msg , msgByteN ) ;
2022-10-08 16:50:22 +00:00
2022-09-10 14:58:30 +00:00
p - > recvBufIdx + = msgByteN ;
2022-12-13 21:53:10 +00:00
2022-09-10 14:58:30 +00:00
return kOkRC ;
2022-09-10 14:18:52 +00:00
}
const char * _get_msg_from_recv_buffer ( ui_t * p )
{
const char * msg = nullptr ;
unsigned i ;
// shift off the previous msg
if ( p - > recvShiftN > 0 )
{
2022-09-10 14:58:30 +00:00
assert ( p - > recvBufIdx > = p - > recvShiftN ) ;
2022-09-10 14:18:52 +00:00
memmove ( p - > recvBuf , p - > recvBuf + p - > recvShiftN , p - > recvBufIdx - p - > recvShiftN ) ;
2022-09-10 14:58:30 +00:00
p - > recvBufIdx - = p - > recvShiftN ;
2022-09-10 14:18:52 +00:00
p - > recvShiftN = 0 ;
2022-09-10 14:58:30 +00:00
2022-09-10 14:18:52 +00:00
}
// locate the end of the next msg.
2022-10-08 16:50:22 +00:00
if ( p - > recvBufIdx > 0 )
2022-09-10 14:18:52 +00:00
{
2022-10-08 16:50:22 +00:00
for ( i = 0 ; p - > recvBuf [ i ] ! = 0 and i < p - > recvBufIdx ; + + i )
{ }
// if the end of the next msg was found
if ( i < p - > recvBufIdx & & p - > recvBuf [ i ] = = 0 )
{
p - > recvShiftN = i + 1 ;
msg = p - > recvBuf ;
}
2022-09-10 14:18:52 +00:00
}
2022-10-08 16:50:22 +00:00
2022-09-10 14:18:52 +00:00
return msg ;
}
2020-03-23 14:48:49 +00:00
}
}
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : create (
2020-04-27 12:14:26 +00:00
handle_t & h ,
sendCallback_t sendCbFunc ,
void * sendCbArg ,
uiCallback_t uiCbFunc ,
void * uiCbArg ,
2021-01-20 18:10:56 +00:00
const object_t * uiRsrc ,
2020-04-27 12:14:26 +00:00
const appIdMap_t * appIdMapA ,
unsigned appIdMapN ,
unsigned fmtBufByteN )
2020-03-23 14:48:49 +00:00
{
rc_t rc = kOkRC ;
2020-03-31 17:10:45 +00:00
ele_t * ele ;
2020-04-07 20:24:34 +00:00
if ( ( rc = destroy ( h ) ) ! = kOkRC )
2020-03-23 14:48:49 +00:00
return rc ;
2020-04-07 20:24:34 +00:00
if ( sendCbFunc = = nullptr )
return cwLogError ( kInvalidArgRC , " The UI send callback function must be a valid pointer. " ) ;
if ( uiCbFunc = = nullptr )
2020-03-31 17:10:45 +00:00
return cwLogError ( kInvalidArgRC , " The UI callback function must be a valid pointer. " ) ;
2020-03-23 14:48:49 +00:00
ui_t * p = mem : : allocZ < ui_t > ( ) ;
2024-01-06 13:44:53 +00:00
p - > eleAllocN = 1024 ;
2020-04-07 20:24:34 +00:00
p - > eleA = mem : : allocZ < ele_t * > ( p - > eleAllocN ) ;
p - > eleN = 0 ;
p - > uiCbFunc = uiCbFunc ;
p - > uiCbArg = uiCbArg ;
p - > sendCbFunc = sendCbFunc ;
p - > sendCbArg = sendCbArg ;
p - > buf = mem : : allocZ < char > ( fmtBufByteN ) ;
p - > bufN = fmtBufByteN ;
2022-05-14 14:16:09 +00:00
p - > recvBuf = mem : : allocZ < char > ( fmtBufByteN ) ;
p - > recvBufN = fmtBufByteN ;
p - > recvBufIdx = 0 ;
2022-09-10 14:18:52 +00:00
p - > recvShiftN = 0 ;
2022-12-13 21:53:10 +00:00
p - > uiRsrc = uiRsrc - > duplicate ( ) ;
2023-05-20 01:59:35 +00:00
p - > msgCacheSessId = kInvalidId ;
2021-01-20 18:10:56 +00:00
2020-03-31 17:10:45 +00:00
// create the root element
2021-11-03 15:03:30 +00:00
if ( ( ele = _createBaseEle ( p , nullptr , kRootAppId , kInvalidId , " uiDivId " ) ) = = nullptr | | ele - > uuId ! = kRootUuId )
2020-03-31 17:10:45 +00:00
{
cwLogError ( kOpFailRC , " The UI root element creation failed. " ) ;
goto errLabel ;
}
2020-03-23 14:48:49 +00:00
2020-04-28 12:40:53 +00:00
// register any supplied appId maps
2020-04-27 12:14:26 +00:00
if ( ( rc = _registerAppIdMap ( p , appIdMapA , appIdMapN ) ) ! = kOkRC )
goto errLabel ;
2021-11-02 01:46:59 +00:00
if ( uiRsrc ! = nullptr )
2022-12-13 21:53:10 +00:00
{
const object_t * main_obj = nullptr ;
2021-11-02 01:46:59 +00:00
2022-12-20 15:33:51 +00:00
if ( ( main_obj = uiRsrc - > find ( " main " ) ) = = nullptr )
cwLogWarning ( " The UI resource does not have a 'main' element. " ) ;
else
2022-12-13 21:53:10 +00:00
if ( ( rc = _createFromObj ( p , main_obj , kInvalidId , kRootUuId , kInvalidId ) ) ! = kOkRC )
2024-02-18 22:58:16 +00:00
{
2022-12-13 21:53:10 +00:00
rc = cwLogError ( rc , " Create from UI resource failed. " ) ;
2024-02-18 22:58:16 +00:00
goto errLabel ;
}
2022-12-13 21:53:10 +00:00
}
2023-12-03 16:18:02 +00:00
2020-04-27 12:14:26 +00:00
2020-03-23 14:48:49 +00:00
h . set ( p ) ;
2020-04-27 12:14:26 +00:00
2020-03-23 14:48:49 +00:00
errLabel :
if ( rc ! = kOkRC )
{
_destroy ( p ) ;
}
return rc ;
}
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : destroy ( handle_t & h )
2020-03-23 14:48:49 +00:00
{
rc_t rc = kOkRC ;
if ( ! h . isValid ( ) )
return rc ;
ui_t * p = _handleToPtr ( h ) ;
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
return rc ;
h . clear ( ) ;
return rc ;
}
2023-05-20 01:59:35 +00:00
cw : : rc_t cw : : ui : : enableCache ( handle_t h , unsigned cacheByteCnt )
{
ui_t * p = _handleToPtr ( h ) ;
p - > msgCacheEnableFl = true ;
p - > msgCacheAllocN = cacheByteCnt ;
return kOkRC ;
}
cw : : rc_t cw : : ui : : flushCache ( handle_t h )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
if ( p - > msgCacheEnableFl & & p - > msgCache ! = nullptr )
rc = _cache_flush ( p ) ;
return rc ;
}
2020-04-26 21:27:29 +00:00
2020-04-26 13:19:19 +00:00
unsigned cw : : ui : : sessionIdCount ( handle_t h )
{
ui_t * p = _handleToPtr ( h ) ;
return p - > sessN ;
}
const unsigned * cw : : ui : : sessionIdArray ( handle_t h )
{
ui_t * p = _handleToPtr ( h ) ;
return p - > sessA ;
}
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : onConnect ( handle_t h , unsigned wsSessId )
{
2020-04-24 14:20:25 +00:00
ui_t * p = _handleToPtr ( h ) ;
2020-04-26 13:19:19 +00:00
// if the session id array is full ...
if ( p - > sessN = = p - > sessAllocN )
{
// ... then expand it
p - > sessAllocN + = 16 ;
p - > sessA = mem : : resizeZ < unsigned > ( p - > sessA , p - > sessAllocN ) ;
}
// append the new session id
p - > sessA [ p - > sessN + + ] = wsSessId ;
2021-11-03 15:03:30 +00:00
p - > uiCbFunc ( p - > uiCbArg , wsSessId , kConnectOpId , kInvalidId , kInvalidId , kInvalidId , kInvalidId , nullptr ) ;
2020-04-07 20:24:34 +00:00
return kOkRC ;
}
cw : : rc_t cw : : ui : : onDisconnect ( handle_t h , unsigned wsSessId )
{
2020-04-24 14:20:25 +00:00
ui_t * p = _handleToPtr ( h ) ;
2020-04-26 13:19:19 +00:00
2021-11-03 15:03:30 +00:00
p - > uiCbFunc ( p - > uiCbArg , wsSessId , kDisconnectOpId , kInvalidId , kInvalidId , kInvalidId , kInvalidId , nullptr ) ;
2022-12-13 21:53:10 +00:00
2020-04-26 13:19:19 +00:00
// erase the disconnected session id by shrinking the array
for ( unsigned i = 0 ; i < p - > sessN ; + + i )
if ( p - > sessA [ i ] = = wsSessId )
{
for ( ; i + 1 < p - > sessN ; + + i )
p - > sessA [ i ] = p - > sessA [ i + 1 ] ;
p - > sessN - = 1 ;
break ;
}
2020-04-07 20:24:34 +00:00
return kOkRC ;
}
2022-12-13 21:53:10 +00:00
2022-05-14 14:16:09 +00:00
cw : : rc_t cw : : ui : : onReceive ( handle_t h , unsigned wsSessId , const void * void_msg , unsigned msgByteN )
2020-04-07 20:24:34 +00:00
{
2022-09-10 14:18:52 +00:00
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
opId_t opId = kInvalidOpId ;
ele_t * ele = nullptr ;
const char * msg = nullptr ;
value_t value ;
/*
2022-05-14 14:16:09 +00:00
const char * src_msg = ( const char * ) void_msg ;
const char * msg = src_msg ;
// if the incoming message is valid
if ( msgByteN > 0 and src_msg ! = nullptr )
{
// if there is a partial msg in the recv buffer (recvBufIdx!=0)
// or the incoming message is a partial mesg - then buffer the message
// (Note: incoming messages that are not zero terminated are partial.")
if ( p - > recvBufIdx ! = 0 | | src_msg [ msgByteN - 1 ] ! = 0 )
{
// verify the buffer is large enough to hold the msg
if ( p - > recvBufIdx + msgByteN > p - > recvBufN )
{
rc = cwLogError ( kOpFailRC , " The UI input buffer (%i) is too small. " , p - > recvBufN ) ;
p - > recvBufIdx = 0 ;
}
else
{
// update it with the incoming text
strncpy ( p - > recvBuf + p - > recvBufIdx , src_msg , msgByteN ) ;
p - > recvBufIdx + = msgByteN ;
msg = p - > recvBuf ;
}
}
// if the incoming message is not zero terminated then it was a partial
// message it was buffered and there is nothing else to do.
if ( src_msg [ msgByteN - 1 ] ! = 0 )
return rc ;
}
// the message is being processed so the buffer will end up empty
// (if it was being used)
p - > recvBufIdx = 0 ;
2022-09-10 14:18:52 +00:00
*/
2022-05-14 14:16:09 +00:00
2022-09-10 14:18:52 +00:00
// buffer the incoming msg
if ( ( rc = _copy_msg_to_recv_buffer ( p , void_msg , msgByteN ) ) ! = kOkRC )
goto errLabel ;
// remove and and act on each buffered msg
while ( ( msg = _get_msg_from_recv_buffer ( p ) ) ! = NULL )
2020-04-07 20:24:34 +00:00
{
2022-09-10 14:18:52 +00:00
2023-12-03 16:18:02 +00:00
p - > recvMsgN + = 1 ;
2022-09-10 14:18:52 +00:00
// parse the 'opId' from the message
opId = _labelToOpId ( msg ) ;
switch ( opId )
{
case kInitOpId :
// if the app cfg included a reference to a UI resource file then instantiate it here
_onNewRemoteUi ( p , wsSessId ) ;
2021-01-20 18:10:56 +00:00
2022-09-10 14:18:52 +00:00
// Pass on the 'init' msg to the app.
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , kInvalidId , kInvalidId , kInvalidId , kInvalidId , nullptr ) ;
break ;
2020-04-07 20:24:34 +00:00
2022-09-10 14:18:52 +00:00
case kValueOpId :
if ( ( ele = _parse_value_msg ( p , value , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI 'value' message parse failed. " ) ;
else
{
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , ele - > logical_parent - > appId , ele - > uuId , ele - > appId , ele - > chanId , & value ) ;
2022-12-13 21:53:10 +00:00
// reflect the UI element value state to other wsSessions
_reflect_value_msg ( p , wsSessId , ele , value ) ;
2022-09-10 14:18:52 +00:00
}
break ;
2020-04-07 20:24:34 +00:00
2022-09-10 14:18:52 +00:00
case kCorruptOpId :
if ( ( ele = _parse_corrupt_msg ( p , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI 'corrupt' message parse failed. " ) ;
else
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , ele - > logical_parent - > appId , ele - > uuId , ele - > appId , ele - > chanId , & value ) ;
break ;
2021-12-30 02:43:56 +00:00
2022-09-10 14:18:52 +00:00
case kClickOpId :
if ( ( ele = _parse_click_msg ( p , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI 'click' message parse failed. " ) ;
else
{
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , ele - > logical_parent - > appId , ele - > uuId , ele - > appId , ele - > chanId , & value ) ;
}
break ;
2021-11-14 16:54:52 +00:00
2022-09-10 14:18:52 +00:00
case kSelectOpId :
if ( ( ele = _parse_select_msg ( p , value , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI 'select' message parse failed. " ) ;
else
{
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , ele - > logical_parent - > appId , ele - > uuId , ele - > appId , ele - > chanId , & value ) ;
}
break ;
2021-11-03 15:03:30 +00:00
2022-09-10 14:18:52 +00:00
case kEchoOpId :
if ( ( ele = _parse_echo_msg ( p , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI Echo message parse failed. " ) ;
else
{
p - > uiCbFunc ( p - > uiCbArg , wsSessId , opId , ele - > logical_parent - > appId , ele - > uuId , ele - > appId , ele - > chanId , nullptr ) ;
}
break ;
2020-04-24 14:20:25 +00:00
2022-09-10 14:18:52 +00:00
case kIdleOpId :
p - > uiCbFunc ( p - > uiCbArg , kInvalidId , opId , kInvalidId , kInvalidId , kInvalidId , kInvalidId , nullptr ) ;
break ;
2020-04-26 13:19:19 +00:00
2022-09-10 14:18:52 +00:00
case kInvalidOpId :
cwLogError ( kInvalidIdRC , " The UI received a NULL op. id. " ) ;
break ;
2020-04-07 20:24:34 +00:00
2022-09-10 14:18:52 +00:00
default :
cwLogError ( kInvalidIdRC , " The UI received an unknown op. id. " ) ;
break ;
2020-04-07 20:24:34 +00:00
2022-09-10 14:18:52 +00:00
} // switch opId
}
2022-12-13 21:53:10 +00:00
2022-09-10 14:18:52 +00:00
errLabel :
2020-04-07 20:24:34 +00:00
return rc ;
}
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : parentAndNameToAppId ( handle_t h , unsigned parentAppId , const char * eleName )
2020-03-23 14:48:49 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > logical_parent - > appId = = parentAppId & & strcmp ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
2020-03-23 14:48:49 +00:00
return p - > eleA [ i ] - > appId ;
2021-11-02 01:46:59 +00:00
2020-03-23 14:48:49 +00:00
return kInvalidId ;
}
2021-11-04 13:33:42 +00:00
unsigned cw : : ui : : parentAndNameToUuId ( handle_t h , unsigned parentAppId , const char * eleName )
2020-03-23 14:48:49 +00:00
{
2020-03-31 17:10:45 +00:00
ui_t * p = _handleToPtr ( h ) ;
2021-11-02 01:46:59 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > logical_parent - > appId = = parentAppId & & strcmp ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
2021-11-02 01:46:59 +00:00
return p - > eleA [ i ] - > uuId ;
2020-03-23 14:48:49 +00:00
return kInvalidId ;
}
2021-11-04 13:33:42 +00:00
unsigned cw : : ui : : parentAndAppIdToUuId ( handle_t h , unsigned parentAppId , unsigned appId )
2020-03-23 14:48:49 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr )
if ( ( ( p - > eleA [ i ] - > phys_parent = = nullptr & & parentAppId = = kRootAppId ) | |
2021-12-27 21:48:58 +00:00
( p - > eleA [ i ] - > logical_parent ! = nullptr & & p - > eleA [ i ] - > logical_parent - > appId = = parentAppId ) )
2021-11-14 16:54:52 +00:00
& & p - > eleA [ i ] - > appId = = appId )
return p - > eleA [ i ] - > uuId ;
2020-03-23 14:48:49 +00:00
return kInvalidId ;
}
2020-04-06 23:17:04 +00:00
const char * cw : : ui : : findElementName ( handle_t h , unsigned uuId )
2020-03-23 14:48:49 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2020-04-06 23:17:04 +00:00
return _findEleEleName ( p , uuId ) ;
2020-03-23 14:48:49 +00:00
}
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : findElementAppId ( handle_t h , unsigned uuId )
2020-03-31 17:10:45 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2021-11-06 02:21:25 +00:00
ele_t * ele = _uuIdToEle ( p , uuId ) ;
return ele = = nullptr ? kInvalidId : ele - > uuId ;
2020-03-31 17:10:45 +00:00
}
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , const char * eleName , unsigned chanId )
2021-01-22 14:46:10 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2021-11-06 02:21:25 +00:00
return _findElementUuId ( p , kInvalidId , eleName , chanId ) ;
2021-01-22 14:46:10 +00:00
}
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , unsigned appId , unsigned chanId )
{
ui_t * p = _handleToPtr ( h ) ;
return _findElementUuId ( p , kInvalidId , appId , chanId ) ;
}
2021-01-22 14:46:10 +00:00
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , unsigned parentUuId , const char * eleName , unsigned chanId )
2020-04-30 19:10:54 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2021-11-06 02:21:25 +00:00
return _findElementUuId ( p , parentUuId , eleName , chanId ) ;
}
2020-04-30 19:10:54 +00:00
2021-11-06 02:21:25 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , unsigned parentUuId , unsigned appId , unsigned chanId )
{
ui_t * p = _handleToPtr ( h ) ;
return _findElementUuId ( p , parentUuId , appId , chanId ) ;
2020-04-30 19:10:54 +00:00
}
2021-11-06 02:21:25 +00:00
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : ui : : createFromObject ( handle_t h , const object_t * o , unsigned parentUuId , unsigned chanId , const char * cfgFieldName )
2020-04-21 18:59:55 +00:00
{
2021-11-01 14:11:27 +00:00
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
//ele_t* parentEle = nullptr;
2021-11-14 16:54:52 +00:00
if ( cfgFieldName ! = nullptr )
if ( ( o = o - > find ( cfgFieldName ) ) = = nullptr )
2020-04-21 18:59:55 +00:00
{
2021-11-14 16:54:52 +00:00
rc = cwLogError ( kSyntaxErrorRC , " Unable to locate the '%s' sub-configuration. " , cwStringNullGuard ( cfgFieldName ) ) ;
2020-04-21 18:59:55 +00:00
goto errLabel ;
}
2021-11-01 14:11:27 +00:00
2021-11-03 15:03:30 +00:00
if ( ( rc = _createFromObj ( p , o , kInvalidId , parentUuId , chanId ) ) ! = kOkRC )
2020-04-21 18:59:55 +00:00
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
2020-04-21 21:56:38 +00:00
rc = cwLogError ( rc , " UI instantiation from object failed. " ) ;
2020-04-21 18:59:55 +00:00
return rc ;
}
2020-03-25 15:35:37 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createFromFile ( handle_t h , const char * fn , unsigned parentUuId , unsigned chanId )
2020-03-25 15:35:37 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
object_t * o = nullptr ;
if ( ( rc = objectFromFile ( fn , o ) ) ! = kOkRC )
goto errLabel ;
2020-03-31 17:10:45 +00:00
2021-11-03 15:03:30 +00:00
if ( ( rc = _createFromObj ( p , o , kInvalidId , parentUuId , chanId ) ) ! = kOkRC )
2020-03-25 15:35:37 +00:00
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
2020-03-31 17:10:45 +00:00
rc = cwLogError ( rc , " UI instantiation from the configuration file '%s' failed. " , cwStringNullGuard ( fn ) ) ;
2020-03-25 15:35:37 +00:00
if ( o ! = nullptr )
o - > free ( ) ;
return rc ;
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createFromText ( handle_t h , const char * text , unsigned parentUuId , unsigned chanId )
2020-03-25 15:35:37 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
object_t * o = nullptr ;
if ( ( rc = objectFromString ( text , o ) ) ! = kOkRC )
goto errLabel ;
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
if ( ( rc = _createFromObj ( p , o , kInvalidId , parentUuId , chanId ) ) ! = kOkRC )
2020-03-25 15:35:37 +00:00
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
2020-03-31 17:10:45 +00:00
rc = cwLogError ( rc , " UI instantiation failed from the configuration from string: '%s'. " , cwStringNullGuard ( text ) ) ;
2020-03-25 15:35:37 +00:00
if ( o ! = nullptr )
o - > free ( ) ;
return rc ;
}
2022-12-13 21:53:10 +00:00
cw : : rc_t cw : : ui : : createFromRsrc ( handle_t h , const char * label , unsigned parentUuId , unsigned chanId )
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
if ( p - > uiRsrc = = nullptr )
rc = cwLogError ( kInvalidStateRC , " The UI resource '%s' was not found because the UI sub-system was not given a resource file. " , cwStringNullGuard ( label ) ) ;
else
rc = createFromObject ( h , p - > uiRsrc , parentUuId , chanId , label ) ;
return rc ;
}
2020-03-25 15:35:37 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createDiv ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " div " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createLabel ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " label " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createButton ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " button " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createCheck ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " check " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-05-18 15:03:42 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createCheck ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , bool value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " check " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " value " , value ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createSelect ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " select " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createOption ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " option " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2021-12-27 21:48:58 +00:00
cw : : rc_t cw : : ui : : createStrDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " str_disp " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
cw : : rc_t cw : : ui : : createStrDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , const char * value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " str_disp " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " value " , value ) ; }
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createStr ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " string " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2020-05-18 15:03:42 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createStr ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , const char * value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " string " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " value " , value ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createNumbDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , unsigned decpl )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " numb_disp " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " decpl " , decpl ) ; }
2020-05-18 15:03:42 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createNumbDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , unsigned decpl , double value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " numb_disp " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " decpl " , decpl , " value " , value ) ; }
2020-05-18 15:03:42 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createNumb ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double stepValue , unsigned decpl )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " number " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " min " , minValue , " max " , maxValue , " step " , stepValue , " decpl " , decpl ) ; }
2020-05-18 15:03:42 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createNumb ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double stepValue , unsigned decpl , double value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " number " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " min " , minValue , " max " , maxValue , " step " , stepValue , " decpl " , decpl , " value " , value ) ; }
2020-05-15 15:59:39 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createProg ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " progress " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " min " , minValue , " max " , maxValue ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createProg ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " progress " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title , " value " , value , " min " , minValue , " max " , maxValue ) ; }
2020-03-23 14:48:49 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : createLog ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " log " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2021-10-12 20:52:08 +00:00
2024-10-12 19:18:05 +00:00
cw : : rc_t cw : : ui : : createVList ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-11-03 15:03:30 +00:00
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " list " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
2021-11-01 14:11:27 +00:00
2024-10-12 19:18:05 +00:00
cw : : rc_t cw : : ui : : createHList ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " hlist " , kInvalidId , parentUuId , eleName , appId , chanId , clas , title ) ; }
cw : : rc_t cw : : ui : : setTitle ( handle_t h , unsigned uuId , const char * title )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
const char * mFmt = " { \" op \" : \" set \" , \" type \" : \" title \" , \" uuId \" :%i, \" value \" : \" %s \" } " ;
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
if ( snprintf ( mbuf , mbufN , mFmt , uuId , title ) > = mbufN - 1 )
return cwLogError ( kBufTooSmallRC , " The msg buffer is too small. " ) ;
rc = _websockSend ( p , kInvalidId , mbuf ) ;
return rc ;
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : setNumbRange ( handle_t h , unsigned uuId , double minValue , double maxValue , double stepValue , unsigned decPl , double value )
2021-10-12 20:52:08 +00:00
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
2021-11-03 15:03:30 +00:00
const char * mFmt = " { \" op \" : \" set \" , \" type \" : \" number_range \" , \" uuId \" :%i, \" min \" :%f, \" max \" :%f, \" step \" :%f, \" decpl \" :%i, \" value \" :%f } " ;
2021-10-12 20:52:08 +00:00
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
if ( snprintf ( mbuf , mbufN , mFmt , uuId , minValue , maxValue , stepValue , decPl , value ) > = mbufN - 1 )
return cwLogError ( kBufTooSmallRC , " The msg buffer is too small. " ) ;
2021-11-03 15:03:30 +00:00
rc = _websockSend ( p , kInvalidId , mbuf ) ;
2021-10-12 20:52:08 +00:00
return rc ;
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : setProgRange ( handle_t h , unsigned uuId , double minValue , double maxValue , double value )
2021-10-12 20:52:08 +00:00
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
2021-11-03 15:03:30 +00:00
const char * mFmt = " { \" op \" : \" set \" , \" type \" : \" progress_range \" , \" uuId \" :%i, \" min \" :%f, \" max \" :%f, \" value \" :%f } " ;
2021-10-12 20:52:08 +00:00
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
if ( snprintf ( mbuf , mbufN , mFmt , uuId , minValue , maxValue , value ) > = mbufN - 1 )
return cwLogError ( kBufTooSmallRC , " The msg buffer is too small. " ) ;
2021-11-03 15:03:30 +00:00
rc = _websockSend ( p , kInvalidId , mbuf ) ;
2021-10-12 20:52:08 +00:00
return rc ;
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : setLogLine ( handle_t h , unsigned uuId , const char * text )
2020-03-23 14:48:49 +00:00
{
2021-10-12 20:52:08 +00:00
rc_t rc = kOkRC ;
unsigned n = 0 ;
const char * c = text ;
for ( ; * c ; + + c )
if ( * c = = ' \n ' )
+ + n ;
if ( n = = 0 )
2021-11-03 15:03:30 +00:00
rc = sendValueString ( h , uuId , text ) ;
2021-10-12 20:52:08 +00:00
else
{
2022-01-22 14:53:26 +00:00
unsigned sn = textLength ( text ) ;
2021-10-12 20:52:08 +00:00
sn + = n + 1 ;
2022-01-22 14:53:26 +00:00
// alloc. a lot of extra space to cover the space need for the '\' escape character
char s [ sn * 2 ] ;
2021-10-12 20:52:08 +00:00
unsigned i , j ;
2022-01-22 14:53:26 +00:00
for ( i = 0 , j = 0 ; text [ i ] & & j < sn ; + + i , + + j )
2021-10-12 20:52:08 +00:00
{
char ch = text [ i ] ;
bool escape_fl = true ;
switch ( ch )
{
case ' \\ ' : ch = ' \\ ' ; break ;
case ' \b ' : ch = ' b ' ; break ;
case ' \f ' : ch = ' f ' ; break ;
case ' \n ' : ch = ' n ' ; break ;
case ' \r ' : ch = ' r ' ; break ;
case ' \t ' : ch = ' t ' ; break ;
default :
escape_fl = false ;
break ;
}
if ( escape_fl )
s [ j + + ] = ' \\ ' ;
2022-01-22 14:53:26 +00:00
if ( j < sn )
s [ j ] = ch ;
2021-10-12 20:52:08 +00:00
}
s [ sn - 1 ] = 0 ;
2022-01-22 14:53:26 +00:00
2021-12-30 02:43:56 +00:00
//printf("%s %s\n",text,s);
2021-10-12 20:52:08 +00:00
2021-11-03 15:03:30 +00:00
rc = sendValueString ( h , uuId , s ) ;
2021-10-12 20:52:08 +00:00
}
2020-03-23 14:48:49 +00:00
return rc ;
}
2024-09-21 21:18:12 +00:00
cw : : rc_t cw : : ui : : emptyParent ( handle_t h , unsigned uuId )
{
2024-10-19 16:36:12 +00:00
rc_t rc = kOkRC ;
unsigned childN = elementPhysChildCount ( h , uuId ) ;
unsigned * childUuIdA = nullptr ;
2024-09-21 21:18:12 +00:00
2024-10-19 16:36:12 +00:00
if ( childN > 0 )
{
childUuIdA = mem : : alloc < unsigned > ( childN ) ;
unsigned actualChildN = 0 ;
if ( ( rc = elementPhysChildUuId ( h , uuId , childUuIdA , childN , actualChildN ) ) ! = kOkRC )
goto errLabel ;
assert ( actualChildN = = childN ) ;
for ( unsigned i = 0 ; i < childN ; + + i )
if ( ( rc = destroyElement ( h , childUuIdA [ i ] ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Child element destroy failed. " ) ;
goto errLabel ;
}
}
2024-09-21 21:18:12 +00:00
2024-10-19 16:36:12 +00:00
/*
2024-09-21 21:18:12 +00:00
const char * mFmt = " { \" op \" : \" empty \" , \" uuId \" :%i } " ;
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
if ( snprintf ( mbuf , mbufN , mFmt , uuId ) > = mbufN - 1 )
{
rc = cwLogError ( kBufTooSmallRC , " The msg buffer is too small for 'empty' msg. " ) ;
goto errLabel ;
}
if ( ( rc = _websockSend ( p , kInvalidId , mbuf ) ) ! = kOkRC )
{
cwLogError ( rc , " 'empty' msg transmit failed. " ) ;
goto errLabel ;
}
2024-10-19 16:36:12 +00:00
*/
2024-09-21 21:18:12 +00:00
errLabel :
2024-10-19 16:36:12 +00:00
mem : : release ( childUuIdA ) ;
2024-09-21 21:18:12 +00:00
return rc ;
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : setClickable ( handle_t h , unsigned uuId , bool clickableFl )
{ return _setPropertyFlag ( h , UI_CLICKABLE_LABEL , uuId , clickableFl ) ; }
cw : : rc_t cw : : ui : : clearClickable ( handle_t h , unsigned uuId )
{ return setClickable ( h , uuId , false ) ; }
bool cw : : ui : : isClickable ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele = nullptr ;
bool clickableFl = false ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
2021-11-14 16:54:52 +00:00
_get_attribute ( ele , UI_CLICKABLE_LABEL , clickableFl ) ;
2021-11-03 15:03:30 +00:00
return clickableFl ;
}
cw : : rc_t cw : : ui : : setSelect ( handle_t h , unsigned uuId , bool enableFl )
{ return _setPropertyFlag ( h , UI_SELECT_LABEL , uuId , enableFl ) ; }
cw : : rc_t cw : : ui : : clearSelect ( handle_t h , unsigned uuId )
{ return setSelect ( h , uuId , false ) ; }
bool cw : : ui : : isSelected ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele = nullptr ;
bool selectFl = false ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
2021-11-14 16:54:52 +00:00
_get_attribute ( ele , UI_SELECT_LABEL , selectFl ) ;
2021-11-03 15:03:30 +00:00
return selectFl ;
}
2021-11-04 13:33:42 +00:00
cw : : rc_t cw : : ui : : setVisible ( handle_t h , unsigned uuId , bool enableFl )
{ return _setPropertyFlag ( h , UI_VISIBLE_LABEL , uuId , enableFl ) ; }
cw : : rc_t cw : : ui : : clearVisible ( handle_t h , unsigned uuId )
{ return setVisible ( h , uuId , false ) ; }
bool cw : : ui : : isVisible ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele = nullptr ;
bool visibleFl = false ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
2021-11-14 16:54:52 +00:00
_get_attribute ( ele , UI_VISIBLE_LABEL , visibleFl ) ;
2021-11-04 13:33:42 +00:00
return visibleFl ;
}
cw : : rc_t cw : : ui : : setEnable ( handle_t h , unsigned uuId , bool enableFl )
{ return _setPropertyFlag ( h , UI_ENABLE_LABEL , uuId , enableFl ) ; }
cw : : rc_t cw : : ui : : clearEnable ( handle_t h , unsigned uuId )
{ return setEnable ( h , uuId , false ) ; }
bool cw : : ui : : isEnabled ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele = nullptr ;
bool enableFl = false ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
2021-11-14 16:54:52 +00:00
enableFl = _get_attribute ( ele , UI_ENABLE_LABEL , enableFl ) ;
2021-11-04 13:33:42 +00:00
return enableFl ;
}
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : ui : : setOrderKey ( handle_t h , unsigned uuId , int orderKey )
{ return _setPropertyValue ( h , UI_ORDER_LABEL , uuId , orderKey ) ; }
int cw : : ui : : getOrderKey ( handle_t h , unsigned uuId )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele = nullptr ;
int orderKey = 0 ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
rc = _get_attribute ( ele , UI_ORDER_LABEL , orderKey ) ;
return rc ;
}
2024-05-11 16:10:21 +00:00
cw : : rc_t cw : : ui : : setScrollTop ( handle_t h , unsigned uuId )
{ return _setPropertyValue ( h , UI_SCROLL_TOP_LABEL , uuId , 0 ) ; }
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : ui : : setBlob ( handle_t h , unsigned uuId , const void * blob , unsigned blobByteN )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) = = nullptr )
return cwLogError ( kInvalidIdRC , " Invalid uuId (%i) blob not set . " ,uuId) ;
ele - > blob = ( void * ) mem : : resize < unsigned char > ( ( unsigned char * ) ele - > blob , blobByteN ) ;
ele - > blobByteN = blobByteN ;
memcpy ( ele - > blob , blob , blobByteN ) ;
return kOkRC ;
}
const void * cw : : ui : : getBlob ( handle_t h , unsigned uuId , unsigned & blobByteN )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
{
blobByteN = ele - > blobByteN ;
return ele - > blob ;
}
blobByteN = 0 ;
return nullptr ;
}
cw : : rc_t cw : : ui : : clearBlob ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) = = nullptr )
return cwLogError ( kInvalidIdRC , " Invalid uuId (%i) blob not cleared . " ,uuId) ;
mem : : release ( ele - > blob ) ;
ele - > blobByteN = 0 ;
return kOkRC ;
}
2024-10-19 16:36:12 +00:00
unsigned cw : : ui : : elementChildCount ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] ! = nullptr & & _is_child_of ( ele , p - > eleA [ i ] ) )
+ + n ;
return n ;
}
cw : : rc_t cw : : ui : : elementChildUuId ( handle_t h , unsigned uuId , unsigned * bufA , unsigned bufN , unsigned & actualN )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] ! = nullptr & & _is_child_of ( ele , p - > eleA [ i ] ) )
{
if ( n > = bufN )
{
rc = cwLogError ( kBufTooSmallRC , " The child ele. id buffer is too small. " ) ;
goto errLabel ;
}
bufA [ n + + ] = p - > eleA [ i ] - > uuId ;
}
actualN = n ;
errLabel :
return rc ;
}
unsigned cw : : ui : : elementPhysChildCount ( handle_t h , unsigned uuId )
{
ui_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
ele_t * ele ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > phys_parent = = ele )
+ + n ;
return n ;
}
cw : : rc_t cw : : ui : : elementPhysChildUuId ( handle_t h , unsigned uuId , unsigned * bufA , unsigned bufN , unsigned & actualN )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
ele_t * ele ;
actualN = 0 ;
if ( ( ele = _uuIdToEle ( p , uuId ) ) ! = nullptr )
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > phys_parent = = ele )
{
if ( n > = bufN )
{
rc = cwLogError ( kBufTooSmallRC , " The child ele. id buffer is too small. " ) ;
goto errLabel ;
}
bufA [ n + + ] = p - > eleA [ i ] - > uuId ;
}
actualN = n ;
errLabel :
return rc ;
}
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : ui : : destroyElement ( handle_t h , unsigned uuId )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
ele_t * del_ele = nullptr ;
const int mbufN = 256 ;
char mbuf [ mbufN ] ;
// locate the element to delete
if ( ( del_ele = _uuIdToEle ( p , uuId ) ) = = nullptr )
{
rc = kInvalidIdRC ;
goto errLabel ;
}
// mark the element for deletion
del_ele - > destroyFl = true ;
2024-10-19 16:36:12 +00:00
_remove_ele_from_hash_table ( p , del_ele ) ;
2020-03-23 14:48:49 +00:00
2024-10-19 16:36:12 +00:00
// mark all child elements of 'del_ele' for deletion and remove them from the hash table
2021-11-14 16:54:52 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2024-10-19 16:36:12 +00:00
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] ! = del_ele )
if ( ( p - > eleA [ i ] - > destroyFl = _is_child_of ( del_ele , p - > eleA [ i ] ) ) = = true )
_remove_ele_from_hash_table ( p , p - > eleA [ i ] ) ;
// Note that all ele's that are going to be deleted
// must be first removed from the hash table.
// The ele's can't be removed from the hash table
// as they are deleted because the removal fromo the hash table
// requires accessing the logical parent - which may have already been deleted.
2021-11-14 16:54:52 +00:00
// release all elements that are marked for deletion
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] ! = nullptr & & p - > eleA [ i ] - > destroyFl )
{
2024-10-19 16:36:12 +00:00
_destroy_element ( p , p - > eleA [ i ] ) ;
2021-11-14 16:54:52 +00:00
p - > eleA [ i ] = nullptr ;
}
2024-10-19 16:36:12 +00:00
snprintf ( mbuf , mbufN , " { \" op \" : \" destroy \" , \" uuId \" :%i } " , uuId ) ;
2021-11-14 16:54:52 +00:00
_websockSend ( p , kInvalidId , mbuf ) ;
errLabel :
if ( rc ! = kOkRC )
2024-10-19 16:36:12 +00:00
rc = cwLogError ( rc , " Element delete failed: uuid:%i. " , uuId ) ;
2021-11-14 16:54:52 +00:00
return rc ;
}
2020-03-23 14:48:49 +00:00
2020-04-27 12:14:26 +00:00
cw : : rc_t cw : : ui : : registerAppIdMap ( handle_t h , const appIdMap_t * map , unsigned mapN )
2020-03-31 17:10:45 +00:00
{
2020-04-27 12:14:26 +00:00
return _registerAppIdMap ( _handleToPtr ( h ) , map , mapN ) ;
2020-03-31 17:10:45 +00:00
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueBool ( handle_t h , unsigned uuId , bool value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kBoolTId ;
2022-12-13 21:53:10 +00:00
v . u . b = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
//return _sendValue<int>(p,kInvalidId,uuId,"%i",value?1:0);
2020-04-24 14:20:25 +00:00
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueInt ( handle_t h , unsigned uuId , int value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kIntTId ;
2022-12-13 21:53:10 +00:00
v . u . i = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
//return _sendValue<int>(p,kInvalidId,uuId,"%i",value);
2020-04-24 14:20:25 +00:00
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueUInt ( handle_t h , unsigned uuId , unsigned value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2022-12-13 21:53:10 +00:00
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kUIntTId ;
2022-12-13 21:53:10 +00:00
v . u . u = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
//return _sendValue<unsigned>(p,kInvalidId,uuId,"%i",value);
2020-04-24 14:20:25 +00:00
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueFloat ( handle_t h , unsigned uuId , float value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kFloatTId ;
2022-12-13 21:53:10 +00:00
v . u . f = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
//return _sendValue<float>(p,kInvalidId,uuId,"%f",value);
2020-04-24 14:20:25 +00:00
}
2020-03-31 17:10:45 +00:00
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueDouble ( handle_t h , unsigned uuId , double value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kDoubleTId ;
2022-12-13 21:53:10 +00:00
v . u . d = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
//return _sendValue<double>(p,kInvalidId,uuId,"%f",value);
2020-04-24 14:20:25 +00:00
}
2021-11-03 15:03:30 +00:00
cw : : rc_t cw : : ui : : sendValueString ( handle_t h , unsigned uuId , const char * value )
2020-04-24 14:20:25 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2024-03-25 14:46:45 +00:00
value_t v = { } ;
v . tid = kStringTId ;
2022-12-13 21:53:10 +00:00
v . u . s = value ;
return _sendValue ( p , kInvalidId , uuId , v ) ;
2020-04-24 14:20:25 +00:00
// +10 allows for extra value buffer space for double quotes and slashed
2022-12-13 21:53:10 +00:00
//return _sendValue<const char*>(p,kInvalidId,uuId,"\"%s\"",value,"value",strlen(value)+10);
2020-04-24 14:20:25 +00:00
}
2020-03-31 17:10:45 +00:00
2024-01-06 13:44:53 +00:00
cw : : rc_t cw : : ui : : sendMsg ( handle_t h , const char * msg )
{
ui_t * p = _handleToPtr ( h ) ;
return _websockSend ( p , kInvalidId , msg ) ;
}
2021-11-01 14:11:27 +00:00
void cw : : ui : : report ( handle_t h )
{
ui_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2021-11-14 16:54:52 +00:00
if ( p - > eleA [ i ] ! = nullptr )
{
const ele_t * e = p - > eleA [ i ] ;
2022-01-22 14:53:26 +00:00
2021-11-01 14:11:27 +00:00
2022-01-22 14:53:26 +00:00
unsigned parUuId = e - > phys_parent = = NULL ? kInvalidId : e - > phys_parent - > uuId ;
const char * parEleName = e - > phys_parent = = NULL | | e - > phys_parent - > eleName = = NULL ? " " : e - > phys_parent - > eleName ;
unsigned logParentAppId = e - > logical_parent = = NULL ? kInvalidId : e - > logical_parent - > appId ;
printf ( " uu:%5i app:%5i chan:%5i %20s : parent uu:%5i app:%5i %20s " , e - > uuId , e - > appId , e - > chanId , e - > eleName = = NULL ? " " : e - > eleName , parUuId , logParentAppId , parEleName ) ;
2021-11-02 01:46:59 +00:00
2021-11-14 16:54:52 +00:00
for ( unsigned i = 0 ; i < e - > attr - > child_count ( ) ; + + i )
{
e - > attr - > child_ele ( i ) - > to_string ( p - > buf , p - > bufN ) ;
printf ( " %s, " , p - > buf ) ;
}
2023-12-03 16:18:02 +00:00
printf ( " \n " ) ;
}
2021-11-01 14:11:27 +00:00
}
2023-12-03 16:18:02 +00:00
void cw : : ui : : realTimeReport ( handle_t h )
{
ui_t * p = _handleToPtr ( h ) ;
printf ( " UI msg count: recv:%i send:%i \n " , p - > recvMsgN , p - > sentMsgN ) ;
2024-03-25 18:29:58 +00:00
2023-12-03 16:18:02 +00:00
}
2021-11-01 14:11:27 +00:00
2020-04-07 20:24:34 +00:00
namespace cw
{
namespace ui
{
namespace ws
{
typedef struct ui_ws_str
{
websock : : handle_t wsH ;
ui : : handle_t uiH ;
void * cbArg ;
uiCallback_t uiCbFunc ;
websock : : cbFunc_t wsCbFunc ;
2022-12-13 21:53:10 +00:00
unsigned idleMsgPeriodMs ;
time : : spec_t lastRecvMsgTimeStamp ;
2020-04-07 20:24:34 +00:00
} ui_ws_t ;
ui_ws_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , ui_ws_t > ( h ) ; }
rc_t _destroy ( ui_ws_t * p )
{
rc_t rc ;
if ( ( rc = websock : : destroy ( p - > wsH ) ) ! = kOkRC )
return rc ;
2020-04-24 14:20:25 +00:00
if ( ( rc = ui : : destroy ( p - > uiH ) ) ! = kOkRC )
return rc ;
2020-04-07 20:24:34 +00:00
mem : : release ( p ) ;
return rc ;
}
void _webSockCb ( void * cbArg , unsigned protocolId , unsigned sessionId , websock : : msgTypeId_t msg_type , const void * msg , unsigned byteN )
{
ui_ws_t * p = static_cast < ui_ws_t * > ( cbArg ) ;
switch ( msg_type )
{
case websock : : kConnectTId :
ui : : onConnect ( p - > uiH , sessionId ) ;
break ;
case websock : : kDisconnectTId :
ui : : onDisconnect ( p - > uiH , sessionId ) ;
break ;
case websock : : kMessageTId :
ui : : onReceive ( p - > uiH , sessionId , msg , byteN ) ;
break ;
default :
cwLogError ( kInvalidIdRC , " An invalid websock msgTypeId (%i) was encountered " , msg_type ) ;
}
2022-12-13 21:53:10 +00:00
time : : get ( p - > lastRecvMsgTimeStamp ) ;
2020-04-07 20:24:34 +00:00
}
rc_t _webSockSend ( void * cbArg , unsigned wsSessId , const void * msg , unsigned msgByteN )
{
ui_ws_t * p = static_cast < ui_ws_t * > ( cbArg ) ;
2022-12-13 21:53:10 +00:00
return websock : : send ( p - > wsH , kUiProtocolId , wsSessId , msg , msgByteN ) ;
2020-04-07 20:24:34 +00:00
}
2024-03-28 23:47:29 +00:00
2020-04-07 20:24:34 +00:00
}
}
}
2021-01-20 18:10:56 +00:00
cw : : rc_t cw : : ui : : ws : : parseArgs ( const object_t & o , args_t & args , const char * object_label )
{
2022-05-14 14:16:09 +00:00
rc_t rc = kOkRC ;
const object_t * op = & o ;
char * uiCfgFn = nullptr ;
char * physRootDir = nullptr ;
2021-01-20 18:10:56 +00:00
memset ( & args , 0 , sizeof ( args ) ) ;
// if no 'ui' cfg record was given then skip
if ( object_label = = nullptr )
op = & o ;
else
if ( ( op = o . find ( object_label ) ) = = nullptr )
2024-02-08 15:55:48 +00:00
return cwLogError ( kEleNotFoundRC , " The ui configuration label '%s' was not found. " , cwStringNullGuard ( object_label ) ) ;
2021-01-20 18:10:56 +00:00
if ( ( rc = op - > getv (
2022-12-13 21:53:10 +00:00
" physRootDir " , args . physRootDir ,
" dfltPageFn " , args . dfltPageFn ,
" port " , args . port ,
" rcvBufByteN " , args . rcvBufByteN ,
" xmtBufByteN " , args . xmtBufByteN ,
" fmtBufByteN " , args . fmtBufByteN ,
" idleMsgPeriodMs " , args . idleMsgPeriodMs ,
2024-03-28 23:47:29 +00:00
" queueBlkCnt " , args . queueBlkCnt ,
" queueBlkByteCnt " , args . queueBlkByteCnt ,
2024-09-12 21:18:42 +00:00
" extraLogsFl " , args . extraLogsFl ,
2022-12-13 21:53:10 +00:00
" uiCfgFn " , uiCfgFn ) ) ! = kOkRC )
2021-01-20 18:10:56 +00:00
{
rc = cwLogError ( rc , " 'ui' cfg. parse failed. " ) ;
}
2024-03-28 23:47:29 +00:00
if ( op - > find_child ( " websockTimeOutMs " ) ! = nullptr )
cwLogWarning ( " The UI parameter 'websockTimeOutMs' is obsolete and ignored. " ) ;
2022-05-14 14:16:09 +00:00
// expand the physical root directory
if ( ( physRootDir = filesys : : expandPath ( args . physRootDir ) ) = = nullptr )
{
rc = cwLogError ( kInvalidArgRC , " The physical root directory of the UI cfg. is invalid. " ) ;
goto errLabel ;
}
2021-01-20 18:10:56 +00:00
// if a default UI resource script was given then convert it into an object
if ( uiCfgFn ! = nullptr )
{
2022-05-14 14:16:09 +00:00
char * fn = filesys : : makeFn ( physRootDir , uiCfgFn , nullptr , nullptr ) ;
2021-01-20 18:10:56 +00:00
if ( ( rc = objectFromFile ( fn , args . uiRsrc ) ) ! = kOkRC )
rc = cwLogError ( rc , " An error occurred while parsing the UI resource script in '%s'. " , cwStringNullGuard ( uiCfgFn ) ) ;
mem : : release ( fn ) ;
}
2022-05-14 14:16:09 +00:00
errLabel :
mem : : release ( physRootDir ) ;
2021-01-20 18:10:56 +00:00
return rc ;
}
cw : : rc_t cw : : ui : : ws : : releaseArgs ( args_t & args )
{
if ( args . uiRsrc ! = nullptr )
args . uiRsrc - > free ( ) ;
return kOkRC ;
}
cw : : rc_t cw : : ui : : ws : : create ( handle_t & h ,
const args_t & args ,
void * cbArg ,
uiCallback_t uiCbFunc ,
const object_t * uiRsrc ,
const appIdMap_t * appIdMapA ,
unsigned appIdMapN ,
websock : : cbFunc_t wsCbFunc )
{
return create ( h ,
2022-12-13 21:53:10 +00:00
args . port ,
args . physRootDir ,
cbArg ,
uiCbFunc ,
uiRsrc ,
appIdMapA ,
appIdMapN ,
wsCbFunc ,
args . dfltPageFn ,
args . idleMsgPeriodMs ,
args . rcvBufByteN ,
args . xmtBufByteN ,
2024-03-28 23:47:29 +00:00
args . fmtBufByteN ,
args . queueBlkCnt ,
2024-09-12 21:18:42 +00:00
args . queueBlkByteCnt ,
args . extraLogsFl ) ;
2021-01-20 18:10:56 +00:00
}
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : ws : : create ( handle_t & h ,
2022-12-13 21:53:10 +00:00
unsigned port ,
const char * physRootDir ,
void * cbArg ,
uiCallback_t uiCbFunc ,
const object_t * uiRsrc ,
const appIdMap_t * appIdMapA ,
unsigned appIdMapN ,
websock : : cbFunc_t wsCbFunc ,
const char * dfltPageFn ,
unsigned idleMsgPeriodMs ,
unsigned rcvBufByteN ,
unsigned xmtBufByteN ,
2024-03-28 23:47:29 +00:00
unsigned fmtBufByteN ,
unsigned queueBlkCnt ,
2024-09-12 21:18:42 +00:00
unsigned queueBlkByteCnt ,
bool extraLogsFl )
2020-04-07 20:24:34 +00:00
{
rc_t rc = kOkRC ;
if ( ( rc = destroy ( h ) ) ! = kOkRC )
return rc ;
ui_ws_t * p = mem : : allocZ < ui_ws_t > ( ) ;
websock : : protocol_t protocolA [ ] =
{
{ " http " , kHttpProtocolId , 0 , 0 } ,
{ " ui_protocol " , kUiProtocolId , rcvBufByteN , xmtBufByteN }
} ;
unsigned protocolN = sizeof ( protocolA ) / sizeof ( protocolA [ 0 ] ) ;
websock : : cbFunc_t wsCbF = wsCbFunc = = nullptr ? _webSockCb : wsCbFunc ;
void * wsCbA = wsCbFunc = = nullptr ? p : cbArg ;
// create the websocket
2024-09-12 21:18:42 +00:00
if ( ( rc = websock : : create ( p - > wsH , wsCbF , wsCbA , physRootDir , dfltPageFn , port , protocolA , protocolN , queueBlkCnt , queueBlkByteCnt , extraLogsFl ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
{
cwLogError ( rc , " UI Websock create failed. " ) ;
goto errLabel ;
}
// create the ui
2021-01-20 18:10:56 +00:00
if ( ( rc = ui : : create ( p - > uiH , _webSockSend , p , uiCbFunc , cbArg , uiRsrc , appIdMapA , appIdMapN , fmtBufByteN ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
{
cwLogError ( rc , " UI object create failed. " ) ;
goto errLabel ;
}
p - > cbArg = cbArg ;
p - > uiCbFunc = uiCbFunc ;
p - > wsCbFunc = wsCbFunc ;
2022-12-13 21:53:10 +00:00
p - > idleMsgPeriodMs = idleMsgPeriodMs ;
// initialize the last received msg
time : : get ( p - > lastRecvMsgTimeStamp ) ;
2020-04-07 20:24:34 +00:00
h . set ( p ) ;
errLabel :
if ( rc ! = kOkRC )
_destroy ( p ) ;
return rc ;
}
2020-04-21 18:59:55 +00:00
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : ws : : destroy ( handle_t & h )
{
rc_t rc = kOkRC ;
ui_ws_t * p = nullptr ;
if ( ! h . isValid ( ) )
return rc ;
p = _handleToPtr ( h ) ;
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
return rc ;
h . clear ( ) ;
return rc ;
}
2024-03-28 23:47:29 +00:00
cw : : rc_t cw : : ui : : ws : : exec ( handle_t h , unsigned wsTimeOutMs )
2020-04-07 20:24:34 +00:00
{
ui_ws_t * p = _handleToPtr ( h ) ;
2024-03-28 23:47:29 +00:00
rc_t rc = kOkRC ;
2022-12-13 21:53:10 +00:00
time : : spec_t t ;
2024-03-28 23:47:29 +00:00
if ( ( rc = websock : : exec ( p - > wsH , wsTimeOutMs ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
cwLogError ( rc , " The UI websock execution failed. " ) ;
2020-04-26 13:19:19 +00:00
// make the idle callback
2022-12-13 21:53:10 +00:00
time : : get ( t ) ;
if ( time : : elapsedMs ( p - > lastRecvMsgTimeStamp , t ) > p - > idleMsgPeriodMs )
{
ui : : onReceive ( p - > uiH , kInvalidId , " idle " , strlen ( " idle " ) + 1 ) ;
p - > lastRecvMsgTimeStamp = t ;
}
2020-04-07 20:24:34 +00:00
return rc ;
}
2024-03-28 23:47:29 +00:00
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : ws : : onReceive ( handle_t h , unsigned protocolId , unsigned sessionId , websock : : msgTypeId_t msg_type , const void * msg , unsigned byteN )
{
ui_ws_t * p = _handleToPtr ( h ) ;
_webSockCb ( p , protocolId , sessionId , msg_type , msg , byteN ) ;
2022-12-13 21:53:10 +00:00
2020-04-07 20:24:34 +00:00
return kOkRC ;
}
cw : : websock : : handle_t cw : : ui : : ws : : websockHandle ( handle_t h )
{
ui_ws_t * p = _handleToPtr ( h ) ;
return p - > wsH ;
}
cw : : ui : : handle_t cw : : ui : : ws : : uiHandle ( handle_t h )
{
ui_ws_t * p = _handleToPtr ( h ) ;
return p - > uiH ;
}
2024-03-25 18:29:58 +00:00
void cw : : ui : : ws : : realTimeReport ( handle_t h )
{
ui_ws_t * p = _handleToPtr ( h ) ;
report ( p - > wsH ) ;
realTimeReport ( p - > uiH ) ;
}
2020-04-07 20:24:34 +00:00
2020-03-23 14:48:49 +00:00
2020-04-07 20:24:34 +00:00
namespace cw
{
namespace ui
{
namespace srv
{
typedef struct ui_ws_srv_str
{
ws : : handle_t wsUiH ;
thread : : handle_t thH ;
2024-03-28 23:47:29 +00:00
unsigned wsTimeOutMs ;
2020-04-07 20:24:34 +00:00
} ui_ws_srv_t ;
ui_ws_srv_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , ui_ws_srv_t > ( h ) ; }
rc_t _destroy ( ui_ws_srv_t * p )
{
rc_t rc ;
if ( ( rc = thread : : destroy ( p - > thH ) ) ! = kOkRC )
return rc ;
if ( ( rc = ws : : destroy ( p - > wsUiH ) ) ! = kOkRC )
return rc ;
mem : : release ( p ) ;
return rc ;
}
bool _threadCallback ( void * arg )
{
ui_ws_srv_t * p = static_cast < ui_ws_srv_t * > ( arg ) ;
rc_t rc ;
2024-03-28 23:47:29 +00:00
if ( ( rc = ws : : exec ( p - > wsUiH , p - > wsTimeOutMs ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
{
cwLogError ( rc , " Websocket UI exec failed. " ) ;
}
return true ;
}
}
}
}
2024-03-28 23:47:29 +00:00
cw : : rc_t cw : : ui : : srv : : create ( handle_t & h ,
const ws : : args_t & args ,
void * cbArg ,
uiCallback_t uiCbFunc ,
const appIdMap_t * appIdMapA ,
unsigned appIdMapN ,
unsigned wsTimeOutMs ,
websock : : cbFunc_t wsCbFunc )
{
return create ( h ,
args . port ,
args . physRootDir ,
cbArg ,
uiCbFunc ,
args . uiRsrc ,
appIdMapA ,
appIdMapN ,
wsTimeOutMs ,
wsCbFunc ,
args . dfltPageFn ,
args . idleMsgPeriodMs ,
args . rcvBufByteN ,
args . xmtBufByteN ,
args . fmtBufByteN ,
args . queueBlkCnt ,
args . queueBlkByteCnt ) ;
}
2020-04-07 20:24:34 +00:00
cw : : rc_t cw : : ui : : srv : : create ( handle_t & h ,
2024-03-28 23:47:29 +00:00
unsigned port ,
const char * physRootDir ,
void * cbArg ,
uiCallback_t uiCbFunc ,
const object_t * uiRsrc ,
const appIdMap_t * appIdMapA ,
unsigned appIdMapN ,
unsigned wsTimeOutMs ,
websock : : cbFunc_t wsCbFunc ,
const char * dfltPageFn ,
unsigned idleMsgPeriodMs ,
unsigned rcvBufByteN ,
unsigned xmtBufByteN ,
unsigned fmtBufByteN ,
unsigned queueBlkCnt ,
2024-09-12 21:18:42 +00:00
unsigned queueBlkByteCnt ,
bool extraLogsFl )
2020-04-07 20:24:34 +00:00
{
rc_t rc = kOkRC ;
if ( ( rc = destroy ( h ) ) ! = kOkRC )
return rc ;
ui_ws_srv_t * p = mem : : allocZ < ui_ws_srv_t > ( ) ;
2024-03-28 23:47:29 +00:00
if ( ( rc = ws : : create ( p - > wsUiH , port , physRootDir , cbArg , uiCbFunc , uiRsrc , appIdMapA , appIdMapN , wsCbFunc , dfltPageFn , idleMsgPeriodMs , rcvBufByteN , xmtBufByteN , fmtBufByteN , queueBlkCnt , queueBlkByteCnt ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
{
cwLogError ( rc , " The websock UI creationg failed. " ) ;
goto errLabel ;
}
2024-02-18 13:49:24 +00:00
if ( ( rc = thread : : create ( p - > thH , _threadCallback , p , " ui " , thread : : kDefaultStateTimeOutMicros , thread : : kDefaultPauseMicros ) ) ! = kOkRC )
2020-04-07 20:24:34 +00:00
{
cwLogError ( rc , " The websock UI server thread create failed. " ) ;
goto errLabel ;
}
2024-03-28 23:47:29 +00:00
p - > wsTimeOutMs = wsTimeOutMs ;
2020-04-07 20:24:34 +00:00
h . set ( p ) ;
errLabel :
if ( rc ! = kOkRC )
_destroy ( p ) ;
return rc ;
}
cw : : rc_t cw : : ui : : srv : : destroy ( handle_t & h )
{
rc_t rc = kOkRC ;
if ( ! h . isValid ( ) )
return rc ;
ui_ws_srv_t * p = _handleToPtr ( h ) ;
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
return rc ;
h . clear ( ) ;
return rc ;
}
cw : : rc_t cw : : ui : : srv : : start ( handle_t h )
{
ui_ws_srv_t * p = _handleToPtr ( h ) ;
rc_t rc ;
if ( ( rc = thread : : unpause ( p - > thH ) ) ! = kOkRC )
cwLogError ( rc , " WebockUI server thread start failed. " ) ;
return rc ;
}
cw : : rc_t cw : : ui : : srv : : stop ( handle_t h )
{
ui_ws_srv_t * p = _handleToPtr ( h ) ;
rc_t rc ;
if ( ( rc = thread : : pause ( p - > thH , thread : : kPauseFl | thread : : kWaitFl ) ) ! = kOkRC )
cwLogError ( rc , " WebockUI server thread stop failed. " ) ;
return rc ;
}
cw : : thread : : handle_t cw : : ui : : srv : : threadHandle ( handle_t h )
{
ui_ws_srv_t * p = _handleToPtr ( h ) ;
return p - > thH ;
}
cw : : websock : : handle_t cw : : ui : : srv : : websockHandle ( handle_t h )
{
ui_ws_srv_t * p = _handleToPtr ( h ) ;
return ws : : websockHandle ( p - > wsUiH ) ;
}
cw : : ui : : handle_t cw : : ui : : srv : : uiHandle ( handle_t h )
{
ui_ws_srv_t * p = _handleToPtr ( h ) ;
return ws : : uiHandle ( p - > wsUiH ) ;
}
2024-03-25 18:29:58 +00:00