test/testime.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Dec 2017 16:08:09 -0800
changeset 11730 ac6c607e065c
parent 11361 b83e2eaed190
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Enable building the Metal renderer by default, and weak link the Metal framework so the SDL library is safe to use on older Macs
Also generate iOS versions of the Metal shaders
slouken@5535
     1
/*
slouken@10737
     2
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@5535
     3
slouken@5535
     4
  This software is provided 'as-is', without any express or implied
slouken@5535
     5
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     6
  arising from the use of this software.
slouken@5535
     7
slouken@5535
     8
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
     9
  including commercial applications, and to alter it and redistribute it
slouken@5535
    10
  freely.
slouken@5535
    11
*/
slouken@10416
    12
/* A simple program to test the Input Method support in the SDL library (2.0+)
slouken@10416
    13
   If you build without SDL_ttf, you can use the GNU Unifont hex file instead.
slouken@10416
    14
   Download at http://unifoundry.com/unifont.html */
slouken@3338
    15
slouken@3280
    16
#include <stdlib.h>
slouken@3280
    17
#include <stdio.h>
slouken@3280
    18
#include <string.h>
slouken@3280
    19
slouken@3280
    20
#include "SDL.h"
slouken@3280
    21
#ifdef HAVE_SDL_TTF
slouken@3280
    22
#include "SDL_ttf.h"
slouken@3280
    23
#endif
slouken@3338
    24
slouken@6785
    25
#include "SDL_test_common.h"
dimitris@6318
    26
slouken@10416
    27
#define DEFAULT_PTSIZE 30
slouken@10416
    28
#ifdef HAVE_SDL_TTF
slouken@10416
    29
#ifdef __MACOSX__
slouken@10416
    30
#define DEFAULT_FONT "/System/Library/Fonts/华文细黑.ttf"
slouken@10416
    31
#elif __WIN32__
slouken@10416
    32
/* Some japanese font present on at least Windows 8.1. */
slouken@10416
    33
#define DEFAULT_FONT "C:\\Windows\\Fonts\\yugothic.ttf"
slouken@10416
    34
#else
slouken@10416
    35
#define DEFAULT_FONT "NoDefaultFont.ttf"
slouken@10416
    36
#endif
slouken@10416
    37
#else
slouken@10416
    38
#define DEFAULT_FONT "unifont-9.0.02.hex"
slouken@10416
    39
#endif
slouken@3280
    40
#define MAX_TEXT_LENGTH 256
slouken@3280
    41
slouken@6785
    42
static SDLTest_CommonState *state;
dimitris@6318
    43
static SDL_Rect textRect, markedRect;
slouken@10416
    44
static SDL_Color lineColor = {0,0,0,255};
philipp@10100
    45
static SDL_Color backColor = {255,255,255,255};
slouken@10416
    46
static SDL_Color textColor = {0,0,0,255};
dimitris@6318
    47
