Merge pull request #43 from richi/master

Text input on iOS and tvOS
This commit is contained in:
Micha Mettke 2016-01-20 02:15:40 +01:00
commit a2e741d540
8 changed files with 484 additions and 150 deletions

View File

@ -27,6 +27,8 @@
500B20451C4B43BC0002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
500B20461C4B43BC0002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = "<group>"; };
500B20471C4B43BC0002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = "<group>"; };
504DFDEA1C4EA2CD00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = "<group>"; };
504DFDED1C4EA34800D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -60,9 +62,11 @@
500B201E1C4B41870002B53F /* Mac_GL */ = {
isa = PBXGroup;
children = (
504DFDED1C4EA34800D3714A /* demo.c */,
500B203E1C4B43930002B53F /* ZahnradBackend.h */,
500B203F1C4B43930002B53F /* ZahnradBackend.m */,
500B20231C4B41870002B53F /* main.m */,
504DFDEA1C4EA2CD00D3714A /* zahnrad.h */,
500B20411C4B43A30002B53F /* zahnrad.c */,
500B202A1C4B41880002B53F /* Info.plist */,
);

View File

@ -3,7 +3,7 @@ Zahnrad for Mac OS X, iOS and tvOS
This is a bunch of experimental render backends for [Zahnrad](https://github.com/vurtun/zahnrad) a gorgeous immediate mode GUI toolkit.
Include are native versions for Mac OS X, iOS and tvOS. The Mac version is using OpenGL 3.2. iOS and tvOS are running on OpenGL ES 3.2.
Included are native versions for Mac OS X, iOS and tvOS. The Mac version is using OpenGL 3.2. iOS and tvOS are running on OpenGL ES 3.2.
Versions utilising Metal are in the making.

View File

@ -24,7 +24,7 @@
#define SIMULATE_TOUCH 0
#if (TARGET_OS_IPHONE && (!TARGET_OS_TV)) || SIMULATE_TOUCH
#define ZR_REFRESH_ON_EVENT_ONLY 1
#define ZR_REFRESH_ON_EVENT_ONLY 0
#define ZR_TOUCH_SCREEN 1
#endif
@ -45,6 +45,7 @@
@interface ZahnradBackend : NSObject
@property (readonly) GLVIEW* view;
- (instancetype) initWithView: (GLVIEW*) view;
@ -56,3 +57,21 @@
@end
struct zr_context;
struct zr_buffer;
struct zr_allocator;
struct zr_user_font;
@interface ZahnradBackend (Adapter)
- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds
allocator: (struct zr_allocator*) alloc
defaultFont: (struct zr_user_font*) defFont;
- (int) fillFrame;
@end

View File

@ -19,6 +19,18 @@
3. This notice may not be removed or altered from any source distribution.
*/
#define MAX_VERTEX_MEMORY (512 * 1024)
#define MAX_ELEMENT_MEMORY (128 * 1024)
// This is the adapter for demo.c.
// #else does provide a generic version to start
// your own project.
#if 1
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
@ -26,17 +38,106 @@
#include <string.h>
#include <math.h>
#include "../../zahnrad.h"
#include "zahnrad.h"
#include "../demo.c"
#undef MAX
#undef MIN
#import "ZahnradBackend.h"
#define MAX_VERTEX_MEMORY (512 * 1024)
#define MAX_ELEMENT_MEMORY (128 * 1024)
@implementation ZahnradBackend (Adapter)
static struct demo gui;
- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds
allocator: (struct zr_allocator*) alloc
defaultFont: (struct zr_user_font*) defFont
{
memset(&gui, 0, sizeof(gui));
zr_buffer_init(cmds, alloc, 4096);
zr_init(&gui.ctx, alloc, defFont);
return &gui.ctx;
}
- (int) fillFrame;
{
return run_demo(&gui);
}
@end
#else
#import "ZahnradBackend.h"
#import "zahnrad.h"
@implementation ZahnradBackend (Adapter)
static struct zr_context context;
- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds
allocator: (struct zr_allocator*) alloc
defaultFont: (struct zr_user_font*) defFont
{
memset(&context, 0, sizeof(context));
zr_buffer_init(cmds, alloc, 4096);
zr_init(&context, alloc, defFont);
return &context;
}
- (int) fillFrame;
{
return 1;
}
@end
#endif
@implementation ZahnradBackend
{
struct zr_context* context;
struct zr_allocator alloc;
struct zr_user_font sysFnt;
struct zr_font font;
struct zr_draw_null_texture nullTexture;
struct zr_buffer cmds;
NSMutableArray* bufferedCommands;
NSMutableArray* events;
struct
{
GLuint vbo, vao, ebo;
GLuint program;
GLuint vertexShader;
GLuint fragmentShader;
GLint attributePosition;
GLint attributeUV;
GLint attributeColor;
GLint uniformTexture;
GLint uniformProjection;
GLuint fontTexture;
} device;
}
static void* mem_alloc(zr_handle unused, size_t size)
@ -51,36 +152,6 @@ static void mem_free(zr_handle unused, void* ptr)
}
struct device
{
GLuint vbo, vao, ebo;
GLuint prog;
GLuint vert_shdr;
GLuint frag_shdr;
GLint attrib_pos;
GLint attrib_uv;
GLint attrib_col;
GLint uniform_tex;
GLint uniform_proj;
GLuint font_tex;
struct zr_draw_null_texture null;
struct zr_buffer cmds;
};
@implementation ZahnradBackend
{
struct demo gui;
struct device device;
struct zr_font font;
struct zr_user_font usrfnt;
struct zr_allocator alloc;
NSMutableArray* events;
NSMutableArray* drawCommands;
}
#pragma mark - Setup -
@ -91,7 +162,7 @@ struct device
_view = view;
events = [NSMutableArray new];
drawCommands = [NSMutableArray new];
bufferedCommands = [NSMutableArray new];
[self setupGL];
@ -102,10 +173,9 @@ struct device
alloc.alloc = mem_alloc;
alloc.free = mem_free;
zr_buffer_init(&device.cmds, &alloc, 4096);
usrfnt = [self fontFromUrl: fontURL height: 14 range: zr_font_default_glyph_ranges()];
zr_init(&gui.ctx, &alloc, &usrfnt);
sysFnt = [self fontFromUrl: fontURL height: 14 range: zr_font_default_glyph_ranges()];
context = [self createContextWithBuffer: &cmds allocator: &alloc defaultFont: &sysFnt];
[self fillFrame];
return self;
@ -149,67 +219,67 @@ struct device
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
device.prog = glCreateProgram();
device.vert_shdr = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(device.vert_shdr, 1, &vss, 0);
glCompileShader(device.vert_shdr);
device.program = glCreateProgram();
device.vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(device.vertexShader, 1, &vss, 0);
glCompileShader(device.vertexShader);
#if defined(DEBUG)
GLint logLength;
glGetShaderiv(device.vert_shdr, GL_INFO_LOG_LENGTH, &logLength);
glGetShaderiv(device.vertexShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar* log = (GLchar*)malloc(logLength);
glGetShaderInfoLog(device.vert_shdr, logLength, &logLength, log);
glGetShaderInfoLog(device.vertexShader, logLength, &logLength, log);
NSLog(@"Shader compile log:\n%s", log);
free(log);
}
#endif
glGetShaderiv(device.vert_shdr, GL_COMPILE_STATUS, &status);
glGetShaderiv(device.vertexShader, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
device.frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(device.frag_shdr, 1, &fss, 0);
glCompileShader(device.frag_shdr);
device.fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(device.fragmentShader, 1, &fss, 0);
glCompileShader(device.fragmentShader);
#if defined(DEBUG)
glGetShaderiv(device.frag_shdr, GL_INFO_LOG_LENGTH, &logLength);
glGetShaderiv(device.fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar* log = (GLchar*)malloc(logLength);
glGetShaderInfoLog(device.frag_shdr, logLength, &logLength, log);
glGetShaderInfoLog(device.fragmentShader, logLength, &logLength, log);
NSLog(@"Shader compile log:\n%s", log);
free(log);
}
#endif
glGetShaderiv(device.frag_shdr, GL_COMPILE_STATUS, &status);
glGetShaderiv(device.fragmentShader, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glAttachShader(device.prog, device.vert_shdr);
glAttachShader(device.prog, device.frag_shdr);
glLinkProgram(device.prog);
glAttachShader(device.program, device.vertexShader);
glAttachShader(device.program, device.fragmentShader);
glLinkProgram(device.program);
#if defined(DEBUG)
glGetProgramiv(device.prog, GL_INFO_LOG_LENGTH, &logLength);
glGetProgramiv(device.program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar* log = (GLchar*)malloc(logLength);
glGetProgramInfoLog(device.prog, logLength, &logLength, log);
glGetProgramInfoLog(device.program, logLength, &logLength, log);
NSLog(@"Program link log:\n%s", log);
free(log);
}
#endif
glGetProgramiv(device.prog, GL_LINK_STATUS, &status);
glGetProgramiv(device.program, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
device.uniform_tex = glGetUniformLocation(device.prog, "Texture");
device.uniform_proj = glGetUniformLocation(device.prog, "ProjMtx");
device.attrib_pos = glGetAttribLocation(device.prog, "Position");
device.attrib_uv = glGetAttribLocation(device.prog, "TexCoord");
device.attrib_col = glGetAttribLocation(device.prog, "Color");
device.uniformTexture = glGetUniformLocation(device.program, "Texture");
device.uniformProjection = glGetUniformLocation(device.program, "ProjMtx");
device.attributePosition = glGetAttribLocation(device.program, "Position");
device.attributeUV = glGetAttribLocation(device.program, "TexCoord");
device.attributeColor = glGetAttribLocation(device.program, "Color");
// buffer setup
GLsizei vs = sizeof(struct zr_draw_vertex);
@ -229,13 +299,13 @@ struct device
glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW);
glEnableVertexAttribArray((GLuint)device.attrib_pos);
glEnableVertexAttribArray((GLuint)device.attrib_uv);
glEnableVertexAttribArray((GLuint)device.attrib_col);
glEnableVertexAttribArray((GLuint)device.attributePosition);
glEnableVertexAttribArray((GLuint)device.attributeUV);
glEnableVertexAttribArray((GLuint)device.attributeColor);
glVertexAttribPointer((GLuint)device.attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
glVertexAttribPointer((GLuint)device.attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
glVertexAttribPointer((GLuint)device.attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
glVertexAttribPointer((GLuint)device.attributePosition, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
glVertexAttribPointer((GLuint)device.attributeUV, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
glVertexAttribPointer((GLuint)device.attributeColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -297,8 +367,8 @@ struct device
img = img_rgba;
// upload baked font image
glGenTextures(1, &device.font_tex);
glBindTexture(GL_TEXTURE_2D, device.font_tex);
glGenTextures(1, &device.fontTexture);
glBindTexture(GL_TEXTURE_2D, device.fontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)img_width, (GLsizei)img_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
@ -307,16 +377,15 @@ struct device
free(img);
// default white pixel in a texture which is needed to draw primitives
device.null.texture.id = (int)device.font_tex;
device.null.uv = zr_vec2((custom.x + 0.5f) / (float)img_width,
(custom.y + 0.5f) / (float)img_height);
nullTexture.texture.id = (int)device.fontTexture;
nullTexture.uv = zr_vec2((custom.x + 0.5f) / (float)img_width, (custom.y + 0.5f) / (float)img_height);
/* setup font with glyphes. IMPORTANT: the font only references the glyphes
this was done to have the possibility to have multible fonts with one
total glyph array. Not quite sure if it is a good thing since the
glyphes have to be freed as well.
*/
zr_font_init(&font, (float)fontHeight, '?', glyphes, &baked_font, device.null.texture);
zr_font_init(&font, (float)fontHeight, '?', glyphes, &baked_font, nullTexture.texture);
return zr_font_ref(&font);
}
@ -354,9 +423,9 @@ struct device
};
ortho[0][0] /= width;
ortho[1][1] /= height;
glUseProgram(device.prog);
glUniform1i(device.uniform_tex, 0);
glUniformMatrix4fv(device.uniform_proj, 1, GL_FALSE, &ortho[0][0]);
glUseProgram(device.program);
glUniform1i(device.uniformTexture, 0);
glUniformMatrix4fv(device.uniformProjection, 1, GL_FALSE, &ortho[0][0]);
// activate vertex and element buffer
glBindVertexArray(device.vao);
@ -364,18 +433,18 @@ struct device
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, device.ebo);
// if draw commands have been buffered we will draw only those
if (drawCommands.count)
if (bufferedCommands.count)
{
const struct zr_draw_command* cmd;
const zr_draw_index* offset = NULL;
// iterate over and execute each draw command
for (NSData* data in drawCommands)
for (NSData* data in bufferedCommands)
{
cmd = data.bytes;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor((GLint)(cmd->clip_rect.x * scale),
(GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale),
(GLint)((height - (cmd->clip_rect.y + cmd->clip_rect.h)) * scale),
(GLint)(cmd->clip_rect.w * scale),
(GLint)(cmd->clip_rect.h * scale));
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
@ -384,8 +453,8 @@ struct device
}
else
{
// load draw vertexes & elements into vertex + element buffer
void* vertexes = glMapBufferRange(GL_ARRAY_BUFFER, 0, MAX_VERTEX_MEMORY, GL_MAP_WRITE_BIT);
// load draw vertices & elements into vertex + element buffer
void* vertices = glMapBufferRange(GL_ARRAY_BUFFER, 0, MAX_VERTEX_MEMORY, GL_MAP_WRITE_BIT);
void* elements = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, MAX_ELEMENT_MEMORY, GL_MAP_WRITE_BIT);
// fill converting configuration
@ -395,13 +464,13 @@ struct device
config.line_AA = ZR_ANTI_ALIASING_ON;
config.circle_segment_count = 22;
config.line_thickness = 1.0f;
config.null = device.null;
config.null = nullTexture;
// setup buffers to load vertexes and elements
// setup buffers to load vertices and elements
struct zr_buffer vbuf, ebuf;
zr_buffer_init_fixed(&vbuf, vertexes, MAX_VERTEX_MEMORY);
zr_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY);
zr_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY);
zr_convert(&gui.ctx, &device.cmds, &vbuf, &ebuf, &config);
zr_convert(context, &cmds, &vbuf, &ebuf, &config);
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
@ -410,15 +479,15 @@ struct device
const struct zr_draw_command* cmd;
const zr_draw_index* offset = NULL;
[drawCommands removeAllObjects];
[bufferedCommands removeAllObjects];
// iterate over and execute each draw command
zr_draw_foreach(cmd, &gui.ctx, &device.cmds)
zr_draw_foreach(cmd, context, &cmds)
{
if (cmd->elem_count > 0)
{
#if ZR_REFRESH_ON_EVENT_ONLY
[drawCommands addObject: [NSData dataWithBytes: cmd length: sizeof(struct zr_draw_command)]];
[bufferedCommands addObject: [NSData dataWithBytes: cmd length: sizeof(struct zr_draw_command)]];
#endif
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor((GLint)(cmd->clip_rect.x * scale),
@ -430,8 +499,8 @@ struct device
}
}
zr_clear(&gui.ctx);
}
zr_clear(context);
}
// restore old state
glUseProgram((GLuint)last_prog);
@ -443,12 +512,6 @@ struct device
}
- (void) fillFrame
{
run_demo(&gui);
}
#pragma mark - Events -
@ -470,7 +533,7 @@ struct device
NSArray* currentEvents = events.copy;
[events removeAllObjects];
zr_input_begin(&gui.ctx);
zr_input_begin(context);
for (NSDictionary* event in currentEvents)
{
@ -487,55 +550,55 @@ struct device
switch (type)
{
#if ZR_REFRESH_ON_EVENT_ONLY || ZR_TOUCH_SCREEN
case 2: case 3: case 4:
zr_input_motion(&gui.ctx, x, y);
zr_input_motion(context, x, y);
break;
case 5:
zr_input_motion(&gui.ctx, x, y);
zr_input_end(&gui.ctx);
zr_input_begin(&gui.ctx);
zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, x, y, zr_true);
zr_input_motion(context, x, y);
zr_input_end(context);
zr_input_begin(context);
zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_true);
break;
case 6:
zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, x, y, zr_false);
zr_input_end(&gui.ctx);
zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_false);
zr_input_end(context);
[self fillFrame];
zr_clear(&gui.ctx);
zr_input_begin(&gui.ctx);
zr_input_motion(&gui.ctx, INT_MAX, INT_MAX);
zr_clear(context);
zr_input_begin(context);
zr_input_motion(context, INT_MAX, INT_MAX);
break;
#else
case 1: case 2: case 3: case 4:
zr_input_motion(&gui.ctx, x, y);
zr_input_motion(context, x, y);
break;
case 5:
zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, x, y, zr_true);
zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_true);
break;
case 6:
zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, x, y, zr_false);
zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_false);
break;
case 7:
zr_input_button(&gui.ctx, ZR_BUTTON_RIGHT, x, y, zr_true);
zr_input_button(context, ZR_BUTTON_RIGHT, x, y, zr_true);
break;
case 8:
zr_input_button(&gui.ctx, ZR_BUTTON_RIGHT, x, y, zr_false);
zr_input_button(context, ZR_BUTTON_RIGHT, x, y, zr_false);
break;
case 9:
zr_input_button(&gui.ctx, ZR_BUTTON_MIDDLE, x, y, zr_true);
zr_input_button(context, ZR_BUTTON_MIDDLE, x, y, zr_true);
break;
case 10:
zr_input_button(&gui.ctx, ZR_BUTTON_MIDDLE, x, y, zr_false);
zr_input_button(context, ZR_BUTTON_MIDDLE, x, y, zr_false);
break;
case 11:
zr_input_scroll(&gui.ctx, -[event[@"deltaY"] floatValue]);
zr_input_scroll(context, -[event[@"deltaY"] floatValue]);
break;
#if !TARGET_OS_IPHONE
#endif
case 12:
case 13:
{
@ -544,6 +607,29 @@ struct device
NSData* data = [str dataUsingEncoding: NSUTF32LittleEndianStringEncoding];
const uint32_t* c = data.bytes;
NSInteger n = data.length / sizeof(uint32_t);
#if TARGET_OS_IPHONE
if (down)
{
if (n && *c == '\b')
{
zr_input_key(context, ZR_KEY_BACKSPACE, zr_true);
zr_input_end(context);
[self fillFrame];
zr_clear(context);
zr_input_begin(context);
zr_input_key(context, ZR_KEY_BACKSPACE, zr_false);
}
else
{
for (NSInteger i = 0; i < n; i += 1, c += 1)
zr_input_unicode(context, *c);
}
}
#else
for (NSInteger i = 0; i < n; i += 1, c += 1)
{
if (*c == 127 || *c < ' ' || *c >= NSUpArrowFunctionKey)
@ -551,28 +637,28 @@ struct device
switch (*c)
{
case NSLeftArrowFunctionKey:
zr_input_key(&gui.ctx, ZR_KEY_LEFT, down);
zr_input_key(context, ZR_KEY_LEFT, down);
break;
case NSRightArrowFunctionKey:
zr_input_key(&gui.ctx, ZR_KEY_RIGHT, down);
zr_input_key(context, ZR_KEY_RIGHT, down);
break;
case 3:
zr_input_key(&gui.ctx, ZR_KEY_COPY, down);
zr_input_key(context, ZR_KEY_COPY, down);
break;
case 22:
zr_input_key(&gui.ctx, ZR_KEY_PASTE, down);
zr_input_key(context, ZR_KEY_PASTE, down);
break;
case 24:
zr_input_key(&gui.ctx, ZR_KEY_CUT, down);
zr_input_key(context, ZR_KEY_CUT, down);
break;
case 9:
zr_input_key(&gui.ctx, ZR_KEY_TAB, down);
zr_input_key(context, ZR_KEY_TAB, down);
break;
case 13:
zr_input_key(&gui.ctx, ZR_KEY_ENTER, down);
zr_input_key(context, ZR_KEY_ENTER, down);
break;
case 127:
zr_input_key(&gui.ctx, ZR_KEY_BACKSPACE, down);
zr_input_key(context, ZR_KEY_BACKSPACE, down);
break;
default:
break;
@ -580,28 +666,80 @@ struct device
}
else if (down)
{
zr_input_unicode(&gui.ctx, *c);
zr_input_unicode(context, *c);
}
}
#endif
}
break;
#endif
#endif
default:
break;
}
}
zr_input_end(&gui.ctx);
zr_input_end(context);
}
- (void) addEvent: (NSDictionary*) event
{
[drawCommands removeAllObjects];
[bufferedCommands removeAllObjects];
[events addObject: event];
}
@end
@protocol ZahnradKeyboardDelegate <NSObject>
- (void) showKeyboard: (NSDictionary*) info;
- (void) hideKeyboard;
@end
#if TARGET_OS_IPHONE
void zr_backend_show_keyboard(zr_hash zrHash, struct zr_rect zrBounds, struct zr_buffer* zrText)
{
CGRect frame = CGRectMake(zrBounds.x, zrBounds.y, zrBounds.w, zrBounds.h);
id ad = [[UIApplication sharedApplication] delegate];
if ([ad respondsToSelector: @selector(showKeyboard:)])
{
char str[zrText->allocated + 1];
strncpy(str, zrText->memory.ptr, zrText->allocated);
str[zrText->allocated] = 0;
NSString* text = [NSString stringWithCString: str encoding: NSUTF8StringEncoding];
[ad performSelector: @selector(showKeyboard:) withObject: @{@"hash" : @(zrHash), @"text" : text, @"frame" : NSStringFromCGRect(frame)}];
}
}
void zr_backend_hide_keyboard(void)
{
id ad = [[UIApplication sharedApplication] delegate];
if ([ad respondsToSelector: @selector(hideKeyboard)])
[ad performSelector: @selector(hideKeyboard)];
}
#else
void zr_backend_show_keyboard(zr_hash zrHash, struct zr_rect zrBounds)
{
}
void zr_backend_hide_keyboard(void)
{
}
#endif

