@ -1,574 +0,0 @@ | |||
#ifndef LINMATH_H | |||
#define LINMATH_H | |||
#include <math.h> | |||
#ifdef _MSC_VER | |||
#define inline __inline | |||
#endif | |||
#define LINMATH_H_DEFINE_VEC(n) \ | |||
typedef float vec##n[n]; \ | |||
static inline void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ | |||
{ \ | |||
int i; \ | |||
for(i=0; i<n; ++i) \ | |||
r[i] = a[i] + b[i]; \ | |||
} \ | |||
static inline void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \ | |||
{ \ | |||
int i; \ | |||
for(i=0; i<n; ++i) \ | |||
r[i] = a[i] - b[i]; \ | |||
} \ | |||
static inline void vec##n##_scale(vec##n r, vec##n const v, float const s) \ | |||
{ \ | |||
int i; \ | |||
for(i=0; i<n; ++i) \ | |||
r[i] = v[i] * s; \ | |||
} \ | |||
static inline float vec##n##_mul_inner(vec##n const a, vec##n const b) \ | |||
{ \ | |||
float p = 0.; \ | |||
int i; \ | |||
for(i=0; i<n; ++i) \ | |||
p += b[i]*a[i]; \ | |||
return p; \ | |||
} \ | |||
static inline float vec##n##_len(vec##n const v) \ | |||
{ \ | |||
return (float) sqrt(vec##n##_mul_inner(v,v)); \ | |||
} \ | |||
static inline void vec##n##_norm(vec##n r, vec##n const v) \ | |||
{ \ | |||
float k = 1.f / vec##n##_len(v); \ | |||
vec##n##_scale(r, v, k); \ | |||
} | |||
LINMATH_H_DEFINE_VEC(2) | |||
LINMATH_H_DEFINE_VEC(3) | |||
LINMATH_H_DEFINE_VEC(4) | |||
static inline void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b) | |||
{ | |||
r[0] = a[1]*b[2] - a[2]*b[1]; | |||
r[1] = a[2]*b[0] - a[0]*b[2]; | |||
r[2] = a[0]*b[1] - a[1]*b[0]; | |||
} | |||
static inline void vec3_reflect(vec3 r, vec3 const v, vec3 const n) | |||
{ | |||
float p = 2.f*vec3_mul_inner(v, n); | |||
int i; | |||
for(i=0;i<3;++i) | |||
r[i] = v[i] - p*n[i]; | |||
} | |||
static inline void vec4_mul_cross(vec4 r, vec4 a, vec4 b) | |||
{ | |||
r[0] = a[1]*b[2] - a[2]*b[1]; | |||
r[1] = a[2]*b[0] - a[0]*b[2]; | |||
r[2] = a[0]*b[1] - a[1]*b[0]; | |||
r[3] = 1.f; | |||
} | |||
static inline void vec4_reflect(vec4 r, vec4 v, vec4 n) | |||
{ | |||
float p = 2.f*vec4_mul_inner(v, n); | |||
int i; | |||
for(i=0;i<4;++i) | |||
r[i] = v[i] - p*n[i]; | |||
} | |||
typedef vec4 mat4x4[4]; | |||
static inline void mat4x4_identity(mat4x4 M) | |||
{ | |||
int i, j; | |||
for(i=0; i<4; ++i) | |||
for(j=0; j<4; ++j) | |||
M[i][j] = i==j ? 1.f : 0.f; | |||
} | |||
static inline void mat4x4_dup(mat4x4 M, mat4x4 N) | |||
{ | |||
int i, j; | |||
for(i=0; i<4; ++i) | |||
for(j=0; j<4; ++j) | |||
M[i][j] = N[i][j]; | |||
} | |||
static inline void mat4x4_row(vec4 r, mat4x4 M, int i) | |||
{ | |||
int k; | |||
for(k=0; k<4; ++k) | |||
r[k] = M[k][i]; | |||
} | |||
static inline void mat4x4_col(vec4 r, mat4x4 M, int i) | |||
{ | |||
int k; | |||
for(k=0; k<4; ++k) | |||
r[k] = M[i][k]; | |||
} | |||
static inline void mat4x4_transpose(mat4x4 M, mat4x4 N) | |||
{ | |||
int i, j; | |||
for(j=0; j<4; ++j) | |||
for(i=0; i<4; ++i) | |||
M[i][j] = N[j][i]; | |||
} | |||
static inline void mat4x4_add(mat4x4 M, mat4x4 a, mat4x4 b) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
vec4_add(M[i], a[i], b[i]); | |||
} | |||
static inline void mat4x4_sub(mat4x4 M, mat4x4 a, mat4x4 b) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
vec4_sub(M[i], a[i], b[i]); | |||
} | |||
static inline void mat4x4_scale(mat4x4 M, mat4x4 a, float k) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
vec4_scale(M[i], a[i], k); | |||
} | |||
static inline void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, float x, float y, float z) | |||
{ | |||
int i; | |||
vec4_scale(M[0], a[0], x); | |||
vec4_scale(M[1], a[1], y); | |||
vec4_scale(M[2], a[2], z); | |||
for(i = 0; i < 4; ++i) { | |||
M[3][i] = a[3][i]; | |||
} | |||
} | |||
static inline void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b) | |||
{ | |||
mat4x4 temp; | |||
int k, r, c; | |||
for(c=0; c<4; ++c) for(r=0; r<4; ++r) { | |||
temp[c][r] = 0.f; | |||
for(k=0; k<4; ++k) | |||
temp[c][r] += a[k][r] * b[c][k]; | |||
} | |||
mat4x4_dup(M, temp); | |||
} | |||
static inline void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v) | |||
{ | |||
int i, j; | |||
for(j=0; j<4; ++j) { | |||
r[j] = 0.f; | |||
for(i=0; i<4; ++i) | |||
r[j] += M[i][j] * v[i]; | |||
} | |||
} | |||
static inline void mat4x4_translate(mat4x4 T, float x, float y, float z) | |||
{ | |||
mat4x4_identity(T); | |||
T[3][0] = x; | |||
T[3][1] = y; | |||
T[3][2] = z; | |||
} | |||
static inline void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z) | |||
{ | |||
vec4 t = {x, y, z, 0}; | |||
vec4 r; | |||
int i; | |||
for (i = 0; i < 4; ++i) { | |||
mat4x4_row(r, M, i); | |||
M[3][i] += vec4_mul_inner(r, t); | |||
} | |||
} | |||
static inline void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 a, vec3 b) | |||
{ | |||
int i, j; | |||
for(i=0; i<4; ++i) for(j=0; j<4; ++j) | |||
M[i][j] = i<3 && j<3 ? a[i] * b[j] : 0.f; | |||
} | |||
static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, float angle) | |||
{ | |||
float s = sinf(angle); | |||
float c = cosf(angle); | |||
vec3 u = {x, y, z}; | |||
if(vec3_len(u) > 1e-4) { | |||
mat4x4 T, C, S = {{0}}; | |||
vec3_norm(u, u); | |||
mat4x4_from_vec3_mul_outer(T, u, u); | |||
S[1][2] = u[0]; | |||
S[2][1] = -u[0]; | |||
S[2][0] = u[1]; | |||
S[0][2] = -u[1]; | |||
S[0][1] = u[2]; | |||
S[1][0] = -u[2]; | |||
mat4x4_scale(S, S, s); | |||
mat4x4_identity(C); | |||
mat4x4_sub(C, C, T); | |||
mat4x4_scale(C, C, c); | |||
mat4x4_add(T, T, C); | |||
mat4x4_add(T, T, S); | |||
T[3][3] = 1.; | |||
mat4x4_mul(R, M, T); | |||
} else { | |||
mat4x4_dup(R, M); | |||
} | |||
} | |||
static inline void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, float angle) | |||
{ | |||
float s = sinf(angle); | |||
float c = cosf(angle); | |||
mat4x4 R = { | |||
{1.f, 0.f, 0.f, 0.f}, | |||
{0.f, c, s, 0.f}, | |||
{0.f, -s, c, 0.f}, | |||
{0.f, 0.f, 0.f, 1.f} | |||
}; | |||
mat4x4_mul(Q, M, R); | |||
} | |||
static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle) | |||
{ | |||
float s = sinf(angle); | |||
float c = cosf(angle); | |||
mat4x4 R = { | |||
{ c, 0.f, s, 0.f}, | |||
{ 0.f, 1.f, 0.f, 0.f}, | |||
{ -s, 0.f, c, 0.f}, | |||
{ 0.f, 0.f, 0.f, 1.f} | |||
}; | |||
mat4x4_mul(Q, M, R); | |||
} | |||
static inline void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, float angle) | |||
{ | |||
float s = sinf(angle); | |||
float c = cosf(angle); | |||
mat4x4 R = { | |||
{ c, s, 0.f, 0.f}, | |||
{ -s, c, 0.f, 0.f}, | |||
{ 0.f, 0.f, 1.f, 0.f}, | |||
{ 0.f, 0.f, 0.f, 1.f} | |||
}; | |||
mat4x4_mul(Q, M, R); | |||
} | |||
static inline void mat4x4_invert(mat4x4 T, mat4x4 M) | |||
{ | |||
float idet; | |||
float s[6]; | |||
float c[6]; | |||
s[0] = M[0][0]*M[1][1] - M[1][0]*M[0][1]; | |||
s[1] = M[0][0]*M[1][2] - M[1][0]*M[0][2]; | |||
s[2] = M[0][0]*M[1][3] - M[1][0]*M[0][3]; | |||
s[3] = M[0][1]*M[1][2] - M[1][1]*M[0][2]; | |||
s[4] = M[0][1]*M[1][3] - M[1][1]*M[0][3]; | |||
s[5] = M[0][2]*M[1][3] - M[1][2]*M[0][3]; | |||
c[0] = M[2][0]*M[3][1] - M[3][0]*M[2][1]; | |||
c[1] = M[2][0]*M[3][2] - M[3][0]*M[2][2]; | |||
c[2] = M[2][0]*M[3][3] - M[3][0]*M[2][3]; | |||
c[3] = M[2][1]*M[3][2] - M[3][1]*M[2][2]; | |||
c[4] = M[2][1]*M[3][3] - M[3][1]*M[2][3]; | |||
c[5] = M[2][2]*M[3][3] - M[3][2]*M[2][3]; | |||
/* Assumes it is invertible */ | |||
idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] ); | |||
T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet; | |||
T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet; | |||
T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet; | |||
T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet; | |||
T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet; | |||
T[1][1] = ( M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet; | |||
T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet; | |||
T[1][3] = ( M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet; | |||
T[2][0] = ( M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet; | |||
T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet; | |||
T[2][2] = ( M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet; | |||
T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet; | |||
T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet; | |||
T[3][1] = ( M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet; | |||
T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet; | |||
T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet; | |||
} | |||
static inline void mat4x4_orthonormalize(mat4x4 R, mat4x4 M) | |||
{ | |||
float s = 1.; | |||
vec3 h; | |||
mat4x4_dup(R, M); | |||
vec3_norm(R[2], R[2]); | |||
s = vec3_mul_inner(R[1], R[2]); | |||
vec3_scale(h, R[2], s); | |||
vec3_sub(R[1], R[1], h); | |||
vec3_norm(R[2], R[2]); | |||
s = vec3_mul_inner(R[1], R[2]); | |||
vec3_scale(h, R[2], s); | |||
vec3_sub(R[1], R[1], h); | |||
vec3_norm(R[1], R[1]); | |||
s = vec3_mul_inner(R[0], R[1]); | |||
vec3_scale(h, R[1], s); | |||
vec3_sub(R[0], R[0], h); | |||
vec3_norm(R[0], R[0]); | |||
} | |||
static inline void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f) | |||
{ | |||
M[0][0] = 2.f*n/(r-l); | |||
M[0][1] = M[0][2] = M[0][3] = 0.f; | |||
M[1][1] = 2.f*n/(t-b); | |||
M[1][0] = M[1][2] = M[1][3] = 0.f; | |||
M[2][0] = (r+l)/(r-l); | |||
M[2][1] = (t+b)/(t-b); | |||
M[2][2] = -(f+n)/(f-n); | |||
M[2][3] = -1.f; | |||
M[3][2] = -2.f*(f*n)/(f-n); | |||
M[3][0] = M[3][1] = M[3][3] = 0.f; | |||
} | |||
static inline void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f) | |||
{ | |||
M[0][0] = 2.f/(r-l); | |||
M[0][1] = M[0][2] = M[0][3] = 0.f; | |||
M[1][1] = 2.f/(t-b); | |||
M[1][0] = M[1][2] = M[1][3] = 0.f; | |||
M[2][2] = -2.f/(f-n); | |||
M[2][0] = M[2][1] = M[2][3] = 0.f; | |||
M[3][0] = -(r+l)/(r-l); | |||
M[3][1] = -(t+b)/(t-b); | |||
M[3][2] = -(f+n)/(f-n); | |||
M[3][3] = 1.f; | |||
} | |||
static inline void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f) | |||
{ | |||
/* NOTE: Degrees are an unhandy unit to work with. | |||
* linmath.h uses radians for everything! */ | |||
float const a = 1.f / (float) tan(y_fov / 2.f); | |||
m[0][0] = a / aspect; | |||
m[0][1] = 0.f; | |||
m[0][2] = 0.f; | |||
m[0][3] = 0.f; | |||
m[1][0] = 0.f; | |||
m[1][1] = a; | |||
m[1][2] = 0.f; | |||
m[1][3] = 0.f; | |||
m[2][0] = 0.f; | |||
m[2][1] = 0.f; | |||
m[2][2] = -((f + n) / (f - n)); | |||
m[2][3] = -1.f; | |||
m[3][0] = 0.f; | |||
m[3][1] = 0.f; | |||
m[3][2] = -((2.f * f * n) / (f - n)); | |||
m[3][3] = 0.f; | |||
} | |||
static inline void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) | |||
{ | |||
/* Adapted from Android's OpenGL Matrix.java. */ | |||
/* See the OpenGL GLUT documentation for gluLookAt for a description */ | |||
/* of the algorithm. We implement it in a straightforward way: */ | |||
/* TODO: The negation of of can be spared by swapping the order of | |||
* operands in the following cross products in the right way. */ | |||
vec3 f; | |||
vec3 s; | |||
vec3 t; | |||
vec3_sub(f, center, eye); | |||
vec3_norm(f, f); | |||
vec3_mul_cross(s, f, up); | |||
vec3_norm(s, s); | |||
vec3_mul_cross(t, s, f); | |||
m[0][0] = s[0]; | |||
m[0][1] = t[0]; | |||
m[0][2] = -f[0]; | |||
m[0][3] = 0.f; | |||
m[1][0] = s[1]; | |||
m[1][1] = t[1]; | |||
m[1][2] = -f[1]; | |||
m[1][3] = 0.f; | |||
m[2][0] = s[2]; | |||
m[2][1] = t[2]; | |||
m[2][2] = -f[2]; | |||
m[2][3] = 0.f; | |||
m[3][0] = 0.f; | |||
m[3][1] = 0.f; | |||
m[3][2] = 0.f; | |||
m[3][3] = 1.f; | |||
mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]); | |||
} | |||
typedef float quat[4]; | |||
static inline void quat_identity(quat q) | |||
{ | |||
q[0] = q[1] = q[2] = 0.f; | |||
q[3] = 1.f; | |||
} | |||
static inline void quat_add(quat r, quat a, quat b) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
r[i] = a[i] + b[i]; | |||
} | |||
static inline void quat_sub(quat r, quat a, quat b) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
r[i] = a[i] - b[i]; | |||
} | |||
static inline void quat_mul(quat r, quat p, quat q) | |||
{ | |||
vec3 w; | |||
vec3_mul_cross(r, p, q); | |||
vec3_scale(w, p, q[3]); | |||
vec3_add(r, r, w); | |||
vec3_scale(w, q, p[3]); | |||
vec3_add(r, r, w); | |||
r[3] = p[3]*q[3] - vec3_mul_inner(p, q); | |||
} | |||
static inline void quat_scale(quat r, quat v, float s) | |||
{ | |||
int i; | |||
for(i=0; i<4; ++i) | |||
r[i] = v[i] * s; | |||
} | |||
static inline float quat_inner_product(quat a, quat b) | |||
{ | |||
float p = 0.f; | |||
int i; | |||
for(i=0; i<4; ++i) | |||
p += b[i]*a[i]; | |||
return p; | |||
} | |||
static inline void quat_conj(quat r, quat q) | |||
{ | |||
int i; | |||
for(i=0; i<3; ++i) | |||
r[i] = -q[i]; | |||
r[3] = q[3]; | |||
} | |||
static inline void quat_rotate(quat r, float angle, vec3 axis) { | |||
int i; | |||
vec3 v; | |||
vec3_scale(v, axis, sinf(angle / 2)); | |||
for(i=0; i<3; ++i) | |||
r[i] = v[i]; | |||
r[3] = cosf(angle / 2); | |||
} | |||
#define quat_norm vec4_norm | |||
static inline void quat_mul_vec3(vec3 r, quat q, vec3 v) | |||
{ | |||
/* | |||
* Method by Fabian 'ryg' Giessen (of Farbrausch) | |||
t = 2 * cross(q.xyz, v) | |||
v' = v + q.w * t + cross(q.xyz, t) | |||
*/ | |||
vec3 t = {q[0], q[1], q[2]}; | |||
vec3 u = {q[0], q[1], q[2]}; | |||
vec3_mul_cross(t, t, v); | |||
vec3_scale(t, t, 2); | |||
vec3_mul_cross(u, u, t); | |||
vec3_scale(t, t, q[3]); | |||
vec3_add(r, v, t); | |||
vec3_add(r, r, u); | |||
} | |||
static inline void mat4x4_from_quat(mat4x4 M, quat q) | |||
{ | |||
float a = q[3]; | |||
float b = q[0]; | |||
float c = q[1]; | |||
float d = q[2]; | |||
float a2 = a*a; | |||
float b2 = b*b; | |||
float c2 = c*c; | |||
float d2 = d*d; | |||
M[0][0] = a2 + b2 - c2 - d2; | |||
M[0][1] = 2.f*(b*c + a*d); | |||
M[0][2] = 2.f*(b*d - a*c); | |||
M[0][3] = 0.f; | |||
M[1][0] = 2*(b*c - a*d); | |||
M[1][1] = a2 - b2 + c2 - d2; | |||
M[1][2] = 2.f*(c*d + a*b); | |||
M[1][3] = 0.f; | |||
M[2][0] = 2.f*(b*d + a*c); | |||
M[2][1] = 2.f*(c*d - a*b); | |||
M[2][2] = a2 - b2 - c2 + d2; | |||
M[2][3] = 0.f; | |||
M[3][0] = M[3][1] = M[3][2] = 0.f; | |||
M[3][3] = 1.f; | |||
} | |||
static inline void mat4x4o_mul_quat(mat4x4 R, mat4x4 M, quat q) | |||
{ | |||
/* XXX: The way this is written only works for othogonal matrices. */ | |||
/* TODO: Take care of non-orthogonal case. */ | |||
quat_mul_vec3(R[0], q, M[0]); | |||
quat_mul_vec3(R[1], q, M[1]); | |||
quat_mul_vec3(R[2], q, M[2]); | |||
R[3][0] = R[3][1] = R[3][2] = 0.f; | |||
R[3][3] = 1.f; | |||
} | |||
static inline void quat_from_mat4x4(quat q, mat4x4 M) | |||
{ | |||
float r=0.f; | |||
int i; | |||
int perm[] = { 0, 1, 2, 0, 1 }; | |||
int *p = perm; | |||
for(i = 0; i<3; i++) { | |||
float m = M[i][i]; | |||
if( m < r ) | |||
continue; | |||
m = r; | |||
p = &perm[i]; | |||
} | |||
r = (float) sqrt(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] ); | |||
if(r < 1e-6) { | |||
q[0] = 1.f; | |||
q[1] = q[2] = q[3] = 0.f; | |||
return; | |||
} | |||
q[0] = r/2.f; | |||
q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.f*r); | |||
q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.f*r); | |||
q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.f*r); | |||
} | |||
#endif |
@ -1,381 +0,0 @@ | |||
/* | |||
* Nuklear - v1.32.0 - public domain | |||
* no warrenty implied; use at your own risk. | |||
* authored from 2015-2017 by Micha Mettke | |||
*/ | |||
/* | |||
* ============================================================== | |||
* | |||
* API | |||
* | |||
* =============================================================== | |||
*/ | |||
#ifndef NK_GLFW_GL2_H_ | |||
#define NK_GLFW_GL2_H_ | |||
#include <GLFW/glfw3.h> | |||
enum nk_glfw_init_state{ | |||
NK_GLFW3_DEFAULT = 0, | |||
NK_GLFW3_INSTALL_CALLBACKS | |||
}; | |||
NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); | |||
NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); | |||
NK_API void nk_glfw3_font_stash_end(void); | |||
NK_API void nk_glfw3_new_frame(void); | |||
NK_API void nk_glfw3_render(enum nk_anti_aliasing); | |||
NK_API void nk_glfw3_shutdown(void); | |||
NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); | |||
NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); | |||
#endif | |||
/* | |||
* ============================================================== | |||
* | |||
* IMPLEMENTATION | |||
* | |||
* =============================================================== | |||
*/ | |||
#ifdef NK_GLFW_GL2_IMPLEMENTATION | |||
#ifndef NK_GLFW_TEXT_MAX | |||
#define NK_GLFW_TEXT_MAX 256 | |||
#endif | |||
#ifndef NK_GLFW_DOUBLE_CLICK_LO | |||
#define NK_GLFW_DOUBLE_CLICK_LO 0.02 | |||
#endif | |||
#ifndef NK_GLFW_DOUBLE_CLICK_HI | |||
#define NK_GLFW_DOUBLE_CLICK_HI 0.2 | |||
#endif | |||
struct nk_glfw_device { | |||
struct nk_buffer cmds; | |||
struct nk_draw_null_texture null; | |||
GLuint font_tex; | |||
}; | |||
struct nk_glfw_vertex { | |||
float position[2]; | |||
float uv[2]; | |||
nk_byte col[4]; | |||
}; | |||
static struct nk_glfw { | |||
GLFWwindow *win; | |||
int width, height; | |||
int display_width, display_height; | |||
struct nk_glfw_device ogl; | |||
struct nk_context ctx; | |||
struct nk_font_atlas atlas; | |||
struct nk_vec2 fb_scale; | |||
unsigned int text[NK_GLFW_TEXT_MAX]; | |||
int text_len; | |||
struct nk_vec2 scroll; | |||
double last_button_click; | |||
int is_double_click_down; | |||
struct nk_vec2 double_click_pos; | |||
} glfw; | |||
NK_INTERN void | |||
nk_glfw3_device_upload_atlas(const void *image, int width, int height) | |||
{ | |||
struct nk_glfw_device *dev = &glfw.ogl; | |||
glGenTextures(1, &dev->font_tex); | |||
glBindTexture(GL_TEXTURE_2D, dev->font_tex); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, | |||
GL_RGBA, GL_UNSIGNED_BYTE, image); | |||
} | |||
NK_API void | |||
nk_glfw3_render(enum nk_anti_aliasing AA) | |||
{ | |||
/* setup global state */ | |||
struct nk_glfw_device *dev = &glfw.ogl; | |||
glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); | |||
glDisable(GL_CULL_FACE); | |||
glDisable(GL_DEPTH_TEST); | |||
glEnable(GL_SCISSOR_TEST); | |||
glEnable(GL_BLEND); | |||
glEnable(GL_TEXTURE_2D); | |||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
/* setup viewport/project */ | |||
glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height); | |||
glMatrixMode(GL_PROJECTION); | |||
glPushMatrix(); | |||
glLoadIdentity(); | |||
glOrtho(0.0f, glfw.width, glfw.height, 0.0f, -1.0f, 1.0f); | |||
glMatrixMode(GL_MODELVIEW); | |||
glPushMatrix(); | |||
glLoadIdentity(); | |||
glEnableClientState(GL_VERTEX_ARRAY); | |||
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |||
glEnableClientState(GL_COLOR_ARRAY); | |||
{ | |||
GLsizei vs = sizeof(struct nk_glfw_vertex); | |||
size_t vp = offsetof(struct nk_glfw_vertex, position); | |||
size_t vt = offsetof(struct nk_glfw_vertex, uv); | |||
size_t vc = offsetof(struct nk_glfw_vertex, col); | |||
/* convert from command queue into draw list and draw to screen */ | |||
const struct nk_draw_command *cmd; | |||
const nk_draw_index *offset = NULL; | |||
struct nk_buffer vbuf, ebuf; | |||
/* fill convert configuration */ | |||
struct nk_convert_config config; | |||
static const struct nk_draw_vertex_layout_element vertex_layout[] = { | |||
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, | |||
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, | |||
{NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, | |||
{NK_VERTEX_LAYOUT_END} | |||
}; | |||
NK_MEMSET(&config, 0, sizeof(config)); | |||
config.vertex_layout = vertex_layout; | |||
config.vertex_size = sizeof(struct nk_glfw_vertex); | |||
config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); | |||
config.null = dev->null; | |||
config.circle_segment_count = 22; | |||
config.curve_segment_count = 22; | |||
config.arc_segment_count = 22; | |||
config.global_alpha = 1.0f; | |||
config.shape_AA = AA; | |||
config.line_AA = AA; | |||
/* convert shapes into vertexes */ | |||
nk_buffer_init_default(&vbuf); | |||
nk_buffer_init_default(&ebuf); | |||
nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config); | |||
/* setup vertex buffer pointer */ | |||
{const void *vertices = nk_buffer_memory_const(&vbuf); | |||
glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp)); | |||
glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt)); | |||
glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));} | |||
/* iterate over and execute each draw command */ | |||
offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); | |||
nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) | |||
{ | |||
if (!cmd->elem_count) continue; | |||
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); | |||
glScissor( | |||
(GLint)(cmd->clip_rect.x * glfw.fb_scale.x), | |||
(GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y), | |||
(GLint)(cmd->clip_rect.w * glfw.fb_scale.x), | |||
(GLint)(cmd->clip_rect.h * glfw.fb_scale.y)); | |||
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); | |||
offset += cmd->elem_count; | |||
} | |||
nk_clear(&glfw.ctx); | |||
nk_buffer_free(&vbuf); | |||
nk_buffer_free(&ebuf); | |||
} | |||
/* default OpenGL state */ | |||
glDisableClientState(GL_VERTEX_ARRAY); | |||
glDisableClientState(GL_TEXTURE_COORD_ARRAY); | |||
glDisableClientState(GL_COLOR_ARRAY); | |||
glDisable(GL_CULL_FACE); | |||
glDisable(GL_DEPTH_TEST); | |||
glDisable(GL_SCISSOR_TEST); | |||
glDisable(GL_BLEND); | |||
glDisable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
glMatrixMode(GL_MODELVIEW); | |||
glPopMatrix(); | |||
glMatrixMode(GL_PROJECTION); | |||
glPopMatrix(); | |||
glPopAttrib(); | |||
} | |||
NK_API void | |||
nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) | |||
{ | |||
(void)win; | |||
if (glfw.text_len < NK_GLFW_TEXT_MAX) | |||
glfw.text[glfw.text_len++] = codepoint; | |||
} | |||
NK_API void | |||
nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) | |||
{ | |||
(void)win; (void)xoff; | |||
glfw.scroll.x += (float)xoff; | |||
glfw.scroll.y += (float)yoff; | |||
} | |||
NK_API void | |||
nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) | |||
{ | |||
double x, y; | |||
if (button != GLFW_MOUSE_BUTTON_LEFT) return; | |||
glfwGetCursorPos(window, &x, &y); | |||
if (action == GLFW_PRESS) { | |||
double dt = glfwGetTime() - glfw.last_button_click; | |||
if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) { | |||
glfw.is_double_click_down = nk_true; | |||
glfw.double_click_pos = nk_vec2((float)x, (float)y); | |||
} | |||
glfw.last_button_click = glfwGetTime(); | |||
} else glfw.is_double_click_down = nk_false; | |||
} | |||
NK_INTERN void | |||
nk_glfw3_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) | |||
{ | |||
const char *text = glfwGetClipboardString(glfw.win); | |||
if (text) nk_textedit_paste(edit, text, nk_strlen(text)); | |||
(void)usr; | |||
} | |||
NK_INTERN void | |||
nk_glfw3_clipbard_copy(nk_handle usr, const char *text, int len) | |||
{ | |||
char *str = 0; | |||
(void)usr; | |||
if (!len) return; | |||
str = (char*)malloc((size_t)len+1); | |||
if (!str) return; | |||
NK_MEMCPY(str, text, (size_t)len); | |||
str[len] = '\0'; | |||
glfwSetClipboardString(glfw.win, str); | |||
free(str); | |||
} | |||
NK_API struct nk_context* | |||
nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state) | |||
{ | |||
glfw.win = win; | |||
if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { | |||
glfwSetScrollCallback(win, nk_gflw3_scroll_callback); | |||
glfwSetCharCallback(win, nk_glfw3_char_callback); | |||
glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback); | |||
} | |||
nk_init_default(&glfw.ctx, 0); | |||
glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; | |||
glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; | |||
glfw.ctx.clip.userdata = nk_handle_ptr(0); | |||
nk_buffer_init_default(&glfw.ogl.cmds); | |||
glfw.is_double_click_down = nk_false; | |||
glfw.double_click_pos = nk_vec2(0, 0); | |||
return &glfw.ctx; | |||
} | |||
NK_API void | |||
nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) | |||
{ | |||
nk_font_atlas_init_default(&glfw.atlas); | |||
nk_font_atlas_begin(&glfw.atlas); | |||
*atlas = &glfw.atlas; | |||
} | |||
NK_API void | |||
nk_glfw3_font_stash_end(void) | |||
{ | |||
const void *image; int w, h; | |||
image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); | |||
nk_glfw3_device_upload_atlas(image, w, h); | |||
nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); | |||
if (glfw.atlas.default_font) | |||
nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); | |||
} | |||
NK_API void | |||
nk_glfw3_new_frame(void) | |||
{ | |||
int i; | |||
double x, y; | |||
struct nk_context *ctx = &glfw.ctx; | |||
struct GLFWwindow *win = glfw.win; | |||
glfwGetWindowSize(win, &glfw.width, &glfw.height); | |||
glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height); | |||
glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width; | |||
glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height; | |||
nk_input_begin(ctx); | |||
for (i = 0; i < glfw.text_len; ++i) | |||
nk_input_unicode(ctx, glfw.text[i]); | |||
/* optional grabbing behavior */ | |||
if (ctx->input.mouse.grab) | |||
glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); | |||
else if (ctx->input.mouse.ungrab) | |||
glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | |||
nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| | |||
glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); | |||
if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || | |||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { | |||
nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); | |||
} else { | |||
nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); | |||
nk_input_key(ctx, NK_KEY_COPY, 0); | |||
nk_input_key(ctx, NK_KEY_PASTE, 0); | |||
nk_input_key(ctx, NK_KEY_CUT, 0); | |||
nk_input_key(ctx, NK_KEY_SHIFT, 0); | |||
} | |||
glfwGetCursorPos(win, &x, &y); | |||
nk_input_motion(ctx, (int)x, (int)y); | |||
if (ctx->input.mouse.grabbed) { | |||
glfwSetCursorPos(glfw.win, (double)ctx->input.mouse.prev.x, (double)ctx->input.mouse.prev.y); | |||
ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; | |||
ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; | |||
} | |||
nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); | |||
nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); | |||
nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); | |||
nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw.double_click_pos.x, (int)glfw.double_click_pos.y, glfw.is_double_click_down); | |||
nk_input_scroll(ctx, glfw.scroll); | |||
nk_input_end(&glfw.ctx); | |||
glfw.text_len = 0; | |||
glfw.scroll = nk_vec2(0,0); | |||
} | |||
NK_API | |||
void nk_glfw3_shutdown(void) | |||
{ | |||
struct nk_glfw_device *dev = &glfw.ogl; | |||
nk_font_atlas_clear(&glfw.atlas); | |||
nk_free(&glfw.ctx); | |||
glDeleteTextures(1, &dev->font_tex); | |||
nk_buffer_free(&dev->cmds); | |||
NK_MEMSET(&glfw, 0, sizeof(glfw)); | |||
} | |||
#endif |
@ -1,594 +0,0 @@ | |||
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- | |||
Copyright (c) 2012 Marcus Geelnard | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source | |||
distribution. | |||
*/ | |||
/* 2013-01-06 Camilla Löwy <elmindreda@glfw.org> | |||
* | |||
* Added casts from time_t to DWORD to avoid warnings on VC++. | |||
* Fixed time retrieval on POSIX systems. | |||
*/ | |||
#include "tinycthread.h" | |||
#include <stdlib.h> | |||
/* Platform specific includes */ | |||
#if defined(_TTHREAD_POSIX_) | |||
#include <signal.h> | |||
#include <sched.h> | |||
#include <unistd.h> | |||
#include <sys/time.h> | |||
#include <errno.h> | |||
#elif defined(_TTHREAD_WIN32_) | |||
#include <process.h> | |||
#include <sys/timeb.h> | |||
#endif | |||
/* Standard, good-to-have defines */ | |||
#ifndef NULL | |||
#define NULL (void*)0 | |||
#endif | |||
#ifndef TRUE | |||
#define TRUE 1 | |||
#endif | |||
#ifndef FALSE | |||
#define FALSE 0 | |||
#endif | |||
int mtx_init(mtx_t *mtx, int type) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
mtx->mAlreadyLocked = FALSE; | |||
mtx->mRecursive = type & mtx_recursive; | |||
InitializeCriticalSection(&mtx->mHandle); | |||
return thrd_success; | |||
#else | |||
int ret; | |||
pthread_mutexattr_t attr; | |||
pthread_mutexattr_init(&attr); | |||
if (type & mtx_recursive) | |||
{ | |||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | |||
} | |||
ret = pthread_mutex_init(mtx, &attr); | |||
pthread_mutexattr_destroy(&attr); | |||
return ret == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
void mtx_destroy(mtx_t *mtx) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
DeleteCriticalSection(&mtx->mHandle); | |||
#else | |||
pthread_mutex_destroy(mtx); | |||
#endif | |||
} | |||
int mtx_lock(mtx_t *mtx) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
EnterCriticalSection(&mtx->mHandle); | |||
if (!mtx->mRecursive) | |||
{ | |||
while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */ | |||
mtx->mAlreadyLocked = TRUE; | |||
} | |||
return thrd_success; | |||
#else | |||
return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) | |||
{ | |||
/* FIXME! */ | |||
(void)mtx; | |||
(void)ts; | |||
return thrd_error; | |||
} | |||
int mtx_trylock(mtx_t *mtx) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy; | |||
if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked) | |||
{ | |||
LeaveCriticalSection(&mtx->mHandle); | |||
ret = thrd_busy; | |||
} | |||
return ret; | |||
#else | |||
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; | |||
#endif | |||
} | |||
int mtx_unlock(mtx_t *mtx) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
mtx->mAlreadyLocked = FALSE; | |||
LeaveCriticalSection(&mtx->mHandle); | |||
return thrd_success; | |||
#else | |||
return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;; | |||
#endif | |||
} | |||
#if defined(_TTHREAD_WIN32_) | |||
#define _CONDITION_EVENT_ONE 0 | |||
#define _CONDITION_EVENT_ALL 1 | |||
#endif | |||
int cnd_init(cnd_t *cond) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
cond->mWaitersCount = 0; | |||
/* Init critical section */ | |||
InitializeCriticalSection(&cond->mWaitersCountLock); | |||
/* Init events */ | |||
cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); | |||
if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL) | |||
{ | |||
cond->mEvents[_CONDITION_EVENT_ALL] = NULL; | |||
return thrd_error; | |||
} | |||
cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); | |||
if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL) | |||
{ | |||
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); | |||
cond->mEvents[_CONDITION_EVENT_ONE] = NULL; | |||
return thrd_error; | |||
} | |||
return thrd_success; | |||
#else | |||
return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
void cnd_destroy(cnd_t *cond) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL) | |||
{ | |||
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); | |||
} | |||
if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL) | |||
{ | |||
CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]); | |||
} | |||
DeleteCriticalSection(&cond->mWaitersCountLock); | |||
#else | |||
pthread_cond_destroy(cond); | |||
#endif | |||
} | |||
int cnd_signal(cnd_t *cond) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
int haveWaiters; | |||
/* Are there any waiters? */ | |||
EnterCriticalSection(&cond->mWaitersCountLock); | |||
haveWaiters = (cond->mWaitersCount > 0); | |||
LeaveCriticalSection(&cond->mWaitersCountLock); | |||
/* If we have any waiting threads, send them a signal */ | |||
if(haveWaiters) | |||
{ | |||
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0) | |||
{ | |||
return thrd_error; | |||
} | |||
} | |||
return thrd_success; | |||
#else | |||
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
int cnd_broadcast(cnd_t *cond) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
int haveWaiters; | |||
/* Are there any waiters? */ | |||
EnterCriticalSection(&cond->mWaitersCountLock); | |||
haveWaiters = (cond->mWaitersCount > 0); | |||
LeaveCriticalSection(&cond->mWaitersCountLock); | |||
/* If we have any waiting threads, send them a signal */ | |||
if(haveWaiters) | |||
{ | |||
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) | |||
{ | |||
return thrd_error; | |||
} | |||
} | |||
return thrd_success; | |||
#else | |||
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
#if defined(_TTHREAD_WIN32_) | |||
static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout) | |||
{ | |||
int result, lastWaiter; | |||
/* Increment number of waiters */ | |||
EnterCriticalSection(&cond->mWaitersCountLock); | |||
++ cond->mWaitersCount; | |||
LeaveCriticalSection(&cond->mWaitersCountLock); | |||
/* Release the mutex while waiting for the condition (will decrease | |||
the number of waiters when done)... */ | |||
mtx_unlock(mtx); | |||
/* Wait for either event to become signaled due to cnd_signal() or | |||
cnd_broadcast() being called */ | |||
result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout); | |||
if (result == WAIT_TIMEOUT) | |||
{ | |||
return thrd_timeout; | |||
} | |||
else if (result == (int)WAIT_FAILED) | |||
{ | |||
return thrd_error; | |||
} | |||
/* Check if we are the last waiter */ | |||
EnterCriticalSection(&cond->mWaitersCountLock); | |||
-- cond->mWaitersCount; | |||
lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && | |||
(cond->mWaitersCount == 0); | |||
LeaveCriticalSection(&cond->mWaitersCountLock); | |||
/* If we are the last waiter to be notified to stop waiting, reset the event */ | |||
if (lastWaiter) | |||
{ | |||
if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) | |||
{ | |||
return thrd_error; | |||
} | |||
} | |||
/* Re-acquire the mutex */ | |||
mtx_lock(mtx); | |||
return thrd_success; | |||
} | |||
#endif | |||
int cnd_wait(cnd_t *cond, mtx_t *mtx) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
return _cnd_timedwait_win32(cond, mtx, INFINITE); | |||
#else | |||
return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
struct timespec now; | |||
if (clock_gettime(CLOCK_REALTIME, &now) == 0) | |||
{ | |||
DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 + | |||
(ts->tv_nsec - now.tv_nsec + 500000) / 1000000); | |||
return _cnd_timedwait_win32(cond, mtx, delta); | |||
} | |||
else | |||
return thrd_error; | |||
#else | |||
int ret; | |||
ret = pthread_cond_timedwait(cond, mtx, ts); | |||
if (ret == ETIMEDOUT) | |||
{ | |||
return thrd_timeout; | |||
} | |||
return ret == 0 ? thrd_success : thrd_error; | |||
#endif | |||
} | |||
/** Information to pass to the new thread (what to run). */ | |||
typedef struct { | |||
thrd_start_t mFunction; /**< Pointer to the function to be executed. */ | |||
void * mArg; /**< Function argument for the thread function. */ | |||
} _thread_start_info; | |||
/* Thread wrapper function. */ | |||
#if defined(_TTHREAD_WIN32_) | |||
static unsigned WINAPI _thrd_wrapper_function(void * aArg) | |||
#elif defined(_TTHREAD_POSIX_) | |||
static void * _thrd_wrapper_function(void * aArg) | |||
#endif | |||
{ | |||
thrd_start_t fun; | |||
void *arg; | |||
int res; | |||
#if defined(_TTHREAD_POSIX_) | |||
void *pres; | |||
#endif | |||
/* Get thread startup information */ | |||
_thread_start_info *ti = (_thread_start_info *) aArg; | |||
fun = ti->mFunction; | |||
arg = ti->mArg; | |||
/* The thread is responsible for freeing the startup information */ | |||
free((void *)ti); | |||
/* Call the actual client thread function */ | |||
res = fun(arg); | |||
#if defined(_TTHREAD_WIN32_) | |||
return res; | |||
#else | |||
pres = malloc(sizeof(int)); | |||
if (pres != NULL) | |||
{ | |||
*(int*)pres = res; | |||
} | |||
return pres; | |||
#endif | |||
} | |||
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) | |||
{ | |||
/* Fill out the thread startup information (passed to the thread wrapper, | |||
which will eventually free it) */ | |||
_thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info)); | |||
if (ti == NULL) | |||
{ | |||
return thrd_nomem; | |||
} | |||
ti->mFunction = func; | |||
ti->mArg = arg; | |||
/* Create the thread */ | |||
#if defined(_TTHREAD_WIN32_) | |||
*thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL); | |||
#elif defined(_TTHREAD_POSIX_) | |||
if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0) | |||
{ | |||
*thr = 0; | |||
} | |||
#endif | |||
/* Did we fail to create the thread? */ | |||
if(!*thr) | |||
{ | |||
free(ti); | |||
return thrd_error; | |||
} | |||
return thrd_success; | |||
} | |||
thrd_t thrd_current(void) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
return GetCurrentThread(); | |||
#else | |||
return pthread_self(); | |||
#endif | |||
} | |||
int thrd_detach(thrd_t thr) | |||
{ | |||
/* FIXME! */ | |||
(void)thr; | |||
return thrd_error; | |||
} | |||
int thrd_equal(thrd_t thr0, thrd_t thr1) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
return thr0 == thr1; | |||
#else | |||
return pthread_equal(thr0, thr1); | |||
#endif | |||
} | |||
void thrd_exit(int res) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
ExitThread(res); | |||
#else | |||
void *pres = malloc(sizeof(int)); | |||
if (pres != NULL) | |||
{ | |||
*(int*)pres = res; | |||
} | |||
pthread_exit(pres); | |||
#endif | |||
} | |||
int thrd_join(thrd_t thr, int *res) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED) | |||
{ | |||
return thrd_error; | |||
} | |||
if (res != NULL) | |||
{ | |||
DWORD dwRes; | |||
GetExitCodeThread(thr, &dwRes); | |||
*res = dwRes; | |||
} | |||
#elif defined(_TTHREAD_POSIX_) | |||
void *pres; | |||
int ires = 0; | |||
if (pthread_join(thr, &pres) != 0) | |||
{ | |||
return thrd_error; | |||
} | |||
if (pres != NULL) | |||
{ | |||
ires = *(int*)pres; | |||
free(pres); | |||
} | |||
if (res != NULL) | |||
{ | |||
*res = ires; | |||
} | |||
#endif | |||
return thrd_success; | |||
} | |||
int thrd_sleep(const struct timespec *time_point, struct timespec *remaining) | |||
{ | |||
struct timespec now; | |||
#if defined(_TTHREAD_WIN32_) | |||
DWORD delta; | |||
#else | |||
long delta; | |||
#endif | |||
/* Get the current time */ | |||
if (clock_gettime(CLOCK_REALTIME, &now) != 0) | |||
return -2; // FIXME: Some specific error code? | |||
#if defined(_TTHREAD_WIN32_) | |||
/* Delta in milliseconds */ | |||
delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 + | |||
(time_point->tv_nsec - now.tv_nsec + 500000) / 1000000); | |||
if (delta > 0) | |||
{ | |||
Sleep(delta); | |||
} | |||
#else | |||
/* Delta in microseconds */ | |||
delta = (time_point->tv_sec - now.tv_sec) * 1000000L + | |||
(time_point->tv_nsec - now.tv_nsec + 500L) / 1000L; | |||
/* On some systems, the usleep argument must be < 1000000 */ | |||
while (delta > 999999L) | |||
{ | |||
usleep(999999); | |||
delta -= 999999L; | |||
} | |||
if (delta > 0L) | |||
{ | |||
usleep((useconds_t)delta); | |||
} | |||
#endif | |||
/* We don't support waking up prematurely (yet) */ | |||
if (remaining) | |||
{ | |||
remaining->tv_sec = 0; | |||
remaining->tv_nsec = 0; | |||
} | |||
return 0; | |||
} | |||
void thrd_yield(void) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
Sleep(0); | |||
#else | |||
sched_yield(); | |||
#endif | |||
} | |||
int tss_create(tss_t *key, tss_dtor_t dtor) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
/* FIXME: The destructor function is not supported yet... */ | |||
if (dtor != NULL) | |||
{ | |||
return thrd_error; | |||
} | |||
*key = TlsAlloc(); | |||
if (*key == TLS_OUT_OF_INDEXES) | |||
{ | |||
return thrd_error; | |||
} | |||
#else | |||
if (pthread_key_create(key, dtor) != 0) | |||
{ | |||
return thrd_error; | |||
} | |||
#endif | |||
return thrd_success; | |||
} | |||
void tss_delete(tss_t key) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
TlsFree(key); | |||
#else | |||
pthread_key_delete(key); | |||
#endif | |||
} | |||
void *tss_get(tss_t key) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
return TlsGetValue(key); | |||
#else | |||
return pthread_getspecific(key); | |||
#endif | |||
} | |||
int tss_set(tss_t key, void *val) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
if (TlsSetValue(key, val) == 0) | |||
{ | |||
return thrd_error; | |||
} | |||
#else | |||
if (pthread_setspecific(key, val) != 0) | |||
{ | |||
return thrd_error; | |||
} | |||
#endif | |||
return thrd_success; | |||
} | |||
#if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_) | |||
int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts) | |||
{ | |||
#if defined(_TTHREAD_WIN32_) | |||
struct _timeb tb; | |||
_ftime(&tb); | |||
ts->tv_sec = (time_t)tb.time; | |||
ts->tv_nsec = 1000000L * (long)tb.millitm; | |||
#else | |||
struct timeval tv; | |||
gettimeofday(&tv, NULL); | |||
ts->tv_sec = (time_t)tv.tv_sec; | |||
ts->tv_nsec = 1000L * (long)tv.tv_usec; | |||
#endif | |||
return 0; | |||
} | |||
#endif // _TTHREAD_EMULATE_CLOCK_GETTIME_ | |||
@ -1,443 +0,0 @@ | |||
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- | |||
Copyright (c) 2012 Marcus Geelnard | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source | |||
distribution. | |||
*/ | |||
#ifndef _TINYCTHREAD_H_ | |||
#define _TINYCTHREAD_H_ | |||
/** | |||
* @file | |||
* @mainpage TinyCThread API Reference | |||
* | |||
* @section intro_sec Introduction | |||
* TinyCThread is a minimal, portable implementation of basic threading | |||
* classes for C. | |||
* | |||
* They closely mimic the functionality and naming of the C11 standard, and | |||
* should be easily replaceable with the corresponding standard variants. | |||
* | |||
* @section port_sec Portability | |||
* The Win32 variant uses the native Win32 API for implementing the thread | |||
* classes, while for other systems, the POSIX threads API (pthread) is used. | |||
* | |||
* @section misc_sec Miscellaneous | |||
* The following special keywords are available: #_Thread_local. | |||
* | |||
* For more detailed information, browse the different sections of this | |||
* documentation. A good place to start is: | |||
* tinycthread.h. | |||
*/ | |||
/* Which platform are we on? */ | |||
#if !defined(_TTHREAD_PLATFORM_DEFINED_) | |||
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) | |||
#define _TTHREAD_WIN32_ | |||
#else | |||
#define _TTHREAD_POSIX_ | |||
#endif | |||
#define _TTHREAD_PLATFORM_DEFINED_ | |||
#endif | |||
/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ | |||
#if defined(_TTHREAD_POSIX_) | |||
#undef _FEATURES_H | |||
#if !defined(_GNU_SOURCE) | |||
#define _GNU_SOURCE | |||
#endif | |||
#if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) | |||
#undef _POSIX_C_SOURCE | |||
#define _POSIX_C_SOURCE 199309L | |||
#endif | |||
#if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) | |||
#undef _XOPEN_SOURCE | |||
#define _XOPEN_SOURCE 500 | |||
#endif | |||
#endif | |||
/* Generic includes */ | |||
#include <time.h> | |||
/* Platform specific includes */ | |||
#if defined(_TTHREAD_POSIX_) | |||
#include <sys/time.h> | |||
#include <pthread.h> | |||
#elif defined(_TTHREAD_WIN32_) | |||
#ifndef WIN32_LEAN_AND_MEAN | |||
#define WIN32_LEAN_AND_MEAN | |||
#define __UNDEF_LEAN_AND_MEAN | |||
#endif | |||
#include <windows.h> | |||
#ifdef __UNDEF_LEAN_AND_MEAN | |||
#undef WIN32_LEAN_AND_MEAN | |||
#undef __UNDEF_LEAN_AND_MEAN | |||
#endif | |||
#endif | |||
/* Workaround for missing TIME_UTC: If time.h doesn't provide TIME_UTC, | |||
it's quite likely that libc does not support it either. Hence, fall back to | |||
the only other supported time specifier: CLOCK_REALTIME (and if that fails, | |||
we're probably emulating clock_gettime anyway, so anything goes). */ | |||
#ifndef TIME_UTC | |||
#ifdef CLOCK_REALTIME | |||
#define TIME_UTC CLOCK_REALTIME | |||
#else | |||
#define TIME_UTC 0 | |||
#endif | |||
#endif | |||
/* Workaround for missing clock_gettime (most Windows compilers, afaik) */ | |||
#if defined(_TTHREAD_WIN32_) || defined(__APPLE_CC__) | |||
#define _TTHREAD_EMULATE_CLOCK_GETTIME_ | |||
/* Emulate struct timespec */ | |||
#if defined(_TTHREAD_WIN32_) | |||
struct _ttherad_timespec { | |||
time_t tv_sec; | |||
long tv_nsec; | |||
}; | |||
#define timespec _ttherad_timespec | |||
#endif | |||
/* Emulate clockid_t */ | |||
typedef int _tthread_clockid_t; | |||
#define clockid_t _tthread_clockid_t | |||
/* Emulate clock_gettime */ | |||
int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts); | |||
#define clock_gettime _tthread_clock_gettime | |||
#ifndef CLOCK_REALTIME | |||
#define CLOCK_REALTIME 0 | |||
#endif | |||
#endif | |||
/** TinyCThread version (major number). */ | |||
#define TINYCTHREAD_VERSION_MAJOR 1 | |||
/** TinyCThread version (minor number). */ | |||
#define TINYCTHREAD_VERSION_MINOR 1 | |||
/** TinyCThread version (full version). */ | |||
#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR) | |||
/** | |||
* @def _Thread_local | |||
* Thread local storage keyword. | |||
* A variable that is declared with the @c _Thread_local keyword makes the | |||
* value of the variable local to each thread (known as thread-local storage, | |||
* or TLS). Example usage: | |||
* @code | |||
* // This variable is local to each thread. | |||
* _Thread_local int variable; | |||
* @endcode | |||
* @note The @c _Thread_local keyword is a macro that maps to the corresponding | |||
* compiler directive (e.g. @c __declspec(thread)). | |||
* @note This directive is currently not supported on Mac OS X (it will give | |||
* a compiler error), since compile-time TLS is not supported in the Mac OS X | |||
* executable format. Also, some older versions of MinGW (before GCC 4.x) do | |||
* not support this directive. | |||
* @hideinitializer | |||
*/ | |||
/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 */ | |||
#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) | |||
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) | |||
#define _Thread_local __thread | |||
#else | |||
#define _Thread_local __declspec(thread) | |||
#endif | |||
#endif | |||
/* Macros */ | |||
#define TSS_DTOR_ITERATIONS 0 | |||
/* Function return values */ | |||
#define thrd_error 0 /**< The requested operation failed */ | |||
#define thrd_success 1 /**< The requested operation succeeded */ | |||
#define thrd_timeout 2 /**< The time specified in the call was reached without acquiring the requested resource */ | |||
#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */ | |||
#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */ | |||
/* Mutex types */ | |||
#define mtx_plain 1 | |||
#define mtx_timed 2 | |||
#define mtx_try 4 | |||
#define mtx_recursive 8 | |||
/* Mutex */ | |||
#if defined(_TTHREAD_WIN32_) | |||
typedef struct { | |||
CRITICAL_SECTION mHandle; /* Critical section handle */ | |||
int mAlreadyLocked; /* TRUE if the mutex is already locked */ | |||
int mRecursive; /* TRUE if the mutex is recursive */ | |||
} mtx_t; | |||
#else | |||
typedef pthread_mutex_t mtx_t; | |||
#endif | |||
/** Create a mutex object. | |||
* @param mtx A mutex object. | |||
* @param type Bit-mask that must have one of the following six values: | |||
* @li @c mtx_plain for a simple non-recursive mutex | |||
* @li @c mtx_timed for a non-recursive mutex that supports timeout | |||
* @li @c mtx_try for a non-recursive mutex that supports test and return | |||
* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive) | |||
* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive) | |||
* @li @c mtx_try | @c mtx_recursive (same as @c mtx_try, but recursive) | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int mtx_init(mtx_t *mtx, int type); | |||
/** Release any resources used by the given mutex. | |||
* @param mtx A mutex object. | |||
*/ | |||
void mtx_destroy(mtx_t *mtx); | |||
/** Lock the given mutex. | |||
* Blocks until the given mutex can be locked. If the mutex is non-recursive, and | |||
* the calling thread already has a lock on the mutex, this call will block | |||
* forever. | |||
* @param mtx A mutex object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int mtx_lock(mtx_t *mtx); | |||
/** NOT YET IMPLEMENTED. | |||
*/ | |||
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); | |||
/** Try to lock the given mutex. | |||
* The specified mutex shall support either test and return or timeout. If the | |||
* mutex is already locked, the function returns without blocking. | |||
* @param mtx A mutex object. | |||
* @return @ref thrd_success on success, or @ref thrd_busy if the resource | |||
* requested is already in use, or @ref thrd_error if the request could not be | |||
* honored. | |||
*/ | |||
int mtx_trylock(mtx_t *mtx); | |||
/** Unlock the given mutex. | |||
* @param mtx A mutex object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int mtx_unlock(mtx_t *mtx); | |||
/* Condition variable */ | |||
#if defined(_TTHREAD_WIN32_) | |||
typedef struct { | |||
HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */ | |||
unsigned int mWaitersCount; /* Count of the number of waiters. */ | |||
CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */ | |||
} cnd_t; | |||
#else | |||
typedef pthread_cond_t cnd_t; | |||
#endif | |||
/** Create a condition variable object. | |||
* @param cond A condition variable object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int cnd_init(cnd_t *cond); | |||
/** Release any resources used by the given condition variable. | |||
* @param cond A condition variable object. | |||
*/ | |||
void cnd_destroy(cnd_t *cond); | |||
/** Signal a condition variable. | |||
* Unblocks one of the threads that are blocked on the given condition variable | |||
* at the time of the call. If no threads are blocked on the condition variable | |||
* at the time of the call, the function does nothing and return success. | |||
* @param cond A condition variable object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int cnd_signal(cnd_t *cond); | |||
/** Broadcast a condition variable. | |||
* Unblocks all of the threads that are blocked on the given condition variable | |||
* at the time of the call. If no threads are blocked on the condition variable | |||
* at the time of the call, the function does nothing and return success. | |||
* @param cond A condition variable object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int cnd_broadcast(cnd_t *cond); | |||
/** Wait for a condition variable to become signaled. | |||
* The function atomically unlocks the given mutex and endeavors to block until | |||
* the given condition variable is signaled by a call to cnd_signal or to | |||
* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex | |||
* before it returns. | |||
* @param cond A condition variable object. | |||
* @param mtx A mutex object. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int cnd_wait(cnd_t *cond, mtx_t *mtx); | |||
/** Wait for a condition variable to become signaled. | |||
* The function atomically unlocks the given mutex and endeavors to block until | |||
* the given condition variable is signaled by a call to cnd_signal or to | |||
* cnd_broadcast, or until after the specified time. When the calling thread | |||
* becomes unblocked it locks the mutex before it returns. | |||
* @param cond A condition variable object. | |||
* @param mtx A mutex object. | |||
* @param xt A point in time at which the request will time out (absolute time). | |||
* @return @ref thrd_success upon success, or @ref thrd_timeout if the time | |||
* specified in the call was reached without acquiring the requested resource, or | |||
* @ref thrd_error if the request could not be honored. | |||
*/ | |||
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); | |||
/* Thread */ | |||
#if defined(_TTHREAD_WIN32_) | |||
typedef HANDLE thrd_t; | |||
#else | |||
typedef pthread_t thrd_t; | |||
#endif | |||
/** Thread start function. | |||
* Any thread that is started with the @ref thrd_create() function must be | |||
* started through a function of this type. | |||
* @param arg The thread argument (the @c arg argument of the corresponding | |||
* @ref thrd_create() call). | |||
* @return The thread return value, which can be obtained by another thread | |||
* by using the @ref thrd_join() function. | |||
*/ | |||
typedef int (*thrd_start_t)(void *arg); | |||
/** Create a new thread. | |||
* @param thr Identifier of the newly created thread. | |||
* @param func A function pointer to the function that will be executed in | |||
* the new thread. | |||
* @param arg An argument to the thread function. | |||
* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could | |||
* be allocated for the thread requested, or @ref thrd_error if the request | |||
* could not be honored. | |||
* @note A thread’s identifier may be reused for a different thread once the | |||
* original thread has exited and either been detached or joined to another | |||
* thread. | |||
*/ | |||
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); | |||
/** Identify the calling thread. | |||
* @return The identifier of the calling thread. | |||
*/ | |||
thrd_t thrd_current(void); | |||
/** NOT YET IMPLEMENTED. | |||
*/ | |||
int thrd_detach(thrd_t thr); | |||
/** Compare two thread identifiers. | |||
* The function determines if two thread identifiers refer to the same thread. | |||
* @return Zero if the two thread identifiers refer to different threads. | |||
* Otherwise a nonzero value is returned. | |||
*/ | |||
int thrd_equal(thrd_t thr0, thrd_t thr1); | |||
/** Terminate execution of the calling thread. | |||
* @param res Result code of the calling thread. | |||
*/ | |||
void thrd_exit(int res); | |||
/** Wait for a thread to terminate. | |||
* The function joins the given thread with the current thread by blocking | |||
* until the other thread has terminated. | |||
* @param thr The thread to join with. | |||
* @param res If this pointer is not NULL, the function will store the result | |||
* code of the given thread in the integer pointed to by @c res. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int thrd_join(thrd_t thr, int *res); | |||
/** Put the calling thread to sleep. | |||
* Suspend execution of the calling thread. | |||
* @param time_point A point in time at which the thread will resume (absolute time). | |||
* @param remaining If non-NULL, this parameter will hold the remaining time until | |||
* time_point upon return. This will typically be zero, but if | |||
* the thread was woken up by a signal that is not ignored before | |||
* time_point was reached @c remaining will hold a positive | |||
* time. | |||
* @return 0 (zero) on successful sleep, or -1 if an interrupt occurred. | |||
*/ | |||
int thrd_sleep(const struct timespec *time_point, struct timespec *remaining); | |||
/** Yield execution to another thread. | |||
* Permit other threads to run, even if the current thread would ordinarily | |||
* continue to run. | |||
*/ | |||
void thrd_yield(void); | |||
/* Thread local storage */ | |||
#if defined(_TTHREAD_WIN32_) | |||
typedef DWORD tss_t; | |||
#else | |||
typedef pthread_key_t tss_t; | |||
#endif | |||
/** Destructor function for a thread-specific storage. | |||
* @param val The value of the destructed thread-specific storage. | |||
*/ | |||
typedef void (*tss_dtor_t)(void *val); | |||
/** Create a thread-specific storage. | |||
* @param key The unique key identifier that will be set if the function is | |||
* successful. | |||
* @param dtor Destructor function. This can be NULL. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
* @note The destructor function is not supported under Windows. If @c dtor is | |||
* not NULL when calling this function under Windows, the function will fail | |||
* and return @ref thrd_error. | |||
*/ | |||
int tss_create(tss_t *key, tss_dtor_t dtor); | |||
/** Delete a thread-specific storage. | |||
* The function releases any resources used by the given thread-specific | |||
* storage. | |||
* @param key The key that shall be deleted. | |||
*/ | |||
void tss_delete(tss_t key); | |||
/** Get the value for a thread-specific storage. | |||
* @param key The thread-specific storage identifier. | |||
* @return The value for the current thread held in the given thread-specific | |||
* storage. | |||
*/ | |||
void *tss_get(tss_t key); | |||
/** Set the value for a thread-specific storage. | |||
* @param key The thread-specific storage identifier. | |||
* @param val The value of the thread-specific storage to set for the current | |||
* thread. | |||
* @return @ref thrd_success on success, or @ref thrd_error if the request could | |||
* not be honored. | |||
*/ | |||
int tss_set(tss_t key, void *val); | |||
#endif /* _TINYTHREAD_H_ */ | |||