static char text[MAX_TEXT_LENGTH], markedText[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
dimitris@6318
    48
static int cursor = 0;
slouken@3280
    49
#ifdef HAVE_SDL_TTF
dimitris@6318
    50
static TTF_Font *font;
slouken@10416
    51
#else
slouken@10416
    52
#define UNIFONT_MAX_CODEPOINT 0x1ffff
slouken@10416
    53
#define UNIFONT_NUM_GLYPHS 0x20000
slouken@10416
    54
/* Using 512x512 textures that are supported everywhere. */
slouken@10416
    55
#define UNIFONT_TEXTURE_WIDTH 512
slouken@10416
    56
#define UNIFONT_GLYPHS_IN_ROW (UNIFONT_TEXTURE_WIDTH / 16)
slouken@10416
    57
#define UNIFONT_GLYPHS_IN_TEXTURE (UNIFONT_GLYPHS_IN_ROW * UNIFONT_GLYPHS_IN_ROW)
slouken@10416
    58
#define UNIFONT_NUM_TEXTURES ((UNIFONT_NUM_GLYPHS + UNIFONT_GLYPHS_IN_TEXTURE - 1) / UNIFONT_GLYPHS_IN_TEXTURE)
slouken@10416
    59
#define UNIFONT_TEXTURE_SIZE (UNIFONT_TEXTURE_WIDTH * UNIFONT_TEXTURE_WIDTH * 4)
slouken@10416
    60
#define UNIFONT_TEXTURE_PITCH (UNIFONT_TEXTURE_WIDTH * 4)
slouken@10416
    61
#define UNIFONT_DRAW_SCALE 2
slouken@10416
    62
struct UnifontGlyph {
slouken@10416
    63
    Uint8 width;
slouken@10416
    64
    Uint8 data[32];
slouken@10416
    65
} *unifontGlyph;
slouken@10416
    66
static SDL_Texture **unifontTexture;
slouken@10416
    67
static Uint8 unifontTextureLoaded[UNIFONT_NUM_TEXTURES] = {0};
slouken@10416
    68
slouken@10416
    69
/* Unifont loading code start */
slouken@10416
    70
slouken@10416
    71
static Uint8 dehex(char c)
slouken@10416
    72
{
slouken@10416
    73
    if (c >= '0' && c <= '9')
slouken@10416
    74
        return c - '0';
slouken@10416
    75
    else if (c >= 'a' && c <= 'f')
slouken@10416
    76
        return c - 'a' + 10;
slouken@10416
    77
    else if (c >= 'A' && c <= 'F')
slouken@10416
    78
        return c - 'A' + 10;
slouken@10416
    79
    return 255;
slouken@10416
    80
}
slouken@10416
    81
slouken@10416
    82
static Uint8 dehex2(char c1, char c2)
slouken@10416
    83
{
slouken@10416
    84
    return (dehex(c1) << 4) | dehex(c2);
slouken@10416
    85
}
slouken@10416
    86
slouken@10416
    87
static Uint8 validate_hex(const char *cp, size_t len, Uint32 *np)
slouken@10416
    88
{
slouken@10416
    89
    Uint32 n = 0;
slouken@10416
    90
    for (; len > 0; cp++, len--)
slouken@10416
    91
    {
slouken@10416
    92
        Uint8 c = dehex(*cp);
slouken@10416
    93
        if (c == 255)
slouken@10416
    94
            return 0;
slouken@10416
    95
        n = (n << 4) | c;
slouken@10416
    96
    }
slouken@10416
    97
    if (np != NULL)
slouken@10416
    98
        *np = n;
slouken@10416
    99
    return 1;
slouken@10416
   100
}
slouken@10416
   101
slouken@11361
   102
static int unifont_init(const char *fontname)
slouken@10416
   103
{
slouken@10416
   104
    Uint8 hexBuffer[65];
slouken@10416
   105
    Uint32 numGlyphs = 0;
slouken@10416
   106
    int lineNumber = 1;
slouken@10416
   107
    size_t bytesRead;
slouken@10416
   108
    SDL_RWops *hexFile;
slouken@10416
   109
    const size_t unifontGlyphSize = UNIFONT_NUM_GLYPHS * sizeof(struct UnifontGlyph);
slouken@10416
   110
    const size_t unifontTextureSize = UNIFONT_NUM_TEXTURES * state->num_windows * sizeof(void *);
slouken@10416
   111
slouken@10416
   112
    /* Allocate memory for the glyph data so the file can be closed after initialization. */
philipp@10503
   113
    unifontGlyph = (struct UnifontGlyph *)SDL_malloc(unifontGlyphSize);
slouken@10416
   114
    if (unifontGlyph == NULL)
slouken@10416
   115
    {
slouken@10416
   116
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Failed to allocate %d KiB for glyph data.\n", (int)(unifontGlyphSize + 1023) / 1024);
slouken@11361
   117
        return -1;
slouken@10416
   118
    }
slouken@10416
   119
    SDL_memset(unifontGlyph, 0, unifontGlyphSize);
slouken@10416
   120
slouken@10416
   121
    /* Allocate memory for texture pointers for all renderers. */
philipp@10503
   122
    unifontTexture = (SDL_Texture **)SDL_malloc(unifontTextureSize);
slouken@10416
   123
    if (unifontTexture == NULL)
slouken@10416
   124
    {
slouken@10416
   125
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Failed to allocate %d KiB for texture pointer data.\n", (int)(unifontTextureSize + 1023) / 1024);
slouken@11361
   126
        return -1;
slouken@10416
   127
    }
slouken@10416
   128
    SDL_memset(unifontTexture, 0, unifontTextureSize);
slouken@10416
   129
slouken@10416
   130
    hexFile = SDL_RWFromFile(fontname, "rb");
slouken@10416
   131
    if (hexFile == NULL)
slouken@10416
   132
    {
slouken@10416
   133
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Failed to open font file: %s\n", fontname);
slouken@11361
   134
        return -1;
slouken@10416
   135
    }
slouken@10416
   136
slouken@10416
   137
    /* Read all the glyph data into memory to make it accessible later when textures are created. */
slouken@10416
   138
    do {
slouken@10416
   139
        int i, codepointHexSize;
slouken@10416
   140
        size_t bytesOverread;
slouken@10416
   141
        Uint8 glyphWidth;
slouken@10416
   142
        Uint32 codepoint;
slouken@10416
   143
slouken@10416
   144
        bytesRead = SDL_RWread(hexFile, hexBuffer, 1, 9);
slouken@10416
   145
        if (numGlyphs > 0 && bytesRead == 0)
slouken@10416
   146
            break; /* EOF */
slouken@10416
   147
        if ((numGlyphs == 0 && bytesRead == 0) || (numGlyphs > 0 && bytesRead < 9))
slouken@10416
   148
        {
philipp@11133
   149
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Unexpected end of hex file.\n");
slouken@11361
   150
            return -1;
slouken@10416
   151
        }
slouken@10416
   152
slouken@10416
   153
        /* Looking for the colon that separates the codepoint and glyph data at position 2, 4, 6 and 8. */
slouken@10416
   154
        if (hexBuffer[2] == ':')
slouken@10416
   155
            codepointHexSize = 2;
slouken@10416
   156
        else if (hexBuffer[4] == ':')
slouken@10416
   157
            codepointHexSize = 4;
slouken@10416
   158
        else if (hexBuffer[6] == ':')
slouken@10416
   159
            codepointHexSize = 6;
slouken@10416
   160
        else if (hexBuffer[8] == ':')
slouken@10416
   161
            codepointHexSize = 8;
slouken@10416
   162
        else
slouken@10416
   163
        {
slouken@10416
   164
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Could not find codepoint and glyph data separator symbol in hex file on line %d.\n", lineNumber);
slouken@11361
   165
            return -1;
slouken@10416
   166
        }
slouken@10416
   167
slouken@10416
   168
        if (!validate_hex((const char *)hexBuffer, codepointHexSize, &codepoint))
slouken@10416
   169
        {
slouken@10416
   170
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Malformed hexadecimal number in hex file on line %d.\n", lineNumber);
slouken@11361
   171
            return -1;
slouken@10416
   172
        }
slouken@10416
   173
        if (codepoint > UNIFONT_MAX_CODEPOINT)
slouken@10416
   174
            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "unifont: Codepoint on line %d exceeded limit of 0x%x.\n", lineNumber, UNIFONT_MAX_CODEPOINT);
slouken@10416
   175
slouken@10416
   176
        /* If there was glyph data read in the last file read, move it to the front of the buffer. */
slouken@10416
   177
        bytesOverread = 8 - codepointHexSize;
slouken@10416
   178
        if (codepointHexSize < 8)
slouken@10416
   179
            SDL_memmove(hexBuffer, hexBuffer + codepointHexSize + 1, bytesOverread);
slouken@10416
   180
        bytesRead = SDL_RWread(hexFile, hexBuffer + bytesOverread, 1, 33 - bytesOverread);
slouken@10416
   181
        if (bytesRead < (33 - bytesOverread))
slouken@10416
   182
        {
slouken@10416
   183
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Unexpected end of hex file.\n");
slouken@11361
   184
            return -1;
slouken@10416
   185
        }
slouken@10416
   186
        if (hexBuffer[32] == '\n')
slouken@10416
   187
            glyphWidth = 8;
slouken@10416
   188
        else
slouken@10416
   189
        {
slouken@10416
   190
            glyphWidth = 16;
slouken@10416
   191
            bytesRead = SDL_RWread(hexFile, hexBuffer + 33, 1, 32);
slouken@10416
   192
            if (bytesRead < 32)
slouken@10416
   193
            {
slouken@10416
   194
                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Unexpected end of hex file.\n");
slouken@11361
   195
                return -1;
slouken@10416
   196
            }
slouken@10416
   197
        }
slouken@10416
   198
slouken@10416
   199
        if (!validate_hex((const char *)hexBuffer, glyphWidth * 4, NULL))
slouken@10416
   200
        {
slouken@10416
   201
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Malformed hexadecimal glyph data in hex file on line %d.\n", lineNumber);
slouken@11361
   202
            return -1;
slouken@10416
   203
        }
slouken@10416
   204
slouken@10416
   205
        if (codepoint <= UNIFONT_MAX_CODEPOINT)
slouken@10416
   206
        {
slouken@10416
   207
            if (unifontGlyph[codepoint].width > 0)
slouken@10416
   208
                SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "unifont: Ignoring duplicate codepoint 0x%08x in hex file on line %d.\n", codepoint, lineNumber);
slouken@10416
   209
            else
slouken@10416
   210
            {
slouken@10416
   211
                unifontGlyph[codepoint].width = glyphWidth;
slouken@10416
   212
                /* Pack the hex data into a more compact form. */
slouken@10416
   213
                for (i = 0; i < glyphWidth * 2; i++)
slouken@10416
   214
                    unifontGlyph[codepoint].data[i] = dehex2(hexBuffer[i * 2], hexBuffer[i * 2 + 1]);
slouken@10416
   215
                numGlyphs++;
slouken@10416
   216
            }
slouken@10416
   217
        }
slouken@10416
   218
slouken@10416
   219
        lineNumber++;
slouken@10416
   220
    } while (bytesRead > 0);
