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
*.elf
*.3dsx
*.cia
# ...and icons
*.smdh

View File

@ -1,4 +1,4 @@
### MIT License
The MIT License (MIT)
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
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)
## 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
* MIT (see [license.md](license.md))

View File

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

View File

@ -17,6 +17,9 @@ using starlight::ThemeManager;
using starlight::InputManager;
using starlight::gfx::RenderCore;
using starlight::threading::Thread;
using starlight::threading::ThreadState;
using starlight::ui::TouchScreenCanvas;
using starlight::ui::TopScreenCanvas;
@ -30,6 +33,7 @@ using starlight::Application;
////////////////////
Application* Application::_currentApp = nullptr;
unsigned long long Application::ftime = 0;
bool Application::Quit() {
if (_currentApp == nullptr) return false;
@ -82,6 +86,9 @@ void Application::_init() {
void Application::_end() {
End();
for (auto& thread : threads) thread->Exit();
threads.clear();
//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
@ -97,6 +104,9 @@ void Application::_end() {
}
void Application::_mainLoop() {
RenderCore::SyncFrame(); // sync to vblank here for more accurate timing
frameTimer.FrameStart();
if (!forms.empty()) {
if (_sFormState) {
_sFormState = false;
@ -124,6 +134,8 @@ void Application::_mainLoop() {
}
// update step
ftime = osGetTime();
InputManager::Update();
Update();
{ // update loop for forms, guarded from snap-outs
@ -151,4 +163,16 @@ void Application::_mainLoop() {
topScreen->Draw();
PostDraw();
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 <memory>
#include <list>
#include "starlight/datatypes/Vector2.h"
#include "starlight/datatypes/VRect.h"
#include "starlight/datatypes/Color.h"
#include "starlight/util/FrameTimer.h"
#include "starlight/threading/Thread.h"
#include "starlight/ui/TouchScreenCanvas.h"
#include "starlight/ui/TopScreenCanvas.h"
@ -22,12 +27,14 @@ namespace starlight {
////////////////////
private:
static Application* _currentApp;
static unsigned long long ftime;
public:
static bool Quit();
static Config& GetConfig(const std::string& path);
static std::string AppName();
static inline Application* Current() { return _currentApp; }
static inline unsigned long long GetTime() { return ftime; }
//////////////////////
// INSTANCE MEMBERS //
@ -39,6 +46,9 @@ namespace starlight {
void _mainLoop();
void _end();
std::list<std::shared_ptr<threading::Thread>> threads;
util::FrameTimer frameTimer;
public:
const std::string appId;
@ -57,13 +67,14 @@ namespace starlight {
void Run();
void EnqueueThread(std::shared_ptr<threading::Thread> thread);
inline void SignalFormState() { _sFormState = true; }
virtual void Init() { }
virtual void Update() { }
virtual void PostUpdate() { }
virtual void Draw() { }
virtual void PostDraw() { }
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
};
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 {
// forward declare this for OpenKeyboard
namespace dialog {
@ -94,11 +97,11 @@ namespace starlight {
static Vector2 CStick();
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 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 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 TouchDelta();

View File

@ -19,12 +19,17 @@
#include "starlight/gfx/DrawableNinePatch.h"
#include "starlight/gfx/DrawableTest.h"
#include "starlight/gfx/FontBMF.h"
#include "starlight/gfx/FontNull.h"
#include "starlight/gfx/RenderCore.h"
#include "starlight/gfx/BitmapFont.h"
#include "starlight/util/JsonConversions.h"
#include "starlight/util/Profiler.h"
using starlight::util::Profiler;
#include <sstream>
using std::string;
using std::shared_ptr;
using std::make_shared;
@ -75,40 +80,48 @@ namespace {
}
CTexture* LoadPNG(const std::string& path, bool isPremult = false) {
//Profiler::TaskStart();
unsigned char* imgbuf;
unsigned width, height;
lodepng::State state;
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);
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) {
u32* src = reinterpret_cast<u32*>(imgbuf); u32* dst = reinterpret_cast<u32*>(gpubuf);
// just convert endianness
for(unsigned iy = 0; iy < height; iy++) {
for (unsigned ix = 0; ix < width; ix++) {
int r = *src++;
int g = *src++;
int b = *src++;
int a = *src++;
u32 clr = *src;
*dst = __builtin_bswap32(clr);
*dst++ = a;
*dst++ = b;
*dst++ = g;
*dst++ = r;
src+=4; dst+=4;
}
dst += (bw - width) * 4; // skip the difference
}
} else {
u8* src = static_cast<u8*>(imgbuf); u8* dst = static_cast<u8*>(gpubuf);
// convert and premultiply
for(unsigned iy = 0; iy < height; iy++) {
for (unsigned ix = 0; ix < width; ix++) {
int r = *src++;
int g = *src++;
int b = *src++;
int a = *src++;
u8 r = *src++;
u8 g = *src++;
u8 b = *src++;
u8 a = *src++;
float aa = (1.0f / 255.0f) * a;
@ -120,11 +133,14 @@ namespace {
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,
// 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);
tx->size = Vector2(width, height); // and for now just fix the size after the fact
//Profiler::TaskFinish("copied into linear");
std::free(imgbuf);
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) {
auto const& itr = drawables.find(name);
if (itr == drawables.end()) {
@ -195,14 +225,14 @@ ThemeRef<Font> ThemeManager::GetFont(const std::string& name) {
void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) {
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>();
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") {
return make_shared<DrawableImage>(LoadPNG(path));
}
@ -213,7 +243,7 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
fs >> j;
}
auto st = j.dump();
printf("file contents: %s\n", st.c_str());
//printf("file contents: %s\n", st.c_str());
string type = j["assetType"];
/**/ if (type == "ninepatch") {
@ -225,9 +255,8 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
// else if (type == "") { }
else if (type == "link") {
string npath = ResolveAssetPath(j["path"]);
//return LoadAsset(npath);
return GetAsset(npath).GetShared(); // I guess this works; may need to be altered for asynchronity if I do that later
// (perhaps by--wait no, making it the same ThemeRefContainer would require a full rearchitecture of this part @.@)
ref.redir = const_cast<ThemeRefContainer<Drawable>*>(GetAsset(npath).cptr); // link containers directly
return nulldrw; // doesn't really matter what's inside, it'll never get used
}
return nulldrw;
}
@ -236,6 +265,10 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
void ThemeManager::Fulfill(ThemeRefContainer<Font>& ref) {
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>();
{ // using:
json j;
@ -258,54 +291,100 @@ void ThemeManager::LoadProc() {
}
string ThemeManager::ResolveAssetPath(const string& id) {
//struct stat buf;
//string path(id.length() + 64, ' '); // preallocate buffer space
string pfx = "";
static const string pfxLocal = "app:/";
if (id.compare(0, pfxLocal.length(), pfxLocal) == 0) {
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]/, else check in romfs
for (auto thm : themeData) {
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;
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;
}
// TBD - directly in romfs, or in an assets folder?
Path bp = Path("romfs:");
Path p = bp.Combine(id+".json");
Path bp = Path("romfs:/");
Path p = bp.Combine(sid);
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;
}
else {
// theme asset; check in each theme from selected to most-fallback
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;
p = thm.basePath.Combine(id+".png");
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();
}
string ThemeManager::ResolveFontPath(const string& id) { // there we go, nice and simple
for (auto thm : themeData) {
Path p = thm.basePath.Combine("fonts").Combine(id+".json");
string pfx = "";
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;
}
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) {

View File

@ -48,13 +48,15 @@ namespace starlight {
static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& 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:
ThemeManager() = delete; // "static" class
static void Init();
static void End();
static void GC();
static gfx::ThemeRef<gfx::Drawable> GetAsset(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() = 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(VRect rect, const std::string& text, Vector2 justification = Vector2::invalid);

View File

@ -1,9 +1,13 @@
#include "Color.h"
#include <limits>
#include "starlight/_incLib/json.hpp"
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::white = Color(1.0f, 1.0f, 1.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 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
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)); }
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 white;
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 <cmath>
#include <algorithm>
#include <fastmath.h>
#include <limits>
@ -11,6 +12,11 @@ using starlight::Vector2;
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::ClampLength(float max) const {
float len = Length();
return *this * (std::min(len, max) / len);
}
Vector2 Vector2::Reciprocal() const { return Vector2(y, x); }
Vector2 Vector2::IntSnap() const { return Vector2(roundf(x), roundf(y)); }

View File

@ -19,6 +19,8 @@ namespace starlight {
Vector2 Normalized() const;
inline float Area() const { return x * y; }
Vector2 ClampLength(float max = 1) const;
Vector2 Reciprocal() 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 bool Valid() const { return x == x && y == y; }
inline explicit operator bool() const { return x == x && y == y; }
static const Vector2 invalid;

View File

@ -37,6 +37,10 @@ namespace {
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;
}
@ -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
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;
touchScreen->Add(cover);
// wip
// build keyboard
setContainer = std::make_shared<ui::UIContainer>(VRect::touchScreen);
touchScreen->Add(setContainer);
//setContainer = touchScreen->AddNew<ui::UIContainer>(VRect::touchScreen); // kept as a test case
setContainer = touchScreen->AddNew<ui::UICanvas>(VRect::touchScreen); // but this is much more efficient
auto actSym = [this](Button& key){
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);
} else {
// 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;
key->SetText(string(1, c));
key->eOnTap = actSym;
setContainer->Add(key);
// 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;
key->SetText(string(1, C));
key->eOnTap = actSym;
setContainer->Add(key);
// and after
bpen.x += key->rect.size.x;
@ -94,26 +95,30 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
// backspace
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->SetText("< <");
key->style.glyph = ThemeManager::GetAsset("glyphs/backspace.small");
key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); };
touchScreen->Add(key);
// enter
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->SetText("Enter");
key->style.glyph = ThemeManager::GetAsset("glyphs/enter.large");
key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); };
touchScreen->Add(key);
previewSc = std::make_shared<ScrollField>(VRect(VRect::touchScreen.TopEdge(66)));
touchScreen->Add(previewSc);
// shift
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); };
previewSc->Add(preview);
RefreshPreview();
}
@ -124,46 +129,57 @@ void OSK::Update(bool focused) {
return;
}
if (focused) {
if (InputManager::Pressed(Keys::B)) handler->Done();
if (InputManager::Pressed(Keys::B | Keys::Start)) handler->Done();
if (handler->showPreview) {
if (InputManager::Pressed(Keys::DPadLeft)) {
auto& tc = PreviewTC();
bool refresh = false;
if (ShiftScroll(Keys::DPadLeft)) {
auto c = handler->GetCursor();
if (c > 0) handler->SetCursor(c - 1);
RefreshPreview();
refresh = true;
}
if (InputManager::Pressed(Keys::DPadRight)) {
if (ShiftScroll(Keys::DPadRight)) {
handler->SetCursor(handler->GetCursor() + 1);
RefreshPreview();
refresh = true;
}
auto& tc = PreviewTC();
if (InputManager::Pressed(Keys::DPadUp)) {
if (ShiftScroll(Keys::DPadUp)) {
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
pt.y -= tc.Measure("|").y * 0.5f;
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());
pt.y += tc.Measure("|").y * 1.5f;
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
RefreshPreview();
refresh = true;
}
if (refresh) RefreshPreview();
}
float& s = setContainer->scrollOffset.y;
float ts = 0;
if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) {
ts = 1000;
}
shiftLock = false;
} else if (shiftLock) ts = 1000;
if (s != ts) {
s = ts;
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() {
shiftLock = false;
RefreshPreview();
}

View File

@ -6,6 +6,7 @@
#include <memory>
#include "starlight/ui/Form.h"
#include "starlight/ui/Button.h"
#include "starlight/ui/ScrollField.h"
#include "starlight/ui/DrawLayerProxy.h"
@ -19,7 +20,10 @@ namespace starlight {
std::shared_ptr<ui::ScrollField> previewSc;
std::shared_ptr<ui::DrawLayerProxy> preview;
std::shared_ptr<ui::Button> shiftKey;
//Vector2 cursorPos;
bool shiftLock = false;
public:
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)); }
// 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()) {
target->Bind(color ? color.get() : Color(1,1,1,1));
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()) {
target->Bind(color ? color.get() : Color(1,1,1,1));
const VRect& sr = sampleRect ? sampleRect.get() : this->rect;

View File

@ -23,8 +23,8 @@ namespace starlight {
DrawContextCanvas(Vector2 size);
~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 VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) 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, BlendMode mode = BlendMode::Normal) override;
Vector2 Size() override;
@ -32,4 +32,4 @@ namespace starlight {
void Clear() override;
};
}
}
}

View File

@ -7,6 +7,8 @@
#include "starlight/datatypes/OptRef.h"
#include "starlight/gfx/Enums.h"
namespace starlight {
namespace gfx {
class Drawable {
@ -17,16 +19,14 @@ namespace starlight {
// pattern after:
// 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);
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;
void Draw(const Vector2& position, OptRef<Vector2> hotspot, OptRef<VRect> sampleRect, OptRef<Color> color, float rotation, float scale) {
Draw(position, hotspot, sampleRect, color, rotation, Vector2(scale, scale));
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, BlendMode mode = BlendMode::Normal) {
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;
};
}
}

View File

@ -15,18 +15,18 @@ using starlight::gfx::DrawableImage;
using starlight::gfx::RenderCore;
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()) {
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);
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);
}
}
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()) {
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);
RenderCore::DrawQuad(rect, sr / texture->txSize);
}

View File

@ -15,11 +15,10 @@ namespace starlight {
DrawableImage(CTexture* texture) : texture(texture) { }
~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 VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) 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, BlendMode mode = BlendMode::Normal) override;
Vector2 Size() override;
};
}
}

View File

@ -15,9 +15,9 @@ using starlight::gfx::DrawableNinePatch;
using starlight::gfx::RenderCore;
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()) {
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();
const VRect& sr = (sampleRect ? sampleRect.get() : VRect(Vector2::zero, texture->size)).IntSnap();

View File

@ -16,7 +16,7 @@ namespace starlight {
~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 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;
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()) {
//static u32 col = Color(0, 0.5f, 1);
//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()) {
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());
}
}

