time_display: get timing data from UDP
This commit is contained in:
parent
7a0518c35b
commit
e72315b787
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
|||||||
*.gch
|
*.gch
|
||||||
*.d
|
*.d
|
||||||
main
|
main
|
||||||
|
serial_forwarder
|
||||||
|
time_display
|
15
Makefile
15
Makefile
@ -13,20 +13,27 @@ DEPFLAGS = -MMD -MP
|
|||||||
|
|
||||||
OPT = -O3 -march=native
|
OPT = -O3 -march=native
|
||||||
|
|
||||||
OBJS = \
|
|
||||||
timer.o
|
|
||||||
|
|
||||||
SERIAL_FORWARDER_OBJS = \
|
SERIAL_FORWARDER_OBJS = \
|
||||||
serial_forwarder.o \
|
serial_forwarder.o \
|
||||||
serial.o \
|
serial.o \
|
||||||
parse_serial.o
|
parse_serial.o
|
||||||
|
|
||||||
all: serial_forwarder
|
TIME_DISPLAY_OBJS = \
|
||||||
|
time_display.o
|
||||||
|
|
||||||
|
all: serial_forwarder time_display
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.d *.gch
|
||||||
|
rm -f serial_forwarder time_display
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
|
$(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
|
||||||
|
|
||||||
serial_forwarder: $(SERIAL_FORWARDER_OBJS)
|
serial_forwarder: $(SERIAL_FORWARDER_OBJS)
|
||||||
|
$(CC) $^ -o $@
|
||||||
|
|
||||||
|
time_display: $(TIME_DISPLAY_OBJS)
|
||||||
$(CC) $(LDFLAGS) $^ -o $@
|
$(CC) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
-include $(shell find -type f -name '*.d')
|
-include $(shell find -type f -name '*.d')
|
||||||
|
10
link.h
Normal file
10
link.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct link_state {
|
||||||
|
uint64_t send_sequence;
|
||||||
|
uint64_t recv_sequence;
|
||||||
|
struct timespec send_ping;
|
||||||
|
struct timespec recv_ping;
|
||||||
|
};
|
71
packet.h
71
packet.h
@ -1,42 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef _Float64 float64_t;
|
#include "timer.h"
|
||||||
|
|
||||||
enum packet_type {
|
#define EVENT_TYPE__INVALID 0x00000000
|
||||||
PACKET_TYPE__TIME_START = 0x2112858517,
|
#define EVENT_TYPE__TIME_START 0x12858517
|
||||||
PACKET_TYPE__TIME_STOP = 0xdf75d3f8,
|
#define EVENT_TYPE__TIME_STOP 0xdf75d3f8
|
||||||
PACKET_TYPE__RTT_DELAY = 0xc7065931,
|
#define EVENT_TYPE__RTT_DELAY 0xc7065931
|
||||||
PACKET_TYPE__ACK = 0xfde7959f,
|
#define EVENT_TYPE__PING 0x0ba0d4c2
|
||||||
};
|
#define EVENT_TYPE__ACK 0xfde7959f
|
||||||
|
|
||||||
struct time_stop_data {
|
static_assert((sizeof (struct timespec)) == 8 + 8);
|
||||||
uint32_t integer_value;
|
|
||||||
uint32_t fraction_value;
|
|
||||||
uint16_t integer_digits;
|
|
||||||
uint16_t fraction_digits;
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct time_stop_data)) == 4 * 3);
|
struct event {
|
||||||
|
|
||||||
struct rtt_delay_data {
|
|
||||||
float64_t offset;
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
struct ack_data {
|
|
||||||
uint64_t seq;
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct rtt_delay_data)) == 8);
|
|
||||||
|
|
||||||
struct timing_packet {
|
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
union {
|
uint32_t sequence;
|
||||||
struct time_stop_data time_stop;
|
|
||||||
struct rtt_delay_data rtt_delay;
|
|
||||||
struct ack_data ack;
|
|
||||||
};
|
};
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
static_assert((sizeof (struct timing_packet)) == 4 * 4);
|
struct packet_stopwatch {
|
||||||
|
struct event event;
|
||||||
|
struct stopwatch_time stopwatch_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert((sizeof (struct packet_stopwatch)) ==
|
||||||
|
(sizeof (struct event)) + (sizeof (struct stopwatch_time)));
|
||||||
|
|
||||||
|
struct packet_start {
|
||||||
|
struct event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert((sizeof (struct packet_start)) == (sizeof (struct event)));
|
||||||
|
|
||||||
|
struct packet_ack {
|
||||||
|
struct event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert((sizeof (struct packet_ack)) == (sizeof (struct event)));
|
||||||
|
|
||||||
|
struct packet_rtt_delay {
|
||||||
|
struct event event;
|
||||||
|
struct timespec offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert((sizeof (struct packet_rtt_delay)) == (sizeof (struct event)) + (sizeof (struct timespec)));
|
||||||
|
@ -80,34 +80,35 @@ bool is_timer_resume(uint8_t const * const buf, int length)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle_line(uint8_t const * const buf, int length,
|
uint32_t handle_line(uint8_t const * const buf, int length,
|
||||||
struct timer_state * timer_state)
|
struct timer_state * timer_state)
|
||||||
{
|
{
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
timer_state->status = TIMER_RUNNING;
|
timer_state->status = TIMER_RUNNING;
|
||||||
int ret = clock_gettime(CLOCK_MONOTONIC, &timer_state->counter.start);
|
int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &timer_state->counter.start);
|
||||||
|
assert(ret != -1);
|
||||||
timer_state->counter.offset.tv_sec = 0;
|
timer_state->counter.offset.tv_sec = 0;
|
||||||
timer_state->counter.offset.tv_nsec = 0;
|
timer_state->counter.offset.tv_nsec = 0;
|
||||||
assert(ret != -1);
|
return EVENT_TYPE__TIME_START;
|
||||||
return true;
|
} /*else if (is_timer_resume(buf, length)) {
|
||||||
} else if (is_timer_resume(buf, length)) {
|
return true; // fixme
|
||||||
return true;
|
} */ else {
|
||||||
} else {
|
|
||||||
int ret = parse_time(buf, length, &timer_state->time);
|
int ret = parse_time(buf, length, &timer_state->time);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
timer_state->status = TIMER_STOPPED;
|
timer_state->status = TIMER_STOPPED;
|
||||||
return true;
|
return EVENT_TYPE__TIME_STOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle_parse(uint8_t * read_buf, int length,
|
uint32_t handle_parse(uint8_t * read_buf, int length,
|
||||||
struct parser_state * parser_state,
|
struct parser_state * parser_state,
|
||||||
struct timer_state * timer_state)
|
struct timer_state * timer_state)
|
||||||
{
|
{
|
||||||
bool have_state_change = false;
|
uint32_t type = EVENT_TYPE__INVALID;
|
||||||
|
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
int copy_length = min(length, BUFSIZE - parser_state->offset);
|
int copy_length = min(length, BUFSIZE - parser_state->offset);
|
||||||
length -= copy_length;
|
length -= copy_length;
|
||||||
@ -119,7 +120,7 @@ bool handle_parse(uint8_t * read_buf, int length,
|
|||||||
while (i < parser_state->offset) {
|
while (i < parser_state->offset) {
|
||||||
if (parser_state->buf[i] == '\r') {
|
if (parser_state->buf[i] == '\r') {
|
||||||
fprintf(stderr, "r at %d %d\n", i, parser_state->offset);
|
fprintf(stderr, "r at %d %d\n", i, parser_state->offset);
|
||||||
have_state_change |= handle_line(parser_state->buf, i,
|
type = handle_line(parser_state->buf, i,
|
||||||
timer_state);
|
timer_state);
|
||||||
parser_state->offset -= i + 1;
|
parser_state->offset -= i + 1;
|
||||||
assert(parser_state->offset >= 0);
|
assert(parser_state->offset >= 0);
|
||||||
@ -135,5 +136,5 @@ bool handle_parse(uint8_t * read_buf, int length,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return have_state_change;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ struct parser_state {
|
|||||||
int offset;
|
int offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool handle_parse(uint8_t * read_buf, int length,
|
uint32_t handle_parse(uint8_t * read_buf, int length,
|
||||||
struct parser_state * parser_state,
|
struct parser_state * parser_state,
|
||||||
struct timer_state * timer_state);
|
struct timer_state * timer_state);
|
||||||
|
BIN
serial_forwarder
BIN
serial_forwarder
Binary file not shown.
@ -8,26 +8,35 @@
|
|||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/timerfd.h>
|
#include <sys/timerfd.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "bufsize.h"
|
#include "bufsize.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "parse_serial.h"
|
#include "parse_serial.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
#define PORT 4321
|
#define PORT 4321
|
||||||
//#define SERIALPORT "/dev/ttyS0"
|
//#define SERIALPORT "/dev/ttyS0"
|
||||||
#define SERIALPORT "/dev/ttyUSB0"
|
//#define SERIALPORT "/dev/ttyUSB0"
|
||||||
|
#define SERIALPORT "foo.fifo"
|
||||||
|
|
||||||
int handle_sock(int sock)
|
static struct sockaddr_in6 dest_addr;
|
||||||
|
|
||||||
|
int handle_sockfd(int sockfd)
|
||||||
{
|
{
|
||||||
struct sockaddr_in src_addr;
|
struct sockaddr_in6 src_addr;
|
||||||
socklen_t addrlen = (sizeof (struct sockaddr_in));
|
socklen_t addrlen = (sizeof (struct sockaddr_in6));
|
||||||
|
|
||||||
char buf[BUFSIZE];
|
char buf[BUFSIZE];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ssize_t recv_len = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr *)&src_addr, &addrlen);
|
ssize_t recv_len = recvfrom(sockfd,
|
||||||
|
buf, (sizeof (buf)),
|
||||||
|
0,
|
||||||
|
(struct sockaddr *)&src_addr, &addrlen);
|
||||||
if (recv_len == -1) {
|
if (recv_len == -1) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
perror("sock eagain");
|
perror("sock eagain");
|
||||||
@ -38,10 +47,10 @@ int handle_sock(int sock)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("got %ld data\n", recv_len);
|
char src_addr_str[INET6_ADDRSTRLEN];
|
||||||
printf("Received packet from %s:%d\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
|
inet_ntop(AF_INET6, &src_addr.sin6_addr, src_addr_str, INET6_ADDRSTRLEN);
|
||||||
buf[recv_len] = 0;
|
printf("Received packet from %s:%d\n", src_addr_str, ntohs(src_addr.sin6_port));
|
||||||
printf("Data: %s\n", buf);
|
printf("length: %ld\n", recv_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,19 +95,57 @@ int handle_timerfd(int timerfd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct receiver_state {
|
int send_stopwatch_event(int sockfd,
|
||||||
uint64_t seq;
|
struct timer_state * timer_state,
|
||||||
uint64_t ack_seq;
|
struct link_state * link_state)
|
||||||
};
|
{
|
||||||
|
struct packet_stopwatch pkt;
|
||||||
|
pkt.event.type = EVENT_TYPE__TIME_STOP;
|
||||||
|
pkt.event.sequence = link_state.send_sequence++;
|
||||||
|
memcpy(&pkt.stopwatch_time, &timer_state->time, (sizeof (struct stopwatch_time)));
|
||||||
|
|
||||||
int handle_serialfd(int timerfd,
|
int ret = sendto(sockfd,
|
||||||
|
&pkt, (sizeof (struct packet_stopwatch)),
|
||||||
|
0,
|
||||||
|
(struct sockaddr *)&dest_addr,
|
||||||
|
(sizeof (struct sockaddr_in6)));
|
||||||
|
if (ret == -1) {
|
||||||
|
perror("sendto()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_start_event(int sockfd,
|
||||||
|
struct timer_state * timer_state,
|
||||||
|
struct link_state * link_state)
|
||||||
|
{
|
||||||
|
struct packet_start pkt;
|
||||||
|
pkt.event.type = EVENT_TYPE__TIME_START;
|
||||||
|
pkt.event.sequence = link_state.send_sequence++;
|
||||||
|
|
||||||
|
int ret = sendto(sockfd,
|
||||||
|
&pkt, (sizeof (struct packet_stopwatch)),
|
||||||
|
0,
|
||||||
|
(struct sockaddr *)&dest_addr,
|
||||||
|
(sizeof (struct sockaddr_in6)));
|
||||||
|
if (ret == -1) {
|
||||||
|
perror("sendto()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_serialfd(int serialfd,
|
||||||
|
int sockfd,
|
||||||
struct parser_state * parser_state,
|
struct parser_state * parser_state,
|
||||||
struct timer_state * timer_state)
|
struct timer_state * timer_state,
|
||||||
|
struct link_state * link_state)
|
||||||
{
|
{
|
||||||
uint8_t buf[BUFSIZE];
|
uint8_t buf[BUFSIZE];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ssize_t len = read(timerfd, buf, (sizeof (buf)));
|
ssize_t len = read(serialfd, buf, (sizeof (buf)));
|
||||||
if (len == -1) {
|
if (len == -1) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
fprintf(stderr, "serialfd eagain\n");
|
fprintf(stderr, "serialfd eagain\n");
|
||||||
@ -115,10 +162,26 @@ int handle_serialfd(int timerfd,
|
|||||||
}
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
*/
|
*/
|
||||||
bool have_state_change = handle_parse(buf, len,
|
uint32_t type = handle_parse(buf, len,
|
||||||
parser_state,
|
parser_state,
|
||||||
timer_state);
|
timer_state);
|
||||||
printf("have_state_change: %d\n", have_state_change);
|
int ret;
|
||||||
|
switch (type) {
|
||||||
|
case EVENT_TYPE__TIME_STOP:
|
||||||
|
ret = send_stopwatch_event(sockfd, timer_state, link_state);
|
||||||
|
if (ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EVENT_TYPE__TIME_START:
|
||||||
|
ret = send_start_event(sockfd, timer_state, link_state);
|
||||||
|
if (ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -128,20 +191,20 @@ int main(void)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
|
int sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
|
||||||
if (sock == -1) {
|
if (sockfd == -1) {
|
||||||
perror("socket");
|
perror("socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in sockaddr = {0};
|
struct sockaddr_in6 sockaddr = {0};
|
||||||
sockaddr.sin_family = AF_INET;
|
sockaddr.sin6_family = AF_INET6;
|
||||||
sockaddr.sin_port = htons(PORT);
|
sockaddr.sin6_port = htons(PORT);
|
||||||
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
sockaddr.sin6_addr = in6addr_any;
|
||||||
|
|
||||||
ret = bind(sock, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in)));
|
ret = bind(sockfd, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in6)));
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
perror("bind");
|
perror("sockfd bind");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,8 +220,8 @@ int main(void)
|
|||||||
{
|
{
|
||||||
struct epoll_event ev;
|
struct epoll_event ev;
|
||||||
ev.events = EPOLLIN | EPOLLET;
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
ev.data.fd = sock;
|
ev.data.fd = sockfd;
|
||||||
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev);
|
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
perror("epoll_ctl: sock");
|
perror("epoll_ctl: sock");
|
||||||
return -1;
|
return -1;
|
||||||
@ -192,10 +255,12 @@ int main(void)
|
|||||||
perror("open: serialport");
|
perror("open: serialport");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
ret = set_terminal_attributes(serialfd);
|
ret = set_terminal_attributes(serialfd);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
{
|
{
|
||||||
struct epoll_event ev;
|
struct epoll_event ev;
|
||||||
@ -208,8 +273,14 @@ int main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dest_addr.sin6_family = AF_INET6;
|
||||||
|
dest_addr.sin6_port = htons(1234);
|
||||||
|
ret = inet_pton(AF_INET6, "::1", &dest_addr.sin6_addr);
|
||||||
|
assert(ret == 1);
|
||||||
|
|
||||||
struct parser_state parser_state = {0};
|
struct parser_state parser_state = {0};
|
||||||
struct timer_state timer_state = {0};
|
struct timer_state timer_state = {0};
|
||||||
|
struct link_state link_state = {0};
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
printf("Wait for datagram\n");
|
printf("Wait for datagram\n");
|
||||||
@ -221,8 +292,8 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int n = 0; n < nfds; ++n) {
|
for (int n = 0; n < nfds; ++n) {
|
||||||
if (events[n].data.fd == sock) {
|
if (events[n].data.fd == sockfd) {
|
||||||
ret = handle_sock(sock);
|
ret = handle_sockfd(sockfd);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (events[n].data.fd == timerfd) {
|
} else if (events[n].data.fd == timerfd) {
|
||||||
@ -232,31 +303,25 @@ int main(void)
|
|||||||
} else if (events[n].data.fd == serialfd) {
|
} else if (events[n].data.fd == serialfd) {
|
||||||
fprintf(stderr, "handle_serialfd\n");
|
fprintf(stderr, "handle_serialfd\n");
|
||||||
ret = handle_serialfd(serialfd,
|
ret = handle_serialfd(serialfd,
|
||||||
|
sockfd,
|
||||||
&parser_state,
|
&parser_state,
|
||||||
&timer_state);
|
&timer_state,
|
||||||
if (ret == -1)
|
&link_state);
|
||||||
|
if (ret == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
struct sockaddr_in dest_addr;
|
|
||||||
dest_addr.sin_family = AF_INET;
|
|
||||||
dest_addr.sin_port = htons(1234);
|
|
||||||
dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
||||||
|
|
||||||
ret = sendto(sock, buf, 3, 0, (struct sockaddr *)&dest_addr, (sizeof (struct sockaddr_in)));
|
|
||||||
if (ret == -1) {
|
|
||||||
perror("sendto()");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//sleep(2);
|
//sleep(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sock);
|
close(sockfd);
|
||||||
|
close(serialfd);
|
||||||
|
close(timerfd);
|
||||||
|
close(epollfd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
358
time_display.c
358
time_display.c
@ -1,61 +1,355 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <arpa/inet.h>
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include "timer.h"
|
||||||
|
#include "bufsize.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
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, "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;
|
||||||
|
}
|
||||||
|
|
||||||
#define BUFLEN 512
|
|
||||||
#define PORT 1234
|
#define PORT 1234
|
||||||
|
|
||||||
int main(void)
|
void handle_buf(void * buf, ssize_t length,
|
||||||
|
struct timer_state * timer_state)
|
||||||
|
{
|
||||||
|
struct event * evt = (struct event *)buf;
|
||||||
|
switch (evt->type) {
|
||||||
|
case EVENT_TYPE__TIME_STOP:
|
||||||
|
if (length != (sizeof (struct packet_stopwatch))) {
|
||||||
|
printf("handle_buf: event_stopwatch: invalid length\n");
|
||||||
|
}
|
||||||
|
printf("handle_buf: event_stopwatch: valid length\n");
|
||||||
|
|
||||||
|
struct packet_stopwatch * time_evt = (struct packet_stopwatch *)buf;
|
||||||
|
memcpy(&timer_state->time, &time_evt->stopwatch_time, (sizeof (struct stopwatch_time)));
|
||||||
|
timer_state->status = TIMER_STOPPED;
|
||||||
|
break;
|
||||||
|
case EVENT_TYPE__TIME_START:
|
||||||
|
int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &timer_state->counter.start);
|
||||||
|
assert(ret != -1);
|
||||||
|
timer_state->counter.offset.tv_sec = 0;
|
||||||
|
timer_state->counter.offset.tv_nsec = 0;
|
||||||
|
timer_state->status = TIMER_RUNNING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("unhandled event %d\n", evt->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_sockfd(int sockfd, struct timer_state * timer_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);
|
||||||
|
handle_buf(buf, recv_len, timer_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
SDL_Window * window;
|
||||||
|
SDL_Renderer * renderer;
|
||||||
|
|
||||||
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
ret = SDL_Init(SDL_INIT_VIDEO);
|
||||||
if (sock == -1) {
|
assert(ret == 0);
|
||||||
|
|
||||||
|
window = SDL_CreateWindow("timer",
|
||||||
|
512, // w
|
||||||
|
512, // h
|
||||||
|
SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_MAXIMIZED);
|
||||||
|
|
||||||
|
renderer = SDL_CreateRenderer(window, NULL);
|
||||||
|
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();
|
||||||
|
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");
|
perror("socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in sockaddr = {0};
|
struct sockaddr_in6 sockaddr;
|
||||||
sockaddr.sin_family = AF_INET;
|
sockaddr.sin6_family = AF_INET6;
|
||||||
sockaddr.sin_port = htons(PORT);
|
sockaddr.sin6_port = htons(PORT);
|
||||||
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
sockaddr.sin6_addr = in6addr_any;
|
||||||
|
|
||||||
ret = bind(sock, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in)));
|
ret = bind(sockfd, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in6)));
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
perror("bind");
|
perror("bind");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[BUFLEN];
|
/*
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct timer_state timer_state = {0};
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
printf("Wait for datagram\n");
|
handle_sockfd(sockfd, &timer_state);
|
||||||
|
|
||||||
struct sockaddr_in src_addr = {0};
|
char str_buf[16];
|
||||||
socklen_t addrlen = (sizeof (struct sockaddr_in));
|
switch (timer_state.status) {
|
||||||
|
case TIMER_STOPPED:
|
||||||
|
snprintf(str_buf, (sizeof (str_buf)) - 1, "%d.%02d", timer_state.time.integer_value, timer_state.time.fraction_value);
|
||||||
|
break;
|
||||||
|
case TIMER_RUNNING:
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &now);
|
||||||
|
assert(ret != -1);
|
||||||
|
|
||||||
ssize_t recv_len = recvfrom(sock, buf, BUFLEN, 0, (struct sockaddr *)&src_addr, &addrlen);
|
double now_d = (double)now.tv_sec + ((double)now.tv_nsec / 1000000000.f);
|
||||||
if (recv_len == -1) {
|
struct timespec * start = &timer_state.counter.start;
|
||||||
perror("recvfrom");
|
double start_d = (double)start->tv_sec + ((double)start->tv_nsec / 1000000000.f);
|
||||||
return -1;
|
double duration = now_d - start_d;
|
||||||
|
|
||||||
|
snprintf(str_buf, (sizeof (str_buf)) - 1, "%.02f", duration);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str_buf[0] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int length = strlen(str_buf);
|
||||||
|
|
||||||
|
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 * 8 / 10));
|
||||||
|
int font_size = (window_width * 95 / 100) / divisor;
|
||||||
|
printf("fs %d %d max_length: %d\n", font_size, window_width, max_length);
|
||||||
|
load_font(renderer, font_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[recv_len] = 0;
|
render(renderer, window_width, window_height, str_buf, length);
|
||||||
printf("Received packet from %s:%d\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
|
|
||||||
printf("Data: %s\n", buf);
|
|
||||||
|
|
||||||
/*
|
while (SDL_GetTicks() - ticks < (1000 / 60)) { SDL_Delay(1); }
|
||||||
//now reply the client with the same data
|
SDL_RenderPresent(renderer);
|
||||||
if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1) {
|
ticks = SDL_GetTicks();
|
||||||
die("sendto()");
|
|
||||||
}
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sock);
|
|
||||||
|
|
||||||
return 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
close(sockfd);
|
||||||
}
|
}
|
||||||
|
270
timer.c
270
timer.c
@ -1,270 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <ft2build.h>
|
|
||||||
#include FT_FREETYPE_H
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#include <libserialport.h>
|
|
||||||
|
|
||||||
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, "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) {
|
|
||||||
float x = (float)(advance + glyph->horiBearingX) / 64.f;
|
|
||||||
float y = (window_height / 2) + (face_height >> 6) / 3 + (float)(-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 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);
|
|
||||||
|
|
||||||
renderer = SDL_CreateRenderer(window, NULL);
|
|
||||||
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();
|
|
||||||
const int min_length = 4;
|
|
||||||
int max_length = min_length;
|
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint8_t read_buf[32] = {'7', '1', '.', '0', '1', '\r'};
|
|
||||||
|
|
||||||
struct state state = {0};
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (1) {
|
|
||||||
i++;
|
|
||||||
//enum sp_return sp_ret = sp_nonblocking_read(port, read_buf, (sizeof (read_buf)));
|
|
||||||
enum sp_return sp_ret = 6;
|
|
||||||
if (sp_ret < 0) {
|
|
||||||
printf("sp_nonblocking_read error\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
handle_state(read_buf, sp_ret, &state);
|
|
||||||
|
|
||||||
char str_buf[16];
|
|
||||||
switch (state.counter_state) {
|
|
||||||
case COUNTER_STOPPED:
|
|
||||||
snprintf(str_buf, (sizeof (str_buf)) - 1, "%d.%02d", state.time.whole, state.time.fraction);
|
|
||||||
break;
|
|
||||||
case COUNTER_RUNNING:
|
|
||||||
{
|
|
||||||
uint64_t counter = SDL_GetPerformanceCounter();
|
|
||||||
uint64_t frequency = SDL_GetPerformanceFrequency();
|
|
||||||
double duration = (((double)counter) - ((double)state.counter)) / ((double)frequency);
|
|
||||||
|
|
||||||
snprintf(str_buf, (sizeof (str_buf)) - 1, "%.02f", duration);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
str_buf[0] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int length = strlen(str_buf);
|
|
||||||
|
|
||||||
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 * 8 / 10));
|
|
||||||
int font_size = (window_width * 95 / 100) / divisor;
|
|
||||||
printf("fs %d %d %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();
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
16
timer.h
16
timer.h
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
enum timer_status {
|
enum timer_status {
|
||||||
TIMER_STOPPED,
|
TIMER_STOPPED,
|
||||||
@ -15,12 +16,21 @@ struct stopwatch_time {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct running_counter {
|
struct running_counter {
|
||||||
struct timespec start; // CLOCK_MONOTONIC (relative)
|
struct timespec start; // (absolute)
|
||||||
struct timespec offset; // CLOCK_MONOTONIC (relative)
|
struct timespec offset; // (relative)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct timer_state {
|
struct timer_state {
|
||||||
enum timer_status status;
|
|
||||||
struct running_counter counter;
|
struct running_counter counter;
|
||||||
struct stopwatch_time time;
|
struct stopwatch_time time;
|
||||||
|
enum timer_status status;
|
||||||
|
int _pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert((sizeof (struct running_counter)) == (8 * 2) * 2);
|
||||||
|
static_assert((sizeof (struct stopwatch_time)) == 4 * 4);
|
||||||
|
static_assert((sizeof (enum timer_status)) == 4);
|
||||||
|
static_assert((sizeof (struct timer_state)) == ((8 * 2) * 2) + (4 * 4) + 4 + 4);
|
||||||
|
static_assert((offsetof (struct timer_state, counter)) == 0);
|
||||||
|
static_assert((offsetof (struct timer_state, time )) == (8 * 2) * 2);
|
||||||
|
static_assert((offsetof (struct timer_state, status )) == ((8 * 2) * 2) + (4 * 4));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user