mirror of
https://git.sr.ht/~calebccff/pbsplash
synced 2026-01-12 20:48:40 -09:00
Compare commits
41 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
080fcf60ca | ||
|
|
52cb0ae649 | ||
|
|
706888caf4 | ||
|
|
3c96de11dd | ||
|
|
4a98f0a0cf | ||
|
|
7b714e433e | ||
|
|
d7e9eca325 | ||
|
|
22f07eb624 | ||
|
|
83ab7e731d | ||
|
|
9d9633e1f3 | ||
|
|
cc37eea120 | ||
|
|
85fc1c0a41 | ||
|
|
1ab85c2a33 | ||
|
|
435f8a7977 | ||
|
|
e2366e9d4f | ||
|
|
a1df67dfce | ||
|
|
fc5f065269 | ||
|
|
36b0e6780b | ||
|
|
2d79d8ad6a | ||
|
|
817f988022 | ||
|
|
f94f3269da | ||
|
|
67c9a10db4 | ||
|
|
99ce784c28 | ||
|
|
883ce77d1e | ||
|
|
9c05d2c7f3 | ||
|
|
3c25e1ba44 | ||
|
|
5780f60199 | ||
|
|
86b7c8f1c3 | ||
|
|
4410f12762 | ||
|
|
a5af6e1a50 | ||
|
|
0e626c1812 | ||
|
|
b6625648c9 | ||
|
|
e5f4c50b18 | ||
|
|
6d899d9971 | ||
|
|
ec1bfbd2fd | ||
|
|
5f6f3726bb | ||
|
|
31a5ad48e4 | ||
|
|
4c90c076eb | ||
|
|
2e07eceb15 | ||
|
|
768aefef93 | ||
|
|
8f09ee1628 |
13 changed files with 1756 additions and 237 deletions
10
.clang-format
Normal file
10
.clang-format
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
UseTab: Always
|
||||
BreakBeforeBraces: Linux
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: true
|
||||
AcrossComments: false
|
||||
IndentCaseLabels: false
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,3 +1,7 @@
|
|||
builddir/
|
||||
build/
|
||||
|
||||
*.json
|
||||
*.jsonc
|
||||
*.bak
|
||||
art/
|
||||
.vscode*
|
||||
|
|
|
|||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -10,6 +10,8 @@
|
|||
"vector": "c",
|
||||
"string_view": "c",
|
||||
"initializer_list": "c",
|
||||
"valarray": "c"
|
||||
"valarray": "c",
|
||||
"random": "c",
|
||||
"nanosvgrast.h": "c"
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,8 @@ extern "C" {
|
|||
nsvgDelete(image);
|
||||
*/
|
||||
|
||||
#define NSVG_MAX_UNICODE_LEN 32
|
||||
|
||||
enum NSVGpaintType {
|
||||
NSVG_PAINT_NONE = 0,
|
||||
NSVG_PAINT_COLOR = 1,
|
||||
|
|
@ -151,7 +153,7 @@ typedef struct NSVGshape
|
|||
char fillRule; // Fill rule, see NSVGfillRule.
|
||||
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
||||
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
||||
char *unicode; // Unicode character code.
|
||||
char unicode[NSVG_MAX_UNICODE_LEN]; // Unicode character code.
|
||||
int horizAdvX; // Horizontal distance to advance after rendering glyph.
|
||||
NSVGpath* paths; // Linked list of paths in the image.
|
||||
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
||||
|
|
@ -180,6 +182,8 @@ NSVGpath* nsvgDuplicatePath(NSVGpath* p);
|
|||
// Deletes an image.
|
||||
void nsvgDelete(NSVGimage* image);
|
||||
|
||||
NSVGshape** nsvgGetTextShapes(const NSVGimage* image, const char* text, int textLen);
|
||||
|
||||
#ifndef NANOSVG_CPLUSPLUS
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
@ -457,8 +461,8 @@ typedef struct NSVGparser
|
|||
float dpi;
|
||||
char pathFlag;
|
||||
char defsFlag;
|
||||
char *unicodeFlag;
|
||||
char *horizAdvFlag;
|
||||
char unicodeFlag[NSVG_MAX_UNICODE_LEN];
|
||||
const char *horizAdvFlag;
|
||||
} NSVGparser;
|
||||
|
||||
static void nsvg__xformIdentity(float* t)
|
||||
|
|
@ -976,8 +980,8 @@ static void nsvg__addShape(NSVGparser* p)
|
|||
shape->fillRule = attr->fillRule;
|
||||
shape->opacity = attr->opacity;
|
||||
|
||||
if (p->unicodeFlag) {
|
||||
shape->unicode = p->unicodeFlag;
|
||||
if (p->unicodeFlag[0]) {
|
||||
strcat(shape->unicode, p->unicodeFlag);
|
||||
if (p->horizAdvFlag) {
|
||||
shape->horizAdvX = strtol(p->horizAdvFlag, &end, 10);
|
||||
if (end == p->horizAdvFlag)
|
||||
|
|
@ -986,7 +990,7 @@ static void nsvg__addShape(NSVGparser* p)
|
|||
if (shape->horizAdvX == 0) {
|
||||
shape->horizAdvX = p->image->defaultHorizAdv;
|
||||
}
|
||||
p->unicodeFlag = NULL;
|
||||
p->unicodeFlag[0] = '\0';
|
||||
p->horizAdvFlag = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -2254,8 +2258,8 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
for (i = 0; attr[i]; i += 2) {
|
||||
if (strcmp(attr[i], "d") == 0) {
|
||||
s = attr[i + 1];
|
||||
} else if (strcmp(attr[i], "unicode") == 0) {
|
||||
p->unicodeFlag = malloc(strlen(attr[i+1]));
|
||||
} else if (strcmp(attr[i], "unicode") == 0
|
||||
&& strlen(attr[i+1]) < NSVG_MAX_UNICODE_LEN) {
|
||||
strcpy(p->unicodeFlag, attr[i+1]);
|
||||
} else if (strcmp(attr[i], "horiz-adv-x") == 0) {
|
||||
p->horizAdvFlag = attr[i+1];
|
||||
|
|
@ -2959,20 +2963,23 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
|||
}
|
||||
}
|
||||
|
||||
NSVGshape** nsvgGetTextShapes(NSVGimage* image, char* text, int textLen)
|
||||
NSVGshape** nsvgGetTextShapes(const NSVGimage* image, const char* text, int textLen)
|
||||
{
|
||||
NSVGshape *shape = NULL;
|
||||
NSVGpath *path = NULL;
|
||||
NSVGshape **ret = malloc(sizeof(NSVGshape*)*textLen); // array of paths, text to render
|
||||
NSVGshape **ret = calloc(textLen, sizeof(shape)); // array of paths, text to render
|
||||
int i;
|
||||
|
||||
// make list of paths representing glyphs to render
|
||||
for (i = 0; i < textLen; i++)
|
||||
{
|
||||
if (text[i] == ' ' || text[i] == '\n') {
|
||||
ret[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
for (shape = image->shapes; shape != NULL; shape = shape->next) {
|
||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
||||
continue;
|
||||
if (shape->unicode && shape->unicode[0] == text[i]) {
|
||||
if (shape->unicode[0] == text[i]) {
|
||||
ret[i] = shape;
|
||||
goto found;
|
||||
}
|
||||
|
|
@ -3033,7 +3040,7 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
|||
error:
|
||||
if (fp) fclose(fp);
|
||||
if (data) free(data);
|
||||
if (image) nsvgDelete(image);
|
||||
nsvgDelete(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,12 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||
|
||||
|
||||
void nsvgRasterizeText(NSVGrasterizer* r,
|
||||
const NSVGimage* font, float tx, float ty, float scale,
|
||||
unsigned char* dst, int w, int h, int stride,
|
||||
const char* text);
|
||||
|
||||
|
||||
#ifndef NANOSVGRAST_CPLUSPLUS
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
@ -1363,15 +1369,22 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
|
|||
*/
|
||||
|
||||
void nsvgRasterizeText(NSVGrasterizer* r,
|
||||
NSVGimage* font, float tx, float ty, float scale,
|
||||
const NSVGimage* font, float tx, float ty, float scale,
|
||||
unsigned char* dst, int w, int h, int stride,
|
||||
char* text)
|
||||
const char* text)
|
||||
{
|
||||
NSVGshape *shape = NULL;
|
||||
NSVGedge *e = NULL;
|
||||
NSVGcachedPaint cache;
|
||||
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
|
||||
int i = 0, j = 0, textLen = strlen(text);
|
||||
int fontHeight = (font->fontAscent - font->fontDescent) * scale;
|
||||
int xStart = tx;
|
||||
int charWidth = font->defaultHorizAdv * scale;
|
||||
|
||||
// Hack because for some reason this has Y increase UP and we
|
||||
// need to go DOWN every line
|
||||
ty = ty + h - fontHeight;
|
||||
|
||||
r->bitmap = dst;
|
||||
r->width = w;
|
||||
|
|
@ -1386,21 +1399,28 @@ void nsvgRasterizeText(NSVGrasterizer* r,
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < h; i++)
|
||||
for (i = 0; i < h; i++) {
|
||||
memset(&dst[i*stride], 0, w*4);
|
||||
}
|
||||
|
||||
for (i = 0; i < textLen; i++) {
|
||||
if (text[i] == '\n') {
|
||||
ty -= fontHeight;
|
||||
// No clue why this is needed
|
||||
tx = xStart - charWidth;
|
||||
continue;
|
||||
}
|
||||
shape = shapes[i];
|
||||
if (!shape) {
|
||||
if (text[i] == ' ')
|
||||
tx += font->defaultHorizAdv * scale;
|
||||
tx += charWidth / 2.f;
|
||||
continue;
|
||||
}
|
||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (i == 0 && strcmp(shape->id, "OpenSansRegular") == 0)
|
||||
tx -= shape->horizAdvX * scale;
|
||||
tx = xStart - charWidth;
|
||||
|
||||
if (shape->fill.type != NSVG_PAINT_NONE) {
|
||||
nsvg__resetPool(r);
|
||||
|
|
|
|||
17
include/pbsplash.h
Normal file
17
include/pbsplash.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef __pbsplash_h__
|
||||
#define __pbsplash_h__
|
||||
|
||||
#define MM_TO_PX(dpi, mm) (dpi / 25.4) * (mm)
|
||||
|
||||
struct col {
|
||||
union {
|
||||
unsigned int rgba;
|
||||
struct {
|
||||
unsigned char r, g, b, a;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void animate_frame(int frame, int w, int y_off, long dpi);
|
||||
|
||||
#endif
|
||||
73
include/timespec.h
Normal file
73
include/timespec.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Functions for working with timespec structures
|
||||
* Written by Daniel Collins (2017-2021)
|
||||
* timespec_mod by Alex Forencich (2019)
|
||||
* Various contributions by Ingo Albrecht (2021)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
|
||||
#ifndef DAN_TIMESPEC_H
|
||||
#define DAN_TIMESPEC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct timespec timespec_add(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_mod(struct timespec ts1, struct timespec ts2);
|
||||
|
||||
struct timespec timespec_min(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_max(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_clamp(struct timespec ts1, struct timespec min, struct timespec max);
|
||||
|
||||
int timespec_cmp(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_eq(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_gt(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_ge(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_lt(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_le(struct timespec ts1, struct timespec ts2);
|
||||
|
||||
struct timespec timespec_from_double(double s);
|
||||
double timespec_to_double(struct timespec ts);
|
||||
struct timespec timespec_from_timeval(struct timeval tv);
|
||||
struct timeval timespec_to_timeval(struct timespec ts);
|
||||
struct timespec timespec_from_ms(long milliseconds);
|
||||
long timespec_to_ms(struct timespec ts);
|
||||
|
||||
struct timespec timespec_normalise(struct timespec ts);
|
||||
|
||||
struct timespec timespec_now();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !DAN_TIMESPEC_H */
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
project('pbsplash', 'c')
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
deps = [
|
||||
dependency('tfblib'),
|
||||
cc.find_library('m', required : false)
|
||||
]
|
||||
|
||||
inc = [
|
||||
|
|
|
|||
37
src/animate.c
Normal file
37
src/animate.c
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "pbsplash.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <tfblib/tfb_colors.h>
|
||||
#include <tfblib/tfblib.h>
|
||||
|
||||
struct col color = {.r = 255, .g = 255, .b = 255, .a = 255};
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define n_circles 3
|
||||
#define speed 2.5
|
||||
|
||||
static void circles_wave(int frame, int w, int y_off, long dpi)
|
||||
{
|
||||
unsigned int t_col = tfb_make_color(color.r, color.g, color.b);
|
||||
int f = round(frame * speed);
|
||||
|
||||
int rad = MM_TO_PX(dpi, 1);
|
||||
int dist = rad * 3.5;
|
||||
int amplitude = rad * 1;
|
||||
|
||||
int left = ((float)w / 2) - (dist * (n_circles - 1) / 2.0);
|
||||
for (unsigned int i = 0; i < n_circles; i++) {
|
||||
int x = left + (i * dist);
|
||||
double offset = sin(f / 60.0 * PI + i);
|
||||
int y = y_off + offset * amplitude;
|
||||
tfb_fill_rect(x - rad - 3, y_off - amplitude - rad - 3,
|
||||
rad * 2 + 6, amplitude * 2 + rad * 2 + 6,
|
||||
tfb_black);
|
||||
tfb_fill_circle(x, y, rad, t_col);
|
||||
}
|
||||
}
|
||||
|
||||
void animate_frame(int frame, int w, int y_off, long dpi)
|
||||
{
|
||||
circles_wave(frame, w, y_off, dpi);
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
src = [
|
||||
'animate.c',
|
||||
'nanosvg.c',
|
||||
'timespec.c',
|
||||
'pbsplash.c',
|
||||
]
|
||||
|
||||
|
|
|
|||
9
src/nanosvg.c
Normal file
9
src/nanosvg.c
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* Build the nanosvg implementation here, to build it only once and have other
|
||||
* C files build fast during development. */
|
||||
#include <stdio.h>
|
||||
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg.h"
|
||||
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvgrast.h"
|
||||
756
src/pbsplash.c
756
src/pbsplash.c
|
|
@ -1,7 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/kd.h>
|
||||
#include <fcntl.h>
|
||||
#include <tfblib/tfblib.h>
|
||||
#include <tfblib/tfb_colors.h>
|
||||
|
|
@ -9,275 +14,618 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
|
||||
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
||||
#include "nanosvg.h"
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvgrast.h"
|
||||
#include "timespec.h"
|
||||
#include "pbsplash.h"
|
||||
|
||||
#define MSG_MAX_LEN 4096
|
||||
#define FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg"
|
||||
#define DEFAULT_FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg"
|
||||
#define LOGO_SIZE_MAX_MM 45
|
||||
#define FONT_SIZE_PT 9
|
||||
#define FONT_SIZE_B_PT 6
|
||||
#define B_MESSAGE_OFFSET_MM 3
|
||||
#define PT_TO_MM 0.38f
|
||||
#define TTY_PATH_LEN 11
|
||||
|
||||
int usage()
|
||||
#define DEBUGRENDER 0
|
||||
|
||||
volatile sig_atomic_t terminate = 0;
|
||||
|
||||
bool debug = false;
|
||||
struct col background_color = { .r = 0, .g = 0, .b = 0, .a = 255 };
|
||||
|
||||
static int screenWidth, screenHeight;
|
||||
|
||||
#define zalloc(size) calloc(1, size)
|
||||
|
||||
#define LOG(fmt, ...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
printf(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static int usage()
|
||||
{
|
||||
fprintf(stderr, "pbsplash: a simple fbsplash tool\n");
|
||||
fprintf(stderr, "--------------------------------\n");
|
||||
fprintf(stderr, "pbsplash [-h] [-s splash image] [-m message]\n\n");
|
||||
// clang-format off
|
||||
fprintf(stderr, "pbsplash: postmarketOS bootsplash generator\n");
|
||||
fprintf(stderr, "-------------------------------------------\n");
|
||||
fprintf(stderr, "pbsplash [-v] [-h] [-f font] [-s splash image] [-m message]\n");
|
||||
fprintf(stderr, " [-b message bottom] [-o font size bottom]\n");
|
||||
fprintf(stderr, " [-p font size] [-q max logo size] [-d] [-e]\n\n");
|
||||
fprintf(stderr, " -v enable verbose logging\n");
|
||||
fprintf(stderr, " -h show this help\n");
|
||||
fprintf(stderr, " -f path to SVG font file (default: %s)\n", DEFAULT_FONT_PATH);
|
||||
fprintf(stderr, " -s path to splash image to display\n");
|
||||
fprintf(stderr, " -m message to show under the splash image\n");
|
||||
fprintf(stderr, " -b message to show at the bottom\n");
|
||||
fprintf(stderr, " -o font size bottom in pt (default: %d)\n", FONT_SIZE_B_PT);
|
||||
fprintf(stderr, " -p font size in pt (default: %d)\n", FONT_SIZE_PT);
|
||||
fprintf(stderr, " -q max logo size in mm (default: %d)\n", LOGO_SIZE_MAX_MM);
|
||||
fprintf(stderr, " -d custom DPI (for testing)\n");
|
||||
fprintf(stderr, " -e error (no loading animation)\n");
|
||||
// clang-format on
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
|
||||
static void term(int signum)
|
||||
{
|
||||
float pqx, pqy, dx, dy, d, t;
|
||||
pqx = qx-px;
|
||||
pqy = qy-py;
|
||||
dx = x-px;
|
||||
dy = y-py;
|
||||
d = pqx*pqx + pqy*pqy;
|
||||
t = pqx*dx + pqy*dy;
|
||||
if (d > 0) t /= d;
|
||||
if (t < 0) t = 0;
|
||||
else if (t > 1) t = 1;
|
||||
dx = px + t*pqx - x;
|
||||
dy = py + t*pqy - y;
|
||||
return dx*dx + dy*dy;
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
static void vertex2f(float x, float y, bool reset)
|
||||
static void blit_buf(unsigned char *buf, int x, int y, int w, int h, bool vflip,
|
||||
bool redraw)
|
||||
{
|
||||
static float x1 = 0.0f;
|
||||
static float y1 = 0.0f;
|
||||
static bool first = true;
|
||||
struct col prev_col = { .r = 0, .g = 0, .b = 0, .a = 0 };
|
||||
unsigned int col = tfb_make_color(
|
||||
background_color.r, background_color.g, background_color.b);
|
||||
|
||||
if (reset) {
|
||||
x1 = 0.0f;
|
||||
x1 = 0.0f;
|
||||
first = true;
|
||||
for (size_t i = 0; i < w; i++) {
|
||||
for (size_t j = 0; j < h; j++) {
|
||||
#if DEBUGRENDER == 1
|
||||
if (i == 0 || i == w - 1 || j == 0 || j == h - 1) {
|
||||
tfb_draw_pixel(x + i, y + h - j, tfb_red);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
struct col rgba =
|
||||
*(struct col *)(buf + (j * w + i) * 4);
|
||||
if (rgba.a == 0 || rgba.rgba == background_color.rgba)
|
||||
continue;
|
||||
|
||||
// Alpha blending
|
||||
if (rgba.a != 255) {
|
||||
rgba.r =
|
||||
(rgba.r * rgba.a +
|
||||
background_color.r * (255 - rgba.a)) >>
|
||||
8;
|
||||
rgba.g =
|
||||
(rgba.g * rgba.a +
|
||||
background_color.g * (255 - rgba.a)) >>
|
||||
8;
|
||||
rgba.b =
|
||||
(rgba.b * rgba.a +
|
||||
background_color.b * (255 - rgba.a)) >>
|
||||
8;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
tfb_draw_line(x1, y1, x, y, tfb_green);
|
||||
// No need to generate the colour again if it's the same as the previous one
|
||||
if (rgba.rgba != prev_col.rgba) {
|
||||
prev_col.rgba = rgba.rgba;
|
||||
col = tfb_make_color(rgba.r, rgba.g, rgba.b);
|
||||
}
|
||||
if (vflip)
|
||||
tfb_draw_pixel(x + i, y + h - j, col);
|
||||
else
|
||||
first = false;
|
||||
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
}
|
||||
|
||||
static void cubicBez(float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4,
|
||||
float tol, int level)
|
||||
{
|
||||
float x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234;
|
||||
float d;
|
||||
|
||||
if (level > 25) {
|
||||
fprintf(stdout, "error: max subdivision level reached\n");
|
||||
return;
|
||||
}
|
||||
|
||||
x12 = (x1 + x2) * 0.5f;
|
||||
y12 = (y1 + y2) * 0.5f;
|
||||
x23 = (x2 + x3) * 0.5f;
|
||||
y23 = (y2 + y3) * 0.5f;
|
||||
x34 = (x3 + x4) * 0.5f;
|
||||
y34 = (y3 + y4) * 0.5f;
|
||||
x123 = (x12 + x23) * 0.5f;
|
||||
y123 = (y12 + y23) * 0.5f;
|
||||
x234 = (x23 + x34) * 0.5f;
|
||||
y234 = (y23 + y34) * 0.5f;
|
||||
x1234 = (x123 + x234) * 0.5f;
|
||||
y1234 = (y123 + y234) * 0.5f;
|
||||
|
||||
d = distPtSeg(x1234, y1234, x1, y1, x4, y4);
|
||||
if (d > tol * tol)
|
||||
{
|
||||
cubicBez(x1, y1, x12, y12, x123, y123, x1234, y1234, tol, level + 1);
|
||||
cubicBez(x1234, y1234, x234, y234, x34, y34, x4, y4, tol, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex2f(x4, y4, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawPath(float *pts, int npts, int xoff, int yoff, float scale_x, float scale_y, char closed, float tol)
|
||||
{
|
||||
int i;
|
||||
vertex2f(xoff + pts[0] * scale_x, yoff - pts[1] * scale_y, true);
|
||||
for (i = 0; i < npts-1; i += 3) {
|
||||
float* p = &pts[i*2];
|
||||
// fprintf(stdout, "ptx0: %f, pty0: %f, ptx1: %f, pty1: %f, ptx2: %f, pty2: %f, ptx3: %f, pty3: %f\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
||||
cubicBez(xoff + p[0] * scale_x, yoff - p[1] * scale_y, xoff + p[2] * scale_x, yoff - p[3] * scale_y, xoff + p[4] * scale_x, yoff - p[5] * scale_y, xoff + p[6] * scale_x, yoff - p[7] * scale_y, tol, 0);
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
vertex2f(xoff + pts[0] * scale_x, yoff - pts[1] * scale_y, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Blit an SVG to the framebuffer using tfblib. The image is
|
||||
// scaled based on the target width and height.
|
||||
static void draw_svg(NSVGimage *image, int x, int y, int width, int height)
|
||||
{
|
||||
//fprintf(stdout, "size: %f x %f\n", image->width, image->height);
|
||||
float sz = (float)width / image->width;
|
||||
int w = image->width * sz + 0.5;
|
||||
int h = image->height * sz + 0.5;
|
||||
//fprintf(stdout, "scale: %f\n", sz);
|
||||
|
||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||
unsigned char *img = malloc(w * h * 4);
|
||||
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 4);
|
||||
|
||||
for (size_t i = 0; i < w; i++)
|
||||
{
|
||||
for (size_t j = 0; j < h; j++)
|
||||
{
|
||||
unsigned int col = tfb_make_color(img[(j * w + i) * 4 + 0], img[(j * w + i) * 4 + 1], img[(j * w + i) * 4 + 2]);
|
||||
tfb_draw_pixel(x + i, y + j, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_svg(NSVGimage *image, int x, int y, int w, int h)
|
||||
{
|
||||
float sz = (int)((float)w / (float)image->width * 100.f) / 100.f;
|
||||
LOG("draw_svg: (%d, %d), %dx%d, %f\n", x, y, w, h, sz);
|
||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||
unsigned char *img = zalloc(w * h * 4);
|
||||
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 4);
|
||||
|
||||
blit_buf(img, x, y, w, h, false, false);
|
||||
|
||||
free(img);
|
||||
nsvgDeleteRasterizer(rast);
|
||||
}
|
||||
|
||||
static void getTextDimensions(NSVGimage *font, char *text, float scale, int *width, int *height)
|
||||
static void draw_text(const NSVGimage *font, const char *text, int x, int y, int width,
|
||||
int height, float scale, unsigned int tfb_col)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
*width = 0;
|
||||
*height = (font->fontAscent - font->fontDescent) * scale;
|
||||
LOG("text '%s': fontsz=%f, x=%d, y=%d, dimensions: %d x %d\n", text,
|
||||
scale, x, y, width, height);
|
||||
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
|
||||
for (size_t i = 0; i < strlen(text); i++)
|
||||
{
|
||||
NSVGshape *shape = shapes[i];
|
||||
if (shape) {
|
||||
*width += shapes[i]->horizAdvX * scale + 1;
|
||||
} else {
|
||||
*width += font->defaultHorizAdv * scale;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_text(NSVGimage *font, char *text, int x, int y, int width, int height, float scale)
|
||||
{
|
||||
//printf("text: %s, scale: %f, x: %d, y: %d\n", text, scale, x, y);
|
||||
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
|
||||
unsigned char *img = malloc(width * height * 4);
|
||||
unsigned char *img = zalloc(width * height * 4);
|
||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||
|
||||
//printf("text dimensions: %d x %d\n", width, height);
|
||||
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height,
|
||||
width * 4, text);
|
||||
|
||||
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height, width * 4, text);
|
||||
|
||||
for (size_t i = 0; i < width; i++)
|
||||
{
|
||||
for (size_t j = 0; j < height; j++)
|
||||
{
|
||||
unsigned int col = tfb_make_color(img[(j * width + i) * 4 + 0], img[(j * width + i) * 4 + 1], img[(j * width + i) * 4 + 2]);
|
||||
tfb_draw_pixel(x + i, y + height - j, col);
|
||||
}
|
||||
}
|
||||
blit_buf(img, x, y, width, height, true, false);
|
||||
|
||||
free(img);
|
||||
free(shapes);
|
||||
nsvgDeleteRasterizer(rast);
|
||||
}
|
||||
|
||||
static inline float getShapeWidth(const NSVGimage *font, const NSVGshape *shape)
|
||||
{
|
||||
if (shape) {
|
||||
return shape->horizAdvX;
|
||||
} else {
|
||||
return font->defaultHorizAdv;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the dimensions of a string in pixels.
|
||||
* based on the font size and the font SVG file.
|
||||
*/
|
||||
static const char *getTextDimensions(const NSVGimage *font, const char *text, float scale,
|
||||
int *width, int *height)
|
||||
{
|
||||
int i, j;
|
||||
int fontHeight = (font->fontAscent - font->fontDescent) * scale;
|
||||
int maxWidth = 0;
|
||||
|
||||
if (text == NULL)
|
||||
return text;
|
||||
|
||||
// Pre-allocate 3x the size to account for any word splitting
|
||||
char *out_text = zalloc(strlen(text) * 3 + 1);
|
||||
|
||||
*width = 2; // font->defaultHorizAdv * scale;
|
||||
// The height is simply the height of the font * the scale factor
|
||||
*height = fontHeight;
|
||||
|
||||
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
|
||||
bool line_has_space = false;
|
||||
// Iterate over every glyph in the string to get the total width
|
||||
// and handle line-splitting
|
||||
for (i = 0, j = 0; text[i] != '\0'; i++, j++) {
|
||||
NSVGshape *shape = shapes[i];
|
||||
out_text[j] = text[i];
|
||||
if (*width > screenWidth * 0.95) {
|
||||
if (!line_has_space)
|
||||
i--;
|
||||
if (!line_has_space) {
|
||||
if (i < 1) {
|
||||
fprintf(stderr,
|
||||
"ERROR: Text is too long to fit on screen!");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
int old_j = j;
|
||||
while (out_text[j] != ' ' && j > 0) {
|
||||
j--;
|
||||
}
|
||||
i = i - (old_j - j);
|
||||
if (i <= 0) {
|
||||
line_has_space = false;
|
||||
fprintf(stderr,
|
||||
"ERROR: Text is too long to fit on screen!");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out_text[j] = '\n';
|
||||
}
|
||||
|
||||
if (out_text[j] == '\n') {
|
||||
printf("LINE SPLIT, %d %s\n", i, out_text);
|
||||
line_has_space = false;
|
||||
*height += fontHeight;
|
||||
maxWidth = *width > maxWidth ? *width : maxWidth;
|
||||
*width = 0;
|
||||
continue;
|
||||
} else if (text[i] == ' ') {
|
||||
printf("SPACE! %s\n", out_text);
|
||||
line_has_space = true;
|
||||
}
|
||||
|
||||
*width += round(getShapeWidth(font, shape) * scale);
|
||||
}
|
||||
|
||||
*width = *width > maxWidth ? *width : maxWidth;
|
||||
|
||||
out:
|
||||
free(shapes);
|
||||
return out_text;
|
||||
}
|
||||
|
||||
struct dpi_info {
|
||||
long dpi;
|
||||
int pixels_per_milli;
|
||||
float logo_size_px;
|
||||
int logo_size_max_mm;
|
||||
};
|
||||
|
||||
static void calculate_dpi_info(struct dpi_info *dpi_info)
|
||||
{
|
||||
int w_mm = tfb_screen_width_mm();
|
||||
int h_mm = tfb_screen_height_mm();
|
||||
|
||||
if ((w_mm < 1 || h_mm < 1) && !dpi_info->dpi) {
|
||||
fprintf(stderr, "ERROR!!!: Invalid screen size: %dx%d\n", w_mm, h_mm);
|
||||
|
||||
// Assume a dpi of 300
|
||||
// This should be readable everywhere
|
||||
// Except ridiculous HiDPI displays
|
||||
// which honestly should expose their physical
|
||||
// dimensions....
|
||||
dpi_info->dpi = 300;
|
||||
}
|
||||
|
||||
// If DPI is specified on cmdline then calculate display size from it
|
||||
// otherwise calculate the dpi based on the display size.
|
||||
if (dpi_info->dpi > 0) {
|
||||
w_mm = screenWidth / (float)dpi_info->dpi * 25.4;
|
||||
h_mm = screenHeight / (float)dpi_info->dpi * 25.4;
|
||||
} else {
|
||||
dpi_info->dpi = (float)screenWidth / (float)w_mm * 25.4;
|
||||
}
|
||||
dpi_info->pixels_per_milli = (float)screenWidth / (float)w_mm;
|
||||
|
||||
if (dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli > screenWidth)
|
||||
dpi_info->logo_size_max_mm = (screenWidth * 0.75f) / dpi_info->pixels_per_milli;
|
||||
|
||||
dpi_info->logo_size_px =
|
||||
(float)(screenWidth < screenHeight ? screenWidth :
|
||||
screenHeight) *
|
||||
0.75f;
|
||||
if (w_mm > 0 && h_mm > 0) {
|
||||
if (w_mm < h_mm) {
|
||||
if (w_mm > dpi_info->logo_size_max_mm * 1.2f)
|
||||
dpi_info->logo_size_px = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
||||
} else {
|
||||
if (h_mm > dpi_info->logo_size_max_mm * 1.2f)
|
||||
dpi_info->logo_size_px = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%dx%d @ %dx%dmm, dpi=%ld, logo_size_px=%f\n", screenWidth,
|
||||
screenHeight, w_mm, h_mm, dpi_info->dpi, dpi_info->logo_size_px);
|
||||
}
|
||||
|
||||
struct msg_info {
|
||||
const char *src_message;
|
||||
const char *message;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
float fontsz;
|
||||
};
|
||||
|
||||
static void load_message(struct msg_info *msg_info, const struct dpi_info *dpi_info, float font_size_pt, const NSVGimage *font)
|
||||
{
|
||||
msg_info->fontsz = (font_size_pt * PT_TO_MM) /
|
||||
(font->fontAscent - font->fontDescent) *
|
||||
dpi_info->pixels_per_milli;
|
||||
msg_info->message = getTextDimensions(font, msg_info->src_message, msg_info->fontsz, &msg_info->width, &msg_info->height);
|
||||
msg_info->x = (screenWidth - msg_info->width) / 2;
|
||||
// Y coordinate is set later
|
||||
}
|
||||
|
||||
static void free_message(struct msg_info *msg_info)
|
||||
{
|
||||
if (!msg_info)
|
||||
return;
|
||||
if (msg_info->message && msg_info->message != msg_info->src_message)
|
||||
free((void *)msg_info->message);
|
||||
}
|
||||
|
||||
struct messages {
|
||||
const char *font_path;
|
||||
NSVGimage *font;
|
||||
int font_size_pt;
|
||||
int font_size_b_pt;
|
||||
struct msg_info *msg;
|
||||
struct msg_info *bottom_msg;
|
||||
};
|
||||
|
||||
static inline void show_message(const struct msg_info *msg_info, const NSVGimage *font)
|
||||
{
|
||||
draw_text(font, msg_info->message, msg_info->x, msg_info->y, msg_info->width,
|
||||
msg_info->height, msg_info->fontsz, tfb_gray);
|
||||
}
|
||||
|
||||
static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info)
|
||||
{
|
||||
static bool font_failed = false;
|
||||
if (font_failed)
|
||||
return;
|
||||
|
||||
if (!msgs->font)
|
||||
msgs->font = nsvgParseFromFile(msgs->font_path, "px", 512);
|
||||
if (!msgs->font || !msgs->font->shapes) {
|
||||
font_failed = true;
|
||||
fprintf(stderr, "failed to load SVG font, can't render messages\n");
|
||||
fprintf(stderr, " font_path: %s\n", msgs->font_path);
|
||||
fprintf(stderr, "msg: %s\n\nbottom_message: %s\n", msgs->msg->src_message, msgs->bottom_msg->src_message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgs->bottom_msg) {
|
||||
if (!msgs->bottom_msg->message) {
|
||||
load_message(msgs->bottom_msg, dpi_info, msgs->font_size_b_pt, msgs->font);
|
||||
msgs->bottom_msg->y = screenHeight - msgs->bottom_msg->height - MM_TO_PX(dpi_info->dpi, B_MESSAGE_OFFSET_MM);
|
||||
}
|
||||
show_message(msgs->bottom_msg, msgs->font);
|
||||
}
|
||||
|
||||
if (msgs->msg) {
|
||||
if (!msgs->msg->message) {
|
||||
load_message(msgs->msg, dpi_info, msgs->font_size_pt, msgs->font);
|
||||
if (msgs->bottom_msg)
|
||||
msgs->msg->y = msgs->bottom_msg->y - msgs->msg->height - (MM_TO_PX(dpi_info->dpi, msgs->font_size_b_pt * PT_TO_MM) * 0.6);
|
||||
else
|
||||
msgs->msg->y = screenHeight - msgs->msg->height - (MM_TO_PX(dpi_info->dpi, msgs->font_size_pt * PT_TO_MM) * 2);
|
||||
}
|
||||
show_message(msgs->msg, msgs->font);
|
||||
}
|
||||
}
|
||||
|
||||
struct image_info {
|
||||
const char *path;
|
||||
NSVGimage *image;
|
||||
float width;
|
||||
float height;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
static int load_image(const struct dpi_info *dpi_info, struct image_info *image_info)
|
||||
{
|
||||
int logo_size_px = dpi_info->logo_size_px;
|
||||
|
||||
image_info->image = nsvgParseFromFile(image_info->path, "", logo_size_px);
|
||||
if (!image_info->image) {
|
||||
fprintf(stderr, "failed to load SVG image\n");
|
||||
fprintf(stderr, " image path: %s\n", image_info->path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// For taller images make sure they don't get too wide
|
||||
if (image_info->image->width < image_info->image->height * 1.1)
|
||||
logo_size_px = MM_TO_PX(dpi_info->dpi, 25);
|
||||
|
||||
float sz =
|
||||
(float)logo_size_px /
|
||||
(image_info->image->width > image_info->image->height ? image_info->image->height : image_info->image->width);
|
||||
image_info->width = image_info->image->width * sz + 0.5;
|
||||
image_info->height = image_info->image->height * sz + 0.5;
|
||||
if (image_info->width > (dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli)) {
|
||||
float scalefactor =
|
||||
((float)(dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli) / image_info->width);
|
||||
//printf("Got scale factor: %f\n", scalefactor);
|
||||
image_info->width = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
||||
image_info->height *= scalefactor;
|
||||
}
|
||||
image_info->x = (float)screenWidth / 2 - image_info->width * 0.5f;
|
||||
image_info->y = (float)screenHeight / 2 - image_info->height * 0.5f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc = 0;
|
||||
int opt;
|
||||
char *message;
|
||||
char *logoText = "postmarketOS";
|
||||
char *splash_image;
|
||||
NSVGimage *image;
|
||||
NSVGimage *font;
|
||||
int textWidth, textHeight;
|
||||
char *message = NULL;
|
||||
char *message_bottom = NULL;
|
||||
char active_tty[TTY_PATH_LEN + 1];
|
||||
struct sigaction action;
|
||||
struct messages msgs = {
|
||||
.font_path = DEFAULT_FONT_PATH,
|
||||
.font_size_pt = FONT_SIZE_PT,
|
||||
.font_size_b_pt = FONT_SIZE_B_PT,
|
||||
.msg = NULL,
|
||||
.bottom_msg = NULL,
|
||||
};
|
||||
struct dpi_info dpi_info = {
|
||||
.dpi = 0,
|
||||
.pixels_per_milli = 0,
|
||||
.logo_size_px = 0,
|
||||
.logo_size_max_mm = LOGO_SIZE_MAX_MM,
|
||||
};
|
||||
struct image_info image_info = {
|
||||
.path = NULL,
|
||||
.image = NULL,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
};
|
||||
int optflag;
|
||||
bool animation = true;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-h") == 0)
|
||||
{
|
||||
rc = usage();
|
||||
return rc;
|
||||
memset(active_tty, '\0', TTY_PATH_LEN);
|
||||
strcat(active_tty, "/dev/");
|
||||
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = term;
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
|
||||
while ((optflag = getopt(argc, argv, "hvf:s:m:b:o:p:q:d:e")) != -1) {
|
||||
char *end = NULL;
|
||||
switch (optflag) {
|
||||
case 'h':
|
||||
return usage();
|
||||
case 'v':
|
||||
debug = true;
|
||||
break;
|
||||
case 'f':
|
||||
msgs.font_path = optarg;
|
||||
break;
|
||||
case 's':
|
||||
image_info.path = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
message = malloc(strlen(optarg) + 1);
|
||||
strcpy(message, optarg);
|
||||
break;
|
||||
case 'b':
|
||||
message_bottom = malloc(strlen(optarg) + 1);
|
||||
strcpy(message_bottom, optarg);
|
||||
break;
|
||||
case 'o':
|
||||
msgs.font_size_b_pt = strtof(optarg, &end);
|
||||
if (end == optarg) {
|
||||
fprintf(stderr, "Invalid font size: %s\n",
|
||||
optarg);
|
||||
return usage();
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-s") == 0 && i + 1 < argc)
|
||||
{
|
||||
splash_image = argv[i + 1];
|
||||
//printf("splash_image path: %s\n", splash_image);
|
||||
continue;
|
||||
break;
|
||||
case 'p':
|
||||
msgs.font_size_pt = strtof(optarg, &end);
|
||||
if (end == optarg) {
|
||||
fprintf(stderr, "Invalid font size: %s\n",
|
||||
optarg);
|
||||
return usage();
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-m") == 0)
|
||||
{
|
||||
message = argv[i + 1];
|
||||
printf("message: %s\n", message);
|
||||
continue;
|
||||
break;
|
||||
case 'q':
|
||||
dpi_info.logo_size_max_mm = strtof(optarg, &end);
|
||||
if (end == optarg) {
|
||||
fprintf(stderr, "Invalid max logo size: %s\n",
|
||||
optarg);
|
||||
return usage();
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
dpi_info.dpi = strtol(optarg, &end, 10);
|
||||
if (end == optarg || dpi_info.dpi < 0) {
|
||||
fprintf(stderr, "Invalid dpi: %s\n", optarg);
|
||||
return usage();
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
animation = false;
|
||||
break;
|
||||
default:
|
||||
return usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((rc = tfb_acquire_fb(0, "/dev/fb0", "/dev/tty1")) != TFB_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
|
||||
FILE *fp = fopen("/sys/devices/virtual/tty/tty0/active", "r");
|
||||
int len = strlen(active_tty);
|
||||
char *ptr = active_tty + len;
|
||||
if (fp != NULL) {
|
||||
fgets(ptr, TTY_PATH_LEN - len, fp);
|
||||
*(ptr + strlen(ptr) - 1) = '\0';
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
LOG("active tty: '%s'\n", active_tty);
|
||||
|
||||
if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0",
|
||||
active_tty)) != TFB_SUCCESS) {
|
||||
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n",
|
||||
rc);
|
||||
rc = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int w = (int)tfb_screen_width();
|
||||
int h = (int)tfb_screen_height();
|
||||
float sz = (float)(w < h ? w : h) * 0.5f;
|
||||
//fprintf(stdout, "w=%du, h=%du\n", w, h);
|
||||
float x = (float)w / 2 - sz / 2;
|
||||
float y = (float)h / 2 - sz / 2 - 300;
|
||||
//fprintf(stdout, "x=%f, y=%f, sz=%f\n", x, y, sz);
|
||||
screenWidth = (int)tfb_screen_width();
|
||||
screenHeight = (int)tfb_screen_height();
|
||||
|
||||
image = nsvgParseFromFile(splash_image, "px", 96);
|
||||
if (!image)
|
||||
{
|
||||
fprintf(stderr, "failed to load SVG image\n");
|
||||
rc = 1;
|
||||
return rc;
|
||||
}
|
||||
font = nsvgParseFromFile(FONT_PATH, "px", 500);
|
||||
if (!font || !font->shapes)
|
||||
{
|
||||
fprintf(stderr, "failed to load SVG font\n");
|
||||
rc = 1;
|
||||
goto free_image;
|
||||
}
|
||||
calculate_dpi_info(&dpi_info);
|
||||
|
||||
/* Paint the whole screen in black */
|
||||
tfb_clear_screen(tfb_black);
|
||||
rc = load_image(&dpi_info, &image_info);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
draw_svg(image, x, y, sz, sz);
|
||||
float animation_y = image_info.y + image_info.height + MM_TO_PX(dpi_info.dpi, 5);
|
||||
|
||||
float fontsz = (sz * 0.25) / (font->fontAscent - font->fontDescent);
|
||||
//fprintf(stdout, "font size: %f\n", fontsz);
|
||||
tfb_clear_screen(tfb_make_color(background_color.r, background_color.g,
|
||||
background_color.b));
|
||||
|
||||
getTextDimensions(font, logoText, fontsz, &textWidth, &textHeight);
|
||||
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width, image_info.height);
|
||||
|
||||
draw_text(font, logoText, w / 2.f - textWidth / 2.f, y + sz + sz*0.2, textWidth, textHeight, fontsz);
|
||||
if (!message && !message_bottom)
|
||||
goto no_messages;
|
||||
|
||||
fontsz = (sz * 0.1) / (font->fontAscent - font->fontDescent);
|
||||
struct msg_info bottom_msg, msg;
|
||||
|
||||
getTextDimensions(font, message, fontsz, &textWidth, &textHeight);
|
||||
memset(&bottom_msg, 0, sizeof(bottom_msg));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
draw_text(font, message, w / 2.f - textWidth / 2.f, y + sz * 2, textWidth, textHeight, fontsz);
|
||||
printf("Rendered text: %s\n", message);
|
||||
bottom_msg.src_message = message_bottom;
|
||||
msg.src_message = message;
|
||||
|
||||
if (message_bottom)
|
||||
msgs.bottom_msg = &bottom_msg;
|
||||
if (message)
|
||||
msgs.msg = &msg;
|
||||
|
||||
show_messages(&msgs, &dpi_info);
|
||||
|
||||
no_messages:
|
||||
tfb_flush_window();
|
||||
tfb_flush_fb();
|
||||
|
||||
//sleep(20);
|
||||
int tick = 0;
|
||||
int tty = open(active_tty, O_RDWR);
|
||||
if (!tty) {
|
||||
fprintf(stderr, "Failed to open tty %s (%d)\n", active_tty, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct timespec epoch, start, end, diff;
|
||||
int target_fps = 60;
|
||||
float tickrate = 60.0;
|
||||
clock_gettime(CLOCK_REALTIME, &epoch);
|
||||
while (!terminate) {
|
||||
if (!animation) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
clock_gettime(CLOCK_REALTIME, &start);
|
||||
tick = timespec_to_double(timespec_sub(start, epoch)) * tickrate;
|
||||
animate_frame(tick, screenWidth, animation_y, dpi_info.dpi);
|
||||
tfb_flush_fb();
|
||||
clock_gettime(CLOCK_REALTIME, &end);
|
||||
diff = timespec_sub(end, start);
|
||||
//printf("%05d: %09ld\n", tick, diff.tv_nsec);
|
||||
if (diff.tv_nsec < 1000000000 / target_fps) {
|
||||
struct timespec sleep_time = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 1000000000 / target_fps - diff.tv_nsec,
|
||||
};
|
||||
nanosleep(&sleep_time, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
nsvgDelete(font);
|
||||
free_image:
|
||||
nsvgDelete(image);
|
||||
// Before we exit print the logo so it will persist
|
||||
if (image_info.image) {
|
||||
ioctl(tty, KDSETMODE, KD_TEXT);
|
||||
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width, image_info.height);
|
||||
}
|
||||
|
||||
// Draw the messages again so they will persist
|
||||
show_messages(&msgs, &dpi_info);
|
||||
|
||||
nsvgDelete(image_info.image);
|
||||
nsvgDelete(msgs.font);
|
||||
free_message(msgs.msg);
|
||||
free_message(msgs.bottom_msg);
|
||||
if (message)
|
||||
free(message);
|
||||
if (message_bottom)
|
||||
free(message_bottom);
|
||||
// The TTY might end up in a weird state if this
|
||||
// is not called!
|
||||
tfb_release_fb();
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
987
src/timespec.c
Normal file
987
src/timespec.c
Normal file
|
|
@ -0,0 +1,987 @@
|
|||
/* Functions for working with timespec structures
|
||||
* Written by Daniel Collins (2017-2021)
|
||||
* timespec_mod by Alex Forencich (2019)
|
||||
* Various contributions by Ingo Albrecht (2021)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
|
||||
/** \file timespec.c
|
||||
* \brief Functions for working with timespec structures.
|
||||
*
|
||||
* This library aims to provide a comprehensive set of functions with
|
||||
* well-defined behaviour that handle all edge cases (e.g. negative values) in
|
||||
* a sensible manner.
|
||||
*
|
||||
* Negative values are allowed in the tv_sec and/or tv_usec field of timespec
|
||||
* structures, tv_usec is always relative to tv_sec, so mixing positive and
|
||||
* negative values will produce consistent results:
|
||||
*
|
||||
* <PRE>
|
||||
* { tv_sec = 1, tv_nsec = 500000000 } == 1.5 seconds
|
||||
* { tv_sec = 1, tv_nsec = 0 } == 1.0 seconds
|
||||
* { tv_sec = 1, tv_nsec = -500000000 } == 0.5 seconds
|
||||
* { tv_sec = 0, tv_nsec = 500000000 } == 0.5 seconds
|
||||
* { tv_sec = 0, tv_nsec = 0 } == 0.0 seconds
|
||||
* { tv_sec = 0, tv_nsec = -500000000 } == -0.5 seconds
|
||||
* { tv_sec = -1, tv_nsec = 500000000 } == -0.5 seconds
|
||||
* { tv_sec = -1, tv_nsec = 0 } == -1.0 seconds
|
||||
* { tv_sec = -1, tv_nsec = -500000000 } == -1.5 seconds
|
||||
* </PRE>
|
||||
*
|
||||
* Furthermore, any timespec structure processed or returned by library functions
|
||||
* is normalised according to the rules in timespec_normalise().
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "timespec.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
/** \fn struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the result of adding two timespec structures.
|
||||
*/
|
||||
struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
ts1.tv_sec += ts2.tv_sec;
|
||||
ts1.tv_nsec += ts2.tv_nsec;
|
||||
|
||||
return timespec_normalise(ts1);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the result of subtracting ts2 from ts1.
|
||||
*/
|
||||
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
ts1.tv_sec -= ts2.tv_sec;
|
||||
ts1.tv_nsec -= ts2.tv_nsec;
|
||||
|
||||
return timespec_normalise(ts1);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the remainder left over after dividing ts1 by ts2 (ts1%ts2).
|
||||
*/
|
||||
struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
int i = 0;
|
||||
bool neg1 = false;
|
||||
bool neg2 = false;
|
||||
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
/* If ts2 is zero, just return ts1
|
||||
*/
|
||||
if (ts2.tv_sec == 0 && ts2.tv_nsec == 0)
|
||||
{
|
||||
return ts1;
|
||||
}
|
||||
|
||||
/* If inputs are negative, flip and record sign
|
||||
*/
|
||||
if (ts1.tv_sec < 0 || ts1.tv_nsec < 0)
|
||||
{
|
||||
neg1 = true;
|
||||
ts1.tv_sec = -ts1.tv_sec;
|
||||
ts1.tv_nsec = -ts1.tv_nsec;
|
||||
}
|
||||
|
||||
if (ts2.tv_sec < 0 || ts2.tv_nsec < 0)
|
||||
{
|
||||
neg2 = true;
|
||||
ts2.tv_sec = -ts2.tv_sec;
|
||||
ts2.tv_nsec = -ts2.tv_nsec;
|
||||
}
|
||||
|
||||
/* Shift ts2 until it is larger than ts1 or is about to overflow
|
||||
*/
|
||||
while ((ts2.tv_sec < (LONG_MAX >> 1)) && timespec_ge(ts1, ts2))
|
||||
{
|
||||
i++;
|
||||
ts2.tv_nsec <<= 1;
|
||||
ts2.tv_sec <<= 1;
|
||||
if (ts2.tv_nsec > NSEC_PER_SEC)
|
||||
{
|
||||
ts2.tv_nsec -= NSEC_PER_SEC;
|
||||
ts2.tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Division by repeated subtraction
|
||||
*/
|
||||
while (i >= 0)
|
||||
{
|
||||
if (timespec_ge(ts1, ts2))
|
||||
{
|
||||
ts1 = timespec_sub(ts1, ts2);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
i--;
|
||||
if (ts2.tv_sec & 1)
|
||||
{
|
||||
ts2.tv_nsec += NSEC_PER_SEC;
|
||||
}
|
||||
ts2.tv_nsec >>= 1;
|
||||
ts2.tv_sec >>= 1;
|
||||
}
|
||||
|
||||
/* If signs differ and result is nonzero, subtract once more to cross zero
|
||||
*/
|
||||
if (neg1 ^ neg2 && (ts1.tv_sec != 0 || ts1.tv_nsec != 0))
|
||||
{
|
||||
ts1 = timespec_sub(ts1, ts2);
|
||||
}
|
||||
|
||||
/* Restore sign
|
||||
*/
|
||||
if (neg1)
|
||||
{
|
||||
ts1.tv_sec = -ts1.tv_sec;
|
||||
ts1.tv_nsec = -ts1.tv_nsec;
|
||||
}
|
||||
|
||||
return ts1;
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_min(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Return the lesser one of the two given timespec values.
|
||||
*/
|
||||
struct timespec timespec_min(struct timespec ts1, struct timespec ts2) {
|
||||
if(timespec_le(ts1, ts2)) {
|
||||
return ts1;
|
||||
} else {
|
||||
return ts2;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_max(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Return the greater one of the two given timespec values.
|
||||
*/
|
||||
struct timespec timespec_max(struct timespec ts1, struct timespec ts2) {
|
||||
if(timespec_ge(ts1, ts2)) {
|
||||
return ts1;
|
||||
} else {
|
||||
return ts2;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max)
|
||||
* \brief Clamp the value of TS between MIN and MAX.
|
||||
*/
|
||||
struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max) {
|
||||
if(timespec_gt(ts, max)) {
|
||||
return max;
|
||||
}
|
||||
if(timespec_lt(ts, min)) {
|
||||
return min;
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
/** \fn int timespec_cmp(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns (1, 0, -1) if ts1 is (greater than, equal to, less than) to ts2.
|
||||
*/
|
||||
int timespec_cmp(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
if(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if((ts1.tv_sec > ts2.tv_sec)
|
||||
|| (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn bool timespec_eq(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if the two timespec structures are equal.
|
||||
*/
|
||||
bool timespec_eq(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec);
|
||||
}
|
||||
|
||||
/** \fn bool timespec_gt(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is greater than ts2.
|
||||
*/
|
||||
bool timespec_gt(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec > ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_ge(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is greater than or equal to ts2.
|
||||
*/
|
||||
bool timespec_ge(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec > ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec >= ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_lt(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is less than ts2.
|
||||
*/
|
||||
bool timespec_lt(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec < ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec < ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_le(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is less than or equal to ts2.
|
||||
*/
|
||||
bool timespec_le(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec < ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec <= ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_double(double s)
|
||||
* \brief Converts a fractional number of seconds to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_double(double s)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = s,
|
||||
.tv_nsec = (s - (long)(s)) * NSEC_PER_SEC,
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn double timespec_to_double(struct timespec ts)
|
||||
* \brief Converts a timespec to a fractional number of seconds.
|
||||
*/
|
||||
double timespec_to_double(struct timespec ts)
|
||||
{
|
||||
return ((double)(ts.tv_sec) + ((double)(ts.tv_nsec) / NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_timeval(struct timeval tv)
|
||||
* \brief Converts a timeval to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_timeval(struct timeval tv)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = tv.tv_sec,
|
||||
.tv_nsec = tv.tv_usec * 1000
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn struct timeval timespec_to_timeval(struct timespec ts)
|
||||
* \brief Converts a timespec to a timeval.
|
||||
*/
|
||||
struct timeval timespec_to_timeval(struct timespec ts)
|
||||
{
|
||||
ts = timespec_normalise(ts);
|
||||
|
||||
struct timeval tv = {
|
||||
.tv_sec = ts.tv_sec,
|
||||
.tv_usec = ts.tv_nsec / 1000,
|
||||
};
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_ms(long milliseconds)
|
||||
* \brief Converts an integer number of milliseconds to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_ms(long milliseconds)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = (milliseconds / 1000),
|
||||
.tv_nsec = (milliseconds % 1000) * 1000000,
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn long timespec_to_ms(struct timespec ts)
|
||||
* \brief Converts a timespec to an integer number of milliseconds.
|
||||
*/
|
||||
long timespec_to_ms(struct timespec ts)
|
||||
{
|
||||
return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_normalise(struct timespec ts)
|
||||
* \brief Normalises a timespec structure.
|
||||
*
|
||||
* Returns a normalised version of a timespec structure, according to the
|
||||
* following rules:
|
||||
*
|
||||
* 1) If tv_nsec is >=1,000,000,00 or <=-1,000,000,000, flatten the surplus
|
||||
* nanoseconds into the tv_sec field.
|
||||
*
|
||||
* 2) If tv_nsec is negative, decrement tv_sec and roll tv_nsec up to represent
|
||||
* the same value attainable by ADDING nanoseconds to tv_sec.
|
||||
*/
|
||||
struct timespec timespec_normalise(struct timespec ts)
|
||||
{
|
||||
while(ts.tv_nsec >= NSEC_PER_SEC)
|
||||
{
|
||||
++(ts.tv_sec);
|
||||
ts.tv_nsec -= NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
while(ts.tv_nsec <= -NSEC_PER_SEC)
|
||||
{
|
||||
--(ts.tv_sec);
|
||||
ts.tv_nsec += NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
if(ts.tv_nsec < 0)
|
||||
{
|
||||
/* Negative nanoseconds isn't valid according to POSIX.
|
||||
* Decrement tv_sec and roll tv_nsec over.
|
||||
*/
|
||||
|
||||
--(ts.tv_sec);
|
||||
ts.tv_nsec = (NSEC_PER_SEC + ts.tv_nsec);
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
struct timespec timespec_now()
|
||||
{
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
return t;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <stdio.h>
|
||||
|
||||
#define TEST_NORMALISE(ts_sec, ts_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec in = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
struct timespec got = timespec_normalise(in); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_BINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
struct timespec got = func(ts1, ts2); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf(#func "({%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TRINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, ts3_sec, ts3_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
struct timespec ts3 = { .tv_sec = ts3_sec, .tv_nsec = ts3_nsec }; \
|
||||
struct timespec got = func(ts1, ts2, ts3); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf(#func "({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), \
|
||||
(long)(ts2_sec), (long)(ts2_nsec), \
|
||||
(long)(ts3_sec), (long)(ts3_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TEST_FUNC(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
int got = func(ts1, ts2); \
|
||||
if(got != expect) \
|
||||
{ \
|
||||
printf("%s:%d: " #func "({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", __FILE__, __LINE__, \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec), \
|
||||
got, #expect); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_DOUBLE(d_secs, expect_sec, expect_nsec) { \
|
||||
struct timespec got = timespec_from_double(d_secs); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_double(%f) returned wrong values\n", __FILE__, __LINE__, (double)(d_secs)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_DOUBLE(ts_sec, ts_nsec, expect) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
double got = timespec_to_double(ts); \
|
||||
if(got != expect) { \
|
||||
printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: %f\n", (double)(expect)); \
|
||||
printf(" Got: %f\n", got); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_TIMEVAL(in_sec, in_usec, expect_sec, expect_nsec) { \
|
||||
struct timeval tv = { .tv_sec = in_sec, .tv_usec = in_usec }; \
|
||||
struct timespec got = timespec_from_timeval(tv); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(in_sec), (long)(in_usec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_TIMEVAL(ts_sec, ts_nsec, expect_sec, expect_usec) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
struct timeval got = timespec_to_timeval(ts); \
|
||||
if(got.tv_sec != expect_sec || got.tv_usec != expect_usec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_usec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_usec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_MS(msecs, expect_sec, expect_nsec) { \
|
||||
struct timespec got = timespec_from_ms(msecs); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, __LINE__, (long)(msecs)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_MS(ts_sec, ts_nsec, expect) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
long got = timespec_to_ms(ts); \
|
||||
if(got != expect) { \
|
||||
printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: %ld\n", (long)(expect)); \
|
||||
printf(" Got: %ld\n", got); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
// timespec_add
|
||||
|
||||
TEST_BINOP(timespec_add, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_add, 0,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_add, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_add, 1,0, 1,0, 2,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,0, 2,500000000);
|
||||
TEST_BINOP(timespec_add, 1,0, 1,500000000, 2,500000000);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,500000000, 3,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,499999999, 2,999999999);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,500000000, 3,0);
|
||||
TEST_BINOP(timespec_add, 1,999999999, 1,999999999, 3,999999998);
|
||||
TEST_BINOP(timespec_add, 0,500000000, 1,500000000, 2,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 0,500000000, 2,0);
|
||||
|
||||
// timespec_sub
|
||||
|
||||
TEST_BINOP(timespec_sub, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_sub, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_sub, 1,500000000, 0,500000000, 1,0);
|
||||
TEST_BINOP(timespec_sub, 5,500000000, 2,999999999, 2,500000001);
|
||||
TEST_BINOP(timespec_sub, 0,0, 1,0, -1,0);
|
||||
TEST_BINOP(timespec_sub, 0,500000000, 1,500000000, -1,0);
|
||||
TEST_BINOP(timespec_sub, 0,0, 1,500000000, -2,500000000);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,500000000, -1,500000000);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,499999999, -1,500000001);
|
||||
|
||||
// timespec_mod
|
||||
|
||||
TEST_BINOP(timespec_mod, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 0,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 3,0, 1,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, -3,0, -2,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, 3,0, 2,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, -3,0, -1,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, -5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, 5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, -5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,500000000, 0,500000000, 0,0);
|
||||
TEST_BINOP(timespec_mod, 5,500000000, 2,999999999, 2,500000001);
|
||||
TEST_BINOP(timespec_mod, 0,500000000, 1,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_mod, 0,0, 1,500000000, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 1,500000000, 1,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,1, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,123456789, 0,1000, 0,789);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,9999999, 0,100);
|
||||
TEST_BINOP(timespec_mod, 12345,54321, 0,100001, 0,5555);
|
||||
TEST_BINOP(timespec_mod, LONG_MAX,0, 0,1, 0,0);
|
||||
TEST_BINOP(timespec_mod, LONG_MAX,0, LONG_MAX,1, LONG_MAX,0);
|
||||
|
||||
// timespec_clamp
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 0,0, 0,0, 0,0, 0,0);
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 1000,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 1500,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 1999,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 2000,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 2001,0, 2000,0, 3000,0, 2001,0);
|
||||
TEST_TRINOP(timespec_clamp, 2250,0, 2000,0, 3000,0, 2250,0);
|
||||
TEST_TRINOP(timespec_clamp, 2500,0, 2000,0, 3000,0, 2500,0);
|
||||
TEST_TRINOP(timespec_clamp, 2750,0, 2000,0, 3000,0, 2750,0);
|
||||
TEST_TRINOP(timespec_clamp, 2999,0, 2000,0, 3000,0, 2999,0);
|
||||
TEST_TRINOP(timespec_clamp, 3000,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 3001,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 3500,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 4000,0, 2000,0, 3000,0, 3000,0);
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 0,1000, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,1500, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,1999, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,2000, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,2001, 0,2000, 0,3000, 0,2001);
|
||||
TEST_TRINOP(timespec_clamp, 0,2250, 0,2000, 0,3000, 0,2250);
|
||||
TEST_TRINOP(timespec_clamp, 0,2500, 0,2000, 0,3000, 0,2500);
|
||||
TEST_TRINOP(timespec_clamp, 0,2750, 0,2000, 0,3000, 0,2750);
|
||||
TEST_TRINOP(timespec_clamp, 0,2999, 0,2000, 0,3000, 0,2999);
|
||||
TEST_TRINOP(timespec_clamp, 0,3000, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,3001, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,3500, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,4000, 0,2000, 0,3000, 0,3000);
|
||||
|
||||
TEST_TRINOP(timespec_clamp,0,-1000, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-1500, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-1999, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3000, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2001, 0,-3000, 0,-2000, 0,-2001);
|
||||
TEST_TRINOP(timespec_clamp,0,-2250, 0,-3000, 0,-2000, 0,-2250);
|
||||
TEST_TRINOP(timespec_clamp,0,-2500, 0,-3000, 0,-2000, 0,-2500);
|
||||
TEST_TRINOP(timespec_clamp,0,-2750, 0,-3000, 0,-2000, 0,-2750);
|
||||
TEST_TRINOP(timespec_clamp,0,-2999, 0,-3000, 0,-2000, 0,-2999);
|
||||
TEST_TRINOP(timespec_clamp,0,-2000, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3001, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3500, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2000, 0,-3000, 0,-2000, 0,-2000);
|
||||
|
||||
TEST_TRINOP(timespec_clamp,0,-4000, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3001, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3000, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2999, 0,-3000, 0,3000, 0,-2999);
|
||||
TEST_TRINOP(timespec_clamp,0,-1500, 0,-3000, 0,3000, 0,-1500);
|
||||
TEST_TRINOP(timespec_clamp,0, -1, 0,-3000, 0,3000, 0, -1);
|
||||
TEST_TRINOP(timespec_clamp,0, 0, 0,-3000, 0,3000, 0, 0);
|
||||
TEST_TRINOP(timespec_clamp,0, 1, 0,-3000, 0,3000, 0, 1);
|
||||
TEST_TRINOP(timespec_clamp,0, 1500, 0,-3000, 0,3000, 0, 1500);
|
||||
TEST_TRINOP(timespec_clamp,0, 2999, 0,-3000, 0,3000, 0, 2999);
|
||||
TEST_TRINOP(timespec_clamp,0, 3000, 0,-3000, 0,3000, 0, 3000);
|
||||
TEST_TRINOP(timespec_clamp,0, 3001, 0,-3000, 0,3000, 0, 3000);
|
||||
TEST_TRINOP(timespec_clamp,0, 4000, 0,-3000, 0,3000, 0, 3000);
|
||||
|
||||
// timespec_min
|
||||
|
||||
TEST_BINOP(timespec_min, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 0,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 3,0, 3,0);
|
||||
TEST_BINOP(timespec_min, 10,0, -3,0, -3,0);
|
||||
TEST_BINOP(timespec_min, -10,0, 3,0, -10,0);
|
||||
TEST_BINOP(timespec_min, -10,0, -3,0, -10,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 5,0, 5,0);
|
||||
TEST_BINOP(timespec_min, 10,0, -5,0, -5,0);
|
||||
TEST_BINOP(timespec_min, -10,0, 5,0, -10,0);
|
||||
TEST_BINOP(timespec_min, -10,0, -5,0, -10,0);
|
||||
TEST_BINOP(timespec_min, 1,500000000, 0,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_min, 5,500000000, 2,999999999, 2,999999999);
|
||||
TEST_BINOP(timespec_min, 0,500000000, 1,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_min, 0,0, 1,500000000, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 1,500000000, 1,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_min, 1,123456789, 0,1000, 0,1000);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,9999999, 0,9999999);
|
||||
TEST_BINOP(timespec_min, 12345,54321, 0,100001, 0,100001);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, 0,1, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, 0,-1, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, LONG_MAX,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, LONG_MIN,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, 0,-1, 0,-1);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, LONG_MIN,0, LONG_MIN,0);
|
||||
|
||||
// timespec_max
|
||||
|
||||
TEST_BINOP(timespec_max, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_max, 0,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 1,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 3,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, -3,0, 10,0);
|
||||
TEST_BINOP(timespec_max, -10,0, 3,0, 3,0);
|
||||
TEST_BINOP(timespec_max, -10,0, -3,0, -3,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 5,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, -5,0, 10,0);
|
||||
TEST_BINOP(timespec_max, -10,0, 5,0, 5,0);
|
||||
TEST_BINOP(timespec_max, -10,0, -5,0, -5,0);
|
||||
TEST_BINOP(timespec_max, 1,500000000, 0,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 5,500000000, 2,999999999, 5,500000000);
|
||||
TEST_BINOP(timespec_max, 0,500000000, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 0,0, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 1,0, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,1, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,123456789, 0,1000, 1,123456789);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,9999999, 1,0);
|
||||
TEST_BINOP(timespec_max, 12345,54321, 0,100001, 12345,54321);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, 0,-1, 0,-1);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, LONG_MIN,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, 0,1, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, 0,-1, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, LONG_MIN,0, LONG_MAX,0);
|
||||
|
||||
// timespec_cmp
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 0,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, 100,0, 100,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -100,0, -100,0, 0);
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, 1,0, 0,0, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 1,0, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,1, 0,0, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 0,1, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 1,0, 0,100, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,100 , 1,0, -1);
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, -0,-0, 0,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000000, -11,500000000, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000001, -11,499999999, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000001, -11,500000001, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, -11,500000001, -10,-500000001, 1);
|
||||
|
||||
// timespec_eq
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, 0,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 100,0, 100,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -200,0, -200,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,300, 0,300, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,-400, 0,-400, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, 100,1, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, 101,0, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, -100,0, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,10, 0,-10, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, -0,-0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000001, -11,500000001, false);
|
||||
|
||||
// timespec_gt
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, -1,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, 0,-1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 1,0, 1,0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, 1,1, 1,1, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -1,0, 0,0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,-1, 0,0, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, -0,-0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000000, -11,500000000, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,500000000, -10,-500000000, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000001, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,499999999, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000001, -11,500000001, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,500000001, -10,-500000001, true);
|
||||
|
||||
// timespec_ge
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, 1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, -1,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, 0,-1, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 1,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 1,1, 1,1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, -1,0, 0,0, false);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,-1, 0,0, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, -0,-0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,500000000, -10,-500000000, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,499999999, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000001, -11,500000001, false);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,500000001, -10,-500000001, true);
|
||||
|
||||
// timespec_lt
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_lt, -1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 0,1, true);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,-1, 0,0, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 1,0, 1,0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 1,1, 1,1, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, -1,0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 0,-1, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, -0,-0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000000, -11,500000000, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,500000000, -10,-500000000, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000001, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,499999999, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000001, -11,500000001, true);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,500000001, -10,-500000001, false);
|
||||
|
||||
// timespec_le
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, -1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 0,1, true);
|
||||
TEST_TEST_FUNC(timespec_le, 0,-1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 1,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 1,1, 1,1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, -1,0, false);
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 0,-1, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, -0,-0, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,500000000, -10,-500000000, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,499999999, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000001, -11,500000001, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,500000001, -10,-500000001, false);
|
||||
|
||||
// timespec_from_double
|
||||
|
||||
TEST_FROM_DOUBLE(0.0, 0,0);
|
||||
TEST_FROM_DOUBLE(10.0, 10,0);
|
||||
TEST_FROM_DOUBLE(-10.0, -10,0);
|
||||
TEST_FROM_DOUBLE(0.5, 0,500000000);
|
||||
TEST_FROM_DOUBLE(-0.5, -1,500000000);
|
||||
TEST_FROM_DOUBLE(10.5, 10,500000000);
|
||||
TEST_FROM_DOUBLE(-10.5, -11,500000000);
|
||||
|
||||
// timespec_to_double
|
||||
|
||||
TEST_TO_DOUBLE(0,0, 0.0);
|
||||
TEST_TO_DOUBLE(10,0, 10.0);
|
||||
TEST_TO_DOUBLE(-10,0, -10.0);
|
||||
TEST_TO_DOUBLE(0,500000000, 0.5);
|
||||
TEST_TO_DOUBLE(0,-500000000, -0.5);
|
||||
TEST_TO_DOUBLE(10,500000000, 10.5);
|
||||
TEST_TO_DOUBLE(10,-500000000, 9.5);
|
||||
TEST_TO_DOUBLE(-10,500000000, -9.5);
|
||||
TEST_TO_DOUBLE(-10,-500000000, -10.5);
|
||||
|
||||
// timespec_from_timeval
|
||||
|
||||
TEST_FROM_TIMEVAL(0,0, 0,0);
|
||||
TEST_FROM_TIMEVAL(1,0, 1,0);
|
||||
TEST_FROM_TIMEVAL(1000,0, 1000,0);
|
||||
TEST_FROM_TIMEVAL(0,0, 0,0);
|
||||
TEST_FROM_TIMEVAL(-1,0, -1,0);
|
||||
TEST_FROM_TIMEVAL(-1000,0, -1000,0);
|
||||
|
||||
TEST_FROM_TIMEVAL(1,1, 1,1000);
|
||||
TEST_FROM_TIMEVAL(1,1000, 1,1000000);
|
||||
TEST_FROM_TIMEVAL(1,-1, 0,999999000);
|
||||
TEST_FROM_TIMEVAL(1,-1000, 0,999000000);
|
||||
TEST_FROM_TIMEVAL(-1,-1, -2,999999000);
|
||||
TEST_FROM_TIMEVAL(-1,-1000, -2,999000000);
|
||||
|
||||
// timespec_to_timeval
|
||||
|
||||
TEST_TO_TIMEVAL(0,0, 0,0);
|
||||
TEST_TO_TIMEVAL(1,0, 1,0);
|
||||
TEST_TO_TIMEVAL(10,0, 10,0);
|
||||
TEST_TO_TIMEVAL(-1,0, -1,0);
|
||||
TEST_TO_TIMEVAL(-10,0, -10,0);
|
||||
|
||||
TEST_TO_TIMEVAL(1,1, 1,0);
|
||||
TEST_TO_TIMEVAL(1,999, 1,0);
|
||||
TEST_TO_TIMEVAL(1,1000, 1,1);
|
||||
TEST_TO_TIMEVAL(1,1001, 1,1);
|
||||
TEST_TO_TIMEVAL(1,2000, 1,2);
|
||||
TEST_TO_TIMEVAL(1,2000000, 1,2000);
|
||||
|
||||
TEST_TO_TIMEVAL(1,-1, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-999, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-1000, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-1001, 0,999998);
|
||||
TEST_TO_TIMEVAL(1,-2000, 0,999998);
|
||||
TEST_TO_TIMEVAL(1,-2000000, 0,998000);
|
||||
|
||||
TEST_TO_TIMEVAL(-1,-1, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-999, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-1000, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-1001, -2,999998);
|
||||
TEST_TO_TIMEVAL(-1,-2000, -2,999998);
|
||||
TEST_TO_TIMEVAL(-1,-2000000, -2,998000);
|
||||
|
||||
TEST_TO_TIMEVAL(1,1500000000, 2,500000);
|
||||
TEST_TO_TIMEVAL(1,-1500000000, -1,500000);
|
||||
TEST_TO_TIMEVAL(-1,-1500000000, -3,500000);
|
||||
|
||||
// timespec_from_ms
|
||||
|
||||
TEST_FROM_MS(0, 0,0);
|
||||
TEST_FROM_MS(1, 0,1000000);
|
||||
TEST_FROM_MS(-1, -1,999000000);
|
||||
TEST_FROM_MS(1500, 1,500000000);
|
||||
TEST_FROM_MS(-1000, -1,0);
|
||||
TEST_FROM_MS(-1500, -2,500000000);
|
||||
|
||||
// timespec_to_ms
|
||||
|
||||
TEST_TO_MS(0,0, 0);
|
||||
TEST_TO_MS(10,0, 10000);
|
||||
TEST_TO_MS(-10,0, -10000);
|
||||
TEST_TO_MS(0,500000000, 500);
|
||||
TEST_TO_MS(0,-500000000, -500);
|
||||
TEST_TO_MS(10,500000000, 10500);
|
||||
TEST_TO_MS(10,-500000000, 9500);
|
||||
TEST_TO_MS(-10,500000000, -9500);
|
||||
TEST_TO_MS(-10,-500000000, -10500);
|
||||
|
||||
// timespec_normalise
|
||||
|
||||
TEST_NORMALISE(0,0, 0,0);
|
||||
|
||||
TEST_NORMALISE(0,1000000000, 1,0);
|
||||
TEST_NORMALISE(0,1500000000, 1,500000000);
|
||||
TEST_NORMALISE(0,-1000000000, -1,0);
|
||||
TEST_NORMALISE(0,-1500000000, -2,500000000);
|
||||
|
||||
TEST_NORMALISE(5,1000000000, 6,0);
|
||||
TEST_NORMALISE(5,1500000000, 6,500000000);
|
||||
TEST_NORMALISE(-5,-1000000000, -6,0);
|
||||
TEST_NORMALISE(-5,-1500000000, -7,500000000);
|
||||
|
||||
TEST_NORMALISE(0,2000000000, 2,0);
|
||||
TEST_NORMALISE(0,2100000000, 2,100000000);
|
||||
TEST_NORMALISE(0,-2000000000, -2,0);
|
||||
TEST_NORMALISE(0,-2100000000, -3,900000000);
|
||||
|
||||
TEST_NORMALISE(1,-500000001, 0,499999999);
|
||||
TEST_NORMALISE(1,-500000000, 0,500000000);
|
||||
TEST_NORMALISE(1,-499999999, 0,500000001);
|
||||
TEST_NORMALISE(0,-499999999, -1,500000001);
|
||||
|
||||
TEST_NORMALISE(-1,500000000, -1,500000000);
|
||||
TEST_NORMALISE(-1,499999999, -1,499999999);
|
||||
|
||||
if(result > 0)
|
||||
{
|
||||
printf("%d tests failed\n", result);
|
||||
}
|
||||
else{
|
||||
printf("All tests passed\n");
|
||||
}
|
||||
|
||||
return !!result; /* Don't overflow the exit status */
|
||||
}
|
||||
#endif
|
||||
Loading…
Reference in a new issue