Compare commits

..

38 Commits

Author SHA1 Message Date
zetaPRIME
a0de416fe5 more missing includes, makefile and permission fixes 2017-12-26 17:16:53 -05:00
zetaPRIME
7e1d160914 rework/optimize RenderCore VBO implementation 2017-12-25 01:26:45 -05:00
zetaPRIME
3c9fc00ac8 don't reinit attrinfo for every quad 2017-12-25 00:09:25 -05:00
zetaPRIME
296826a927 new stuff compat 2017-12-24 23:52:25 -05:00
zetaPRIME
1a3948216f Vector2::ClampLength() and misc fixes 2017-05-19 22:37:55 -04:00
zetaPRIME
ab5e6d42a6 oh wait, that's not a vector2 2017-05-11 22:46:25 -04:00
zetaPRIME
8ae734680d fix UICanvas garbage-on-empty 2017-05-11 18:01:51 -04:00
zetaPRIME
e183532747 added FontNull, should hopefully prevent crashes from trying to load fonts with no fallback 2017-05-11 17:42:07 -04:00
zetaPRIME
15dbabd917 app:/, romfs:/ and sdmc:/ font loading 2017-05-11 15:45:14 -04:00
zetaPRIME
7928e56475 give the testbed the *good* hb logo 2017-05-10 14:16:40 -04:00
zetaPRIME
4e766d765d make cia use smdh as icon 2017-05-10 00:18:37 -04:00
zetaPRIME
c667c1b0d1 profiler; rsf, makefile and image loading tweaks; pngcrush'd largest images; removed leftover debug text from asset loading 2017-05-09 05:21:52 -04:00
zetaPRIME
f852ec8134 fix cia romfs... very manually 2017-05-08 05:57:21 -04:00
zetaPRIME
6b735ca722 excise excess binary 2017-05-08 04:22:16 -04:00
zetaPRIME
53712cd94c fix debug console on hardware (also fix gitignore) 2017-05-08 04:21:14 -04:00
zetaPRIME
a3d3a0efcd switch to testing via CIA since 3dsx stopped working for no discernible reason (temp borrowed banner assets);
semi-working debug console (svcBreak on hw!?)
2017-05-08 00:29:56 -04:00
zetaPRIME
260000aea3 that's not supposed to be indented 2017-05-07 14:39:56 -04:00
zetaPRIME
d4947d8d11 fix app:/ asset loading (whoops) 2017-05-07 14:11:23 -04:00
zetaPRIME
244e2c2ed0 ...and remove testing artifacts 2017-05-03 21:38:23 -04:00
zetaPRIME
c3207cc2f9 implement mask blendmode 2017-05-03 21:37:34 -04:00
zetaPRIME
2d93b903e4 fix replace blend mode on citra (which also fixes the clearing workaround)
...also, actually disable depth testing instead of just thinking I did
2017-05-03 09:10:02 -04:00
zetaPRIME
5577b5a6a8 threads now end cleanly, and do so on exit 2017-05-03 04:19:30 -04:00
zetaPRIME
88d640f3e7 thread testing and fixes, exposed blend mode in drawables, made frame timing a bit more accurate 2017-05-03 00:52:46 -04:00
zetaPRIME
c4a75355eb blend modes on bind, workaround for the clear bug (just shove a quad there in replace mode) 2017-05-02 16:27:10 -04:00
zetaPRIME
144510b4e7 more complete threading, update to newest libctru and c3d-next (clearing a new rendertarget doesn't work the first time yet) 2017-05-01 21:35:51 -04:00
zetaPRIME
9645920759 start of actual thread work 2017-05-01 00:40:13 -04:00
zetaPRIME
2e4d55a1ff sdmc:/ and romfs:/ drawable loading, themeref refcounting and container gc (and not inner const anymore) 2017-04-30 13:24:29 -04:00
zetaPRIME
a4d23ea438 once more, with feeling! 2017-04-17 15:16:23 -04:00
zetaPRIME
4e614e5cab ...right 2017-04-17 15:13:53 -04:00
zetaPRIME
5dcbb6bb8a actually fix license detection 2017-04-17 15:13:11 -04:00
zetaPRIME
40d0c39725 fix github license detection? 2017-04-17 15:05:01 -04:00
zetaPRIME
c6a5781cf8 Application::GetTime(), InputManager improvements,
shiftkey and fast scroll for OSK, partial ThemeManager GC,
and planning
2017-03-21 01:28:13 -04:00
zetaPRIME
7f27018808 UIContainer::AddNew, Add uses std::move, Optional type,
button styles and glyphs, lower memory footprint for default text
style on Label
2017-03-18 19:29:32 -04:00
zetaPRIME
ecf47db716 out, annoying warning 2017-03-18 17:15:25 -04:00
zetaPRIME
ddb272fd66 let's not waste precious VRAM on a depth buffer we don't use 2017-03-18 16:13:12 -04:00
zetaPRIME
c17200e8db whoops, wrong name there 2017-03-17 15:21:53 -04:00
zetaPRIME
abc14995d8 adjust readme 2017-03-17 07:37:54 -04:00
zetaPRIME
a428ceefcc adjust readme 2017-03-17 07:37:07 -04:00
69 changed files with 1346 additions and 249 deletions

1
.gitignore vendored
View File

@ -32,6 +32,7 @@
*.app *.app
*.elf *.elf
*.3dsx *.3dsx
*.cia
# ...and icons # ...and icons
*.smdh *.smdh

View File

@ -1,4 +1,4 @@
### MIT License The MIT License (MIT)
Copyright (c) 2017 Beau Jessee ("zetaPRIME") Copyright (c) 2017 Beau Jessee ("zetaPRIME")
@ -20,4 +20,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
// Bundled dependencies (see [\_incLib](libstarlight/source/starlight/_incLib)) have their own terms and/or notices, as listed in their respective files. // Bundled dependencies (see _incLib) have their own terms and/or notices, as listed in their respective files.

View File

@ -29,9 +29,11 @@ Additionally, libstarlight contains versions of the following bundled within:
* [nlohmann::json (JSON For Modern C++)](https://github.com/nlohmann/json) * [nlohmann::json (JSON For Modern C++)](https://github.com/nlohmann/json)
## Okay, so how do I use this? ## Okay, so how do I use this?
(section WIP, take a look at the testbed for a somewhat messy example) (section WIP, take a look at the testbed for a slightly scattered example)
To ensure your application runs properly without themes installed to the SD card, it is recommended to include a copy of the default theme (or any theme with no fallback) at `romfs:/.fallback_theme`. To ensure your application runs properly without themes installed to the SD card, it is recommended to include a copy of the default theme (or any theme with no fallback) at `romfs:/.fallback_theme/`.
(Themes on SD go in `sdmc:/.starlight/themes/`)
## License ## License
* MIT (see [license.md](license.md)) * MIT (see [license.md](license.md))

View File

@ -30,7 +30,7 @@ INCLUDES := include
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
# why was -Werror here? # why was -Werror here?
CFLAGS := -g -Wall -O2 -mword-relocations \ CFLAGS := -g -Wall -Wno-psabi -O2 -mword-relocations \
-ffunction-sections \ -ffunction-sections \
-fomit-frame-pointer \ -fomit-frame-pointer \
$(ARCH) $(ARCH)
@ -38,7 +38,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations \
CFLAGS += $(INCLUDE) -DARM11 -D_3DS CFLAGS += $(INCLUDE) -DARM11 -D_3DS
# json requires exception support, nuke -fno-exceptions # json requires exception support, nuke -fno-exceptions
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++14 CXXFLAGS := $(CFLAGS) -fno-rtti -std=c++17
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)

View File

@ -17,6 +17,9 @@ using starlight::ThemeManager;
using starlight::InputManager; using starlight::InputManager;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
using starlight::threading::Thread;
using starlight::threading::ThreadState;
using starlight::ui::TouchScreenCanvas; using starlight::ui::TouchScreenCanvas;
using starlight::ui::TopScreenCanvas; using starlight::ui::TopScreenCanvas;
@ -30,6 +33,7 @@ using starlight::Application;
//////////////////// ////////////////////
Application* Application::_currentApp = nullptr; Application* Application::_currentApp = nullptr;
unsigned long long Application::ftime = 0;
bool Application::Quit() { bool Application::Quit() {
if (_currentApp == nullptr) return false; if (_currentApp == nullptr) return false;
@ -82,6 +86,9 @@ void Application::_init() {
void Application::_end() { void Application::_end() {
End(); End();
for (auto& thread : threads) thread->Exit();
threads.clear();
//for (auto& f : forms) f->Close(); //for (auto& f : forms) f->Close();
forms.clear(); // not sure why, but not doing this results in a data abort if any forms are active forms.clear(); // not sure why, but not doing this results in a data abort if any forms are active
@ -97,6 +104,9 @@ void Application::_end() {
} }
void Application::_mainLoop() { void Application::_mainLoop() {
RenderCore::SyncFrame(); // sync to vblank here for more accurate timing
frameTimer.FrameStart();
if (!forms.empty()) { if (!forms.empty()) {
if (_sFormState) { if (_sFormState) {
_sFormState = false; _sFormState = false;
@ -124,6 +134,8 @@ void Application::_mainLoop() {
} }
// update step // update step
ftime = osGetTime();
InputManager::Update(); InputManager::Update();
Update(); Update();
{ // update loop for forms, guarded from snap-outs { // update loop for forms, guarded from snap-outs
@ -151,4 +163,16 @@ void Application::_mainLoop() {
topScreen->Draw(); topScreen->Draw();
PostDraw(); PostDraw();
RenderCore::EndFrame(); RenderCore::EndFrame();
while (!threads.empty() && frameTimer.GetSubframe() < 0.9) {
auto thread = threads.front();
thread->Resume();
if (thread->state != ThreadState::Finished) threads.splice(threads.end(), threads, threads.begin()); // move to back of queue
else threads.pop_front(); // or just discard if already exited
}
}
void Application::EnqueueThread(std::shared_ptr<starlight::threading::Thread> thread) {
threads.push_back(thread);
thread->Start();
} }

View File

@ -3,11 +3,16 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <list>
#include "starlight/datatypes/Vector2.h" #include "starlight/datatypes/Vector2.h"
#include "starlight/datatypes/VRect.h" #include "starlight/datatypes/VRect.h"
#include "starlight/datatypes/Color.h" #include "starlight/datatypes/Color.h"
#include "starlight/util/FrameTimer.h"
#include "starlight/threading/Thread.h"
#include "starlight/ui/TouchScreenCanvas.h" #include "starlight/ui/TouchScreenCanvas.h"
#include "starlight/ui/TopScreenCanvas.h" #include "starlight/ui/TopScreenCanvas.h"
@ -22,12 +27,14 @@ namespace starlight {
//////////////////// ////////////////////
private: private:
static Application* _currentApp; static Application* _currentApp;
static unsigned long long ftime;
public: public:
static bool Quit(); static bool Quit();
static Config& GetConfig(const std::string& path); static Config& GetConfig(const std::string& path);
static std::string AppName(); static std::string AppName();
static inline Application* Current() { return _currentApp; } static inline Application* Current() { return _currentApp; }
static inline unsigned long long GetTime() { return ftime; }
////////////////////// //////////////////////
// INSTANCE MEMBERS // // INSTANCE MEMBERS //
@ -39,6 +46,9 @@ namespace starlight {
void _mainLoop(); void _mainLoop();
void _end(); void _end();
std::list<std::shared_ptr<threading::Thread>> threads;
util::FrameTimer frameTimer;
public: public:
const std::string appId; const std::string appId;
@ -57,13 +67,14 @@ namespace starlight {
void Run(); void Run();
void EnqueueThread(std::shared_ptr<threading::Thread> thread);
inline void SignalFormState() { _sFormState = true; }
virtual void Init() { } virtual void Init() { }
virtual void Update() { } virtual void Update() { }
virtual void PostUpdate() { } virtual void PostUpdate() { }
virtual void Draw() { } virtual void Draw() { }
virtual void PostDraw() { } virtual void PostDraw() { }
virtual void End() { } virtual void End() { }
inline void SignalFormState() { _sFormState = true; }
}; };
} }

View File

@ -43,6 +43,9 @@ enum class Keys : unsigned int {
Right = DPadRight | CPadRight, ///< D-Pad Right or Circle Pad Right Right = DPadRight | CPadRight, ///< D-Pad Right or Circle Pad Right
}; };
inline constexpr unsigned int operator*(Keys k) { return static_cast<unsigned int>(k); }
inline constexpr Keys operator|(Keys k1, Keys k2) { return static_cast<Keys>(*k1 | *k2); }
namespace starlight { namespace starlight {
// forward declare this for OpenKeyboard // forward declare this for OpenKeyboard
namespace dialog { namespace dialog {
@ -94,11 +97,11 @@ namespace starlight {
static Vector2 CStick(); static Vector2 CStick();
static bool Held(unsigned int mask); static bool Held(unsigned int mask);
static inline bool Held(Keys mask) { return Held(static_cast<unsigned int>(mask)); } static inline bool Held(Keys mask) { return Held(*mask); }
static bool Pressed(unsigned int mask); static bool Pressed(unsigned int mask);
static inline bool Pressed(Keys mask) { return Pressed(static_cast<unsigned int>(mask)); } static inline bool Pressed(Keys mask) { return Pressed(*mask); }
static bool Released(unsigned int mask); static bool Released(unsigned int mask);
static inline bool Released(Keys mask) { return Released(static_cast<unsigned int>(mask)); } static inline bool Released(Keys mask) { return Released(*mask); }
static Vector2 TouchPos(); static Vector2 TouchPos();
static Vector2 TouchDelta(); static Vector2 TouchDelta();

View File

@ -19,12 +19,17 @@
#include "starlight/gfx/DrawableNinePatch.h" #include "starlight/gfx/DrawableNinePatch.h"
#include "starlight/gfx/DrawableTest.h" #include "starlight/gfx/DrawableTest.h"
#include "starlight/gfx/FontBMF.h" #include "starlight/gfx/FontBMF.h"
#include "starlight/gfx/FontNull.h"
#include "starlight/gfx/RenderCore.h" #include "starlight/gfx/RenderCore.h"
#include "starlight/gfx/BitmapFont.h" #include "starlight/gfx/BitmapFont.h"
#include "starlight/util/JsonConversions.h" #include "starlight/util/JsonConversions.h"
#include "starlight/util/Profiler.h"
using starlight::util::Profiler;
#include <sstream>
using std::string; using std::string;
using std::shared_ptr; using std::shared_ptr;
using std::make_shared; using std::make_shared;
@ -75,40 +80,48 @@ namespace {
} }
CTexture* LoadPNG(const std::string& path, bool isPremult = false) { CTexture* LoadPNG(const std::string& path, bool isPremult = false) {
//Profiler::TaskStart();
unsigned char* imgbuf; unsigned char* imgbuf;
unsigned width, height; unsigned width, height;
lodepng::State state;
lodepng_decode32_file(&imgbuf, &width, &height, path.c_str()); lodepng_decode32_file(&imgbuf, &width, &height, path.c_str());
if (state.info_png.color.colortype != 6) isPremult = true; // expect no alpha if not rgba
/*{
std::stringstream ss; ss << "loaded png, ";
ss << width; ss << "x"; ss << height;
Profiler::TaskFinish(ss.str());
}*/
unsigned bw = NextPow2(width), bh = NextPow2(height); unsigned bw = NextPow2(width), bh = NextPow2(height);
u8* gpubuf = static_cast<u8*>(linearAlloc(bw*bh*4)); u8* gpubuf = static_cast<u8*>(linearAlloc(bw*bh*4));
//Profiler::TaskStart();
//memset(gpubuf, 0, bw*bh*4);
//Profiler::TaskFinish("cleared canvas");
u8* src = static_cast<u8*>(imgbuf); u8* dst = static_cast<u8*>(gpubuf);
//Profiler::TaskStart();
if (isPremult) { if (isPremult) {
u32* src = reinterpret_cast<u32*>(imgbuf); u32* dst = reinterpret_cast<u32*>(gpubuf);
// just convert endianness // just convert endianness
for(unsigned iy = 0; iy < height; iy++) { for(unsigned iy = 0; iy < height; iy++) {
for (unsigned ix = 0; ix < width; ix++) { for (unsigned ix = 0; ix < width; ix++) {
int r = *src++; u32 clr = *src;
int g = *src++; *dst = __builtin_bswap32(clr);
int b = *src++;
int a = *src++;
*dst++ = a; src+=4; dst+=4;
*dst++ = b;
*dst++ = g;
*dst++ = r;
} }
dst += (bw - width) * 4; // skip the difference dst += (bw - width) * 4; // skip the difference
} }
} else { } else {
u8* src = static_cast<u8*>(imgbuf); u8* dst = static_cast<u8*>(gpubuf);
// convert and premultiply // convert and premultiply
for(unsigned iy = 0; iy < height; iy++) { for(unsigned iy = 0; iy < height; iy++) {
for (unsigned ix = 0; ix < width; ix++) { for (unsigned ix = 0; ix < width; ix++) {
int r = *src++; u8 r = *src++;
int g = *src++; u8 g = *src++;
int b = *src++; u8 b = *src++;
int a = *src++; u8 a = *src++;
float aa = (1.0f / 255.0f) * a; float aa = (1.0f / 255.0f) * a;
@ -120,11 +133,14 @@ namespace {
dst += (bw - width) * 4; // skip the difference dst += (bw - width) * 4; // skip the difference
} }
} }
//Profiler::TaskFinish("made into canvas, flipped and premult");
// completely skipping over the difference instead of erasing might eventually lead to garbage outside of frame, // completely skipping over the difference instead of erasing might eventually lead to garbage outside of frame,
// but meh; that'll only be visible if you intentionally push the UVs outside the image proper // but meh; that'll only be visible if you intentionally push the UVs outside the image proper
//Profiler::TaskStart();
CTexture* tx = RenderCore::LoadTexture(static_cast<void*>(gpubuf), bw, bh); CTexture* tx = RenderCore::LoadTexture(static_cast<void*>(gpubuf), bw, bh);
tx->size = Vector2(width, height); // and for now just fix the size after the fact tx->size = Vector2(width, height); // and for now just fix the size after the fact
//Profiler::TaskFinish("copied into linear");
std::free(imgbuf); std::free(imgbuf);
linearFree(gpubuf); linearFree(gpubuf);
@ -179,6 +195,20 @@ void ThemeManager::End() {
} }
void ThemeManager::GC() {
constexpr const int keepCycles = 5; // how many gc sweeps a drawable gets to stay loaded without being used
std::vector<string> rem;
// WIP
for (auto& d : drawables) {
if (++d.second.lastAccess > keepCycles) {
d.second.Unload();
if (d.second.refCount <= 0) rem.push_back(d.first); // mark for full removal when no references exist
}
}
for (auto& s : rem) drawables.erase(s); // and remove everything queued
}
ThemeRef<Drawable> ThemeManager::GetAsset(const std::string& name) { ThemeRef<Drawable> ThemeManager::GetAsset(const std::string& name) {
auto const& itr = drawables.find(name); auto const& itr = drawables.find(name);
if (itr == drawables.end()) { if (itr == drawables.end()) {
@ -195,14 +225,14 @@ ThemeRef<Font> ThemeManager::GetFont(const std::string& name) {
void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) { void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) {
string path = ResolveAssetPath(ref.name); string path = ResolveAssetPath(ref.name);
ref.ptr = LoadAsset(path); ref.ptr = LoadAsset(path, ref);
} }
shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) { shared_ptr<Drawable> ThemeManager::LoadAsset(string& path, ThemeRefContainer<Drawable>& ref) {
static shared_ptr<Drawable> nulldrw = make_shared<starlight::gfx::DrawableTest>(); static shared_ptr<Drawable> nulldrw = make_shared<starlight::gfx::DrawableTest>();
string ext = FindExtension(path); string ext = FindExtension(path);
printf("load: %s (%s)\n", path.c_str(), ext.c_str()); //printf("load: %s (%s)\n", path.c_str(), ext.c_str());
/**/ if (ext == "png") { /**/ if (ext == "png") {
return make_shared<DrawableImage>(LoadPNG(path)); return make_shared<DrawableImage>(LoadPNG(path));
} }
@ -213,7 +243,7 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
fs >> j; fs >> j;
} }
auto st = j.dump(); auto st = j.dump();
printf("file contents: %s\n", st.c_str()); //printf("file contents: %s\n", st.c_str());
string type = j["assetType"]; string type = j["assetType"];
/**/ if (type == "ninepatch") { /**/ if (type == "ninepatch") {
@ -225,9 +255,8 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
// else if (type == "") { } // else if (type == "") { }
else if (type == "link") { else if (type == "link") {
string npath = ResolveAssetPath(j["path"]); string npath = ResolveAssetPath(j["path"]);
//return LoadAsset(npath); ref.redir = const_cast<ThemeRefContainer<Drawable>*>(GetAsset(npath).cptr); // link containers directly
return GetAsset(npath).GetShared(); // I guess this works; may need to be altered for asynchronity if I do that later return nulldrw; // doesn't really matter what's inside, it'll never get used
// (perhaps by--wait no, making it the same ThemeRefContainer would require a full rearchitecture of this part @.@)
} }
return nulldrw; return nulldrw;
} }
@ -236,6 +265,10 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
void ThemeManager::Fulfill(ThemeRefContainer<Font>& ref) { void ThemeManager::Fulfill(ThemeRefContainer<Font>& ref) {
string path = ResolveFontPath(ref.name); string path = ResolveFontPath(ref.name);
if (path == "") { // no fonts found, emergency fallback
ref.ptr = make_shared<starlight::gfx::FontNull>();
return;
}
auto font = make_shared<starlight::gfx::FontBMF>(); auto font = make_shared<starlight::gfx::FontBMF>();
{ // using: { // using:
json j; json j;
@ -258,54 +291,100 @@ void ThemeManager::LoadProc() {
} }
string ThemeManager::ResolveAssetPath(const string& id) { string ThemeManager::ResolveAssetPath(const string& id) {
//struct stat buf; string pfx = "";
//string path(id.length() + 64, ' '); // preallocate buffer space
static const string pfxLocal = "app:/"; size_t cpos = id.find(":/");
if (id.compare(0, pfxLocal.length(), pfxLocal) == 0) { if (cpos != string::npos) {
pfx = id.substr(0, cpos);
cpos += 2;
} else cpos = 0;
if (pfx == "app") {
string sid = id.substr(cpos); // strip off the "app:/"
// app-local asset // app-local asset
// check if present in theme/app/[appname]/, else check in romfs // check if present in theme/app/[appname]/, else check in romfs
for (auto thm : themeData) { for (auto thm : themeData) {
Path bp = thm.basePath.Combine("app").Combine(Application::AppName()); Path bp = thm.basePath.Combine("app").Combine(Application::AppName());
Path p = bp.Combine(id+".json"); Path p = bp.Combine(sid);
if (p.IsFile()) return p; if (p.IsFile()) return p;
p = bp.Combine(id+".png"); p = bp.Combine(sid+".json");
if (p.IsFile()) return p;
p = bp.Combine(sid+".png");
if (p.IsFile()) return p; if (p.IsFile()) return p;
} }
// TBD - directly in romfs, or in an assets folder? // TBD - directly in romfs, or in an assets folder?
Path bp = Path("romfs:"); Path bp = Path("romfs:/");
Path p = bp.Combine(id+".json"); Path p = bp.Combine(sid);
if (p.IsFile()) return p; if (p.IsFile()) return p;
p = bp.Combine(id+".png"); p = bp.Combine(sid+".json");
if (p.IsFile()) return p;
p = bp.Combine(sid+".png");
if (p.IsFile()) return p;
}
else if (pfx == "sdmc" || pfx == "romfs") {
Path p = Path(id);
if (p.IsFile()) return p;
p = Path(id + ".json");
if (p.IsFile()) return p;
p = Path(id + ".png");
if (p.IsFile()) return p; if (p.IsFile()) return p;
} }
else { else {
// theme asset; check in each theme from selected to most-fallback // theme asset; check in each theme from selected to most-fallback
for (auto thm : themeData) { for (auto thm : themeData) {
Path p = thm.basePath.Combine(id+".json"); Path p = thm.basePath.Combine(id);
if (p.IsFile()) return p;
p = thm.basePath.Combine(id+".json");
if (p.IsFile()) return p; if (p.IsFile()) return p;
p = thm.basePath.Combine(id+".png"); p = thm.basePath.Combine(id+".png");
if (p.IsFile()) return p; if (p.IsFile()) return p;
} }
} }
/*path.clear(); path.append("romfs:/"); path.append(id); path.append(".json");
printf("attempt: %s\n", path.c_str());
if (stat(path.c_str(), &buf) == 0) return path;
path.erase(path.end()-5, path.end()); path.append(".png");
printf("attempt: %s\n", path.c_str());
if (stat(path.c_str(), &buf) == 0) return path;//*/
return string(); return string();
} }
string ThemeManager::ResolveFontPath(const string& id) { // there we go, nice and simple string ThemeManager::ResolveFontPath(const string& id) { // there we go, nice and simple
for (auto thm : themeData) { string pfx = "";
Path p = thm.basePath.Combine("fonts").Combine(id+".json");
size_t cpos = id.find(":/");
if (cpos != string::npos) {
pfx = id.substr(0, cpos);
cpos += 2;
} else cpos = 0;
if (pfx == "app") {
string sid = id.substr(cpos); // strip off the "app:/"
// app-local asset
// check if present in theme/app/[appname]/fonts/, else check in romfs
for (auto thm : themeData) {
Path bp = thm.basePath.Combine("app").Combine(Application::AppName()).Combine("fonts");
Path p = bp.Combine(sid+".json");
if (p.IsFile()) return p;
}
// TBD - directly in romfs, or in an assets folder?
Path bp = Path("romfs:/fonts/");
Path p = bp.Combine(sid+".json");
if (p.IsFile()) return p; if (p.IsFile()) return p;
} }
else if (pfx == "sdmc" || pfx == "romfs") {
// no forced "fonts" here, of course; this is an actual path
Path p = Path(id + ".json");
if (p.IsFile()) return p;
}
else {
// theme asset; check in each theme from selected to most-fallback
for (auto thm : themeData) {
Path p = thm.basePath.Combine("fonts").Combine(id+".json");
if (p.IsFile()) return p;
}
}
return string(); // I guess fall back to 12px monospace if it's just nowhere to be found
const string fallback = "mono.12";
if (id != fallback) return ResolveFontPath(fallback);
return string(); // fallback not found; no themes on sdmc or romfs, probably
} }
json& ThemeManager::GetMetric(const string& path) { json& ThemeManager::GetMetric(const string& path) {

View File

@ -48,13 +48,15 @@ namespace starlight {
static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& ref); static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& ref);
static void Fulfill(gfx::ThemeRefContainer<gfx::Font>& ref); static void Fulfill(gfx::ThemeRefContainer<gfx::Font>& ref);
static std::shared_ptr<gfx::Drawable> LoadAsset(std::string& path); static std::shared_ptr<gfx::Drawable> LoadAsset(std::string& path, gfx::ThemeRefContainer<gfx::Drawable>& ref);
public: public:
ThemeManager() = delete; // "static" class ThemeManager() = delete; // "static" class
static void Init(); static void Init();
static void End(); static void End();
static void GC();
static gfx::ThemeRef<gfx::Drawable> GetAsset(const std::string& name); static gfx::ThemeRef<gfx::Drawable> GetAsset(const std::string& name);
static gfx::ThemeRef<gfx::Font> GetFont(const std::string& name); static gfx::ThemeRef<gfx::Font> GetFont(const std::string& name);
@ -85,6 +87,17 @@ namespace starlight {
TextConfig(const std::string& fontName, Color text, Color border = Color::transparent); TextConfig(const std::string& fontName, Color text, Color border = Color::transparent);
~TextConfig() = default; ~TextConfig() = default;
TextConfig(const TextConfig& o) : textColor(o.textColor), borderColor(o.borderColor), justification(o.justification) { font = o.font; }
TextConfig(const TextConfig&& o) : textColor(o.textColor), borderColor(o.borderColor), justification(o.justification) { font = o.font; }
TextConfig& operator =(const TextConfig& o) {
font = o.font; textColor = o.textColor; borderColor = o.borderColor; justification = o.justification;
return *this;
}
TextConfig& operator =(const TextConfig&& o) {
font = o.font; textColor = o.textColor; borderColor = o.borderColor; justification = o.justification;
return *this;
}
void Print(Vector2 position, const std::string& text, Vector2 justification = Vector2::invalid); void Print(Vector2 position, const std::string& text, Vector2 justification = Vector2::invalid);
void Print(VRect rect, const std::string& text, Vector2 justification = Vector2::invalid); void Print(VRect rect, const std::string& text, Vector2 justification = Vector2::invalid);

View File

@ -1,9 +1,13 @@
#include "Color.h" #include "Color.h"
#include <limits>
#include "starlight/_incLib/json.hpp" #include "starlight/_incLib/json.hpp"
using starlight::Color; using starlight::Color;
const Color Color::invalid = Color(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
const Color Color::transparent = Color(0.0f, 0.0f, 0.0f, 0.0f); const Color Color::transparent = Color(0.0f, 0.0f, 0.0f, 0.0f);
const Color Color::white = Color(1.0f, 1.0f, 1.0f); const Color Color::white = Color(1.0f, 1.0f, 1.0f);
const Color Color::black = Color(0.0f, 0.0f, 0.0f); const Color Color::black = Color(0.0f, 0.0f, 0.0f);

View File

@ -25,11 +25,17 @@ namespace starlight {
inline bool operator != (const Color& o) const { return r != o.r || g != o.g || b != o.b || a != o.a; } inline bool operator != (const Color& o) const { return r != o.r || g != o.g || b != o.b || a != o.a; }
inline Color operator * (const Color& o) const { return Color(r * o.r, g * o.g, b * o.b, a * o.a); } inline Color operator * (const Color& o) const { return Color(r * o.r, g * o.g, b * o.b, a * o.a); }
//inline Color operator * (const float m) const { return Color(r * m, g * m, b * m, a * m); }
// hmm. I guess this will do ¯\_(ツ)_/¯ don't really want to force cstdint // hmm. I guess this will do ¯\_(ツ)_/¯ don't really want to force cstdint
inline operator unsigned int() const { return (((((int)(a*255))&0xFF)<<24) | ((((int)(b*255))&0xFF)<<16) | ((((int)(g*255))&0xFF)<<8) | ((((int)(r*255))&0xFF)<<0)); } inline operator unsigned int() const { return (((((int)(a*255))&0xFF)<<24) | ((((int)(b*255))&0xFF)<<16) | ((((int)(g*255))&0xFF)<<8) | ((((int)(r*255))&0xFF)<<0)); }
// premult: inline operator unsigned int() const { return (((((int)(a*255))&0xFF)<<24) | ((((int)(a*b*255))&0xFF)<<16) | ((((int)(a*g*255))&0xFF)<<8) | ((((int)(a*r*255))&0xFF)<<0)); } // premult: inline operator unsigned int() const { return (((((int)(a*255))&0xFF)<<24) | ((((int)(a*b*255))&0xFF)<<16) | ((((int)(a*g*255))&0xFF)<<8) | ((((int)(a*r*255))&0xFF)<<0)); }
inline bool Valid() const { return a == a && r == r && g == g && b == b; }
inline explicit operator bool() const { return a == a && r == r && g == g && b == b; }
static const Color invalid;
static const Color transparent; static const Color transparent;
static const Color white; static const Color white;
static const Color black; static const Color black;

View File

@ -0,0 +1,64 @@
#pragma once
#include "starlight/_global.h"
#include <memory>
#include <functional>
namespace starlight {
template<typename T>
class Optional {
private:
std::unique_ptr<T> p = nullptr;
std::function<T&()>* getdef = nullptr;
inline void initp() {
if (!p) {
p = std::make_unique<T>();
if (getdef) *p = (*getdef)();
}
}
public:
Optional<T>() = default;
Optional<T>(std::function<T&()>* getDefault) : getdef(getDefault) { }
Optional<T>(nullptr_t) : p(nullptr) { }
Optional<T>(const Optional<T>& o) { // copy operator *actually copies the inner object*
if (o.p) {
p = std::make_unique<T>();
*p = *o.p;
}
getdef = o.getdef;
}
Optional<T>& operator=(const nullptr_t&) { p.reset(); return *this; }
Optional<T>& operator=(const T& o) { // assign by type's assignment operator if passed a "value"
if (!p) p = std::make_unique<T>();
*p = o;
return *this;
}
T& operator *() {
initp();
return *p;
}
T* operator ->() {
initp();
return &*p;
}
inline T& Get(T& defaultRef) {
if (p) return *p;
return defaultRef;
}
inline T& ROGet() {
if (p) return *p;
if (getdef) return (*getdef)();
static T fb; return fb; // meh, hackish but you shouldn't do this without a getdef anyway
// todo: clean this up somehow ^ (throw instead? or maybe have a static unique_ptr instead so we save memory when this is never called for a type)
}
//
};
}

View File

@ -1,5 +1,6 @@
#include <cstdlib> #include <cstdlib>
#include <cmath> #include <cmath>
#include <algorithm>
#include <fastmath.h> #include <fastmath.h>
#include <limits> #include <limits>
@ -11,6 +12,11 @@ using starlight::Vector2;
float Vector2::Length() const { return sqrtf(x * x + y * y); } float Vector2::Length() const { return sqrtf(x * x + y * y); }
Vector2 Vector2::Normalized() const { float m = Length(); return m == 0.0f ? Vector2::zero : Vector2(x / m, y / m); } Vector2 Vector2::Normalized() const { float m = Length(); return m == 0.0f ? Vector2::zero : Vector2(x / m, y / m); }
Vector2 Vector2::ClampLength(float max) const {
float len = Length();
return *this * (std::min(len, max) / len);
}
Vector2 Vector2::Reciprocal() const { return Vector2(y, x); } Vector2 Vector2::Reciprocal() const { return Vector2(y, x); }
Vector2 Vector2::IntSnap() const { return Vector2(roundf(x), roundf(y)); } Vector2 Vector2::IntSnap() const { return Vector2(roundf(x), roundf(y)); }

View File

@ -19,6 +19,8 @@ namespace starlight {
Vector2 Normalized() const; Vector2 Normalized() const;
inline float Area() const { return x * y; } inline float Area() const { return x * y; }
Vector2 ClampLength(float max = 1) const;
Vector2 Reciprocal() const; Vector2 Reciprocal() const;
Vector2 IntSnap() const; Vector2 IntSnap() const;
@ -47,7 +49,8 @@ namespace starlight {
inline Vector2 & operator += (const Vector2 & o) { x += o.x; y += o.y; return *this; } inline Vector2 & operator += (const Vector2 & o) { x += o.x; y += o.y; return *this; }
inline Vector2 & operator -= (const Vector2 & o) { x -= o.x; y -= o.y; return *this; } inline Vector2 & operator -= (const Vector2 & o) { x -= o.x; y -= o.y; return *this; }
inline Vector2 & operator *= (const Vector2 & o) { x *= o.x; y *= o.y; return *this; } inline Vector2 & operator *= (const Vector2 & o) { x *= o.x; y *= o.y; return *this; }
inline bool Valid() const { return x == x && y == y; }
inline explicit operator bool() const { return x == x && y == y; } inline explicit operator bool() const { return x == x && y == y; }
static const Vector2 invalid; static const Vector2 invalid;

View File

@ -37,6 +37,10 @@ namespace {
return tc; return tc;
} }
inline bool ShiftScroll(Keys k) {
return InputManager::Pressed(k) || (InputManager::Held(Keys::L | Keys::R) && InputManager::Held(k));
}
const constexpr float textHang = 4; const constexpr float textHang = 4;
} }
@ -44,14 +48,13 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
priority = 1000; // probably don't want all that much displaying above the keyboard priority = 1000; // probably don't want all that much displaying above the keyboard
handler->parent = this; handler->parent = this;
auto cover = std::make_shared<Image>(touchScreen->rect, "decorations/osk.background"); auto cover = touchScreen->AddNew<Image>(touchScreen->rect, "decorations/osk.background");
cover->blockTouch = true; cover->blockTouch = true;
touchScreen->Add(cover);
// wip // build keyboard
setContainer = std::make_shared<ui::UIContainer>(VRect::touchScreen); //setContainer = touchScreen->AddNew<ui::UIContainer>(VRect::touchScreen); // kept as a test case
touchScreen->Add(setContainer); setContainer = touchScreen->AddNew<ui::UICanvas>(VRect::touchScreen); // but this is much more efficient
auto actSym = [this](Button& key){ auto actSym = [this](Button& key){
this->handler->InputSymbol(key.label); this->handler->InputSymbol(key.label);
@ -74,18 +77,16 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
bpen = bpstart + Vector2(linestart[line] * bs.x, bs.y * line); bpen = bpstart + Vector2(linestart[line] * bs.x, bs.y * line);
} else { } else {
// lower // lower
auto key = std::make_shared<Button>(VRect(bpen, bs)); auto key = setContainer->AddNew<Button>(VRect(bpen, bs));
if (c == ' ') key->rect.size.x *= 6; if (c == ' ') key->rect.size.x *= 6;
key->SetText(string(1, c)); key->SetText(string(1, c));
key->eOnTap = actSym; key->eOnTap = actSym;
setContainer->Add(key);
// upper // upper
key = std::make_shared<Button>(VRect(bpen + Vector2(0, 1000), bs)); key = setContainer->AddNew<Button>(VRect(bpen + Vector2(0, 1000), bs));
if (C == ' ') key->rect.size.x *= 6; if (C == ' ') key->rect.size.x *= 6;
key->SetText(string(1, C)); key->SetText(string(1, C));
key->eOnTap = actSym; key->eOnTap = actSym;
setContainer->Add(key);
// and after // and after
bpen.x += key->rect.size.x; bpen.x += key->rect.size.x;
@ -94,26 +95,30 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
// backspace // backspace
bpen = bpstart + bs * Vector2(linestart[3] + 10, 3); bpen = bpstart + bs * Vector2(linestart[3] + 10, 3);
auto key = std::make_shared<Button>(VRect(bpen, bs)); auto key = touchScreen->AddNew<Button>(VRect(bpen, bs));
key->rect.size.x *= 1.25; key->rect.size.x *= 1.25;
key->SetText("< <"); key->style.glyph = ThemeManager::GetAsset("glyphs/backspace.small");
key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); }; key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); };
touchScreen->Add(key);
// enter // enter
bpen = bpstart + bs * Vector2(linestart[4] + 8, 4); bpen = bpstart + bs * Vector2(linestart[4] + 8, 4);
key = std::make_shared<Button>(VRect(bpen, bs)); key = touchScreen->AddNew<Button>(VRect(bpen, bs));
key->rect.size.x *= 2.5; key->rect.size.x *= 2.5;
key->SetText("Enter"); key->style.glyph = ThemeManager::GetAsset("glyphs/enter.large");
key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); }; key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); };
touchScreen->Add(key);
previewSc = std::make_shared<ScrollField>(VRect(VRect::touchScreen.TopEdge(66))); // shift
touchScreen->Add(previewSc); bpen = bpstart + bs * Vector2(linestart[0] + .25, 4);
key = touchScreen->AddNew<Button>(VRect(bpen, bs));
key->rect.size.x = bs.x * (linestart[4] - linestart[0] - .25);
key->style.glyph = ThemeManager::GetAsset("glyphs/shift.large");
key->eOnTap = [this](auto& btn){ this->shiftLock ^= true; };
shiftKey = key;
preview = std::make_shared<DrawLayerProxy>(VRect::touchScreen.TopEdge(66).Expand(-2, 0), [this](auto& layer){ this->DrawPreview(layer); }, true); previewSc = touchScreen->AddNew<ScrollField>(VRect(VRect::touchScreen.TopEdge(66)));
preview = previewSc->AddNew<DrawLayerProxy>(VRect::touchScreen.TopEdge(66).Expand(-2, 0), [this](auto& layer){ this->DrawPreview(layer); }, true);
preview->eOnTap = [this](auto& layer){ this->OnPreviewTap(layer); }; preview->eOnTap = [this](auto& layer){ this->OnPreviewTap(layer); };
previewSc->Add(preview);
RefreshPreview(); RefreshPreview();
} }
@ -124,46 +129,57 @@ void OSK::Update(bool focused) {
return; return;
} }
if (focused) { if (focused) {
if (InputManager::Pressed(Keys::B)) handler->Done(); if (InputManager::Pressed(Keys::B | Keys::Start)) handler->Done();
if (handler->showPreview) { if (handler->showPreview) {
if (InputManager::Pressed(Keys::DPadLeft)) { auto& tc = PreviewTC();
bool refresh = false;
if (ShiftScroll(Keys::DPadLeft)) {
auto c = handler->GetCursor(); auto c = handler->GetCursor();
if (c > 0) handler->SetCursor(c - 1); if (c > 0) handler->SetCursor(c - 1);
RefreshPreview(); refresh = true;
} }
if (InputManager::Pressed(Keys::DPadRight)) { if (ShiftScroll(Keys::DPadRight)) {
handler->SetCursor(handler->GetCursor() + 1); handler->SetCursor(handler->GetCursor() + 1);
RefreshPreview(); refresh = true;
} }
if (ShiftScroll(Keys::DPadUp)) {
auto& tc = PreviewTC();
if (InputManager::Pressed(Keys::DPadUp)) {
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
pt.y -= tc.Measure("|").y * 0.5f; pt.y -= tc.Measure("|").y * 0.5f;
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
RefreshPreview(); refresh = true;
} }
if (InputManager::Pressed(Keys::DPadDown)) { if (ShiftScroll(Keys::DPadDown)) {
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
pt.y += tc.Measure("|").y * 1.5f; pt.y += tc.Measure("|").y * 1.5f;
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
RefreshPreview(); refresh = true;
} }
if (refresh) RefreshPreview();
} }
float& s = setContainer->scrollOffset.y; float& s = setContainer->scrollOffset.y;
float ts = 0; float ts = 0;
if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) { if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) {
ts = 1000; ts = 1000;
} shiftLock = false;
} else if (shiftLock) ts = 1000;
if (s != ts) { if (s != ts) {
s = ts; s = ts;
setContainer->MarkForRedraw(); setContainer->MarkForRedraw();
if (ts > 0) {
static TextConfig stc = ThemeManager::GetMetric<starlight::TextConfig>("/dialogs/OSK/keyHighlight");
shiftKey->style.textConfig = stc;
} else {
shiftKey->style.textConfig = nullptr;
}
} }
} }
} }
void OSK::OnKey() { void OSK::OnKey() {
shiftLock = false;
RefreshPreview(); RefreshPreview();
} }

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "starlight/ui/Form.h" #include "starlight/ui/Form.h"
#include "starlight/ui/Button.h"
#include "starlight/ui/ScrollField.h" #include "starlight/ui/ScrollField.h"
#include "starlight/ui/DrawLayerProxy.h" #include "starlight/ui/DrawLayerProxy.h"
@ -19,7 +20,10 @@ namespace starlight {
std::shared_ptr<ui::ScrollField> previewSc; std::shared_ptr<ui::ScrollField> previewSc;
std::shared_ptr<ui::DrawLayerProxy> preview; std::shared_ptr<ui::DrawLayerProxy> preview;
std::shared_ptr<ui::Button> shiftKey;
//Vector2 cursorPos; //Vector2 cursorPos;
bool shiftLock = false;
public: public:
std::unique_ptr<osk::InputHandler> handler; std::unique_ptr<osk::InputHandler> handler;

View File

@ -45,7 +45,7 @@ void DrawContextCanvas::Clear(Color color) {
void DrawContextCanvas::Clear() { Clear(Color(0,0,0,0)); } void DrawContextCanvas::Clear() { Clear(Color(0,0,0,0)); }
// drawable stuff // drawable stuff
void DrawContextCanvas::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale) { void DrawContextCanvas::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
target->Bind(color ? color.get() : Color(1,1,1,1)); target->Bind(color ? color.get() : Color(1,1,1,1));
const VRect& sr = sampleRect ? sampleRect.get() : this->rect; const VRect& sr = sampleRect ? sampleRect.get() : this->rect;
@ -54,7 +54,7 @@ void DrawContextCanvas::Draw(const Vector2& position, const Vector2& origin, Opt
} }
} }
void DrawContextCanvas::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color) { void DrawContextCanvas::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
target->Bind(color ? color.get() : Color(1,1,1,1)); target->Bind(color ? color.get() : Color(1,1,1,1));
const VRect& sr = sampleRect ? sampleRect.get() : this->rect; const VRect& sr = sampleRect ? sampleRect.get() : this->rect;

View File

@ -23,8 +23,8 @@ namespace starlight {
DrawContextCanvas(Vector2 size); DrawContextCanvas(Vector2 size);
~DrawContextCanvas(); ~DrawContextCanvas();
void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) override; void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one, BlendMode mode = BlendMode::Normal) override;
void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) override; void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, BlendMode mode = BlendMode::Normal) override;
Vector2 Size() override; Vector2 Size() override;
@ -32,4 +32,4 @@ namespace starlight {
void Clear() override; void Clear() override;
}; };
} }
} }

