diff --git a/src/luaapi.c b/src/luaapi.c index b91346a..9652144 100644 --- a/src/luaapi.c +++ b/src/luaapi.c @@ -26,6 +26,9 @@ * of sanity. * */ +extern uint16_t GameWndW; +extern uint16_t GameWndH; + static lua_State *L; #define seti(src, dst) \ @@ -1251,6 +1254,8 @@ static int game_camfocus(lua_State *L) { #define MENUITEM_TEXTBUTTON 2 #define MENUITEM_TEXTINPUT 3 #define MENUITEM_OBJ 4 +#define MENUITEM_IMAGE 5 +#define MENUITEM_SCROLLBOX 6 struct menuitem { int type; struct k3MObj *ptr; @@ -1303,7 +1308,16 @@ static int game_set(lua_State *L) { } else if(!strcmp(i, "menu")) { struct menuitem *item = lua_touserdata(L, 3); UiActive = item ? item->ptr : NULL; + set_ui_mode(!!UiActive); + + if(UiActive) { + UiActive->w = UiActive->wDesired = GameWndW; + UiActive->h = UiActive->hDesired = GameWndH; + + k3MMeasure(UiActive); + k3MArrange(UiActive); + } } else { lua_pushvalue(L, 2); lua_pushvalue(L, 3); @@ -1909,7 +1923,7 @@ static int dagame_draw(lua_State *L) { lua_getfield(L, 1, "x"); lua_getfield(L, 1, "y"); - struct k3RectF rect; + struct k3RectF rect = {}; k3FontSz(font, lua_tonumber(L, -4), lua_tostring(L, -3), &rect); if(!strcmp(lua_tostring(L, -9), "bottommiddle")) { @@ -2020,6 +2034,33 @@ static int dagame_ui_textinput(lua_State *L) { return 1; } +static int dagame_ui_image(lua_State *L) { + struct k3Tex *tex; + + if(lua_type(L, 1) == LUA_TSTRING) { + tex = resman_ref(RESMAN_TEXTURE, lua_tostring(L, 1)); + } else { + tex = *(void**) lua_touserdata(L, 1); + + struct ResManRes *res = resman_rev(tex); + res->refs++; + } + + struct menuitem *item = lua_newuserdata(L, sizeof(*item)); + item->type = MENUITEM_IMAGE; + item->ptr = (void*) k3MImage(tex); + luaL_setmetatable(L, "k3menuitem"); + return 1; +} + +static int dagame_ui_scrollbox(lua_State *L) { + struct menuitem *item = lua_newuserdata(L, sizeof(*item)); + item->type = MENUITEM_SCROLLBOX; + item->ptr = (void*) k3MScrollbox(); + luaL_setmetatable(L, "k3menuitem"); + return 1; +} + static int dagame_k3menuitem_set_bounds(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); @@ -2049,12 +2090,19 @@ static bool k3menuitem_event_callback(struct k3MEvent *ev, uint8_t *ud_) { lua_State *L = ud[0]; intptr_t luaref = (intptr_t) ud[1]; + void *ptr = ud[2]; + intptr_t type = (intptr_t) ud[3]; int oldtop = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, luaref); - if(lua_pcall(L, 0, 1, 0) != LUA_OK) { + struct menuitem *item = lua_newuserdata(L, sizeof(*item)); + item->type = type; + item->ptr = ptr; + luaL_setmetatable(L, "k3menuitem"); + + if(lua_pcall(L, 1, 1, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return false; @@ -2078,7 +2126,7 @@ static int dagame_k3menuitem_on(lua_State *L) { } else if(!strcmp(lua_tostring(L, 2), "mouse_motion")) { evcode = k3M_EVENT_MOUSE_MOTION; } else if(!strcmp(lua_tostring(L, 2), "mouse_leave")) { - evcode = k3M_EVENT_MOUSE_RELEASE; + evcode = k3M_EVENT_MOUSE_LEAVE; } else if(!strcmp(lua_tostring(L, 2), "mouse_press")) { evcode = k3M_EVENT_MOUSE_PRESS; } else if(!strcmp(lua_tostring(L, 2), "mouse_release")) { @@ -2095,13 +2143,17 @@ static int dagame_k3menuitem_on(lua_State *L) { evcode = k3M_EVENT_DRAW; } else if(!strcmp(lua_tostring(L, 2), "complete")) { evcode = k3M_EVENT_COMPLETE; + } else if(!strcmp(lua_tostring(L, 2), "measure")) { + evcode = k3M_EVENT_MEASURE; + } else if(!strcmp(lua_tostring(L, 2), "arrange")) { + evcode = k3M_EVENT_ARRANGE; } else if(!strcmp(lua_tostring(L, 2), "all")) { evcode = k3M_EVENT_ALL; } if(evcode != 0xFFFF) { lua_pushvalue(L, 3); - void *ud[2] = {L, (intptr_t) luaL_ref(L, LUA_REGISTRYINDEX)}; + void *ud[4] = {L, (intptr_t) luaL_ref(L, LUA_REGISTRYINDEX), item->ptr, item->type}; k3MRegisterEventHandler(item->ptr, evcode, k3menuitem_event_callback, ud, sizeof(ud)); } @@ -2110,6 +2162,33 @@ static int dagame_k3menuitem_on(lua_State *L) { return 1; } +static int dagame_k3menuitem_measure(lua_State *L) { + struct menuitem *item = lua_touserdata(L, 1); + + k3MMeasure(item->ptr); + + lua_pushvalue(L, 1); + return 1; +} + +static int dagame_k3menuitem_arrange(lua_State *L) { + struct menuitem *item = lua_touserdata(L, 1); + + k3MArrange(item->ptr); + + lua_pushvalue(L, 1); + return 1; +} + +static int dagame_k3menuitem_set_layout_linear(lua_State *L) { + struct menuitem *item = lua_touserdata(L, 1); + + k3MSetLayoutLinear(item->ptr, lua_type(L, 2) == LUA_TNIL ? true : lua_toboolean(L, 2)); + + lua_pushvalue(L, 1); + return 1; +} + static int dagame_k3menuitem_get(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); @@ -2123,6 +2202,20 @@ static int dagame_k3menuitem_get(lua_State *L) { lua_pushcfunction(L, dagame_k3menuitem_add_child); } else if(!strcmp(lua_tostring(L, 2), "on")) { lua_pushcfunction(L, dagame_k3menuitem_on); + } else if(!strcmp(lua_tostring(L, 2), "measure")) { + lua_pushcfunction(L, dagame_k3menuitem_measure); + } else if(!strcmp(lua_tostring(L, 2), "arrange")) { + lua_pushcfunction(L, dagame_k3menuitem_arrange); + } else if(!strcmp(lua_tostring(L, 2), "set_layout_linear")) { + lua_pushcfunction(L, dagame_k3menuitem_set_layout_linear); + } else if(!strcmp(lua_tostring(L, 2), "x")) { + lua_pushnumber(L, item->ptr->x); + } else if(!strcmp(lua_tostring(L, 2), "y")) { + lua_pushnumber(L, item->ptr->y); + } else if(!strcmp(lua_tostring(L, 2), "w")) { + lua_pushnumber(L, item->ptr->w); + } else if(!strcmp(lua_tostring(L, 2), "h")) { + lua_pushnumber(L, item->ptr->h); } else if(!strcmp(lua_tostring(L, 2), "text")) { char *txt = NULL; @@ -2141,11 +2234,75 @@ static int dagame_k3menuitem_get(lua_State *L) { lua_pushnil(L); } + } else if(item->type == MENUITEM_SCROLLBOX && !strcmp(lua_tostring(L, 2), "content")) { + struct menuitem *c = lua_newuserdata(L, sizeof(*c)); + c->type = MENUITEM_OBJ; + c->ptr = k3MScrollboxGetContent(item->ptr); + luaL_setmetatable(L, "k3menuitem"); } else lua_pushnil(L); return 1; } +static uint8_t unit_from_string(const char *str) { + if(!strcmp(str, "p")) { + return k3M_UNIT_PROPORTION; + } + return k3M_UNIT_ABSOLUTE; +} + +static float parse_prop_float(uint8_t *unit) { + if(lua_type(L, -1) == LUA_TNUMBER) { + *unit = k3M_UNIT_ABSOLUTE; + return lua_tonumber(L, -1); + } else if(lua_type(L, -1) == LUA_TBOOLEAN) { + *unit = k3M_UNIT_ABSOLUTE; + return lua_toboolean(L, -1); + } else if(lua_type(L, -1) == LUA_TTABLE) { + lua_geti(L, -1, 2); + *unit = unit_from_string(lua_tostring(L, -1)); + lua_pop(L, 1); + + lua_geti(L, -1, 1); + float val = lua_tonumber(L, -1); + lua_pop(L, 1); + + return val; + } else if(lua_type(L, -1) == LUA_TSTRING) { + const char *str = lua_tostring(L, -1); + float val = strtof(str, (char**) &str); + *unit = unit_from_string(str); + + return val; + } +} + +static intmax_t parse_prop_int(uint8_t *unit) { + if(lua_type(L, -1) == LUA_TNUMBER) { + *unit = k3M_UNIT_ABSOLUTE; + return lua_tointeger(L, -1); + } else if(lua_type(L, -1) == LUA_TBOOLEAN) { + *unit = k3M_UNIT_ABSOLUTE; + return lua_toboolean(L, -1); + } else if(lua_type(L, -1) == LUA_TTABLE) { + lua_geti(L, -1, 2); + *unit = unit_from_string(lua_tostring(L, -1)); + lua_pop(L, 1); + + lua_geti(L, -1, 1); + float val = lua_tointeger(L, -1); + lua_pop(L, 1); + + return val; + } else if(lua_type(L, -1) == LUA_TSTRING) { + const char *str = lua_tostring(L, -1); + long long val = strtoll(str, (char**) &str, 0); + *unit = unit_from_string(str); + + return val; + } +} + static int dagame_k3menuitem_set(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); @@ -2175,11 +2332,19 @@ static int dagame_k3menuitem_set(lua_State *L) { *txt = strdup(new ? new : "nil"); } + } else if(!strcmp(lua_tostring(L, 2), "x")) { + item->ptr->x = lua_tonumber(L, 3); + } else if(!strcmp(lua_tostring(L, 2), "y")) { + item->ptr->y = lua_tonumber(L, 3); + } else if(!strcmp(lua_tostring(L, 2), "w")) { + item->ptr->w = lua_tonumber(L, 3); + } else if(!strcmp(lua_tostring(L, 2), "h")) { + item->ptr->h = lua_tonumber(L, 3); } else if(strstr(key, "prop_") == key) { const char *propname = key + 5; enum CommonType { - NONE, COL4, INT4 + NONE, COL4, INT4, FLT4 } typ; struct k3MProperty prop; @@ -2187,11 +2352,38 @@ static int dagame_k3menuitem_set(lua_State *L) { if(!strcmp(propname, "bg_color")) { prop.type = k3M_PROP_BG_COLOR; typ = COL4; + } else if(!strcmp(propname, "fg_color")) { + prop.type = k3M_PROP_FG_COLOR; + typ = COL4; } else if(!strcmp(propname, "border_radius")) { prop.type = k3M_PROP_BORDER_RADIUS; typ = INT4; } else if(!strcmp(propname, "margin")) { prop.type = k3M_PROP_MARGIN; + typ = FLT4; + } else if(!strcmp(propname, "padding")) { + prop.type = k3M_PROP_PADDING; + typ = FLT4; + } else if(!strcmp(propname, "left")) { + prop.type = k3M_PROP_LEFT; + typ = FLT4; + } else if(!strcmp(propname, "top")) { + prop.type = k3M_PROP_TOP; + typ = FLT4; + } else if(!strcmp(propname, "width")) { + prop.type = k3M_PROP_WIDTH; + typ = FLT4; + } else if(!strcmp(propname, "height")) { + prop.type = k3M_PROP_HEIGHT; + typ = FLT4; + } else if(!strcmp(propname, "font_size")) { + prop.type = k3M_PROP_FONT_SIZE; + typ = FLT4; + } else if(!strcmp(propname, "scrollbar_size")) { + prop.type = k3M_PROP_SCROLLBAR_SIZE; + typ = INT4; + } else if(!strcmp(propname, "scroll_anywhere")) { + prop.type = k3M_PROP_SCROLL_ANYWHERE; typ = INT4; } else if(!strcmp(propname, "horizontal_alignment")) { prop.type = k3M_PROP_HORIZONTAL_ALIGNMENT; @@ -2202,6 +2394,17 @@ static int dagame_k3menuitem_set(lua_State *L) { } else { prop.si[0] = k3M_ALIGN_CENTER; } + } else if(!strcmp(propname, "vertical_alignment")) { + prop.type = k3M_PROP_VERTICAL_ALIGNMENT; + if(!strcmp(lua_tostring(L, 3), "top")) { + prop.si[0] = k3M_ALIGN_TOP; + } else if(!strcmp(lua_tostring(L, 3), "bottom")) { + prop.si[0] = k3M_ALIGN_BOTTOM; + } else { + prop.si[0] = k3M_ALIGN_CENTER; + } + } else { + return 0; } if(typ == COL4) { @@ -2209,6 +2412,8 @@ static int dagame_k3menuitem_set(lua_State *L) { lua_geti(L, 3, 4); if(lua_type(L, -1) == LUA_TNIL) { prop.si[3] = 255; + } else { + prop.si[3] = lua_tonumber(L, -1) * 255; } lua_pop(L, 1); @@ -2221,28 +2426,100 @@ static int dagame_k3menuitem_set(lua_State *L) { lua_pop(L, 3); } else if(lua_isnumber(L, 3)) { - prop.si[0] = lua_tonumber(L, 3); - prop.si[1] = lua_tonumber(L, 3); - prop.si[2] = lua_tonumber(L, 3); + prop.si[0] = lua_tonumber(L, 3) * 255; + prop.si[1] = lua_tonumber(L, 3) * 255; + prop.si[2] = lua_tonumber(L, 3) * 255; prop.si[3] = 255; } } else if(typ == INT4) { + bool isSingleComponent = false; if(lua_type(L, 3) == LUA_TTABLE) { - lua_geti(L, 3, 1); - lua_geti(L, 3, 2); - lua_geti(L, 3, 3); - lua_geti(L, 3, 4); - prop.si[0] = lua_tonumber(L, -4) * 255; - prop.si[1] = lua_tonumber(L, -3) * 255; - prop.si[2] = lua_tonumber(L, -2) * 255; - prop.si[3] = lua_tonumber(L, -1) * 255; + lua_len(L, 3); + if(lua_tointeger(L, -1) == 2) { + lua_geti(L, 3, 2); + isSingleComponent = lua_type(L, -1) == LUA_TSTRING; + lua_pop(L, 2); + } else { + lua_pop(L, 1); + } + } else { + isSingleComponent = true; + } + + if(isSingleComponent) { + lua_pushvalue(L, 3); - lua_pop(L, 4); - } else if(lua_isnumber(L, 3)) { - prop.si[0] = lua_tonumber(L, 3); - prop.si[1] = lua_tonumber(L, 3); - prop.si[2] = lua_tonumber(L, 3); - prop.si[3] = lua_tonumber(L, 4); + prop.si[0] = parse_prop_int(&prop.units[0]); + prop.si[1] = prop.si[0]; + prop.si[2] = prop.si[0]; + prop.si[3] = prop.si[0]; + + prop.units[1] = prop.units[0]; + prop.units[2] = prop.units[0]; + prop.units[3] = prop.units[0]; + + lua_pop(L, 1); + } else if(lua_type(L, 3) == LUA_TTABLE) { + lua_geti(L, 3, 1); + prop.si[0] = parse_prop_int(&prop.units[0]); + lua_pop(L, 1); + + lua_geti(L, 3, 2); + prop.si[1] = parse_prop_int(&prop.units[1]); + lua_pop(L, 1); + + lua_geti(L, 3, 3); + prop.si[2] = parse_prop_int(&prop.units[2]); + lua_pop(L, 1); + + lua_geti(L, 3, 4); + prop.si[3] = parse_prop_int(&prop.units[3]); + lua_pop(L, 1); + } + } else if(typ == FLT4) { + bool isSingleComponent = false; + if(lua_type(L, 3) == LUA_TTABLE) { + lua_len(L, 3); + if(lua_tointeger(L, -1) == 2) { + lua_geti(L, 3, 2); + isSingleComponent = lua_type(L, -1) == LUA_TSTRING; + lua_pop(L, 2); + } else { + lua_pop(L, 1); + } + } else { + isSingleComponent = true; + } + + if(isSingleComponent) { + lua_pushvalue(L, 3); + + prop.f[0] = parse_prop_float(&prop.units[0]); + prop.f[1] = prop.f[0]; + prop.f[2] = prop.f[0]; + prop.f[3] = prop.f[0]; + + prop.units[1] = prop.units[0]; + prop.units[2] = prop.units[0]; + prop.units[3] = prop.units[0]; + + lua_pop(L, 1); + } else if(lua_type(L, 3) == LUA_TTABLE) { + lua_geti(L, 3, 1); + prop.f[0] = parse_prop_float(&prop.units[0]); + lua_pop(L, 1); + + lua_geti(L, 3, 2); + prop.f[1] = parse_prop_float(&prop.units[1]); + lua_pop(L, 1); + + lua_geti(L, 3, 3); + prop.f[2] = parse_prop_float(&prop.units[2]); + lua_pop(L, 1); + + lua_geti(L, 3, 4); + prop.f[3] = parse_prop_float(&prop.units[3]); + lua_pop(L, 1); } } @@ -3239,6 +3516,12 @@ void luaapi_init() { lua_pushcfunction(L, dagame_ui_textinput); lua_setfield(L, -2, "textinput"); + + lua_pushcfunction(L, dagame_ui_image); + lua_setfield(L, -2, "image"); + + lua_pushcfunction(L, dagame_ui_scrollbox); + lua_setfield(L, -2, "scrollbox"); lua_setfield(L, -2, "ui"); k3Log(k3_DEBUG, "Creating table game.net"); @@ -3296,6 +3579,12 @@ void luaapi_init() { lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); + int width_mm, height_mm; + glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &width_mm, &height_mm); + const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + lua_pushnumber(L, (double) mode->width / width_mm); + lua_setfield(L, -2, "dpmm"); + lua_setglobal(L, "game"); luaL_newmetatable(L, "k3water"); diff --git a/src/main.c b/src/main.c index 7d0bde6..c6794bd 100644 --- a/src/main.c +++ b/src/main.c @@ -41,6 +41,8 @@ #include GLFWwindow *GameWnd; +uint16_t GameWndW; +uint16_t GameWndH; static int TextureResolutionReduction = 0; @@ -70,9 +72,6 @@ static double xposold, yposold; static void buttoncallback(GLFWwindow *GameWnd, int button, int action, int mods) { if(button == GLFW_MOUSE_BUTTON_LEFT) { if(UiActive) { - int ww, wh; - glfwGetWindowSize(GameWnd, &ww, &wh); - struct k3MEvent ev = { .original = (void*) UiActive, .target = (void*) UiActive, @@ -80,8 +79,8 @@ static void buttoncallback(GLFWwindow *GameWnd, int button, int action, int mods .code = action == GLFW_PRESS ? k3M_EVENT_MOUSE_PRESS : k3M_EVENT_MOUSE_RELEASE, .mouse = { .button = k3M_MOUSE_BUTTON_0, - .x = xposold * UiActive->w / ww, - .y = (wh - yposold - 1) * UiActive->h / wh, + .x = xposold * UiActive->w / GameWndW, + .y = yposold * UiActive->h / GameWndH, }, }; @@ -110,11 +109,8 @@ static void motioncallback(GLFWwindow *GameWnd, double xpos, double ypos) { first = 0; if(UiActive) { - int ww, wh; - glfwGetWindowSize(GameWnd, &ww, &wh); - - uint16_t uix = xposold * UiActive->w / ww; - uint16_t uiy = (wh - yposold - 1) * UiActive->h / wh; + uint16_t uix = xposold * UiActive->w / GameWndW; + uint16_t uiy = yposold * UiActive->h / GameWndH; struct k3MEvent ev = { .original = (void*) UiActive, @@ -172,8 +168,16 @@ static void charcallback(GLFWwindow *window, unsigned int codepoint) { static void fix_resol(); static void resizecallback(GLFWwindow *window, int width, int height) { - k3Resize(width, height); + k3Resize(GameWndW = width, GameWndH = height); fix_resol(); + + if(UiActive) { + UiActive->w = UiActive->wDesired = GameWndW; + UiActive->h = UiActive->hDesired = GameWndH; + + k3MMeasure(UiActive); + k3MArrange(UiActive); + } } static int argc; @@ -217,14 +221,17 @@ static int eng_init() { #define START_WIDTH 960 #define START_HEIGHT 540 #define START_TITLE "k4" - + + GameWndW = START_WIDTH; + GameWndH = START_HEIGHT; + // Prefer core if(!k4_get_arg("core") || strcmp(k4_get_arg("core"), "0")) { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - GameWnd = glfwCreateWindow(START_WIDTH, START_HEIGHT, START_TITLE, NULL, NULL); + GameWnd = glfwCreateWindow(GameWndW, GameWndH, START_TITLE, NULL, NULL); } // Attempt compatibility @@ -233,7 +240,7 @@ static int eng_init() { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - GameWnd = glfwCreateWindow(START_WIDTH, START_HEIGHT, START_TITLE, NULL, NULL); + GameWnd = glfwCreateWindow(GameWndW, GameWndH, START_TITLE, NULL, NULL); } k3MixInit(44100, 2); @@ -433,6 +440,9 @@ int main(int argc_, char **argv_) { net_hi_update(CurrentTime); + int windowW, windowH; + glfwGetWindowSize(GameWnd, &windowW, &windowH); + float alpha = fmodf(accumulator * GAME_TPS, 1.f); vec3 cameraForwardDir = {0, 0, -1}; @@ -644,13 +654,7 @@ int main(int argc_, char **argv_) { glClear(GL_DEPTH_BUFFER_BIT); - if(!k3IsCore) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, 3600, 0, 2025, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } + k3BatchSetResolution(windowW, windowH); luaapi_render2d();