|
@@ -21,9 +21,11 @@
|
|
|
#include "squeezelite.h"
|
|
|
#include "display.h"
|
|
|
|
|
|
+#pragma pack(push, 1)
|
|
|
+
|
|
|
struct grfb_packet {
|
|
|
char opcode[4];
|
|
|
- u16_t brightness;
|
|
|
+ s16_t brightness;
|
|
|
};
|
|
|
|
|
|
struct grfe_packet {
|
|
@@ -51,13 +53,28 @@ struct grfg_packet {
|
|
|
u16_t width; // # of pixels of scrollable
|
|
|
};
|
|
|
|
|
|
-#define LINELEN 40
|
|
|
+#pragma pack(pop)
|
|
|
|
|
|
-static log_level loglevel = lINFO;
|
|
|
+static struct scroller_s {
|
|
|
+ TaskHandle_t task;
|
|
|
+ bool active;
|
|
|
+ u8_t screen, direction;
|
|
|
+ u32_t pause, speed;
|
|
|
+ u16_t by, mode, width, scroll_width;
|
|
|
+ u16_t max, size;
|
|
|
+ u8_t *scroll_frame, *back_frame;
|
|
|
+} scroller;
|
|
|
|
|
|
+#define SCROLL_STACK_SIZE 2048
|
|
|
+#define LINELEN 40
|
|
|
+
|
|
|
+static log_level loglevel = lINFO;
|
|
|
+static SemaphoreHandle_t display_sem;
|
|
|
static bool (*slimp_handler_chain)(u8_t *data, int len);
|
|
|
static void (*notify_chain)(in_addr_t ip, u16_t hport, u16_t cport);
|
|
|
|
|
|
+#define max(a,b) (((a) > (b)) ? (a) : (b))
|
|
|
+
|
|
|
static void server(in_addr_t ip, u16_t hport, u16_t cport);
|
|
|
static bool handler(u8_t *data, int len);
|
|
|
static void vfdc_handler( u8_t *_data, int bytes_read);
|
|
@@ -65,6 +82,7 @@ static void grfe_handler( u8_t *data, int len);
|
|
|
static void grfb_handler(u8_t *data, int len);
|
|
|
static void grfs_handler(u8_t *data, int len);
|
|
|
static void grfg_handler(u8_t *data, int len);
|
|
|
+static void scroll_task(void* arg);
|
|
|
|
|
|
/* scrolling undocumented information
|
|
|
grfs
|
|
@@ -91,7 +109,19 @@ ANIM_SCREEN_2 0x08 # For scrollonce only, screen 2 was scrolling
|
|
|
*
|
|
|
*/
|
|
|
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)));
|
|
|
+
|
|
|
+ // 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.scroll_frame = malloc(scroller.max);
|
|
|
+ scroller.back_frame = malloc(display->width * display->height / 8);
|
|
|
+
|
|
|
+ // chain handlers
|
|
|
slimp_handler_chain = slimp_handler;
|
|
|
slimp_handler = handler;
|
|
|
|
|
@@ -251,21 +281,26 @@ static void vfdc_handler( u8_t *_data, int bytes_read) {
|
|
|
* Process graphic display data
|
|
|
*/
|
|
|
static void grfe_handler( u8_t *data, int len) {
|
|
|
- display->v_draw(data + 8);
|
|
|
+ scroller.active = false;
|
|
|
+ xSemaphoreTake(display_sem, portMAX_DELAY);
|
|
|
+ display->draw_cbr(data + sizeof(struct grfe_packet));
|
|
|
+ xSemaphoreGive(display_sem);
|
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
|
* Brightness
|
|
|
*/
|
|
|
static void grfb_handler(u8_t *data, int len) {
|
|
|
- s16_t brightness = htons(*(uint16_t*) (data + 4));
|
|
|
+ struct grfb_packet *pkt = (struct grfb_packet*) data;
|
|
|
|
|
|
- LOG_INFO("brightness %hx", brightness);
|
|
|
- if (brightness < 0) {
|
|
|
+ pkt->brightness = htons(pkt->brightness);
|
|
|
+
|
|
|
+ LOG_INFO("brightness %hu", pkt->brightness);
|
|
|
+ if (pkt->brightness < 0) {
|
|
|
display->on(false);
|
|
|
} else {
|
|
|
display->on(true);
|
|
|
- display->brightness(brightness);
|
|
|
+ display->brightness(pkt->brightness);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -273,12 +308,108 @@ static void grfb_handler(u8_t *data, int len) {
|
|
|
* Scroll set
|
|
|
*/
|
|
|
static void grfs_handler(u8_t *data, int len) {
|
|
|
-}
|
|
|
+ struct grfs_packet *pkt = (struct grfs_packet*) data;
|
|
|
+ int size = len - sizeof(struct grfs_packet);
|
|
|
+ int offset = htons(pkt->offset);
|
|
|
|
|
|
+ LOG_INFO("gfrs s:%u d:%u p:%u sp:%u by:%hu m:%hu w:%hu o:%hu",
|
|
|
+ (int) pkt->screen,
|
|
|
+ (int) pkt->direction, // 1=left, 2=right
|
|
|
+ htonl(pkt->pause), // in ms
|
|
|
+ htonl(pkt->speed), // in ms
|
|
|
+ htons(pkt->by), // # of pixel of scroll step
|
|
|
+ htons(pkt->mode), // 0=continuous, 1=once and stop, 2=once and end
|
|
|
+ htons(pkt->width), // total width of animation
|
|
|
+ htons(pkt->offset) // offset if multiple packets are sent
|
|
|
+ );
|
|
|
+
|
|
|
+ // copy scroll parameters
|
|
|
+ scroller.screen = pkt->screen;
|
|
|
+ scroller.direction = pkt->direction;
|
|
|
+ scroller.pause = htonl(pkt->pause);
|
|
|
+ scroller.speed = htonl(pkt->speed);
|
|
|
+ scroller.by = htons(pkt->by);
|
|
|
+ scroller.mode = htons(pkt->mode);
|
|
|
+ scroller.width = htons(pkt->width);
|
|
|
+
|
|
|
+ // copy scroll frame data
|
|
|
+ if (scroller.size + size < scroller.max) {
|
|
|
+ memcpy(scroller.scroll_frame + offset, data + sizeof(struct grfs_packet), size);
|
|
|
+ scroller.size = offset + size;
|
|
|
+ LOG_INFO("scroller size now %u", scroller.size);
|
|
|
+ } else {
|
|
|
+ LOG_INFO("scroller too larger %u/%u", scroller.size + size, scroller.max);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/****************************************************************************************
|
|
|
- * Scroll go
|
|
|
+ * Scroll background frame update & go
|
|
|
*/
|
|
|
static void grfg_handler(u8_t *data, int len) {
|
|
|
+ struct grfg_packet *pkt = (struct grfg_packet*) data;
|
|
|
+
|
|
|
+ memcpy(scroller.back_frame, data + sizeof(struct grfg_packet), len - sizeof(struct grfg_packet));
|
|
|
+ scroller.scroll_width = htons(pkt->width);
|
|
|
+ scroller.active = true;
|
|
|
+ vTaskResume(scroller.task);
|
|
|
+
|
|
|
+ LOG_INFO("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len);
|
|
|
+}
|
|
|
+
|
|
|
+/****************************************************************************************
|
|
|
+ * Scroll task
|
|
|
+ */
|
|
|
+static void scroll_task(void *args) {
|
|
|
+ u8_t *scroll_ptr, *frame;
|
|
|
+ bool active = false;
|
|
|
+ int len = display->width * display->height / 8;
|
|
|
+ int scroll_len;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ if (!active) vTaskSuspend(NULL);
|
|
|
+
|
|
|
+ // restart at the beginning - I don't know what right scrolling means ...
|
|
|
+ scroll_ptr = scroller.scroll_frame;
|
|
|
+ scroll_len = len - (display->width - scroller.scroll_width) * display->height / 8;
|
|
|
+ frame = malloc(display->width * display->height / 8);
|
|
|
+
|
|
|
+ // scroll required amount of columns (within the window)
|
|
|
+ while (scroll_ptr <= scroller.scroll_frame + scroller.size - scroller.by * display->height / 8 - len) {
|
|
|
+ scroll_ptr += scroller.by * display->height / 8;
|
|
|
+
|
|
|
+ // build a combined frame
|
|
|
+ memcpy(frame, scroller.back_frame, len);
|
|
|
+ for (int i = 0; i < scroll_len; i++) frame[i] |= scroll_ptr[i];
|
|
|
+
|
|
|
+ xSemaphoreTake(display_sem, portMAX_DELAY);
|
|
|
+ active = scroller.active;
|
|
|
+
|
|
|
+ if (!active) {
|
|
|
+ LOG_INFO("scrolling interrupted");
|
|
|
+ xSemaphoreGive(display_sem);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ display->draw_cbr(frame);
|
|
|
+ xSemaphoreGive(display_sem);
|
|
|
+ usleep(scroller.speed * 1000);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (active) {
|
|
|
+ // build default screen
|
|
|
+ memcpy(frame, scroller.back_frame, len);
|
|
|
+ for (int i = 0; i < scroll_len; i++) frame[i] |= scroller.scroll_frame[i];
|
|
|
+ xSemaphoreTake(display_sem, portMAX_DELAY);
|
|
|
+ display->draw_cbr(frame);
|
|
|
+ xSemaphoreGive(display_sem);
|
|
|
+
|
|
|
+ // pause for required time and continue (or not)
|
|
|
+ usleep(scroller.pause * 1000);
|
|
|
+ active = (scroller.mode == 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ free(frame);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|