diff options
Diffstat (limited to 'tigr.c')
| -rw-r--r-- | tigr.c | 6610 |
1 files changed, 0 insertions, 6610 deletions
@@ -1,6610 +0,0 @@ -//////// Start of inlined file: tigr_amalgamated.c //////// - - -#include "tigr.h" - -//////// Start of inlined file: tigr_internal.h //////// - -// can't use pragma once here because this file probably will endup in .c -#ifndef __TIGR_INTERNAL_H__ -#define __TIGR_INTERNAL_H__ - -#define _CRT_SECURE_NO_WARNINGS NOPE - -// Graphics configuration. -#ifndef TIGR_HEADLESS -#define TIGR_GAPI_GL - -// Calculates the biggest scale that a bitmap can fit into an area at. -int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH); -#endif - -// Creates a new bitmap, with extra payload bytes. -Tigr* tigrBitmap2(int w, int h, int extra); - -// Resizes an existing bitmap. -void tigrResize(Tigr* bmp, int w, int h); - -// Calculates a new scale, taking minimum-scale flags into account. -int tigrEnforceScale(int scale, int flags); - -// Calculates the correct position for a bitmap to fit into a window. -void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]); - -// ---------------------------------------------------------- -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#endif - -#if !defined(TIGR_HEADLESS) && __linux__ && !__ANDROID__ -#include <X11/X.h> -#include <X11/Xlib.h> -#endif - -#ifdef __APPLE__ -#include "TargetConditionals.h" -#if TARGET_OS_IPHONE -#define __IOS__ 1 -#else -#define __MACOS__ 1 -#endif -#endif - -#ifdef TIGR_GAPI_GL -#if __MACOS__ -#define GL_SILENCE_DEPRECATION -#include <OpenGL/gl3.h> -#endif -#ifdef _WIN32 -#include <GL/gl.h> -#endif -#if __linux__ && !__ANDROID__ -#define GL_GLEXT_PROTOTYPES -#include <GL/gl.h> -#include <GL/glx.h> -#endif -#if __ANDROID__ -#include <EGL/egl.h> -#include <GLES3/gl3.h> -#endif -#if __IOS__ -#define GLES_SILENCE_DEPRECATION -#include <OpenGLES/ES3/gl.h> -#endif - -typedef struct { -#ifdef _WIN32 - HGLRC hglrc; - HDC dc; -#endif -#ifdef __APPLE__ - void* glContext; -#endif - GLuint tex[2]; - GLuint vao; - GLuint program; - GLuint uniform_projection; - GLuint uniform_model; - GLuint uniform_parameters; - int gl_legacy; - int gl_user_opengl_rendering; -} GLStuff; -#endif - -#define MAX_TOUCH_POINTS 10 - -typedef struct { - int shown, closed; -#ifdef TIGR_GAPI_GL - GLStuff gl; -#ifdef _WIN32 - wchar_t* wtitle; - DWORD dwStyle; - RECT oldPos; -#endif -#ifdef __linux__ -#if __ANDROID__ - EGLContext context; -#else - Display* dpy; - Window win; - GLXContext glc; - XIC ic; -#endif // __ANDROID__ -#endif // __linux__ -#endif // TIGR_GAPI_GL - - Tigr* widgets; - int widgetsWanted; - unsigned char widgetAlpha; - float widgetsScale; - - float p1, p2, p3, p4; - - int flags; - int scale; - int pos[4]; - int lastChar; - char keys[256], prev[256]; -#if defined(__ANDROID__) - char released[256]; -#endif // __ANDROID__ -#if defined(__MACOS__) - int mouseInView; - int mouseButtons; - double frameTime; -#endif // __MACOS__ -#if defined(__linux__) || defined(__IOS__) - int mouseButtons; - int mouseX; - int mouseY; -#endif // __linux__ __IOS__ -#if defined(__ANDROID__) || defined(__IOS__) - int numTouchPoints; - TigrTouchPoint touchPoints[MAX_TOUCH_POINTS]; -#endif // __ANDROID__ __IOS__ -#if defined(_WIN32) || defined(__linux__) || defined(__MACOS__) - float scrollDeltaX; - float scrollDeltaY; -#endif // _WIN32 __linux__ __MACOS__ -} TigrInternal; -// ---------------------------------------------------------- - -TigrInternal* tigrInternal(Tigr* bmp); - -void tigrGAPICreate(Tigr* bmp); -void tigrGAPIDestroy(Tigr* bmp); -int tigrGAPIBegin(Tigr* bmp); -int tigrGAPIEnd(Tigr* bmp); -void tigrGAPIPresent(Tigr* bmp, int w, int h); - -#endif - -//////// End of inlined file: tigr_internal.h //////// - -//////// Start of inlined file: tigr_upscale_gl_vs.h //////// - -#ifndef __TIGR_UPSCALE_GL_VS_H__ -#define __TIGR_UPSCALE_GL_VS_H__ - -//////// Start of inlined file: tigr_glsl_hdr.h //////// - -#ifndef __TIGR_GLSL_HDR_H__ -#define __TIGR_GLSL_HDR_H__ - -#if __ANDROID__ || __IOS__ -#define GLSL_VERSION_HEADER \ - "#version 300 es\n" \ - "precision mediump float;\n" -#else -#define GLSL_VERSION_HEADER "#version 330 core\n" -#endif - -#endif // __TIGR_GLSL_HDR_H__ - -//////// End of inlined file: tigr_glsl_hdr.h //////// - - -// clang-format off -const char tigr_upscale_gl_vs[] = { - GLSL_VERSION_HEADER - "layout (location = 0) in vec2 pos_in;" - "layout (location = 1) in vec2 uv_in;" - "out vec2 uv;" - "uniform mat4 model;" - "uniform mat4 projection;" - "void main()" - "{" - " uv = uv_in;" - " gl_Position = projection * model * vec4(pos_in, 0.0, 1.0);" - "}" -}; -// clang-format on - -const int tigr_upscale_gl_vs_size = (int)sizeof(tigr_upscale_gl_vs) - 1; - -#endif - -//////// End of inlined file: tigr_upscale_gl_vs.h //////// - -//////// Start of inlined file: tigr_upscale_gl_fs.h //////// - -#ifndef __TIGR_UPSCALE_GL_FS_H__ -#define __TIGR_UPSCALE_GL_FS_H__ - -//#include "tigr_glsl_hdr.h" - -// clang-format off -const char tigr_upscale_gl_fs[] = { - GLSL_VERSION_HEADER - "in vec2 uv;" - "out vec4 color;" - "uniform sampler2D image;" - "uniform vec4 parameters;" - "void fxShader(out vec4 color, in vec2 coord);" - "void main()" - "{" - " fxShader(color, uv);" - "}\n" -}; -// clang-format on - -const int tigr_upscale_gl_fs_size = (int)sizeof(tigr_upscale_gl_fs) - 1; - -// clang-format off -const char tigr_default_fx_gl_fs[] = { - "void fxShader(out vec4 color, in vec2 uv) {" - " vec2 tex_size = vec2(textureSize(image, 0));" - " vec2 uv_blur = mix(floor(uv * tex_size) + 0.5, uv * tex_size, parameters.xy) / tex_size;" - " vec4 c = texture(image, uv_blur);" - " c.rgb *= mix(0.5, 1.0 - fract(uv.y * tex_size.y), parameters.z) * 2.0; //scanline\n" - " c = mix(vec4(0.5), c, parameters.w); //contrast\n" - " color = c;" - "}" -}; -// clang-format on - -const int tigr_default_fx_gl_fs_size = (int)sizeof(tigr_default_fx_gl_fs) - 1; - -#endif - -//////// End of inlined file: tigr_upscale_gl_fs.h //////// - - -//////// Start of inlined file: tigr_bitmaps.c //////// - -//#include "tigr_internal.h" -#include <stdlib.h> -#include <string.h> - -// Expands 0-255 into 0-256 -#define EXPAND(X) ((X) + ((X) > 0)) - -#define CLIP0(CX, X, X2, W) \ - if (X < CX) { \ - int D = CX - X; \ - W -= D; \ - X2 += D; \ - X += D; \ - } -#define CLIP1(X, DW, W) \ - if (X + W > DW) \ - W = DW - X; -#define CLIP() \ - CLIP0(dst->cx, dx, sx, w); \ - CLIP0(dst->cy, dy, sy, h); \ - CLIP0(0, sx, dx, w); \ - CLIP0(0, sy, dy, h); \ - CLIP1(dx, dst->cx + cw, w); \ - CLIP1(dy, dst->cy + ch, h); \ - CLIP1(sx, src->w, w); \ - CLIP1(sy, src->h, h); \ - if (w <= 0 || h <= 0) \ - return - -Tigr* tigrBitmap2(int w, int h, int extra) { - Tigr* tigr = (Tigr*)calloc(1, sizeof(Tigr) + extra); - tigr->w = w; - tigr->h = h; - tigr->cw = -1; - tigr->ch = -1; - tigr->pix = (TPixel*)calloc(w * h, sizeof(TPixel)); - tigr->blitMode = TIGR_BLEND_ALPHA; - return tigr; -} - -Tigr* tigrBitmap(int w, int h) { - return tigrBitmap2(w, h, 0); -} - -#ifdef TIGR_HEADLESS -void tigrFree(Tigr* bmp) { - free(bmp->pix); - free(bmp); -} -#endif // TIGR_HEADLESS - - -void tigrResize(Tigr* bmp, int w, int h) { - if (bmp->w == w && bmp->h == h) { - return; - } - - int y, cw, ch; - TPixel* newpix = (TPixel*)calloc(w * h, sizeof(TPixel)); - cw = (w < bmp->w) ? w : bmp->w; - ch = (h < bmp->h) ? h : bmp->h; - - // Copy any old data across. - for (y = 0; y < ch; y++) - memcpy(newpix + y * w, bmp->pix + y * bmp->w, cw * sizeof(TPixel)); - - free(bmp->pix); - bmp->pix = newpix; - bmp->w = w; - bmp->h = h; -} - -#ifndef TIGR_HEADLESS -int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH) { - if (bmpW == 0 || bmpH == 0) { - tigrError(0, "Invalid bitmap size, (%dx%d)", bmpW, bmpH); - } - // We want it as big as possible in the window, but still - // maintaining the correct aspect ratio, and always - // having an integer pixel size. - int scale = 0; - for (;;) { - scale++; - if (bmpW * scale > areaW || bmpH * scale > areaH) { - scale--; - break; - } - } - return (scale > 1) ? scale : 1; -} -#endif // TIGR_HEADLESS - -int tigrEnforceScale(int scale, int flags) { - if ((flags & TIGR_4X) && scale < 4) - scale = 4; - if ((flags & TIGR_3X) && scale < 3) - scale = 3; - if ((flags & TIGR_2X) && scale < 2) - scale = 2; - return scale; -} - -void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]) { - // Center the image on screen at this scale. - out[0] = (windowW - bmp->w * scale) / 2; - out[1] = (windowH - bmp->h * scale) / 2; - out[2] = out[0] + bmp->w * scale; - out[3] = out[1] + bmp->h * scale; -} - -void tigrClear(Tigr* bmp, TPixel color) { - int count = bmp->w * bmp->h; - int n; - for (n = 0; n < count; n++) - bmp->pix[n] = color; -} - -void tigrFill(Tigr* bmp, int x, int y, int w, int h, TPixel color) { - TPixel* td; - int dt, i; - - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - if (x + w > bmp->w) { - w = bmp->w - x; - } - if (y + h > bmp->h) { - h = bmp->h - y; - } - if (w <= 0 || h <= 0) - return; - - td = &bmp->pix[y * bmp->w + x]; - dt = bmp->w; - do { - for (i = 0; i < w; i++) - td[i] = color; - td += dt; - } while (--h); -} - -void tigrLine(Tigr* bmp, int x0, int y0, int x1, int y1, TPixel color) { - int sx, sy, dx, dy, err, e2; - dx = abs(x1 - x0); - dy = abs(y1 - y0); - if (x0 < x1) - sx = 1; - else - sx = -1; - if (y0 < y1) - sy = 1; - else - sy = -1; - err = dx - dy; - - do { - tigrPlot(bmp, x0, y0, color); - e2 = 2 * err; - if (e2 > -dy) { - err -= dy; - x0 += sx; - } - if (e2 < dx) { - err += dx; - y0 += sy; - } - } while (x0 != x1 || y0 != y1); -} - -void tigrFillRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) { - x += 1; - y += 1; - w -= 2; - h -= 2; - - int cx = bmp->cx; - int cy = bmp->cy; - int cw = bmp->cw >= 0 ? bmp->cw : bmp->w; - int ch = bmp->ch >= 0 ? bmp->ch : bmp->h; - - if (x < cx) { - w += (x - cx); - x = cx; - } - if (y < cy) { - h += (y - cy); - y = cy; - } - if (x + w > cx + cw) { - w -= (x + w) - (cx + cw); - } - if (y + h > cy + ch) { - h -= (y + h) - (cy + ch); - } - if (w <= 0 || h <= 0) - return; - - TPixel* td = &bmp->pix[y * bmp->w + x]; - int dt = bmp->w; - int xa = EXPAND(color.a); - int a = xa * xa; - - do { - for (int i = 0; i < w; i++) { - td[i].r += (unsigned char)((color.r - td[i].r) * a >> 16); - td[i].g += (unsigned char)((color.g - td[i].g) * a >> 16); - td[i].b += (unsigned char)((color.b - td[i].b) * a >> 16); - td[i].a += (bmp->blitMode) * (unsigned char)((color.a - td[i].a) * a >> 16); - } - td += dt; - } while (--h); -} - -void tigrRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) { - int x1, y1; - if (w <= 0 || h <= 0) { - return; - } - - if (w == 1) { - tigrLine(bmp, x, y, x, y + h, color); - } else if (h == 1) { - tigrLine(bmp, x, y, x + w, y, color); - } else { - x1 = x + w - 1; - y1 = y + h - 1; - tigrLine(bmp, x, y, x1, y, color); - tigrLine(bmp, x1, y, x1, y1, color); - tigrLine(bmp, x1, y1, x, y1, color); - tigrLine(bmp, x, y1, x, y, color); - } -} - -void tigrFillCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) { - if (r <= 0) { - return; - } - - int E = 1 - r; - int dx = 0; - int dy = -2 * r; - int x = 0; - int y = r; - - tigrLine(bmp, x0 - r + 1, y0, x0 + r, y0, color); - - while (x < y - 1) { - x++; - - if (E >= 0) { - y--; - dy += 2; - E += dy; - tigrLine(bmp, x0 - x + 1, y0 + y, x0 + x, y0 + y, color); - tigrLine(bmp, x0 - x + 1, y0 - y, x0 + x, y0 - y, color); - } - - dx += 2; - E += dx + 1; - - if (x != y) { - tigrLine(bmp, x0 - y + 1, y0 + x, x0 + y, y0 + x, color); - tigrLine(bmp, x0 - y + 1, y0 - x, x0 + y, y0 - x, color); - } - } -} - -void tigrCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) { - int E = 1 - r; - int dx = 0; - int dy = -2 * r; - int x = 0; - int y = r; - - tigrPlot(bmp, x0, y0 + r, color); - tigrPlot(bmp, x0, y0 - r, color); - tigrPlot(bmp, x0 + r, y0, color); - tigrPlot(bmp, x0 - r, y0, color); - - while (x < y - 1) { - x++; - - if (E >= 0) { - y--; - dy += 2; - E += dy; - } - - dx += 2; - E += dx + 1; - - tigrPlot(bmp, x0 + x, y0 + y, color); - tigrPlot(bmp, x0 - x, y0 + y, color); - tigrPlot(bmp, x0 + x, y0 - y, color); - tigrPlot(bmp, x0 - x, y0 - y, color); - - if (x != y) { - tigrPlot(bmp, x0 + y, y0 + x, color); - tigrPlot(bmp, x0 - y, y0 + x, color); - tigrPlot(bmp, x0 + y, y0 - x, color); - tigrPlot(bmp, x0 - y, y0 - x, color); - } - } -} - -TPixel tigrGet(Tigr* bmp, int x, int y) { - TPixel empty = { 0, 0, 0, 0 }; - if (x >= 0 && y >= 0 && x < bmp->w && y < bmp->h) - return bmp->pix[y * bmp->w + x]; - return empty; -} - -void tigrPlot(Tigr* bmp, int x, int y, TPixel pix) { - int xa, i, a; - - int cx = bmp->cx; - int cy = bmp->cy; - int cw = bmp->cw >= 0 ? bmp->cw : bmp->w; - int ch = bmp->ch >= 0 ? bmp->ch : bmp->h; - - if (x >= cx && y >= cy && x < cx + cw && y < cy + ch) { - xa = EXPAND(pix.a); - a = xa * xa; - i = y * bmp->w + x; - - bmp->pix[i].r += (unsigned char)((pix.r - bmp->pix[i].r) * a >> 16); - bmp->pix[i].g += (unsigned char)((pix.g - bmp->pix[i].g) * a >> 16); - bmp->pix[i].b += (unsigned char)((pix.b - bmp->pix[i].b) * a >> 16); - bmp->pix[i].a += (bmp->blitMode) * (unsigned char)((pix.a - bmp->pix[i].a) * a >> 16); - } -} - -void tigrClip(Tigr* bmp, int cx, int cy, int cw, int ch) { - bmp->cx = cx; - bmp->cy = cy; - bmp->cw = cw; - bmp->ch = ch; -} - -void tigrBlit(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h) { - int cw = dst->cw >= 0 ? dst->cw : dst->w; - int ch = dst->ch >= 0 ? dst->ch : dst->h; - - CLIP(); - - TPixel* ts = &src->pix[sy * src->w + sx]; - TPixel* td = &dst->pix[dy * dst->w + dx]; - int st = src->w; - int dt = dst->w; - do { - memcpy(td, ts, w * sizeof(TPixel)); - ts += st; - td += dt; - } while (--h); -} - -void tigrBlitTint(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint) { - int cw = dst->cw >= 0 ? dst->cw : dst->w; - int ch = dst->ch >= 0 ? dst->ch : dst->h; - - CLIP(); - - int xr = EXPAND(tint.r); - int xg = EXPAND(tint.g); - int xb = EXPAND(tint.b); - int xa = EXPAND(tint.a); - - TPixel* ts = &src->pix[sy * src->w + sx]; - TPixel* td = &dst->pix[dy * dst->w + dx]; - int st = src->w; - int dt = dst->w; - do { - for (int x = 0; x < w; x++) { - unsigned r = (xr * ts[x].r) >> 8; - unsigned g = (xg * ts[x].g) >> 8; - unsigned b = (xb * ts[x].b) >> 8; - unsigned a = xa * EXPAND(ts[x].a); - td[x].r += (unsigned char)((r - td[x].r) * a >> 16); - td[x].g += (unsigned char)((g - td[x].g) * a >> 16); - td[x].b += (unsigned char)((b - td[x].b) * a >> 16); - td[x].a += (dst->blitMode) * (unsigned char)((ts[x].a - td[x].a) * a >> 16); - } - ts += st; - td += dt; - } while (--h); -} - -void tigrBlitAlpha(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, float alpha) { - alpha = (alpha < 0) ? 0 : (alpha > 1 ? 1 : alpha); - tigrBlitTint(dst, src, dx, dy, sx, sy, w, h, tigrRGBA(0xff, 0xff, 0xff, (unsigned char)(alpha * 255))); -} - -void tigrBlitMode(Tigr* dst, int mode) { - dst->blitMode = mode; -} - -#undef CLIP0 -#undef CLIP1 -#undef CLIP - -//////// End of inlined file: tigr_bitmaps.c //////// - -//////// Start of inlined file: tigr_loadpng.c //////// - -//#include "tigr_internal.h" -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -typedef struct { - const unsigned char *p, *end; -} PNG; - -static unsigned get32(const unsigned char* v) { - return (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3]; -} - -static const unsigned char* find(PNG* png, const char* chunk, unsigned minlen) { - const unsigned char* start; - while (png->p < png->end) { - unsigned len = get32(png->p + 0); - start = png->p; - png->p += len + 12; - if (memcmp(start + 4, chunk, 4) == 0 && len >= minlen && png->p <= png->end) - return start + 8; - } - - return NULL; -} - -static unsigned char paeth(unsigned char a, unsigned char b, unsigned char c) { - int p = a + b - c; - int pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); - return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; -} - -static int rowBytes(int w, int bipp) { - int rowBits = w * bipp; - return rowBits / 8 + ((rowBits % 8) ? 1 : 0); -} - -static int unfilter(int w, int h, int bipp, unsigned char* raw) { - int len = rowBytes(w, bipp); - int bpp = rowBytes(1, bipp); - int x, y; - unsigned char* first = (unsigned char*)malloc(len + 1); - memset(first, 0, len + 1); - unsigned char* prev = first; - for (y = 0; y < h; y++, prev = raw, raw += len) { -#define LOOP(A, B) \ - for (x = 0; x < bpp; x++) \ - raw[x] += A; \ - for (; x < len; x++) \ - raw[x] += B; \ - break - switch (*raw++) { - case 0: - break; - case 1: - LOOP(0, raw[x - bpp]); - case 2: - LOOP(prev[x], prev[x]); - case 3: - LOOP(prev[x] / 2, (raw[x - bpp] + prev[x]) / 2); - case 4: - LOOP(prev[x], paeth(raw[x - bpp], prev[x], prev[x - bpp])); - default: - return 0; - } -#undef LOOP - } - free(first); - return 1; -} - -static void convert(int bypp, int w, int h, const unsigned char* src, TPixel* dest, const unsigned char* trns) { - int x, y; - for (y = 0; y < h; y++) { - src++; // skip filter byte - for (x = 0; x < w; x++, src += bypp) { - switch (bypp) { - case 1: { - unsigned char c = src[0]; - if (trns && c == *trns) { - *dest++ = tigrRGBA(c, c, c, 0); - break; - } else { - *dest++ = tigrRGB(c, c, c); - break; - } - } - case 2: - *dest++ = tigrRGBA(src[0], src[0], src[0], src[1]); - break; - case 3: { - unsigned char r = src[0]; - unsigned char g = src[1]; - unsigned char b = src[2]; - if (trns && trns[1] == r && trns[3] == g && trns[5] == b) { - *dest++ = tigrRGBA(r, g, b, 0); - break; - } else { - *dest++ = tigrRGB(r, g, b); - break; - } - } - case 4: - *dest++ = tigrRGBA(src[0], src[1], src[2], src[3]); - break; - } - } - } -} - -static void depalette(int w, - int h, - unsigned char* src, - TPixel* dest, - int bipp, - const unsigned char* plte, - const unsigned char* trns, - int trnsSize) { - int x, y, c; - unsigned char alpha; - int mask = 0, len = 0; - - switch (bipp) { - case 4: - mask = 15; - len = 1; - break; - case 2: - mask = 3; - len = 3; - break; - case 1: - mask = 1; - len = 7; - } - - for (y = 0; y < h; y++) { - src++; // skip filter byte - for (x = 0; x < w; x++) { - if (bipp == 8) { - c = *src++; - } else { - int pos = x & len; - c = (src[0] >> ((len - pos) * bipp)) & mask; - if (pos == len) { - src++; - } - } - alpha = 255; - if (c < trnsSize) { - alpha = trns[c]; - } - *dest++ = tigrRGBA(plte[c * 3 + 0], plte[c * 3 + 1], plte[c * 3 + 2], alpha); - } - } -} - -#define FAIL() \ - { \ - errno = EINVAL; \ - goto err; \ - } -#define CHECK(X) \ - if (!(X)) \ - FAIL() - -static int outsize(Tigr* bmp, int bipp) { - return (rowBytes(bmp->w, bipp) + 1) * bmp->h; -} - -static Tigr* tigrLoadPng(PNG* png) { - const unsigned char *ihdr, *idat, *plte, *trns, *first; - int trnsSize = 0; - int depth, ctype, bipp; - int datalen = 0; - unsigned char *data = NULL, *out; - Tigr* bmp = NULL; - - CHECK(memcmp(png->p, "\211PNG\r\n\032\n", 8) == 0); // PNG signature - png->p += 8; - first = png->p; - - // Read IHDR - ihdr = find(png, "IHDR", 13); - CHECK(ihdr); - depth = ihdr[8]; - ctype = ihdr[9]; - switch (ctype) { - case 0: - bipp = depth; - break; // greyscale - case 2: - bipp = 3 * depth; - break; // RGB - case 3: - bipp = depth; - break; // paletted - case 4: - bipp = 2 * depth; - break; // grey+alpha - case 6: - bipp = 4 * depth; - break; // RGBA - default: - FAIL(); - } - - // Allocate bitmap (+1 width to save room for stupid PNG filter bytes) - bmp = tigrBitmap(get32(ihdr + 0) + 1, get32(ihdr + 4)); - CHECK(bmp); - bmp->w--; - - // We support 8-bit color components and 1, 2, 4 and 8 bit palette formats. - // No interlacing, or wacky filter types. - CHECK((depth != 16) && ihdr[10] == 0 && ihdr[11] == 0 && ihdr[12] == 0); - - // Join IDAT chunks. - for (idat = find(png, "IDAT", 0); idat; idat = find(png, "IDAT", 0)) { - unsigned len = get32(idat - 8); - data = (unsigned char*)realloc(data, datalen + len); - if (!data) - break; - - memcpy(data + datalen, idat, len); - datalen += len; - } - - // Find palette. - png->p = first; - plte = find(png, "PLTE", 0); - - // Find transparency info. - png->p = first; - trns = find(png, "tRNS", 0); - if (trns) { - trnsSize = get32(trns - 8); - } - - CHECK(data && datalen >= 6); - CHECK((data[0] & 0x0f) == 0x08 // compression method (RFC 1950) - && (data[0] & 0xf0) <= 0x70 // window size - && (data[1] & 0x20) == 0); // preset dictionary present - - out = (unsigned char*)bmp->pix + outsize(bmp, 32) - outsize(bmp, bipp); - CHECK(tigrInflate(out, outsize(bmp, bipp), data + 2, datalen - 6)); - CHECK(unfilter(bmp->w, bmp->h, bipp, out)); - - if (ctype == 3) { - CHECK(plte); - depalette(bmp->w, bmp->h, out, bmp->pix, bipp, plte, trns, trnsSize); - } else { - CHECK(bipp % 8 == 0); - convert(bipp / 8, bmp->w, bmp->h, out, bmp->pix, trns); - } - - free(data); - return bmp; - -err: - if (data) - free(data); - if (bmp) - tigrFree(bmp); - return NULL; -} - -#undef CHECK -#undef FAIL - -Tigr* tigrLoadImageMem(const void* data, int length) { - PNG png; - png.p = (unsigned char*)data; - png.end = (unsigned char*)data + length; - return tigrLoadPng(&png); -} - -Tigr* tigrLoadImage(const char* fileName) { - int len; - void* data; - Tigr* bmp; - - data = tigrReadFile(fileName, &len); - if (!data) - return NULL; - - bmp = tigrLoadImageMem(data, len); - free(data); - return bmp; -} - -//////// End of inlined file: tigr_loadpng.c //////// - -//////// Start of inlined file: tigr_savepng.c //////// - -//#include "tigr_internal.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -typedef struct { - unsigned crc, adler, bits, prev, runlen; - FILE* out; - unsigned crcTable[256]; -} Save; - -static const unsigned crctable[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, - 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; - -static void put(Save* s, unsigned v) { - fputc(v, s->out); - s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v & 15)]; - s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v >> 4)]; -} - -static void updateAdler(Save* s, unsigned v) { - unsigned s1 = s->adler & 0xffff, s2 = (s->adler >> 16) & 0xffff; - s1 = (s1 + v) % 65521; - s2 = (s2 + s1) % 65521; - s->adler = (s2 << 16) + s1; -} - -static void put32(Save* s, unsigned v) { - put(s, (v >> 24) & 0xff); - put(s, (v >> 16) & 0xff); - put(s, (v >> 8) & 0xff); - put(s, v & 0xff); -} - -void putbits(Save* s, unsigned data, unsigned bitcount) { - while (bitcount--) { - unsigned prev = s->bits; - s->bits = (s->bits >> 1) | ((data & 1) << 7); - data >>= 1; - if (prev & 1) { - put(s, s->bits); - s->bits = 0x80; - } - } -} - -void putbitsr(Save* s, unsigned data, unsigned bitcount) { - while (bitcount--) - putbits(s, data >> bitcount, 1); -} - -static void begin(Save* s, const char* id, unsigned len) { - put32(s, len); - s->crc = 0xffffffff; - put(s, id[0]); - put(s, id[1]); - put(s, id[2]); - put(s, id[3]); -} - -static void literal(Save* s, unsigned v) { - // Encode a literal/length using the built-in tables. - // Could do better with a custom table but whatever. - if (v < 144) - putbitsr(s, 0x030 + v - 0, 8); - else if (v < 256) - putbitsr(s, 0x190 + v - 144, 9); - else if (v < 280) - putbitsr(s, 0x000 + v - 256, 7); - else - putbitsr(s, 0x0c0 + v - 280, 8); -} - -static void encodelen(Save* s, unsigned code, unsigned bits, unsigned len) { - literal(s, code + (len >> bits)); - putbits(s, len, bits); - putbits(s, 0, 5); -} - -static void endrun(Save* s) { - s->runlen--; - literal(s, s->prev); - - if (s->runlen >= 67) - encodelen(s, 277, 4, s->runlen - 67); - else if (s->runlen >= 35) - encodelen(s, 273, 3, s->runlen - 35); - else if (s->runlen >= 19) - encodelen(s, 269, 2, s->runlen - 19); - else if (s->runlen >= 11) - encodelen(s, 265, 1, s->runlen - 11); - else if (s->runlen >= 3) - encodelen(s, 257, 0, s->runlen - 3); - else - while (s->runlen--) - literal(s, s->prev); -} - -static void encodeByte(Save* s, unsigned char v) { - updateAdler(s, v); - - // Simple RLE compression. We could do better by doing a search - // to find matches, but this works pretty well TBH. - if (s->prev == v && s->runlen < 115) { - s->runlen++; - } else { - if (s->runlen) - endrun(s); - - s->prev = v; - s->runlen = 1; - } -} - -static void savePngHeader(Save* s, Tigr* bmp) { - fwrite("\211PNG\r\n\032\n", 8, 1, s->out); - begin(s, "IHDR", 13); - put32(s, bmp->w); - put32(s, bmp->h); - put(s, 8); // bit depth - put(s, 6); // RGBA - put(s, 0); // compression (deflate) - put(s, 0); // filter (standard) - put(s, 0); // interlace off - put32(s, ~s->crc); -} - -static long savePngData(Save* s, Tigr* bmp, long dataPos) { - int x, y; - long dataSize; - begin(s, "IDAT", 0); - put(s, 0x08); // zlib compression method - put(s, 0x1d); // zlib compression flags - putbits(s, 3, 3); // zlib last block + fixed dictionary - for (y = 0; y < bmp->h; y++) { - TPixel* row = &bmp->pix[y * bmp->w]; - TPixel prev = tigrRGBA(0, 0, 0, 0); - - encodeByte(s, 1); // sub filter - for (x = 0; x < bmp->w; x++) { - encodeByte(s, row[x].r - prev.r); - encodeByte(s, row[x].g - prev.g); - encodeByte(s, row[x].b - prev.b); - encodeByte(s, row[x].a - prev.a); - prev = row[x]; - } - } - endrun(s); - literal(s, 256); // terminator - while (s->bits != 0x80) - putbits(s, 0, 1); - put32(s, s->adler); - dataSize = (ftell(s->out) - dataPos) - 8; - put32(s, ~s->crc); - return dataSize; -} - -int tigrSaveImage(const char* fileName, Tigr* bmp) { - Save s; - long dataPos, dataSize, err; - - // TODO - unicode? - FILE* out = fopen(fileName, "wb"); - if (!out) - return 1; - - s.out = out; - s.adler = 1; - s.bits = 0x80; - s.prev = 0xffff; - s.runlen = 0; - - savePngHeader(&s, bmp); - dataPos = ftell(s.out); - dataSize = savePngData(&s, bmp, dataPos); - - // End chunk. - begin(&s, "IEND", 0); - put32(&s, ~s.crc); - - // Write back payload size. - fseek(out, dataPos, SEEK_SET); - put32(&s, dataSize); - - err = ferror(out); - fclose(out); - return !err; -} - -//////// End of inlined file: tigr_savepng.c //////// - -//////// Start of inlined file: tigr_inflate.c //////// - -//#include "tigr_internal.h" -#include <stdlib.h> -#include <setjmp.h> - -typedef struct { - unsigned bits, count; - const unsigned char *in, *inend; - unsigned char *out, *outend; - jmp_buf jmp; - unsigned litcodes[288], distcodes[32], lencodes[19]; - int tlit, tdist, tlen; -} State; - -#define FAIL() longjmp(s->jmp, 1) -#define CHECK(X) \ - if (!(X)) \ - FAIL() - -// Built-in DEFLATE standard tables. -static char order[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; -static char lenBits[29 + 2] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; -static int lenBase[29 + 2] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; -static char distBits[30 + 2] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; -static int distBase[30 + 2] = { - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, - 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 -}; - -// Table to bit-reverse a byte. -static const unsigned char reverseTable[256] = { -#define R2(n) n, n + 128, n + 64, n + 192 -#define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48) -#define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12) - R6(0), R6(2), R6(1), R6(3) -}; - -static unsigned rev16(unsigned n) { - return (reverseTable[n & 0xff] << 8) | reverseTable[(n >> 8) & 0xff]; -} - -static int bits(State* s, int n) { - int v = s->bits & ((1 << n) - 1); - s->bits >>= n; - s->count -= n; - while (s->count < 16) { - CHECK(s->in != s->inend); - s->bits |= (*s->in++) << s->count; - s->count += 8; - } - return v; -} - -static unsigned char* emit(State* s, int len) { - s->out += len; - CHECK(s->out <= s->outend); - return s->out - len; -} - -static void copy(State* s, const unsigned char* src, int len) { - unsigned char* dest = emit(s, len); - while (len--) - *dest++ = *src++; -} - -static int build(State* s, unsigned* tree, unsigned char* lens, unsigned int symcount) { - unsigned int codes[16], first[16], counts[16] = { 0 }; - - // Frequency count. - for (unsigned int n = 0; n < symcount; n++) - counts[lens[n]]++; - - // Distribute codes. - counts[0] = codes[0] = first[0] = 0; - for (unsigned int n = 1; n <= 15; n++) { - codes[n] = (codes[n - 1] + counts[n - 1]) << 1; - first[n] = first[n - 1] + counts[n - 1]; - } - CHECK(first[15] + counts[15] <= symcount); - - // Insert keys into the tree for each symbol. - for (unsigned int n = 0; n < symcount; n++) { - int len = lens[n]; - if (len != 0) { - unsigned code = codes[len]++, slot = first[len]++; - tree[slot] = (code << (32 - len)) | (n << 4) | len; - } - } - - return first[15]; -} - -static int decode(State* s, unsigned tree[], int max) { - // Find the next prefix code. - unsigned lo = 0, hi = max, key; - unsigned search = (rev16(s->bits) << 16) | 0xffff; - while (lo < hi) { - unsigned guess = (lo + hi) / 2; - if (search < tree[guess]) - hi = guess; - else - lo = guess + 1; - } - - // Pull out the key and check it. - key = tree[lo - 1]; - CHECK(((search ^ key) >> (32 - (key & 0xf))) == 0); - - bits(s, key & 0xf); - return (key >> 4) & 0xfff; -} - -static void run(State* s, int sym) { - int length = bits(s, lenBits[sym]) + lenBase[sym]; - int dsym = decode(s, s->distcodes, s->tdist); - int offs = bits(s, distBits[dsym]) + distBase[dsym]; - copy(s, s->out - offs, length); -} - -static void block(State* s) { - for (;;) { - int sym = decode(s, s->litcodes, s->tlit); - if (sym < 256) - *emit(s, 1) = (unsigned char)sym; - else if (sym > 256) - run(s, sym - 257); - else - break; - } -} - -static void stored(State* s) { - // Uncompressed data block. - int len; - bits(s, s->count & 7); - len = bits(s, 16); - CHECK(((len ^ s->bits) & 0xffff) == 0xffff); - CHECK(s->in + len <= s->inend); - - copy(s, s->in, len); - s->in += len; - bits(s, 16); -} - -static void fixed(State* s) { - // Fixed set of Huffman codes. - int n; - unsigned char lens[288 + 32]; - for (n = 0; n <= 143; n++) - lens[n] = 8; - for (n = 144; n <= 255; n++) - lens[n] = 9; - for (n = 256; n <= 279; n++) - lens[n] = 7; - for (n = 280; n <= 287; n++) - lens[n] = 8; - for (n = 0; n < 32; n++) - lens[288 + n] = 5; - - // Build lit/dist trees. - s->tlit = build(s, s->litcodes, lens, 288); - s->tdist = build(s, s->distcodes, lens + 288, 32); -} - -static void dynamic(State* s) { - int n, i, nlit, ndist, nlen; - unsigned char lenlens[19] = { 0 }, lens[288 + 32]; - nlit = 257 + bits(s, 5); - ndist = 1 + bits(s, 5); - nlen = 4 + bits(s, 4); - for (n = 0; n < nlen; n++) - lenlens[(int) order[n]] = (unsigned char)bits(s, 3); - - // Build the tree for decoding code lengths. - s->tlen = build(s, s->lencodes, lenlens, 19); - - // Decode code lengths. - for (n = 0; n < nlit + ndist;) { - int sym = decode(s, s->lencodes, s->tlen); - switch (sym) { - case 16: - for (i = 3 + bits(s, 2); i; i--, n++) - lens[n] = lens[n - 1]; - break; - case 17: - for (i = 3 + bits(s, 3); i; i--, n++) - lens[n] = 0; - break; - case 18: - for (i = 11 + bits(s, 7); i; i--, n++) - lens[n] = 0; - break; - default: - lens[n++] = (unsigned char)sym; - break; - } - } - - // Build lit/dist trees. - s->tlit = build(s, s->litcodes, lens, nlit); - s->tdist = build(s, s->distcodes, lens + nlit, ndist); -} - -int tigrInflate(void* out, unsigned outlen, const void* in, unsigned inlen) { - int last; - State* s = (State*)calloc(1, sizeof(State)); - - // We assume we can buffer 2 extra bytes from off the end of 'in'. - s->in = (unsigned char*)in; - s->inend = s->in + inlen + 2; - s->out = (unsigned char*)out; - s->outend = s->out + outlen; - s->bits = 0; - s->count = 0; - bits(s, 0); - - if (setjmp(s->jmp) == 1) { - free(s); - return 0; - } - - do { - last = bits(s, 1); - switch (bits(s, 2)) { - case 0: - stored(s); - break; - case 1: - fixed(s); - block(s); - break; - case 2: - dynamic(s); - block(s); - break; - case 3: - FAIL(); - } - } while (!last); - - free(s); - return 1; -} - -#undef CHECK -#undef FAIL - -//////// End of inlined file: tigr_inflate.c //////// - -//////// Start of inlined file: tigr_print.c //////// - -//#include "tigr_internal.h" -//////// Start of inlined file: tigr_font.h //////// - -// Auto-generated by incbin.pl from font.png - -const unsigned char tigr_font[] = { - 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, - 0x00,0x00,0x00,0xfd,0x00,0x00,0x00,0x5c,0x08,0x03,0x00,0x00,0x00,0x92,0xab,0x43, - 0x85,0x00,0x00,0x03,0x00,0x50,0x4c,0x54,0x45,0x5f,0x53,0x87,0x00,0x00,0x00,0x54, - 0x54,0x54,0xff,0xff,0xfa,0x31,0x2a,0x42,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, - 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, - 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, - 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x4a,0x44,0xb7,0x5a,0x00,0x00,0x00, - 0x01,0x74,0x52,0x4e,0x53,0x00,0x40,0xe6,0xd8,0x66,0x00,0x00,0x00,0x01,0x62,0x4b, - 0x47,0x44,0x00,0x88,0x05,0x1d,0x48,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00, - 0x00,0x0b,0x13,0x00,0x00,0x0b,0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07, - 0x74,0x49,0x4d,0x45,0x07,0xe5,0x04,0x1c,0x16,0x0f,0x2b,0x56,0x9d,0x54,0x3b,0x00, - 0x00,0x0b,0x7c,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0x5c,0x89,0x9a,0xec,0xac,0x0a, - 0x04,0xf5,0xfd,0x9f,0xf9,0x4e,0x94,0xa5,0x40,0x93,0x98,0xee,0x3e,0x73,0xe7,0xfb, - 0x67,0xce,0xd2,0x66,0x31,0x6a,0x29,0x51,0x4a,0x20,0xd4,0x7e,0xf3,0x1f,0x6a,0x34, - 0xfe,0xb4,0x5a,0xf9,0xf8,0x7f,0x24,0x54,0xf9,0xf8,0xf7,0x75,0xeb,0xeb,0x97,0xe0, - 0xd4,0x73,0x92,0x64,0xd7,0xab,0x90,0xa4,0xc3,0x91,0x59,0xb2,0x56,0x29,0x50,0xce, - 0xed,0xfa,0x71,0x74,0x94,0xd7,0x2f,0xd8,0xf5,0x7c,0x0e,0xf9,0xb1,0x74,0x69,0x15, - 0x13,0x14,0xfd,0x09,0xf4,0xd2,0x08,0x3e,0xfe,0xf6,0x53,0x86,0x9c,0xa5,0x8d,0xcc, - 0xd2,0x45,0x9a,0x2c,0xd0,0x7f,0x65,0x63,0x45,0xc1,0xa3,0x00,0xae,0x78,0x7d,0xb4, - 0xb9,0x9f,0xf3,0x57,0xb9,0x7a,0xfd,0x38,0xc3,0x7c,0x90,0x36,0xad,0xba,0x68,0x75, - 0x1c,0x8a,0x7e,0x05,0x3d,0x1f,0xcf,0xfa,0x18,0x0c,0xd8,0xad,0xff,0x96,0x9e,0xa3, - 0xd7,0xc0,0x36,0xec,0x90,0xe9,0x0c,0x7d,0x1d,0x68,0x6c,0x6c,0x7b,0x01,0x5f,0xa5, - 0x0d,0x74,0x23,0x25,0xe9,0xf5,0xd1,0xbb,0xbd,0x22,0x62,0xc1,0xa5,0xd7,0x53,0x2a, - 0x02,0x38,0x9a,0x35,0x04,0xa4,0x8f,0x90,0x16,0xf9,0x0a,0x7a,0xfa,0x7a,0xb6,0x8f, - 0xe9,0xd1,0x09,0x63,0x6c,0x8f,0x46,0x70,0xd1,0x7e,0x1d,0x08,0x07,0x20,0x41,0xdf, - 0x81,0x15,0x6b,0x03,0x11,0x1e,0xf6,0x3e,0xb1,0xde,0xec,0xc3,0x22,0x12,0x31,0x46, - 0x4d,0x9f,0xfd,0xaa,0x41,0x7b,0x5d,0xf3,0xb3,0xe7,0xe3,0x5a,0x3d,0x1d,0xbd,0xd6, - 0xff,0x1f,0xe0,0xa9,0x4b,0x48,0x2f,0x6c,0x0c,0xc2,0x78,0xe4,0x25,0xf4,0x32,0x6a, - 0x2a,0xf9,0xac,0xaf,0xeb,0xa8,0xa6,0x8e,0x54,0x87,0x27,0x8c,0xfd,0xd1,0x5f,0x63, - 0xb8,0xbc,0x39,0xe4,0xa3,0x63,0x12,0x2e,0x6f,0x49,0xbf,0xfe,0xd5,0x60,0xc6,0xf7, - 0x3d,0x4b,0xba,0xe6,0xb3,0x73,0xd6,0xeb,0xa3,0x11,0x2a,0xec,0x15,0x5f,0x83,0xf1, - 0xc8,0x53,0xf4,0xec,0xaf,0x50,0x92,0xfc,0xa3,0xdc,0x31,0x24,0x7a,0x8e,0xe8,0x0f, - 0xc1,0x18,0xd5,0xae,0x24,0x5f,0x85,0xa6,0x04,0xf4,0xde,0x8d,0x84,0xa8,0xb3,0xa4, - 0x6b,0x3e,0x3b,0xf7,0xfb,0x0d,0x85,0x7d,0x74,0xb6,0x74,0xe8,0xa8,0xe2,0x8d,0xb1, - 0x07,0xc9,0xaf,0xa3,0xdc,0xca,0x3a,0x24,0x32,0xa8,0x8e,0x9e,0x48,0x90,0x68,0x43, - 0x1b,0x4e,0xa3,0xa9,0xf5,0x36,0xeb,0x15,0x5a,0xa3,0xca,0x69,0xc8,0x37,0xbd,0xf7, - 0x2a,0xec,0x63,0xce,0x97,0x66,0xf4,0xf4,0x33,0x92,0xdf,0xfb,0x13,0x3b,0x43,0xc6, - 0x9e,0x6d,0xe2,0x8f,0x2b,0xde,0x84,0x5e,0x32,0xfb,0xdc,0x80,0x92,0x2e,0xd7,0x4d, - 0x06,0xf0,0xfd,0x96,0xc9,0x05,0x9f,0xb7,0x74,0xcc,0x4d,0xc7,0x73,0x30,0xe7,0x17, - 0xa2,0xd7,0xc7,0x1e,0x24,0x7f,0x4c,0x7e,0x36,0x9e,0x76,0x7a,0x48,0x98,0xbe,0xd0, - 0x32,0xf3,0xe4,0xf5,0x1e,0x06,0xa3,0xe7,0x28,0x92,0xb9,0x67,0x65,0x11,0x52,0x4d, - 0xf5,0xfa,0x91,0x3d,0x9c,0x5b,0xfe,0x42,0xf8,0x3c,0xe4,0x6f,0xd6,0xf1,0xa1,0x01, - 0x2f,0xa3,0xcf,0x05,0x9d,0x9d,0x7a,0xce,0x36,0x67,0x6a,0x51,0x1a,0xda,0x69,0x01, - 0xef,0xa6,0xcd,0x3a,0x3e,0xa3,0x7f,0x4d,0xf2,0x3f,0x83,0x3e,0x4a,0xc3,0x3f,0x44, - 0x9f,0x4b,0x4f,0x55,0xfd,0x7f,0xd0,0xaf,0x0e,0x7f,0x32,0xfa,0xdf,0xce,0x72,0x54, - 0x01,0xa5,0xac,0x6a,0xa5,0xf3,0x3c,0x65,0xfb,0x7d,0x5d,0x88,0xe0,0x7a,0x5a,0xc0, - 0x74,0xe1,0xca,0xeb,0xf9,0x65,0x85,0x34,0x55,0x3c,0xad,0x7c,0x34,0x1f,0x60,0x53, - 0xaa,0x2f,0x3c,0x35,0x90,0x92,0x21,0x20,0x63,0x3d,0x63,0xd5,0x66,0xce,0x48,0x05, - 0x67,0x12,0x82,0x64,0x44,0xab,0x64,0xd3,0x8c,0xd9,0x16,0xac,0x88,0xde,0xf5,0xfd, - 0xdb,0x8a,0x30,0xd5,0x82,0xf7,0xd1,0x83,0xd6,0x58,0x7c,0xd9,0xe5,0x26,0x7a,0x9b, - 0xa3,0xef,0x7a,0x98,0x6a,0x33,0x53,0xe1,0xbc,0x44,0x11,0xc9,0x48,0x56,0x66,0xc6, - 0xea,0x4d,0xc2,0x18,0x0b,0xa2,0x8f,0xc2,0xb3,0xaa,0x30,0x8d,0xa0,0xe8,0xcc,0xa6, - 0x25,0xad,0xd1,0x33,0x56,0x55,0x55,0xdd,0x57,0x32,0xc6,0xa6,0x60,0x1e,0x60,0x61, - 0x4d,0x6e,0x83,0xbf,0xaa,0x2e,0x07,0x8d,0x0b,0xad,0x76,0x71,0x52,0xc5,0x9a,0x43, - 0x2f,0x98,0xa6,0x9d,0xd0,0x9b,0x9a,0xae,0xe5,0xd8,0x98,0xdf,0xa0,0x77,0xed,0x86, - 0x61,0x11,0x3f,0x45,0x4f,0x2c,0x55,0xb9,0xfe,0xe4,0x10,0x1a,0x39,0xfa,0x63,0x35, - 0xf4,0x35,0x59,0xd0,0x93,0x6b,0xb2,0x31,0x4d,0xe8,0x59,0x15,0x57,0x4d,0xeb,0x44, - 0x4a,0xa6,0xb1,0x0f,0xe8,0x95,0xcb,0xc5,0x57,0x06,0xbb,0x3b,0x14,0xc8,0x71,0x57, - 0x64,0x81,0xde,0xe9,0x93,0xd0,0x4f,0xe1,0x4f,0xd0,0xf1,0x42,0x07,0x08,0x95,0x59, - 0x40,0xdf,0xe5,0xe6,0xec,0xb5,0x4a,0x95,0x26,0xc9,0x0f,0x20,0x16,0xe8,0xf9,0x72, - 0xd6,0x43,0xb4,0xda,0xd6,0x58,0x20,0xd7,0xc4,0x0f,0x16,0x73,0x9b,0x6f,0x10,0x60, - 0x3f,0x39,0xc3,0x10,0x9a,0xbd,0x46,0xcf,0x75,0x28,0xb0,0xb4,0xf1,0x1a,0xce,0xef, - 0xfd,0x0a,0x3d,0x8e,0xf9,0x2e,0x7a,0x7b,0x35,0x72,0xaf,0xac,0xd0,0x03,0x6f,0xb0, - 0x07,0x0a,0x9f,0xf1,0x28,0x25,0x43,0x4b,0xf4,0xc4,0x75,0xb5,0x72,0x05,0xb2,0x91, - 0x49,0x4a,0x24,0x1f,0x79,0x99,0x61,0x10,0xbb,0x3c,0xc9,0xc5,0x99,0xf9,0x84,0xcd, - 0xe4,0xfb,0x8a,0xc9,0xce,0xd9,0x2a,0x28,0x1c,0x27,0x00,0x1f,0x7b,0x5f,0xf1,0x74, - 0x7b,0x0d,0x09,0x58,0x3f,0x6b,0x43,0x33,0x76,0x52,0x61,0x24,0xa4,0x24,0x72,0x12, - 0x48,0x4a,0x24,0x1f,0xe1,0x39,0x21,0x2b,0x85,0xe6,0xeb,0xfd,0xaa,0x1e,0x20,0xab, - 0xc1,0x02,0x75,0xc7,0x2b,0xb2,0x20,0x16,0xba,0x15,0xcf,0x43,0x81,0xb2,0x17,0xc6, - 0xbe,0xdf,0x33,0xaa,0x2e,0x42,0x05,0x19,0x68,0x9c,0xdc,0x75,0x4d,0xf7,0xdf,0x28, - 0xa4,0xab,0xf4,0x3a,0x83,0xb7,0xf1,0x79,0x81,0xeb,0x1c,0x8d,0xce,0xd4,0xf2,0x9f, - 0x87,0xfe,0x9d,0x02,0xff,0xd0,0x3f,0x44,0xff,0xbb,0x59,0x8e,0x4f,0x85,0xbe,0x96, - 0x41,0x17,0xe1,0x6d,0x51,0x1f,0x73,0x6e,0x53,0x2d,0xe3,0xe2,0xf6,0xea,0x58,0x37, - 0x57,0x1b,0x9e,0x88,0x4e,0x0b,0x3b,0x9e,0x78,0xf7,0x53,0xe8,0x6b,0xc5,0x49,0xc9, - 0x16,0x2a,0x99,0x57,0x93,0xc2,0xf4,0xed,0xe8,0x75,0xb5,0x6c,0x1c,0x87,0xec,0x12, - 0x7d,0xdf,0xb5,0x5d,0x51,0x4b,0xa3,0xaa,0xf1,0x36,0x30,0x58,0xd3,0x13,0x60,0xec, - 0x51,0xbf,0x36,0xb5,0xe7,0x82,0xc1,0x42,0x41,0x14,0xaa,0xb9,0x64,0x74,0xb3,0xa2, - 0xdc,0xc8,0x1b,0xe3,0x64,0xf6,0x0e,0x7d,0x57,0x1b,0x16,0x06,0xb3,0x48,0x61,0x59, - 0xfb,0x74,0x71,0x39,0xa0,0x07,0xfd,0x1a,0x38,0x1d,0xef,0x33,0x5a,0xac,0x66,0x0f, - 0x3d,0xb0,0x59,0x35,0x49,0xd5,0x3a,0xec,0x6f,0x9d,0xd6,0xdd,0xec,0x6c,0x99,0x89, - 0x28,0xaa,0x9e,0xab,0x6d,0x8b,0x13,0xb2,0x01,0xe8,0x5d,0xbf,0x46,0x4e,0x77,0xab, - 0x42,0x33,0x58,0xce,0xee,0xf9,0x6c,0xa2,0xb3,0x66,0x53,0x85,0xb1,0x2f,0x9d,0xcc, - 0x1e,0xdb,0xca,0x77,0xe8,0x67,0xd2,0xc5,0x99,0xdc,0x5d,0x37,0x2b,0x8c,0x3d,0xa0, - 0x37,0x4e,0xb7,0x45,0x20,0xd4,0x72,0x76,0xc7,0xe8,0x92,0x66,0x4e,0xc6,0xeb,0xa2, - 0xe4,0x93,0x6c,0xcb,0x6f,0xa0,0xa7,0x6b,0xf4,0xb4,0x3b,0xf6,0x4e,0x70,0xae,0x59, - 0xcd,0x92,0xe5,0x98,0xa1,0xfb,0x84,0xd3,0x4c,0x2f,0x12,0x98,0x7c,0x56,0xe8,0xef, - 0xdf,0xfb,0x28,0xf9,0xc8,0x6f,0xdc,0x66,0xaa,0xec,0x02,0xc8,0xc6,0x29,0x7a,0x9b, - 0x21,0xae,0x58,0x4d,0xb4,0xc9,0x22,0xcb,0x11,0x12,0x83,0xb5,0xb2,0xed,0x5c,0xf0, - 0x4c,0x82,0x60,0xd6,0x43,0xdb,0x7e,0x79,0x80,0x9e,0xd0,0xc8,0x62,0xfc,0xc6,0x2e, - 0x83,0x2d,0xa5,0x2c,0xc8,0x88,0xac,0x78,0x89,0x9f,0x5c,0xb0,0x1a,0xe5,0xf3,0x58, - 0x90,0x36,0x43,0x48,0xcc,0x9a,0xe3,0x94,0xc0,0xaa,0xfc,0x71,0x36,0x86,0xd3,0x92, - 0x8d,0x69,0x0b,0xfd,0xf6,0x46,0x7e,0x36,0x3e,0x92,0x5b,0xb4,0xd3,0xa5,0x8b,0x05, - 0x1a,0xd5,0x8a,0xfc,0xd4,0x5d,0xed,0xa9,0x98,0x4c,0xda,0x5b,0xae,0xfd,0xe3,0xe8, - 0xd1,0xf8,0xe8,0xa6,0xf4,0x7c,0xe9,0xdf,0xa3,0x8f,0xed,0xf8,0x2e,0xf4,0x68,0x7c, - 0xb4,0x0a,0xa7,0x4b,0x17,0xe8,0x4d,0xd2,0x97,0x8c,0x76,0x1b,0x7d,0x6c,0xc7,0x6b, - 0xe8,0xff,0x3c,0xd6,0xa4,0x8f,0x80,0x2c,0xa4,0xfe,0x86,0x6d,0x65,0x77,0xc0,0x60, - 0xb3,0xf4,0x8f,0x53,0xd5,0xed,0x45,0x83,0x1d,0xc5,0xf8,0x8d,0x16,0x1d,0x5e,0x2a, - 0xa3,0x3a,0xbc,0x30,0x23,0xca,0x56,0xb4,0x39,0xbe,0x24,0x6b,0x04,0xb4,0x2d,0x0c, - 0xb5,0xfa,0x97,0x0d,0x17,0x84,0x2d,0x4b,0x16,0x03,0xfa,0xd9,0x27,0x61,0x89,0x5e, - 0xd4,0xc8,0xe1,0xc9,0xe4,0x9e,0x0e,0x3e,0x05,0x0e,0x99,0xf6,0x1b,0xcf,0xd1,0xbb, - 0xcf,0x58,0x51,0xa7,0x19,0xa8,0xe5,0x04,0xbd,0xe4,0x10,0x9f,0x83,0x5b,0xf4,0x5d, - 0xc9,0xe7,0x80,0x9e,0x16,0xe8,0x27,0xc7,0x8b,0x32,0x3c,0x28,0x64,0xf7,0xd4,0xfa, - 0xd0,0x37,0x75,0x15,0xb0,0x49,0x01,0xba,0x7c,0x70,0x05,0xcb,0x86,0xba,0x44,0x24, - 0xf4,0xa2,0xab,0x8e,0x71,0x3c,0x7a,0x20,0xd4,0x32,0x54,0xbe,0x68,0x64,0x9c,0x28, - 0xd2,0x3d,0xbf,0x17,0x52,0xc1,0xcd,0x50,0xae,0x24,0x5f,0x8d,0x69,0x66,0xad,0x13, - 0x2f,0x0f,0xdc,0xdc,0x1f,0xcc,0x4a,0x76,0xa6,0x87,0xa3,0xa3,0x71,0x90,0xfe,0x0f, - 0x5c,0x3e,0xc6,0xea,0xdd,0x74,0x11,0x07,0xb5,0x05,0xde,0x1b,0x82,0x71,0x24,0x25, - 0x58,0xc3,0x2e,0x29,0x9a,0x95,0x29,0x7e,0xc3,0x1a,0x09,0x06,0xbc,0x2d,0xf4,0x2c, - 0x96,0x19,0x05,0xc4,0x9b,0x92,0x4f,0xe6,0x8d,0x68,0x8f,0x89,0xe7,0x94,0x4a,0x7e, - 0x91,0x42,0xab,0x90,0x0d,0xeb,0xb2,0x93,0x55,0xb0,0x05,0xc7,0x96,0x65,0x23,0x74, - 0x04,0x5c,0xcd,0x77,0xff,0xb2,0xbe,0x71,0xcd,0xc9,0xc5,0x6f,0x6b,0xec,0x89,0x6e, - 0x25,0x3f,0x6f,0x95,0x1b,0x7a,0x99,0x66,0x44,0xc1,0x57,0xa7,0x4e,0x93,0x7c,0xf3, - 0xcd,0x6b,0x0b,0xa7,0x87,0xb5,0xdb,0xd0,0x7a,0xe1,0x45,0x1e,0xe4,0x46,0x57,0x33, - 0x28,0x2b,0x7a,0xa9,0x8e,0x77,0xfc,0xf6,0x64,0xec,0x75,0x7a,0xb6,0x4d,0x82,0x31, - 0x19,0xe9,0xdb,0x89,0x86,0x82,0x28,0xf9,0xd5,0x69,0xb4,0x28,0xf8,0x3c,0xbd,0xf7, - 0xe2,0x4a,0x76,0x8d,0x7e,0x39,0xe7,0xcf,0x93,0x8f,0xf2,0x20,0x53,0xf7,0x8d,0x6c, - 0x08,0x7a,0xab,0x6e,0xc7,0x7b,0x89,0xd4,0xfe,0xb9,0x8d,0x3e,0x4a,0xbe,0xd8,0x7f, - 0x0b,0x30,0x14,0xa5,0x1f,0x32,0xd7,0x85,0x59,0xef,0x5d,0xf4,0xea,0x16,0xeb,0xf6, - 0xda,0xba,0xf2,0x86,0xe3,0x7d,0xf4,0xa6,0xa6,0x35,0x73,0x9a,0x35,0xbd,0xbd,0x8c, - 0x49,0x09,0xcd,0x24,0x9e,0x9a,0x33,0x9b,0xb9,0xb3,0x0d,0x27,0x37,0x61,0x47,0xb2, - 0xe2,0xf9,0xd1,0x07,0xd0,0x4b,0x2d,0x60,0xd9,0x09,0xc4,0xcc,0x88,0xda,0xa6,0xef, - 0x56,0xd0,0x76,0xdc,0xac,0xd4,0x0c,0xf2,0xc6,0x56,0x79,0x6e,0x69,0xb8,0xda,0xf0, - 0xe8,0x7d,0xf4,0xab,0x5a,0xae,0x94,0xf4,0xe7,0xba,0xde,0x55,0xc5,0x7f,0xe8,0xff, - 0x4b,0xe8,0xff,0x58,0x0e,0x86,0x52,0x10,0xaa,0xfe,0x94,0xdd,0x14,0xec,0x9e,0xfa, - 0xae,0xc3,0x2e,0x66,0x5f,0x72,0x2c,0x52,0xa6,0xa1,0xfd,0x87,0x28,0x6e,0x0c,0xe3, - 0x46,0x49,0xf0,0xf7,0x26,0xa0,0x49,0xdc,0x12,0x7d,0x91,0x1f,0x64,0x56,0x44,0xee, - 0x42,0x94,0x8e,0x36,0x86,0x7f,0x8d,0x5e,0x75,0x70,0xb6,0xb8,0x1a,0x82,0x68,0x90, - 0xb1,0xe9,0x5e,0x86,0x66,0xc7,0xe2,0x23,0xaf,0xf1,0x01,0xc9,0x7b,0x18,0xb3,0xa8, - 0x5a,0x02,0x81,0x2c,0xac,0x81,0x0d,0x11,0x3d,0x46,0x02,0x50,0xe8,0x28,0xb6,0xf8, - 0x99,0x80,0x94,0x59,0xba,0x04,0x8e,0x1e,0xa1,0xaf,0x40,0x22,0x84,0xa1,0xcd,0xce, - 0x19,0x83,0xd5,0xa9,0xdb,0xd3,0xf0,0x76,0x63,0x73,0x8c,0x18,0xe0,0xc3,0xd4,0xd1, - 0x86,0xd2,0x8b,0xc8,0x44,0x3c,0x94,0x26,0xbb,0x76,0x5c,0x2e,0xd1,0xb3,0x4b,0x9c, - 0xc0,0x6f,0x03,0x68,0x91,0x72,0xa6,0xa3,0xfb,0x25,0xff,0x24,0x26,0xcb,0x76,0xb4, - 0x39,0x99,0x0f,0x1a,0x6b,0x70,0x8e,0x52,0xd0,0xae,0x28,0x8a,0xba,0x57,0xd4,0x96, - 0x61,0x7b,0xfb,0xcd,0x94,0x41,0x54,0xd5,0x2d,0x8c,0x47,0xb5,0xd2,0xb1,0xb3,0x99, - 0xb6,0xee,0xb9,0xc5,0xb7,0xc1,0xdf,0x37,0x89,0x9e,0xf2,0xdd,0x85,0xa3,0x1c,0x67, - 0xda,0xaa,0x67,0x3e,0x40,0x6f,0x5e,0x98,0xa5,0xd5,0x13,0x17,0x54,0x16,0x65,0xce, - 0xa9,0x2c,0x0f,0xd9,0x6b,0xb5,0xa2,0x44,0xca,0xbb,0x21,0x82,0xde,0xe3,0x52,0x18, - 0xf7,0xa3,0x0a,0x69,0x18,0x4f,0x0d,0x1b,0xd0,0x61,0xaf,0x3a,0xa3,0xd7,0x2a,0x8a, - 0xbf,0x61,0x43,0xd9,0x53,0x57,0x43,0xeb,0x30,0xeb,0xee,0x5b,0x85,0x67,0x1a,0x7b, - 0x67,0x52,0x34,0xb9,0xa0,0x5a,0x34,0x88,0xb1,0x61,0xf1,0x76,0x13,0xa7,0x18,0x95, - 0x48,0x88,0x12,0x9c,0xc7,0xde,0x26,0x43,0xe9,0x27,0xd8,0x80,0xf6,0x5d,0x5a,0xd6, - 0x50,0xa7,0x02,0xa3,0x33,0xc2,0xc3,0x5c,0xb8,0x47,0x2f,0x6b,0xfd,0x76,0x54,0x3c, - 0x9c,0xe8,0x2d,0xc9,0x9f,0x23,0x4a,0xfa,0x4c,0xc8,0xd5,0xd9,0x30,0xb3,0x9f,0xb9, - 0x44,0xba,0x43,0x64,0x1f,0x2d,0xce,0xdb,0xbd,0x1e,0xf6,0x11,0xbd,0xdd,0xd1,0xd1, - 0x34,0xbf,0xf7,0x90,0x0b,0xde,0xec,0xe1,0x66,0x7b,0x76,0xf4,0x0c,0x3d,0x27,0xb4, - 0xac,0x32,0xec,0x63,0xaf,0xee,0x68,0x46,0x38,0xc3,0x56,0xd3,0x88,0xe7,0x0b,0x30, - 0x31,0x0b,0x4c,0x86,0x81,0x26,0x3b,0x7a,0x70,0x51,0x3b,0x45,0x2f,0x9b,0x1d,0x22, - 0x40,0xbe,0x84,0xe4,0x23,0xa1,0x7b,0x0f,0xa2,0x11,0x6b,0x70,0x4d,0x2b,0x3a,0x8a, - 0xec,0x01,0x57,0x8c,0x24,0x27,0x9d,0xcd,0x81,0x02,0x31,0x8b,0x5f,0x86,0xfd,0x8b, - 0x38,0xf6,0x6e,0xbe,0x09,0xdb,0x5d,0x0b,0x35,0x51,0x05,0x88,0x2c,0x30,0x2c,0x1e, - 0xa9,0xc7,0xe2,0x83,0x98,0xac,0x2b,0xaf,0x89,0xc6,0x97,0xf6,0x8a,0x33,0xf4,0xab, - 0xc2,0xda,0x12,0x52,0xf2,0x1d,0x41,0x1d,0xe4,0x04,0x3d,0x11,0xc6,0x65,0xc5,0xa3, - 0x8d,0xcd,0xad,0x8c,0x7e,0x1e,0xa6,0xa5,0x2e,0xfe,0x8f,0xd0,0xa3,0x99,0xc3,0x03, - 0x1a,0xef,0xd0,0x9f,0x1c,0x6d,0x6c,0x6e,0x5d,0x6d,0x2d,0x7d,0x3f,0xfa,0x65,0x85, - 0xac,0x2f,0xf9,0x43,0xf4,0x6a,0xea,0xf9,0x63,0x39,0x57,0xd6,0x0c,0xb6,0x50,0xf9, - 0x90,0xd6,0xf4,0xed,0x81,0xe0,0x4a,0x45,0x18,0xeb,0x1f,0x1e,0x05,0x2d,0x16,0x62, - 0x03,0xfc,0x8e,0xea,0xba,0x35,0x7f,0x93,0x60,0xbb,0x21,0xd3,0x70,0x9f,0x3c,0xe8, - 0x0f,0x60,0x54,0x05,0xb6,0x7b,0xac,0xd2,0xbe,0x39,0x6d,0x31,0xc7,0x6e,0x36,0xd7, - 0xc0,0xcc,0x19,0xbd,0xbb,0x90,0xb3,0x07,0x6b,0x1b,0x6c,0xdd,0xf2,0x92,0x3e,0xd0, - 0x3b,0x4d,0x96,0x45,0x78,0x58,0xb7,0xcc,0x36,0x1b,0x32,0xa1,0x3f,0x79,0xd0,0x5b, - 0x8e,0x31,0xe4,0xe4,0x5e,0x5a,0x8b,0xcf,0x40,0xdc,0xa5,0x11,0x3d,0x31,0x68,0xed, - 0x67,0xc9,0xea,0x97,0x16,0x7e,0x31,0x5b,0x0d,0x10,0xe5,0x5a,0x62,0xc9,0xa2,0x27, - 0x4b,0x49,0x29,0xe1,0xb9,0x4a,0x9b,0x14,0xd1,0xd1,0x3f,0x72,0x27,0xb3,0x32,0xa2, - 0xcb,0x09,0x78,0xb8,0x4d,0x09,0x9b,0xe1,0x2a,0xfe,0x56,0x8f,0xcc,0x7a,0x18,0xa1, - 0x55,0xb9,0x82,0xec,0x94,0x2d,0xb7,0x20,0xed,0x6f,0xb5,0x3a,0x88,0xb5,0x72,0xfb, - 0xd9,0x14,0x30,0xa1,0x00,0xdd,0xfa,0x75,0x92,0xd0,0xc9,0x2f,0x87,0x2f,0x2e,0x3c, - 0x68,0x80,0x45,0x86,0x57,0xae,0x53,0x0c,0xc8,0xd2,0x9f,0x0f,0xdd,0x8e,0x14,0x3d, - 0xeb,0xd8,0xe7,0x30,0xb1,0x9d,0x34,0x47,0x65,0x99,0x3f,0xce,0x94,0xd0,0x55,0x1f, - 0xec,0x45,0x68,0xe5,0x21,0x64,0x73,0xec,0xdc,0x42,0x8f,0xa1,0x4a,0xb2,0x5f,0xff, - 0xb2,0xe4,0xeb,0xd8,0x97,0x29,0x5e,0x69,0x9d,0xec,0xa0,0xe7,0x47,0xc2,0x27,0x31, - 0x75,0x22,0x3b,0x1b,0xe8,0xe1,0x93,0x0f,0xd2,0x6e,0x44,0xff,0x54,0xf2,0x29,0x7f, - 0x3e,0xe0,0xc5,0x59,0x6f,0x1d,0x85,0x73,0x9b,0x8a,0x39,0x97,0x6b,0x0a,0xc2,0x3b, - 0x4b,0xd1,0x02,0xc9,0xb6,0xe1,0x34,0xde,0x7b,0xf0,0x3b,0x7b,0x90,0x9a,0x2b,0x9b, - 0xd3,0xa2,0x72,0x9e,0xac,0x7e,0xa7,0x70,0xa0,0xe9,0x1b,0x03,0x67,0xa9,0x19,0xf7, - 0x39,0x85,0xeb,0x9c,0x3e,0xe0,0xc1,0x3a,0xc5,0x9f,0x2d,0xd4,0x5e,0x0f,0x88,0x20, - 0xca,0x3a,0xc0,0x45,0xd2,0x76,0x55,0xe0,0x07,0x7e,0xec,0xf4,0x72,0xcb,0xfd,0xca, - 0x9b,0xe8,0x91,0xbd,0xfe,0x3e,0xf4,0x77,0xb0,0x7f,0x3a,0xfa,0x3f,0x96,0x73,0x49, - 0x2e,0x4c,0xdd,0x5e,0xb0,0x9c,0x4d,0x9e,0x21,0x9a,0x7e,0xa3,0xe0,0x6e,0x66,0x04, - 0xc9,0xa9,0xd1,0x37,0x70,0x1c,0x74,0x66,0xdb,0x20,0x17,0xca,0x56,0x2e,0xd1,0x5f, - 0xf3,0x0c,0xd9,0xed,0x77,0x2f,0x37,0x09,0x90,0x57,0xdd,0xa5,0x3c,0x22,0x5b,0xfc, - 0x16,0xc7,0x41,0x67,0xb6,0x17,0x58,0x8e,0x2b,0x7b,0xfb,0xcf,0x8c,0x65,0x5a,0xbc, - 0xdc,0x64,0xc7,0x4e,0xdc,0xa3,0x7a,0xdf,0x3e,0x21,0x59,0x66,0xa8,0x79,0x8d,0xe3, - 0xe0,0x8e,0x17,0xc4,0xe4,0x3c,0x4b,0x9f,0xea,0x87,0x1e,0x2a,0x86,0x01,0x49,0xf8, - 0x51,0x8f,0xdd,0xa2,0x2a,0x32,0x96,0xe7,0x1c,0x27,0xa2,0x0f,0x21,0x08,0xdb,0xa9, - 0x13,0x13,0x5e,0x7c,0x42,0x6a,0xf1,0x89,0x89,0x84,0xde,0xe9,0xd6,0x43,0x3d,0x97, - 0x3d,0x48,0x66,0x8f,0xe3,0x84,0xc0,0x92,0x09,0xfd,0xab,0x7a,0x7e,0xe4,0x78,0xf3, - 0xf7,0x48,0x52,0x3a,0xa3,0xa7,0x47,0x8d,0x07,0xa5,0x5d,0xbf,0x0f,0xc3,0x9f,0x41, - 0xff,0x82,0x9e,0xff,0xf8,0x99,0xcf,0xa1,0xb7,0x20,0x9c,0xf2,0x01,0xf4,0x2f,0x4b, - 0xfe,0x0e,0x21,0x01,0xe3,0x6f,0x44,0x3f,0x7d,0xf2,0x60,0xb7,0x28,0x77,0x60,0xde, - 0xe4,0x38,0x5c,0x13,0xab,0xd2,0x0b,0xe2,0xaf,0xb7,0x08,0xc2,0xb9,0x4d,0x8d,0x47, - 0xd4,0x1d,0x62,0x12,0x02,0x66,0xd0,0xc1,0xec,0x3b,0x38,0x0e,0x7b,0xa4,0x57,0xb8, - 0x90,0x7d,0x37,0xde,0x64,0x39,0x7b,0x1f,0xc6,0x6a,0xdf,0xac,0xe7,0xc6,0x70,0x3a, - 0x4a,0x2b,0xde,0xe7,0x58,0xce,0xcf,0x44,0x8f,0x7b,0x10,0x9c,0x6c,0x3c,0x9f,0x64, - 0x39,0x3f,0x13,0x3d,0x7e,0x3d,0xa4,0x24,0x1b,0xcf,0xef,0x66,0x39,0xff,0x03,0xca, - 0x1b,0xaa,0xdf,0x6a,0xd1,0xba,0xa4,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae, - 0x42,0x60,0x82 }; - -int tigr_font_size = (int)sizeof(tigr_font); - - -//////// End of inlined file: tigr_font.h //////// - -#include <stdlib.h> -#include <stdarg.h> -#include <errno.h> -#include <stdio.h> - -#ifdef _MSC_VER -#define vsnprintf _vsnprintf -#endif - -TigrFont tigrStockFont; -TigrFont* tfont = &tigrStockFont; - -// Converts 8-bit codepage entries into Unicode code points. -static int cp1252[] = { - 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, - 0xfffd, 0x017d, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, - 0x0161, 0x203a, 0x0153, 0xfffd, 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, - 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, - 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, - 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, - 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, - 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, - 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, - 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, -}; - -static int border(Tigr* bmp, int x, int y) { - TPixel top = tigrGet(bmp, 0, 0); - TPixel c = tigrGet(bmp, x, y); - return (c.r == top.r && c.g == top.g && c.b == top.b) || x >= bmp->w || y >= bmp->h; -} - -static void scan(Tigr* bmp, int* x, int* y, int* rowh) { - while (*y < bmp->h) { - if (*x >= bmp->w) { - *x = 0; - (*y) += *rowh; - *rowh = 1; - } - if (!border(bmp, *x, *y)) - return; - (*x)++; - } -} - -/* - * Watermarks are encoded vertically in the alpha channel using seven pixels - * starting at x, y. The first and last alpha values contain the magic values - * 0b10101010 and 0b01010101 respectively. - */ -static int readWatermark(Tigr* bmp, int x, int y, int* big, int* small) { - const int magicHeader = 0xAA; - const int magicFooter = 0x55; - - unsigned char watermark[7]; - - for (int i = 0; i < 7; i++) { - TPixel c = tigrGet(bmp, x, y + i); - watermark[i] = c.a; - } - - if (watermark[0] != magicHeader || watermark[6] != magicFooter) { - return 0; - } - - *big = watermark[1] | (watermark[2] << 8) | (watermark[3] << 16) | (watermark[4] << 24); - *small = watermark[5]; - - return 1; -} - -int tigrLoadGlyphs(TigrFont* font, int codepage) { - int x = 0; - int y = 0; - int w = 0; - int h = 0; - int rowh = 1; - - TigrGlyph* g; - switch (codepage) { - case TCP_ASCII: - font->numGlyphs = 128 - 32; - break; - case TCP_1252: - font->numGlyphs = 256 - 32; - break; - case TCP_UTF32: - if (!readWatermark(font->bitmap, 0, 0, &font->numGlyphs, &rowh)) { - return 0; - } - h = rowh; - x = 1; - break; - default: - errno = EINVAL; - return 0; - } - - font->glyphs = (TigrGlyph*)calloc(font->numGlyphs, sizeof(TigrGlyph)); - - for (int index = 0; index < font->numGlyphs; index++) { - // Look up the Unicode code point. - g = &font->glyphs[index]; - - if (codepage != TCP_UTF32) { - // Find the next glyph. - scan(font->bitmap, &x, &y, &rowh); - - if (y >= font->bitmap->h) { - errno = EINVAL; - return 0; - } - - // Scan the width and height - w = h = 0; - while (!border(font->bitmap, x + w, y)) { - w++; - } - - while (!border(font->bitmap, x, y + h)) { - h++; - } - } - - switch (codepage) { - case TCP_ASCII: - g->code = index + 32; - break; - case TCP_1252: - if (index < 96) { - g->code = index + 32; - } else { - g->code = cp1252[index - 96]; - } - break; - case TCP_UTF32: - if (!readWatermark(font->bitmap, x, y, &g->code, &w)) { - // Maybe we are at the end of a row? - x = 0; - y += rowh; - if (!readWatermark(font->bitmap, x, y, &g->code, &w)) { - return 0; - } - } - x++; - break; - default: - return 0; - } - - g->x = x; - g->y = y; - g->w = w; - g->h = h; - x += w; - if (h != font->glyphs[0].h) { - errno = EINVAL; - return 0; - } - - if (h > rowh) { - rowh = h; - } - } - - // Sort by code point. - for (int i = 1; i < font->numGlyphs; i++) { - int j = i; - TigrGlyph g = font->glyphs[i]; - while (j > 0 && font->glyphs[j - 1].code > g.code) { - font->glyphs[j] = font->glyphs[j - 1]; - j--; - } - font->glyphs[j] = g; - } - - return 1; -} - -TigrFont* tigrLoadFont(Tigr* bitmap, int codepage) { - TigrFont* font = (TigrFont*)calloc(1, sizeof(TigrFont)); - font->bitmap = bitmap; - if (!tigrLoadGlyphs(font, codepage)) { - tigrFreeFont(font); - return NULL; - } - return font; -} - -void tigrFreeFont(TigrFont* font) { - tigrFree(font->bitmap); - free(font->glyphs); - free(font); -} - -static TigrGlyph* get(TigrFont* font, int code) { - unsigned lo = 0, hi = font->numGlyphs; - while (lo < hi) { - unsigned guess = (lo + hi) / 2; - if (code < font->glyphs[guess].code) - hi = guess; - else - lo = guess + 1; - } - - if (lo == 0 || font->glyphs[lo - 1].code != code) - return &font->glyphs['?' - 32]; - else - return &font->glyphs[lo - 1]; -} - -void tigrSetupFont(TigrFont* font) { - // Load the stock font if needed. - if (font == tfont && !tfont->bitmap) { - tfont->bitmap = tigrLoadImageMem(tigr_font, tigr_font_size); - tigrLoadGlyphs(tfont, 1252); - } -} - -void tigrPrint(Tigr* dest, TigrFont* font, int x, int y, TPixel color, const char* text, ...) { - char tmp[1024]; - TigrGlyph* g; - va_list args; - const char* p; - int start = x, c; - - tigrSetupFont(font); - - // Expand the formatting string. - va_start(args, text); - vsnprintf(tmp, sizeof(tmp), text, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - // Print each glyph. - p = tmp; - while (*p) { - p = tigrDecodeUTF8(p, &c); - if (c == '\r') - continue; - if (c == '\n') { - x = start; - y += tigrTextHeight(font, ""); - continue; - } - g = get(font, c); - tigrBlitTint(dest, font->bitmap, x, y, g->x, g->y, g->w, g->h, color); - x += g->w; - } -} - -int tigrTextWidth(TigrFont* font, const char* text) { - int x = 0, w = 0, c; - tigrSetupFont(font); - - while (*text) { - text = tigrDecodeUTF8(text, &c); - if (c == '\n' || c == '\r') { - x = 0; - } else { - x += get(font, c)->w; - w = (x > w) ? x : w; - } - } - return w; -} - -int tigrTextHeight(TigrFont* font, const char* text) { - int rowh, h, c; - tigrSetupFont(font); - - h = rowh = get(font, 0)->h; - while (*text) { - text = tigrDecodeUTF8(text, &c); - if (c == '\n' && *text) - h += rowh; - } - return h; -} - -//////// End of inlined file: tigr_print.c //////// - -//////// Start of inlined file: tigr_win.c //////// - -#ifndef TIGR_HEADLESS - -//#include "tigr_internal.h" -#include <assert.h> - -// not really windows stuff -TigrInternal* tigrInternal(Tigr* bmp) { - assert(bmp->handle); - return (TigrInternal*)(bmp + 1); -} - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <shellapi.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> - -#ifdef _MSC_VER -#pragma comment(lib, "opengl32") // glViewport -#pragma comment(lib, "shell32") // CommandLineToArgvW -#pragma comment(lib, "user32") // SetWindowLong -#pragma comment(lib, "gdi32") // ChoosePixelFormat -#pragma comment(lib, "advapi32") // RegSetValueEx -#endif - -#define WIDGET_SCALE 3 -#define WIDGET_FADE 16 - -int main(int argc, char* argv[]); - -#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION -HKEY tigrRegKey; -#endif - -#ifdef __TINYC__ -#define CP_UTF8 65001 -int WINAPI MultiByteToWideChar(); -int WINAPI WideCharToMultiByte(); -#endif - -static wchar_t* unicode(const char* str) { - int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0); - wchar_t* dest = (wchar_t*)malloc(sizeof(wchar_t) * len); - MultiByteToWideChar(CP_UTF8, 0, str, -1, dest, len); - return dest; -} - -void tigrError(Tigr* bmp, const char* message, ...) { - char tmp[1024]; - - va_list args; - va_start(args, message); - _vsnprintf(tmp, sizeof(tmp), message, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - MessageBoxW(bmp ? (HWND)bmp->handle : NULL, unicode(tmp), bmp ? tigrInternal(bmp)->wtitle : L"Error", - MB_OK | MB_ICONERROR); - exit(1); -} - -void tigrEnterBorderlessWindowed(Tigr* bmp) { - // Enter borderless windowed mode. - MONITORINFO mi = { sizeof(mi) }; - TigrInternal* win = tigrInternal(bmp); - - GetWindowRect((HWND)bmp->handle, &win->oldPos); - - GetMonitorInfo(MonitorFromWindow((HWND)bmp->handle, MONITOR_DEFAULTTONEAREST), &mi); - win->dwStyle = WS_VISIBLE | WS_POPUP; - SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle); - SetWindowPos((HWND)bmp->handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, - mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, 0); -} - -void tigrLeaveBorderlessWindowed(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - - win->dwStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW; - SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle); - - SetWindowPos((HWND)bmp->handle, NULL, win->oldPos.left, win->oldPos.top, win->oldPos.right - win->oldPos.left, - win->oldPos.bottom - win->oldPos.top, 0); -} - -void tigrWinUpdateWidgets(Tigr* bmp, int dw, int dh) { - POINT pt; - int i, x, clicked = 0; - char str[8]; - TPixel col; - TPixel off = tigrRGB(255, 255, 255); - TPixel on = tigrRGB(0, 200, 255); - TigrInternal* win = tigrInternal(bmp); - (void)dh; - - tigrClear(win->widgets, tigrRGBA(0, 0, 0, 0)); - - if (!(win->dwStyle & WS_POPUP)) { - win->widgetsWanted = 0; - win->widgetAlpha = 0; - return; - } - - // See if we want to be showing widgets or not. - GetCursorPos(&pt); - ScreenToClient((HWND)bmp->handle, &pt); - if (pt.y == 0) - win->widgetsWanted = 1; - if (pt.y > win->widgets->h * WIDGET_SCALE) - win->widgetsWanted = 0; - - // Track the alpha. - if (win->widgetsWanted) - win->widgetAlpha = (win->widgetAlpha <= 255 - WIDGET_FADE) ? win->widgetAlpha + WIDGET_FADE : 255; - else - win->widgetAlpha = (win->widgetAlpha >= WIDGET_FADE) ? win->widgetAlpha - WIDGET_FADE : 0; - - // Get relative coords. - pt.x -= (dw - win->widgets->w * WIDGET_SCALE); - pt.x /= WIDGET_SCALE; - pt.y /= WIDGET_SCALE; - - tigrClear(win->widgets, tigrRGBA(0, 0, 0, win->widgetAlpha)); - - // Render it. - for (i = 0; i < 3; i++) { - switch (i) { - case 0: - str[0] = '_'; - str[1] = 0; - break; // "_" (minimize) - case 1: - str[0] = (char)0xEF; - str[1] = (char)0xBF; - str[2] = (char)0xBD; - str[3] = 0; - break; // "[]" (maximize) - case 2: - str[0] = (char)0xC3; - str[1] = (char)0x97; - str[2] = 0; - break; // "x" (close) - } - x = win->widgets->w + (i - 3) * 12; - if (i == 2) - off = tigrRGB(255, 0, 0); - if (pt.x >= x && pt.x < x + 10 && pt.y < win->widgets->h) { - col = on; - if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) - clicked |= 1 << i; - } else { - col = off; - } - col.a = win->widgetAlpha; - tigrPrint(win->widgets, tfont, x, 2, col, str); - } - - if (clicked & 1) - ShowWindow((HWND)bmp->handle, SW_MINIMIZE); - if (clicked & 2) - tigrLeaveBorderlessWindowed(bmp); - if (clicked & 4) - SendMessage((HWND)bmp->handle, WM_CLOSE, 0, 0); -} - -void tigrUpdate(Tigr* bmp) { - MSG msg; - RECT rc; - int dw, dh; - TigrInternal* win = tigrInternal(bmp); - - if (!win->shown) { - win->shown = 1; - UpdateWindow((HWND)bmp->handle); - ShowWindow((HWND)bmp->handle, SW_SHOW); - } - - // Get the window size. - GetClientRect((HWND)bmp->handle, &rc); - dw = rc.right - rc.left; - dh = rc.bottom - rc.top; - - // Update the widget overlay. - tigrWinUpdateWidgets(bmp, dw, dh); - - if (!tigrGAPIBegin(bmp)) { - tigrGAPIPresent(bmp, dw, dh); - SwapBuffers(win->gl.dc); - tigrGAPIEnd(bmp); - } - - memcpy(win->prev, win->keys, 256); - win->scrollDeltaX = 0; - win->scrollDeltaY = 0; - - // Run the message pump. - while (PeekMessage(&msg, (HWND)bmp->handle, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) - break; - - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALFARPROC_)(int); -static PFNWGLSWAPINTERVALFARPROC_ wglSwapIntervalEXT_ = 0; - -int tigrGAPIBegin(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - - return wglMakeCurrent(win->gl.dc, win->gl.hglrc) ? 0 : -1; -} - -int tigrGAPIEnd(Tigr* bmp) { - (void)bmp; - return wglMakeCurrent(NULL, NULL) ? 0 : -1; -} - -static BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) { - BOOL fRc; - RECT rc; - SetRectEmpty(&rc); - fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle); - if (fRc) { - prc->left -= rc.left; - prc->top -= rc.top; - prc->right -= rc.right; - prc->bottom -= rc.bottom; - } - return fRc; -} - -LRESULT CALLBACK tigrWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - Tigr* bmp; - TigrInternal* win = NULL; - RECT rc; - int dw, dh; - - GetClientRect(hWnd, &rc); - dw = rc.right - rc.left; - dh = rc.bottom - rc.top; - - bmp = (Tigr*)GetPropW(hWnd, L"Tigr"); - if (bmp) - win = tigrInternal(bmp); - - switch (message) { - case WM_PAINT: - if (!tigrGAPIBegin(bmp)) { - tigrGAPIPresent(bmp, dw, dh); - SwapBuffers(win->gl.dc); - tigrGAPIEnd(bmp); - } - ValidateRect(hWnd, NULL); - break; - case WM_CLOSE: - if (win) - win->closed = 1; - break; - case WM_GETMINMAXINFO: - if (bmp) { - MINMAXINFO* info = (MINMAXINFO*)lParam; - RECT rc; - rc.left = 0; - rc.top = 0; - if (win->flags & TIGR_AUTO) { - rc.right = 32; - rc.bottom = 32; - } else { - int minscale = tigrEnforceScale(1, win->flags); - rc.right = bmp->w * minscale; - rc.bottom = bmp->h * minscale; - } - AdjustWindowRectEx(&rc, win->dwStyle, FALSE, 0); - info->ptMinTrackSize.x = rc.right - rc.left; - info->ptMinTrackSize.y = rc.bottom - rc.top; - } - return 0; - case WM_SIZING: - if (win) { - // Calculate scale-constrained sizes. - RECT* rc = (RECT*)lParam; - int dx, dy; - UnadjustWindowRectEx(rc, win->dwStyle, FALSE, 0); - dx = (rc->right - rc->left) % win->scale; - dy = (rc->bottom - rc->top) % win->scale; - switch (wParam) { - case WMSZ_LEFT: - rc->left += dx; - break; - case WMSZ_RIGHT: - rc->right -= dx; - break; - case WMSZ_TOP: - rc->top += dy; - break; - case WMSZ_TOPLEFT: - rc->left += dx; - rc->top += dy; - break; - case WMSZ_TOPRIGHT: - rc->right -= dx; - rc->top += dy; - break; - case WMSZ_BOTTOM: - rc->bottom -= dy; - break; - case WMSZ_BOTTOMLEFT: - rc->left += dx; - rc->bottom -= dy; - break; - case WMSZ_BOTTOMRIGHT: - rc->right -= dx; - rc->bottom -= dy; - break; - } - AdjustWindowRectEx(rc, win->dwStyle, FALSE, 0); - } - return TRUE; - case WM_SIZE: - if (win) { - if (wParam != SIZE_MINIMIZED) { - // Detect window size changes and update our bitmap accordingly. - dw = LOWORD(lParam); - dh = HIWORD(lParam); - if (win->flags & TIGR_AUTO) { - tigrResize(bmp, dw / win->scale, dh / win->scale); - } else { - win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, dw, dh), win->flags); - } - tigrPosition(bmp, win->scale, dw, dh, win->pos); - } - - // If someone tried to maximize us (e.g. via shortcut launch options), - // prefer instead to be borderless. - if (wParam == SIZE_MAXIMIZED) { - ShowWindow((HWND)bmp->handle, SW_NORMAL); - tigrEnterBorderlessWindowed(bmp); - } - } - return 0; -#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION - case WM_WINDOWPOSCHANGED: { - // Save our position. - WINDOWPLACEMENT wp = { sizeof(WINDOWPLACEMENT) }; - GetWindowPlacement(hWnd, &wp); - if (win->dwStyle & WS_POPUP) - wp.showCmd = SW_MAXIMIZE; - RegSetValueExW(tigrRegKey, win->wtitle, 0, REG_BINARY, (BYTE*)&wp, sizeof(wp)); - return DefWindowProcW(hWnd, message, wParam, lParam); - } -#endif - case WM_ACTIVATE: - if (win) { - memset(win->keys, 0, 256); - memset(win->prev, 0, 256); - win->lastChar = 0; - } - return 0; - case WM_CHAR: - if (win) { - if (wParam == '\r') { - wParam = '\n'; - } - int repeating = (HIWORD(lParam) & KF_REPEAT) == KF_REPEAT; - if (!repeating) { - win->lastChar = wParam; - } - } - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_MENUCHAR: - // Disable beep on Alt+Enter - if (LOWORD(wParam) == VK_RETURN) - return MNC_CLOSE << 16; - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_SYSKEYDOWN: - if (win) { - if (wParam == VK_RETURN) { - // Alt+Enter - if (win->dwStyle & WS_POPUP) - tigrLeaveBorderlessWindowed(bmp); - else - tigrEnterBorderlessWindowed(bmp); - return 0; - } - } - // fall-thru - case WM_KEYDOWN: - if (win) - win->keys[wParam] = 1; - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_SYSKEYUP: - // fall-thru - case WM_KEYUP: - if (win) - win->keys[wParam] = 0; - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_MOUSEWHEEL: - if (win) - win->scrollDeltaY += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_MOUSEHWHEEL: - if (win) - win->scrollDeltaX += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - return DefWindowProcW(hWnd, message, wParam, lParam); - default: - return DefWindowProcW(hWnd, message, wParam, lParam); - } - return 0; -} - -Tigr* tigrWindow(int w, int h, const char* title, int flags) { - WNDCLASSEXW wcex = { 0 }; - int maxW, maxH, scale; - HWND hWnd; - DWORD dwStyle; - RECT rc; - Tigr* bmp; - TigrInternal* win; -#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION - WINDOWPLACEMENT wp; - DWORD wpsize = sizeof(wp); -#endif - - wchar_t* wtitle = unicode(title); - -// Find our registry key. -#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION - RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\TIGR", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, - &tigrRegKey, NULL); -#endif - - // Register a window class. - wcex.cbSize = sizeof(WNDCLASSEXW); - wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wcex.lpfnWndProc = tigrWndProc; - wcex.hInstance = GetModuleHandle(NULL); - wcex.hIcon = NULL; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.lpszClassName = L"TIGR"; - RegisterClassExW(&wcex); - - if (flags & TIGR_AUTO) { - // Always use a 1:1 pixel size. - scale = 1; - } else { - // See how big we can make it and still fit on-screen. - maxW = GetSystemMetrics(SM_CXSCREEN) * 3 / 4; - maxH = GetSystemMetrics(SM_CYSCREEN) * 3 / 4; - scale = tigrCalcScale(w, h, maxW, maxH); - } - - scale = tigrEnforceScale(scale, flags); - - // Get the final window size. - dwStyle = WS_OVERLAPPEDWINDOW; - rc.left = 0; - rc.top = 0; - rc.right = w * scale; - rc.bottom = h * scale; - AdjustWindowRect(&rc, dwStyle, FALSE); - - // Make a window. - hWnd = CreateWindowW(L"TIGR", wtitle, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, - NULL, NULL, wcex.hInstance, NULL); - if (!hWnd) - ExitProcess(1); - - if (flags & TIGR_NOCURSOR) { - ShowCursor(FALSE); - } - - // Wrap a bitmap around it. - bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); - bmp->handle = hWnd; - - // Set up the Windows parts. - win = tigrInternal(bmp); - win->dwStyle = dwStyle; - win->wtitle = wtitle; - win->shown = 0; - win->closed = 0; - win->scale = scale; - win->lastChar = 0; - win->flags = flags; - - win->p1 = win->p2 = win->p3 = 0; - win->p4 = 1; - - win->widgetsWanted = 0; - win->widgetAlpha = 0; - win->widgetsScale = WIDGET_SCALE; - win->widgets = tigrBitmap(40, 14); - - SetPropW(hWnd, L"Tigr", bmp); - - tigrGAPICreate(bmp); - - if (flags & TIGR_FULLSCREEN) { - tigrEnterBorderlessWindowed(bmp); - } else { -// Try and restore our window position. -#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION - if (RegQueryValueExW(tigrRegKey, wtitle, NULL, NULL, (BYTE*)&wp, &wpsize) == ERROR_SUCCESS) { - if (wp.showCmd == SW_MAXIMIZE) - tigrEnterBorderlessWindowed(bmp); - else - SetWindowPlacement(hWnd, &wp); - } -#endif - } - - wglSwapIntervalEXT_ = (PFNWGLSWAPINTERVALFARPROC_)wglGetProcAddress("wglSwapIntervalEXT"); - if (wglSwapIntervalEXT_) - wglSwapIntervalEXT_(1); - - return bmp; -} - -void tigrFree(Tigr* bmp) { - if (bmp->handle) { - TigrInternal* win = tigrInternal(bmp); - tigrGAPIDestroy(bmp); - - if (win->gl.hglrc && !wglDeleteContext(win->gl.hglrc)) { - tigrError(bmp, "Cannot delete OpenGL context.\n"); - } - win->gl.hglrc = NULL; - - if (win->gl.dc && !ReleaseDC((HWND)bmp->handle, win->gl.dc)) { - tigrError(bmp, "Cannot release OpenGL device context.\n"); - } - win->gl.dc = NULL; - - DestroyWindow((HWND)bmp->handle); - free(win->wtitle); - tigrFree(win->widgets); - } - free(bmp->pix); - free(bmp); -} - -int tigrClosed(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int val = win->closed; - win->closed = 0; - return val; -} - -float tigrTime(void) { - static int first = 1; - static LARGE_INTEGER prev; - - LARGE_INTEGER cnt, freq; - ULONGLONG diff; - QueryPerformanceCounter(&cnt); - QueryPerformanceFrequency(&freq); - - if (first) { - first = 0; - prev = cnt; - } - - diff = cnt.QuadPart - prev.QuadPart; - prev = cnt; - return (float)(diff / (double)freq.QuadPart); -} - -void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) { - POINT pt; - TigrInternal* win; - - win = tigrInternal(bmp); - GetCursorPos(&pt); - ScreenToClient((HWND)bmp->handle, &pt); - *x = (pt.x - win->pos[0]) / win->scale; - *y = (pt.y - win->pos[1]) / win->scale; - *buttons = 0; - if (GetFocus() != bmp->handle) - return; - if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) - *buttons |= 1; - if (GetAsyncKeyState(VK_MBUTTON) & 0x8000) - *buttons |= 2; - if (GetAsyncKeyState(VK_RBUTTON) & 0x8000) - *buttons |= 4; -} - -int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) { - int buttons = 0; - if (maxPoints > 0) { - tigrMouse(bmp, &points[0].x, &points[1].y, &buttons); - } - return buttons ? 1 : 0; -} - -void tigrScrollWheel(Tigr* bmp, float* x, float* y) { - TigrInternal* win; - win = tigrInternal(bmp); - if (x) { - *x = win->scrollDeltaX; - } - if (y) { - *y = win->scrollDeltaY; - } -} - -static int tigrWinVK(int key) { - if (key >= 'A' && key <= 'Z') - return key; - if (key >= '0' && key <= '9') - return key; - switch (key) { - case TK_BACKSPACE: - return VK_BACK; - case TK_TAB: - return VK_TAB; - case TK_RETURN: - return VK_RETURN; - case TK_SHIFT: - return VK_SHIFT; - case TK_CONTROL: - return VK_CONTROL; - case TK_ALT: - return VK_MENU; - case TK_PAUSE: - return VK_PAUSE; - case TK_CAPSLOCK: - return VK_CAPITAL; - case TK_ESCAPE: - return VK_ESCAPE; - case TK_SPACE: - return VK_SPACE; - case TK_PAGEUP: - return VK_PRIOR; - case TK_PAGEDN: - return VK_NEXT; - case TK_END: - return VK_END; - case TK_HOME: - return VK_HOME; - case TK_LEFT: - return VK_LEFT; - case TK_UP: - return VK_UP; - case TK_RIGHT: - return VK_RIGHT; - case TK_DOWN: - return VK_DOWN; - case TK_INSERT: - return VK_INSERT; - case TK_DELETE: - return VK_DELETE; - case TK_LWIN: - return VK_LWIN; - case TK_RWIN: - return VK_RWIN; - // case TK_APPS: return VK_APPS; // this key doesn't exist on OS X - case TK_PAD0: - return VK_NUMPAD0; - case TK_PAD1: - return VK_NUMPAD1; - case TK_PAD2: - return VK_NUMPAD2; - case TK_PAD3: - return VK_NUMPAD3; - case TK_PAD4: - return VK_NUMPAD4; - case TK_PAD5: - return VK_NUMPAD5; - case TK_PAD6: - return VK_NUMPAD6; - case TK_PAD7: - return VK_NUMPAD7; - case TK_PAD8: - return VK_NUMPAD8; - case TK_PAD9: - return VK_NUMPAD9; - case TK_PADMUL: - return VK_MULTIPLY; - case TK_PADADD: - return VK_ADD; - case TK_PADENTER: - return VK_SEPARATOR; - case TK_PADSUB: - return VK_SUBTRACT; - case TK_PADDOT: - return VK_DECIMAL; - case TK_PADDIV: - return VK_DIVIDE; - case TK_F1: - return VK_F1; - case TK_F2: - return VK_F2; - case TK_F3: - return VK_F3; - case TK_F4: - return VK_F4; - case TK_F5: - return VK_F5; - case TK_F6: - return VK_F6; - case TK_F7: - return VK_F7; - case TK_F8: - return VK_F8; - case TK_F9: - return VK_F9; - case TK_F10: - return VK_F10; - case TK_F11: - return VK_F11; - case TK_F12: - return VK_F12; - case TK_NUMLOCK: - return VK_NUMLOCK; - case TK_SCROLL: - return VK_SCROLL; - case TK_LSHIFT: - return VK_LSHIFT; - case TK_RSHIFT: - return VK_RSHIFT; - case TK_LCONTROL: - return VK_LCONTROL; - case TK_RCONTROL: - return VK_RCONTROL; - case TK_LALT: - return VK_LMENU; - case TK_RALT: - return VK_RMENU; - case TK_SEMICOLON: - return VK_OEM_1; - case TK_EQUALS: - return VK_OEM_PLUS; - case TK_COMMA: - return VK_OEM_COMMA; - case TK_MINUS: - return VK_OEM_MINUS; - case TK_DOT: - return VK_OEM_PERIOD; - case TK_SLASH: - return VK_OEM_2; - case TK_BACKTICK: - return VK_OEM_3; - case TK_LSQUARE: - return VK_OEM_4; - case TK_BACKSLASH: - return VK_OEM_5; - case TK_RSQUARE: - return VK_OEM_6; - case TK_TICK: - return VK_OEM_7; - } - return 0; -} - -int tigrKeyDown(Tigr* bmp, int key) { - TigrInternal* win; - int k = tigrWinVK(key); - if (GetFocus() != bmp->handle) - return 0; - win = tigrInternal(bmp); - return win->keys[k] && !win->prev[k]; -} - -int tigrKeyHeld(Tigr* bmp, int key) { - TigrInternal* win; - int k = tigrWinVK(key); - if (GetFocus() != bmp->handle) - return 0; - win = tigrInternal(bmp); - return win->keys[k]; -} - -int tigrReadChar(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int c = win->lastChar; - win->lastChar = 0; - return c; -} - -// We supply our own WinMain and just chain through to the user's -// real entry point. -#ifdef UNICODE -int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) -#else -int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -#endif -{ - int n, argc; - LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &argc); - char** argv = (char**)calloc(argc + 1, sizeof(int)); - - (void)hInstance; - (void)hPrevInstance; - (void)lpCmdLine; - (void)nCmdShow; - - for (n = 0; n < argc; n++) { - int len = WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, 0, 0, NULL, NULL); - argv[n] = (char*)malloc(len); - WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, argv[n], len, NULL, NULL); - } - return main(argc, argv); -} -#endif - -#endif // #ifndef TIGR_HEADLESS -//////// End of inlined file: tigr_win.c //////// - -//////// Start of inlined file: tigr_osx.c //////// - -#ifndef TIGR_HEADLESS - -// originally based on https://github.com/jimon/osx_app_in_plain_c - -//#include "tigr_internal.h" - -#if __MACOS__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -//////// Start of inlined file: tigr_objc.h //////// - -#ifndef TIGR_OBJC_H -#define TIGR_OBJC_H - -#if defined(__IOS__) || defined(__MACOS__) - -#if defined(__OBJC__) && __has_feature(objc_arc) -#error "Can't compile as objective-c code!" -#endif - -// ABI is a bit different between platforms -#ifdef __arm64__ -#define abi_objc_msgSend_stret objc_msgSend -#else -#define abi_objc_msgSend_stret objc_msgSend_stret -#endif -#ifdef __i386__ -#define abi_objc_msgSend_fpret objc_msgSend_fpret -#else -#define abi_objc_msgSend_fpret objc_msgSend -#endif - -#define objc_msgSendSuper_t(RET, ...) ((RET(*)(struct objc_super*, SEL, ##__VA_ARGS__))objc_msgSendSuper) -#define objc_msgSend_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))objc_msgSend) -#define objc_msgSend_stret_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))abi_objc_msgSend_stret) -#define objc_msgSend_id objc_msgSend_t(id) -#define objc_msgSend_void objc_msgSend_t(void) -#define objc_msgSend_void_id objc_msgSend_t(void, id) -#define objc_msgSend_void_bool objc_msgSend_t(void, bool) - -#define sel(NAME) sel_registerName(NAME) -#define class(NAME) ((id)objc_getClass(NAME)) -#define makeClass(NAME, SUPER) objc_allocateClassPair((Class)objc_getClass(SUPER), NAME, 0) - -// Check here to get the signature right: https://nshipster.com/type-encodings/ -#define addMethod(CLASS, NAME, IMPL, SIGNATURE) \ - if (!class_addMethod(CLASS, sel(NAME), (IMP)(IMPL), (SIGNATURE))) \ - assert(false) - -#define addIvar(CLASS, NAME, SIZE, SIGNATURE) \ - if (!class_addIvar(CLASS, NAME, SIZE, rint(log2(SIZE)), SIGNATURE)) \ - assert(false) - -#define objc_alloc(CLASS) objc_msgSend_id(class(CLASS), sel("alloc")) - -#if __LP64__ || NS_BUILD_32_LIKE_64 -#define NSIntegerEncoding "q" -#define NSUIntegerEncoding "L" -#else -#define NSIntegerEncoding "i" -#define NSUIntegerEncoding "I" -#endif - -#endif // defined(__IOS__) || defined (__MACOS__) -#endif // TIGR_OBJC_H - -//////// End of inlined file: tigr_objc.h //////// - -#pragma clang diagnostic pop - -#include <assert.h> -#include <limits.h> -#include <math.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -#include <CoreFoundation/CoreFoundation.h> -#include <CoreGraphics/CoreGraphics.h> -#include <mach/mach_time.h> -#include <objc/NSObjCRuntime.h> -#include <objc/message.h> -#include <objc/objc.h> -#include <objc/runtime.h> - -#include <CoreGraphics/CGBase.h> -#include <CoreGraphics/CGGeometry.h> - -typedef CGPoint NSPoint; -typedef CGSize NSSize; -typedef CGRect NSRect; - -enum { - NSKeyDown = 10, - NSKeyDownMask = 1 << NSKeyDown, - NSKeyUp = 11, - NSKeyUpMask = 1 << NSKeyUp, -}; - -NSUInteger NSAllEventMask = NSUIntegerMax; - -extern id NSApp; -extern id const NSDefaultRunLoopMode; - -#define NSApplicationActivationPolicyRegular 0 - -bool terminated = false; - -static double _tigrTimestamp = 0; - -double _currentMediaTime(void) { - uint64_t now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); - double current = now * 1.0E-9; - return current; -} - -TigrInternal* _tigrInternalFromWindowDelegate(id wdg) { - Tigr* bmp = 0; - object_getInstanceVariable(wdg, "tigrHandle", (void**)&bmp); - return bmp ? tigrInternal(bmp) : NULL; -} - -TigrInternal* _tigrInternalFromWindow(id window) { - if (!window) - return NULL; - - id wdg = objc_msgSend_id(window, sel("delegate")); - if (!wdg) - return NULL; - - return _tigrInternalFromWindowDelegate(wdg); -} - -NSUInteger applicationShouldTerminate(id self, SEL sel, id sender) { - terminated = true; - return 0; -} - -void windowWillClose(id self, SEL _sel, id notification) { - NSUInteger value = true; - object_setInstanceVariable(self, "closed", (void*)value); - object_setInstanceVariable(self, "tigrHandle", (void*)0); -} - -void windowDidEnterFullScreen(id self, SEL _sel, id notification) { - NSUInteger value = true; - object_setInstanceVariable(self, "visible", (void*)value); -} - -void windowDidResize(id self, SEL _sel, id notification) { - TigrInternal* win; - Tigr* bmp = 0; - object_getInstanceVariable(self, "tigrHandle", (void**)&bmp); - win = bmp ? tigrInternal(bmp) : NULL; - if (win) { - win->mouseButtons = 0; - } -} - -void windowDidBecomeKey(id self, SEL _sel, id notification) { - TigrInternal* win; - Tigr* bmp = 0; - object_getInstanceVariable(self, "tigrHandle", (void**)&bmp); - win = bmp ? tigrInternal(bmp) : NULL; - - if (win) { - memset(win->keys, 0, 256); - memset(win->prev, 0, 256); - win->lastChar = 0; - win->mouseButtons = 0; - } -} - -void onFrame(id self, SEL _sel, id displayLink) { - double timestamp = objc_msgSend_t(double)(displayLink, sel("timestamp")); - TigrInternal* win = _tigrInternalFromWindowDelegate(self); - if (win == NULL) { - return; - } - win->frameTime = timestamp; -} - -void mouseEntered(id self, SEL _sel, id event) { - id window = objc_msgSend_id(event, sel("window")); - TigrInternal* win = _tigrInternalFromWindow(window); - if (win) { - win->mouseInView = 1; - if (win->flags & TIGR_NOCURSOR) { - objc_msgSend_id(class("NSCursor"), sel("hide")); - } - } -} - -void mouseExited(id self, SEL _sel, id event) { - id window = objc_msgSend_id(event, sel("window")); - TigrInternal* win = _tigrInternalFromWindow(window); - if (win) { - win->mouseInView = 0; - if (win->flags & TIGR_NOCURSOR) { - objc_msgSend_id(class("NSCursor"), sel("unhide")); - } - } -} - -bool _tigrIsWindowClosed(id window) { - id wdg = objc_msgSend_id(window, sel("delegate")); - if (!wdg) - return false; - NSUInteger value = 0; - object_getInstanceVariable(wdg, "closed", (void**)&value); - return value ? true : false; -} - -bool _tigrIsWindowVisible(id window) { - id wdg = objc_msgSend_id(window, sel("delegate")); - if (!wdg) - return false; - NSUInteger value = 0; - object_getInstanceVariable(wdg, "visible", (void**)&value); - return value ? true : false; -} - -static bool tigrOSXInited = false; -static id autoreleasePool = NULL; - -#ifdef DEBUG -static void _showPools(const char* context) { - fprintf(stderr, "NSAutoreleasePool@%s:\n", context); - objc_msgSend(class("NSAutoreleasePool"), sel("showPools")); -} -#define showPools(x) _showPools((x)) -#else -#define showPools(x) -#endif - -static id pushPool(void) { - id pool = objc_msgSend_id(class("NSAutoreleasePool"), sel("alloc")); - return objc_msgSend_id(pool, sel("init")); -} - -static void popPool(id pool) { - objc_msgSend_void(pool, sel("drain")); -} - -void _tigrCleanupOSX(void) { - showPools("cleanup"); - popPool(autoreleasePool); -} - -void tigrInitOSX(void) { - if (tigrOSXInited) - return; - - atexit(&_tigrCleanupOSX); - - autoreleasePool = pushPool(); - - showPools("init start"); - - objc_msgSend_id(class("NSApplication"), sel("sharedApplication")); - objc_msgSend_t(void, NSInteger)(NSApp, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular); - - Class appDelegateClass = makeClass("AppDelegate", "NSObject"); - addMethod(appDelegateClass, "applicationShouldTerminate", applicationShouldTerminate, NSUIntegerEncoding "@:@"); - id dgAlloc = objc_msgSend_id((id)appDelegateClass, sel("alloc")); - id dg = objc_msgSend_id(dgAlloc, sel("init")); - - objc_msgSend_void_id(NSApp, sel("setDelegate:"), dg); - objc_msgSend_void(NSApp, sel("finishLaunching")); - - id menuBar = objc_alloc("NSMenu"); - menuBar = objc_msgSend_id(menuBar, sel("init")); - - id appMenuItem = objc_alloc("NSMenuItem"); - appMenuItem = objc_msgSend_id(appMenuItem, sel("init")); - - objc_msgSend_void_id(menuBar, sel("addItem:"), appMenuItem); - objc_msgSend_t(id, id)(NSApp, sel("setMainMenu:"), menuBar); - - id processInfo = objc_msgSend_id(class("NSProcessInfo"), sel("processInfo")); - id appName = objc_msgSend_id(processInfo, sel("processName")); - - id appMenu = objc_alloc("NSMenu"); - appMenu = objc_msgSend_t(id, id)(appMenu, sel("initWithTitle:"), appName); - - id quitTitlePrefixString = - objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "Quit "); - id quitTitle = objc_msgSend_t(id, id)(quitTitlePrefixString, sel("stringByAppendingString:"), appName); - - id quitMenuItemKey = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "q"); - id quitMenuItem = objc_alloc("NSMenuItem"); - quitMenuItem = objc_msgSend_t(id, id, SEL, id)(quitMenuItem, sel("initWithTitle:action:keyEquivalent:"), quitTitle, - sel("terminate:"), quitMenuItemKey); - - objc_msgSend_void_id(appMenu, sel("addItem:"), quitMenuItem); - objc_msgSend_void_id(appMenuItem, sel("setSubmenu:"), appMenu); - - tigrOSXInited = true; - - showPools("init end"); -} - -void tigrError(Tigr* bmp, const char* message, ...) { - char tmp[1024]; - - va_list args; - va_start(args, message); - vsnprintf(tmp, sizeof(tmp), message, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - printf("tigr fatal error: %s\n", tmp); - - exit(1); -} - -NSSize _tigrContentBackingSize(id window) { - id contentView = objc_msgSend_id(window, sel("contentView")); - NSRect rect = objc_msgSend_stret_t(NSRect)(contentView, sel("frame")); - rect = objc_msgSend_stret_t(NSRect, NSRect)(contentView, sel("convertRectToBacking:"), rect); - - return rect.size; -} - -enum { - NSWindowStyleMaskTitled = 1 << 0, - NSWindowStyleMaskClosable = 1 << 1, - NSWindowStyleMaskMiniaturizable = 1 << 2, - NSWindowStyleMaskResizable = 1 << 3, - NSWindowStyleRegular = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | - NSWindowStyleMaskResizable, - NSWindowStyleMaskFullSizeContentView = 1 << 15 -}; - -Tigr* tigrWindow(int w, int h, const char* title, int flags) { - Tigr* bmp; - TigrInternal* win; - - tigrInitOSX(); - - NSUInteger windowStyleMask = NSWindowStyleRegular & ~NSWindowStyleMaskMiniaturizable; - - // In AUTO mode, window follows requested size, unless downscaled by tigrEnforceScale below. - int windowScale = 1; - - // In non-AUTO mode, see how big we can make it and still fit on-screen. - if ((flags & TIGR_AUTO) == 0) { - CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID()); - int maxW = CGRectGetWidth(mainMonitor); - int maxH = CGRectGetHeight(mainMonitor); - NSRect screen = { { 0, 0 }, { maxW, maxH } }; - NSRect content = objc_msgSend_stret_t(NSRect, NSRect, NSUInteger)( - class("NSWindow"), sel("contentRectForFrameRect:styleMask:"), screen, windowStyleMask); - windowScale = tigrCalcScale(w, h, content.size.width, content.size.height); - } - - windowScale = tigrEnforceScale(windowScale, flags); - - NSRect rect = { { 0, 0 }, { w * windowScale, h * windowScale } }; - id windowAlloc = objc_msgSend_id(class("NSWindow"), sel("alloc")); - id window = ((id(*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)( - windowAlloc, sel("initWithContentRect:styleMask:backing:defer:"), rect, windowStyleMask, 2, NO); - - objc_msgSend_void_bool(window, sel("setReleasedWhenClosed:"), NO); - - Class WindowDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0); - addIvar(WindowDelegateClass, "closed", sizeof(NSUInteger), NSUIntegerEncoding); - addIvar(WindowDelegateClass, "visible", sizeof(NSUInteger), NSUIntegerEncoding); - addIvar(WindowDelegateClass, "tigrHandle", sizeof(void*), "ˆv"); - addMethod(WindowDelegateClass, "windowWillClose:", windowWillClose, "v@:@"); - addMethod(WindowDelegateClass, "windowDidEnterFullScreen:", windowDidEnterFullScreen, "v@:@"); - addMethod(WindowDelegateClass, "windowDidResize:", windowDidResize, "v@:@"); - addMethod(WindowDelegateClass, "windowDidBecomeKey:", windowDidBecomeKey, "v@:@"); - addMethod(WindowDelegateClass, "mouseEntered:", mouseEntered, "v@:@"); - addMethod(WindowDelegateClass, "mouseExited:", mouseExited, "v@:@"); - addMethod(WindowDelegateClass, "onFrame:", onFrame, "v@:@"); - - id wdgAlloc = objc_msgSend_id((id)WindowDelegateClass, sel("alloc")); - id wdg = objc_msgSend_id(wdgAlloc, sel("init")); - - if (flags & TIGR_FULLSCREEN) { - objc_msgSend_void_id(window, sel("toggleFullScreen:"), window); - if (flags & TIGR_NOCURSOR) { - objc_msgSend_id(class("NSCursor"), sel("hide")); - } - } else { - NSUInteger value = true; - object_setInstanceVariable(wdg, "visible", (void*)value); - } - - objc_msgSend_void_id(window, sel("setDelegate:"), wdg); - - id contentView = objc_msgSend_id(window, sel("contentView")); - - objc_msgSend_void_bool(contentView, sel("setWantsLayer:"), NO); - - bool useDisplayLink = objc_msgSend_t(bool, SEL)(class("NSView"), sel("instancesRespondToSelector:"), - sel("displayLinkWithTarget:selector:")); - - if (useDisplayLink) { - // Use display link to pace frames, if possible - id link = - objc_msgSend_t(id, id, SEL)(contentView, sel("displayLinkWithTarget:selector:"), wdg, sel("onFrame:")); - id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop")); - objc_msgSend_t(void, id, id)(link, sel("addToRunLoop:forMode:"), runLoop, NSDefaultRunLoopMode); - objc_msgSend_void_bool(link, sel("setPaused:"), NO); - } - - int wantsHighRes = (flags & TIGR_RETINA); - objc_msgSend_void_bool(contentView, sel("setWantsBestResolutionOpenGLSurface:"), wantsHighRes); - - NSPoint point = { 20, 20 }; - ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel("cascadeTopLeftFromPoint:"), point); - - id titleString = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), title); - objc_msgSend_void_id(window, sel("setTitle:"), titleString); - - uint32_t glAttributes[] = { 8, 24, // NSOpenGLPFAColorSize, 24, - 11, 8, // NSOpenGLPFAAlphaSize, 8, - 5, // NSOpenGLPFADoubleBuffer, - 73, // NSOpenGLPFAAccelerated, - // 72, // NSOpenGLPFANoRecovery, - // 55, 1, // NSOpenGLPFASampleBuffers, 1, - // 56, 4, // NSOpenGLPFASamples, 4, - 99, 0x3200, // NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - // 70, 0x00020400, // NSOpenGLPFARendererID, kCGLRendererGenericFloatID - 0 }; - - id pixelFormat = objc_alloc("NSOpenGLPixelFormat"); - pixelFormat = objc_msgSend_t(id, const uint32_t*)(pixelFormat, sel("initWithAttributes:"), glAttributes); - objc_msgSend_void(pixelFormat, sel("autorelease")); - - id openGLContext = objc_alloc("NSOpenGLContext"); - openGLContext = objc_msgSend_t(id, id, id)(openGLContext, sel("initWithFormat:shareContext:"), pixelFormat, nil); - - objc_msgSend_void_id(openGLContext, sel("setView:"), contentView); - objc_msgSend_void_id(window, sel("makeKeyAndOrderFront:"), window); - objc_msgSend_void_bool(window, sel("setAcceptsMouseMovedEvents:"), YES); - - id blackColor = objc_msgSend_id(class("NSColor"), sel("blackColor")); - objc_msgSend_void_id(window, sel("setBackgroundColor:"), blackColor); - - objc_msgSend_void_bool(NSApp, sel("activateIgnoringOtherApps:"), YES); - - NSSize windowContentSize = _tigrContentBackingSize(window); - - // In AUTO mode, always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below. - int bitmapScale = 1; - - // In non-AUTO mode, scale based on backing size - if ((flags & TIGR_AUTO) == 0) { - bitmapScale = tigrEnforceScale(tigrCalcScale(w, h, windowContentSize.width, windowContentSize.height), flags); - } else { - // In AUTO mode, bitmap size follows window size - w = windowContentSize.width / windowScale; - h = windowContentSize.height / windowScale; - bitmapScale = tigrEnforceScale(bitmapScale, flags); - } - - bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); - bmp->handle = window; - - // Set the handle - object_setInstanceVariable(wdg, "tigrHandle", (void*)bmp); - - { -#define NSTrackingMouseEnteredAndExited 1 -#define NSTrackingActiveInKeyWindow 0x20 -#define NSTrackingInVisibleRect 0x200 - - int trackingFlags = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect; - id trackingArea = objc_msgSend_id(class("NSTrackingArea"), sel("alloc")); - trackingArea = objc_msgSend_t(id, NSRect, int, id, id)( - trackingArea, sel("initWithRect:options:owner:userInfo:"), rect, trackingFlags, wdg, 0); - objc_msgSend_void_id(contentView, sel("addTrackingArea:"), trackingArea); - } - - // Set up the Windows parts. - win = tigrInternal(bmp); - win->shown = 0; - win->closed = 0; - win->scale = bitmapScale; - win->lastChar = 0; - win->flags = flags; - win->p1 = win->p2 = win->p3 = 0; - win->p4 = 1; - win->widgetsWanted = 0; - win->widgetAlpha = 0; - win->widgetsScale = 0; - win->widgets = 0; - win->gl.gl_legacy = 0; - win->gl.glContext = openGLContext; - win->mouseButtons = 0; - win->mouseInView = 0; - win->frameTime = 0; - - tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos); - - GLint swapInterval = 1; - NSInteger NSOpenGLCPSwapInterval = 222; - - objc_msgSend_t(void, GLint*, NSInteger)(openGLContext, sel("setValues:forParameter:"), &swapInterval, - NSOpenGLCPSwapInterval); - - objc_msgSend_void(openGLContext, sel("makeCurrentContext")); - - tigrGAPICreate(bmp); - - return bmp; -} - -void tigrFree(Tigr* bmp) { - if (bmp->handle) { - TigrInternal* win = tigrInternal(bmp); - tigrGAPIDestroy(bmp); - - id window = (id)bmp->handle; - - if (!_tigrIsWindowClosed(window) && !terminated) { - objc_msgSend_void(window, sel("close")); - } - - if (win->flags & TIGR_NOCURSOR) { - objc_msgSend_id(class("NSCursor"), sel("unhide")); - } - - id wdg = objc_msgSend_id(window, sel("delegate")); - objc_msgSend_void(wdg, sel("release")); - objc_msgSend_void((id)win->gl.glContext, sel("release")); - objc_msgSend_void(window, sel("release")); - } - free(bmp->pix); - free(bmp); -} - -uint8_t _tigrKeyFromOSX(uint16_t key) { - // from Carbon HIToolbox/Events.h - enum { - kVK_ANSI_A = 0x00, - kVK_ANSI_S = 0x01, - kVK_ANSI_D = 0x02, - kVK_ANSI_F = 0x03, - kVK_ANSI_H = 0x04, - kVK_ANSI_G = 0x05, - kVK_ANSI_Z = 0x06, - kVK_ANSI_X = 0x07, - kVK_ANSI_C = 0x08, - kVK_ANSI_V = 0x09, - kVK_ANSI_B = 0x0B, - kVK_ANSI_Q = 0x0C, - kVK_ANSI_W = 0x0D, - kVK_ANSI_E = 0x0E, - kVK_ANSI_R = 0x0F, - kVK_ANSI_Y = 0x10, - kVK_ANSI_T = 0x11, - kVK_ANSI_1 = 0x12, - kVK_ANSI_2 = 0x13, - kVK_ANSI_3 = 0x14, - kVK_ANSI_4 = 0x15, - kVK_ANSI_6 = 0x16, - kVK_ANSI_5 = 0x17, - kVK_ANSI_Equal = 0x18, - kVK_ANSI_9 = 0x19, - kVK_ANSI_7 = 0x1A, - kVK_ANSI_Minus = 0x1B, - kVK_ANSI_8 = 0x1C, - kVK_ANSI_0 = 0x1D, - kVK_ANSI_RightBracket = 0x1E, - kVK_ANSI_O = 0x1F, - kVK_ANSI_U = 0x20, - kVK_ANSI_LeftBracket = 0x21, - kVK_ANSI_I = 0x22, - kVK_ANSI_P = 0x23, - kVK_ANSI_L = 0x25, - kVK_ANSI_J = 0x26, - kVK_ANSI_Quote = 0x27, - kVK_ANSI_K = 0x28, - kVK_ANSI_Semicolon = 0x29, - kVK_ANSI_Backslash = 0x2A, - kVK_ANSI_Comma = 0x2B, - kVK_ANSI_Slash = 0x2C, - kVK_ANSI_N = 0x2D, - kVK_ANSI_M = 0x2E, - kVK_ANSI_Period = 0x2F, - kVK_ANSI_Grave = 0x32, - kVK_ANSI_KeypadDecimal = 0x41, - kVK_ANSI_KeypadMultiply = 0x43, - kVK_ANSI_KeypadPlus = 0x45, - kVK_ANSI_KeypadClear = 0x47, - kVK_ANSI_KeypadDivide = 0x4B, - kVK_ANSI_KeypadEnter = 0x4C, - kVK_ANSI_KeypadMinus = 0x4E, - kVK_ANSI_KeypadEquals = 0x51, - kVK_ANSI_Keypad0 = 0x52, - kVK_ANSI_Keypad1 = 0x53, - kVK_ANSI_Keypad2 = 0x54, - kVK_ANSI_Keypad3 = 0x55, - kVK_ANSI_Keypad4 = 0x56, - kVK_ANSI_Keypad5 = 0x57, - kVK_ANSI_Keypad6 = 0x58, - kVK_ANSI_Keypad7 = 0x59, - kVK_ANSI_Keypad8 = 0x5B, - kVK_ANSI_Keypad9 = 0x5C, - kVK_Return = 0x24, - kVK_Tab = 0x30, - kVK_Space = 0x31, - kVK_Delete = 0x33, - kVK_Escape = 0x35, - kVK_Command = 0x37, - kVK_Shift = 0x38, - kVK_CapsLock = 0x39, - kVK_Option = 0x3A, - kVK_Control = 0x3B, - kVK_RightShift = 0x3C, - kVK_RightOption = 0x3D, - kVK_RightControl = 0x3E, - kVK_Function = 0x3F, - kVK_F17 = 0x40, - kVK_VolumeUp = 0x48, - kVK_VolumeDown = 0x49, - kVK_Mute = 0x4A, - kVK_F18 = 0x4F, - kVK_F19 = 0x50, - kVK_F20 = 0x5A, - kVK_F5 = 0x60, - kVK_F6 = 0x61, - kVK_F7 = 0x62, - kVK_F3 = 0x63, - kVK_F8 = 0x64, - kVK_F9 = 0x65, - kVK_F11 = 0x67, - kVK_F13 = 0x69, - kVK_F16 = 0x6A, - kVK_F14 = 0x6B, - kVK_F10 = 0x6D, - kVK_F12 = 0x6F, - kVK_F15 = 0x71, - kVK_Help = 0x72, - kVK_Home = 0x73, - kVK_PageUp = 0x74, - kVK_ForwardDelete = 0x75, - kVK_F4 = 0x76, - kVK_End = 0x77, - kVK_F2 = 0x78, - kVK_PageDown = 0x79, - kVK_F1 = 0x7A, - kVK_LeftArrow = 0x7B, - kVK_RightArrow = 0x7C, - kVK_DownArrow = 0x7D, - kVK_UpArrow = 0x7E - }; - - switch (key) { - case kVK_ANSI_Q: - return 'Q'; - case kVK_ANSI_W: - return 'W'; - case kVK_ANSI_E: - return 'E'; - case kVK_ANSI_R: - return 'R'; - case kVK_ANSI_T: - return 'T'; - case kVK_ANSI_Y: - return 'Y'; - case kVK_ANSI_U: - return 'U'; - case kVK_ANSI_I: - return 'I'; - case kVK_ANSI_O: - return 'O'; - case kVK_ANSI_P: - return 'P'; - case kVK_ANSI_A: - return 'A'; - case kVK_ANSI_S: - return 'S'; - case kVK_ANSI_D: - return 'D'; - case kVK_ANSI_F: - return 'F'; - case kVK_ANSI_G: - return 'G'; - case kVK_ANSI_H: - return 'H'; - case kVK_ANSI_J: - return 'J'; - case kVK_ANSI_K: - return 'K'; - case kVK_ANSI_L: - return 'L'; - case kVK_ANSI_Z: - return 'Z'; - case kVK_ANSI_X: - return 'X'; - case kVK_ANSI_C: - return 'C'; - case kVK_ANSI_V: - return 'V'; - case kVK_ANSI_B: - return 'B'; - case kVK_ANSI_N: - return 'N'; - case kVK_ANSI_M: - return 'M'; - case kVK_ANSI_0: - return '0'; - case kVK_ANSI_1: - return '1'; - case kVK_ANSI_2: - return '2'; - case kVK_ANSI_3: - return '3'; - case kVK_ANSI_4: - return '4'; - case kVK_ANSI_5: - return '5'; - case kVK_ANSI_6: - return '6'; - case kVK_ANSI_7: - return '7'; - case kVK_ANSI_8: - return '8'; - case kVK_ANSI_9: - return '9'; - case kVK_ANSI_Keypad0: - return TK_PAD0; - case kVK_ANSI_Keypad1: - return TK_PAD1; - case kVK_ANSI_Keypad2: - return TK_PAD2; - case kVK_ANSI_Keypad3: - return TK_PAD3; - case kVK_ANSI_Keypad4: - return TK_PAD4; - case kVK_ANSI_Keypad5: - return TK_PAD5; - case kVK_ANSI_Keypad6: - return TK_PAD6; - case kVK_ANSI_Keypad7: - return TK_PAD7; - case kVK_ANSI_Keypad8: - return TK_PAD8; - case kVK_ANSI_Keypad9: - return TK_PAD9; - case kVK_ANSI_KeypadMultiply: - return TK_PADMUL; - case kVK_ANSI_KeypadPlus: - return TK_PADADD; - case kVK_ANSI_KeypadEnter: - return TK_PADENTER; - case kVK_ANSI_KeypadMinus: - return TK_PADSUB; - case kVK_ANSI_KeypadDecimal: - return TK_PADDOT; - case kVK_ANSI_KeypadDivide: - return TK_PADDIV; - case kVK_F1: - return TK_F1; - case kVK_F2: - return TK_F2; - case kVK_F3: - return TK_F3; - case kVK_F4: - return TK_F4; - case kVK_F5: - return TK_F5; - case kVK_F6: - return TK_F6; - case kVK_F7: - return TK_F7; - case kVK_F8: - return TK_F8; - case kVK_F9: - return TK_F9; - case kVK_F10: - return TK_F10; - case kVK_F11: - return TK_F11; - case kVK_F12: - return TK_F12; - case kVK_Shift: - return TK_LSHIFT; - case kVK_Control: - return TK_LCONTROL; - case kVK_Option: - return TK_LALT; - case kVK_CapsLock: - return TK_CAPSLOCK; - case kVK_Command: - return TK_LWIN; - case kVK_Command - 1: - return TK_RWIN; - case kVK_RightShift: - return TK_RSHIFT; - case kVK_RightControl: - return TK_RCONTROL; - case kVK_RightOption: - return TK_RALT; - case kVK_Delete: - return TK_BACKSPACE; - case kVK_Tab: - return TK_TAB; - case kVK_Return: - return TK_RETURN; - case kVK_Escape: - return TK_ESCAPE; - case kVK_Space: - return TK_SPACE; - case kVK_PageUp: - return TK_PAGEUP; - case kVK_PageDown: - return TK_PAGEDN; - case kVK_End: - return TK_END; - case kVK_Home: - return TK_HOME; - case kVK_LeftArrow: - return TK_LEFT; - case kVK_UpArrow: - return TK_UP; - case kVK_RightArrow: - return TK_RIGHT; - case kVK_DownArrow: - return TK_DOWN; - case kVK_Help: - return TK_INSERT; - case kVK_ForwardDelete: - return TK_DELETE; - case kVK_F14: - return TK_SCROLL; - case kVK_F15: - return TK_PAUSE; - case kVK_ANSI_KeypadClear: - return TK_NUMLOCK; - case kVK_ANSI_Semicolon: - return TK_SEMICOLON; - case kVK_ANSI_Equal: - return TK_EQUALS; - case kVK_ANSI_Comma: - return TK_COMMA; - case kVK_ANSI_Minus: - return TK_MINUS; - case kVK_ANSI_Slash: - return TK_SLASH; - case kVK_ANSI_Backslash: - return TK_BACKSLASH; - case kVK_ANSI_Grave: - return TK_BACKTICK; - case kVK_ANSI_Quote: - return TK_TICK; - case kVK_ANSI_LeftBracket: - return TK_LSQUARE; - case kVK_ANSI_RightBracket: - return TK_RSQUARE; - case kVK_ANSI_Period: - return TK_DOT; - default: - return 0; - } -} - -void _tigrOnCocoaEvent(id event, id window) { - if (!event) - return; - - TigrInternal* win = _tigrInternalFromWindow(window); - if (!win) // just pipe the event - { - objc_msgSend_void_id(NSApp, sel("sendEvent:"), event); - return; - } - - NSUInteger eventType = objc_msgSend_t(NSUInteger)(event, sel("type")); - switch (eventType) { - case 1: // NSLeftMouseDown - if (win->mouseInView) { - win->mouseButtons |= 1; - } - break; - case 2: // NSLeftMouseUp - win->mouseButtons &= ~1; - break; - case 3: // NSRightMouseDown - if (win->mouseInView) { - win->mouseButtons |= 2; - } - break; - case 4: // NSRightMouseUp - win->mouseButtons &= ~2; - break; - case 25: // NSOtherMouseDown - { - // number == 2 is a middle button - NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber")); - if (number == 2 && win->mouseInView) { - win->mouseButtons |= 4; - } - break; - } - case 26: // NSOtherMouseUp - { - NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber")); - if (number == 2) - win->mouseButtons &= ~4; - break; - } - case 22: // NSScrollWheel - { - CGFloat deltaX = objc_msgSend_t(CGFloat)(event, sel("deltaX")); - win->scrollDeltaX += deltaX; - CGFloat deltaY = objc_msgSend_t(CGFloat)(event, sel("deltaY")); - win->scrollDeltaY += deltaY; - break; - } - case 12: // NSFlagsChanged - { - NSUInteger modifiers = objc_msgSend_t(NSUInteger)(event, sel("modifierFlags")); - - // based on NSEventModifierFlags and - // NSDeviceIndependentModifierFlagsMask - struct { - union { - struct { - uint8_t alpha_shift : 1; - uint8_t shift : 1; - uint8_t control : 1; - uint8_t alternate : 1; - uint8_t command : 1; - uint8_t numeric_pad : 1; - uint8_t help : 1; - uint8_t function : 1; - }; - uint8_t mask; - }; - } keys; - - keys.mask = (modifiers & 0xffff0000UL) >> 16; - - // TODO L,R variation of keys? - win->keys[TK_CONTROL] = keys.alpha_shift; - win->keys[TK_SHIFT] = keys.shift; - win->keys[TK_CONTROL] = keys.control; - win->keys[TK_ALT] = keys.alternate; - win->keys[TK_LWIN] = keys.command; - win->keys[TK_RWIN] = keys.command; - break; - } - case 10: // NSKeyDown - { - uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode")); - int tigrKey = _tigrKeyFromOSX(keyCode); - - // Ignore keyboard repeats - if (!win->keys[tigrKey]) { - win->keys[tigrKey] = 1; - id inputText = objc_msgSend_id(event, sel("characters")); - const char* inputTextUTF8 = objc_msgSend_t(const char*)(inputText, sel("UTF8String")); - - int decoded = 0; - tigrDecodeUTF8(inputTextUTF8, &decoded); - if (decoded < 0xe000 || decoded > 0xf8ff) { - win->lastChar = decoded; - } - } - - // Pass through cmd+key - if (win->keys[TK_LWIN]) { - break; - } - return; - } - case 11: // NSKeyUp - { - uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode")); - win->keys[_tigrKeyFromOSX(keyCode)] = 0; - return; - } - default: - break; - } - - objc_msgSend_void_id(NSApp, sel("sendEvent:"), event); -} - -void tigrUpdate(Tigr* bmp) { - popPool(autoreleasePool); - autoreleasePool = pushPool(); - - TigrInternal* win; - id openGLContext; - id window; - win = tigrInternal(bmp); - window = (id)bmp->handle; - openGLContext = (id)win->gl.glContext; - - if (terminated || _tigrIsWindowClosed(window)) { - return; - } - - id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow")); - unsigned long long eventMask = NSAllEventMask; - - if (keyWindow == window) { - memcpy(win->prev, win->keys, 256); - } else { - eventMask &= ~(NSKeyDownMask | NSKeyUpMask); - } - - win->scrollDeltaX = 0; - win->scrollDeltaY = 0; - - id event = 0; - BOOL visible = 0; - - double now = _currentMediaTime(); - double passed = now - _tigrTimestamp; - - do { - event = objc_msgSend_t(id, NSUInteger, id, id, BOOL)( - NSApp, sel("nextEventMatchingMask:untilDate:inMode:dequeue:"), eventMask, nil, NSDefaultRunLoopMode, YES); - - if (event != 0) { - _tigrOnCocoaEvent(event, window); - } else { - visible = _tigrIsWindowVisible(window); - } - } while (event != 0 || !visible); - - // The event processing loop above blocks during resize, which causes updates to freeze - // but real time keeps ticking. We pretend that the event processing took no time - // to avoid huge jumps in tigrTime. - if (_tigrTimestamp != 0) { - _tigrTimestamp = _currentMediaTime() - passed; - } - - objc_msgSend_void(NSApp, sel("updateWindows")); - objc_msgSend_void(openGLContext, sel("update")); - tigrGAPIBegin(bmp); - - NSSize windowSize = _tigrContentBackingSize(window); - - if (win->flags & TIGR_AUTO) - tigrResize(bmp, windowSize.width / win->scale, windowSize.height / win->scale); - else - win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, windowSize.width, windowSize.height), win->flags); - - tigrPosition(bmp, win->scale, windowSize.width, windowSize.height, win->pos); - tigrGAPIPresent(bmp, windowSize.width, windowSize.height); - objc_msgSend_void(openGLContext, sel("flushBuffer")); - tigrGAPIEnd(bmp); - - id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop")); - id until = objc_msgSend_t(id, float)(class("NSDate"), sel("dateWithTimeIntervalSinceNow:"), 0.005f); - double lastFrameTime = win->frameTime; - static const double cap = 1.0 / 60; - - while (win->frameTime <= lastFrameTime) { - // Run the event loop for 5ms at a time.. - objc_msgSend_t(void, id, id)(runLoop, sel("runMode:beforeDate:"), NSDefaultRunLoopMode, until); - // ..until we get a frame callback.. - if (win->frameTime > lastFrameTime) { - break; - } - double now = _currentMediaTime(); - double passed = now - lastFrameTime; - // ..or it's time to cap the wait at 60 fps - if (passed > cap) { - win->frameTime = now; - break; - } - } -} - -int tigrGAPIBegin(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - objc_msgSend_void((id)win->gl.glContext, sel("makeCurrentContext")); - return 0; -} - -int tigrGAPIEnd(Tigr* bmp) { - (void)bmp; - objc_msgSend_void(class("NSOpenGLContext"), sel("clearCurrentContext")); - return 0; -} - -int tigrClosed(Tigr* bmp) { - return (terminated || _tigrIsWindowClosed((id)bmp->handle)) ? 1 : 0; -} - -void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) { - TigrInternal* win; - id window; - win = tigrInternal(bmp); - window = (id)bmp->handle; - - id windowContentView = objc_msgSend_id(window, sel("contentView")); - NSRect adjustFrame = objc_msgSend_stret_t(NSRect)(windowContentView, sel("frame")); - - // NSPoint is small enough to fit a register, so no need for - // objc_msgSend_stret - NSPoint p = objc_msgSend_t(NSPoint)(window, sel("mouseLocationOutsideOfEventStream")); - - // map input to content view rect - if (p.x < 0) - p.x = 0; - else if (p.x > adjustFrame.size.width) - p.x = adjustFrame.size.width; - if (p.y < 0) - p.y = 0; - else if (p.y > adjustFrame.size.height) - p.y = adjustFrame.size.height; - - // map input to pixels - NSRect r = { p, { 0, 0 } }; - r = objc_msgSend_stret_t(NSRect, NSRect)(windowContentView, sel("convertRectToBacking:"), r); - p = r.origin; - - p.x = (p.x - win->pos[0]) / win->scale; - p.y = bmp->h - (p.y - win->pos[1]) / win->scale; - - if (x) - *x = p.x; - if (y) - *y = p.y; - - if (buttons) { - id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow")); - *buttons = keyWindow != bmp->handle ? 0 : win->mouseButtons; - } -} - -int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) { - int buttons = 0; - if (maxPoints > 0) { - tigrMouse(bmp, &points[0].x, &points[1].y, &buttons); - } - return buttons ? 1 : 0; -} - -void tigrScrollWheel(Tigr* bmp, float* x, float* y) { - TigrInternal* win; - win = tigrInternal(bmp); - if (x) { - *x = win->scrollDeltaX; - } - if (y) { - *y = win->scrollDeltaY; - } -} - -int tigrKeyDown(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return (win->keys[key] != 0) && (win->prev[key] == 0); -} - -int tigrKeyHeld(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key]; -} - -int tigrReadChar(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int c = win->lastChar; - win->lastChar = 0; - return c; -} - -float tigrTime(void) { - double elapsed = 0; - - double now = _currentMediaTime(); - if (_tigrTimestamp != 0) { - elapsed = now - _tigrTimestamp; - } - _tigrTimestamp = now; - - return (float)elapsed; -} - -#endif // __MACOS__ -#endif // #ifndef TIGR_HEADLESS - -//////// End of inlined file: tigr_osx.c //////// - -//////// Start of inlined file: tigr_ios.c //////// - -#ifndef TIGR_HEADLESS - -//#include "tigr_internal.h" -//#include "tigr_objc.h" - -#ifdef __IOS__ - -#include <CoreGraphics/CoreGraphics.h> -#include <objc/message.h> -#include <objc/objc.h> -#include <objc/runtime.h> -#include <dispatch/dispatch.h> -#include <os/log.h> -#include <time.h> -#include <stdatomic.h> - -id makeNSString(const char* str) { - return objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), str); -} - -id joinNSStrings(id a, id b) { - return objc_msgSend_t(id, id)(a, sel("stringByAppendingString:"), b); -} - -const char* UTF8StringFromNSString(id a) { - return objc_msgSend_t(const char*)(a, sel("UTF8String")); -} - -extern id UIApplication; -static int NSQualityOfServiceUserInteractive = 0x21; - -typedef struct { - TigrTouchPoint points[MAX_TOUCH_POINTS]; - int numPoints; -} InputState; - -typedef struct { - int keyCode; - int codePoint; -} KeyEvent; - -enum { - KBD_HIDDEN = 0, - KBD_SHOWREQ, - KBD_HIDEREQ, - KBD_SHOWN, -}; - -/// Global state -static struct { - InputState inputState; - id viewController; - id view; - id context; - id frameCondition; - int screenW; - int screenH; - double scaleFactor; - double timeSinceLastDraw; - int renderReadFd; - int mainWriteFd; - _Atomic(int) keyboardState; -} gState = { - .inputState = { - .numPoints = 0, - }, - .viewController = 0, - .view = 0, - .context = 0, - .frameCondition = 0, - .screenW = 0, - .screenH = 0, - .scaleFactor = 1, - .timeSinceLastDraw = 0, - .renderReadFd = 0, - .mainWriteFd = 0, - .keyboardState = ATOMIC_VAR_INIT(KBD_HIDDEN), -}; - -typedef enum { - SET_INPUT, - KEY_EVENT, -} TigrMessage; - -typedef struct TigrMessageData { - TigrMessage message; - union { - InputState inputState; - KeyEvent keyEvent; - }; -} TigrMessageData; - -static id autoreleasePool = NULL; - -void writeToRenderThread(const TigrMessageData* message) { - if (write(gState.mainWriteFd, message, sizeof(TigrMessageData)) != sizeof(TigrMessageData)) { - os_log_error(OS_LOG_DEFAULT, "Failed to write message to render thread: %{public}s", strerror(errno)); - } -} - -int readFromMainThread(TigrMessageData* message) { - int result = read(gState.renderReadFd, message, sizeof(TigrMessageData)); - if (result == -1 && errno == EAGAIN) { - return 0; - } - if (result != sizeof(TigrMessageData)) { - os_log_error(OS_LOG_DEFAULT, "Failed to read message from main thread: %{public}s", strerror(errno)); - return 0; - } - return 1; -} - -void viewWillTransitionToSize(id self, SEL _sel, CGSize size, id transitionCoordinator) { - // No animation, just set them - gState.screenW = size.width * gState.scaleFactor; - gState.screenH = size.height * gState.scaleFactor; - struct objc_super super = { - self, - objc_getClass("GLKViewController"), - }; - objc_msgSendSuper_t(void, CGSize, id)(&super, _sel, size, transitionCoordinator); -} - -BOOL prefersStatusBarHidden(id self, SEL _sel) { - return YES; -} - -BOOL hasText(id self, SEL _sel) { - return NO; -} - -void tigrShowKeyboard(int show) { - int expected = show ? KBD_HIDDEN : KBD_SHOWN; - int desired = show ? KBD_SHOWREQ : KBD_HIDEREQ; - atomic_compare_exchange_weak(&gState.keyboardState, &expected, desired); -} - -void insertText(id self, SEL _sel, id text) { - const char* inserted = UTF8StringFromNSString(text); - int codePoint = 0; - - do { - inserted = tigrDecodeUTF8(inserted, &codePoint); - if (codePoint != 0) { - KeyEvent event; - event.codePoint = codePoint; - event.keyCode = (codePoint < 128) ? codePoint : 0; - - TigrMessageData message = { - .message = KEY_EVENT, - .keyEvent = event, - }; - writeToRenderThread(&message); - } - } while (*inserted != 0); -} - -void deleteBackward(id self, SEL _sel) { - KeyEvent event; - event.codePoint = 0; - event.keyCode = 8; // BS - - TigrMessageData message = { .message = KEY_EVENT, - .keyEvent = { - .codePoint = 0, - .keyCode = 8, - } }; - writeToRenderThread(&message); -} - -BOOL canBecomeFirstResponder(id self, SEL _sel) { - return YES; -} - -BOOL canResignFirstResponder(id self, SEL _sel) { - return YES; -} - -enum RenderState { SWAPPED = 5150, RENDERED }; - -BOOL didFinishLaunchingWithOptions(id self, SEL _sel, id application, id options) { - id screen = objc_msgSend_id(class("UIScreen"), sel("mainScreen")); - CGRect bounds = objc_msgSend_t(CGRect)(screen, sel("bounds")); - CGSize size = bounds.size; - - id window = objc_alloc("UIWindow"); - window = objc_msgSend_t(id, CGRect)(window, sel("initWithFrame:"), bounds); - - Class ViewController = makeClass("TigrViewController", "GLKViewController"); - addMethod(ViewController, "viewWillTransitionToSize:withTransitionCoordinator:", viewWillTransitionToSize, - "v@:{CGSize}@"); - addMethod(ViewController, "prefersStatusBarHidden", prefersStatusBarHidden, "c@:"); - id vc = objc_msgSend_t(id)((id)ViewController, sel("alloc")); - vc = objc_msgSend_id(vc, sel("init")); - gState.viewController = vc; - objc_msgSend_t(void, int)(vc, sel("setPreferredFramesPerSecond:"), 60); - int framesPerSecond = objc_msgSend_t(int)(vc, sel("framesPerSecond")); - - id context = objc_alloc("EAGLContext"); - static int kEAGLRenderingAPIOpenGLES3 = 3; - context = objc_msgSend_t(id, int)(context, sel("initWithAPI:"), kEAGLRenderingAPIOpenGLES3); - gState.context = context; - - Class View = makeClass("TigrView", "GLKView"); - addMethod(View, "insertText:", insertText, "v@:@"); - addMethod(View, "deleteBackward", deleteBackward, "v@:"); - addMethod(View, "hasText", hasText, "c@:"); - addMethod(View, "canBecomeFirstResponder", canBecomeFirstResponder, "c@:"); - addMethod(View, "canResignFirstResponder", canResignFirstResponder, "c@:"); - - Protocol* UIKeyInput = objc_getProtocol("UIKeyInput"); - class_addProtocol(View, UIKeyInput); - - id view = objc_msgSend_id((id)View, sel("alloc")); - view = objc_msgSend_t(id, CGRect, id)(view, sel("initWithFrame:context:"), bounds, context); - gState.view = view; - objc_msgSend_t(void, BOOL)(view, sel("setMultipleTouchEnabled:"), YES); - objc_msgSend_t(void, id)(view, sel("setDelegate:"), self); - objc_msgSend_t(void, id)(vc, sel("setView:"), view); - objc_msgSend_t(void, id)(vc, sel("setDelegate:"), self); - objc_msgSend_t(void, id)(window, sel("setRootViewController:"), vc); - objc_msgSend_t(void)(window, sel("makeKeyAndVisible")); - - gState.scaleFactor = objc_msgSend_t(double)(view, sel("contentScaleFactor")); - gState.screenW = size.width * gState.scaleFactor; - gState.screenH = size.height * gState.scaleFactor; - - gState.frameCondition = objc_msgSend_t(id, int)(objc_alloc("NSConditionLock"), sel("initWithCondition:"), RENDERED); - objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED); - - id renderThread = objc_msgSend_t(id, id, SEL, id)(objc_alloc("NSThread"), sel("initWithTarget:selector:object:"), - self, sel("renderMain"), NULL); - objc_msgSend_t(void, int)(renderThread, sel("setQualityOfService:"), NSQualityOfServiceUserInteractive); - objc_msgSend_t(void, id)(renderThread, sel("setName:"), makeNSString("Tigr Render Thread")); - objc_msgSend_void(renderThread, sel("start")); - - return YES; -} - -void waitForFrame() { - objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), 0); - objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), RENDERED); - objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED); - objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context); -} - -void processKeyboardRequest() { - int showReq = KBD_SHOWREQ; - int hideReq = KBD_HIDEREQ; - - if (atomic_compare_exchange_weak(&gState.keyboardState, &showReq, KBD_SHOWN)) { - objc_msgSend_t(BOOL)(gState.view, sel("becomeFirstResponder")); - } else if (atomic_compare_exchange_weak(&gState.keyboardState, &hideReq, KBD_HIDDEN)) { - objc_msgSend_t(BOOL)(gState.view, sel("resignFirstResponder")); - } -} - -void drawInRect(id _self, SEL _sel, id view, CGRect rect) { - gState.timeSinceLastDraw = objc_msgSend_t(double)(gState.viewController, sel("timeSinceLastDraw")); - objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), SWAPPED); - objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED); - objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context); - - processKeyboardRequest(); -} - -extern void tigrMain(); - -void renderMain(id _self, SEL _sel) { - objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED); - objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context); - tigrMain(); -} - -enum { - UITouchPhaseBegan, - UITouchPhaseMoved, - UITouchPhaseStationary, - UITouchPhaseEnded, - UITouchPhaseCancelled, -}; - -void touches(id self, SEL sel, id touches, id event) { - id allTouches = objc_msgSend_t(id)(event, sel("allTouches")); - id enumerator = objc_msgSend_t(id)(allTouches, sel("objectEnumerator")); - id touch = 0; - InputState input = { - .numPoints = 0, - }; - while ((touch = objc_msgSend_t(id)(enumerator, sel("nextObject")))) { - CGPoint location = objc_msgSend_t(CGPoint, id)(touch, sel("locationInView:"), NULL); - int phase = objc_msgSend_t(int)(touch, sel("phase")); - switch (phase) { - case UITouchPhaseBegan: - case UITouchPhaseMoved: - case UITouchPhaseStationary: - input.points[input.numPoints].x = location.x * gState.scaleFactor; - input.points[input.numPoints].y = location.y * gState.scaleFactor; - input.numPoints++; - break; - } - if (input.numPoints >= MAX_TOUCH_POINTS) { - break; - } - } - TigrMessageData message = { - .message = SET_INPUT, - .inputState = input, - }; - writeToRenderThread(&message); -} - -Class tigrAppDelegate() { - static Class delegateClass = 0; - if (delegateClass != 0) { - return delegateClass; - } - - id application = objc_msgSend_id(class("UIApplication"), sel("sharedApplication")); - delegateClass = makeClass("TigrAppDelegate", "UIResponder"); - addMethod(delegateClass, "application:didFinishLaunchingWithOptions:", didFinishLaunchingWithOptions, "c@:@@"); - addMethod(delegateClass, "touchesBegan:withEvent:", touches, "v@:@@"); - addMethod(delegateClass, "touchesMoved:withEvent:", touches, "v@:@@"); - addMethod(delegateClass, "touchesEnded:withEvent:", touches, "v@:@@"); - addMethod(delegateClass, "touchesCancelled:withEvent:", touches, "v@:@@"); - addMethod(delegateClass, "glkView:drawInRect:", drawInRect, "v@:@{CGRect}"); - addMethod(delegateClass, "renderMain", renderMain, "v@:"); - objc_registerClassPair(delegateClass); - - int fds[2]; - if (pipe(fds) != 0) { - tigrError(0, "Failed to create message pipe"); - } - int flags = fcntl(fds[0], F_GETFL, 0); - fcntl(fds[0], F_SETFL, flags | O_NONBLOCK); - - gState.renderReadFd = fds[0]; - gState.mainWriteFd = fds[1]; - return delegateClass; -} - -Tigr* tigrWindow(int w, int h, const char* title, int flags) { - int scale = 1; - if (flags & TIGR_AUTO) { - // Always use a 1:1 pixel size. - scale = 1; - } else { - // See how big we can make it and still fit on-screen. - scale = tigrCalcScale(w, h, gState.screenW, gState.screenH); - } - - scale = tigrEnforceScale(scale, flags); - Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); - bmp->handle = (void*)4711; - TigrInternal* win = tigrInternal(bmp); - win->shown = 0; - win->closed = 0; - win->scale = scale; - - win->lastChar = 0; - win->flags = flags; - win->p1 = win->p2 = win->p3 = 0; - win->p4 = 1; - win->widgetsWanted = 0; - win->widgetAlpha = 0; - win->widgetsScale = 0; - win->widgets = 0; - win->gl.gl_legacy = 0; - - tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos); - tigrGAPICreate(bmp); - - return bmp; -} - -void processEvents(TigrInternal* win) { - memset(win->keys, 0, 255); - - TigrMessageData data; - - while (readFromMainThread(&data)) { - switch (data.message) { - case SET_INPUT: - gState.inputState = data.inputState; - break; - case KEY_EVENT: - win->keys[data.keyEvent.keyCode] = 1; - win->lastChar = data.keyEvent.codePoint; - break; - } - } -} - -static int toWindowX(TigrInternal* win, int x) { - return (x - win->pos[0]) / win->scale; -} - -static int toWindowY(TigrInternal* win, int y) { - return (y - win->pos[1]) / win->scale; -} - -void tigrUpdate(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - - processEvents(win); - - win->numTouchPoints = gState.inputState.numPoints; - for (int i = 0; i < win->numTouchPoints; i++) { - win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x); - win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y); - } - - win->mouseButtons = win->numTouchPoints; - if (win->mouseButtons > 0) { - win->mouseX = win->touchPoints[0].x; - win->mouseY = win->touchPoints[0].y; - } - - if (win->flags & TIGR_AUTO) { - tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale); - } else { - win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags); - } - - tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos); - tigrGAPIPresent(bmp, gState.screenW, gState.screenH); - waitForFrame(); -} - -int tigrClosed(Tigr* bmp) { - return 0; -} - -void tigrError(Tigr* bmp, const char* message, ...) { - char tmp[1024]; - - va_list args; - va_start(args, message); - vsnprintf(tmp, sizeof(tmp), message, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - os_log_error(OS_LOG_DEFAULT, "tigr fatal error: %{public}s\n", tmp); - - exit(1); -} - -float tigrTime() { - return (float)gState.timeSinceLastDraw; -} - -void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) { - TigrInternal* win = tigrInternal(bmp); - if (x) { - *x = win->mouseX; - } - if (y) { - *y = win->mouseY; - } - if (buttons) { - *buttons = win->mouseButtons; - } -} - -int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) { - TigrInternal* win = tigrInternal(bmp); - for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) { - points[i] = win->touchPoints[i]; - } - return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints; -} - -void tigrMouseWheel(Tigr* bmp, float* x, float* y) { - if (x) { - *x = 0; - } - if (y) { - *y = 0; - } -} - -void tigrFree(Tigr* bmp) { - if (bmp->handle) { - TigrInternal* win = tigrInternal(bmp); - } - free(bmp->pix); - free(bmp); -} - -int tigrGAPIBegin(Tigr* bmp) { - (void)bmp; - return 0; -} - -int tigrGAPIEnd(Tigr* bmp) { - (void)bmp; - return 0; -} - -int tigrKeyDown(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key]; -} - -int tigrKeyHeld(Tigr* bmp, int key) { - return tigrKeyDown(bmp, key); -} - -int tigrReadChar(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int c = win->lastChar; - win->lastChar = 0; - return c; -} - -extern void* _tigrReadFile(const char* fileName, int* length); - -void* tigrReadFile(const char* fileName, int* length) { - id mainBundle = objc_msgSend_id(class("NSBundle"), sel("mainBundle")); - id resourcePath = objc_msgSend_id(mainBundle, sel("resourcePath")); - resourcePath = joinNSStrings(resourcePath, makeNSString("/")); - resourcePath = joinNSStrings(resourcePath, makeNSString(fileName)); - return _tigrReadFile(UTF8StringFromNSString(resourcePath), length); -} - -#endif // __IOS__ - -#endif // #ifndef TIGR_HEADLESS - -//////// End of inlined file: tigr_ios.c //////// - -//////// Start of inlined file: tigr_android.h //////// - -#ifndef TIGR_ANDROID_H -#define TIGR_ANDROID_H -#ifdef __ANDROID__ - -#include <android/input.h> -#include <android/native_window.h> -#include <EGL/egl.h> - -typedef enum { - AE_INPUT, - AE_WINDOW_CREATED, - AE_WINDOW_DESTROYED, - AE_RESUME, - AE_CLOSE, -} AndroidEventType; - -typedef struct { - AndroidEventType type; - AInputEvent* inputEvent; - ANativeWindow* window; - double time; -} AndroidEvent; - -#ifdef __cplusplus -extern "C" { -#endif - -/// Calls from TIGR to Android side, render thread -extern int android_pollEvent(int (*eventHandler)(AndroidEvent, void*), void*); -extern void android_swap(EGLDisplay display, EGLSurface surface); -extern void* android_loadAsset(const char* filename, int* outLength); -extern void android_showKeyboard(int show); - -/// Calls from Android to TIGR side, main thread -void tigr_android_create(); -void tigr_android_destroy(); - -#ifdef __cplusplus -} -#endif - -#endif // __ANDROID__ -#endif // TIGR_ANDROID_H - -//////// End of inlined file: tigr_android.h //////// - -//////// Start of inlined file: tigr_linux.c //////// - -#ifndef TIGR_HEADLESS - -//#include "tigr_internal.h" - -#if __linux__ && !__ANDROID__ - -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <stdarg.h> -#include <sys/time.h> -#include <X11/X.h> -#include <X11/Xlib.h> -#include <X11/Xlocale.h> -#include <X11/XKBlib.h> -#include <X11/Xatom.h> -#include <GL/glx.h> - -static Display* dpy; -static Window root; -static XVisualInfo* vi; -static Atom wmDeleteMessage; -static XIM inputMethod; -static GLXFBConfig fbConfig; - -static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 0; - -static void tearDownX11Stuff() { - XCloseDisplay(dpy); -} - -static void initX11Stuff() { - static int done = 0; - if (!done) { - dpy = XOpenDisplay(NULL); - if (dpy == NULL) { - tigrError(0, "Cannot connect to X server"); - } - - root = DefaultRootWindow(dpy); - - static int attribList[] = { GLX_RENDER_TYPE, - GLX_RGBA_BIT, - GLX_DRAWABLE_TYPE, - GLX_WINDOW_BIT, - GLX_DOUBLEBUFFER, - 1, - GLX_RED_SIZE, - 1, - GLX_GREEN_SIZE, - 1, - GLX_BLUE_SIZE, - 1, - None }; - - int fbcCount = 0; - GLXFBConfig* fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribList, &fbcCount); - if (!fbc) { - tigrError(0, "Failed to choose FB config"); - } - fbConfig = fbc[0]; - XFree(fbc); - - vi = glXGetVisualFromFBConfig(dpy, fbConfig); - if (vi == NULL) { - tigrError(0, "No appropriate visual found"); - } - - GLXContext tmpCtx = glXCreateContext(dpy, vi, 0, GL_TRUE); - glXCreateContextAttribsARB = - (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); - glXDestroyContext(dpy, tmpCtx); - if (!glXCreateContextAttribsARB) { - tigrError(0, "Failed to get glXCreateContextAttribsARB"); - } - - inputMethod = XOpenIM(dpy, NULL, NULL, NULL); - if (inputMethod == NULL) { - tigrError(0, "Failed to create input method"); - } - - wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - - atexit(tearDownX11Stuff); - - done = 1; - } -} - -static int hasGLXExtension(Display* display, const char* wanted) { - const char* extensions = glXQueryExtensionsString(display, DefaultScreen(display)); - char* dup = strdup(extensions); - char* found = 0; - - for (char* start = dup;; start = 0) { - found = strtok(start, " "); - if (found == 0 || strcmp(found, wanted) == 0) { - break; - } - } - - free(dup); - return found != 0; -} - -static void setupVSync(Display* display, Window win) { - if (hasGLXExtension(display, "GLX_EXT_swap_control")) { - PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = - (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT"); - if (glXSwapIntervalEXT) { - glXSwapIntervalEXT(display, win, 1); - } - } else if (hasGLXExtension(display, "GLX_MESA_swap_control")) { - PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA = - (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA"); - if (glXSwapIntervalMESA) { - glXSwapIntervalMESA(1); - } - } else if (hasGLXExtension(display, "GLX_SGI_swap_control")) { - PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = - (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI"); - if (glXSwapIntervalSGI) { - glXSwapIntervalSGI(1); - } - } -} - -static void tigrHideCursor(TigrInternal* win) { - Cursor invisibleCursor; - Pixmap bitmapNoData; - XColor black; - static char noData[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - black.red = black.green = black.blue = 0; - - bitmapNoData = XCreateBitmapFromData(win->dpy, win->win, noData, 8, 8); - invisibleCursor = XCreatePixmapCursor(win->dpy, bitmapNoData, bitmapNoData, &black, &black, 0, 0); - XDefineCursor(win->dpy, win->win, invisibleCursor); - XFreeCursor(win->dpy, invisibleCursor); - XFreePixmap(win->dpy, bitmapNoData); -} - -typedef struct { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long inputMode; - unsigned long status; -} WindowHints; - -Tigr* tigrWindow(int w, int h, const char* title, int flags) { - Tigr* bmp = 0; - Colormap cmap; - XSetWindowAttributes swa; - Window xwin; - GLXContext glc; - XIC ic; - int scale; - - initX11Stuff(); - - if (flags & TIGR_AUTO) { - // Always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below. - scale = 1; - } else { - // See how big we can make it and still fit on-screen. - Screen* screen = DefaultScreenOfDisplay(dpy); - int maxW = WidthOfScreen(screen); - int maxH = HeightOfScreen(screen); - scale = tigrCalcScale(w, h, maxW, maxH); - } - - scale = tigrEnforceScale(scale, flags); - - cmap = XCreateColormap(dpy, root, vi->visual, AllocNone); - swa.colormap = cmap; - swa.event_mask = StructureNotifyMask; - - // Create window of wanted size - xwin = XCreateWindow(dpy, root, 0, 0, w * scale, h * scale, 0, vi->depth, InputOutput, vi->visual, - CWColormap | CWEventMask, &swa); - XMapWindow(dpy, xwin); - - if (flags & TIGR_FULLSCREEN) { - // https://superuser.com/questions/1680077/does-x11-actually-have-a-native-fullscreen-mode - Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", true); - Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", true); - XChangeProperty(dpy, xwin, wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_fullscreen, 1); - } else { - // Wait for window to get mapped - for (;;) { - XEvent e; - XNextEvent(dpy, &e); - if (e.type == MapNotify) { - break; - } - } - - // Reset size if we did not get the window size we wanted above. - XWindowAttributes wa; - XGetWindowAttributes(dpy, xwin, &wa); - scale = tigrCalcScale(w, h, wa.width, wa.height); - scale = tigrEnforceScale(scale, flags); - XResizeWindow(dpy, xwin, w * scale, h * scale); - } - - // Enable mouse events reporting (needed for mouse wheel events) - XSelectInput(dpy, xwin, ButtonPressMask); - - XTextProperty prop; - int result = Xutf8TextListToTextProperty(dpy, (char**)&title, 1, XUTF8StringStyle, &prop); - if (result == Success) { - Atom wmName = XInternAtom(dpy, "_NET_WM_NAME", 0); - XSetTextProperty(dpy, xwin, &prop, wmName); - XFree(prop.value); - } - - ic = XCreateIC(inputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xwin, NULL); - if (ic == NULL) { - printf("Failed to create input context\n"); - exit(0); - } - XSetICFocus(ic); - - XSetWMProtocols(dpy, xwin, &wmDeleteMessage, 1); - - int contextAttributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, None }; - glc = glXCreateContextAttribsARB(dpy, fbConfig, NULL, GL_TRUE, contextAttributes); - glXMakeCurrent(dpy, xwin, glc); - - setupVSync(dpy, xwin); - - bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); - bmp->handle = (void*)xwin; - - TigrInternal* win = tigrInternal(bmp); - win->win = xwin; - win->dpy = dpy; - win->glc = glc; - win->ic = ic; - - win->shown = 0; - win->closed = 0; - win->scale = scale; - - win->lastChar = 0; - win->flags = flags; - win->p1 = win->p2 = win->p3 = 0; - win->p4 = 1; - win->widgetsWanted = 0; - win->widgetAlpha = 0; - win->widgetsScale = 0; - win->widgets = 0; - win->gl.gl_legacy = 0; - - memset(win->keys, 0, 256); - memset(win->prev, 0, 256); - - if (flags & TIGR_NOCURSOR) { - tigrHideCursor(win); - } - - tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos); - tigrGAPICreate(bmp); - tigrGAPIBegin(bmp); - - return bmp; -} - -int tigrClosed(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - return win->win == 0; -} - -int tigrGAPIBegin(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - return glXMakeCurrent(win->dpy, win->win, win->glc) ? 0 : -1; -} - -int tigrGAPIEnd(Tigr* bmp) { - (void)bmp; - return glXMakeCurrent(NULL, 0, 0) ? 0 : -1; -} - -int tigrKeyDown(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key] && !win->prev[key]; -} - -int tigrKeyHeld(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key]; -} - -int tigrReadChar(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int c = win->lastChar; - win->lastChar = 0; - return c; -} - -uint8_t tigrKeyFromX11(KeySym sym) { - if (sym >= 'a' && sym <= 'z') { - return (uint8_t)sym - ('a' - 'A'); - } - - if (sym >= '0' && sym <= '9') { - return (uint8_t)sym; - } - - switch (sym) { - case XK_KP_0: - return TK_PAD0; - case XK_KP_1: - return TK_PAD1; - case XK_KP_2: - return TK_PAD2; - case XK_KP_3: - return TK_PAD3; - case XK_KP_4: - return TK_PAD4; - case XK_KP_5: - return TK_PAD5; - case XK_KP_6: - return TK_PAD6; - case XK_KP_7: - return TK_PAD7; - case XK_KP_8: - return TK_PAD8; - case XK_KP_9: - return TK_PAD9; - - case XK_KP_Multiply: - return TK_PADMUL; - case XK_KP_Divide: - return TK_PADDIV; - case XK_KP_Add: - return TK_PADADD; - case XK_KP_Subtract: - return TK_PADSUB; - case XK_KP_Decimal: - return TK_PADDOT; - case XK_KP_Enter: - return TK_PADENTER; - - case XK_F1: - return TK_F1; - case XK_F2: - return TK_F2; - case XK_F3: - return TK_F3; - case XK_F4: - return TK_F4; - case XK_F5: - return TK_F5; - case XK_F6: - return TK_F6; - case XK_F7: - return TK_F7; - case XK_F8: - return TK_F8; - case XK_F9: - return TK_F9; - case XK_F10: - return TK_F10; - case XK_F11: - return TK_F11; - case XK_F12: - return TK_F12; - - case XK_BackSpace: - return TK_BACKSPACE; - case XK_Tab: - return TK_TAB; - case XK_Return: - return TK_RETURN; - case XK_Pause: - return TK_PAUSE; - case XK_Caps_Lock: - return TK_CAPSLOCK; - case XK_Escape: - return TK_ESCAPE; - case XK_space: - return TK_SPACE; - - case XK_Page_Up: - return TK_PAGEUP; - case XK_Page_Down: - return TK_PAGEDN; - case XK_End: - return TK_END; - case XK_Home: - return TK_HOME; - case XK_Left: - return TK_LEFT; - case XK_Up: - return TK_UP; - case XK_Right: - return TK_RIGHT; - case XK_Down: - return TK_DOWN; - case XK_Insert: - return TK_INSERT; - case XK_Delete: - return TK_DELETE; - - case XK_Meta_L: - return TK_LWIN; - case XK_Meta_R: - return TK_RWIN; - case XK_Num_Lock: - return TK_NUMLOCK; - case XK_Scroll_Lock: - return TK_SCROLL; - case XK_Shift_L: - return TK_LSHIFT; - case XK_Shift_R: - return TK_RSHIFT; - case XK_Control_L: - return TK_LCONTROL; - case XK_Control_R: - return TK_RCONTROL; - case XK_Alt_L: - return TK_LALT; - case XK_Alt_R: - return TK_RALT; - - case XK_semicolon: - return TK_SEMICOLON; - case XK_equal: - return TK_EQUALS; - case XK_comma: - return TK_COMMA; - case XK_minus: - return TK_MINUS; - case XK_period: - return TK_DOT; - case XK_slash: - return TK_SLASH; - case XK_grave: - return TK_BACKTICK; - case XK_bracketleft: - return TK_LSQUARE; - case XK_backslash: - return TK_BACKSLASH; - case XK_bracketright: - return TK_RSQUARE; - case XK_apostrophe: - return TK_TICK; - } - return 0; -} - -static void tigrUpdateModifiers(TigrInternal* win) { - win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT]; - win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL]; - win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT]; -} - -static void tigrInterpretChar(TigrInternal* win, Window root, unsigned int keycode, unsigned int mask) { - XKeyEvent event; - memset(&event, 0, sizeof(event)); - event.type = KeyPress; - event.display = win->dpy; - event.root = root; - event.window = win->win; - event.state = mask; - event.keycode = keycode; - char inputTextUTF8[10]; - Status status = 0; - Xutf8LookupString(win->ic, &event, inputTextUTF8, sizeof(inputTextUTF8), NULL, &status); - - if (status == XLookupChars) { - tigrDecodeUTF8(inputTextUTF8, &win->lastChar); - } -} - -static void tigrProcessInput(TigrInternal* win, int winWidth, int winHeight) { - { - Window focused; - int revertTo; - XGetInputFocus(win->dpy, &focused, &revertTo); - - if (win->win != focused) { - return; - } - } - - Window root; - Window child; - int rootX; - int rootY; - int winX; - int winY; - unsigned int mask; - - if (XQueryPointer(win->dpy, win->win, &root, &child, &rootX, &rootY, &winX, &winY, &mask)) { - static unsigned int prevButtons; - unsigned int buttons = mask & (Button1Mask | Button2Mask | Button3Mask); - - win->mouseX = (winX - win->pos[0]) / win->scale; - win->mouseY = (winY - win->pos[1]) / win->scale; - - if (buttons != prevButtons && (winX > 0 && winX < winWidth) && (winY > 0 && winY < winHeight)) { - win->mouseButtons = (buttons & Button1Mask) ? 1 - : 0 | (buttons & Button3Mask) ? 2 - : 0 | (buttons & Button2Mask) ? 4 - : 0; - } - prevButtons = buttons; - } - - // Retrieve mouse wheel events - Cannot be done in the XQueryPointer call - // -> https://comp.unix.programmer.narkive.com/eOnjkQ6L/xlib-xquerypointer-and-button4mask-button5mask#post2 - // Button4 = WheelUp / Button5 = WheelDown - XEvent mouseButtonEvent; - while (XPending(win->dpy)) { - XPeekEvent(win->dpy, &mouseButtonEvent); - if (mouseButtonEvent.xany.type == ButtonPress) { - switch (mouseButtonEvent.xbutton.button) { - case Button4: - win->scrollDeltaY += 1; - break; - case Button5: - win->scrollDeltaY -= 1; - break; - case 6: - win->scrollDeltaX += 1; - break; - case 7: - win->scrollDeltaX -= 1; - break; - } - XNextEvent(win->dpy, &mouseButtonEvent); - } else { - break; - } - } - - static char prevKeys[32]; - char keys[32]; - XQueryKeymap(win->dpy, keys); - for (int i = 0; i < 32; i++) { - char thisBlock = keys[i]; - char prevBlock = prevKeys[i]; - if (thisBlock != prevBlock) { - for (int j = 0; j < 8; j++) { - int thisBit = thisBlock & 1; - int prevBit = prevBlock & 1; - thisBlock >>= 1; - prevBlock >>= 1; - if (thisBit != prevBit) { - int keyCode = 8 * i + j; - KeySym keySym = XkbKeycodeToKeysym(win->dpy, keyCode, 0, 0); - if (keySym != NoSymbol) { - int key = tigrKeyFromX11(keySym); - win->keys[key] = thisBit; - tigrUpdateModifiers(win); - - if (thisBit) { - tigrInterpretChar(win, root, keyCode, mask); - } - } - } - } - } - } - memcpy(prevKeys, keys, 32); - - XEvent event; - while (XCheckTypedWindowEvent(win->dpy, win->win, ClientMessage, &event)) { - if (event.xclient.data.l[0] == wmDeleteMessage) { - glXMakeCurrent(win->dpy, None, NULL); - glXDestroyContext(win->dpy, win->glc); - XDestroyWindow(win->dpy, win->win); - win->win = 0; - } - } - XFlush(win->dpy); -} - -void tigrUpdate(Tigr* bmp) { - XWindowAttributes gwa; - - TigrInternal* win = tigrInternal(bmp); - - memcpy(win->prev, win->keys, 256); - win->scrollDeltaX = 0; - win->scrollDeltaY = 0; - - XGetWindowAttributes(win->dpy, win->win, &gwa); - - if (win->flags & TIGR_AUTO) - tigrResize(bmp, gwa.width / win->scale, gwa.height / win->scale); - else - win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gwa.width, gwa.height), win->flags); - - tigrPosition(bmp, win->scale, gwa.width, gwa.height, win->pos); - glXMakeCurrent(win->dpy, win->win, win->glc); - tigrGAPIPresent(bmp, gwa.width, gwa.height); - glXSwapBuffers(win->dpy, win->win); - - tigrProcessInput(win, gwa.width, gwa.height); -} - -void tigrFree(Tigr* bmp) { - if (bmp->handle) { - TigrInternal* win = tigrInternal(bmp); - if (win->win) { - glXMakeCurrent(win->dpy, None, NULL); - glXDestroyContext(win->dpy, win->glc); - XDestroyWindow(win->dpy, win->win); - win->win = 0; - } - } - free(bmp->pix); - free(bmp); -} - -void tigrError(Tigr* bmp, const char* message, ...) { - char tmp[1024]; - - va_list args; - va_start(args, message); - vsnprintf(tmp, sizeof(tmp), message, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - printf("tigr fatal error: %s\n", tmp); - - exit(1); -} - -float tigrTime() { - static double lastTime = 0; - - struct timeval tv; - gettimeofday(&tv, NULL); - - double now = (double)tv.tv_sec + (tv.tv_usec / 1000000.0); - double elapsed = lastTime == 0 ? 0 : now - lastTime; - lastTime = now; - - return (float)elapsed; -} - -void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) { - TigrInternal* win = tigrInternal(bmp); - if (x) { - *x = win->mouseX; - } - if (y) { - *y = win->mouseY; - } - if (buttons) { - *buttons = win->mouseButtons; - } -} - -int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) { - int buttons = 0; - if (maxPoints > 0) { - tigrMouse(bmp, &points->x, &points->y, &buttons); - } - return buttons ? 1 : 0; -} - -void tigrScrollWheel(Tigr* bmp, float* x, float* y) { - TigrInternal* win; - win = tigrInternal(bmp); - if (x) { - *x = win->scrollDeltaX; - } - if (y) { - *y = win->scrollDeltaY; - } -} - -#endif // __linux__ && !__ANDROID__ - -#endif // #ifndef TIGR_HEADLESS - -//////// End of inlined file: tigr_linux.c //////// - -//////// Start of inlined file: tigr_android.c //////// - -#ifndef TIGR_HEADLESS - -//#include "tigr_internal.h" - -#ifdef __ANDROID__ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <android/log.h> -#include <android/native_window.h> -#include <android/input.h> - -#ifndef NDEBUG -#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "tigr", __VA_ARGS__)) -#else -#define LOGD(...) ((void)0) -#endif -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "tigr", __VA_ARGS__)) -#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "tigr", __VA_ARGS__)) - -typedef struct { - TigrTouchPoint points[MAX_TOUCH_POINTS]; - int numPoints; -} InputState; - -/// Global state -static struct { - ANativeWindow* window; - InputState inputState; - EGLDisplay display; - EGLSurface surface; - EGLint screenW; - EGLint screenH; - EGLConfig config; - double lastTime; - int closed; -} gState = { - .window = 0, - .inputState = { - .numPoints = 0, - }, - .display = EGL_NO_DISPLAY, - .surface = EGL_NO_SURFACE, - .screenW = 0, - .screenH = 0, - .config = 0, - .lastTime = 0, - .closed = 0, -}; - -static const EGLint contextAttribs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE }; - -static EGLConfig getGLConfig(EGLDisplay display) { - EGLConfig config = 0; - - const EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, - EGL_NONE }; - EGLint numConfigs; - - eglChooseConfig(display, attribs, NULL, 0, &numConfigs); - EGLConfig* supportedConfigs = (EGLConfig*)malloc(sizeof(EGLConfig) * numConfigs); - eglChooseConfig(display, attribs, supportedConfigs, numConfigs, &numConfigs); - - int i = 0; - for (; i < numConfigs; i++) { - EGLConfig* cfg = supportedConfigs[i]; - EGLint r, g, b, d; - if (eglGetConfigAttrib(display, cfg, EGL_RED_SIZE, &r) && - eglGetConfigAttrib(display, cfg, EGL_GREEN_SIZE, &g) && - eglGetConfigAttrib(display, cfg, EGL_BLUE_SIZE, &b) && - eglGetConfigAttrib(display, cfg, EGL_DEPTH_SIZE, &d) && r == 8 && g == 8 && b == 8 && d == 0) { - config = supportedConfigs[i]; - break; - } - } - - if (i == numConfigs) { - config = supportedConfigs[0]; - } - - if (config == NULL) { - tigrError(NULL, "Unable to initialize EGLConfig"); - } - - free(supportedConfigs); - - return config; -} - -/// Android interface, called from main thread - -void tigr_android_create() { - gState.closed = 0; - gState.window = 0; - gState.inputState.numPoints = 0; - gState.surface = EGL_NO_SURFACE; - gState.lastTime = 0; - - if (gState.display == EGL_NO_DISPLAY) { - gState.display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLBoolean status = eglInitialize(gState.display, NULL, NULL); - if (!status) { - tigrError(NULL, "Failed to init EGL"); - } - gState.config = getGLConfig(gState.display); - } -} - -void tigr_android_destroy() { - eglTerminate(gState.display); - gState.display = EGL_NO_DISPLAY; -} - -/// Internals /// - -static void logEglError() { - int error = eglGetError(); - switch (error) { - case EGL_BAD_DISPLAY: - LOGE("EGL error: Bad display"); - break; - case EGL_NOT_INITIALIZED: - LOGE("EGL error: Not initialized"); - break; - case EGL_BAD_NATIVE_WINDOW: - LOGE("EGL error: Bad native window"); - break; - case EGL_BAD_ALLOC: - LOGE("EGL error: Bad alloc"); - break; - case EGL_BAD_MATCH: - LOGE("EGL error: Bad match"); - break; - default: - LOGE("EGL error: %d", error); - } -} - -static void setupOpenGL() { - LOGD("setupOpenGL"); - assert(gState.surface == EGL_NO_SURFACE); - assert(gState.window != 0); - - gState.surface = eglCreateWindowSurface(gState.display, gState.config, gState.window, NULL); - if (gState.surface == EGL_NO_SURFACE) { - logEglError(); - } - assert(gState.surface != EGL_NO_SURFACE); - eglQuerySurface(gState.display, gState.surface, EGL_WIDTH, &gState.screenW); - eglQuerySurface(gState.display, gState.surface, EGL_HEIGHT, &gState.screenH); - LOGD("Screen is %d x %d", gState.screenW, gState.screenH); -} - -static void tearDownOpenGL() { - LOGD("tearDownOpenGL"); - if (gState.display != EGL_NO_DISPLAY) { - eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (gState.surface != EGL_NO_SURFACE) { - LOGD("eglDestroySurface"); - if (!eglDestroySurface(gState.display, gState.surface)) { - logEglError(); - } - gState.surface = EGL_NO_SURFACE; - } - } -} - -static int tigrKeyFromAndroidKey(int key) { - switch (key) { - case AKEYCODE_Q: - return 'Q'; - case AKEYCODE_W: - return 'W'; - case AKEYCODE_E: - return 'E'; - case AKEYCODE_R: - return 'R'; - case AKEYCODE_T: - return 'T'; - case AKEYCODE_Y: - return 'Y'; - case AKEYCODE_U: - return 'U'; - case AKEYCODE_I: - return 'I'; - case AKEYCODE_O: - return 'O'; - case AKEYCODE_P: - return 'P'; - - case AKEYCODE_A: - return 'A'; - case AKEYCODE_S: - return 'S'; - case AKEYCODE_D: - return 'D'; - case AKEYCODE_F: - return 'F'; - case AKEYCODE_G: - return 'G'; - case AKEYCODE_H: - return 'H'; - case AKEYCODE_J: - return 'J'; - case AKEYCODE_K: - return 'K'; - case AKEYCODE_L: - return 'L'; - - case AKEYCODE_Z: - return 'Z'; - case AKEYCODE_X: - return 'X'; - case AKEYCODE_C: - return 'C'; - case AKEYCODE_V: - return 'V'; - case AKEYCODE_B: - return 'B'; - case AKEYCODE_N: - return 'N'; - case AKEYCODE_M: - return 'M'; - - case AKEYCODE_0: - return '0'; - case AKEYCODE_1: - return '1'; - case AKEYCODE_2: - return '2'; - case AKEYCODE_3: - return '3'; - case AKEYCODE_4: - return '4'; - case AKEYCODE_5: - return '5'; - case AKEYCODE_6: - return '6'; - case AKEYCODE_7: - return '7'; - case AKEYCODE_8: - return '8'; - case AKEYCODE_9: - return '9'; - - case AKEYCODE_NUMPAD_0: - return TK_PAD0; - case AKEYCODE_NUMPAD_1: - return TK_PAD1; - case AKEYCODE_NUMPAD_2: - return TK_PAD2; - case AKEYCODE_NUMPAD_3: - return TK_PAD3; - case AKEYCODE_NUMPAD_4: - return TK_PAD4; - case AKEYCODE_NUMPAD_5: - return TK_PAD5; - case AKEYCODE_NUMPAD_6: - return TK_PAD6; - case AKEYCODE_NUMPAD_7: - return TK_PAD7; - case AKEYCODE_NUMPAD_8: - return TK_PAD8; - case AKEYCODE_NUMPAD_9: - return TK_PAD9; - - case AKEYCODE_NUMPAD_MULTIPLY: - return TK_PADMUL; - case AKEYCODE_NUMPAD_DIVIDE: - return TK_PADDIV; - case AKEYCODE_NUMPAD_ADD: - return TK_PADADD; - case AKEYCODE_NUMPAD_SUBTRACT: - return TK_PADSUB; - case AKEYCODE_NUMPAD_ENTER: - return TK_PADENTER; - case AKEYCODE_NUMPAD_DOT: - return TK_PADDOT; - - case AKEYCODE_F1: - return TK_F1; - case AKEYCODE_F2: - return TK_F2; - case AKEYCODE_F3: - return TK_F3; - case AKEYCODE_F4: - return TK_F4; - case AKEYCODE_F5: - return TK_F5; - case AKEYCODE_F6: - return TK_F6; - case AKEYCODE_F7: - return TK_F7; - case AKEYCODE_F8: - return TK_F8; - case AKEYCODE_F9: - return TK_F9; - case AKEYCODE_F10: - return TK_F10; - case AKEYCODE_F11: - return TK_F11; - case AKEYCODE_F12: - return TK_F12; - - case AKEYCODE_SHIFT_LEFT: - return TK_LSHIFT; - case AKEYCODE_SHIFT_RIGHT: - return TK_RSHIFT; - case AKEYCODE_CTRL_LEFT: - return TK_LCONTROL; - case AKEYCODE_CTRL_RIGHT: - return TK_RCONTROL; - case AKEYCODE_ALT_LEFT: - return TK_LALT; - case AKEYCODE_ALT_RIGHT: - return TK_RALT; - case AKEYCODE_META_LEFT: - return TK_LWIN; - case AKEYCODE_META_RIGHT: - return TK_RWIN; - - case AKEYCODE_DEL: - return TK_BACKSPACE; - case AKEYCODE_TAB: - return TK_TAB; - case AKEYCODE_ENTER: - return TK_RETURN; - case AKEYCODE_CAPS_LOCK: - return TK_CAPSLOCK; - case AKEYCODE_ESCAPE: - case AKEYCODE_BACK: - return TK_ESCAPE; - case AKEYCODE_SPACE: - return TK_SPACE; - - case AKEYCODE_PAGE_UP: - return TK_PAGEUP; - case AKEYCODE_PAGE_DOWN: - return TK_PAGEDN; - case AKEYCODE_MOVE_END: - return TK_END; - case AKEYCODE_MOVE_HOME: - return TK_HOME; - case AKEYCODE_DPAD_LEFT: - return TK_LEFT; - case AKEYCODE_DPAD_RIGHT: - return TK_RIGHT; - case AKEYCODE_DPAD_UP: - return TK_UP; - case AKEYCODE_DPAD_DOWN: - return TK_DOWN; - - case AKEYCODE_INSERT: - return TK_INSERT; - case AKEYCODE_FORWARD_DEL: - return TK_DELETE; - case AKEYCODE_NUM_LOCK: - return TK_NUMLOCK; - case AKEYCODE_SCROLL_LOCK: - return TK_SCROLL; - - case AKEYCODE_SEMICOLON: - return TK_SEMICOLON; - case AKEYCODE_EQUALS: - return TK_EQUALS; - case AKEYCODE_COMMA: - return TK_COMMA; - case AKEYCODE_MINUS: - return TK_MINUS; - case AKEYCODE_PERIOD: - return TK_DOT; - case AKEYCODE_SLASH: - return TK_SLASH; - case AKEYCODE_BACKSLASH: - return TK_BACKSLASH; - case AKEYCODE_GRAVE: - return TK_BACKTICK; - case AKEYCODE_APOSTROPHE: - return TK_TICK; - case AKEYCODE_LEFT_BRACKET: - return TK_LSQUARE; - case AKEYCODE_RIGHT_BRACKET: - return TK_RSQUARE; - - default: - return 0; - } -} - -static int processInputEvent(AInputEvent* event, TigrInternal* win) { - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - int32_t action = AMotionEvent_getAction(event); - int32_t actionCode = action & AMOTION_EVENT_ACTION_MASK; - - size_t touchPoints = AMotionEvent_getPointerCount(event); - size_t releasedIndex = -1; - if (actionCode == AMOTION_EVENT_ACTION_POINTER_UP) { - releasedIndex = - (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - size_t targetPointCount = 0; - - for (size_t i = 0; i < touchPoints && i < MAX_TOUCH_POINTS; i++) { - if (i == releasedIndex) { - continue; - } - gState.inputState.points[targetPointCount].x = AMotionEvent_getX(event, i); - gState.inputState.points[targetPointCount].y = AMotionEvent_getY(event, i); - targetPointCount++; - } - - if (actionCode == AMOTION_EVENT_ACTION_UP || actionCode == AMOTION_EVENT_ACTION_CANCEL) { - gState.inputState.numPoints = 0; - } else { - gState.inputState.numPoints = targetPointCount; - } - return 1; - } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { - if (!win) { - return 1; - } - int32_t deviceID = AInputEvent_getDeviceId(event); - // KeyCharacterMap#VIRTUAL_KEYBOARD == -1 - if (deviceID != -1) { - return 1; - } - int32_t action = AKeyEvent_getAction(event); - int32_t keyCode = AKeyEvent_getKeyCode(event); - int key = tigrKeyFromAndroidKey(keyCode); - // We pass the character in the scancode field from the Java side - int32_t unicodeChar = AKeyEvent_getScanCode(event); - - if (action == AKEY_EVENT_ACTION_DOWN) { - win->keys[key] = 1; - win->lastChar = unicodeChar; - } else if (action == AKEY_EVENT_ACTION_UP) { - win->released[key] = 1; - } - return 1; - } - return 0; -} - -static int handleEvent(AndroidEvent event, void* userData) { - switch (event.type) { - case AE_WINDOW_CREATED: - gState.window = event.window; - setupOpenGL(); - break; - - case AE_WINDOW_DESTROYED: - tearDownOpenGL(); - gState.window = 0; - break; - - case AE_INPUT: - return processInputEvent(event.inputEvent, (TigrInternal*)userData); - - case AE_RESUME: - gState.lastTime = event.time; - break; - - case AE_CLOSE: - gState.closed = 1; - break; - - default: - LOGE("Unhandled event type: %d", event.type); - return 0; - } - - return 1; -} - -static int processEvents(TigrInternal* win) { - if (gState.closed) { - return 0; - } - - while (android_pollEvent(handleEvent, win)) { - if (gState.closed) { - return 0; - } - } - - return 1; -} - -static Tigr* refreshWindow(Tigr* bmp) { - if (bmp->handle == gState.window) { - return bmp; - } - - bmp->handle = gState.window; - if (gState.window == 0) { - return 0; - } - - TigrInternal* win = tigrInternal(bmp); - - int scale = 1; - if (win->flags & TIGR_AUTO) { - // Always use a 1:1 pixel size. - scale = 1; - } else { - // See how big we can make it and still fit on-screen. - scale = tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH); - } - - win->scale = tigrEnforceScale(scale, win->flags); - - return bmp; -} - -/// TIGR interface implementation, called from render thread /// - -Tigr* tigrWindow(int w, int h, const char* title, int flags) { - while (gState.window == NULL) { - if (!processEvents(0)) { - return NULL; - } - } - - EGLContext context = eglCreateContext(gState.display, gState.config, NULL, contextAttribs); - - int scale = 1; - if (flags & TIGR_AUTO) { - // Always use a 1:1 pixel size. - scale = 1; - } else { - // See how big we can make it and still fit on-screen. - scale = tigrCalcScale(w, h, gState.screenW, gState.screenH); - } - - scale = tigrEnforceScale(scale, flags); - - Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); - bmp->handle = (void*)gState.window; - - TigrInternal* win = tigrInternal(bmp); - win->context = context; - - win->shown = 0; - win->closed = 0; - win->scale = scale; - - win->lastChar = 0; - win->flags = flags; - win->p1 = win->p2 = win->p3 = 0; - win->p4 = 1; - win->widgetsWanted = 0; - win->widgetAlpha = 0; - win->widgetsScale = 0; - win->widgets = 0; - win->gl.gl_legacy = 0; - - memset(win->keys, 0, 256); - memset(win->prev, 0, 256); - memset(win->released, 0, 256); - - tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos); - - if (eglMakeCurrent(gState.display, gState.surface, gState.surface, context) == EGL_FALSE) { - LOGE("Unable to eglMakeCurrent"); - return 0; - } - - tigrGAPICreate(bmp); - - return bmp; -} - -int tigrClosed(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - return win->closed; -} - -int tigrGAPIBegin(Tigr* bmp) { - assert(gState.display != EGL_NO_DISPLAY); - assert(gState.surface != EGL_NO_SURFACE); - - TigrInternal* win = tigrInternal(bmp); - if (eglMakeCurrent(gState.display, gState.surface, gState.surface, win->context) == EGL_FALSE) { - return -1; - } - return 0; -} - -int tigrGAPIEnd(Tigr* bmp) { - (void)bmp; - eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - return 0; -} - -int tigrKeyDown(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key] && !win->prev[key]; -} - -int tigrKeyHeld(Tigr* bmp, int key) { - TigrInternal* win; - assert(key < 256); - win = tigrInternal(bmp); - return win->keys[key]; -} - -int tigrReadChar(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - int c = win->lastChar; - win->lastChar = 0; - return c; -} - -static void tigrUpdateModifiers(TigrInternal* win) { - win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT]; - win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL]; - win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT]; -} - -static int toWindowX(TigrInternal* win, int x) { - return (x - win->pos[0]) / win->scale; -} - -static int toWindowY(TigrInternal* win, int y) { - return (y - win->pos[1]) / win->scale; -} - -void tigrUpdate(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - memcpy(win->prev, win->keys, 256); - for (int i = 0; i < 256; i++) { - win->keys[i] ^= win->released[i]; - win->released[i] = 0; - } - - if (!processEvents(win)) { - win->closed = 1; - return; - } - - tigrUpdateModifiers(win); - - if (gState.window == 0) { - return; - } - - bmp = refreshWindow(bmp); - if (bmp == 0) { - return; - } - - win->numTouchPoints = gState.inputState.numPoints; - for (int i = 0; i < win->numTouchPoints; i++) { - win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x); - win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y); - } - - win->mouseButtons = win->numTouchPoints; - if (win->mouseButtons > 0) { - win->mouseX = win->touchPoints[0].x; - win->mouseY = win->touchPoints[0].y; - } - - if (win->flags & TIGR_AUTO) { - tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale); - } else { - win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags); - } - - tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos); - tigrGAPIBegin(bmp); - tigrGAPIPresent(bmp, gState.screenW, gState.screenH); - android_swap(gState.display, gState.surface); - tigrGAPIEnd(bmp); -} - -void tigrFree(Tigr* bmp) { - if (bmp->handle) { - TigrInternal* win = tigrInternal(bmp); - - eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (win->context != EGL_NO_CONTEXT) { - // Win closed means app windows has closed, and the call would fail. - if (!win->closed) { - tigrGAPIDestroy(bmp); - } - eglDestroyContext(gState.display, win->context); - } - - win->context = EGL_NO_CONTEXT; - } - free(bmp->pix); - free(bmp); -} - -void tigrError(Tigr* bmp, const char* message, ...) { - char tmp[1024]; - - va_list args; - va_start(args, message); - vsnprintf(tmp, sizeof(tmp), message, args); - tmp[sizeof(tmp) - 1] = 0; - va_end(args); - - LOGE("tigr fatal error: %s\n", tmp); - - exit(1); -} - -float tigrTime() { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - double now = (double)ts.tv_sec + (ts.tv_nsec / 1000000000.0); - double elapsed = gState.lastTime == 0 ? 0 : now - gState.lastTime; - gState.lastTime = now; - - return (float)elapsed; -} - -void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) { - TigrInternal* win = tigrInternal(bmp); - if (x) { - *x = win->mouseX; - } - if (y) { - *y = win->mouseY; - } - if (buttons) { - *buttons = win->mouseButtons; - } -} - -int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) { - TigrInternal* win = tigrInternal(bmp); - for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) { - points[i] = win->touchPoints[i]; - } - return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints; -} - -void tigrMouseWheel(Tigr* bmp, float* x, float* y) { - if (x) { - *x = 0; - } - if (y) { - *y = 0; - } -} - -void* tigrReadFile(const char* fileName, int* length) { - if (length != 0) { - *length = 0; - } - - void* asset = android_loadAsset(fileName, length); - return asset; -} - -#endif // __ANDROID__ -#endif // #ifndef TIGR_HEADLESS - -//////// End of inlined file: tigr_android.c //////// - -//////// Start of inlined file: tigr_gl.c //////// - -#ifndef TIGR_HEADLESS - -//#include "tigr_internal.h" -#include <assert.h> - -#ifdef TIGR_GAPI_GL -#if __linux__ -#if __ANDROID__ -#include <GLES3/gl3.h> -#else -#define GLX_GLXEXT_PROTOTYPES -#include <GL/glext.h> -#endif -#endif -extern const char tigr_upscale_gl_vs[], tigr_upscale_gl_fs[], tigr_default_fx_gl_fs[]; -extern const int tigr_upscale_gl_vs_size, tigr_upscale_gl_fs_size, tigr_default_fx_gl_fs_size; - -#ifdef _WIN32 - -#ifdef TIGR_GAPI_GL_WIN_USE_GLEXT -#include <glext.h> -#include <wglext.h> -#else // short version of glext.h and wglext.h so we don't need to depend on them -#ifndef APIENTRY -#define APIENTRY -#endif -#ifndef APIENTRYP -#define APIENTRYP APIENTRY* -#endif -typedef ptrdiff_t GLsizeiptr; -#define GL_COMPILE_STATUS 0x8B81 -#define GL_LINK_STATUS 0x8B82 -#define GL_ARRAY_BUFFER 0x8892 -#define GL_STATIC_DRAW 0x88E4 -#define GL_VERTEX_SHADER 0x8B31 -#define GL_FRAGMENT_SHADER 0x8B30 -#define GL_BGRA 0x80E1 -#define GL_TEXTURE0 0x84C0 -typedef void(APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays); -typedef void(APIENTRYP PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint* buffers); -typedef void(APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); -typedef void(APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void* data, GLenum usage); -typedef void(APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); -typedef void(APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); -typedef void(APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, - GLint size, - GLenum type, - GLboolean normalized, - GLsizei stride, - const void* pointer); -typedef GLuint(APIENTRYP PFNGLCREATESHADERPROC)(GLenum type); -typedef char GLchar; -typedef void(APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, - GLsizei count, - const GLchar* const* string, - const GLint* length); -typedef void(APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); -typedef GLuint(APIENTRYP PFNGLCREATEPROGRAMPROC)(void); -typedef void(APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); -typedef void(APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); -typedef void(APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); -typedef void(APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); -typedef void(APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint* params); -typedef void(APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); -typedef void(APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint* params); -typedef void(APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog); -typedef void(APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); -typedef GLint(APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar* name); -typedef void(APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void(APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, - GLsizei count, - GLboolean transpose, - const GLfloat* value); -typedef void(APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_COLOR_BITS_ARB 0x2014 -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_TYPE_RGBA_ARB 0x202B -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, - const int* piAttribIList, - const FLOAT* pfAttribFList, - UINT nMaxFormats, - int* piFormats, - UINT* nNumFormats); -typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int* attribList); -#endif - -PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat; -PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs; -PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; -PFNGLGENBUFFERSARBPROC glGenBuffers; -PFNGLBINDBUFFERPROC glBindBuffer; -PFNGLBUFFERDATAPROC glBufferData; -PFNGLBINDVERTEXARRAYPROC glBindVertexArray; -PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; -PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; -PFNGLCREATESHADERPROC glCreateShader; -PFNGLSHADERSOURCEPROC glShaderSource; -PFNGLCOMPILESHADERPROC glCompileShader; -PFNGLCREATEPROGRAMPROC glCreateProgram; -PFNGLATTACHSHADERPROC glAttachShader; -PFNGLLINKPROGRAMPROC glLinkProgram; -PFNGLDELETESHADERPROC glDeleteShader; -PFNGLDELETEPROGRAMPROC glDeleteProgram; -PFNGLGETSHADERIVPROC glGetShaderiv; -PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; -PFNGLGETPROGRAMIVPROC glGetProgramiv; -PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; -PFNGLUSEPROGRAMPROC glUseProgram; -PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; -PFNGLUNIFORM4FPROC glUniform4f; -PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; -PFNGLACTIVETEXTUREPROC glActiveTexture; -int tigrGL11Init(Tigr* bmp) { - int pixel_format; - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), - 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE, - PFD_TYPE_RGBA, - 32, // color bits - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 24, // depth - 8, // stencil - 0, - PFD_MAIN_PLANE, // is it ignored ? - 0, - 0, - 0, - 0 }; - if (!(gl->dc = GetDC((HWND)bmp->handle))) { - tigrError(bmp, "Cannot create OpenGL device context.\n"); - return -1; - } - if (!(pixel_format = ChoosePixelFormat(gl->dc, &pfd))) { - tigrError(bmp, "Cannot choose OpenGL pixel format.\n"); - return -1; - } - if (!SetPixelFormat(gl->dc, pixel_format, &pfd)) { - tigrError(bmp, "Cannot set OpenGL pixel format.\n"); - return -1; - } - if (!(gl->hglrc = wglCreateContext(gl->dc))) { - tigrError(bmp, "Cannot create OpenGL context.\n"); - return -1; - } - if (!wglMakeCurrent(gl->dc, gl->hglrc)) { - tigrError(bmp, "Cannot activate OpenGL context.\n"); - return -1; - } - gl->gl_legacy = 1; - return 0; -} -int tigrGL33Init(Tigr* bmp) { - int pixel_format; - UINT num_formats; - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - - wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); - wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); - glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays"); - glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers"); - glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer"); - glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData"); - glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray"); - glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray"); - glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); - glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); - glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); - glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); - glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"); - glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"); - glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"); - glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"); - glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram"); - glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"); - glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); - glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv"); - glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog"); - glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"); - glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"); - glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f"); - glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv"); - glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture"); - - if (!wglChoosePixelFormat || !wglCreateContextAttribs) { - tigrError(bmp, "Cannot create OpenGL context.\n"); - return -1; - } - const int attribList[] = { WGL_DRAW_TO_WINDOW_ARB, - GL_TRUE, - WGL_SUPPORT_OPENGL_ARB, - GL_TRUE, - WGL_DOUBLE_BUFFER_ARB, - GL_TRUE, - WGL_PIXEL_TYPE_ARB, - WGL_TYPE_RGBA_ARB, - WGL_COLOR_BITS_ARB, - 32, - WGL_DEPTH_BITS_ARB, - 24, - WGL_STENCIL_BITS_ARB, - 8, - 0 }; - int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, 0 }; - if (!wglChoosePixelFormat(gl->dc, attribList, NULL, 1, &pixel_format, &num_formats)) { - tigrError(bmp, "Cannot choose OpenGL pixel format.\n"); - return -1; - } - if (!(gl->hglrc = wglCreateContextAttribs(gl->dc, gl->hglrc, attribs))) { - tigrError(bmp, "Cannot create OpenGL context attribs.\n"); - return -1; - } - if (!wglMakeCurrent(gl->dc, gl->hglrc)) { - tigrError(bmp, "Cannot activate OpenGL context.\n"); - return -1; - } - gl->gl_legacy = 0; - return 0; -} -#endif - -void tigrCheckGLError(const char* state) { - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - tigrError(NULL, "got GL error %x when doing %s\n", err, state); - } -} - -void tigrCheckShaderErrors(GLuint object) { - GLint success; - GLchar info[2048]; - glGetShaderiv(object, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(object, sizeof(info), NULL, info); - tigrError(NULL, "shader compile error : %s\n", info); - } -} - -void tigrCheckProgramErrors(GLuint object) { - GLint success; - GLchar info[2048]; - glGetProgramiv(object, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(object, sizeof(info), NULL, info); - tigrError(NULL, "shader link error : %s\n", info); - } -} - -void tigrCreateShaderProgram(GLStuff* gl, const char* fxSource, int fxSize) { - if (gl->program != 0) { - glDeleteProgram(gl->program); - gl->program = 0; - } - - GLuint vs = glCreateShader(GL_VERTEX_SHADER); - const char* vs_source = (const char*)&tigr_upscale_gl_vs; - glShaderSource(vs, 1, &vs_source, &tigr_upscale_gl_vs_size); - glCompileShader(vs); - tigrCheckShaderErrors(vs); - - GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - const char* fs_sources[] = { - (const char*)tigr_upscale_gl_fs, - fxSource, - }; - const int fs_lengths[] = { - tigr_upscale_gl_fs_size, - fxSize, - }; - glShaderSource(fs, 2, fs_sources, fs_lengths); - glCompileShader(fs); - tigrCheckShaderErrors(fs); - - gl->program = glCreateProgram(); - glAttachShader(gl->program, vs); - glAttachShader(gl->program, fs); - glLinkProgram(gl->program); - tigrCheckProgramErrors(gl->program); - glDeleteShader(vs); - glDeleteShader(fs); - - gl->uniform_projection = glGetUniformLocation(gl->program, "projection"); - gl->uniform_model = glGetUniformLocation(gl->program, "model"); - gl->uniform_parameters = glGetUniformLocation(gl->program, "parameters"); -} - -void tigrGAPICreate(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - GLuint VBO; - GLfloat vertices[] = { // pos uv - 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f - }; - -#ifdef _WIN32 - if (tigrGL11Init(bmp)) - return; - tigrGL33Init(bmp); -#endif - - if (!gl->gl_legacy) { - // create vao - glGenVertexArrays(1, &gl->vao); - glGenBuffers(1, &VBO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindVertexArray(gl->vao); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL); - - // create program - tigrCreateShaderProgram(gl, tigr_default_fx_gl_fs, tigr_default_fx_gl_fs_size); - } - - // create textures - if (gl->gl_legacy) { - glEnable(GL_TEXTURE_2D); - } - glGenTextures(2, gl->tex); - for (int i = 0; i < 2; ++i) { - glBindTexture(GL_TEXTURE_2D, gl->tex[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - } - - tigrCheckGLError("initialization"); -} - -void tigrGAPIDestroy(Tigr* bmp) { - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - - if (tigrGAPIBegin(bmp) < 0) { - tigrError(bmp, "Cannot activate OpenGL context.\n"); - return; - } - - if (!gl->gl_legacy) { - glDeleteTextures(2, gl->tex); - glDeleteProgram(gl->program); - } - - tigrCheckGLError("destroy"); - - if (tigrGAPIEnd(bmp) < 0) { - tigrError(bmp, "Cannot deactivate OpenGL context.\n"); - return; - } -} - -void tigrGAPIDraw(int legacy, GLuint uniform_model, GLuint tex, Tigr* bmp, int x1, int y1, int x2, int y2) { - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bmp->w, bmp->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp->pix); - - if (!legacy) { - float sx = (float)(x2 - x1); - float sy = (float)(y2 - y1); - float tx = (float)x1; - float ty = (float)y1; - - float model[16] = { sx, 0.0f, 0.0f, 0.0f, 0.0f, sy, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, tx, ty, 0.0f, 1.0f }; - - glUniformMatrix4fv(uniform_model, 1, GL_FALSE, model); - glDrawArrays(GL_TRIANGLES, 0, 6); - } else { -#if !(__APPLE__ || __ANDROID__) - glBegin(GL_QUADS); - glTexCoord2f(1.0f, 0.0f); - glVertex2i(x2, y1); - glTexCoord2f(0.0f, 0.0f); - glVertex2i(x1, y1); - glTexCoord2f(0.0f, 1.0f); - glVertex2i(x1, y2); - glTexCoord2f(1.0f, 1.0f); - glVertex2i(x2, y2); - glEnd(); -#else - assert(0); -#endif - } -} - -void tigrGAPIPresent(Tigr* bmp, int w, int h) { - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - - glViewport(0, 0, w, h); - if (!gl->gl_user_opengl_rendering) { - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - } - - if (!gl->gl_legacy) { - float projection[16] = { 2.0f / w, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f / h, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f }; - - glActiveTexture(GL_TEXTURE0); - glBindVertexArray(gl->vao); - glUseProgram(gl->program); - glUniformMatrix4fv(gl->uniform_projection, 1, GL_FALSE, projection); - glUniform4f(gl->uniform_parameters, win->p1, win->p2, win->p3, win->p4); - } else { -#if !(__APPLE__ || __ANDROID__) - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, w, h, 0, -1.0f, 1.0f); - glEnable(GL_TEXTURE_2D); -#else - assert(0); -#endif - } - - if (gl->gl_user_opengl_rendering) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisable(GL_BLEND); - } - tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[0], bmp, win->pos[0], win->pos[1], win->pos[2], win->pos[3]); - - if (win->widgetsScale > 0) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[1], win->widgets, - (int)(w - win->widgets->w * win->widgetsScale), 0, w, (int)(win->widgets->h * win->widgetsScale)); - } - - tigrCheckGLError("present"); - - gl->gl_user_opengl_rendering = 0; -} - -#endif -#endif // #ifndef TIGR_HEADLESS -//////// End of inlined file: tigr_gl.c //////// - -//////// Start of inlined file: tigr_utils.c //////// - -//#include "tigr_internal.h" -#include <stdio.h> -#include <stdlib.h> - -#ifndef __ANDROID__ - -#ifdef __IOS__ -void* _tigrReadFile(const char* fileName, int* length) { -#else -void* tigrReadFile(const char* fileName, int* length) { -#endif - // TODO - unicode? - FILE* file; - char* data; - size_t len; - - if (length) - *length = 0; - - file = fopen(fileName, "rb"); - if (!file) - return NULL; - - fseek(file, 0, SEEK_END); - len = ftell(file); - fseek(file, 0, SEEK_SET); - - data = (char*)malloc(len + 1); - if (!data) { - fclose(file); - return NULL; - } - - if (fread(data, 1, len, file) != len) { - free(data); - fclose(file); - return NULL; - } - data[len] = '\0'; - fclose(file); - - if (length) - *length = len; - - return data; -} - -#endif // __ANDROID__ - -// Reads a single UTF8 codepoint. -const char* tigrDecodeUTF8(const char* text, int* cp) { - unsigned char c = *text++; - int extra = 0, min = 0; - *cp = 0; - if (c >= 0xf0) { - *cp = c & 0x07; - extra = 3; - min = 0x10000; - } else if (c >= 0xe0) { - *cp = c & 0x0f; - extra = 2; - min = 0x800; - } else if (c >= 0xc0) { - *cp = c & 0x1f; - extra = 1; - min = 0x80; - } else if (c >= 0x80) { - *cp = 0xfffd; - } else { - *cp = c; - } - while (extra--) { - c = *text++; - if ((c & 0xc0) != 0x80) { - *cp = 0xfffd; - break; - } - (*cp) = ((*cp) << 6) | (c & 0x3f); - } - if (*cp < min) { - *cp = 0xfffd; - } - return text; -} - -char* tigrEncodeUTF8(char* text, int cp) { - if (cp < 0 || cp > 0x10ffff) { - cp = 0xfffd; - } - -#define EMIT(X, Y, Z) *text++ = X | ((cp >> Y) & Z) - if (cp < 0x80) { - EMIT(0x00, 0, 0x7f); - } else if (cp < 0x800) { - EMIT(0xc0, 6, 0x1f); - EMIT(0x80, 0, 0x3f); - } else if (cp < 0x10000) { - EMIT(0xe0, 12, 0xf); - EMIT(0x80, 6, 0x3f); - EMIT(0x80, 0, 0x3f); - } else { - EMIT(0xf0, 18, 0x7); - EMIT(0x80, 12, 0x3f); - EMIT(0x80, 6, 0x3f); - EMIT(0x80, 0, 0x3f); - } - return text; -#undef EMIT -} - -#ifndef TIGR_HEADLESS - -int tigrBeginOpenGL(Tigr* bmp) { -#ifdef TIGR_GAPI_GL - TigrInternal* win = tigrInternal(bmp); - win->gl.gl_user_opengl_rendering = 1; - return tigrGAPIBegin(bmp) == 0; -#else - return 0; -#endif -} - -void tigrSetPostShader(Tigr* bmp, const char* code, int size) { -#ifdef TIGR_GAPI_GL - tigrGAPIBegin(bmp); - TigrInternal* win = tigrInternal(bmp); - GLStuff* gl = &win->gl; - tigrCreateShaderProgram(gl, code, size); - tigrGAPIEnd(bmp); -#endif -} - -void tigrSetPostFX(Tigr* bmp, float p1, float p2, float p3, float p4) { - TigrInternal* win = tigrInternal(bmp); - win->p1 = p1; - win->p2 = p2; - win->p3 = p3; - win->p4 = p4; -} - -#endif // TIGR_HEADLESS - -//////// End of inlined file: tigr_utils.c //////// - - -//////// End of inlined file: tigr_amalgamated.c //////// - - |
