// lite editor, platform details // - rlyeh, public domain #define LT_DATAPATH "/lite" #define lt_assert(x) ASSERT(x) #define lt_realpath(p, q) file_pathabs(p) #define lt_realpath_free(p) #define lt_malloc(n) MALLOC(n) #define lt_calloc(n,m) CALLOC(n,m) #define lt_free(p) FREE(p) #define lt_memcpy(d,s,c) memcpy(d,s,c) #define lt_memset(p,ch,c) memset(p,ch,c) #define lt_time_ms() time_ms() #define lt_sleep_ms(ms) sleep_ms(ms) #define lt_getclipboard(w) window_clipboard() #define lt_setclipboard(w,s) window_setclipboard(s) #define lt_window() window_handle() #define lt_setwindowmode(m) window_fullscreen(m == 2), (m < 2 && (window_maximize(m),1)) // 0:normal,1:maximized,2:fullscreen #define lt_setwindowtitle(t) //window_title(t) #define lt_haswindowfocus() window_has_focus() #define lt_setcursor(shape) window_cursor_shape(lt_events & (1<<31) ? CURSOR_SW_AUTO : shape+1) // 0:arrow,1:ibeam,2:sizeh,3:sizev,4:hand #define lt_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES)) unsigned lt_events = ~0u; int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0; typedef struct lt_surface { unsigned w, h; void *pixels; texture_t t; } lt_surface; typedef struct lt_rect { int x, y, width, height; } lt_rect; lt_surface *lt_getsurface(void *window) { static lt_surface s = {0}; return &s; } void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) { if(0) for( int i = 0; i < count; ++i ) { memset( (unsigned*)s->pixels + (rects[i].x + rects[i].y * s->w), 0xFF, rects[i].width*4 ); memset( (unsigned*)s->pixels + (rects[i].x + (rects[i].y + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 ); for( int y = 1; y < (rects[i].height-1); ++y ) { ((unsigned*)s->pixels)[ rects[i].x + y * s->w ] = ((unsigned*)s->pixels)[ rects[i].x + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF; } } // update contents texture_update(&s->t, s->w, s->h, 4, s->pixels, TEXTURE_LINEAR|TEXTURE_BGRA); } void ren_set_clip_rect(struct lt_rect rect); void rencache_invalidate(void); int lt_resizesurface(lt_surface *s, int ww, int wh) { s->w = ww, s->h = wh; if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) { // invalidate tiles ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } ); rencache_invalidate(); // texture clear if( !s->t.id ) s->t = texture_create(1, 1, 4, " ", TEXTURE_LINEAR|TEXTURE_RGBA|TEXTURE_BYTE ); s->pixels = REALLOC(s->pixels, s->w * s->h * 4); memset(s->pixels, 0, s->w * s->h * 4); // texture update lt_updatesurfacerects(s,0,0); return 1; // resized } return 0; // unchanged } void *lt_load_file(const char *filename, int *size) { int datalen; char *data = file_load(filename, &datalen); if( !data || !datalen ) { filename = (const char *)file_normalize(filename); if( strbegi(filename, app_path()) ) filename += strlen(app_path()); data = vfs_load(filename, &datalen); } if (size) *size = 0; if (!data) { return NULL; } if (size) *size = datalen; // return permanent buffers here, as file_load() and vfs_load() do return temporaries data = memcpy(MALLOC(datalen+1), data, datalen); data[datalen] = 0; return data; } const char* lt_button_name(int button) { if(button == GLFW_MOUSE_BUTTON_LEFT) return "left"; if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right"; if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle"; return "?"; } char* lt_key_name(char *dst, int key, int vk, int mods) { // @todo: "altgr" -> left ctrl + right alt if( key == GLFW_KEY_UP ) return "up"; if( key == GLFW_KEY_DOWN ) return "down"; if( key == GLFW_KEY_LEFT ) return "left"; if( key == GLFW_KEY_RIGHT ) return "right"; if( key == GLFW_KEY_LEFT_ALT ) return "left alt"; if( key == GLFW_KEY_RIGHT_ALT ) return "right alt"; if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift"; if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift"; if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl"; if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl"; if( key == GLFW_KEY_LEFT_SUPER ) return "left windows"; if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows"; if( key == GLFW_KEY_MENU ) return "menu"; if( key == GLFW_KEY_ESCAPE ) return "escape"; if( key == GLFW_KEY_BACKSPACE ) return "backspace"; if( key == GLFW_KEY_ENTER ) return "return"; if( key == GLFW_KEY_KP_ENTER ) return "keypad enter"; if( key == GLFW_KEY_TAB ) return "tab"; if( key == GLFW_KEY_CAPS_LOCK ) return "capslock"; if( key == GLFW_KEY_HOME ) return "home"; if( key == GLFW_KEY_END ) return "end"; if( key == GLFW_KEY_INSERT ) return "insert"; if( key == GLFW_KEY_DELETE ) return "delete"; if( key == GLFW_KEY_PAGE_UP ) return "pageup"; if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown"; if( key == GLFW_KEY_F1 ) return "f1"; if( key == GLFW_KEY_F2 ) return "f2"; if( key == GLFW_KEY_F3 ) return "f3"; if( key == GLFW_KEY_F4 ) return "f4"; if( key == GLFW_KEY_F5 ) return "f5"; if( key == GLFW_KEY_F6 ) return "f6"; if( key == GLFW_KEY_F7 ) return "f7"; if( key == GLFW_KEY_F8 ) return "f8"; if( key == GLFW_KEY_F9 ) return "f9"; if( key == GLFW_KEY_F10 ) return "f10"; if( key == GLFW_KEY_F11 ) return "f11"; if( key == GLFW_KEY_F12 ) return "f12"; const char *name = glfwGetKeyName(key, vk); strcpy(dst, name ? name : ""); char *p = dst; while (*p) { *p = tolower(*p); p++; } return dst; } void lt_globpath(struct lua_State*L, const char *path) { unsigned j = 0; if(!strend(path, "/")) path = (const char *)va("%s/", path); for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) { for( unsigned i = 0, end = dir_count(d); i < end; ++i ) { char *name = dir_name(d,i); char *last = name + strlen(name) - 1; if( *last == '/' ) *last = '\0'; name = file_name(name); lua_pushstring(L, name); lua_rawseti(L, -2, ++j); } } for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) { array(char*) list = vfs_list("**"); for( unsigned i = 0, end = array_count(list); i < end; ++i ) { char *name = list[i]; if( !strstri(name, section+1) ) continue; lua_pushstring(L, file_name(name)); lua_rawseti(L, -2, ++j); } if( array_count(list) ) return; } } int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) { int count = 0; lua_pushstring(L, event_name); if( event_fmt ) { va_list va; va_start(va, event_fmt); for( ; event_fmt[count]; ++count ) { /**/ if( event_fmt[count] == 'd' ) { int d = va_arg(va, int); lua_pushnumber(L, d); } else if( event_fmt[count] == 'f' ) { double f = va_arg(va, double); lua_pushnumber(L, f); } else if( event_fmt[count] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); } } va_end(va); } return 1+count; } int printi(int i) { // printf("clicks: %d\n", i); return i; } static const char* codepoint_to_utf8(unsigned c); int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext int rc = 0; char buf[16]; static int prevx = 0, prevy = 0; static unsigned clicks_time = 0, clicks = 0; if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0; // for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) ) if( lt_events & e.type ) switch (e.type) { default: break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow rc += lt_emit_event(L, "quit", NULL); return rc; break; case GLEQ_WINDOW_MOVED: lt_wx = e.pos.x; lt_wy = e.pos.y; break; case GLEQ_WINDOW_RESIZED: rc += lt_emit_event(L, "resized", "dd", lt_ww = e.size.width, lt_wh = e.size.height); lt_resizesurface(lt_getsurface(lt_window()), lt_ww, lt_wh); break; case GLEQ_WINDOW_REFRESH: rc += lt_emit_event(L, "exposed", NULL); rencache_invalidate(); break; case GLEQ_FILE_DROPPED: rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my); break; case GLEQ_KEY_PRESSED: case GLEQ_KEY_REPEATED: rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods)); goto bottom; break; case GLEQ_KEY_RELEASED: rc += lt_emit_event(L, "keyreleased", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods)); goto bottom; break; case GLEQ_CODEPOINT_INPUT: rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8(e.codepoint)); break; case GLEQ_BUTTON_PRESSED: rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks)); break; case GLEQ_BUTTON_RELEASED: clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1; clicks_time = lt_time_ms(); rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my); break; case GLEQ_CURSOR_MOVED: lt_mx = e.pos.x - lt_wx, lt_my = e.pos.y - lt_wy; rc += lt_emit_event(L, "mousemoved", "dddd", lt_mx, lt_my, lt_mx - prevx, lt_my - prevy); prevx = lt_mx, prevy = lt_my; break; case GLEQ_SCROLLED: rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y); } bottom:; return rc; }