nuclear@6: /* nuclear@6: GoatKit - a themable/animated widget toolkit for games nuclear@9: Copyright (C) 2014-2015 John Tsiombikas nuclear@6: nuclear@6: This program is free software: you can redistribute it and/or modify nuclear@6: it under the terms of the GNU Lesser General Public License as published by nuclear@6: the Free Software Foundation, either version 3 of the License, or nuclear@6: (at your option) any later version. nuclear@6: nuclear@6: This program is distributed in the hope that it will be useful, nuclear@6: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@6: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@6: GNU Lesser General Public License for more details. nuclear@6: nuclear@6: You should have received a copy of the GNU Lesser General Public License nuclear@6: along with this program. If not, see . nuclear@6: */ nuclear@6: #include "config.h" nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@9: #include nuclear@6: #include "theme.h" nuclear@6: #include "widget.h" nuclear@6: nuclear@6: #ifdef WIN32 nuclear@6: #include nuclear@6: nuclear@9: #define RTLD_DEFAULT ((void*)0) nuclear@9: nuclear@6: static void *dlopen(const char *name, int flags); nuclear@6: static void dlclose(void *so); nuclear@6: static void *dlsym(void *so, const char *symbol); nuclear@6: #else nuclear@6: #include nuclear@6: #include nuclear@6: #endif nuclear@6: nuclear@6: #ifdef HAVE_OPENGL_H nuclear@6: #include "opengl.h" nuclear@6: nuclear@6: #else nuclear@6: nuclear@6: #ifdef __APPLE__ nuclear@6: #include nuclear@6: #else nuclear@6: #include nuclear@6: #endif nuclear@6: nuclear@6: #endif /* HAVE_OPENGL_H_ */ nuclear@6: nuclear@6: #ifndef PREFIX nuclear@6: #define PREFIX "/usr/local" nuclear@6: #endif nuclear@6: nuclear@6: namespace goatkit { nuclear@6: nuclear@6: struct ThemeImpl { nuclear@6: void *so; nuclear@6: WidgetDrawFunc (*lookup_theme_draw_func)(const char*); nuclear@6: mutable std::map func_cache; nuclear@6: }; nuclear@6: nuclear@6: Theme *theme; nuclear@6: static std::vector search_paths; nuclear@6: static const char *fallback_paths[] = { nuclear@6: PREFIX "/share/goatkit", nuclear@6: 0 nuclear@6: }; nuclear@6: nuclear@11: typedef std::map ThemeMap; nuclear@11: static ThemeMap *themes; nuclear@11: nuclear@11: nuclear@6: void add_theme_path(const char *path) nuclear@6: { nuclear@6: if(!path || !*path) return; nuclear@6: nuclear@6: std::string s = path; nuclear@6: int last = s.length() - 1; nuclear@6: if(s[last] == '/' || s[last] == '\\') { nuclear@6: s.erase(last); nuclear@6: } nuclear@6: nuclear@6: if(std::find(search_paths.begin(), search_paths.end(), s) != search_paths.end()) { nuclear@6: return; nuclear@6: } nuclear@6: nuclear@6: search_paths.push_back(s); nuclear@6: } nuclear@6: nuclear@11: void register_theme(const char *name, Theme *theme) nuclear@11: { nuclear@11: if(!themes) { nuclear@11: themes = new ThemeMap; nuclear@11: } nuclear@11: nuclear@11: Theme *prev = (*themes)[name]; nuclear@11: if(prev) { nuclear@11: delete prev; nuclear@11: } nuclear@11: (*themes)[name] = theme; nuclear@11: } nuclear@11: nuclear@11: Theme *get_theme(const char *name) nuclear@11: { nuclear@11: // first search in the already registered themes nuclear@11: ThemeMap::const_iterator it = themes->find(name); nuclear@11: if(it != themes->end()) { nuclear@11: return it->second; nuclear@11: } nuclear@11: nuclear@11: // then try loading it from a theme plugin nuclear@11: Theme *theme = new Theme; nuclear@11: if(theme->load(name)) { nuclear@11: return theme; nuclear@11: } nuclear@11: nuclear@11: fprintf(stderr, "[goatkit] theme \"%s\" not found!\n", name); nuclear@11: return 0; nuclear@11: } nuclear@11: nuclear@6: Theme::Theme() nuclear@6: { nuclear@6: impl = new ThemeImpl; nuclear@6: impl->so = 0; nuclear@6: impl->lookup_theme_draw_func = 0; nuclear@6: } nuclear@6: nuclear@11: Theme::Theme(const char *name, WidgetLookupFunc func) nuclear@11: { nuclear@11: impl = new ThemeImpl; nuclear@11: impl->so = 0; nuclear@11: impl->lookup_theme_draw_func = func; nuclear@11: nuclear@11: register_theme(name, this); nuclear@11: } nuclear@11: nuclear@6: Theme::~Theme() nuclear@6: { nuclear@6: unload(); nuclear@6: delete impl; nuclear@6: } nuclear@6: nuclear@6: typedef WidgetDrawFunc (*LookupFunc)(const char*); nuclear@6: nuclear@6: bool Theme::load(const char *name) nuclear@6: { nuclear@6: unload(); nuclear@6: nuclear@11: std::string fname = std::string(name) + ".gtheme"; nuclear@11: if(!(impl->so = dlopen(fname.c_str(), RTLD_LAZY))) { nuclear@11: for(size_t i=0; iso = dlopen(path.c_str(), RTLD_LAZY))) { nuclear@11: break; nuclear@11: } nuclear@11: } nuclear@11: nuclear@11: // try the fallback paths nuclear@11: if(!impl->so) { nuclear@11: for(int i=0; fallback_paths[i]; i++) { nuclear@11: std::string path = std::string(fallback_paths[i]) + "/" + fname; nuclear@6: nuclear@6: if((impl->so = dlopen(path.c_str(), RTLD_LAZY))) { nuclear@6: break; nuclear@6: } nuclear@6: } nuclear@11: } nuclear@6: nuclear@11: if(!impl->so) { nuclear@11: fprintf(stderr, "%s: failed to load theme plugin: %s\n", __func__, name); nuclear@11: return false; nuclear@6: } nuclear@6: } nuclear@6: nuclear@6: // loaded the shared object, now get the lookup function nuclear@6: impl->lookup_theme_draw_func = (LookupFunc)dlsym(impl->so, "get_widget_func"); nuclear@6: if(!impl->lookup_theme_draw_func) { nuclear@6: fprintf(stderr, "%s: invalid theme plugin %s\n", __func__, name); nuclear@6: unload(); nuclear@6: return false; nuclear@6: } nuclear@6: nuclear@11: register_theme(name, this); nuclear@6: return true; nuclear@6: } nuclear@6: nuclear@6: void Theme::unload() nuclear@6: { nuclear@6: if(impl->so) { nuclear@7: if(impl->so != RTLD_DEFAULT) { nuclear@7: dlclose(impl->so); nuclear@7: } nuclear@6: impl->so = 0; nuclear@6: } nuclear@6: impl->func_cache.clear(); nuclear@6: } nuclear@6: nuclear@6: WidgetDrawFunc Theme::get_draw_func(const char *type) const nuclear@6: { nuclear@6: std::map::const_iterator it = impl->func_cache.find(type); nuclear@6: if(it == impl->func_cache.end()) { nuclear@6: // don't have it cached, try to look it up nuclear@6: WidgetDrawFunc func; nuclear@6: if(impl->lookup_theme_draw_func && (func = impl->lookup_theme_draw_func(type))) { nuclear@6: impl->func_cache[type] = func; nuclear@6: return func; nuclear@6: } nuclear@6: nuclear@6: // can't look it up, return the default nuclear@6: return default_draw_func; nuclear@6: } nuclear@6: return it->second; nuclear@6: } nuclear@6: nuclear@6: #define LERP(a, b, t) ((a) + ((b) - (a)) * t) nuclear@6: #define DEF_TEX_SZ 32 nuclear@6: void default_draw_func(const Widget *w) nuclear@6: { nuclear@6: static unsigned int tex; nuclear@6: nuclear@6: if(!tex) { nuclear@6: unsigned char *pixels = new unsigned char[DEF_TEX_SZ * DEF_TEX_SZ * 3]; nuclear@6: unsigned char *ptr = pixels; nuclear@6: for(int i=0; iget_position(); nuclear@6: Vec2 sz = w->get_size(); nuclear@6: float aspect = sz.x / sz.y; nuclear@6: nuclear@6: #if !defined(GL_ES_VERSION_2_0) nuclear@6: glPushAttrib(GL_ENABLE_BIT); nuclear@6: glEnable(GL_TEXTURE_2D); nuclear@6: #endif nuclear@6: glBindTexture(GL_TEXTURE_2D, tex); nuclear@6: nuclear@6: float offs = w->get_pressed() * 0.1 * sz.y; nuclear@6: glMatrixMode(GL_MODELVIEW); nuclear@6: glPushMatrix(); nuclear@6: glTranslatef(offs, -offs, 0); nuclear@6: nuclear@6: float active = w->get_active(); nuclear@6: float hover = w->get_under_mouse(); nuclear@6: nuclear@6: float rg = LERP(0.4, 1.0, hover); nuclear@6: float b = LERP(rg, 0, active); nuclear@6: glColor3f(rg, rg, b); nuclear@6: nuclear@6: glBegin(GL_QUADS); nuclear@6: glTexCoord2f(0, 1); nuclear@6: glVertex2f(pos.x, pos.y); nuclear@6: glTexCoord2f(aspect, 1); nuclear@6: glVertex2f(pos.x + sz.x, pos.y); nuclear@6: glTexCoord2f(aspect, 0); nuclear@6: glVertex2f(pos.x + sz.x, pos.y + sz.y); nuclear@6: glTexCoord2f(0, 0); nuclear@6: glVertex2f(pos.x, pos.y + sz.y); nuclear@6: glEnd(); nuclear@6: nuclear@6: glPopMatrix(); nuclear@6: nuclear@6: #ifndef GL_ES_VERSION_2_0 nuclear@6: glPopAttrib(); nuclear@6: #endif nuclear@6: } nuclear@6: nuclear@6: } // namespace goatkit nuclear@6: nuclear@6: #ifdef WIN32 nuclear@6: // XXX untested nuclear@6: static void *dlopen(const char *name, int flags) nuclear@6: { nuclear@6: return LoadLibrary(name); nuclear@6: } nuclear@6: nuclear@6: static void dlclose(void *so) nuclear@6: { nuclear@6: FreeLibrary(so); nuclear@6: } nuclear@6: nuclear@6: static void *dlsym(void *so, const char *symbol) nuclear@6: { nuclear@7: if(so == RTLD_DEFAULT) { nuclear@7: so = GetModuleHandle(0); nuclear@7: } nuclear@6: return (void*)GetProcAddress(so, symbol); nuclear@6: } nuclear@6: #endif