slouken@10416
   221
slouken@10416
   222
    SDL_RWclose(hexFile);
slouken@10416
   223
    SDL_Log("unifont: Loaded %u glyphs.\n", numGlyphs);
slouken@11361
   224
    return 0;
slouken@10416
   225
}
slouken@10416
   226
slouken@10416
   227
static void unifont_make_rgba(Uint8 *src, Uint8 *dst, Uint8 width)
slouken@10416
   228
{
slouken@10416
   229
    int i, j;
slouken@10416
   230
    Uint8 *row = dst;
slouken@10416
   231
slouken@10416
   232
    for (i = 0; i < width * 2; i++)
slouken@10416
   233
    {
slouken@10416
   234
        Uint8 data = src[i];
slouken@10416
   235
        for (j = 0; j < 8; j++)
slouken@10416
   236
        {
slouken@10416
   237
            if (data & 0x80)
slouken@10416
   238
            {
slouken@10416
   239
                row[0] = textColor.r;
slouken@10416
   240
                row[1] = textColor.g;
slouken@10416
   241
                row[2] = textColor.b;
slouken@10416
   242
                row[3] = textColor.a;
slouken@10416
   243
            }
slouken@10416
   244
            else
slouken@10416
   245
            {
slouken@10416
   246
                row[0] = 0;
slouken@10416
   247
                row[1] = 0;
slouken@10416
   248
                row[2] = 0;
slouken@10416
   249
                row[3] = 0;
slouken@10416
   250
            }
slouken@10416
   251
            data <<= 1;
slouken@10416
   252
            row += 4;
slouken@10416
   253
        }
slouken@10416
   254
slouken@10416
   255
        if (width == 8 || (width == 16 && i % 2 == 1))
slouken@10416
   256
        {
slouken@10416
   257
            dst += UNIFONT_TEXTURE_PITCH;
slouken@10416
   258
            row = dst;
slouken@10416
   259
        }
slouken@10416
   260
    }
slouken@10416
   261
}
slouken@10416
   262
slouken@11361
   263