View File

@ -10,11 +10,10 @@ namespace starlight {
DrawableTest() { }
~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 VRect& rect, OptRef<VRect> sampleRect = nullptr, OptRef<Color> color = nullptr) 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, BlendMode mode = BlendMode::Normal) override;
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 {
class Font {
public:
static constexpr const int defaultSize = 16;
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::Color;
using starlight::util::WorkerThread;
using starlight::gfx::BlendMode;
using starlight::gfx::CTexture;
using starlight::gfx::CRenderTarget;
using starlight::gfx::RenderCore;
@ -24,41 +25,33 @@ namespace { // internals
typedef struct {
float x, y, z, u, v;
} 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;
size_t bufferInd = 0;
size_t bufferSize = 0;
vbo_xyzuv* vboArray = nullptr;
size_t vboIndex = 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;
shaderProgram_s shader;
int sLocProjection = -1;
void ResetBuffer() { bufferInd = 0; }
C3D_AttrInfo* attrInfo = nullptr;
void* AllocBuffer(size_t size, size_t align = 1) {
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) {
inline unsigned int NextPow2(unsigned int x) {
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
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 {
@ -77,8 +70,8 @@ namespace { // internals
C3D_TexDelete(texture);
delete texture;
}
void Bind(Color color = Color::white) override {
RenderCore::BindTexture(texture, color);
void Bind(Color color = Color::white, BlendMode mode = BlendMode::Normal) override {
RenderCore::BindTexture(texture, color, mode);
}
};
}
@ -94,9 +87,21 @@ void RenderCore::Open() {
gfxSet3D(true);
C3D_Init(0x80000*8);
bufferSize = 0x80000;
bufferStart = linearAlloc(bufferSize);
bufferInd = 0;
// allocate and initialize VBO
vboSize = 0x80000;
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
targetTopLeft = std::make_unique<CRenderTarget>(240, 400, true);
@ -119,7 +124,7 @@ void RenderCore::Open() {
// 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_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); // hmm.
C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL); // hmm.
C3D_CullFace(GPU_CULL_NONE);
}
@ -129,45 +134,75 @@ void RenderCore::Close() {
targetTopRight.reset(nullptr);
targetBottom.reset(nullptr);
linearFree(bufferStart);
linearFree(reinterpret_cast<void*>(vboArray));
C3D_Fini();
gfxExit();
}
void RenderCore::SyncFrame() {
C3D_FrameSync();
}
void RenderCore::BeginFrame() {
ResetBuffer();
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
C3D_FrameBegin(0/*C3D_FRAME_SYNCDRAW*/);
vboIndex = 0;
}
void RenderCore::EndFrame() {
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_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0);
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_MODULATE);//REPLACE); // let's see...
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
ApplyBlendMode(env, mode);
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_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, 0, 0);
C3D_TexEnvOp(env, C3D_RGB, 0, 0, 0);
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);
ApplyBlendMode(env, mode);
C3D_TexEnvColor(env, color.Premultiplied());
}
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
@ -179,41 +214,23 @@ void RenderCore::DrawQuad(const VRect& rect, const VRect& src, bool noSnap) {
// 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 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);
setXYZUV(verts[1], rr, rt, 0, srr, srt);
setXYZUV(verts[2], rl, rb, 0, srl, srb);
setXYZUV(verts[3], rr, rb, 0, srr, srb);
addVertXYZUV(rl, rt, 0, srl, srt);
addVertXYZUV(rr, rt, 0, srr, srt);
addVertXYZUV(rl, rb, 0, srl, srb);
addVertXYZUV(rr, rb, 0, srr, srb);
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
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);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, vboNum, 4);
}
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());
setXYZUV(verts[1], rect.TopRight().RotateAround(anchor, angle), src.TopRight());
setXYZUV(verts[2], rect.BottomLeft().RotateAround(anchor, angle), src.BottomLeft());
setXYZUV(verts[3], rect.BottomRight().RotateAround(anchor, angle), src.BottomRight());
addVertXYZUV(rect.TopLeft().RotateAround(anchor, angle), src.TopLeft());
addVertXYZUV(rect.TopRight().RotateAround(anchor, angle), src.TopRight());
addVertXYZUV(rect.BottomLeft().RotateAround(anchor, angle), src.BottomLeft());
addVertXYZUV(rect.BottomRight().RotateAround(anchor, angle), src.BottomRight());
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
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);
C3D_DrawArrays(GPU_TRIANGLE_STRIP, vboNum, 4);
}
// specifically RGBA
@ -234,7 +251,7 @@ CTexture* RenderCore::LoadTexture(void* src, int width, int height) {
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;
}
@ -248,9 +265,22 @@ CRenderTarget::CRenderTarget(int width, int height, bool forceExact) {
auto w = forceExact ? width : NextPow2(width),
h = forceExact ? height : NextPow2(height);
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_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() {
@ -258,17 +288,33 @@ CRenderTarget::~CRenderTarget() {
}
void CRenderTarget::Clear(Color color) {
unsigned int c = color;
c = ((c>>24)&0x000000FF) | ((c>>8)&0x0000FF00) | ((c<<8)&0x00FF0000) | ((c<<24)&0xFF000000); // reverse endianness
C3D_RenderTargetSetClear(tgt, C3D_CLEAR_ALL, c, 0);
//unsigned int c = color;
//c = ((c>>24)&0x000000FF) | ((c>>8)&0x0000FF00) | ((c<<8)&0x00FF0000) | ((c<<24)&0xFF000000); // reverse endianness
//C3D_RenderTargetSetClear(tgt, C3D_CLEAR_ALL, c, 0);
//C3D_FrameBufClear(&(tgt->frameBuf), C3D_CLEAR_COLOR, c, 0);
clearColor = color;
}
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_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) {
C3D_RenderTargetSetClear(tgt, 0, 0, 0); // don't clear again until marked to
RenderCore::BindTexture(&(tgt->renderBuf.colorBuf), color);
void CRenderTarget::Bind(Color color, BlendMode mode) {
//C3D_RenderTargetSetClear(tgt, static_cast<C3D_ClearBits>(0), 0, 0); // don't clear again until marked to
RenderCore::BindTexture(&tex, color, mode);
}

View File

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

View File

@ -16,21 +16,30 @@ namespace starlight {
protected:
const std::string name;
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();
if (full) redir = nullptr;
}
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) : 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:
~ThemeRefContainer() { }
T* operator ->() const {
if (ptr == nullptr) {
ThemeManager::Fulfill(const_cast<ThemeRefContainer<T>&>(*this)); // call thememanager to grab things
}
return &*ptr;
return &*(getptr());
}
/*T& operator *() const {
@ -44,15 +53,32 @@ namespace starlight {
template <class T>
class ThemeRef {
private:
const ThemeRefContainer<T>* cptr;
friend class starlight::ThemeManager;
protected:
ThemeRefContainer<T>* cptr;
public:
ThemeRef() : cptr(nullptr) { }
ThemeRef(ThemeRefContainer<T>* c) : cptr(c) { }
~ThemeRef() { }
inline const ThemeRefContainer<T>& operator ->() const { return *cptr; }
ThemeRef(ThemeRefContainer<T>* c) : cptr(c) { if (cptr) cptr->refCount++; }
ThemeRef(ThemeRef<T>& o) : cptr(o.cptr) { if (cptr) cptr->refCount++; }
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 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; }
};
}

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::ThemeManager;
using starlight::TextConfig;
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) {
label = text;
MarkForRedraw();
@ -28,18 +35,18 @@ void Button::Draw() {
static auto idle = ThemeManager::GetAsset("controls/button.idle");
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();
if (InputManager::GetDragHandle() == this) {
press->Draw(rect);
(style.press ? style.press : press)->Draw(rect);
} 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);
if (style.glyph) style.glyph->Draw(rect.Center(), Vector2::half, nullptr, tc.textColor);
}
void Button::OnTouchOn() {

View File

@ -4,15 +4,30 @@
#include <string>
#include <functional>
#include "starlight/datatypes/Optional.h"
#include "starlight/ThemeManager.h"
#include "starlight/gfx/ThemeRef.h"
#include "starlight/ui/UIElement.h"
namespace starlight {
namespace ui {
class Button : public UIElement {
public:
struct Style {
gfx::ThemeRef<gfx::Drawable>
idle = nullptr,
press = nullptr,
glyph = nullptr;
Optional<TextConfig> textConfig = &Button::defCfg;
};
private:
//
static std::function<TextConfig&()> defCfg;
public:
Style style;
std::string label = "";
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"
using starlight::GFXManager;
using starlight::TextConfig;
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) {
this->rect = rect;
textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/normal.12", TextConfig());
}
void Label::AutoSize() {
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);
}
@ -28,12 +33,13 @@ void Label::SetText(const std::string& text) {
}
void Label::SetFont(const std::string& fontName) {
textConfig.font = ThemeManager::GetFont(fontName);
textConfig->font = ThemeManager::GetFont(fontName);
textConfig->Measure(""); // force load
AutoSize();
}
void Label::SetPreset(const std::string& name) {
textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/" + name, textConfig);
textConfig = ThemeManager::GetMetric<TextConfig>("/textPresets/" + name, textConfig.ROGet());
AutoSize();
}
@ -48,7 +54,8 @@ void Label::PreDraw() {
buffer = std::make_unique<gfx::DrawContextCanvas>(rect.size + Vector2(0, 8));
buffer->Clear();
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();
}
}
@ -58,6 +65,6 @@ void Label::Draw() {
if (buffer) {
buffer->Draw(VRect(rect.pos, buffer->rect.size));
} else {
textConfig.Print(rect, text);
textConfig.ROGet().Print(rect, text);
}
}

View File

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

View File

@ -35,6 +35,7 @@ void UICanvas::PreDraw() {
drawContext->Clear();
GFXManager::PushContext(drawContext.get());
GFXManager::PushOffsetAdd(-scrollOffset);
GFXManager::PrepareForDrawing(); // force clear to take so you don't get garbage if nothing renders
VRect vr = ViewportRect();
@ -51,4 +52,3 @@ void UICanvas::PreDraw() {
void UICanvas::Draw() {
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) {
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()));
if (front) children.push_front(std::move(elem));
else children.push_back(std::move(elem));
MarkForRedraw();
}
//void UIContainer::Add(UIElement* elem) {
@ -71,6 +71,7 @@ void UIContainer::RemoveAll() {
it->parent = std::weak_ptr<UIContainer>(); // clear parent
}
children.clear();
MarkForRedraw();
}
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 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 Remove(std::shared_ptr<UIElement> elem);
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 {
- ding!
roadmap to v0.5.1 {
- 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" {
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)
add customization for Button (alternate idle/press images, optional glyph drawable)
^ use that to spice up the OSK
garbage collection for not-recently-used theme assets {
- 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
input prompt dialog
make the panel background not just the button image
"shortcut" overloads for InputManager::OpenKeyboard
language config and atlas support
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
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 {

4
maketest.sh Normal file → Executable file
View File

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

View File

@ -44,6 +44,8 @@ APP_TITLE := Starlight Testbed
APP_DESCRIPTION := Test application for libstarlight
APP_AUTHOR := zetaPRIME
3DSIP := 10.0.0.6
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
@ -56,7 +58,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations \
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
# 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
#CXXFLAGS := $(CFLAGS) -std=gnu++14
@ -87,6 +89,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
export ROMFS_ROOT := $(CURDIR)/$(ROMFS)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
@ -162,14 +165,19 @@ cci: $(TARGET)-strip.elf
@echo "built ... sf2d_sample.3ds"
#---------------------------------------------------------------------------------
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"
#---------------------------------------------------------------------------------
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)
@citra $(TARGET).3dsx
@citra-qt $(TARGET).3dsx
#---------------------------------------------------------------------------------
copy_cia: $(TARGET).cia
@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/util/Path.h"
#include "starlight/util/Profiler.h"
#include "starlight/ui/ParallaxLayer.h"
#include "starlight/ui/ScrollField.h"
#include "starlight/ui/Button.h"
#include "starlight/ui/TextBox.h"
#include "starlight/ui/Label.h"
#include "starlight/ui/Image.h"
#include "starlight/ui/DebugConsole.h"
#include "starlight/dialog/Backdrop.h"
#include "starlight/dialog/MessageBox.h"
#include "starlight/dialog/OSK.h"
#include "ThreadTest.h"
using starlight::Vector2;
using starlight::VRect;
using starlight::Color;
@ -32,19 +37,24 @@ using starlight::GFXManager;
using starlight::gfx::RenderCore;
using starlight::util::Path;
using starlight::util::Profiler;
using starlight::Application;
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));
touchScreen->Add(container);
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->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);
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
auto form = std::make_shared<sl::ui::Form>(true);
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->textConfig.justification = Vector2::half;
auto tbtn = form->touchScreen->AddNew<sl::ui::Button>(VRect(4, 28, 80, 24));
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->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));
xbtn->eOnTap = [](auto& btn){
@ -66,13 +85,13 @@ void Core::Init() {
xbtn->SetText("(exit)");
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->SetPreset("normal.16");
tlbl->textConfig.justification = Vector2::zero;
tlbl->textConfig.borderColor = Color::black;
tlbl->textConfig->justification = Vector2::zero;
tlbl->textConfig->borderColor = Color::black;
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));
tb->text = "Single-line TextBox widget example. Tap me!";
@ -88,6 +107,9 @@ void Core::Init() {
// open a backdrop with the default asset
sl::dialog::Backdrop::New()->Open();
// make a test thread
std::make_shared<ThreadTest>()->Enqueue();
//
};
container->Add(button);
@ -100,9 +122,10 @@ void Core::Init() {
auto pipf = std::make_shared<sl::ui::Label>(VRect(0,0,400,240));
pipf->SetPreset("normal.16");
pipf->textConfig.borderColor = Color::black;
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->textConfig->borderColor = Color::black;
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(ThemeManager::ResolveAssetPath("app:/decorations/osk.background"));
parallax->Add(pipf);
clearColor = Color(0.0f, 0.5f, 0.5f);
@ -112,7 +135,7 @@ void Core::Init() {
cc.Json()["panini"] = "yes please!";
cc.Save();
//
//*/
}
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",
"borderColor" : "darkGray",
"justification" : [0.5, 0.5]
},
"keyHighlight" : {
"_inherit" : "/controls/button/text",
"textColor" : [0.75, 0.825, 1]
}
}
},