View File

@ -7,6 +7,8 @@
#include "starlight/datatypes/OptRef.h" #include "starlight/datatypes/OptRef.h"
#include "starlight/gfx/Enums.h"
namespace starlight { namespace starlight {
namespace gfx { namespace gfx {
class Drawable { class Drawable {
@ -17,16 +19,14 @@ namespace starlight {
// pattern after: // pattern after:
// public abstract void Draw(DrawContext context, PxRect rect, PxRect? sampleRect = null, DrawColor? color = null); // public abstract void Draw(DrawContext context, PxRect rect, PxRect? sampleRect = null, DrawColor? color = null);
// public abstract void Draw(DrawContext context, FxVector position, FxVector? align = null, PxRect? sampleRect = null, DrawColor? color = null, float rotation = 0, FxVector? scale = null); // public abstract void Draw(DrawContext context, FxVector position, FxVector? align = null, PxRect? sampleRect = null, DrawColor? color = null, float rotation = 0, FxVector? scale = null);
virtual void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) = 0; virtual void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one, BlendMode mode = BlendMode::Normal) = 0;
void Draw(const Vector2& position, OptRef<Vector2> hotspot, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, float scale) { void Draw(const Vector2& position, OptRef<Vector2> hotspot, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, float scale, BlendMode mode = BlendMode::Normal) {
Draw(position, hotspot, sampleRect, color, rotation, Vector2(scale, scale)); Draw(position, hotspot, sampleRect, color, rotation, Vector2(scale, scale), mode);
} }
virtual void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) = 0; virtual void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, BlendMode mode = BlendMode::Normal) = 0;
virtual Vector2 Size() = 0; virtual Vector2 Size() = 0;
}; };
} }
} }