static int unifont_load_texture(Uint32 textureID)
slouken@10416
   264
{
slouken@10416
   265
    int i;
slouken@10416
   266
    Uint8 * textureRGBA;
slouken@10416
   267
slouken@10416
   268
    if (textureID >= UNIFONT_NUM_TEXTURES)
slouken@10416
   269
    {
slouken@10416
   270
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Tried to load out of range texture %u.\n", textureID);
slouken@11361
   271
        return -1;
slouken@10416
   272
    }
slouken@10416
   273
philipp@10503
   274
    textureRGBA = (Uint8 *)SDL_malloc(UNIFONT_TEXTURE_SIZE);
slouken@10416
   275
    if (textureRGBA == NULL)
slouken@10416
   276
    {
slouken@10416
   277
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Failed to allocate %d MiB for a texture.\n", UNIFONT_TEXTURE_SIZE / 1024 / 1024);
slouken@11361
   278
        return -1;
slouken@10416
   279
    }
slouken@10416
   280
    SDL_memset(textureRGBA, 0, UNIFONT_TEXTURE_SIZE);
slouken@10416
   281
slouken@10416
   282
    /* Copy the glyphs into memory in RGBA format. */
slouken@10416
   283
    for (i = 0; i < UNIFONT_GLYPHS_IN_TEXTURE; i++)
slouken@10416
   284
    {
slouken@10416
   285
        Uint32 codepoint = UNIFONT_GLYPHS_IN_TEXTURE * textureID + i;
slouken@10416
   286
        if (unifontGlyph[codepoint].width > 0)
slouken@10416
   287
        {
slouken@10416
   288
            const Uint32 cInTex = codepoint % UNIFONT_GLYPHS_IN_TEXTURE;
slouken@10416
   289
            const size_t offset = (cInTex / UNIFONT_GLYPHS_IN_ROW) * UNIFONT_TEXTURE_PITCH * 16 + (cInTex % UNIFONT_GLYPHS_IN_ROW) * 16 * 4;
slouken@10416
   290
            unifont_make_rgba(unifontGlyph[codepoint].data, textureRGBA + offset, unifontGlyph[codepoint].width);
slouken@10416
   291
        }
slouken@10416
   292
    }
slouken@10416
   293
slouken@10416
   294
    /* Create textures and upload the RGBA data from above. */
slouken@10416
   295
    for (i = 0; i < state->num_windows; ++i)
slouken@10416
   296
    {
slouken@10416
   297
        SDL_Renderer *renderer = state->renderers[i];
slouken@10416
   298
        SDL_Texture *tex = unifontTexture[UNIFONT_NUM_TEXTURES * i + textureID];
slouken@10416
   299
        if (state->windows[i] == NULL || renderer == NULL || tex != NULL)
slouken@10416
   300
            continue;
slouken@10416
   301
        tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, UNIFONT_TEXTURE_WIDTH, UNIFONT_TEXTURE_WIDTH);
slouken@10416
   302
        if (tex == NULL)
slouken@10416
   303
        {
slouken@10416
   304
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "unifont: Failed to create texture %u for renderer %d.\n", textureID, i);
slouken@11361
   305
            return -1;
slouken@10416
   306
        }
slouken@10416
   307
        unifontTexture[UNIFONT_NUM_TEXTURES * i + textureID] = tex;
slouken@10416
   308
        SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
slouken@10416
   309
        if (SDL_UpdateTexture(tex, NULL, textureRGBA, UNIFONT_TEXTURE_PITCH) != 0)
slouken@10416
   310
        {
slouken@10416
   311
            SDL_Log("unifont error: Failed to update texture %u data for renderer %d.\n", textureID, i);
slouken@10416
   312
        }
slouken@10416
   313
    }
slouken@10416
   314
slouken@10416
   315
    SDL_free(textureRGBA);
slouken@10416
   316
    unifontTextureLoaded[textureID] = 1;
slouken@11361
   317
    return 0;
slouken@10416
   318
}
slouken@10416
   319
slouken@10416
   320
static Sint32 unifont_draw_glyph(Uint32 codepoint, int rendererID, SDL_Rect *dstrect)
slouken@10416
   321
{
slouken@10416
   322
    SDL_Texture *texture;
slouken@10416
   323
    const Uint32 textureID = codepoint / UNIFONT_GLYPHS_IN_TEXTURE;
slouken@10416
   324
    SDL_Rect srcrect;
slouken@10416
   325
    srcrect.w = srcrect.h = 16;
slouken@11361
   326
    if (codepoint > UNIFONT_MAX_CODEPOINT) {
slouken@10416
   327
        return 0;
slouken@11361
   328
    }
slouken@11361
   329
    if (!unifontTextureLoaded[textureID]) {
slouken@11361
   330
        if (unifont_load_texture(textureID) < 0) {
slouken@11361
   331
            return 0;
slouken@11361
   332
        }
slouken@11361
   333
    }
slouken@10416
   334
    texture = unifontTexture[UNIFONT_NUM_TEXTURES * rendererID + textureID];
slouken@10416
   335
    if (texture != NULL)
slouken@10416
   336
    {
slouken@10416
   337
        const Uint32 cInTex = codepoint % UNIFONT_GLYPHS_IN_TEXTURE;
slouken@10416
   338
        srcrect.x = cInTex % UNIFONT_GLYPHS_IN_ROW * 16;
slouken@10416
   339
        srcrect.y = cInTex / UNIFONT_GLYPHS_IN_ROW * 16;
slouken@10416
   340
        SDL_RenderCopy(state->renderers[rendererID], texture, &srcrect, dstrect);
slouken@10416
   341
    }
slouken@10416
   342
    return unifontGlyph[codepoint].width;
slouken@10416
   343
}
slouken@10416
   344
slouken@10416
   345
static void unifont_cleanup()
slouken@10416
   346
{
slouken@10416
   347
    int i, j;
slouken@10416
   348
    for (i = 0; i < state->num_windows; ++i)
slouken@10416
   349
    {
slouken@10416
   350
        SDL_Renderer *renderer = state->renderers[i];
slouken@10416
   351
        if (state->windows[i] == NULL || renderer == NULL)
slouken@10416
   352
            continue;
slouken@10416
   353
        for (j = 0; j < UNIFONT_NUM_TEXTURES; j++)
slouken@10416
   354
        {
slouken@10416
   355
            SDL_Texture *tex = unifontTexture[UNIFONT_NUM_TEXTURES * i + j];
slouken@10416
   356
            if (tex != NULL)
slouken@10416
   357
                SDL_DestroyTexture(tex);
slouken@10416
   358
        }
slouken@10416
   359
    }
slouken@10416
   360
slouken@10416
   361
    for (j = 0; j < UNIFONT_NUM_TEXTURES; j++)
slouken@10416
   362
          unifontTextureLoaded[j] = 0;
slouken@10416
   363
slouken@10416
   364
    SDL_free(unifontTexture);
slouken@10416
   365
    SDL_free(unifontGlyph);
slouken@10416
   366
}
slouken@10416
   367
slouken@10416
   368
/* Unifont code end */
slouken@3280
   369