View File

@ -7,13 +7,13 @@
objects = {
/* Begin PBXBuildFile section */
500B1FFA1C4B410F0002B53F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B1FF91C4B410F0002B53F /* main.m */; };
500B20321C4B42B60002B53F /* ZahnradBackend.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B20311C4B42B60002B53F /* ZahnradBackend.m */; };
500B20341C4B43010002B53F /* zahnrad.c in Sources */ = {isa = PBXBuildFile; fileRef = 500B20331C4B43010002B53F /* zahnrad.c */; };
500B203A1C4B43380002B53F /* DroidSans.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20361C4B43380002B53F /* DroidSans.ttf */; };
500B203B1C4B43380002B53F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20371C4B43380002B53F /* Roboto-Bold.ttf */; };
500B203C1C4B43380002B53F /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20381C4B43380002B53F /* Roboto-Light.ttf */; };
500B203D1C4B43380002B53F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20391C4B43380002B53F /* Roboto-Regular.ttf */; };
504DFDF11C4EA36500D3714A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B1FF91C4B410F0002B53F /* main.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -27,6 +27,8 @@
500B20371C4B43380002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
500B20381C4B43380002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = "<group>"; };
500B20391C4B43380002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = "<group>"; };
504DFDEB1C4EA2EE00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = "<group>"; };
504DFDEF1C4EA36200D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -60,9 +62,11 @@
500B1FF71C4B410F0002B53F /* iOS_GL_ES */ = {
isa = PBXGroup;
children = (
504DFDEF1C4EA36200D3714A /* demo.c */,
500B20301C4B42B60002B53F /* ZahnradBackend.h */,
500B20311C4B42B60002B53F /* ZahnradBackend.m */,
500B1FF91C4B410F0002B53F /* main.m */,
504DFDEB1C4EA2EE00D3714A /* zahnrad.h */,
500B20331C4B43010002B53F /* zahnrad.c */,
500B200D1C4B410F0002B53F /* Info.plist */,
);
@ -152,8 +156,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
500B1FFA1C4B410F0002B53F /* main.m in Sources */,
500B20341C4B43010002B53F /* zahnrad.c in Sources */,
504DFDF11C4EA36500D3714A /* main.m in Sources */,
500B20321C4B42B60002B53F /* ZahnradBackend.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -24,12 +24,15 @@
#import "ZahnradBackend.h"
@interface GameViewController : GLKViewController
@interface GameViewController : GLKViewController <UIKeyInput>
@end
@implementation GameViewController
{
BOOL keyboardAllowed;
NSUInteger keyboardHash;
EAGLContext* context;
ZahnradBackend* zr;
}
@ -39,6 +42,8 @@
{
[super viewDidLoad];
keyboardHash = -1;
context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3];
assert(context != nil);
@ -110,6 +115,58 @@
}
- (void) insertText: (NSString*) text
{
[zr addEvent: @{@"type" : @12, @"txt" : text, @"mod" : @0}];
}
- (void) deleteBackward
{
[zr addEvent: @{@"type" : @12, @"txt" : @"\b", @"mod" : @0}];
}
- (BOOL) hasText
{
return NO;
}
- (BOOL) canBecomeFirstResponder
{
return keyboardAllowed;
}
- (BOOL) canResignFirstResponder
{
return YES;
}
- (void) showKeyboard: (NSDictionary*) info
{
NSUInteger hash = [info[@"hash"] unsignedIntegerValue];
if (hash != keyboardHash)
{
keyboardHash = hash;
keyboardAllowed = YES;
if (!self.isFirstResponder)
[self becomeFirstResponder];
}
}
- (void) hideKeyboard
{
keyboardHash = -1;
keyboardAllowed = NO;
[self resignFirstResponder];
}
@end
@ -119,6 +176,7 @@
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow* window;
@property (strong, nonatomic) GameViewController* gameViewController;
@end
@ -126,10 +184,27 @@
@implementation AppDelegate
- (void) showKeyboard: (NSDictionary*) info
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.gameViewController showKeyboard: info];
});
}
- (void) hideKeyboard
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.gameViewController hideKeyboard];
});
}
- (BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions
{
_window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
_window.rootViewController = [GameViewController new];
_gameViewController = [GameViewController new];
_window.rootViewController = _gameViewController;
[_window makeKeyAndVisible];
return YES;

View File

@ -27,6 +27,8 @@
500B208F1C4B4D700002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
500B20901C4B4D700002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = "<group>"; };
500B20911C4B4D700002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = "<group>"; };
504DFDEC1C4EA2FF00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = "<group>"; };
504DFDF21C4EA37700D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -60,9 +62,11 @@
500B20631C4B4AB00002B53F /* tvOS_GL_ES */ = {
isa = PBXGroup;
children = (
504DFDF21C4EA37700D3714A /* demo.c */,
500B20881C4B4D460002B53F /* ZahnradBackend.h */,
500B20891C4B4D460002B53F /* ZahnradBackend.m */,
500B20651C4B4AB00002B53F /* main.m */,
504DFDEC1C4EA2FF00D3714A /* zahnrad.h */,
500B208B1C4B4D580002B53F /* zahnrad.c */,
500B20741C4B4AB00002B53F /* Info.plist */,
);

View File

@ -24,17 +24,21 @@
#import "ZahnradBackend.h"
@interface GameViewController : GLKViewController
@interface GameViewController : GLKViewController <UITextFieldDelegate>
@end
@implementation GameViewController
{
EAGLContext* context;
ZahnradBackend* zr;
NSUInteger keyboardHash;
UITextField* textInputField;
CGPoint cursor;
UIView* cursorView;
EAGLContext* context;
ZahnradBackend* zr;
}
@ -42,6 +46,8 @@
{
[super viewDidLoad];
keyboardHash = -1;
context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3];
assert(context != nil);
@ -53,10 +59,14 @@
UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action: @selector(pan:)];
[self.view addGestureRecognizer:panRecognizer];
cursorView = [[UIView alloc] initWithFrame: CGRectMake(100, 100, 10, 10)];
cursorView.backgroundColor = UIColor.redColor;
[self.view addSubview: cursorView];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
cursor = CGPointMake(95, 70);
cursorView = [[UIView alloc] initWithFrame: CGRectMake(cursor.x, cursor.y, 10, 10)];
cursorView.backgroundColor = UIColor.redColor;
[self.view addSubview: cursorView];
[zr addEvent: @{@"type" : @2, @"pos" : NSStringFromCGPoint(cursor)}];
});
}
@ -85,8 +95,23 @@
{
CGPoint delta = [panRecognizer translationInView: self.view];
cursor.x += delta.x / 33.0;
cursor.y += delta.y / 33.0;
#if 0
CGFloat s = 33.0;
cursor.x += delta.x / s;
cursor.y += delta.y / s;
#else
CGFloat s = 150.0;
if (delta.x != 0.0)
cursor.x += pow(2.0, fabs(delta.x / s)) * (delta.x > 0.0 ? 1.0 : -1.0);
if (delta.y != 0.0)
cursor.y += pow(2.0, fabs(delta.y / s)) * (delta.y > 0.0 ? 1.0 : -1.0);
#endif
CGRect bounds = self.view.bounds;
if (cursor.x < CGRectGetMinX(bounds)) cursor.x = CGRectGetMinX(bounds);
@ -95,7 +120,7 @@
if (cursor.y > CGRectGetMaxY(bounds)) cursor.y = CGRectGetMaxY(bounds);
cursorView.center = cursor;
[zr addEvent: @{@"type" : @2, @"pos" : NSStringFromCGPoint(cursor)}];
}
@ -124,6 +149,53 @@
}
- (BOOL) textFieldShouldReturn: (UITextField*) textField
{
return YES;
}
- (void) textFieldDidEndEditing: (UITextField*) textField
{
[textInputField resignFirstResponder];
[textInputField removeFromSuperview];
textInputField = nil;
[zr addEvent: @{@"type" : @12, @"txt" : textField.text, @"mod" : @0}];
}
- (void) showKeyboard: (NSDictionary*) info
{
NSUInteger hash = [info[@"hash"] unsignedIntegerValue];
if (hash != keyboardHash)
{
keyboardHash = hash;
if (!textInputField)
{
CGRect frame = CGRectFromString(info[@"frame"]);
textInputField = [[UITextField alloc] initWithFrame: frame];
textInputField.delegate = self;
textInputField.text = info[@"text"];
[self.view addSubview: textInputField];
[textInputField becomeFirstResponder];
}
}
}
- (void) hideKeyboard
{
[textInputField resignFirstResponder];
[textInputField removeFromSuperview];
textInputField = nil;
keyboardHash = -1;
}
@end
@ -133,6 +205,7 @@
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow* window;
@property (strong, nonatomic) GameViewController* gameViewController;
@end
@ -140,10 +213,27 @@
@implementation AppDelegate
- (void) showKeyboard: (NSDictionary*) info
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.gameViewController showKeyboard: info];
});
}
- (void) hideKeyboard
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.gameViewController hideKeyboard];
});
}
- (BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions
{
_window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
_window.rootViewController = [GameViewController new];
_gameViewController = [GameViewController new];
_window.rootViewController = _gameViewController;
[_window makeKeyAndVisible];
return YES;