View File

@ -15,18 +15,18 @@ using starlight::gfx::DrawableImage;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
using starlight::gfx::CRenderTarget; using starlight::gfx::CRenderTarget;
void DrawableImage::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale) { void DrawableImage::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
texture->Bind(color ? color.get() : Color(1,1,1,1)); texture->Bind(color ? color.get() : Color(1,1,1,1), mode);
const VRect& sr = sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size); const VRect& sr = sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size);
VRect rect(position - origin * scale, sr.size * scale); VRect rect(position - (texture->size * origin) * scale, sr.size * scale);
RenderCore::DrawQuad(rect, position, rotation, sr / texture->txSize); RenderCore::DrawQuad(rect, position, rotation, sr / texture->txSize);
} }
} }
void DrawableImage::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color) { void DrawableImage::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
texture->Bind(color ? color.get() : Color(1,1,1,1)); texture->Bind(color ? color.get() : Color(1,1,1,1), mode);
const VRect& sr = sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size); const VRect& sr = sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size);
RenderCore::DrawQuad(rect, sr / texture->txSize); RenderCore::DrawQuad(rect, sr / texture->txSize);
} }

View File

@ -15,11 +15,10 @@ namespace starlight {
DrawableImage(CTexture* texture) : texture(texture) { } DrawableImage(CTexture* texture) : texture(texture) { }
~DrawableImage() override { } ~DrawableImage() override { }
void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) override; void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one, BlendMode mode = BlendMode::Normal) override;
void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) override; void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, BlendMode mode = BlendMode::Normal) override;
Vector2 Size() override; Vector2 Size() override;
}; };
} }
} }