#endif
dewyatt@4757
   370
slouken@5150
   371
size_t utf8_length(unsigned char c)
slouken@5150
   372
{
slouken@5150
   373
    c = (unsigned char)(0xff & c);
slouken@5150
   374
    if (c < 0x80)
slouken@5150
   375
        return 1;
slouken@5150
   376
    else if ((c >> 5) ==0x6)
slouken@5150
   377
        return 2;
slouken@5150
   378
    else if ((c >> 4) == 0xe)
slouken@5150
   379
        return 3;
slouken@5150
   380
    else if ((c >> 3) == 0x1e)
slouken@5150
   381
        return 4;
slouken@5150
   382
    else
slouken@5150
   383
        return 0;
slouken@5150
   384
}
slouken@5150
   385
slouken@5150
   386
char *utf8_next(char *p)
slouken@5150
   387
{
slouken@5150
   388
    size_t len = utf8_length(*p);
slouken@5150
   389
    size_t i = 0;
slouken@5150
   390
    if (!len)
slouken@5150
   391
        return 0;
slouken@5150
   392
slouken@5150
   393
    for (; i < len; ++i)
slouken@5150
   394
    {
slouken@5150
   395
        ++p;
slouken@5150
   396
        if (!*p)
slouken@5150
   397
            return 0;
slouken@5150
   398
    }
slouken@5150
   399
    return p;
slouken@5150
   400
}
dewyatt@4757
   401
dewyatt@4757
   402
char *utf8_advance(char *p, size_t distance)
dewyatt@4757
   403
{
dewyatt@4757
   404
    size_t i = 0;
dewyatt@4757
   405
    for (; i < distance && p; ++i)
dewyatt@4757
   406
    {
dewyatt@4757
   407
        p = utf8_next(p);
dewyatt@4757
   408
    }
dewyatt@4757
   409
    return p;
dewyatt@4757
   410
}
slouken@3280
   411
slouken@10416
   412
Uint32 utf8_decode(char *p, size_t len)
slouken@10416
   413
{
slouken@10416
   414
    Uint32 codepoint = 0;
slouken@10416
   415
    size_t i = 0;
slouken@10416
   416
    if (!len)
slouken@10416
   417
        return 0;
slouken@10416
   418
slouken@10416
   419
    for (; i < len; ++i)
slouken@10416
   420
    {
slouken@10416
   421
        if (i == 0)
slouken@10416
   422
            codepoint = (0xff >> len) & *p;
slouken@10416
   423
        else
slouken@10416
   424
        {
slouken@10416
   425
            codepoint <<= 6;
slouken@10416
   426
            codepoint |= 0x3f & *p;
slouken@10416
   427
        }
slouken@10416
   428
        if (!*p)
slouken@10416
   429
            return 0;
slouken@10416
   430
        p++;
slouken@10416
   431
    }
slouken@10416
   432
slouken@10416
   433
    return codepoint;
slouken@10416
   434
}
slouken@10416
   435
slouken@3280
   436
void usage()
slouken@3280
   437
{
aschiffler@7639
   438
    SDL_Log("usage: testime [--font fontfile]\n");
slouken@3280
   439
}
slouken@3280
   440
dimitris@6318
   441
void InitInput()
dimitris@6318
   442
{
dimitris@6318
   443
    /* Prepare a rect for text input */
dimitris@6318
   444
    textRect.x = textRect.y = 100;
dimitris@6318
   445
    textRect.w = DEFAULT_WINDOW_WIDTH - 2 * textRect.x;
dimitris@6318
   446
    textRect.h = 50;
dimitris@6318
   447
dimitris@6318
   448
    text[0] = 0;
dimitris@6318
   449
    markedRect = textRect;
dimitris@6318
   450
    markedText[0] = 0;
dimitris@6318
   451
dimitris@6318
   452
    SDL_StartTextInput();
dimitris@6318
   453
}
dimitris@6318
   454
dimitris@6318
   455
void CleanupVideo()
slouken@3280
   456
{
dimitris@6318
   457
    SDL_StopTextInput();
dimitris@6318
   458
#ifdef HAVE_SDL_TTF
dimitris@6318
   459
    TTF_CloseFont(font);
dimitris@6318
   460
    TTF_Quit();
slouken@10416
   461
#else
slouken@10416
   462
    unifont_cleanup();
dimitris@6318
   463
#endif
dimitris@6318
   464
}
dimitris@6318
   465
slouken@11361
   466
