|  | @@ -53,6 +53,12 @@ struct grfg_packet {
 | 
	
		
			
				|  |  |  	u16_t  width;		// # of pixels of scrollable
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +struct ANIC_header {
 | 
	
		
			
				|  |  | +	char  opcode[4];
 | 
	
		
			
				|  |  | +	u32_t length;
 | 
	
		
			
				|  |  | +	u8_t mode;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #pragma pack(pop)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static struct scroller_s {
 | 
	
	
		
			
				|  | @@ -69,30 +75,28 @@ static struct scroller_s {
 | 
	
		
			
				|  |  |  	int scroll_len, scroll_step;
 | 
	
		
			
				|  |  |  } scroller;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -	ANIM_SCREEN_1                    => end of first scroll on screen 1
 | 
	
		
			
				|  |  | -	ANIM_SCREEN_2                    => end of first scroll on screen 2
 | 
	
		
			
				|  |  | -	ANIM_SCROLL_ONCE | ANIM_SCREEN_1 => end of scroll once on screen 1
 | 
	
		
			
				|  |  | -	ANIM_SCROLL_ONCE | ANIM_SCREEN_2 => end of scroll once on screen 2
 | 
	
		
			
				|  |  | -*/	
 | 
	
		
			
				|  |  | +#define ANIM_NONE		  0x00
 | 
	
		
			
				|  |  |  #define ANIM_TRANSITION   0x01 // A transition animation has finished
 | 
	
		
			
				|  |  |  #define ANIM_SCROLL_ONCE  0x02 
 | 
	
		
			
				|  |  |  #define ANIM_SCREEN_1     0x04 
 | 
	
		
			
				|  |  |  #define ANIM_SCREEN_2     0x08 
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define SCROLL_STACK_SIZE	2048
 | 
	
		
			
				|  |  | +static u8_t ANIC_resp = ANIM_NONE;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#define SCROLL_STACK_SIZE	2048
 | 
	
		
			
				|  |  |  #define LINELEN				40
 | 
	
		
			
				|  |  | -#define HEIGHT				32
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static log_level loglevel = lINFO;
 | 
	
		
			
				|  |  |  static SemaphoreHandle_t display_sem;
 | 
	
		
			
				|  |  |  static bool (*slimp_handler_chain)(u8_t *data, int len);
 | 
	
		
			
				|  |  | +static void (*slimp_loop_chain)(void);
 | 
	
		
			
				|  |  |  static void (*notify_chain)(in_addr_t ip, u16_t hport, u16_t cport);
 | 
	
		
			
				|  |  | +static int display_width, display_height;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define max(a,b) (((a) > (b)) ? (a) : (b))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void server(in_addr_t ip, u16_t hport, u16_t cport);
 | 
	
		
			
				|  |  | +static void send_server(void);
 | 
	
		
			
				|  |  |  static bool handler(u8_t *data, int len);
 | 
	
		
			
				|  |  |  static void vfdc_handler( u8_t *_data, int bytes_read);
 | 
	
		
			
				|  |  |  static void grfe_handler( u8_t *data, int len);
 | 
	
	
		
			
				|  | @@ -135,23 +139,55 @@ void sb_display_init(void) {
 | 
	
		
			
				|  |  |  	static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
 | 
	
		
			
				|  |  |  	static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  | +	// need to force height to 32 maximum
 | 
	
		
			
				|  |  | +	display_width = display->width;
 | 
	
		
			
				|  |  | +	display_height = min(display->height, 32);
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  |  	// create scroll management task
 | 
	
		
			
				|  |  |  	display_sem = xSemaphoreCreateMutex();
 | 
	
		
			
				|  |  |  	scroller.task = xTaskCreateStatic( (TaskFunction_t) scroll_task, "scroll_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	// size scroller
 | 
	
		
			
				|  |  | -	scroller.max = (display->width * display->height / 8) * 10;
 | 
	
		
			
				|  |  | +	scroller.max = (display_width * display_height / 8) * 10;
 | 
	
		
			
				|  |  |  	scroller.scroll_frame = malloc(scroller.max);
 | 
	
		
			
				|  |  | -	scroller.back_frame = malloc(display->width * display->height / 8);
 | 
	
		
			
				|  |  | +	scroller.back_frame = malloc(display_width * display_height / 8);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// chain handlers
 | 
	
		
			
				|  |  |  	slimp_handler_chain = slimp_handler;
 | 
	
		
			
				|  |  |  	slimp_handler = handler;
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  | +	slimp_loop_chain = slimp_loop;
 | 
	
		
			
				|  |  | +	slimp_loop = send_server;
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  |  	notify_chain = server_notify;
 | 
	
		
			
				|  |  |  	server_notify = server;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/****************************************************************************************
 | 
	
		
			
				|  |  | + * Send message to server (ANIC at that time)
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void send_server(void) {
 | 
	
		
			
				|  |  | +	/* 
 | 
	
		
			
				|  |  | +	 This complication is needed as we cannot send direclty to LMS, because 
 | 
	
		
			
				|  |  | +	 send_packet is not thread safe. So must subscribe to slimproto busy loop
 | 
	
		
			
				|  |  | +	 end send from there
 | 
	
		
			
				|  |  | +	*/ 
 | 
	
		
			
				|  |  | +	if (ANIC_resp != ANIM_NONE) {
 | 
	
		
			
				|  |  | +		struct ANIC_header pkt_header;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		memset(&pkt_header, 0, sizeof(pkt_header));
 | 
	
		
			
				|  |  | +		memcpy(&pkt_header.opcode, "ANIC", 4);
 | 
	
		
			
				|  |  | +		pkt_header.length = htonl(sizeof(pkt_header) - 8);
 | 
	
		
			
				|  |  | +		pkt_header.mode = ANIC_resp;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		send_packet((u8_t *)&pkt_header, sizeof(pkt_header));
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		ANIC_resp = ANIM_NONE;
 | 
	
		
			
				|  |  | +	}	
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	if (slimp_loop_chain) (*slimp_loop_chain)();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /****************************************************************************************
 | 
	
		
			
				|  |  |   * 
 | 
	
		
			
				|  |  |   */
 | 
	
	
		
			
				|  | @@ -307,7 +343,7 @@ static void grfe_handler( u8_t *data, int len) {
 | 
	
		
			
				|  |  |  	xSemaphoreTake(display_sem, portMAX_DELAY);
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	scroller.active = false;
 | 
	
		
			
				|  |  | -	display->draw_cbr(data + sizeof(struct grfe_packet), HEIGHT);
 | 
	
		
			
				|  |  | +	display->draw_cbr(data + sizeof(struct grfe_packet), display_height);
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	xSemaphoreGive(display_sem);
 | 
	
		
			
				|  |  |  	
 | 
	
	
		
			
				|  | @@ -393,13 +429,13 @@ static void grfg_handler(u8_t *data, int len) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// can't be in grfs as we need full size & scroll_width
 | 
	
		
			
				|  |  |  	if (scroller.updated) {
 | 
	
		
			
				|  |  | -		scroller.scroll_len = display->width * display->height / 8 - (display->width - scroller.window_width) * display->height / 8;
 | 
	
		
			
				|  |  | +		scroller.scroll_len = display_width * display_height / 8 - (display_width - scroller.window_width) * display_height / 8;
 | 
	
		
			
				|  |  |  		if (scroller.direction == 1) {
 | 
	
		
			
				|  |  |  			scroller.scroll_ptr = scroller.scroll_frame; 
 | 
	
		
			
				|  |  | -			scroller.scroll_step = scroller.by * display->height / 8;
 | 
	
		
			
				|  |  | +			scroller.scroll_step = scroller.by * display_height / 8;
 | 
	
		
			
				|  |  |  		} else	{
 | 
	
		
			
				|  |  |  			scroller.scroll_ptr = scroller.scroll_frame + scroller.size - scroller.scroll_len;
 | 
	
		
			
				|  |  | -			scroller.scroll_step = -scroller.by * display->height / 8;
 | 
	
		
			
				|  |  | +			scroller.scroll_step = -scroller.by * display_height / 8;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		
 | 
	
		
			
				|  |  |  		scroller.updated = false;	
 | 
	
	
		
			
				|  | @@ -407,10 +443,10 @@ static void grfg_handler(u8_t *data, int len) {
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	if (!scroller.active) {
 | 
	
		
			
				|  |  |  		// this is a background update and scroller has been finished, so need to update here
 | 
	
		
			
				|  |  | -		u8_t *frame = malloc(display->width * display->height / 8);
 | 
	
		
			
				|  |  | -		memcpy(frame, scroller.back_frame, display->width * display->height / 8);
 | 
	
		
			
				|  |  | +		u8_t *frame = malloc(display_width * display_height / 8);
 | 
	
		
			
				|  |  | +		memcpy(frame, scroller.back_frame, display_width * display_height / 8);
 | 
	
		
			
				|  |  |  		for (int i = 0; i < scroller.scroll_len; i++) frame[i] |= scroller.scroll_ptr[i];
 | 
	
		
			
				|  |  | -		display->draw_cbr(frame, HEIGHT);		
 | 
	
		
			
				|  |  | +		display->draw_cbr(frame, display_height);		
 | 
	
		
			
				|  |  |  		free(frame);
 | 
	
		
			
				|  |  |  		LOG_DEBUG("direct drawing");
 | 
	
		
			
				|  |  |  	}	
 | 
	
	
		
			
				|  | @@ -428,7 +464,7 @@ static void grfg_handler(u8_t *data, int len) {
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  static void scroll_task(void *args) {
 | 
	
		
			
				|  |  |  	u8_t *frame = NULL;
 | 
	
		
			
				|  |  | -	int len = display->width * display->height / 8;
 | 
	
		
			
				|  |  | +	int len = display_width * display_height / 8;
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	while (1) {
 | 
	
		
			
				|  |  |  		xSemaphoreTake(display_sem, portMAX_DELAY);
 | 
	
	
		
			
				|  | @@ -441,7 +477,7 @@ static void scroll_task(void *args) {
 | 
	
		
			
				|  |  |  		}	
 | 
	
		
			
				|  |  |  		
 | 
	
		
			
				|  |  |  		// lock screen & active status
 | 
	
		
			
				|  |  | -		frame = malloc(display->width * display->height / 8);
 | 
	
		
			
				|  |  | +		frame = malloc(display_width * display_height / 8);
 | 
	
		
			
				|  |  |  				
 | 
	
		
			
				|  |  |  		// scroll required amount of columns (within the window)
 | 
	
		
			
				|  |  |  		while (scroller.direction == 1 ? (scroller.scroll_ptr <= scroller.scroll_frame + scroller.size - scroller.scroll_step - len) :
 | 
	
	
		
			
				|  | @@ -451,10 +487,10 @@ static void scroll_task(void *args) {
 | 
	
		
			
				|  |  |  			if (!scroller.active) break;
 | 
	
		
			
				|  |  |  					
 | 
	
		
			
				|  |  |  			// scroll required amount of columns (within the window)
 | 
	
		
			
				|  |  | -			memcpy(frame, scroller.back_frame, display->width * display->height / 8);
 | 
	
		
			
				|  |  | +			memcpy(frame, scroller.back_frame, display_width * display_height / 8);
 | 
	
		
			
				|  |  |  			for (int i = 0; i < scroller.scroll_len; i++) frame[i] |= scroller.scroll_ptr[i];
 | 
	
		
			
				|  |  |  			scroller.scroll_ptr += scroller.scroll_step;
 | 
	
		
			
				|  |  | -			display->draw_cbr(frame, HEIGHT);		
 | 
	
		
			
				|  |  | +			display->draw_cbr(frame, display_height);		
 | 
	
		
			
				|  |  |  			
 | 
	
		
			
				|  |  |  			xSemaphoreGive(display_sem);
 | 
	
		
			
				|  |  |  			usleep(scroller.speed * 1000);
 | 
	
	
		
			
				|  | @@ -468,14 +504,15 @@ static void scroll_task(void *args) {
 | 
	
		
			
				|  |  |  		if (scroller.active) {
 | 
	
		
			
				|  |  |  			memcpy(frame, scroller.back_frame, len);
 | 
	
		
			
				|  |  |  			for (int i = 0; i < scroller.scroll_len; i++) frame[i] |= scroller.scroll_ptr[i];
 | 
	
		
			
				|  |  | -			display->draw_cbr(frame, HEIGHT);
 | 
	
		
			
				|  |  | +			display->draw_cbr(frame, display_height);
 | 
	
		
			
				|  |  |  			free(frame);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			// see if we need to pause or if we are done 				
 | 
	
		
			
				|  |  |  			if (scroller.mode) {
 | 
	
		
			
				|  |  |  				scroller.active = false;
 | 
	
		
			
				|  |  |  				xSemaphoreGive(display_sem);
 | 
	
		
			
				|  |  | -				//sendANIC(ANIM_SCROLL_ONCE | ANIM_SCREEN_1);
 | 
	
		
			
				|  |  | +				// can't call directly send_packet from slimproto as it's not re-entrant
 | 
	
		
			
				|  |  | +				ANIC_resp = ANIM_SCROLL_ONCE | ANIM_SCREEN_1;
 | 
	
		
			
				|  |  |  				LOG_INFO("scroll-once terminated");
 | 
	
		
			
				|  |  |  			} else {
 | 
	
		
			
				|  |  |  				xSemaphoreGive(display_sem);
 |