View File

@ -15,9 +15,9 @@ using starlight::gfx::DrawableNinePatch;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
using starlight::gfx::CRenderTarget; using starlight::gfx::CRenderTarget;
void DrawableNinePatch::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color) { void DrawableNinePatch::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
texture->Bind(color ? color.get() : Color(1,1,1,1)); texture->Bind(color ? color.get() : Color(1,1,1,1), mode);
VRect rr = rect.IntSnap(); VRect rr = rect.IntSnap();
const VRect& sr = (sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size)).IntSnap(); const VRect& sr = (sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size)).IntSnap();

View File

@ -16,7 +16,7 @@ namespace starlight {
~DrawableNinePatch() override { } ~DrawableNinePatch() override { }
//void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) override; //void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) override;
void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) override; void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, BlendMode mode = BlendMode::Normal) override;
}; };
} }
} }

View File

@ -11,17 +11,16 @@ using starlight::gfx::DrawableTest;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
void DrawableTest::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale) { void DrawableTest::Draw(const Vector2& position, const Vector2& origin, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, const Vector2& scale, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
//static u32 col = Color(0, 0.5f, 1); //static u32 col = Color(0, 0.5f, 1);
//sf2d_draw_rectangle_rotate(position.x, position.y, 16, 16, col, rotation); //sf2d_draw_rectangle_rotate(position.x, position.y, 16, 16, col, rotation);
} }
} }
void DrawableTest::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color) { void DrawableTest::Draw(const VRect& rect, OptRef<VRect> sampleRect, OptRef<Color> color, BlendMode mode) {
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
RenderCore::BindColor(color ? color.get() : Color(1,1,1,1)); RenderCore::BindColor(color ? color.get() : Color(1,1,1,1), mode);
RenderCore::DrawQuad(rect, VRect()); RenderCore::DrawQuad(rect, VRect());
} }
} }

View File

@ -10,11 +10,10 @@ namespace starlight {
DrawableTest() { } DrawableTest() { }
~DrawableTest() override { } ~DrawableTest() override { }
void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one) override; void Draw(const Vector2& position, const Vector2& origin = Vector2::zero, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, float rotation = 0, const Vector2& scale = Vector2::one, BlendMode mode = BlendMode::Normal) override;
void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) override; void Draw(const VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr, BlendMode mode = BlendMode::Normal) override;
Vector2 Size() override { return Vector2::zero; } Vector2 Size() override { return Vector2::zero; }
}; };
} }
} }

View File

@ -0,0 +1,14 @@
#pragma once
#include "starlight/_global.h"
namespace starlight {
namespace gfx {
enum class BlendMode {
Blend,
Mask,
Replace,
Normal = Blend
};
}
}

View File