void _Redraw(int rendererID)
slouken@11361
   467
{
slouken@10416
   468
    SDL_Renderer * renderer = state->renderers[rendererID];
slouken@10416
   469
    SDL_Rect drawnTextRect, cursorRect, underlineRect;
slouken@10416
   470
    drawnTextRect = textRect;
slouken@10416
   471
    drawnTextRect.w = 0;
dimitris@6318
   472
philipp@10100
   473
    SDL_SetRenderDrawColor(renderer, backColor.r, backColor.g, backColor.b, backColor.a);
dimitris@6318
   474
    SDL_RenderFillRect(renderer,&textRect);
dimitris@6318
   475
dimitris@6318
   476
    if (*text)
dimitris@6318
   477
    {
slouken@10416
   478
#ifdef HAVE_SDL_TTF
dimitris@6318
   479
        SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, text, textColor);
philipp@10111
   480
        SDL_Texture *texture;
slouken@10416
   481
slouken@10416
   482
        /* Vertically center text */
slouken@10416
   483
        drawnTextRect.y = textRect.y + (textRect.h - textSur->h) / 2;
slouken@10416
   484
        drawnTextRect.w = textSur->w;
slouken@10416
   485
        drawnTextRect.h = textSur->h;
dimitris@6318
   486
philipp@10111
   487
        texture = SDL_CreateTextureFromSurface(renderer,textSur);
dimitris@6318
   488
        SDL_FreeSurface(textSur);
dimitris@6318
   489
slouken@10416
   490
        SDL_RenderCopy(renderer,texture,NULL,&drawnTextRect);
dimitris@6318
   491
        SDL_DestroyTexture(texture);
slouken@10416
   492
#else
slouken@10416
   493
        char *utext = text;
slouken@10416
   494
        Uint32 codepoint;
slouken@10416
   495
        size_t len;
slouken@10416
   496
        SDL_Rect dstrect;
slouken@10416
   497
slouken@10416
   498
        dstrect.x = textRect.x;
slouken@10416
   499
        dstrect.y = textRect.y + (textRect.h - 16 * UNIFONT_DRAW_SCALE) / 2;
slouken@10416
   500
        dstrect.w = 16 * UNIFONT_DRAW_SCALE;
slouken@10416
   501
        dstrect.h = 16 * UNIFONT_DRAW_SCALE;
slouken@10416
   502
        drawnTextRect.y = dstrect.y;
slouken@10416
   503
        drawnTextRect.h = dstrect.h;
slouken@10416
   504
slouken@10416
   505
        while ((codepoint = utf8_decode(utext, len = utf8_length(*utext))))
slouken@10416
   506
        {
slouken@10416
   507
            Sint32 advance = unifont_draw_glyph(codepoint, rendererID, &dstrect) * UNIFONT_DRAW_SCALE;
slouken@10416
   508
            dstrect.x += advance;
slouken@10416
   509
            drawnTextRect.w += advance;
slouken@10416
   510
            utext += len;
slouken@10416
   511
        }
slouken@10416
   512
#endif
dimitris@6318
   513
    }
dimitris@6318
   514
slouken@10416
   515
    markedRect.x = textRect.x + drawnTextRect.w;
slouken@10416
   516
    markedRect.w = textRect.w - drawnTextRect.w;
dimitris@6318
   517
    if (markedRect.w < 0)
dimitris@6318
   518
    {
gabomdq@7663
   519
        /* Stop text input because we cannot hold any more characters */
dimitris@6318
   520
        SDL_StopTextInput();
dimitris@6318
   521
        return;
dimitris@6318
   522
    }
dimitris@6318
   523
    else
dimitris@6318
   524
    {
dimitris@6318
   525
        SDL_StartTextInput();
dimitris@6318
   526
    }
dimitris@6318
   527
slouken@10416
   528
    cursorRect = drawnTextRect;
slouken@10416
   529
    cursorRect.x += cursorRect.w;
dimitris@6318
   530
    cursorRect.w = 2;
slouken@10416
   531
    cursorRect.h = drawnTextRect.h;
slouken@10416
   532
slouken@10416
   533
    drawnTextRect.x += drawnTextRect.w;
slouken@10416
   534
    drawnTextRect.w = 0;
dimitris@6318
   535
philipp@10100
   536
    SDL_SetRenderDrawColor(renderer, backColor.r, backColor.g, backColor.b, backColor.a);
dimitris@6318
   537
    SDL_RenderFillRect(renderer,&markedRect);
dimitris@6318
   538
dimitris@6318
   539
    if (markedText[0])
dimitris@6318
   540
    {
dimitris@6318
   541
#ifdef HAVE_SDL_TTF
philipp@10111
   542
        SDL_Surface *textSur;
philipp@10111
   543
        SDL_Texture *texture;
dimitris@6318
   544
        if (cursor)
dimitris@6318
   545
        {
dimitris@6318
   546
            char *p = utf8_advance(markedText, cursor);
dimitris@6318
   547
            char c = 0;
dimitris@6318
   548
            if (!p)
philipp@10097
   549
                p = &markedText[SDL_strlen(markedText)];
dimitris@6318
   550
dimitris@6318
   551
            c = *p;
dimitris@6318
   552
            *p = 0;
slouken@10416
   553
            TTF_SizeUTF8(font, markedText, &drawnTextRect.w, NULL);
slouken@10416
   554
            cursorRect.x += drawnTextRect.w;
dimitris@6318
   555
            *p = c;
dimitris@6318
   556
        }
philipp@10111
   557
        textSur = TTF_RenderUTF8_Blended(font, markedText, textColor);
slouken@10416
   558
        /* Vertically center text */
slouken@10416
   559
        drawnTextRect.y = textRect.y + (textRect.h - textSur->h) / 2;
slouken@10416
   560
        drawnTextRect.w = textSur->w;
slouken@10416
   561
        drawnTextRect.h = textSur->h;
slouken@10416
   562
philipp@10111
   563
        texture = SDL_CreateTextureFromSurface(renderer,textSur);
dimitris@6318
   564
        SDL_FreeSurface(textSur);
dimitris@6318
   565
slouken@10416
   566
        SDL_RenderCopy(renderer,texture,NULL,&drawnTextRect);
dimitris@6318
   567
        SDL_DestroyTexture(texture);
slouken@10416
   568
#else
slouken@10416
   569
        int i = 0;
slouken@10416
   570
        char *utext = markedText;
slouken@10416
   571
        Uint32 codepoint;
slouken@10416
   572
        size_t len;
slouken@10416
   573
        SDL_Rect dstrect;
slouken@10416
   574
slouken@10416
   575
        dstrect.x = drawnTextRect.x;
slouken@10416
   576
        dstrect.y = textRect.y + (textRect.h - 16 * UNIFONT_DRAW_SCALE) / 2;
slouken@10416
   577
        dstrect.w = 16 * UNIFONT_DRAW_SCALE;
slouken@10416
   578
        dstrect.h = 16 * UNIFONT_DRAW_SCALE;
slouken@10416
   579
        drawnTextRect.y = dstrect.y;
slouken@10416
   580
        drawnTextRect.h = dstrect.h;
slouken@10416
   581
slouken@10416
   582
        while ((codepoint = utf8_decode(utext, len = utf8_length(*utext))))
slouken@10416
   583
        {
slouken@10416
   584
            Sint32 advance = unifont_draw_glyph(codepoint, rendererID, &dstrect) * UNIFONT_DRAW_SCALE;
slouken@10416
   585
            dstrect.x += advance;
slouken@10416
   586
            drawnTextRect.w += advance;
slouken@10416
   587
            if (i < cursor)
slouken@10416
   588
                cursorRect.x += advance;
slouken@10416
   589
            i++;
slouken@10416
   590
            utext += len;
slouken@10416
   591
        }
dimitris@6318
   592
#endif
dimitris@6318
   593
slouken@10416
   594
        if (cursor > 0)
slouken@10416
   595
        {
slouken@10416
   596
            cursorRect.y = drawnTextRect.y;
slouken@10416
   597
            cursorRect.h = drawnTextRect.h;
slouken@10416
   598
        }
slouken@10416
   599
dimitris@6318
   600
        underlineRect = markedRect;
slouken@10416
   601
        underlineRect.y = drawnTextRect.y + drawnTextRect.h - 2;
dimitris@6318
   602
        underlineRect.h = 2;
slouken@10416
   603
        underlineRect.w = drawnTextRect.w;
dimitris@6318
   604
philipp@10100
   605
        SDL_SetRenderDrawColor(renderer, lineColor.r, lineColor.g, lineColor.b, lineColor.a);
slouken@10416
   606
        SDL_RenderFillRect(renderer, &underlineRect);
dimitris@6318
   607
    }
