summaryrefslogtreecommitdiff
path: root/tigr.c
diff options
context:
space:
mode:
Diffstat (limited to 'tigr.c')
-rw-r--r--tigr.c6610
1 files changed, 0 insertions, 6610 deletions
diff --git a/tigr.c b/tigr.c
deleted file mode 100644
index 117a5dd..0000000
--- a/tigr.c
+++ /dev/null
@@ -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 ////////
-
-