@ -14,8 +14,6 @@ namespace starlight {
namespace gfx { namespace gfx {
class Font { class Font {
public: public:
static constexpr const int defaultSize = 16;
Font() { } Font() { }
virtual ~Font() { } virtual ~Font() { }

View File

@ -0,0 +1,21 @@
#pragma once
#include "starlight/_global.h"
namespace starlight {
namespace gfx {
class FontNull : public Font {
public:
FontNull() { }
~FontNull() override { }
void Print(Vector2 position, const std::string& text, float scale = 1, Color color = Color::white, Vector2 justification = Vector2::zero, OptRef<Color> borderColor = nullptr) override {}
void Print(VRect rect, const std::string& text, float scale = 1, Color color = Color::white, Vector2 justification = Vector2::zero, OptRef<Color> borderColor = nullptr) override {}
Vector2 Measure(const std::string& text, float scale = 1, float maxWidth = 400) override { return Vector2::zero; }
Vector2 GetCursorPosition(VRect rect, const std::string& text, unsigned int end, float scale = 1) override { return Vector2::zero; }
unsigned int GetCursorFromPoint(VRect rect, const std::string& text, Vector2 pt, float scale = 1) override { return 0; }
};
}
}

View File

@ -16,6 +16,7 @@ using starlight::Vector2;
using starlight::VRect; using starlight::VRect;
using starlight::Color; using starlight::Color;
using starlight::util::WorkerThread; using starlight::util::WorkerThread;
using starlight::gfx::BlendMode;
using starlight::gfx::CTexture; using starlight::gfx::CTexture;
using starlight::gfx::CRenderTarget; using starlight::gfx::CRenderTarget;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
@ -24,41 +25,33 @@ namespace { // internals
typedef struct { typedef struct {
float x, y, z, u, v; float x, y, z, u, v;
} vbo_xyzuv; } vbo_xyzuv;
void setXYZUV(vbo_xyzuv& vbo, float x, float y, float z, float u, float v) {
vbo.x = x;
vbo.y = y;
vbo.z = z;
vbo.u = u;
vbo.v = v;
}
void setXYZUV(vbo_xyzuv& vbo, Vector2 xy, Vector2 uv) { setXYZUV(vbo, xy.x, xy.y, 0, uv.x, uv.y); }
void* bufferStart = nullptr; vbo_xyzuv* vboArray = nullptr;
size_t bufferInd = 0; size_t vboIndex = 0;
size_t bufferSize = 0; size_t vboSize = 0;
void addVertXYZUV(float x, float y, float z, float u, float v) {
// TODO: maybe add bounds check
vbo_xyzuv* vert = &vboArray[vboIndex++];
vert->x = x; vert->y = y; vert->z = z;
vert->u = u; vert->v = v;
}
void addVertXYZUV(Vector2 xy, Vector2 uv) { addVertXYZUV(xy.x, xy.y, 0, uv.x, uv.y); }
DVLB_s* dvlb = nullptr; DVLB_s* dvlb = nullptr;
shaderProgram_s shader; shaderProgram_s shader;
int sLocProjection = -1; int sLocProjection = -1;
void ResetBuffer() { bufferInd = 0; } C3D_AttrInfo* attrInfo = nullptr;
void* AllocBuffer(size_t size, size_t align = 1) { inline unsigned int NextPow2(unsigned int x) {
bufferInd += align - (bufferInd % align); // prealign
void* b = reinterpret_cast<void*>(reinterpret_cast<size_t>(bufferStart) + bufferInd);
bufferInd += size;
if (bufferInd > bufferSize) return nullptr;
return b;
}
inline int NextPow2(unsigned int x) {
--x; --x;
x |= x >> 1; x |= x >> 1;
x |= x >> 2; x |= x >> 2;
x |= x >> 4; x |= x >> 4;
x |= x >> 8; x |= x >> 8;
x |= x >> 16; x |= x >> 16;
return ++x >= 64 ? x : 64; // min size to keep gpu from locking return std::min(std::max(++x, 64U), 1024U); // clamp size to keep gpu from locking
} }
class CRawTexture : public CTexture { class CRawTexture : public CTexture {
@ -77,8 +70,8 @@ namespace { // internals
C3D_TexDelete(texture); C3D_TexDelete(texture);
delete texture; delete texture;
} }
void Bind(Color color = Color::white) override { void Bind(Color color = Color::white, BlendMode mode = BlendMode::Normal) override {
RenderCore::BindTexture(texture, color); RenderCore::BindTexture(texture, color, mode);
} }
}; };
} }
@ -94,9 +87,21 @@ void RenderCore::Open() {
gfxSet3D(true); gfxSet3D(true);
C3D_Init(0x80000*8); C3D_Init(0x80000*8);
bufferSize = 0x80000; // allocate and initialize VBO
bufferStart = linearAlloc(bufferSize); vboSize = 0x80000;
bufferInd = 0; void* vboAddr = linearAlloc(vboSize);
vboArray = reinterpret_cast<vbo_xyzuv*>(vboAddr);
vboIndex = 0;
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, vboAddr, sizeof(vbo_xyzuv), 2, 0x10);
// set up shader attribute passing
attrInfo = C3D_GetAttrInfo();
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
// set up screen targets // set up screen targets
targetTopLeft = std::make_unique<CRenderTarget>(240, 400, true); targetTopLeft = std::make_unique<CRenderTarget>(240, 400, true);
@ -119,7 +124,7 @@ void RenderCore::Open() {
// set up mode defaults // set up mode defaults
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // premult C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // premult
C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); // hmm. C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL); // hmm.
C3D_CullFace(GPU_CULL_NONE); C3D_CullFace(GPU_CULL_NONE);
} }
@ -129,45 +134,75 @@ void RenderCore::Close() {
targetTopRight.reset(nullptr); targetTopRight.reset(nullptr);
targetBottom.reset(nullptr); targetBottom.reset(nullptr);
linearFree(bufferStart); linearFree(reinterpret_cast<void*>(vboArray));
C3D_Fini(); C3D_Fini();
gfxExit(); gfxExit();
} }
void RenderCore::SyncFrame() {
C3D_FrameSync();
}
void RenderCore::BeginFrame() { void RenderCore::BeginFrame() {
ResetBuffer(); C3D_FrameBegin(0/*C3D_FRAME_SYNCDRAW*/);
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
vboIndex = 0;
} }
void RenderCore::EndFrame() { void RenderCore::EndFrame() {
C3D_FrameEnd(0); C3D_FrameEnd(0);
} }
void RenderCore::BindTexture(C3D_Tex* tex, const Color& color) { namespace {
void ApplyBlendMode(C3D_TexEnv* env, BlendMode mode) {
switch(mode) {
case BlendMode::Mask: // multiplies the buffer contents by the mask texture
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ZERO, GPU_SRC_COLOR, GPU_ZERO, GPU_SRC_ALPHA); // zero + (buffer * texel)
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0); // and the rest is the same as blend
C3D_TexEnvOp(env, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_A_SRC_ALPHA, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_MODULATE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
break;
case BlendMode::Replace:
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ZERO, GPU_ONE, GPU_ZERO); // flat replace
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0);
C3D_TexEnvOp(env, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_A_SRC_ALPHA, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_REPLACE);
break;
default:
case BlendMode::Blend:
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // premult
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0);
C3D_TexEnvOp(env, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_A_SRC_ALPHA, 0); // for color, the second op was 0... but that's the same value so whatever
C3D_TexEnvFunc(env, C3D_RGB, GPU_MODULATE); // and for color, this was REPLACE, not sure if that actually matters
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
break;
}
}
}
void RenderCore::BindTexture(C3D_Tex* tex, const Color& color, BlendMode mode) {
C3D_TexBind(0, tex); // 0 should be correct C3D_TexBind(0, tex); // 0 should be correct
C3D_TexEnv* env = C3D_GetTexEnv(0); C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0); C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0);
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0); ApplyBlendMode(env, mode);
C3D_TexEnvOp(env, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_A_SRC_ALPHA, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_MODULATE);//REPLACE); // let's see...
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
C3D_TexEnvColor(env, color.Premultiplied()); C3D_TexEnvColor(env, color.Premultiplied());
} }
void RenderCore::BindColor(const Color& color) { void RenderCore::BindColor(const Color& color, BlendMode mode) {
C3D_TexEnv* env = C3D_GetTexEnv(0); C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, 0, 0); C3D_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, 0, 0);
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0); ApplyBlendMode(env, mode);
C3D_TexEnvOp(env, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, 0, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);//REPLACE); // let's see...
C3D_TexEnvFunc(env, C3D_Alpha, GPU_REPLACE);
C3D_TexEnvColor(env, color.Premultiplied()); C3D_TexEnvColor(env, color.Premultiplied());
} }
void RenderCore::DrawQuad(const VRect& rect, const VRect& src, bool noSnap) { void RenderCore::DrawQuad(const VRect& rect, const VRect& src, bool noSnap) {
vbo_xyzuv* verts = static_cast<vbo_xyzuv*>(AllocBuffer(4 * sizeof(vbo_xyzuv), 8)); size_t vboNum = vboIndex;
VRect r = noSnap ? rect : rect.IntSnap(); // screen-space snap VRect r = noSnap ? rect : rect.IntSnap(); // screen-space snap
@ -179,41 +214,23 @@ void RenderCore::DrawQuad(const VRect& rect, const VRect& src, bool noSnap) {
// let's make this recalculate things a bit less // let's make this recalculate things a bit less
float rl = r.pos.x, rr = rl + r.size.x, rt = r.pos.y, rb = rt + r.size.y; float rl = r.pos.x, rr = rl + r.size.x, rt = r.pos.y, rb = rt + r.size.y;
float srl = src.pos.x, srr = srl + src.size.x, srt = src.pos.y, srb = srt + src.size.y; float srl = src.pos.x, srr = srl + src.size.x, srt = src.pos.y, srb = srt + src.size.y;
setXYZUV(verts[0], rl, rt, 0, srl, srt); addVertXYZUV(rl, rt, 0, srl, srt);
setXYZUV(verts[1], rr, rt, 0, srr, srt); addVertXYZUV(rr, rt, 0, srr, srt);
setXYZUV(verts[2], rl, rb, 0, srl, srb); addVertXYZUV(rl, rb, 0, srl, srb);
setXYZUV(verts[3], rr, rb, 0, srr, srb); addVertXYZUV(rr, rb, 0, srr, srb);
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); C3D_DrawArrays(GPU_TRIANGLE_STRIP, vboNum, 4);
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, verts, sizeof(vbo_xyzuv), 2, 0x10);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4);
} }
void RenderCore::DrawQuad(const VRect& rect, const Vector2& anchor, float angle, const VRect& src) { void RenderCore::DrawQuad(const VRect& rect, const Vector2& anchor, float angle, const VRect& src) {
vbo_xyzuv* verts = static_cast<vbo_xyzuv*>(AllocBuffer(4 * sizeof(vbo_xyzuv), 8)); size_t vboNum = vboIndex;
setXYZUV(verts[0], rect.TopLeft().RotateAround(anchor, angle), src.TopLeft()); addVertXYZUV(rect.TopLeft().RotateAround(anchor, angle), src.TopLeft());
setXYZUV(verts[1], rect.TopRight().RotateAround(anchor, angle), src.TopRight()); addVertXYZUV(rect.TopRight().RotateAround(anchor, angle), src.TopRight());
setXYZUV(verts[2], rect.BottomLeft().RotateAround(anchor, angle), src.BottomLeft()); addVertXYZUV(rect.BottomLeft().RotateAround(anchor, angle), src.BottomLeft());
setXYZUV(verts[3], rect.BottomRight().RotateAround(anchor, angle), src.BottomRight()); addVertXYZUV(rect.BottomRight().RotateAround(anchor, angle), src.BottomRight());
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); C3D_DrawArrays(GPU_TRIANGLE_STRIP, vboNum, 4);
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, verts, sizeof(vbo_xyzuv), 2, 0x10);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4);
} }
// specifically RGBA // specifically RGBA
@ -234,7 +251,7 @@ CTexture* RenderCore::LoadTexture(void* src, int width, int height) {
C3D_TexBind(0, tex->texture); C3D_TexBind(0, tex->texture);
printf("loaded image w %i (%i) h %i (%i)\n", width, owidth, height, oheight); //printf("loaded image w %i (%i) h %i (%i)\n", width, owidth, height, oheight);
return tex; return tex;
} }
@ -248,9 +265,22 @@ CRenderTarget::CRenderTarget(int width, int height, bool forceExact) {
auto w = forceExact ? width : NextPow2(width), auto w = forceExact ? width : NextPow2(width),
h = forceExact ? height : NextPow2(height); h = forceExact ? height : NextPow2(height);
txSize = Vector2(w, h); txSize = Vector2(w, h);
tgt = C3D_RenderTargetCreate(w, h, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); // though actually, do we really need stenciling? could drop to 16 if we don't
tgt = C3D_RenderTargetCreate(w, h, GPU_RB_RGBA8, -1/*GPU_RB_DEPTH24_STENCIL8*/); // I don't think we need a depth buffer >.>
//tgt = C3D_RenderTargetCreateFromTex(&tex, GPU_TEXFACE_2D, -1, -1); // create target from texture, actually
C3D_TexInit(&tex, w, h, GPU_RGBA8);
//memset(tex.data, 0, w*h*4); // manually zero out; TODO: optimize this
C3D_TexDelete(&tex); tex.data = tgt->frameBuf.colorBuf; // replace stuff...
Mtx_Ortho(&projection, 0.0f, w, 0.0f, h, 0.0f, 1.0f, true); Mtx_Ortho(&projection, 0.0f, w, 0.0f, h, 0.0f, 1.0f, true);
//Mtx_OrthoTilt(&projection, 0.0f, h, 0.0f, w, 0.0f, 1.0f, true); //Mtx_OrthoTilt(&projection, 0.0f, h, 0.0f, w, 0.0f, 1.0f, true);
//C3D_FrameBufClear(&(tgt->frameBuf), C3D_CLEAR_ALL, 0, 0);
//Clear(Color::transparent);
//RenderCore::BindTexture(&tex, Color::white);
//C3D_FrameBufClear(&(tgt->frameBuf), C3D_CLEAR_COLOR, 0, 0);
C3D_RenderTargetSetClear(tgt, static_cast<C3D_ClearBits>(0), 0, 0);
} }
CRenderTarget::~CRenderTarget() { CRenderTarget::~CRenderTarget() {
@ -258,17 +288,33 @@ CRenderTarget::~CRenderTarget() {
} }
void CRenderTarget::Clear(Color color) { void CRenderTarget::Clear(Color color) {
unsigned int c = color; //unsigned int c = color;
c = ((c>>24)&0x000000FF) | ((c>>8)&0x0000FF00) | ((c<<8)&0x00FF0000) | ((c<<24)&0xFF000000); // reverse endianness //c = ((c>>24)&0x000000FF) | ((c>>8)&0x0000FF00) | ((c<<8)&0x00FF0000) | ((c<<24)&0xFF000000); // reverse endianness
C3D_RenderTargetSetClear(tgt, C3D_CLEAR_ALL, c, 0); //C3D_RenderTargetSetClear(tgt, C3D_CLEAR_ALL, c, 0);
//C3D_FrameBufClear(&(tgt->frameBuf), C3D_CLEAR_COLOR, c, 0);
clearColor = color;
} }
void CRenderTarget::BindTarget() { void CRenderTarget::BindTarget() {
if (clearColor.Valid()) { // clear if color valid
unsigned int c = clearColor;
c = ((c>>24)&0x000000FF) | ((c>>8)&0x0000FF00) | ((c<<8)&0x00FF0000) | ((c<<24)&0xFF000000); // reverse endianness
//C3D_RenderTargetSetClear(tgt, static_cast<C3D_ClearBits>(0), c, 0);
//C3D_RenderTargetSetClear(tgt, C3D_CLEAR_ALL, c, 0);
C3D_FrameBufClear(&(tgt->frameBuf), C3D_CLEAR_COLOR, c, 0);
}
C3D_FrameDrawOn(tgt); C3D_FrameDrawOn(tgt);
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, sLocProjection, &projection); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, sLocProjection, &projection);
if (!firstClearDone) { firstClearDone = true; // workaround for faulty clearing; just draw a quad in replace mode to force the matter!
if (clearColor.Valid()) {
RenderCore::BindColor(clearColor, BlendMode::Replace);
RenderCore::DrawQuad(VRect(Vector2::zero, txSize), VRect::zero, false);
}
}
} }
void CRenderTarget::Bind(Color color) { void CRenderTarget::Bind(Color color, BlendMode mode) {
C3D_RenderTargetSetClear(tgt, 0, 0, 0); // don't clear again until marked to //C3D_RenderTargetSetClear(tgt, static_cast<C3D_ClearBits>(0), 0, 0); // don't clear again until marked to
RenderCore::BindTexture(&(tgt->renderBuf.colorBuf), color); RenderCore::BindTexture(&tex, color, mode);
} }

View File