dimitris@6318
   608
philipp@10100
   609
    SDL_SetRenderDrawColor(renderer, lineColor.r, lineColor.g, lineColor.b, lineColor.a);
dimitris@6318
   610
    SDL_RenderFillRect(renderer,&cursorRect);
dimitris@6318
   611
dimitris@6318
   612
    SDL_SetTextInputRect(&markedRect);
dimitris@6318
   613
}
dimitris@6318
   614
slouken@11361
   615
void Redraw()
slouken@11361
   616
{
dimitris@6318
   617
    int i;
dimitris@6318
   618
    for (i = 0; i < state->num_windows; ++i) {
dimitris@6318
   619
        SDL_Renderer *renderer = state->renderers[i];
slouken@7787
   620
        if (state->windows[i] == NULL)
slouken@7787
   621
            continue;
dimitris@6318
   622
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
dimitris@6318
   623
        SDL_RenderClear(renderer);
dimitris@6318
   624
slouken@10416
   625
        /* Sending in the window id to let the font renderers know which one we're working with. */
slouken@10416
   626
        _Redraw(i);
dimitris@6318
   627
dimitris@6318
   628
        SDL_RenderPresent(renderer);
dimitris@6318
   629
    }
dimitris@6318
   630
}
dimitris@6318
   631
slouken@11361
   632
