#include "net.h" #include "canvaspixel.h" #include "led-matrix-c.h" #include #include #include //sprintf #include #include #include unsigned int px_width = 192; unsigned int px_height = 128; unsigned int px_pixelcount = 0; unsigned int px_clientcount = 0; struct RGBLedMatrix *matrix; struct LedCanvas *offscreen_canvas; uint32_t colorarray[192][128] = {0}; //bad bad style hardcoded size... // User sessions typedef struct PxSession { } PxSession; // Helper functions static inline int fast_str_startswith(const char* prefix, const char* str) { char cp, cs; while ((cp = *prefix++) == (cs = *str++)) { if (cp == 0) return 1; } return !cp; } // Decimal string to unsigned int. This variant does NOT consume +, - or whitespace. // If **endptr is not NULL, it will point to the first non-decimal character, which // may be \0 at the end of the string. static inline uint32_t fast_strtoul10(const char *str, const char **endptr) { uint32_t result = 0; unsigned char c; for (; (c = *str - '0') <= 9; str++) result = result * 10 + c; if (endptr) *endptr = str; return result; } // Same as fast_strtoul10, but for hex strings. static inline uint32_t fast_strtoul16(const char *str, const char **endptr) { uint32_t result = 0; unsigned char c; while ((c = *str - '0') <= 9 // 0-9 || ((c -= 7) >= 10 && c <= 15) // A-F || ((c -= 32) >= 10 && c <= 15)) { // a-f result = result * 16 + c; str++; } if (endptr) *endptr = str; return result; } // server callbacks void px_on_connect(NetClient *client) { px_clientcount++; } void px_on_close(NetClient *client, int error) { px_clientcount--; } static inline uint32_t blend(uint32_t fg, uint32_t bg) { uint32_t result; uint32_t fg_red = (fg & 0xFF000000) >> 24; uint32_t fg_green = (fg & 0x00FF0000) >> 16; uint32_t fg_blue = (fg & 0x0000FF00) >> 8; uint32_t fg_alpha = (fg & 0x000000FF) >> 0; uint32_t bg_red = (bg & 0xFF000000) >> 24; uint32_t bg_green = (bg & 0x00FF0000) >> 16; uint32_t bg_blue = (bg & 0x0000FF00) >> 8; uint32_t bg_alpha = 256 - fg_alpha; result = (((fg_red * fg_alpha + bg_red * bg_alpha) >> 8) << 24) + (((fg_green * fg_alpha + bg_green * bg_alpha) >> 8) << 16) + (((fg_blue * fg_alpha + bg_blue * bg_alpha) >> 8) << 8) + 0xff; return result; } void px_on_read(NetClient *client, char *line) { if (fast_str_startswith("PX ", line)) { const char * ptr = line + 3; const char * endptr = ptr; errno = 0; uint32_t x = fast_strtoul10(ptr, &endptr); if (endptr == ptr) { net_err(client, "Invalid command (expected decimal as first parameter)"); return; } if (*endptr == '\0') { net_err(client, "Invalid command (second parameter required)"); return; } endptr++; // eat space (or whatever non-decimal is found here) uint32_t y = fast_strtoul10((ptr = endptr), &endptr); if (endptr == ptr) { net_err(client, "Invalid command (expected decimal as second parameter)"); return; } // PX -> Get RGB color at position (x,y) or '0x000000' for out-of-range queries if (*endptr == '\0') { uint32_t c; //canvaspixel_get_px(x, y, &c); c = colorarray[x][y]; char str[64]; sprintf(str, "PX %u %u %06X", x, y, (c >> 8)); net_send(client, str); return; } endptr++; // eat space (or whatever non-decimal is found here) // PX BB|RRGGBB|RRGGBBAA uint32_t c = fast_strtoul16((ptr = endptr), &endptr); if (endptr == ptr) { net_err(client, "Third parameter missing or invalid (should be hex color)"); return; } if (endptr - ptr == 6) { // RGB -> RGBA (most common) c = (c << 8) + 0xff; } else if (endptr - ptr == 8) { // done } else if (endptr - ptr == 2) { // WW -> RGBA c = (c << 24) + (c << 16) + (c << 8) + 0xff; } else { net_err(client, "Color hex code must be 2, 6 or 8 characters long (WW, RGB or RGBA)"); return; } px_pixelcount++; //canvaspixel_set_px(x, y, c); if (x < px_width && y < px_height){ c = blend(c, colorarray[x][y]); colorarray[x][y] = c; led_canvas_set_pixel(offscreen_canvas, x, y, c >>24&255 , c>>16&255, c>>8&255); } //offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas); } else if (fast_str_startswith("SIZE", line)) { char str[64]; snprintf(str, 64, "SIZE %d %d", px_width, px_height); net_send(client, str); } else if (fast_str_startswith("STATS", line)) { char str[128]; snprintf(str, 128, "STATS px:%u conn:%u", px_pixelcount, px_clientcount); net_send(client, str); } else if (fast_str_startswith("HELP", line)) { net_send(client, "\ PX x y: Get color at position (x,y)\n\ PX x y rrggbb(aa): Draw a pixel (with optional alpha channel)\n\ SIZE: Get canvaspixel size\n\ STATS: Return statistics"); } else { net_err(client, "Unknown command"); } } void px_on_key(int key, int scancode, int mods) { printf("Key pressed: key:%d scancode:%d mods:%d\n", key, scancode, mods); if (key == 301) { // F12 canvaspixel_fullscreen(canvaspixel_get_display() + 1); } else if (key == 67) { // c canvaspixel_fill(0x00000088); } else if (key == 81 || key == 256) { // q or ESC canvaspixel_close(); } } //void px_on_resize() { // canvaspixel_get_size(&px_width, &px_height); //} void px_on_window_close() { printf("Window closed\n"); net_stop(); //led_matrix_delete(matrix); } void *myThreadFun(/**struct RGBLedMatrix *matrix, struct LedCanvas *offscreen_canvas**/) { while(true){ offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas); } } int main(int argc, char **argv) { canvaspixel_setcb_key(&px_on_key); struct RGBLedMatrixOptions options; struct RGBLedRuntimeOptions rt_options; //int width, height; //int x, y, i; memset(&options, 0, sizeof(options)); options.rows = 32; options.cols = 64; options.chain_length = 6; options.parallel = 2; options.hardware_mapping = "regular"; options.pixel_mapper_config = "U-mapper"; memset(&rt_options, 0, sizeof(rt_options)); rt_options.gpio_slowdown = 4; options.chain_length = 6; /* This supports all the led commandline options. Try --led-help */ matrix = led_matrix_create_from_options_and_rt_options(&options, &rt_options); if (matrix == NULL) return 1; /* Let's do an example with double-buffering. We create one extra * buffer onto which we draw, which is then swapped on each refresh. * This is typically a good aproach for animations and such. */ offscreen_canvas = led_matrix_create_offscreen_canvas(matrix); // canvaspixel_setcb_resize(&px_on_resize); //canvaspixel_start(1024, &px_on_window_close); pthread_t thread_id; pthread_create(&thread_id, NULL, myThreadFun, NULL); net_start(1337, &px_on_connect, &px_on_read, &px_on_close); return 0; }