@ -9,12 +9,13 @@
#include "starlight/datatypes/VRect.h" #include "starlight/datatypes/VRect.h"
#include "starlight/datatypes/Color.h" #include "starlight/datatypes/Color.h"
#include "starlight/gfx/Enums.h"
#include "starlight/util/WorkerThread.h" #include "starlight/util/WorkerThread.h"
namespace starlight { namespace starlight {
namespace gfx { namespace gfx {
class RenderCore; class RenderCore;
class CTexture { class CTexture {
protected: protected:
CTexture() = default; CTexture() = default;
@ -24,13 +25,16 @@ namespace starlight {
Vector2 txSize; Vector2 txSize;
virtual ~CTexture() = default; virtual ~CTexture() = default;
virtual void Bind(Color color = Color::white) = 0; virtual void Bind(Color color = Color::white, BlendMode mode = BlendMode::Normal) = 0;
}; };
class CRenderTarget : public CTexture { class CRenderTarget : public CTexture {
friend class starlight::gfx::RenderCore; friend class starlight::gfx::RenderCore;
protected: protected:
C3D_RenderTarget* tgt; C3D_RenderTarget* tgt;
C3D_Tex tex;
Color clearColor = Color::transparent;
bool firstClearDone = false;
public: public:
C3D_Mtx projection; C3D_Mtx projection;
@ -41,7 +45,7 @@ namespace starlight {
void Clear(Color color); void Clear(Color color);
void BindTarget(); void BindTarget();
void Bind(Color color = Color::white) override; void Bind(Color color = Color::white, BlendMode mode = BlendMode::Normal) override;
}; };
class RenderCore { class RenderCore {
@ -57,11 +61,12 @@ namespace starlight {
static void Open(); static void Open();
static void Close(); static void Close();
static void SyncFrame();
static void BeginFrame(); static void BeginFrame();
static void EndFrame(); static void EndFrame();
static void BindTexture(C3D_Tex* tex, const Color& color); static void BindTexture(C3D_Tex* tex, const Color& color, BlendMode mode = BlendMode::Normal);
static void BindColor(const Color& color); static void BindColor(const Color& color, BlendMode mode = BlendMode::Normal);
static void DrawQuad(const VRect& rect, const VRect& src, bool noSnap = false); static void DrawQuad(const VRect& rect, const VRect& src, bool noSnap = false);
static void DrawQuad(const VRect& rect, const Vector2& anchor, float angle, const VRect& src); static void DrawQuad(const VRect& rect, const Vector2& anchor, float angle, const VRect& src);

View File

@ -16,21 +16,30 @@ namespace starlight {
protected: protected:
const std::string name; const std::string name;
std::shared_ptr<T> ptr = nullptr; std::shared_ptr<T> ptr = nullptr;
void Unload() { ThemeRefContainer* redir = nullptr;
unsigned int lastAccess = 0; // how many gc sweeps since last use
volatile int refCount = 0;
void Unload(bool full = false) {
ptr.reset(); ptr.reset();
if (full) redir = nullptr;
} }
ThemeRefContainer(std::string name, std::shared_ptr<T> ptr) : name(name), ptr(ptr) { } ThemeRefContainer(std::string name, std::shared_ptr<T> ptr) : name(name), ptr(ptr) { }
ThemeRefContainer(std::string name, T* ptr) : name(name), ptr(ptr) { } ThemeRefContainer(std::string name, T* ptr) : name(name), ptr(ptr) { }
ThemeRefContainer(std::string name) : name(name) { } ThemeRefContainer(std::string name) : name(name) { }
inline std::shared_ptr<T>& _getptr() {
lastAccess = 0;
if (!redir && !ptr) ThemeManager::Fulfill(*this); // call thememanager to grab things
if (redir) return redir->_getptr();
return ptr;
}
inline std::shared_ptr<T>& getptr() const { return const_cast<ThemeRefContainer<T>&>(*this)._getptr(); }
public: public:
~ThemeRefContainer() { } ~ThemeRefContainer() { }
T* operator ->() const { T* operator ->() const {
if (ptr == nullptr) { return &*(getptr());
ThemeManager::Fulfill(const_cast<ThemeRefContainer<T>&>(*this)); // call thememanager to grab things
}
return &*ptr;
} }
/*T& operator *() const { /*T& operator *() const {
@ -44,15 +53,32 @@ namespace starlight {
template <class T> template <class T>
class ThemeRef { class ThemeRef {
private: friend class starlight::ThemeManager;
const ThemeRefContainer<T>* cptr; protected:
ThemeRefContainer<T>* cptr;
public: public:
ThemeRef() : cptr(nullptr) { } ThemeRef() : cptr(nullptr) { }
ThemeRef(ThemeRefContainer<T>* c) : cptr(c) { } ThemeRef(ThemeRefContainer<T>* c) : cptr(c) { if (cptr) cptr->refCount++; }
~ThemeRef() { } ThemeRef(ThemeRef<T>& o) : cptr(o.cptr) { if (cptr) cptr->refCount++; }
inline const ThemeRefContainer<T>& operator ->() const { return *cptr; } ThemeRef(ThemeRef<T>&& o) : cptr(o.cptr) { if (cptr) cptr->refCount++; }
~ThemeRef() { if (cptr) cptr->refCount--; }
ThemeRef<T>& operator =(const ThemeRef<T>& o) {
if (cptr) cptr->refCount--;
cptr = o.cptr;
if (cptr) cptr->refCount++;
return *this;
}
ThemeRef<T>& operator =(const ThemeRef<T>&& o) {
if (cptr) cptr->refCount--;
cptr = o.cptr;
if (cptr) cptr->refCount++;
return *this;
}
inline const ThemeRefContainer<T>& operator ->() const { return const_cast<const ThemeRefContainer<T>&>(*cptr); }
inline explicit operator bool() const { return cptr != nullptr; } inline explicit operator bool() const { return cptr != nullptr; }
inline std::shared_ptr<T> GetShared() const { return (*cptr).ptr; } inline std::shared_ptr<T> GetShared() const { return (*cptr).getptr(); }
inline const std::string& GetName() const { return (*cptr).name; } inline const std::string& GetName() const { return (*cptr).name; }
}; };
} }

View File

@ -0,0 +1,77 @@
#include "Thread.h"
#include "3ds.h"
#include "starlight/Application.h"
using starlight::threading::Thread;
using SysThread = ::Thread;
using SThread = starlight::threading::Thread;
namespace {
void _ThreadEnter(void* arg) {
// cast to thread and start up
static_cast<SThread*>(arg)->_FinishStart();
}
}
Thread::~Thread() {
// ...threadjoin? something like that??
if (event != 0) svcCloseHandle(event);
}
void Thread::Enqueue() {
if (state != ThreadState::Unqueued) return; // don't double enqueue, you derp
Application::Current()->EnqueueThread(shared_from_this());
}
void Thread::Start() {
state = ThreadState::Init;
svcCreateEvent(&event, RESET_ONESHOT);
sthread = static_cast<void*>(threadCreate(_ThreadEnter, static_cast<void*>(this), 4*1024, 0x3F, -2, false));
}
void Thread::_FinishStart() {
// lock out once already done
if (state != ThreadState::Init) return;
state = ThreadState::Running;
Yield();
Body();
state = ThreadState::Finished;
OnExit();
threadExit(0);
}
void Thread::Yield() {
if (state != ThreadState::Running) return; // not executing this right now, this would just futz it up
state = ThreadState::Idle;
svcWaitSynchronization(event, -1 /*U64_MAX*/);
//svcWaitSynchronization(event, 65536);
svcClearEvent(event);
if (state == ThreadState::Finished && OnExit()) {
threadExit(0);
}
state = ThreadState::Running;
}
void Thread::Exit() {
if (state == ThreadState::Idle) { // exited from outside
state = ThreadState::Finished;
Resume();
threadJoin(static_cast<SysThread>(sthread), -1);
} else if (state == ThreadState::Running) { // exited self
state = ThreadState::Finished;
OnExit();
threadExit(0);
}
}
void Thread::Resume() {
if (state != ThreadState::Idle && state != ThreadState::Finished) return; // not applicable
svcSignalEvent(event);
}
bool Thread::OnExit() { return true; } // default to "trivial" (no cleanup necessary)
//

View File

@ -0,0 +1,39 @@
#pragma once
#include "starlight/_global.h"
#include <memory>
namespace starlight {
class Application;
namespace threading {
enum class ThreadState : unsigned char {
Unqueued, Init, Idle, Running, Finished
};
class Thread : public std::enable_shared_from_this<Thread> {
friend class starlight::Application;
protected:
volatile ThreadState state = ThreadState::Unqueued;
void* sthread;
long unsigned int event = 0;
void Start();
public:
~Thread();
inline ThreadState State() { return state; }
void _FinishStart();
void Enqueue();
void Yield();
void Resume();
void Exit();
virtual void Body() = 0;
virtual bool OnExit();
};
}
}

View File

@ -15,8 +15,15 @@ using starlight::InputManager;
using starlight::GFXManager; using starlight::GFXManager;
using starlight::ThemeManager; using starlight::ThemeManager;
using starlight::TextConfig;
using starlight::ui::Button; using starlight::ui::Button;
std::function<TextConfig&()> Button::defCfg = []() -> TextConfig& {
static TextConfig _tc = ThemeManager::GetMetric("/controls/button/text", TextConfig());
return _tc;
};
void Button::SetText(const std::string& text) { void Button::SetText(const std::string& text) {
label = text; label = text;
MarkForRedraw(); MarkForRedraw();
@ -28,18 +35,18 @@ void Button::Draw() {
static auto idle = ThemeManager::GetAsset("controls/button.idle"); static auto idle = ThemeManager::GetAsset("controls/button.idle");
static auto press = ThemeManager::GetAsset("controls/button.press"); static auto press = ThemeManager::GetAsset("controls/button.press");
static TextConfig tc = ThemeManager::GetMetric("/controls/button/text", TextConfig()); TextConfig& tc = style.textConfig.ROGet();
auto rect = (this->rect + GFXManager::GetOffset()).IntSnap(); auto rect = (this->rect + GFXManager::GetOffset()).IntSnap();
if (InputManager::GetDragHandle() == this) { if (InputManager::GetDragHandle() == this) {
press->Draw(rect); (style.press ? style.press : press)->Draw(rect);
} else { } else {
idle->Draw(rect); (style.idle ? style.idle : idle)->Draw(rect);
} }
//font->Print(rect, label, 1, cl/*Color::white*/, Vector2(0.5f, 0.5f), Color::black);
tc.Print(rect, label); tc.Print(rect, label);
if (style.glyph) style.glyph->Draw(rect.Center(), Vector2::half, nullptr, tc.textColor);
} }
void Button::OnTouchOn() { void Button::OnTouchOn() {

View File

@ -4,15 +4,30 @@
#include <string> #include <string>
#include <functional> #include <functional>
#include "starlight/datatypes/Optional.h"
#include "starlight/ThemeManager.h"
#include "starlight/gfx/ThemeRef.h"
#include "starlight/ui/UIElement.h" #include "starlight/ui/UIElement.h"
namespace starlight { namespace starlight {
namespace ui { namespace ui {
class Button : public UIElement { class Button : public UIElement {
public:
struct Style {
gfx::ThemeRef<gfx::Drawable>
idle = nullptr,
press = nullptr,
glyph = nullptr;
Optional<TextConfig> textConfig = &Button::defCfg;
};
private: private:
// static std::function<TextConfig&()> defCfg;
public: public:
Style style;
std::string label = ""; std::string label = "";
std::function<void(Button&)> eOnTap; std::function<void(Button&)> eOnTap;

View File

@ -0,0 +1,102 @@
#include "DebugConsole.h"
#include "starlight/GFXManager.h"
#include "starlight/gfx/RenderCore.h"
#include "sys/iosupport.h"
using starlight::GFXManager;
using starlight::TextConfig;
using starlight::gfx::RenderCore;
using starlight::ui::DebugConsole;
namespace {
bool csInit = false;
//std::weak_ptr<DebugConsole> curDC = std::shared_ptr<DebugConsole>(nullptr);
DebugConsole* cs = nullptr;
ssize_t consoleWrite(struct _reent* r, void* fd, const char* ptr, size_t len) {
if (!ptr) return -1;
//if (curDC.expired()) return -1;
//auto cs = curDC.lock();
if (cs == nullptr) return -1; // nullref but not expired???
cs->text.append(ptr, len);
cs->dirty = true;
return len;
}
const devoptab_t devoptab_console = {
"con",
0,
NULL,
NULL,
consoleWrite,
NULL,
NULL,
NULL
};
}
DebugConsole::DebugConsole(VRect rect) {
this->rect = rect;
}
DebugConsole::~DebugConsole() {
if (cs == this) cs = nullptr;
}
void DebugConsole::Start() {
//curDC = std::static_pointer_cast<DebugConsole>(shared_from_this());
cs = this;
if (!csInit) {
csInit = true;
devoptab_list[STD_OUT] = &devoptab_console;
devoptab_list[STD_ERR] = &devoptab_console;
setvbuf(stdout, NULL , _IONBF, 0);
setvbuf(stderr, NULL , _IONBF, 0);
}
}
void DebugConsole::PreDrawOffscreen() {
buffer.reset(); // I guess?
}
void DebugConsole::PreDraw() {
if (dirty || !buffer) {
dirty = false;
static TextConfig textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/normal.12", TextConfig());
textConfig.font = ThemeManager::GetFont("mono.12");
textConfig.justification = Vector2(0, 1);
textConfig.borderColor = Color::black;
// clip text at top left corner
Vector2 measure = textConfig.Measure(text, rect.size.x);
if (measure.y > rect.size.y) {
unsigned int cfp = textConfig.GetCursorFromPoint(rect, text, Vector2(0, measure.y - (rect.size.y + 16)));
text = text.substr(cfp);
}
if (!buffer) buffer = std::make_unique<gfx::DrawContextCanvas>(rect.size + Vector2(0, 8));
buffer->Clear();
GFXManager::PushContext(buffer.get());
GFXManager::PrepareForDrawing(); // force clear even if nothing to write
textConfig.Print(buffer->rect, text);
GFXManager::PopContext();
}
}
void DebugConsole::Draw() {
auto rect = (this->rect + GFXManager::GetOffset()).IntSnap();
if (buffer) {
buffer->Draw(VRect(rect.pos, buffer->rect.size));
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "starlight/_global.h"
#include <string>
#include <memory>
#include <functional>
#include "starlight/datatypes/Optional.h"
#include "starlight/ThemeManager.h"
#include "starlight/gfx/ThemeRef.h"
#include "starlight/gfx/DrawContextCanvas.h"
#include "starlight/ui/UIElement.h"
namespace starlight {
namespace ui {
class DebugConsole : public UIElement {
private:
//
public:
std::string text = "";
std::unique_ptr<gfx::DrawContextCanvas> buffer;
bool dirty = false;
DebugConsole(VRect rect);
~DebugConsole() override;
void Start();
void PreDrawOffscreen() override;
void PreDraw() override;
void Draw() override;
};
}
}

View File

@ -3,17 +3,22 @@
#include "starlight/GFXManager.h" #include "starlight/GFXManager.h"
using starlight::GFXManager; using starlight::GFXManager;
using starlight::TextConfig;
using starlight::ui::Label; using starlight::ui::Label;
std::function<TextConfig&()> Label::defCfg = []() -> TextConfig& {
static TextConfig _tc = ThemeManager::GetMetric<TextConfig>("/textPresets/normal.12", TextConfig());
return _tc;
};
Label::Label(VRect rect) { Label::Label(VRect rect) {
this->rect = rect; this->rect = rect;
textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/normal.12", TextConfig());
} }
void Label::AutoSize() { void Label::AutoSize() {
if (autoSizeV) { if (autoSizeV) {
float h = textConfig.font->Measure(text, 1, rect.size.x).y; float h = textConfig.ROGet().font->Measure(text, 1, rect.size.x).y;
Resize(rect.size.x, h); Resize(rect.size.x, h);
} }
@ -28,12 +33,13 @@ void Label::SetText(const std::string& text) {
} }
void Label::SetFont(const std::string& fontName) { void Label::SetFont(const std::string& fontName) {
textConfig.font = ThemeManager::GetFont(fontName); textConfig->font = ThemeManager::GetFont(fontName);
textConfig->Measure(""); // force load
AutoSize(); AutoSize();
} }
void Label::SetPreset(const std::string& name) { void Label::SetPreset(const std::string& name) {
textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/" + name, textConfig); textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/" + name, textConfig.ROGet());
AutoSize(); AutoSize();
} }
@ -48,7 +54,8 @@ void Label::PreDraw() {
buffer = std::make_unique<gfx::DrawContextCanvas>(rect.size + Vector2(0, 8)); buffer = std::make_unique<gfx::DrawContextCanvas>(rect.size + Vector2(0, 8));
buffer->Clear(); buffer->Clear();
GFXManager::PushContext(buffer.get()); GFXManager::PushContext(buffer.get());
textConfig.Print(buffer->rect, text); GFXManager::PrepareForDrawing(); // force clear even if nothing to write
textConfig.ROGet().Print(buffer->rect, text);
GFXManager::PopContext(); GFXManager::PopContext();
} }
} }
@ -58,6 +65,6 @@ void Label::Draw() {
if (buffer) { if (buffer) {
buffer->Draw(VRect(rect.pos, buffer->rect.size)); buffer->Draw(VRect(rect.pos, buffer->rect.size));
} else { } else {
textConfig.Print(rect, text); textConfig.ROGet().Print(rect, text);
} }
} }

View File

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include "starlight/datatypes/Optional.h"
#include "starlight/ThemeManager.h" #include "starlight/ThemeManager.h"
#include "starlight/gfx/ThemeRef.h" #include "starlight/gfx/ThemeRef.h"
@ -15,11 +17,13 @@ namespace starlight {
namespace ui { namespace ui {
class Label : public UIElement { class Label : public UIElement {
private: private:
static std::function<TextConfig&()> defCfg;
void AutoSize(); void AutoSize();
public: public:
std::string text = ""; std::string text = "";
TextConfig textConfig; Optional<TextConfig> textConfig = &defCfg;
std::unique_ptr<gfx::DrawContextCanvas> buffer; std::unique_ptr<gfx::DrawContextCanvas> buffer;

View File

@ -35,6 +35,7 @@ void UICanvas::PreDraw() {
drawContext->Clear(); drawContext->Clear();
GFXManager::PushContext(drawContext.get()); GFXManager::PushContext(drawContext.get());
GFXManager::PushOffsetAdd(-scrollOffset); GFXManager::PushOffsetAdd(-scrollOffset);
GFXManager::PrepareForDrawing(); // force clear to take so you don't get garbage if nothing renders
VRect vr = ViewportRect(); VRect vr = ViewportRect();
@ -51,4 +52,3 @@ void UICanvas::PreDraw() {
void UICanvas::Draw() { void UICanvas::Draw() {
static_cast<DrawContextCanvas*>(drawContext.get())->Draw(rect + GFXManager::GetOffset()); static_cast<DrawContextCanvas*>(drawContext.get())->Draw(rect + GFXManager::GetOffset());
} }

View File

@ -47,9 +47,9 @@ void UIContainer::_Dive(std::function<bool(UIElement*)>& func, bool consumable,
} }
void UIContainer::Add(std::shared_ptr<UIElement> elem, bool front) { void UIContainer::Add(std::shared_ptr<UIElement> elem, bool front) {
if (front) children.push_front(elem);
else children.push_back(elem);
elem->parent = std::weak_ptr<UIContainer>(std::static_pointer_cast<UIContainer>(this->shared_from_this())); elem->parent = std::weak_ptr<UIContainer>(std::static_pointer_cast<UIContainer>(this->shared_from_this()));
if (front) children.push_front(std::move(elem));
else children.push_back(std::move(elem));
MarkForRedraw(); MarkForRedraw();
} }
//void UIContainer::Add(UIElement* elem) { //void UIContainer::Add(UIElement* elem) {
@ -71,6 +71,7 @@ void UIContainer::RemoveAll() {
it->parent = std::weak_ptr<UIContainer>(); // clear parent it->parent = std::weak_ptr<UIContainer>(); // clear parent
} }
children.clear(); children.clear();
MarkForRedraw();
} }
void UIContainer::Update() { void UIContainer::Update() {

View File

@ -35,6 +35,11 @@ namespace starlight {
void Dive(std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true); void Dive(std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true);
void Add(std::shared_ptr<UIElement> elem, bool front = false); void Add(std::shared_ptr<UIElement> elem, bool front = false);
template<class E, typename ... Args>
inline std::shared_ptr<E> AddNew(Args... args) {
auto n = std::make_shared<E>(args...);
Add(n); return n;
}
//void Add(UIElement* elem); //void Add(UIElement* elem);
void Remove(std::shared_ptr<UIElement> elem); void Remove(std::shared_ptr<UIElement> elem);
void RemoveAll(); void RemoveAll();

View File

@ -0,0 +1,19 @@
#include "FrameTimer.h"
#include "3ds.h"
using starlight::util::FrameTimer;
FrameTimer::FrameTimer() {
osTickCounterStart(reinterpret_cast<TickCounter*>(&tc));
}
void FrameTimer::FrameStart() {
osTickCounterUpdate(reinterpret_cast<TickCounter*>(&tc));
}
double FrameTimer::GetSubframe() {
TickCounter tmp = *(reinterpret_cast<TickCounter*>(&tc));
osTickCounterUpdate(&tmp);
return osTickCounterRead(&tmp) * (60.0/1000.0);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "starlight/_global.h"
namespace starlight {
namespace util {
class FrameTimer {
private:
struct TickCount {
unsigned long long int elapsed;
unsigned long long int ref;
};
TickCount tc;
public:
FrameTimer();
void FrameStart();
double GetSubframe();
};
}
}

View File

@ -0,0 +1,19 @@
#include "Profiler.h"
#include <cstdio>
#include "3ds.h"
using starlight::util::Profiler;
Profiler::TickCount Profiler::tc = Profiler::TickCount();
void Profiler::TaskStart() {
osTickCounterUpdate(reinterpret_cast<TickCounter*>(&tc));
}
void Profiler::TaskFinish(const std::string& msg) {
osTickCounterUpdate(reinterpret_cast<TickCounter*>(&tc));
double tm = osTickCounterRead(reinterpret_cast<TickCounter*>(&tc));
printf("T:%f - %s\n", tm, msg.c_str());
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "starlight/_global.h"
#include <string>
namespace starlight {
namespace util {
class Profiler {
private:
struct TickCount {
unsigned long long int elapsed;
unsigned long long int ref;
};
static TickCount tc;
Profiler() {};
public:
static void TaskStart();
static void TaskFinish(const std::string& msg);
};
}
}

View File

@ -1,18 +1,89 @@
roadmap to first release, in no particular order { roadmap to v0.5.1 {
- ding! - make font-not-found not outright crash the app (hopefully)
- fix UICanvas garbage-on-empty
make asset gc actually sweep every 5sec
maybe entirely replace clearing with transparent knockout on bind?
implement more blend modes??
make button glyphs usable in conjunction with text (left edge, margin etc.)
give glyphs a color mode where they aren't set to text color
predrawoffscreen on hidden forms
paged field
figure out why first-draw in a given font sometimes derps up?
^ mitigate with a force-load-on-set for now
- libctru console as ui element
- pngcrush the biggest assets (default and osk backdrops etc.)
- profile image loading (for large images, loading the png and spanned copy/premult take about the same time; memset to preclear is only ~2ms with a 512x512)
- fix the hang on osk when pressing (L|R)+up+left
figure out what (else) to put on the left side of the keyboard (opposite backspace and enter)
temporary drawable loading, local themeref, discard etc.
^ both png and raw load
maybe rgb565 for smdh icon loading?
some examples (minesweeper?)
fix `, ' and " glyph spacing/offset
adjust /\ some?
proper thread dispatch? {
- Application main loop keeps a(n abstracted) libctru TickCounter and keeps track of frame time
- thread objects are held in a std::list, Application dispatches resume events and splice()s them to the end until frame time reaches some proportion of 1/60s
- thread gets a yield function that calls svcWaitSynchronization on its resume event
...and some mechanism for allowing it to opt out of the rest of the cycle
Trackable sideclass for threads; float progress 0..1, etc.
^ make progress bar and use it for a progress/"please wait" dialog
- MAKE THREADS END CLEANLY
^ observed a single instance of being stalled on redscreen, not really sure what that was about
lambda task thread
}
} then by v0.5.5 {
refactor ResolveAsset/FontPath to use Path objects for cleaner code
event propagation system of some sort; threadsafe to whatever extent is needed on 3DS
figure out how to *actually* fix the clear bug...?
some sort of tagging for longer retention for large drawables such as backgrounds (particularly for the OSK)
convert the profiler from "single static thing" to "here, have an instance on the stack"
} then consider these before 1.0 "gold" { } then consider these before 1.0 "gold" {
replace some of the OptRef stuffs on invalidatable types with invalid checks; add implicit conversions from nullptr to invalid
make closing forms a bit less finicky (add them to a separate list and let the Application remove them from the list) make closing forms a bit less finicky (add them to a separate list and let the Application remove them from the list)
add customization for Button (alternate idle/press images, optional glyph drawable) garbage collection for not-recently-used theme assets {
^ use that to spice up the OSK - keep track of last-use in ThemeRefContainer
have ThemeManager sweep gc every so often
- rework redirects (proxy drawable I guess...?) or a pointer to another container
}
HANDLE CANVAS OVERRUNS FOR LABELS AND OSK PREVIEW {
- well, it doesn't actually *crash* anymore... or at least nowhere near as fast
use a "windowed" approach; only cache a bit more than is visible and redraw when the viewport changes enough
have a way for text rendering to skip lines that won't be visible, and use that with windowing to cut down render times further
}
actual cursor image for OSK instead of just using a | glypyh actual cursor image for OSK instead of just using a | glypyh
input prompt dialog input prompt dialog
make the panel background not just the button image
"shortcut" overloads for InputManager::OpenKeyboard "shortcut" overloads for InputManager::OpenKeyboard
language config and atlas support language config and atlas support
maybe implement some way of "knocking out" and replacing metrics during runtime for theme switching maybe implement some way of "knocking out" and replacing metrics during runtime for theme switching
fix font glyph padding to eliminate slight "crosstalk" in bordered variants fix font glyph padding to eliminate slight "crosstalk" in bordered variants
SOUND.
UI heirarchy from json
quick includes for all UI elements, etc.
cross-app integrations {
app icon and manifest.json in romfs, copied into .starlight on launch {
app name, description etc.
themeLevel: 0 (default) as fallback only, 1 or 2 to override user theme unless opted out, 1 for "just by default", 2 for "this doesn't look so good otherwise"
}
settings pane data for each app, contained in manifest.json and used by Starlight Settings to provide centralized configuration, iOS-style
some standard means of passing parameters into another application, which works on a cia (probably a json file)
}
} }
today's agenda { today's agenda {

4
maketest.sh Normal file → Executable file
View File

@ -4,7 +4,9 @@ function abort {
exit exit
} }
mode=send mode=send
if [ "$1" = "c" ]; then if [ "$1" = "sc" ]; then
mode=send-cia
elif [ "$1" = "c" ]; then
mode=run mode=run
fi fi
cd libstarlight cd libstarlight

View File

@ -44,6 +44,8 @@ APP_TITLE := Starlight Testbed
APP_DESCRIPTION := Test application for libstarlight APP_DESCRIPTION := Test application for libstarlight
APP_AUTHOR := zetaPRIME APP_AUTHOR := zetaPRIME
3DSIP := 10.0.0.6
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -56,7 +58,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations \
CFLAGS += $(INCLUDE) -DARM11 -D_3DS CFLAGS += $(INCLUDE) -DARM11 -D_3DS
# was gnu++11; -fno-rtti -fno-exceptions (why no-exceptions???) # was gnu++11; -fno-rtti -fno-exceptions (why no-exceptions???)
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++14 CXXFLAGS := $(CFLAGS) -fno-rtti -std=c++17
# on second thought, let's not use -D_GLIBCXX_USE_C99 # on second thought, let's not use -D_GLIBCXX_USE_C99
#CXXFLAGS := $(CFLAGS) -std=gnu++14 #CXXFLAGS := $(CFLAGS) -std=gnu++14
@ -87,6 +89,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) $(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD) export DEPSDIR := $(CURDIR)/$(BUILD)
export ROMFS_ROOT := $(CURDIR)/$(ROMFS)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
@ -162,14 +165,19 @@ cci: $(TARGET)-strip.elf
@echo "built ... sf2d_sample.3ds" @echo "built ... sf2d_sample.3ds"
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
cia: $(TARGET)-strip.elf cia: $(TARGET)-strip.elf
@makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -icon resources/icon.icn -banner resources/banner.bnr -exefslogo -target t @makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -icon $(TARGET).smdh -banner resources/banner.bnr -logo resources/logo.bcma.lz -exefslogo -target t
@echo "built ... $(TARGET).cia" @echo "built ... $(TARGET).cia"
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
send: $(BUILD) send: $(BUILD)
@3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx #@3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx || 3dslink $(TARGET).3dsx
@while true; do 3dslink -a $(3DSIP) $(TARGET).3dsx && break; done
#---------------------------------------------------------------------------------
send-cia: $(TARGET)-strip.elf
@makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -icon $(TARGET).smdh -banner resources/banner.bnr -logo resources/logo.bcma.lz -exefslogo -target t
@sockme $(TARGET).cia $(3DSIP) || sockme $(TARGET).cia $(3DSIP) || sockme $(TARGET).cia $(3DSIP)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
run: $(BUILD) run: $(BUILD)
@citra $(TARGET).3dsx @citra-qt $(TARGET).3dsx
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
copy_cia: $(TARGET).cia copy_cia: $(TARGET).cia
@cp $(TARGET).cia /mnt/3DS @cp $(TARGET).cia /mnt/3DS

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

View File

@ -0,0 +1,242 @@
BasicInfo:
Title : "starlight-testbed"
CompanyCode : "00"
ProductCode : "CTR-N-SLTB"
ContentType : Application # Application / SystemUpdate / Manual / Child / Trial
Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem
RomFs:
# Specifies the root path of the file system to include in the ROM.
RootPath : "../themes/default"
TitleInfo:
UniqueId : 0xf1001 # same as sf2d test because meh
Category : Application # Application / SystemApplication / Applet / Firmware / Base / DlpChild / Demo / Contents / SystemContents / SharedContents / AddOnContents / Patch / AutoUpdateContents
#CardInfo:
# MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB / 8GB / 16GB / 32GB
# MediaType : Card1 # Card1 / Card2
# CardDevice : None # NorFlash / None
Option:
UseOnSD : true # true if App is to be installed to SD
EnableCompress : false # Compresses exefs code
FreeProductCode : true # Removes limitations on ProductCode
EnableCrypt : false # Enables encryption for NCCH and CIA
MediaFootPadding : false # If true CCI files are created with padding
#ExeFs: # these are the program segments from the ELF, check your elf for the appropriate segment names
# ReadOnly:
# - .rodata
# - RO
# ReadWrite:
# - .data
# - RO
# Text:
# - .init
# - .text
# - STUP_ENTRY
#PlainRegion: # only used with SDK ELFs
# # - .module_id
AccessControlInfo:
# UseOtherVariationSaveData : true
# UseExtSaveData : true
# ExtSaveDataId: 0xffffffff
# SystemSaveDataId1: 0x220
# SystemSaveDataId2: 0x00040010
# OtherUserSaveDataId1: 0x220
# OtherUserSaveDataId2: 0x330
# OtherUserSaveDataId3: 0x440
# UseExtendedSaveDataAccessControl: true
# AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606]
FileSystemAccess:
# - CategorySystemApplication
# - CategoryHardwareCheck
# - CategoryFileSystemTool
- Debug
# - TwlCardBackup
# - TwlNandData
# - Boss
- DirectSdmc
# - Core
# - CtrNandRo
# - CtrNandRw
# - CtrNandRoWrite
# - CategorySystemSettings
# - CardBoard
# - ExportImportIvs
# - DirectSdmcWrite
# - SwitchCleanup
# - SaveDataMove
# - Shop
# - Shell
# - CategoryHomeMenu
IoAccessControl:
# - FsMountNand
# - FsMountNandRoWrite
# - FsMountTwln
# - FsMountWnand
# - FsMountCardSpi
# - UseSdif3
# - CreateSeed
# - UseCardSpi
IdealProcessor : 0
AffinityMask : 1
Priority : 16
MaxCpu : 0x9E # Default
# New3DS Exclusive Process Settings
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
CpuSpeed : 804MHz # 268MHz(Default)/804MHz
EnableL2Cache : true # false(default)/true
CanAccessCore2 : true
DisableDebug : true
EnableForceDebug : false
CanWriteSharedPage : true
CanUsePrivilegedPriority : false
CanUseNonAlphabetAndNumber : true
PermitMainFunctionArgument : true
CanShareDeviceMemory : true
RunnableOnSleep : false
SpecialMemoryArrange : true
CoreVersion : 2
DescVersion : 2
ReleaseKernelMajor : "02"
ReleaseKernelMinor : "33"
MemoryType : Application # Application / System / Base
HandleTableSize: 512
IORegisterMapping:
- 1ff50000-1ff57fff
- 1ff70000-1ff77fff
MemoryMapping:
- 1f000000-1f5fffff:r
SystemCallAccess:
ArbitrateAddress: 34
Break: 60
CancelTimer: 28
ClearEvent: 25
ClearTimer: 29
CloseHandle: 35
ConnectToPort: 45
ControlMemory: 1
CreateAddressArbiter: 33
CreateEvent: 23
CreateMemoryBlock: 30
CreateMutex: 19
CreateSemaphore: 21
CreateThread: 8
CreateTimer: 26
DuplicateHandle: 39
ExitProcess: 3
ExitThread: 9
GetCurrentProcessorNumber: 17
GetHandleInfo: 41
GetProcessId: 53
GetProcessIdOfThread: 54
GetProcessIdealProcessor: 6
GetProcessInfo: 43
GetResourceLimit: 56
GetResourceLimitCurrentValues: 58
GetResourceLimitLimitValues: 57
GetSystemInfo: 42
GetSystemTick: 40
GetThreadContext: 59
GetThreadId: 55
GetThreadIdealProcessor: 15
GetThreadInfo: 44
GetThreadPriority: 11
MapMemoryBlock: 31
OutputDebugString: 61
QueryMemory: 2
ReleaseMutex: 20
ReleaseSemaphore: 22
SendSyncRequest1: 46
SendSyncRequest2: 47
SendSyncRequest3: 48
SendSyncRequest4: 49
SendSyncRequest: 50
SetThreadPriority: 12
SetTimer: 27
SignalEvent: 24
SleepThread: 10
UnmapMemoryBlock: 32
WaitSynchronization1: 36
WaitSynchronizationN: 37
InterruptNumbers:
ServiceAccessControl:
- APT:U
- $hioFIO
- $hostio0
- $hostio1
- ac:u
- boss:U
- cam:u
- cecd:u
- cfg:u
- dlp:FKCL
- dlp:SRVR
- dsp::DSP
- frd:u
- fs:USER
- gsp::Gpu
- hid:USER
- http:C
- mic:u
- ndm:u
- news:u
- nwm::UDS
- ptm:u
- pxi:dev
- soc:U
- ssl:C
- y2r:u
- ldr:ro
- ir:USER
SystemControlInfo:
SaveDataSize: 0KB # It doesn't use any save data.
RemasterVersion: 2
StackSize: 0x40000
# JumpId: 0
Dependency:
ac: 0x0004013000002402L
am: 0x0004013000001502L
boss: 0x0004013000003402L
camera: 0x0004013000001602L
cecd: 0x0004013000002602L
cfg: 0x0004013000001702L
codec: 0x0004013000001802L
csnd: 0x0004013000002702L
dlp: 0x0004013000002802L
dsp: 0x0004013000001a02L
friends: 0x0004013000003202L
gpio: 0x0004013000001b02L
gsp: 0x0004013000001c02L
hid: 0x0004013000001d02L
http: 0x0004013000002902L
i2c: 0x0004013000001e02L
ir: 0x0004013000003302L
mcu: 0x0004013000001f02L
mic: 0x0004013000002002L
ndm: 0x0004013000002b02L
news: 0x0004013000003502L
nim: 0x0004013000002c02L
nwm: 0x0004013000002d02L
pdn: 0x0004013000002102L
ps: 0x0004013000003102L
ptm: 0x0004013000002202L
ro: 0x0004013000003702L
socket: 0x0004013000002e02L
spi: 0x0004013000002302L
ssl: 0x0004013000002f02L

View File

@ -11,17 +11,22 @@
#include "starlight/gfx/RenderCore.h" #include "starlight/gfx/RenderCore.h"
#include "starlight/util/Path.h" #include "starlight/util/Path.h"
#include "starlight/util/Profiler.h"
#include "starlight/ui/ParallaxLayer.h" #include "starlight/ui/ParallaxLayer.h"
#include "starlight/ui/ScrollField.h" #include "starlight/ui/ScrollField.h"
#include "starlight/ui/Button.h" #include "starlight/ui/Button.h"
#include "starlight/ui/TextBox.h" #include "starlight/ui/TextBox.h"
#include "starlight/ui/Label.h" #include "starlight/ui/Label.h"
#include "starlight/ui/Image.h"
#include "starlight/ui/DebugConsole.h"
#include "starlight/dialog/Backdrop.h" #include "starlight/dialog/Backdrop.h"
#include "starlight/dialog/MessageBox.h" #include "starlight/dialog/MessageBox.h"
#include "starlight/dialog/OSK.h" #include "starlight/dialog/OSK.h"
#include "ThreadTest.h"
using starlight::Vector2; using starlight::Vector2;
using starlight::VRect; using starlight::VRect;
using starlight::Color; using starlight::Color;
@ -32,19 +37,24 @@ using starlight::GFXManager;
using starlight::gfx::RenderCore; using starlight::gfx::RenderCore;
using starlight::util::Path; using starlight::util::Path;
using starlight::util::Profiler;
using starlight::Application; using starlight::Application;
void Core::Init() { void Core::Init() {
//consoleInit(GFX_TOP, consoleGetDefault()); /*clearColor = Color(0,0,0.5);
auto img = touchScreen->AddNew<sl::ui::Image>(Vector2(32, 32), "sdmc:/snes9x_3ds_top.png");
auto lbl = touchScreen->AddNew<sl::ui::Label>(VRect(0,0,320,240));
lbl->SetText("text go here\ntest test test\nnickelpickle");
return;*/
auto container = std::make_shared<sl::ui::ScrollField>(VRect(0,0,320-0,240-0)); auto container = std::make_shared<sl::ui::ScrollField>(VRect(0,0,320-0,240-0));
touchScreen->Add(container); touchScreen->Add(container);
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0)); auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->textConfig.justification = Vector2::half; label->textConfig->justification = Vector2::half;
label->autoSizeV = true; label->autoSizeV = true;
label->SetText("~libstardust UI test~\n\nHello. I'm a label.\nI have multiple lines and can resize to fit my content. Did you know that miles per gallon is actually a measure of volume?"); label->SetText("~libstarlight UI test~\n\nHello. I'm a label.\nI have multiple lines and can resize to fit my content. Did you know that miles per gallon is actually a measure of volume? " + std::to_string(sizeof(unsigned long long)));
container->Add(label); container->Add(label);
auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32)); auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32));
@ -53,11 +63,20 @@ void Core::Init() {
// assemble and open a basic form // assemble and open a basic form
auto form = std::make_shared<sl::ui::Form>(true); auto form = std::make_shared<sl::ui::Form>(true);
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0)); auto tbtn = form->touchScreen->AddNew<sl::ui::Button>(VRect(4, 28, 80, 24));
label->textConfig.justification = Vector2::half; tbtn->SetText("print something");
tbtn->eOnTap = [](auto& btn){
printf("pickles!\n");
};
/*auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->textConfig->justification = Vector2::half;
label->autoSizeV = true; label->autoSizeV = true;
label->SetText("This is a form, coming in and nuking the non-form UI elements. Whoops."); label->SetText("This is a form, coming in and nuking the non-form UI elements. Whoops.");
form->touchScreen->Add(label); form->touchScreen->Add(label);*/
auto console = form->topScreen->AddNew<sl::ui::DebugConsole>(VRect::topScreen);
console->Start();
auto xbtn = std::make_shared<sl::ui::Button>(VRect(320-96,28,32,24)); auto xbtn = std::make_shared<sl::ui::Button>(VRect(320-96,28,32,24));
xbtn->eOnTap = [](auto& btn){ xbtn->eOnTap = [](auto& btn){
@ -66,13 +85,13 @@ void Core::Init() {
xbtn->SetText("(exit)"); xbtn->SetText("(exit)");
form->touchScreen->Add(xbtn); form->touchScreen->Add(xbtn);
auto tlbl = std::make_shared<sl::ui::Label>(VRect(2, 2, 396, 0)); /*auto tlbl = std::make_shared<sl::ui::Label>(VRect(2, 2, 396, 0));
tlbl->autoSizeV = true; tlbl->autoSizeV = true;
tlbl->SetPreset("normal.16"); tlbl->SetPreset("normal.16");
tlbl->textConfig.justification = Vector2::zero; tlbl->textConfig->justification = Vector2::zero;
tlbl->textConfig.borderColor = Color::black; tlbl->textConfig->borderColor = Color::black;
tlbl->SetText("3DS:~# sudo make me a sandwich_"); tlbl->SetText("3DS:~# sudo make me a sandwich_");
form->topScreen->Add(tlbl); form->topScreen->Add(tlbl);*/
auto tb = std::make_shared<sl::ui::TextBox>(VRect(0, 64, 320, 24).Expand(-16, 0)); auto tb = std::make_shared<sl::ui::TextBox>(VRect(0, 64, 320, 24).Expand(-16, 0));
tb->text = "Single-line TextBox widget example. Tap me!"; tb->text = "Single-line TextBox widget example. Tap me!";
@ -88,6 +107,9 @@ void Core::Init() {
// open a backdrop with the default asset // open a backdrop with the default asset
sl::dialog::Backdrop::New()->Open(); sl::dialog::Backdrop::New()->Open();
// make a test thread
std::make_shared<ThreadTest>()->Enqueue();
// //
}; };
container->Add(button); container->Add(button);
@ -100,9 +122,10 @@ void Core::Init() {
auto pipf = std::make_shared<sl::ui::Label>(VRect(0,0,400,240)); auto pipf = std::make_shared<sl::ui::Label>(VRect(0,0,400,240));
pipf->SetPreset("normal.16"); pipf->SetPreset("normal.16");
pipf->textConfig.borderColor = Color::black; pipf->textConfig->borderColor = Color::black;
pipf->textConfig.justification = Vector2::half; pipf->textConfig->justification = Vector2::half;
pipf->SetText("This label is on a parallax layer. Try moving the 3D slider.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."); //pipf->SetText("This label is on a parallax layer. Try moving the 3D slider.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
pipf->SetText(ThemeManager::ResolveAssetPath("app:/decorations/osk.background"));
parallax->Add(pipf); parallax->Add(pipf);
clearColor = Color(0.0f, 0.5f, 0.5f); clearColor = Color(0.0f, 0.5f, 0.5f);
@ -112,7 +135,7 @@ void Core::Init() {
cc.Json()["panini"] = "yes please!"; cc.Json()["panini"] = "yes please!";
cc.Save(); cc.Save();
// //*/
} }
void Core::End() { void Core::End() {

View File

@ -0,0 +1,21 @@
#include "ThreadTest.h"
#include "starlight/Application.h"
#include "starlight/ConfigManager.h"
using starlight::Application;
void ThreadTest::Body() {
auto& cc = Application::GetConfig("test");
int count = 0;
cc.Json()["pork"] = 0;
cc.Save();
cc.autoSave = true;
while(true) {
cc.Json()["pork"] = ++count;
//cc.Save();
Yield();
}
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "starlight/threading/Thread.h"
class ThreadTest : public sl::threading::Thread {
void Body() override;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

View File

@ -55,6 +55,10 @@
"textColor" : "midGray", "textColor" : "midGray",
"borderColor" : "darkGray", "borderColor" : "darkGray",
"justification" : [0.5, 0.5] "justification" : [0.5, 0.5]
},
"keyHighlight" : {
"_inherit" : "/controls/button/text",
"textColor" : [0.75, 0.825, 1]
} }
} }
}, },