int main(int argc, char *argv[])
slouken@11361
   633
{
dimitris@6318
   634
    int i, done;
dimitris@6318
   635
    SDL_Event event;
slouken@3280
   636
    const char *fontname = DEFAULT_FONT;
slouken@3280
   637
aschiffler@7639
   638
    /* Enable standard application logging */
aschiffler@7639
   639
    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
aschiffler@7639
   640
dimitris@6318
   641
    /* Initialize test framework */
slouken@6785
   642
    state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
dimitris@6318
   643
    if (!state) {
dimitris@6318
   644
        return 1;
dimitris@6318
   645
    }
dimitris@6318
   646
    for (i = 1; i < argc;i++) {
slouken@6785
   647
        SDLTest_CommonArg(state, i);
dimitris@6318
   648
    }
slouken@3280
   649
    for (argc--, argv++; argc > 0; argc--, argv++)
slouken@3280
   650
    {
dimitris@6318
   651
        if (strcmp(argv[0], "--help") == 0) {
slouken@3280
   652
            usage();
dimitris@6318
   653
            return 0;
dimitris@6318
   654
        }
slouken@3280
   655
slouken@3280
   656
        else if (strcmp(argv[0], "--font") == 0)
slouken@3280
   657
        {
slouken@3280
   658
            argc--;
slouken@3280
   659
            argv++;
slouken@3280
   660
slouken@3280
   661
            if (argc > 0)
slouken@3280
   662
                fontname = argv[0];
dimitris@6318
   663
            else {
slouken@3280
   664
                usage();
dimitris@6318
   665
                return 0;
dimitris@6318
   666
            }
slouken@3280
   667
        }
slouken@3280
   668
    }
slouken@7191
   669
slouken@6785
   670
    if (!SDLTest_CommonInit(state)) {
dimitris@6318
   671
        return 2;
dimitris@6318
   672
    }
slouken@3280
   673
slouken@3280
   674
slouken@3280
   675
#ifdef HAVE_SDL_TTF
slouken@3280
   676
    /* Initialize fonts */
slouken@3280
   677
    TTF_Init();
slouken@3280
   678
slouken@3280
   679
    font = TTF_OpenFont(fontname, DEFAULT_PTSIZE);
slouken@3280
   680
    if (! font)
slouken@3280
   681
    {
aschiffler@7639
   682
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to find font: %s\n", TTF_GetError());
slouken@11361
   683
        return -1;
slouken@3280
   684
    }
slouken@10416
   685
#else
slouken@11361
   686
    if (unifont_init(fontname) < 0) {
slouken@11361
   687
        return -1;
slouken@11361
   688
    }
slouken@3280
   689
#endif
slouken@3280
   690
aschiffler@7639
   691
    SDL_Log("Using font: %s\n", fontname);
slouken@3280
   692
dimitris@6318
   693
    InitInput();
dimitris@6318
   694
    /* Create the windows and initialize the renderers */
dimitris@6318
   695
    for (i = 0; i < state->num_windows; ++i) {
dimitris@6318
   696
        SDL_Renderer *renderer = state->renderers[i];
dimitris@6318
   697
        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
dimitris@6318
   698
        SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
dimitris@6318
   699
        SDL_RenderClear(renderer);
slouken@3280
   700
    }
slouken@7191
   701
    Redraw();
dimitris@6318
   702
    /* Main render loop */
dimitris@6318
   703
    done = 0;
dimitris@6318
   704
    while (!done) {
dimitris@6318
   705
        /* Check for events */
dimitris@6318
   706
        while (SDL_PollEvent(&event)) {
slouken@6785
   707
            SDLTest_CommonEvent(state, &event, &done);
dimitris@6318
   708
            switch(event.type) {
dimitris@6318
   709
                case SDL_KEYDOWN: {
dimitris@6318
   710
                    switch (event.key.keysym.sym)
dimitris@6318
   711
                    {
dimitris@6318
   712
                        case SDLK_RETURN:
dimitris@6318
   713
                             text[0]=0x00;
dimitris@6318
   714
                             Redraw();
dimitris@6318
   715
                             break;
dimitris@6318
   716
                        case SDLK_BACKSPACE:
slouken@10416
   717
                            /* Only delete text if not in editing mode. */
slouken@10416
   718
                             if (!markedText[0])
dimitris@6318
   719
                             {
philipp@9938
   720
                                 size_t textlen = SDL_strlen(text);
slouken@3280
   721
dimitris@6318
   722
                                 do {
dimitris@6318
   723
                                     if (textlen==0)
dimitris@6318
   724
                                     {
dimitris@6318
   725
                                         break;
dimitris@6318
   726
                                     }
dimitris@6318
   727
                                     if ((text[textlen-1] & 0x80) == 0x00)
dimitris@6318
   728
                                     {
dimitris@6318
   729
                                         /* One byte */
dimitris@6318
   730
                                         text[textlen-1]=0x00;
dimitris@6318
   731
                                         break;
dimitris@6318
   732
                                     }
dimitris@6318
   733
                                     if ((text[textlen-1] & 0xC0) == 0x80)
dimitris@6318
   734
                                     {
dimitris@6318
   735
                                         /* Byte from the multibyte sequence */
dimitris@6318
   736
                                         text[textlen-1]=0x00;
dimitris@6318
   737
                                         textlen--;
dimitris@6318
   738
                                     }
dimitris@6318
   739
                                     if ((text[textlen-1] & 0xC0) == 0xC0)
dimitris@6318
   740
                                     {
dimitris@6318
   741
                                         /* First byte of multibyte sequence */
dimitris@6318
   742
                                         text[textlen-1]=0x00;
dimitris@6318
   743
                                         break;
dimitris@6318
   744
                                     }
dimitris@6318
   745
                                 } while(1);
slouken@3280
   746
dimitris@6318
   747
                                 Redraw();
dimitris@6318
   748
                             }
dimitris@6318
   749
                             break;
dimitris@6318
   750
                    }
dimitris@6318
   751
dimitris@6318
   752
                    if (done)
dimitris@6318
   753
                    {
dimitris@6318
   754
                        break;
dimitris@6318
   755
                    }
dimitris@6318
   756
aschiffler@7639
   757
                    SDL_Log("Keyboard: scancode 0x%08X = %s, keycode 0x%08X = %s\n",
dimitris@6318
   758
                            event.key.keysym.scancode,
dimitris@6318
   759
                            SDL_GetScancodeName(event.key.keysym.scancode),
dimitris@6318
   760
                            event.key.keysym.sym, SDL_GetKeyName(event.key.keysym.sym));
dimitris@6318
   761
                    break;
dimitris@6318
   762
dimitris@6318
   763
                case SDL_TEXTINPUT:
slouken@7721
   764
                    if (event.text.text[0] == '\0' || event.text.text[0] == '\n' ||
dimitris@6318
   765
                        markedRect.w < 0)
dimitris@6318
   766
                        break;
dimitris@6318
   767
aschiffler@7639
   768
                    SDL_Log("Keyboard: text input \"%s\"\n", event.text.text);
slouken@3280
   769
dimitris@6318
   770
                    if (SDL_strlen(text) + SDL_strlen(event.text.text) < sizeof(text))
dimitris@6318
   771
                        SDL_strlcat(text, event.text.text, sizeof(text));
dimitris@6318
   772
aschiffler@7639
   773
                    SDL_Log("text inputed: %s\n", text);
dimitris@6318
   774
gabomdq@7663
   775
                    /* After text inputed, we can clear up markedText because it */
gabomdq@7677
   776
                    /* is committed */
dimitris@6318
   777
                    markedText[0] = 0;
dimitris@6318
   778
                    Redraw();
dimitris@6318
   779
                    break;
slouken@3280
   780
dimitris@6318
   781
                case SDL_TEXTEDITING:
aschiffler@7639
   782
                    SDL_Log("text editing \"%s\", selected range (%d, %d)\n",
dimitris@6318
   783
                            event.edit.text, event.edit.start, event.edit.length);
slouken@3280
   784
slouken@10416
   785
                    SDL_strlcpy(markedText, event.edit.text, SDL_TEXTEDITINGEVENT_TEXT_SIZE);
dimitris@6318
   786
                    cursor = event.edit.start;
dimitris@6318
   787
                    Redraw();
dimitris@6318
   788
                    break;
dimitris@6318
   789
                }
dimitris@6318
   790
                break;
slouken@7191
   791
dimitris@6318
   792
            }
dimitris@6318
   793
        }
dimitris@6318
   794
    }
dimitris@6318
   795
    CleanupVideo();
slouken@6785
   796
    SDLTest_CommonQuit(state);
dimitris@6318
   797
    return 0;
slouken@3280
   798
}
slouken@3280
   799
slouken@5244
   800
slouken@5244
   801
/* vi: set ts=4 sw=4 expandtab: */