From 6206d87eb7b24c04c5675404bdb13e9ace86f2fe Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sat, 11 Aug 2018 09:14:55 +0300 Subject: [PATCH] initial commit --- .gitignore | 4 + Makefile | 42 +++++ src/game.cc | 82 +++++++++ src/game.h | 61 +++++++ src/gamescr.cc | 46 +++++ src/goatkit/boolanm.cc | 156 +++++++++++++++++ src/goatkit/boolanm.h | 59 +++++++ src/goatkit/button.cc | 40 +++++ src/goatkit/button.h | 40 +++++ src/goatkit/checkbox.cc | 93 ++++++++++ src/goatkit/checkbox.h | 50 ++++++ src/goatkit/event.cc | 35 ++++ src/goatkit/event.h | 83 +++++++++ src/goatkit/goatkit.h | 31 ++++ src/goatkit/label.cc | 40 +++++ src/goatkit/label.h | 40 +++++ src/goatkit/screen.cc | 318 +++++++++++++++++++++++++++++++++++ src/goatkit/screen.h | 69 ++++++++ src/goatkit/slider.cc | 196 ++++++++++++++++++++++ src/goatkit/slider.h | 62 +++++++ src/goatkit/textbox.cc | 168 +++++++++++++++++++ src/goatkit/textbox.h | 59 +++++++ src/goatkit/theme.cc | 302 +++++++++++++++++++++++++++++++++ src/goatkit/theme.h | 58 +++++++ src/goatkit/vec.h | 33 ++++ src/goatkit/widget.cc | 429 +++++++++++++++++++++++++++++++++++++++++++++++ src/goatkit/widget.h | 130 ++++++++++++++ src/main.cc | 190 +++++++++++++++++++++ src/menuscr.cc | 142 ++++++++++++++++ src/opengl.c | 150 +++++++++++++++++ src/opengl.h | 26 +++ src/screen.cc | 95 +++++++++++ src/screen.h | 65 +++++++ src/uitheme.cc | 411 +++++++++++++++++++++++++++++++++++++++++++++ 34 files changed, 3805 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/game.cc create mode 100644 src/game.h create mode 100644 src/gamescr.cc create mode 100644 src/goatkit/boolanm.cc create mode 100644 src/goatkit/boolanm.h create mode 100644 src/goatkit/button.cc create mode 100644 src/goatkit/button.h create mode 100644 src/goatkit/checkbox.cc create mode 100644 src/goatkit/checkbox.h create mode 100644 src/goatkit/event.cc create mode 100644 src/goatkit/event.h create mode 100644 src/goatkit/goatkit.h create mode 100644 src/goatkit/label.cc create mode 100644 src/goatkit/label.h create mode 100644 src/goatkit/screen.cc create mode 100644 src/goatkit/screen.h create mode 100644 src/goatkit/slider.cc create mode 100644 src/goatkit/slider.h create mode 100644 src/goatkit/textbox.cc create mode 100644 src/goatkit/textbox.h create mode 100644 src/goatkit/theme.cc create mode 100644 src/goatkit/theme.h create mode 100644 src/goatkit/vec.h create mode 100644 src/goatkit/widget.cc create mode 100644 src/goatkit/widget.h create mode 100644 src/main.cc create mode 100644 src/menuscr.cc create mode 100644 src/opengl.c create mode 100644 src/opengl.h create mode 100644 src/screen.cc create mode 100644 src/screen.h create mode 100644 src/uitheme.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad1a4b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.swp +*.d +game diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4189eb2 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +ccsrc = $(wildcard src/*.cc) \ + $(wildcard src/goatkit/*.cc) +csrc = $(wildcard src/*.c) +obj = $(ccsrc:.cc=.o) $(csrc:.c=.o) +dep = $(obj:.o=.d) +bin = game + +warn = -pedantic -Wall +dbg = -g +opt = -O0 +inc = + +CFLAGS = $(warn) $(dbg) $(opt) $(inc) +CXXFLAGS = $(warn) $(dbg) $(opt) $(inc) +LDFLAGS = $(libgl) $(syslibs) + +sys := $(shell uname -s | sed 's/MINGW.*/mingw/') +ifeq ($(sys), mingw) + libgl = -lopengl32 -lglu32 -lfreeglut -lglew32 +else + libgl = -lGL -lGLU -lglut -lGLEW + syslibs = -ldl +endif + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) diff --git a/src/game.cc b/src/game.cc new file mode 100644 index 0000000..cf3f62a --- /dev/null +++ b/src/game.cc @@ -0,0 +1,82 @@ +#include +#include "opengl.h" +#include "game.h" +#include "screen.h" + +int win_width, win_height; +float win_aspect; +long frame_time; +float frame_dt; + +bool game_init() +{ + if(init_opengl() == -1) { + return false; + } + + if(!init_screens()) { + return false; + } + + glEnable(GL_MULTISAMPLE); + glEnable(GL_FRAMEBUFFER_SRGB); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + active_screen->start(); + return true; +} + +void game_cleanup() +{ + cleanup_screens(); +} + +void game_draw() +{ + active_screen->draw(); + + assert(glGetError() == GL_NO_ERROR); +} + +void game_reshape(int x, int y) +{ + active_screen->draw(); +} + +void game_keyboard(int key, bool pressed) +{ + if(pressed) { + switch(key) { + case 'q': + case 'Q': + if(game_modkeys() & MODKEY_CTRL) { + game_quit(); + return; + } + break; + + default: + break; + } + } + + active_screen->keyboard(key, pressed); +} + +void game_mbutton(int bn, bool pressed, int x, int y) +{ + active_screen->mbutton(bn, pressed, x, y); +} + +void game_mmotion(int x, int y) +{ + active_screen->mmotion(x, y); +} + +void game_mwheel(int dir, int x, int y) +{ + active_screen->mwheel(dir, x, y); +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..0d7e791 --- /dev/null +++ b/src/game.h @@ -0,0 +1,61 @@ +#ifndef GAME_H_ +#define GAME_H_ + +enum { + KEY_ESC = 27, + KEY_DEL = 127, + + KEY_F1 = 256, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_HOME, + KEY_END, + KEY_PGUP, + KEY_PGDOWN, + KEY_INSERT +}; + +enum { + MODKEY_SHIFT = 1, + MODKEY_CTRL = 2, + MODKEY_ALT = 4 +}; + + +extern int win_width, win_height; +extern float win_aspect; +extern long frame_time; +extern float frame_dt; + +bool game_init(); +void game_cleanup(); + +void game_draw(); +void game_reshape(int x, int y); + +void game_keyboard(int key, bool pressed); +void game_mbutton(int bn, bool pressed, int x, int y); +void game_mmotion(int x, int y); +void game_mwheel(int dir, int x, int y); + +// defined in main.cc +void game_quit(); +bool game_keystate(int key); +bool game_bnstate(int bn); + +unsigned int game_modkeys(); + +#endif /* GAME_H_ */ diff --git a/src/gamescr.cc b/src/gamescr.cc new file mode 100644 index 0000000..0ae1672 --- /dev/null +++ b/src/gamescr.cc @@ -0,0 +1,46 @@ +#include "game.h" +#include "screen.h" +#include "opengl.h" + +bool GameScreen::init() +{ + return true; +} + +void GameScreen::destroy() +{ +} + + +void GameScreen::draw() +{ + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +void GameScreen::keyboard(int key, bool pressed) +{ + if(pressed) { + switch(key) { + case KEY_ESC: + pop_screen(); + break; + + default: + break; + } + } +} + +void GameScreen::mbutton(int bn, bool pressed, int x, int y) +{ +} + +void GameScreen::mmotion(int x, int y) +{ +} + +void GameScreen::mwheel(int dir, int x, int y) +{ +} diff --git a/src/goatkit/boolanm.cc b/src/goatkit/boolanm.cc new file mode 100644 index 0000000..7ca38a6 --- /dev/null +++ b/src/goatkit/boolanm.cc @@ -0,0 +1,156 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "boolanm.h" + +static long default_get_msec(); + +BoolAnim::BoolAnim(bool st) +{ + set(st); + trans_start = 0; + trans_dur = 500; + get_msec = default_get_msec; +} + +void BoolAnim::update(long tm) const +{ + if(trans_dir == 0.0) return; + + float dt = (tm - trans_start) / 1000.0; + float t = dt / (trans_dur / 1000.0); + + if(trans_dir > 0.0) { + value = t; + } else { + value = 1.0 - t; + } + + if(value < 0.0) { + value = 0.0; + trans_dir = 0.0; + } else if(value > 1.0) { + value = 1.0; + trans_dir = 0.0; + } +} + +void BoolAnim::set_transition_duration(long dur) +{ + trans_dur = dur; +} + +long BoolAnim::get_transition_duration() const +{ + return trans_dur; +} + +void BoolAnim::set_time_callback(long (*time_func)()) +{ + get_msec = time_func; +} + +void BoolAnim::set(bool st) +{ + value = st ? 1.0 : 0.0; + trans_dir = 0.0; +} + +void BoolAnim::change(bool st) +{ + change(st, get_msec()); +} + +void BoolAnim::change(bool st, long tm) +{ + trans_dir = st ? 1.0 : -1.0; + trans_start = tm; +} + +bool BoolAnim::get_state() const +{ + return get_state(get_msec()); +} + +bool BoolAnim::get_state(long tm) const +{ + update(tm); + + // if we're not in transition use the value (should be 0 or 1) + if(trans_dir == 0.0) { + return value > 0.5; + } + + // if we're in transition base it on the direction of the transition + return trans_dir > 0.0; +} + +float BoolAnim::get_value() const +{ + return get_value(get_msec()); +} + +float BoolAnim::get_value(long tm) const +{ + update(tm); + return value; +} + +float BoolAnim::get_dir() const +{ + return get_dir(get_msec()); +} + +float BoolAnim::get_dir(long tm) const +{ + update(tm); + return trans_dir; +} + +BoolAnim::operator bool() const +{ + return get_state(); +} + +BoolAnim::operator float() const +{ + return get_value(); +} + +#ifdef WIN32 +#include + +static long default_get_msec() +{ + return GetTickCount(); +} +#else +#include + +static long default_get_msec() +{ + static struct timeval tv0; + struct timeval tv; + + gettimeofday(&tv, 0); + if(tv0.tv_sec == 0 && tv0.tv_usec == 0) { + tv0 = tv; + return 0; + } + return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; +} +#endif diff --git a/src/goatkit/boolanm.h b/src/goatkit/boolanm.h new file mode 100644 index 0000000..52233c1 --- /dev/null +++ b/src/goatkit/boolanm.h @@ -0,0 +1,59 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef BOOLANIM_H_ +#define BOOLANIM_H_ + +class BoolAnim { +private: + mutable float value; + mutable float trans_dir; // transition direction (sign) + long trans_start; // transition start time + long trans_dur; + + long (*get_msec)(); + + void update(long tm) const; + +public: + BoolAnim(bool st = false); + + void set_transition_duration(long dur); + long get_transition_duration() const; + + void set_time_callback(long (*time_func)()); + + void set(bool st); + + void change(bool st); + void change(bool st, long trans_start); + + bool get_state() const; + bool get_state(long tm) const; + + float get_value() const; + float get_value(long tm) const; + + // transition direction (-1, 0, 1) + float get_dir() const; + float get_dir(long tm) const; + + operator bool() const; // equivalent to get_state + operator float() const; // equivalent to get_value +}; + +#endif // BOOLANIM_H_ diff --git a/src/goatkit/button.cc b/src/goatkit/button.cc new file mode 100644 index 0000000..aa7f1d0 --- /dev/null +++ b/src/goatkit/button.cc @@ -0,0 +1,40 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "button.h" + +namespace goatkit { + +struct ButtonImpl { +}; + +Button::Button() +{ + button = new ButtonImpl; +} + +Button::~Button() +{ + delete button; +} + +const char *Button::get_type_name() const +{ + return "button"; +} + +} // namespace goatkit diff --git a/src/goatkit/button.h b/src/goatkit/button.h new file mode 100644 index 0000000..a0339af --- /dev/null +++ b/src/goatkit/button.h @@ -0,0 +1,40 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_BUTTON_H_ +#define GOATKIT_BUTTON_H_ + +#include "widget.h" + +namespace goatkit { + +struct ButtonImpl; + +class Button : public Widget { +private: + ButtonImpl *button; + +public: + Button(); + virtual ~Button(); + + virtual const char *get_type_name() const; +}; + +} // namespace goatkit + +#endif // GOATKIT_BUTTON_H_ diff --git a/src/goatkit/checkbox.cc b/src/goatkit/checkbox.cc new file mode 100644 index 0000000..3492fa6 --- /dev/null +++ b/src/goatkit/checkbox.cc @@ -0,0 +1,93 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "checkbox.h" +#include "boolanm.h" + +namespace goatkit { + +struct CheckBoxImpl { + BoolAnim checked; +}; + +CheckBox::CheckBox() +{ + cbox = new CheckBoxImpl; + cbox->checked = false; + cbox->checked.set_transition_duration(60); +} + +CheckBox::~CheckBox() +{ + delete cbox; +} + +const char *CheckBox::get_type_name() const +{ + return "checkbox"; +} + + +void CheckBox::check() +{ + cbox->checked.change(true); +} + +void CheckBox::uncheck() +{ + cbox->checked.change(false); +} + +float CheckBox::get_checked() const +{ + return cbox->checked.get_value(); +} + +bool CheckBox::is_checked() const +{ + return cbox->checked.get_state(); +} + +void CheckBox::toggle() +{ + if(is_checked()) { + uncheck(); + } else { + check(); + } +} + +void CheckBox::set_toggle_transition(long msec) +{ + cbox->checked.set_transition_duration(msec); +} + +long CheckBox::get_toggle_transition() const +{ + return cbox->checked.get_transition_duration(); +} + +void CheckBox::on_click() +{ + toggle(); + + Event ev; + ev.type = EV_CHANGE; + handle_event(ev); +} + +} // namespace goatkit diff --git a/src/goatkit/checkbox.h b/src/goatkit/checkbox.h new file mode 100644 index 0000000..66e3735 --- /dev/null +++ b/src/goatkit/checkbox.h @@ -0,0 +1,50 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_CHECKBOX_H_ +#define GOATKIT_CHECKBOX_H_ + +#include "widget.h" + +namespace goatkit { + +struct CheckBoxImpl; + +class CheckBox : public Widget { +private: + CheckBoxImpl *cbox; + +public: + CheckBox(); + virtual ~CheckBox(); + + virtual const char *get_type_name() const; + + virtual void check(); + virtual void uncheck(); + virtual float get_checked() const; + virtual bool is_checked() const; + virtual void toggle(); + virtual void set_toggle_transition(long msec); + virtual long get_toggle_transition() const; + + virtual void on_click(); +}; + +} // namespace goatkit + +#endif // GOATKIT_CHECKBOX_H_ diff --git a/src/goatkit/event.cc b/src/goatkit/event.cc new file mode 100644 index 0000000..c7a59cc --- /dev/null +++ b/src/goatkit/event.cc @@ -0,0 +1,35 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "event.h" + + +const char *goatkit::event_type_name(EventType type) +{ + // XXX keep this array synchronized with enum EventType + static const char *names[NUM_EVENTS] = { + "mouse button", + "mouse motion", + "focus", + "keyboard", + "click", + "double click", + "change" + }; + + return names[type]; +} diff --git a/src/goatkit/event.h b/src/goatkit/event.h new file mode 100644 index 0000000..9dc0a07 --- /dev/null +++ b/src/goatkit/event.h @@ -0,0 +1,83 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_EVENT_H_ +#define GOATKIT_EVENT_H_ + +#include "vec.h" + +namespace goatkit { + +enum EventType { + // primary events + EV_MOUSE_BUTTON, + EV_MOUSE_MOTION, + EV_MOUSE_FOCUS, + EV_KEY, + + // derived events + EV_CLICK, + EV_DOUBLE_CLICK, + EV_CHANGE, + + NUM_EVENTS +}; + +enum SpecialKeys { + KEY_ESCAPE = 27, + KEY_DELETE = 127, + + KEY_F1 = 256, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, + KEY_HOME, KEY_END, KEY_PGUP, KEY_PGDOWN, + KEY_INSERT +}; + +struct ButtonEvent { + Vec2 pos; + int button; + bool press; +}; + +struct MotionEvent { + Vec2 pos; +}; + +struct FocusEvent { + bool enter; +}; + +struct KeyEvent { + int key; + bool press; +}; + +struct Event { + EventType type; + + ButtonEvent button; + MotionEvent motion; + FocusEvent focus; + KeyEvent key; +}; + +const char *event_type_name(EventType type); + +} // namespace goatkit + +#endif // GOATKIT_EVENT_H_ diff --git a/src/goatkit/goatkit.h b/src/goatkit/goatkit.h new file mode 100644 index 0000000..ce7f5ce --- /dev/null +++ b/src/goatkit/goatkit.h @@ -0,0 +1,31 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_H_ +#define GOATKIT_H_ + +#include "screen.h" +#include "widget.h" +#include "button.h" +#include "checkbox.h" +#include "label.h" +#include "textbox.h" +#include "slider.h" +#include "event.h" +#include "theme.h" + +#endif // GOATKIT_H_ diff --git a/src/goatkit/label.cc b/src/goatkit/label.cc new file mode 100644 index 0000000..31c5623 --- /dev/null +++ b/src/goatkit/label.cc @@ -0,0 +1,40 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "label.h" + +namespace goatkit { + +struct LabelImpl { +}; + +Label::Label() +{ + label = new LabelImpl; +} + +Label::~Label() +{ + delete label; +} + +const char *Label::get_type_name() const +{ + return "label"; +} + +} // namespace goatkit diff --git a/src/goatkit/label.h b/src/goatkit/label.h new file mode 100644 index 0000000..8b7ee1f --- /dev/null +++ b/src/goatkit/label.h @@ -0,0 +1,40 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_LABEL_H_ +#define GOATKIT_LABEL_H_ + +#include "widget.h" + +namespace goatkit { + +struct LabelImpl; + +class Label : public Widget { +private: + LabelImpl *label; + +public: + Label(); + virtual ~Label(); + + virtual const char *get_type_name() const; +}; + +} // namespace goatkit + +#endif // GOATKIT_LABEL_H_ diff --git a/src/goatkit/screen.cc b/src/goatkit/screen.cc new file mode 100644 index 0000000..65e734d --- /dev/null +++ b/src/goatkit/screen.cc @@ -0,0 +1,318 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "screen.h" +#include "widget.h" +#include "boolanm.h" + +#define MAX_BUTTONS 16 + +namespace goatkit { + +struct ScreenImpl { + BoolAnim visible; + std::vector widgets; + BBox box; + + Widget *inp_focused, *over, *pressed[MAX_BUTTONS]; + Widget *mgrab; +}; + +static Vec2 world_to_scr(const ScreenImpl *scr, const Vec2 &v); +//static Vec2 scr_to_world(const ScreenImpl *scr, const Vec2 &v) + +Screen::Screen() +{ + scr = new ScreenImpl; + + scr->box.bmin = Vec2(0, 0); + scr->box.bmax = Vec2(1, 1); + + scr->inp_focused = scr->over = 0; + for(int i=0; ipressed[i] = 0; + } + + scr->visible = true; + scr->mgrab = 0; +} + +Screen::~Screen() +{ + delete scr; +} + +void Screen::set_position(float x, float y) +{ + set_position(Vec2(x, y)); +} + +void Screen::set_position(const Vec2 &pos) +{ + Vec2 sz = get_size(); + + scr->box.bmin = pos; + scr->box.bmax.x = pos.x + sz.x; + scr->box.bmax.y = pos.y + sz.y; +} + +const Vec2 &Screen::get_position() const +{ + return scr->box.bmin; +} + +void Screen::set_size(float x, float y) +{ + set_size(Vec2(x, y)); +} + +void Screen::set_size(const Vec2 &sz) +{ + scr->box.bmax.x = scr->box.bmin.x + sz.x; + scr->box.bmax.y = scr->box.bmin.y + sz.y; +} + +const Vec2 Screen::get_size() const +{ + return Vec2(scr->box.bmax.x - scr->box.bmin.x, + scr->box.bmax.y - scr->box.bmin.y); +} + +const BBox &Screen::get_box() const +{ + return scr->box; +} + +void Screen::add_widget(Widget *w) +{ + scr->widgets.push_back(w); + if(scr->visible) { + w->show(); + } else { + w->hide(); + } + + w->set_screen(this); +} + +int Screen::get_widget_count() const +{ + return (int)scr->widgets.size(); +} + +Widget *Screen::get_widget(int idx) const +{ + if(idx < 0 || idx >= (int)scr->widgets.size()) { + return 0; + } + return scr->widgets[idx]; +} + +Widget *Screen::get_widget(const char *name) const +{ + for(size_t i=0; iwidgets.size(); i++) { + if(strcmp(scr->widgets[i]->get_name(), name) == 0) { + return scr->widgets[i]; + } + } + return 0; +} + +void Screen::show() +{ + scr->visible.change(true); + + for(size_t i=0; iwidgets.size(); i++) { + scr->widgets[i]->show(); + } +} + +void Screen::hide() +{ + scr->visible.change(false); + + for(size_t i=0; iwidgets.size(); i++) { + scr->widgets[i]->hide(); + } +} + +bool Screen::is_visible() const +{ + return scr->visible; +} + +float Screen::get_visibility() const +{ + return scr->visible.get_value(); +} + +void Screen::set_visibility_transition(long msec) +{ + scr->visible.set_transition_duration(msec); + for(size_t i=0; iwidgets.size(); i++) { + scr->widgets[i]->set_visibility_transition(msec); + } +} + +long Screen::get_visibility_transition() const +{ + return scr->visible.get_transition_duration(); +} + +bool Screen::grab_mouse(Widget *w) +{ + if(!scr->mgrab || !w) { + scr->mgrab = w; + return true; + } + return false; +} + +void Screen::draw() const +{ + for(size_t i=0; iwidgets.size(); i++) { + scr->widgets[i]->draw(); + } +} + +static Widget *find_widget_at(const ScreenImpl *scr, const Vec2 &pt) +{ + for(size_t i=0; iwidgets.size(); i++) { + Widget *w = scr->widgets[i]; + + if(w->hit_test(pt)) { + return w; + } + } + return 0; +} + +void Screen::sysev_keyboard(int key, bool press) +{ + Event ev; + + if(scr->inp_focused) { + ev.type = EV_KEY; + ev.key.key = key; + ev.key.press = press; + scr->inp_focused->handle_event(ev); + } +} + +void Screen::sysev_mouse_button(int bn, bool press, float x, float y) +{ + Event ev; + Vec2 pt = world_to_scr(scr, Vec2(x, y)); + Widget *new_over = scr->mgrab ? scr->mgrab : find_widget_at(scr, pt); + + ev.type = EV_MOUSE_BUTTON; + ev.button.button = bn; + ev.button.pos = pt; + ev.button.press = press; + + if(press) { + if(bn == 0) { + // left click gives input focus + // TODO: add input focus event in widget + if(new_over && new_over != scr->inp_focused && new_over->can_focus()) { + printf("input focus %p -> %p\n", (void*)scr->inp_focused, (void*)new_over); + new_over->focusin(); + + if(scr->inp_focused) { + scr->inp_focused->focusout(); + } + scr->inp_focused = new_over; + } + } + + scr->pressed[bn] = new_over; + scr->over = new_over; + + if(new_over) { + new_over->handle_event(ev); + } + + } else { + // send the mouse release event to the widget that got the matching press + if(scr->pressed[bn]) { + scr->pressed[bn]->handle_event(ev); + scr->pressed[bn] = 0; + } + } + + // if we're not over the same widget any more send the leave/enter events + // TODO also add drag/drop events + if(scr->over != new_over) { + ev.type = EV_MOUSE_FOCUS; + if(scr->over) { + ev.focus.enter = false; + scr->over->handle_event(ev); + } + if(new_over) { + ev.focus.enter = true; + new_over->handle_event(ev); + } + scr->over = new_over; + } +} + +void Screen::sysev_mouse_motion(float x, float y) +{ + Event ev; + Vec2 pt = world_to_scr(scr, Vec2(x, y)); + Widget *new_over = scr->mgrab ? scr->mgrab : find_widget_at(scr, pt); + + // if we're not over the same widget any more send the leave/enter events + if(scr->over != new_over) { + ev.type = EV_MOUSE_FOCUS; + if(scr->over) { + ev.focus.enter = false; + scr->over->handle_event(ev); + } + if(new_over) { + ev.focus.enter = true; + new_over->handle_event(ev); + } + scr->over = new_over; + } + + if(new_over) { + // send motion event + ev.type = EV_MOUSE_MOTION; + ev.motion.pos = pt; + new_over->handle_event(ev); + } +} + +static Vec2 world_to_scr(const ScreenImpl *scr, const Vec2 &v) +{ + return Vec2(v.x - scr->box.bmin.x, v.y - scr->box.bmin.y); +} + +/* +static Vec2 scr_to_world(const ScreenImpl *scr, const Vec2 &v) +{ + return Vec2(v.x + scr->box.bmin.x, v.y + scr->box.bmin.y); +} +*/ + + +} // namespace goatkit diff --git a/src/goatkit/screen.h b/src/goatkit/screen.h new file mode 100644 index 0000000..d587bae --- /dev/null +++ b/src/goatkit/screen.h @@ -0,0 +1,69 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_SCREEN_H_ +#define GOATKIT_SCREEN_H_ + +#include "vec.h" +#include "widget.h" + +namespace goatkit { + +class ScreenImpl; + +class Screen { +private: + ScreenImpl *scr; + +public: + Screen(); + ~Screen(); + + void set_position(float x, float y); + void set_position(const Vec2 &pos); + const Vec2 &get_position() const; + void set_size(float x, float y); + void set_size(const Vec2 &sz); + const Vec2 get_size() const; + const BBox &get_box() const; + + void add_widget(Widget *w); + int get_widget_count() const; + Widget *get_widget(int idx) const; + Widget *get_widget(const char *name) const; + + void show(); + void hide(); + bool is_visible() const; + float get_visibility() const; + void set_visibility_transition(long msec); + long get_visibility_transition() const; + + bool grab_mouse(Widget *w); + + void draw() const; + + // window system events used to generate widget events (see event.h) + void sysev_keyboard(int key, bool press); + // mouse position as reported by the window system. might fall outside + void sysev_mouse_button(int bn, bool press, float x, float y); + void sysev_mouse_motion(float x, float y); +}; + +} // namespace goatkit + +#endif /* GOATKIT_SCREEN_H_ */ diff --git a/src/goatkit/slider.cc b/src/goatkit/slider.cc new file mode 100644 index 0000000..6f03571 --- /dev/null +++ b/src/goatkit/slider.cc @@ -0,0 +1,196 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include "slider.h" +#include "screen.h" + +namespace goatkit { + +struct SliderImpl { + float value, prev_value; + float range_min, range_max; + float padding; + float step; + bool dragging; + bool cont_change; +}; + +static float remap(float val, float inlow, float inhigh, float outlow, float outhigh); + +Slider::Slider() +{ + slider = new SliderImpl; + slider->value = slider->prev_value = 0.0f; + slider->dragging = false; + slider->cont_change = true; + + slider->range_min = 0.0; + slider->range_max = 1.0; + slider->step = 0.0; + + slider->padding = -1.0; +} + +Slider::~Slider() +{ + delete slider; +} + +const char *Slider::get_type_name() const +{ + return "slider"; +} + +void Slider::set_value(float val) +{ + slider->value = remap(val, slider->range_min, slider->range_max, 0, 1); +} + +float Slider::get_value() const +{ + return remap(slider->value, 0, 1, slider->range_min, slider->range_max); +} + +void Slider::set_value_norm(float val) +{ + slider->value = val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val); +} + +float Slider::get_value_norm() const +{ + return slider->value; +} + +void Slider::set_padding(float pad) +{ + slider->padding = pad; +} + +float Slider::get_padding() const +{ + if(slider->padding < 0.0) { + BBox box = get_box(); + return (box.bmax.y - box.bmin.y) * 0.25; + } + return slider->padding; +} + +void Slider::set_continuous_change(bool cont) +{ + slider->cont_change = cont; +} + +bool Slider::get_continuous_change() const +{ + return slider->cont_change; +} + +void Slider::set_range(float min, float max) +{ + slider->range_min = min; + slider->range_max = max; +} + +float Slider::get_range_min() const +{ + return slider->range_min; +} + +float Slider::get_range_max() const +{ + return slider->range_max; +} + +void Slider::set_step(float step) +{ + slider->step = step; +} + +float Slider::get_step() const +{ + return slider->step; +} + +void Slider::on_mouse_button(const ButtonEvent &ev) +{ + if(ev.button == 0) { + Screen *scr = get_screen(); + + slider->dragging = ev.press; + if(ev.press) { + if(scr) scr->grab_mouse(this); + } else { + if(scr) scr->grab_mouse(0); + + // on release, if the value has changed send the appropriate event + if(slider->prev_value != slider->value) { + Event ev; + ev.type = EV_CHANGE; + handle_event(ev); + slider->prev_value = slider->value; + } + } + } +} + +#define ROUND(x) floor((x) + 0.5) + +void Slider::on_mouse_motion(const MotionEvent &ev) +{ + if(!slider->dragging) { + return; + } + + BBox box = get_box(); + + float padding = get_padding(); + float start = box.bmin.x + padding; + float end = box.bmax.x - padding; + float new_val = (ev.pos.x - start) / (end - start); + + // if we have a non-zero step, snap to the nearest value + if(slider->step > 0.0) { + float range = slider->range_max - slider->range_min; + float st = slider->step / range; + + new_val = ROUND(new_val / st) * st; + } + + if(new_val < 0.0) new_val = 0.0; + if(new_val > 1.0) new_val = 1.0; + + if(new_val != slider->value) { + slider->value = new_val; + if(slider->cont_change) { + Event cev; + cev.type = EV_CHANGE; + handle_event(cev); + } + } +} + +static float remap(float val, float inlow, float inhigh, float outlow, float outhigh) +{ + float t = (val - inlow) / (inhigh - inlow); + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + return t * (outhigh - outlow) + outlow; +} + +} // namespace goatkit diff --git a/src/goatkit/slider.h b/src/goatkit/slider.h new file mode 100644 index 0000000..7b5899f --- /dev/null +++ b/src/goatkit/slider.h @@ -0,0 +1,62 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_SLIDER_H_ +#define GOATKIT_SLIDER_H_ + +#include "widget.h" + +namespace goatkit { + +struct SliderImpl; + +class Slider : public Widget { +private: + SliderImpl *slider; + +public: + Slider(); + virtual ~Slider(); + + virtual const char *get_type_name() const; + + virtual void set_value(float val); + virtual float get_value() const; + + virtual void set_value_norm(float val); + virtual float get_value_norm() const; + + virtual void set_padding(float pad); + virtual float get_padding() const; + + virtual void set_continuous_change(bool cont); + virtual bool get_continuous_change() const; + + virtual void set_range(float min, float max); + virtual float get_range_min() const; + virtual float get_range_max() const; + + virtual void set_step(float step); + virtual float get_step() const; + + virtual void on_mouse_button(const ButtonEvent &ev); + virtual void on_mouse_motion(const MotionEvent &ev); +}; + +} // namespace goatkit + +#endif // GOATKIT_SLIDER_H_ diff --git a/src/goatkit/textbox.cc b/src/goatkit/textbox.cc new file mode 100644 index 0000000..60f82ed --- /dev/null +++ b/src/goatkit/textbox.cc @@ -0,0 +1,168 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include "textbox.h" + +namespace goatkit { + +struct TextBoxImpl { + std::string text; + int cursor; +}; + +TextBox::TextBox() +{ + tbox = new TextBoxImpl; + tbox->cursor = 0; +} + +TextBox::~TextBox() +{ + delete tbox; +} + +const char *TextBox::get_type_name() const +{ + return "textbox"; +} + +bool TextBox::can_focus() const +{ + return true; +} + +void TextBox::clear() +{ + tbox->text.clear(); +} + +void TextBox::set_text(const char *t) +{ + tbox->text = std::string(t); +} + +const char *TextBox::get_text() const +{ + return tbox->text.c_str(); +} + +int TextBox::set_cursor(int idx) +{ + int len = tbox->text.size(); + + if(idx < 0) { + tbox->cursor = 0; + } else if(idx > len) { + tbox->cursor = len; + } else { + tbox->cursor = idx; + } + return tbox->cursor; +} + +int TextBox::get_cursor() const +{ + return tbox->cursor; +} + +int TextBox::cursor_begin() +{ + return tbox->cursor = 0; +} + +int TextBox::cursor_end() +{ + return set_cursor(INT_MAX); +} + +int TextBox::cursor_prev() +{ + return set_cursor(tbox->cursor - 1); +} + +int TextBox::cursor_next() +{ + return set_cursor(tbox->cursor + 1); +} + +void TextBox::insert(char c) +{ + int len = tbox->text.size(); + if(tbox->cursor >= len) { + tbox->text.push_back(c); + tbox->cursor++; + } else { + tbox->text.insert(tbox->cursor++, 1, c); + } +} + +void TextBox::on_key(const KeyEvent &ev) +{ + if(!ev.press) return; // ignore key release events + + switch(ev.key) { + case KEY_LEFT: + cursor_prev(); + break; + + case KEY_RIGHT: + cursor_next(); + break; + + case KEY_HOME: + cursor_begin(); + break; + + case KEY_END: + cursor_end(); + break; + + case KEY_DELETE: + tbox->text.erase(tbox->cursor, 1); + break; + + case '\b': + if(tbox->cursor > 0) { + tbox->text.erase(--tbox->cursor, 1); + } + break; + + case '\n': + case '\t': + { + Event ev; + ev.type = EV_CHANGE; + handle_event(ev); + } + break; + + default: + if(isprint(ev.key)) { + insert(ev.key); + } + } +} + +void TextBox::on_click() +{ + // TODO place cursor +} + +} // namespace goatkit diff --git a/src/goatkit/textbox.h b/src/goatkit/textbox.h new file mode 100644 index 0000000..59ea8e0 --- /dev/null +++ b/src/goatkit/textbox.h @@ -0,0 +1,59 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_TEXTBOX_H_ +#define GOATKIT_TEXTBOX_H_ + +#include "widget.h" + +namespace goatkit { + +struct TextBoxImpl; + +class TextBox : public Widget { +private: + TextBoxImpl *tbox; + +public: + TextBox(); + virtual ~TextBox(); + + virtual const char *get_type_name() const; + virtual bool can_focus() const; + + virtual void clear(); + + virtual void set_text(const char *t); + virtual const char *get_text() const; + + virtual int set_cursor(int idx); + virtual int get_cursor() const; + + virtual int cursor_begin(); + virtual int cursor_end(); + virtual int cursor_prev(); + virtual int cursor_next(); + + virtual void insert(char c); + + virtual void on_key(const KeyEvent &ev); + virtual void on_click(); +}; + +} // namespace goatkit + +#endif // GOATKIT_TEXTBOX_H_ diff --git a/src/goatkit/theme.cc b/src/goatkit/theme.cc new file mode 100644 index 0000000..8f46615 --- /dev/null +++ b/src/goatkit/theme.cc @@ -0,0 +1,302 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include +#include "theme.h" +#include "widget.h" + +#ifdef WIN32 +#include + +static void *dlopen(const char *name, int flags); +static void dlclose(void *so); +static void *dlsym(void *so, const char *symbol); +#else +#include +#include +#endif + +#ifdef HAVE_OPENGL_H +#include "opengl.h" + +#else + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#endif /* HAVE_OPENGL_H_ */ + +#ifndef PREFIX +#define PREFIX "/usr/local" +#endif + +namespace goatkit { + +struct ThemeImpl { + void *so; + WidgetDrawFunc (*lookup_theme_draw_func)(const char*); + mutable std::map func_cache; +}; + +Theme *theme; +static std::vector search_paths; +static const char *fallback_paths[] = { + PREFIX "/share/goatkit", + 0 +}; + +typedef std::map ThemeMap; +static ThemeMap *themes; + + +void add_theme_path(const char *path) +{ + if(!path || !*path) return; + + std::string s = path; + int last = s.length() - 1; + if(s[last] == '/' || s[last] == '\\') { + s.erase(last); + } + + if(std::find(search_paths.begin(), search_paths.end(), s) != search_paths.end()) { + return; + } + + search_paths.push_back(s); +} + +void register_theme(const char *name, Theme *theme) +{ + if(!themes) { + themes = new ThemeMap; + } + + Theme *prev = (*themes)[name]; + if(prev) { + delete prev; + } + (*themes)[name] = theme; +} + +Theme *get_theme(const char *name) +{ + // first search in the already registered themes + ThemeMap::const_iterator it = themes->find(name); + if(it != themes->end()) { + return it->second; + } + + // then try loading it from a theme plugin + Theme *theme = new Theme; + if(theme->load(name)) { + return theme; + } + + fprintf(stderr, "[goatkit] theme \"%s\" not found!\n", name); + delete theme; + return 0; +} + +Theme::Theme() +{ + impl = new ThemeImpl; + impl->so = 0; + impl->lookup_theme_draw_func = 0; +} + +Theme::Theme(const char *name, WidgetLookupFunc func) +{ + impl = new ThemeImpl; + impl->so = 0; + impl->lookup_theme_draw_func = func; + + register_theme(name, this); +} + +Theme::~Theme() +{ + unload(); + delete impl; +} + +typedef WidgetDrawFunc (*LookupFunc)(const char*); + +bool Theme::load(const char *name) +{ + unload(); + + std::string fname = std::string(name) + ".gtheme"; + if(!(impl->so = dlopen(fname.c_str(), RTLD_LAZY))) { + for(size_t i=0; iso = dlopen(path.c_str(), RTLD_LAZY))) { + break; + } + } + + // try the fallback paths + if(!impl->so) { + for(int i=0; fallback_paths[i]; i++) { + std::string path = std::string(fallback_paths[i]) + "/" + fname; + + if((impl->so = dlopen(path.c_str(), RTLD_LAZY))) { + break; + } + } + } + + if(!impl->so) { + fprintf(stderr, "%s: failed to load theme plugin: %s\n", __func__, name); + return false; + } + } + + // loaded the shared object, now get the lookup function + impl->lookup_theme_draw_func = (LookupFunc)dlsym(impl->so, "get_widget_func"); + if(!impl->lookup_theme_draw_func) { + fprintf(stderr, "%s: invalid theme plugin %s\n", __func__, name); + unload(); + return false; + } + + register_theme(name, this); + return true; +} + +void Theme::unload() +{ + if(impl->so) { + dlclose(impl->so); + impl->so = 0; + } + impl->func_cache.clear(); +} + +WidgetDrawFunc Theme::get_draw_func(const char *type) const +{ + std::map::const_iterator it = impl->func_cache.find(type); + if(it == impl->func_cache.end()) { + // don't have it cached, try to look it up + WidgetDrawFunc func; + if(impl->lookup_theme_draw_func && (func = impl->lookup_theme_draw_func(type))) { + impl->func_cache[type] = func; + return func; + } + + // can't look it up, return the default + return default_draw_func; + } + return it->second; +} + +#define LERP(a, b, t) ((a) + ((b) - (a)) * t) +#define DEF_TEX_SZ 32 +void default_draw_func(const Widget *w) +{ + static unsigned int tex; + + if(!tex) { + unsigned char *pixels = new unsigned char[DEF_TEX_SZ * DEF_TEX_SZ * 3]; + unsigned char *ptr = pixels; + for(int i=0; iget_position(); + Vec2 sz = w->get_size(); + float aspect = sz.x / sz.y; + +#if !defined(GL_ES_VERSION_2_0) + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, tex); + + float offs = w->get_pressed() * 0.1 * sz.y; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(offs, -offs, 0); + + float active = w->get_active(); + float hover = w->get_under_mouse(); + + float rg = LERP(0.4, 1.0, hover); + float b = LERP(rg, 0, active); + glColor3f(rg, rg, b); + + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex2f(pos.x, pos.y); + glTexCoord2f(aspect, 1); + glVertex2f(pos.x + sz.x, pos.y); + glTexCoord2f(aspect, 0); + glVertex2f(pos.x + sz.x, pos.y + sz.y); + glTexCoord2f(0, 0); + glVertex2f(pos.x, pos.y + sz.y); + glEnd(); + + glPopMatrix(); + +#ifndef GL_ES_VERSION_2_0 + glPopAttrib(); +#endif +} + +} // namespace goatkit + +#ifdef WIN32 +// XXX untested +static void *dlopen(const char *name, int flags) +{ + return LoadLibrary(name); +} + +static void dlclose(void *so) +{ + FreeLibrary(so); +} + +static void *dlsym(void *so, const char *symbol) +{ + if(!so) { + so = GetModuleHandle(0); + } + return (void*)GetProcAddress(so, symbol); +} +#endif diff --git a/src/goatkit/theme.h b/src/goatkit/theme.h new file mode 100644 index 0000000..7845230 --- /dev/null +++ b/src/goatkit/theme.h @@ -0,0 +1,58 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_THEME_H_ +#define GOATKIT_THEME_H_ + +#define GOATKIT_BUILTIN_THEME(n, f) \ + static goatkit::Theme goatkit_theme##__LINE__(n, f) + +namespace goatkit { + +class Widget; +class Theme; +struct ThemeImpl; + +typedef void (*WidgetDrawFunc)(const Widget*); +typedef WidgetDrawFunc (*WidgetLookupFunc)(const char*); + +void add_theme_path(const char *path); +void default_draw_func(const Widget *w); + +void register_theme(const char *name, Theme *theme); +Theme *get_theme(const char *name); + +class Theme { +private: + ThemeImpl *impl; + +public: + Theme(); + Theme(const char *name, WidgetLookupFunc func); + ~Theme(); + + bool load(const char *name); + void unload(); + + WidgetDrawFunc get_draw_func(const char *type) const; +}; + +extern Theme *theme; // the current theme + +} // namespace goatkit + +#endif // GOATKIT_THEME_H_ diff --git a/src/goatkit/vec.h b/src/goatkit/vec.h new file mode 100644 index 0000000..7819a4b --- /dev/null +++ b/src/goatkit/vec.h @@ -0,0 +1,33 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_VEC_H_ +#define GOATKIT_VEC_H_ + +namespace goatkit { + +class Vec2 { +public: + float x, y; + + Vec2() : x(0), y(0) {} + Vec2(float xx, float yy) : x(xx), y(yy) {} +}; + +} // namespace goatkit + +#endif // GOATKIT_VEC_H_ diff --git a/src/goatkit/widget.cc b/src/goatkit/widget.cc new file mode 100644 index 0000000..aeee9e0 --- /dev/null +++ b/src/goatkit/widget.cc @@ -0,0 +1,429 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include "widget.h" +#include "boolanm.h" +#include "theme.h" +#include "screen.h" + +namespace goatkit { + +struct WidgetImpl { + Screen *scr; + std::string name, text; + BBox box; + + BoolAnim visible, active, press, hover, focus; + + struct { + EventCallback func; + void *cls; + } cb[NUM_EVENTS]; +}; + + +Widget::Widget() +{ + static int widget_count; + + widget = new WidgetImpl; + widget->scr = 0; + + std::stringstream sstr; + sstr << get_type_name() << widget_count++; + widget->name = sstr.str(); + + widget->box.bmin = Vec2(0, 0); + widget->box.bmax = Vec2(1, 1); + + widget->visible.set(true); + widget->active.set(true); + + widget->hover.set_transition_duration(250); + widget->press.set_transition_duration(50); + + memset(widget->cb, 0, sizeof widget->cb); +} + +Widget::~Widget() +{ + delete widget; +} + +void Widget::set_screen(Screen *scr) +{ + widget->scr = scr; +} + +Screen *Widget::get_screen() const +{ + return widget->scr; +} + +const char *Widget::get_type_name() const +{ + return "widget"; +} + +void Widget::set_name(const char *name) +{ + widget->name = std::string(name); +} + +const char *Widget::get_name() const +{ + return widget->name.c_str(); +} + +void Widget::set_text(const char *text) +{ + widget->text = std::string(text); +} + +const char *Widget::get_text() const +{ + return widget->text.c_str(); +} + +void Widget::show() +{ + widget->visible.change(true); +} + +void Widget::hide() +{ + widget->visible.change(false); +} + +float Widget::get_visibility() const +{ + return widget->visible.get_value(); +} + +bool Widget::is_visible() const +{ + return widget->visible.get_state(); +} + +void Widget::set_visibility_transition(long msec) +{ + widget->visible.set_transition_duration(msec); +} + +long Widget::get_visibility_transition() const +{ + return widget->visible.get_transition_duration(); +} + +void Widget::activate() +{ + widget->active.change(true); +} + +void Widget::deactivate() +{ + widget->active.change(false); +} + +float Widget::get_active() const +{ + return widget->active.get_value(); +} + +bool Widget::is_active() const +{ + return widget->active.get_state(); +} + +void Widget::set_active_transition(long msec) +{ + widget->active.set_transition_duration(msec); +} + +long Widget::get_active_transition() const +{ + return widget->active.get_transition_duration(); +} + +void Widget::press() +{ + widget->press.change(true); +} + +void Widget::release() +{ + widget->press.change(false); +} + +float Widget::get_pressed() const +{ + return widget->press.get_value(); +} + +bool Widget::is_pressed() const +{ + return widget->press.get_state(); +} + +void Widget::set_press_transition(long msec) +{ + widget->press.set_transition_duration(msec); +} + +long Widget::get_press_transition() const +{ + return widget->press.get_transition_duration(); +} + +void Widget::mousein() +{ + widget->hover.change(true); +} + +void Widget::mouseout() +{ + widget->hover.change(false); + if(widget->press) { + widget->press.change(false); + } +} + +float Widget::get_under_mouse() const +{ + return widget->hover.get_value(); +} + +bool Widget::is_under_mouse() const +{ + return widget->hover.get_state(); +} + +void Widget::set_hover_transition(long msec) +{ + widget->hover.set_transition_duration(msec); +} + +long Widget::get_hover_transition() const +{ + return widget->hover.get_transition_duration(); +} + +bool Widget::can_focus() const +{ + return false; +} + +void Widget::focusin() +{ + widget->focus.change(true); +} + +void Widget::focusout() +{ + widget->focus.change(false); +} + +float Widget::get_focus() const +{ + return widget->focus.get_value(); +} + +bool Widget::is_focused() const +{ + return widget->focus.get_state(); +} + +void Widget::set_focus_transition(long msec) +{ + widget->focus.set_transition_duration(msec); +} + +long Widget::get_focus_transition() const +{ + return widget->focus.get_transition_duration(); +} + +void Widget::set_position(float x, float y) +{ + set_position(Vec2(x, y)); +} + +void Widget::set_position(const Vec2 &pos) +{ + Vec2 sz = get_size(); + + widget->box.bmin = pos; + widget->box.bmax.x = pos.x + sz.x; + widget->box.bmax.y = pos.y + sz.y; +} + +const Vec2 &Widget::get_position() const +{ + return widget->box.bmin; +} + +void Widget::set_size(float x, float y) +{ + set_size(Vec2(x, y)); +} + +void Widget::set_size(const Vec2 &sz) +{ + widget->box.bmax.x = widget->box.bmin.x + sz.x; + widget->box.bmax.y = widget->box.bmin.y + sz.y; +} + +const Vec2 Widget::get_size() const +{ + return Vec2(widget->box.bmax.x - widget->box.bmin.x, + widget->box.bmax.y - widget->box.bmin.y); +} + + +const BBox &Widget::get_box() const +{ + return widget->box; +} + +bool Widget::hit_test(const Vec2 &pt) const +{ + return pt.x >= widget->box.bmin.x && pt.x < widget->box.bmax.x && + pt.y >= widget->box.bmin.y && pt.y < widget->box.bmax.y; +} + +void Widget::draw() const +{ + WidgetDrawFunc draw_func = default_draw_func; + + if(theme) { + draw_func = theme->get_draw_func(get_type_name()); + } + + draw_func(this); +} + +// dummy event handlers +void Widget::on_mouse_button(const ButtonEvent &ev) +{ +} + +void Widget::on_mouse_motion(const MotionEvent &ev) +{ +} + +void Widget::on_mouse_focus(const FocusEvent &ev) +{ +} + +void Widget::on_key(const KeyEvent &ev) +{ +} + +void Widget::on_click() +{ +} + +void Widget::on_double_click() +{ +} + +void Widget::on_change() +{ +} + + +#define CALL_CB(w, ev) \ + do { \ + if((w)->widget->cb[ev.type].func) { \ + (w)->widget->cb[ev.type].func((w), ev, (w)->widget->cb[ev.type].cls); \ + } \ + } while(0) + +#define CALL_CB_TYPE(w, t) \ + do { \ + Event ev; \ + ev.type = (t); \ + CALL_CB(w, ev); \ + } while(0) + +/* the event dispatcher generates high-level events (click, etc) + * and calls the on_whatever() functions for both low and high-level + * events. + * The on_whatever functions are called *after* any other actions performed + * here, to give subclasses the opportunity to override them easily, by + * overriding the on_ functions, without having to override handle_event itself + */ +// TODO also call callbacks here I guess... +void Widget::handle_event(const Event &ev) +{ + switch(ev.type) { + case EV_MOUSE_BUTTON: + if(ev.button.press) { + press(); + } else { + if(is_pressed()) { + CALL_CB_TYPE(this, EV_CLICK); + on_click(); + } + release(); + } + + on_mouse_button(ev.button); + break; + + case EV_MOUSE_MOTION: + on_mouse_motion(ev.motion); + break; + + case EV_MOUSE_FOCUS: + if(ev.focus.enter) { + mousein(); + } else { + mouseout(); + } + on_mouse_focus(ev.focus); + break; + + case EV_KEY: + on_key(ev.key); + break; + + case EV_CHANGE: + on_change(); + break; + + default: + break; + } + + CALL_CB(this, ev); +} + + +void Widget::set_callback(EventType evtype, EventCallback func, void *cls) +{ + widget->cb[evtype].func = func; + widget->cb[evtype].cls = cls; +} + + +} // namespace goatkit diff --git a/src/goatkit/widget.h b/src/goatkit/widget.h new file mode 100644 index 0000000..3cdee78 --- /dev/null +++ b/src/goatkit/widget.h @@ -0,0 +1,130 @@ +/* +GoatKit - a themable/animated widget toolkit for games +Copyright (C) 2014-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOATKIT_WIDGET_H_ +#define GOATKIT_WIDGET_H_ + +#include "vec.h" +#include "event.h" + +namespace goatkit { + +struct BBox { + Vec2 bmin, bmax; +}; + +class Screen; +class Widget; +struct WidgetImpl; + +typedef void (*EventCallback)(Widget*, const Event &ev, void *cls); + +class Widget { +protected: + WidgetImpl *widget; + + virtual void set_screen(Screen *scr); + Screen *get_screen() const; + +public: + Widget(); + virtual ~Widget(); + + virtual const char *get_type_name() const; + + virtual void set_name(const char *name); + virtual const char *get_name() const; + + virtual void set_text(const char *text); + virtual const char *get_text() const; + + virtual void show(); + virtual void hide(); + virtual float get_visibility() const; + virtual bool is_visible() const; + virtual void set_visibility_transition(long msec); + virtual long get_visibility_transition() const; + + virtual void activate(); + virtual void deactivate(); + virtual float get_active() const; + virtual bool is_active() const; + virtual void set_active_transition(long msec); + virtual long get_active_transition() const; + + virtual void press(); + virtual void release(); + virtual float get_pressed() const; + virtual bool is_pressed() const; + virtual void set_press_transition(long msec); + virtual long get_press_transition() const; + + virtual void mousein(); + virtual void mouseout(); + virtual float get_under_mouse() const; + virtual bool is_under_mouse() const; + virtual void set_hover_transition(long msec); + virtual long get_hover_transition() const; + + // input focus, managed by the screen + virtual bool can_focus() const; + virtual void focusin(); + virtual void focusout(); + virtual float get_focus() const; + virtual bool is_focused() const; + virtual void set_focus_transition(long msec); + virtual long get_focus_transition() const; + + virtual void set_position(float x, float y); + virtual void set_position(const Vec2 &pos); + virtual const Vec2 &get_position() const; + + virtual void set_size(float x, float y); + virtual void set_size(const Vec2 &size); + virtual const Vec2 get_size() const; + + virtual const BBox &get_box() const; + + virtual bool hit_test(const Vec2 &pt) const; + + virtual void draw() const; + + // low level events + virtual void on_mouse_button(const ButtonEvent &ev); + virtual void on_mouse_motion(const MotionEvent &ev); + virtual void on_mouse_focus(const FocusEvent &ev); + virtual void on_key(const KeyEvent &ev); + + // high level events + virtual void on_click(); + virtual void on_double_click(); + virtual void on_change(); + //virtual void on_drag_move(int bn, const Vec2 &pt); + //virtual void on_drag_release(int bn, const Vec2 &pt); + + // event dispatcher + virtual void handle_event(const Event &ev); + + // external callback setting + virtual void set_callback(EventType evtype, EventCallback func, void *cls = 0); + + friend class Screen; +}; + +} + +#endif // GOATKIT_WIDGET_H_ diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..1ee34ad --- /dev/null +++ b/src/main.cc @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include "game.h" + +#define KEYST_SZ 65536 / 32 + +static void display(); +static void idle(); +static void reshape(int x, int y); +static void keydown(unsigned char key, int x, int y); +static void keyup(unsigned char key, int x, int y); +static void skeydown(int key, int x, int y); +static void skeyup(int key, int x, int y); +static void mouse(int bn, int st, int x, int y); +static void motion(int x, int y); +static void wheel(int wheel, int dir, int x, int y); + +static long prev_time; +static uint32_t keystate[KEYST_SZ]; +static bool bnstate[16]; +static unsigned int modkeys; + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + glutInitWindowSize(1024, 600); + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_SRGB | GLUT_MULTISAMPLE); + glutCreateWindow("ludum dare 42"); + + glutDisplayFunc(display); + glutIdleFunc(idle); + glutReshapeFunc(reshape); + glutKeyboardFunc(keydown); + glutKeyboardUpFunc(keyup); + glutSpecialFunc(skeydown); + glutSpecialUpFunc(skeyup); + glutMouseFunc(mouse); + glutMotionFunc(motion); + glutPassiveMotionFunc(motion); + glutMouseWheelFunc(wheel); + + if(!game_init()) { + return 1; + } + atexit(game_cleanup); + + prev_time = glutGet(GLUT_ELAPSED_TIME); + + glutMainLoop(); + return 0; +} + +void game_quit() +{ + exit(0); +} + +bool game_keystate(int key) +{ + int idx = key / 32; + int bit = key % 32; + return keystate[idx] & (1 << bit); +} + +bool game_bnstate(int bn) +{ + return bnstate[bn]; +} + +unsigned int game_modkeys() +{ + return modkeys; +} + +static void display() +{ + frame_time = glutGet(GLUT_ELAPSED_TIME); + frame_dt = (frame_time - prev_time) / 1000.0f; + + game_draw(); + glutSwapBuffers(); +} + +static void idle() +{ + glutPostRedisplay(); +} + +static void reshape(int x, int y) +{ + glViewport(0, 0, x, y); + win_width = x; + win_height = y; + win_aspect = (float)x / (float)y; + + game_reshape(x, y); +} + +static void keydown(unsigned char key, int x, int y) +{ + modkeys = glutGetModifiers(); + keystate[key / 32] |= (1 << (key % 32)); + game_keyboard(key, true); +} + +static void keyup(unsigned char key, int x, int y) +{ + modkeys = glutGetModifiers(); + keystate[key / 32] &= ~(1 << (key % 32)); + game_keyboard(key, false); +} + +static int conv_skey(int key) +{ + if(key >= GLUT_KEY_F1 && key <= GLUT_KEY_F12) { + return KEY_F1 + (key - GLUT_KEY_F1); + } + + switch(key) { + case GLUT_KEY_LEFT: + return KEY_LEFT; + case GLUT_KEY_UP: + return KEY_UP; + case GLUT_KEY_RIGHT: + return KEY_RIGHT; + case GLUT_KEY_DOWN: + return KEY_DOWN; + case GLUT_KEY_PAGE_UP: + return KEY_PGUP; + case GLUT_KEY_PAGE_DOWN: + return KEY_PGDOWN; + case GLUT_KEY_HOME: + return KEY_HOME; + case GLUT_KEY_END: + return KEY_END; + case GLUT_KEY_INSERT: + return KEY_INSERT; + default: + break; + } + + return 0; +} + +static void skeydown(int key, int x, int y) +{ + modkeys = glutGetModifiers(); + keystate[key / 32] |= (1 << (key % 32)); + game_keyboard(conv_skey(key), true); +} + +static void skeyup(int key, int x, int y) +{ + modkeys = glutGetModifiers(); + keystate[key / 32] &= ~(1 << (key % 32)); + game_keyboard(conv_skey(key), false); +} + +static void mouse(int bn, int st, int x, int y) +{ + int idx = bn - GLUT_LEFT_BUTTON; + bool pressed = st == GLUT_DOWN; + + modkeys = glutGetModifiers(); + + if(idx == 3) { + wheel(0, 1, x, y); + return; + } else if(idx == 4) { + wheel(0, -1, x, y); + return; + } + + if(idx < 16) { + bnstate[idx] = pressed; + } + game_mbutton(idx, pressed, x, y); +} + +static void motion(int x, int y) +{ + game_mmotion(x, y); +} + +static void wheel(int wheel, int dir, int x, int y) +{ + game_mwheel(dir, x, y); +} diff --git a/src/menuscr.cc b/src/menuscr.cc new file mode 100644 index 0000000..7e910c4 --- /dev/null +++ b/src/menuscr.cc @@ -0,0 +1,142 @@ +#include +#include "game.h" +#include "screen.h" +#include "opengl.h" +#include "goatkit/goatkit.h" + +enum { + BN_START, + BN_EXIT +}; + +static void bn_handler(goatkit::Widget *w, const goatkit::Event &ev, void *cls); + +extern GameScreen *scr_game; + +static int virt_width = 800; +static int virt_height = 600; +static goatkit::Screen ui; +static goatkit::Button *bn_start, *bn_exit; +static bool start_pending, exit_pending; +static bool stopped; + +bool MenuScreen::init() +{ + ui.hide(); + + int bnwidth = 200; + int bnheight = 40; + + int xpos = (virt_width - bnwidth) / 2; + int ypos = 100; + int vsep = 80; + + bn_start = new goatkit::Button; + bn_start->set_position(xpos, ypos += vsep); + bn_start->set_size(bnwidth, bnheight); + bn_start->set_text("Start"); + bn_start->set_callback(goatkit::EV_CLICK, bn_handler, (void*)BN_START); + ui.add_widget(bn_start); + + bn_exit = new goatkit::Button; + bn_exit->set_position(xpos, ypos += vsep); + bn_exit->set_size(bnwidth, bnheight); + bn_exit->set_text("Exit"); + bn_exit->set_callback(goatkit::EV_CLICK, bn_handler, (void*)BN_EXIT); + ui.add_widget(bn_exit); + + ui.set_visibility_transition(300); + + if(!(goatkit::theme = goatkit::get_theme("simple"))) { + return false; + } + + return true; +} + +void MenuScreen::destroy() +{ +} + + +void MenuScreen::start() +{ + ui.show(); + stopped = false; +} + +void MenuScreen::stop() +{ + ui.hide(); + stopped = true; +} + +void MenuScreen::draw() +{ + if(ui.get_visibility() == 0.0f) { + if(start_pending) { + start_pending = false; + push_screen(scr_game); + stop(); + return; + } + if(exit_pending) { + exit_pending = false; + game_quit(); + return; + } + } + + if(stopped) { + start(); + } + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, virt_width, virt_height, 0, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + ui.draw(); + +} + +void MenuScreen::reshape(int x, int y) +{ + ui.set_size(virt_width, virt_height); +} + + +void MenuScreen::keyboard(int key, bool pressed) +{ + ui.sysev_keyboard(key, pressed); +} + +void MenuScreen::mbutton(int bn, bool pressed, int x, int y) +{ + x = x * virt_width / win_width; + y = y * virt_height / win_height; + ui.sysev_mouse_button(bn, pressed, x, y); +} + +void MenuScreen::mmotion(int x, int y) +{ + x = x * virt_width / win_width; + y = y * virt_height / win_height; + ui.sysev_mouse_motion(x, y); +} + +static void bn_handler(goatkit::Widget *w, const goatkit::Event &ev, void *cls) +{ + if(w == bn_start) { + start_pending = true; + ui.hide(); + } else if(w == bn_exit) { + exit_pending = true; + ui.hide(); + } +} diff --git a/src/opengl.c b/src/opengl.c new file mode 100644 index 0000000..7042514 --- /dev/null +++ b/src/opengl.c @@ -0,0 +1,150 @@ +#include +#include +#include "opengl.h" + + +static void GLAPIENTRY gldebug_logger(GLenum src, GLenum type, GLuint id, GLenum severity, + GLsizei len, const char *msg, const void *cls); + +static const char *gldebug_srcstr(unsigned int src); +static const char *gldebug_typestr(unsigned int type); + +struct GLCaps glcaps; + +int init_opengl(void) +{ + glewInit(); + + glcaps.debug = GLEW_ARB_debug_output; + +#ifndef NDEBUG + if(glcaps.debug) { + printf("Installing OpenGL debug callback\n"); + glDebugMessageCallbackARB(gldebug_logger, 0); + } +#endif + + return 0; +} + +int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + +void dump_gl_texture(unsigned int tex, const char *fname) +{ + FILE *fp; + int i, width, height; + unsigned char *pixels, *pptr; + + glBindTexture(GL_TEXTURE_2D, tex); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + + if(!(pixels = malloc(width * height * 4))) { + return; + } + pptr = pixels; + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + if(!(fp = fopen(fname, "wb"))) { + free(pixels); + return; + } + fprintf(fp, "P6\n%d %d\n255\n", width, height); + for(i=0; i + +struct GLCaps { + int debug; /* ARB_debug_output */ +}; + +extern struct GLCaps glcaps; + +#ifdef __cplusplus +extern "C" { +#endif + +int init_opengl(void); + +int next_pow2(int x); + +void dump_gl_texture(unsigned int tex, const char *fname); + +#ifdef __cplusplus +} +#endif + +#endif /* OPENGL_H_ */ diff --git a/src/screen.cc b/src/screen.cc new file mode 100644 index 0000000..6b9a274 --- /dev/null +++ b/src/screen.cc @@ -0,0 +1,95 @@ +#include "screen.h" + +ScreenBase *active_screen; +MenuScreen *scr_menu; +GameScreen *scr_game; + +bool init_screens() +{ + scr_menu = new MenuScreen; + if(!scr_menu->init()) { + return false; + } + + scr_game = new GameScreen; + if(!scr_game->init()) { + return false; + } + + push_screen(scr_menu); + return true; +} + +void cleanup_screens() +{ + scr_game->destroy(); + delete scr_game; + + scr_menu->destroy(); + delete scr_menu; +} + +void push_screen(ScreenBase *scr) +{ + scr->next = active_screen; + active_screen = scr; + + scr->start(); +} + +void pop_screen() +{ + ScreenBase *scr = active_screen; + active_screen = scr->next; + + scr->stop(); +} + + +ScreenBase::ScreenBase() +{ + next = 0; +} + +ScreenBase::~ScreenBase() +{ +} + +bool ScreenBase::init() +{ + return true; +} + +void ScreenBase::destroy() +{ +} + +void ScreenBase::start() +{ +} + +void ScreenBase::stop() +{ +} + + +void ScreenBase::reshape(int x, int y) +{ +} + + +void ScreenBase::keyboard(int key, bool pressed) +{ +} + +void ScreenBase::mbutton(int bn, bool pressed, int x, int y) +{ +} + +void ScreenBase::mmotion(int x, int y) +{ +} + +void ScreenBase::mwheel(int dir, int x, int y) +{ +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..c5422f9 --- /dev/null +++ b/src/screen.h @@ -0,0 +1,65 @@ +#ifndef SCREEN_H_ +#define SCREEN_H_ + +class ScreenBase; + +bool init_screens(); +void cleanup_screens(); +void push_screen(ScreenBase *scr); +void pop_screen(); + +extern ScreenBase *active_screen; + + +class ScreenBase { +public: + ScreenBase *next; + + ScreenBase(); + virtual ~ScreenBase(); + + virtual bool init(); + virtual void destroy(); + + virtual void start(); + virtual void stop(); + + virtual void draw() = 0; + virtual void reshape(int x, int y); + + virtual void keyboard(int key, bool pressed); + virtual void mbutton(int bn, bool pressed, int x, int y); + virtual void mmotion(int x, int y); + virtual void mwheel(int dir, int x, int y); +}; + +class MenuScreen : public ScreenBase { +public: + bool init(); + void destroy(); + + void start(); + void stop(); + + void draw(); + void reshape(int x, int y); + + void keyboard(int key, bool pressed); + void mbutton(int bn, bool pressed, int x, int y); + void mmotion(int x, int y); +}; + +class GameScreen : public ScreenBase { +public: + bool init(); + void destroy(); + + void draw(); + + void keyboard(int key, bool pressed); + void mbutton(int bn, bool pressed, int x, int y); + void mmotion(int x, int y); + void mwheel(int dir, int x, int y); +}; + +#endif // SCREEN_H_ diff --git a/src/uitheme.cc b/src/uitheme.cc new file mode 100644 index 0000000..28c497c --- /dev/null +++ b/src/uitheme.cc @@ -0,0 +1,411 @@ +#include +#include +#include +#include "goatkit/goatkit.h" + +#if !defined(WIN32) && !defined(__WIN32__) +#include +#else +#include +#endif + +#include + +#define FONT GLUT_BITMAP_HELVETICA_18 + +using namespace goatkit; + +typedef void (*DrawFunc)(const Widget*); + +static void draw_button(const Widget *w); +static void draw_checkbox(const Widget *w); +static void draw_label(const Widget *w); +static void draw_slider(const Widget *w); +static void draw_textbox(const Widget *w); +static float calc_text_width(const char *text); +static void draw_text(float x, float y, const char *text); +static void draw_rect(const Widget *w, float x, float y, float xsz, float ysz); + +static void get_fgcolor(const Widget *w, float *col); +static void get_bgcolor(const Widget *w, float *col); + +static struct { + const char *name; + DrawFunc func; +} widget_funcs[] = { + { "button", draw_button }, + { "checkbox", draw_checkbox }, + { "label", draw_label }, + { "slider", draw_slider }, + { "textbox", draw_textbox }, + { 0, 0 } +}; + +static bool initialized; +static std::map funcmap; + +/* theme colors */ +static float fgcol[] = {0.8, 0.6, 0.4, 1.0}; +static float fgcol_off[] = {0.65, 0.65, 0.6, 1.0}; +static float fgcol_inact[] = {0.4, 0.4, 0.4, 1.0}; +static float bgcol[] = {0.3, 0.3, 0.3, 1.0}; +static float bgcol_off[] = {0.3, 0.3, 0.3, 1.0}; +static float bgcol_inact[] = {0.3, 0.3, 0.3, 1.0}; + + +extern "C" goatkit::WidgetDrawFunc get_widget_func(const char *name) +{ + int i; + + if(!initialized) { + for(i=0; widget_funcs[i].func; i++) { + funcmap[widget_funcs[i].name] = widget_funcs[i].func; + } + + initialized = true; + } + return funcmap[name]; +} + +// register ourselves as a built-in theme +GOATKIT_BUILTIN_THEME("simple", get_widget_func); + + +#define VIS_THRES 0.0001 + +static void begin_drawing(const Widget *w) +{ + Vec2 pos = w->get_position(); + + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(pos.x, pos.y, 0); +} + +static void end_drawing(const Widget *w) +{ + glPopMatrix(); + glPopAttrib(); +} + + +#define LERP(a, b, t) ((a) + ((b) - (a)) * t) + +static void draw_button(const Widget *w) +{ + Vec2 sz = w->get_size(); + float bnaspect = sz.x / sz.y; + + float pressed = w->get_pressed(); + float vis = w->get_visibility(); + + if(vis < VIS_THRES) { + return; + } + + float tor_rad = sz.x * 0.5 * vis; + float scale = LERP(1.0, 0.85, pressed); + + float fg[4], bg[4]; + get_fgcolor(w, fg); + get_bgcolor(w, bg); + + begin_drawing(w); + + glTranslatef(sz.x / 2.0, sz.y / 2.0, 0.0); + glScalef(1, 1, 1.0 / sz.x); + glRotatef(90, 1, 0, 0); + + glColor4fv(fg); + glutSolidTorus(scale * sz.y / 2.0, scale * tor_rad / 2.0, 18, 16); + + glScalef(1.0 - 0.1 / bnaspect, 1.0, 1.0 - 0.1); + glColor4fv(bg); + glutSolidTorus(scale * sz.y / 2.0, scale * tor_rad / 2.0, 18, 16); + + if(vis >= 1.0) { + glTranslatef(-calc_text_width(w->get_text()) / 2.0, 0, -5); + glColor4fv(fg); + draw_text(0, 0, w->get_text()); + } + + end_drawing(w); +} + +static void draw_checkbox(const Widget *w) +{ + Vec2 sz = w->get_size(); + float vis = w->get_visibility(); + + if(vis < VIS_THRES) { + return; + } + + float fg[4]; + get_fgcolor(w, fg); + + goatkit::CheckBox *cbox = (goatkit::CheckBox*)w; + + begin_drawing(w); + + draw_rect(w, 0, 0, sz.y, sz.y); + + float state = cbox->get_checked(); + + float bottom[2] = {sz.y / 2, sz.y - 1}; + float left[2] = {-2, sz.y / 4}; + float right[2] = {sz.y, -4}; + + if(state > 0.0) { + // draw tickmark + glBegin(GL_TRIANGLES); + glColor4fv(fg); + float t = state * 2.0 > 1.0 ? 1.0 : state * 2.0; + glVertex2f(left[0], left[1]); + glVertex2f(LERP(left[0], bottom[0], t), LERP(left[1], bottom[1], t)); + glVertex2f(LERP(left[0], bottom[0], t), LERP(left[1], bottom[1] - sz.y / 3, t)); + + if((t = state * 2.0 - 1.0) > 0.0) { + glVertex2f(bottom[0], bottom[1]); + glVertex2f(LERP(bottom[0], right[0], t), LERP(bottom[1], right[1], t)); + glVertex2f(bottom[0], bottom[1] - sz.y / 3); + } + glEnd(); + } + + glTranslatef(sz.y * 1.5, sz.y / 2.0 + 5, 0); + glColor4fv(fg); + draw_text(0, 0, w->get_text()); + + end_drawing(w); +} + +static void draw_label(const Widget *w) +{ + Vec2 sz = w->get_size(); + float vis = w->get_visibility(); + + if(vis < VIS_THRES) { + return; + } + + float fg[4]; + get_fgcolor(w, fg); + + begin_drawing(w); + + glTranslatef((sz.x - calc_text_width(w->get_text())) / 2.0, sz.y / 2.0, 0); + glColor4fv(fg); + draw_text(0, 0, w->get_text()); + + end_drawing(w); +} + +static void draw_slider(const Widget *w) +{ + Vec2 sz = w->get_size(); + float vis = w->get_visibility(); + + if(vis < VIS_THRES) { + return; + } + + float fg[4], bg[4]; + get_fgcolor(w, fg); + get_bgcolor(w, bg); + + Slider *slider = (Slider*)w; + float pad = slider->get_padding(); + float handle_width = pad * 2.0; + + float value = slider->get_value(); + char valtext[16]; + sprintf(valtext, "%g", value); + + float trough_sz = sz.x - 2.0 * pad; + float x = pad + trough_sz * slider->get_value_norm(); + + float act_height = sz.y / 2.0; + + begin_drawing(w); + + float step = slider->get_step(); + if(step > 0.0) { + float beg = slider->get_range_min(); + float end = slider->get_range_max(); + int num_seg = (end - beg) / step; + int num_ticks = num_seg + 1; + float x = pad; + float dx = trough_sz / num_seg; + + glLineWidth(1.0); + glBegin(GL_LINES); + glColor4fv(fg); + for(int i=0; iis_visible()) { + return; + } + + Vec2 sz = w->get_size(); + + float fg[4]; + get_fgcolor(w, fg); + + TextBox *tbox = (TextBox*)w; + + begin_drawing(w); + + draw_rect(w, 0, 0, sz.x, sz.y); + + const char *str = tbox->get_text(); + char *buf = (char*)alloca(strlen(str) + 1); + + // figure out how many characters fit in the textbox + float tsz = 0.0; + const char *sptr = str; + char *dptr = buf; + while(*sptr) { + float nsz = tsz + glutBitmapWidth(FONT, *sptr); + if(nsz >= sz.x) { + break; + } + *dptr++ = *sptr++; + tsz = nsz; + } + *dptr = 0; + + glColor4fv(fg); + draw_text(2, 2.0 * sz.y / 3.0, buf); + + // draw the cursor + int cursor = tbox->get_cursor(); + float cx = 1.0; + + if(cursor > 0) { + memcpy(buf, str, cursor); + buf[cursor] = 0; + cx += calc_text_width(buf); + } + + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1.0 - fg[0], 1.0 - fg[1], 1.0 - fg[2], fg[3]); + draw_text(cx, 2.0 * sz.y / 3.0, "|"); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + end_drawing(w); +} + +static float calc_text_width(const char *text) +{ + float res = 0.0f; + + while(*text) { + res += glutBitmapWidth(FONT, *text++); + } + return res; +} + +static void draw_text(float x, float y, const char *text) +{ + glRasterPos2f(x, y); + + while(*text) { + glutBitmapCharacter(FONT, *text++); + } +} + +#define DISP(s, t) LERP((s) * 2.0, 0, (t)) +static void draw_rect(const Widget *w, float x, float y, float xsz, float ysz) +{ + float fg[4], bg[4]; + float vis = w->get_visibility(); + + get_fgcolor(w, fg); + get_bgcolor(w, bg); + + glBegin(GL_QUADS); + glColor4fv(bg); + glVertex2f(x, y); + glVertex2f(x + xsz, y); + glVertex2f(x + xsz, y + ysz); + glVertex2f(x, y + ysz); + glEnd(); + + glLineWidth(w->get_focus() + 1.0); + + glPushMatrix(); + glTranslatef(0.5, 0.5, 0); + + glBegin(GL_LINES); + glColor4fv(fg); + + // top + glVertex2f(x - DISP(xsz, vis), y); + glVertex2f(x + xsz - DISP(xsz, vis), y); + + // right + glVertex2f(x + xsz, y - DISP(ysz, vis)); + glVertex2f(x + xsz, y + ysz - DISP(ysz, vis)); + + // bottom + glVertex2f(x + xsz + DISP(xsz, vis), y + ysz); + glVertex2f(x + DISP(xsz, vis), y + ysz); + + // left + glVertex2f(x, y + ysz + DISP(ysz, vis)); + glVertex2f(x, y + DISP(ysz, vis)); + + glEnd(); + glPopMatrix(); + + glLineWidth(1); +} + + +static void get_fgcolor(const Widget *w, float *col) +{ + float hover = w->get_under_mouse(); + float act = w->get_active(); + float vis = w->get_visibility(); + + for(int i=0; i<4; i++) { + float c = LERP(fgcol_off[i], fgcol[i], hover); + col[i] = LERP(fgcol_inact[i], c, act); + } + col[3] *= vis; +} + +static void get_bgcolor(const Widget *w, float *col) +{ + float hover = w->get_under_mouse(); + float act = w->get_active(); + float vis = w->get_visibility(); + + for(int i=0; i<4; i++) { + float c = LERP(bgcol_off[i], bgcol[i], hover); + col[i] = LERP(bgcol_inact[i], c, act); + } + col[3] *= vis; +} -- 1.7.10.4