#include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include #include "timer.h" #include "bufsize.h" #include "packet.h" #include "timespec.h" #include "ping_pong.h" #include "gpio.h" #define DEST_PORT 4321 #define DEST_ADDR "fd00::2" #define PORT 1234 struct glyph { int32_t width; int32_t height; int32_t horiBearingX; int32_t horiBearingY; int32_t horiAdvance; SDL_Texture * texture; }; static struct glyph glyphs[0x80 - 0x20] = {0}; int32_t face_height; int load_outline_char(SDL_Renderer * renderer, const FT_Face face, const FT_Int32 load_flags, const FT_Render_Mode render_mode, const FT_ULong char_code) { FT_Error error; FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); error = FT_Load_Glyph(face, glyph_index, load_flags); if (error) { printf("FT_Load_Glyph %s\n", FT_Error_String(error)); return -1; } error = FT_Render_Glyph(face->glyph, render_mode); if (error) { printf("FT_Render_Glyph %s\n", FT_Error_String(error)); return -1; } struct glyph * glyph = &glyphs[char_code - 0x20]; glyph->width = face->glyph->bitmap.width; glyph->height = face->glyph->bitmap.rows; glyph->horiBearingX = face->glyph->metrics.horiBearingX; glyph->horiBearingY = face->glyph->metrics.horiBearingY; glyph->horiAdvance = face->glyph->metrics.horiAdvance; if (face->glyph->bitmap.pitch != 0) { SDL_Surface * surface = SDL_CreateSurface(face->glyph->bitmap.width, face->glyph->bitmap.rows, SDL_PIXELFORMAT_RGBA8888); SDL_LockSurface(surface); for (int y = 0; y < face->glyph->bitmap.rows; y++) { for (int x = 0; x < face->glyph->bitmap.width; x++) { int s_ix = y * face->glyph->bitmap.pitch + x; int d_ix = y * surface->pitch + x * 4; uint8_t gray = face->glyph->bitmap.buffer[s_ix]; ((uint8_t *)surface->pixels)[d_ix + 0] = gray; ((uint8_t *)surface->pixels)[d_ix + 1] = gray; ((uint8_t *)surface->pixels)[d_ix + 2] = gray; ((uint8_t *)surface->pixels)[d_ix + 3] = 255; } } SDL_UnlockSurface(surface); if (glyph->texture != NULL) SDL_DestroyTexture(glyph->texture); glyph->texture = SDL_CreateTextureFromSurface(renderer, surface); if (glyph->texture == NULL) { printf("%s\n", SDL_GetError()); } assert(glyph->texture != NULL); SDL_DestroySurface(surface); } return 0; } int load_font(SDL_Renderer * renderer, int font_size) { FT_Library library; FT_Face face; FT_Error error; error = FT_Init_FreeType(&library); if (error) { printf("FT_Init_FreeType\n"); return -1; } error = FT_New_Face(library, "/home/bilbo/timer/DejaVuSansMono.ttf", 0, &face); if (error) { printf("FT_New_Face\n"); return -1; } error = FT_Set_Pixel_Sizes(face, 0, font_size); if (error) { printf("FT_Set_Pixel_Sizes: %s %d\n", FT_Error_String(error), error); return -1; } for (int char_code = 0x20; char_code <= 0x7f; char_code++) { load_outline_char(renderer, face, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL, char_code); } printf("loaded size %ld\n", face->size->metrics.height >> 6); face_height = face->size->metrics.height; return 0; } void render(SDL_Renderer * renderer, int window_width, int window_height, const char * s, int length) { int32_t advance = (window_width - (window_width * 5 / 100)) << 6; for (int i = (length - 1); i >= 0; i--) { char c = s[i]; struct glyph * glyph = &glyphs[c - 0x20]; advance -= glyph->horiAdvance; if (glyph->texture != NULL) { double x = (double)(advance + glyph->horiBearingX) / 64.f; double y = (window_height / 2) + (face_height >> 6) / 3 + ((double)glyph->horiBearingY / -64.f); SDL_FRect srect = { .x = 0.f, .y = 0.f, .w = glyph->width, .h = glyph->height }; SDL_FRect drect = { .x = x, .y = y, .w = glyph->width, .h = glyph->height }; SDL_RenderTexture(renderer, glyph->texture, &srect, &drect); } } } int max(int a, int b) { return a > b ? a : b; } int handle_buf(int sockfd, void * buf, ssize_t length, struct timer_state * timer_state, struct link_state * link_state) { struct event * evt = (struct event *)buf; if (evt->sequence != 0) { if (evt->sequence == link_state->recv_sequence) { printf("ignore duplicate seq %d; recv %ld type %d\n", evt->sequence, link_state->recv_sequence, evt->type); return 0; } else { printf("type %d seq %d\n", evt->sequence, evt->type); link_state->recv_sequence = evt->sequence; } } switch (evt->type) { case EVENT_TYPE__TIME_STOP: { if (length != (sizeof (struct packet_stopwatch))) { printf("handle_buf: packet_stopwatch: invalid length\n"); return 0; } struct packet_stopwatch * time_pkt = (struct packet_stopwatch *)buf; memcpy(&timer_state->time, &time_pkt->stopwatch_time, (sizeof (struct stopwatch_time))); timer_state->status = TIMER_STOPPED; packet_send_ack(sockfd, &link_state->dest_addr, evt->sequence); } break; case EVENT_TYPE__TIME_START: { if (length != (sizeof (struct packet_start))) { printf("handle_buf: time start: invalid length\n"); return 0; } int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &timer_state->counter.start); assert(ret != -1); timer_state->counter.offset = timespec_div(&link_state->remote_average_rtt, 2); printf("counter_offset %ld.%09ld\n", timer_state->counter.offset.tv_sec, timer_state->counter.offset.tv_nsec); timer_state->status = TIMER_RUNNING; packet_send_ack(sockfd, &link_state->dest_addr, evt->sequence); } break; case EVENT_TYPE__TIME_RESUME: { if (length != (sizeof (struct packet_resume))) { printf("handle_buf: time resume: invalid length\n"); return 0; } struct packet_resume * resume_pkt = (struct packet_resume *)buf; printf("resume offset %ld.%09ld\n", resume_pkt->offset.tv_sec, resume_pkt->offset.tv_nsec); int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &timer_state->counter.start); assert(ret != -1); timer_state->counter.offset = timespec_div(&link_state->remote_average_rtt, 2); timer_state->counter.offset = timespec_add(&timer_state->counter.offset, &resume_pkt->offset); printf("counter offset %ld.%09ld\n", timer_state->counter.offset.tv_sec, timer_state->counter.offset.tv_nsec); timer_state->status = TIMER_RUNNING; packet_send_ack(sockfd, &link_state->dest_addr, evt->sequence); } break; case EVENT_TYPE__PING: [[fallthrough]]; case EVENT_TYPE__PONG: [[fallthrough]]; case EVENT_TYPE__AVERAGE_RTT: { int ret = handle_ping_pong(sockfd, buf, length, link_state); if (ret == -1) { return -1; } } break; default: printf("unhandled event %d\n", evt->type); break; } return 0; } int handle_sockfd(int sockfd, struct timer_state * timer_state, struct link_state * link_state) { char buf[BUFSIZE]; struct sockaddr_in6 src_addr; socklen_t addrlen = (sizeof (struct sockaddr_in6)); while (1) { ssize_t recv_len = recvfrom(sockfd, buf, (sizeof (buf)), 0, (struct sockaddr *)&src_addr, &addrlen); if (recv_len == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { //perror("sock eagain"); return 0; } else { perror("recvfrom sock"); return -1; } } //char src_addr_str[INET6_ADDRSTRLEN]; //inet_ntop(AF_INET6, &src_addr.sin6_addr, src_addr_str, INET6_ADDRSTRLEN); //printf("received packet from %s:%d\n", src_addr_str, ntohs(src_addr.sin6_port)); //printf("length: %ld\n", recv_len); int ret = handle_buf(sockfd, buf, recv_len, timer_state, link_state); if (ret == -1) { return -1; } } return 0; } char * format_base10(char * buf, int value, int digits) { static int table[10] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; if (digits <= 0) return buf; int power = table[digits - 1]; for (int i = 0; i < digits; i++) { buf[i] = '0' + ((value / power) % 10); power /= 10; } return buf + digits; } int main() { int ret; SDL_Window * window; SDL_Renderer * renderer; ret = SDL_Init(SDL_INIT_VIDEO); assert(ret == 0); window = SDL_CreateWindow("timer", 512, // w 512, // h SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_MAXIMIZED); int num_drivers = SDL_GetNumRenderDrivers(); printf("available drivers:\n"); for (int i = 0; i < num_drivers; i++) { const char * s = SDL_GetRenderDriver(i); printf(" %s\n", s); } renderer = SDL_CreateRenderer(window, "opengles2"); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_PropertiesID props = SDL_GetRendererProperties(renderer); const char * name = SDL_GetRendererName(renderer); assert(name != NULL); printf("renderer: %s\n", name); const SDL_PixelFormatEnum * formats = SDL_GetProperty(props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL); assert(formats != NULL); while (*formats != SDL_PIXELFORMAT_UNKNOWN) { printf("%s\n", SDL_GetPixelFormatName(*formats++)); } int last_width = -1; uint64_t ticks = SDL_GetTicks(); uint64_t last_ping_tick = 0; const int min_length = 5; int max_length = min_length; /* socket */ int sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); if (sockfd == -1) { perror("socket"); return -1; } struct sockaddr_in6 sockaddr; sockaddr.sin6_family = AF_INET6; sockaddr.sin6_port = htons(PORT); sockaddr.sin6_addr = in6addr_any; ret = bind(sockfd, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in6))); if (ret == -1) { perror("bind"); return -1; } /* */ struct timer_state timer_state = {0}; struct link_state link_state = {0}; struct gpio_state gpio_state = {0}; link_state.dest_addr.sin6_family = AF_INET6; link_state.dest_addr.sin6_port = htons(DEST_PORT); ret = inet_pton(AF_INET6, DEST_ADDR, &link_state.dest_addr.sin6_addr); assert(ret == 1); gpio_open("/dev/gpiochip0", &gpio_state); // ignored gpio_open error while (1) { handle_sockfd(sockfd, &timer_state, &link_state); char str_buf[20 * 2 + 1 + 1]; switch (timer_state.status) { case TIMER_STOPPED: { char * bufi = str_buf; bufi = format_base10(bufi, timer_state.time.integer_value, timer_state.time.integer_digits); *bufi++ = '.'; bufi = format_base10(bufi, timer_state.time.fraction_value, timer_state.time.fraction_digits); *bufi = 0; } break; case TIMER_RUNNING: { struct timespec now; int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &now); assert(ret != -1); struct timespec real_start = timespec_sub(&timer_state.counter.start, &timer_state.counter.offset); struct timespec duration = timespec_sub(&now, &real_start); snprintf(str_buf, (sizeof (str_buf)) - 1, "%ld.%02ld", duration.tv_sec, duration.tv_nsec / (1000000000 / 100)); // 2 digits } break; default: str_buf[0] = 0; break; } int length = strlen(str_buf); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); int window_width; int window_height; ret = SDL_GetWindowSizeInPixels(window, &window_width, &window_height); assert(ret == 0); if ((window_width != last_width) || (length > max_length)) { max_length = max(min_length, length); last_width = window_width; int divisor = max(1, max_length); int font_size = (window_width * 150 / 100) / divisor; printf("fs %d %d max_length: %d\n", font_size, window_width, max_length); load_font(renderer, font_size); } render(renderer, window_width, window_height, str_buf, length); while (SDL_GetTicks() - ticks < (1000 / 60)) { SDL_Delay(1); } SDL_RenderPresent(renderer); ticks = SDL_GetTicks(); if (ticks - last_ping_tick > PING_INTERVAL * 1000) { //printf("send ping\n"); int ret = packet_send_ping(sockfd, &link_state.dest_addr); if (ret == -1) { return -1; } last_ping_tick = ticks; gpio_set_values_from_link_states(&gpio_state, &link_state, 1); } SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_QUIT: goto exit; case SDL_EVENT_KEY_DOWN: if (event.key.keysym.sym == SDLK_ESCAPE) goto exit; break; default: break; } } } exit: SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); close(sockfd); }