Chesto 0.9
A declarative and element-based library for creating GUIs on homebrew'd consoles
RootDisplay.cpp
1#if defined(__EMSCRIPTEN__)
2#include <emscripten.h>
3#elif defined(SWITCH)
4#include <switch.h>
5#define PLATFORM "Switch"
6#elif defined(__WIIU__)
7#define PLATFORM "Wii U"
8#include <coreinit/core.h>
9#include <coreinit/foreground.h>
10#include <proc_ui/procui.h>
11#include <sysapp/launch.h>
12#elif defined(_3DS)
13#define PLATFORM "3DS"
14#else
15#define PLATFORM "Console"
16#endif
17
18#if defined(USE_RAMFS)
19#include "../libs/resinfs/include/romfs-wiiu.h"
20#endif
21
22#include "RootDisplay.hpp"
23#include "DownloadQueue.hpp"
24#include "Button.hpp"
25
26CST_Renderer* RootDisplay::renderer = NULL;
27CST_Window* RootDisplay::window = NULL;
28Element* RootDisplay::subscreen = NULL;
29Element* RootDisplay::nextsubscreen = NULL;
30RootDisplay* RootDisplay::mainDisplay = NULL;
31bool RootDisplay::isDebug = false;
32
33int RootDisplay::screenWidth = 1280;
34int RootDisplay::screenHeight = 720;
35float RootDisplay::dpiScale = 1.0f;
36
37bool RootDisplay::idleCursorPulsing = false;
38
39RootDisplay::RootDisplay()
40{
41 // initialize the romfs for switch/wiiu
42#if defined(USE_RAMFS)
43 ramfsInit();
44#endif
45 // initialize internal drawing library
46 CST_DrawInit(this);
47
48 this->x = 0;
49 this->y = 0;
50 this->width = SCREEN_WIDTH;
51 this->height = SCREEN_HEIGHT;
52
53 this->hasBackground = true;
54
55#ifdef __APPLE__
56 chdirForPlatform();
57#endif
58
59 // set the display scale on high resolution displays
60 RootDisplay::dpiScale = CST_GetDpiScale();
61
62 // set platform-specific default background colors, (which can be overridden)
63#if defined(__WIIU__) || defined(WIIU_MOCK)
64 this->backgroundColor = fromRGB(0x20 - 0x10, 154 - 0x10, 199 - 0x10);
65#elif defined(WII) || defined(WII_MOCK)
66 // the system wii gray
67 this->backgroundColor = fromRGB(0x8b, 0x8b, 0x8b);
68#elif defined(_3DS) || defined(_3DS_MOCK)
69 this->backgroundColor = fromRGB(30, 30, 30);
70#elif defined(SWITCH) || defined(SWITCH_MOCK)
71 this->backgroundColor = fromRGB(0xd6, 0x0 + 0x20, 0x12 + 0x20);
72#else
73 this->backgroundColor = fromRGB(0x2d, 0x26, 0x49);
74#endif
75
76 // set starting resolution based on SDL version
77#if defined(WII) || defined(WII_MOCK)
78 setScreenResolution(640, 480);
79#elif defined(_3DS) || defined(_3DS_MOCK)
80 setScreenResolution(400, 480); // 3ds has a special resolution!
81#else
82 // setScreenResolution(640, 480);
83 setScreenResolution(1280, 720);
84#endif
85 // the main input handler
86 this->events = new InputEvents();
87
88 // TODO: initialize this in a way that doesn't block the main thread
89 // always load english first, to initialize defaults
90 TextElement::loadI18nCache("en-us");
91
92 // TODO: detect language and system, and store preference
93 // TextElement::loadI18nCache("zh-cn");
94}
95
96void RootDisplay::initMusic()
97{
98#ifdef SWITCH
99 // no music if we're in applet mode
100 // they use up too much memory, and a lot of people only use applet mode
101 AppletType at = appletGetAppletType();
102 if (at != AppletType_Application && at != AppletType_SystemApplication) {
103 return;
104 }
105#endif
106
107 // Initialize CST_mixer
108 CST_MixerInit(this);
109}
110
111void RootDisplay::startMusic()
112{
113 CST_FadeInMusic(this);
114}
115
116void RootDisplay::setScreenResolution(int width, int height)
117{
118 // set the screen resolution
119 SCREEN_WIDTH = width;
120 SCREEN_HEIGHT = height;
121
122 // update the root element
123 this->width = SCREEN_WIDTH;
124 this->height = SCREEN_HEIGHT;
125
126 // update the renderer, but respect the DPI scaling
127 CST_SetWindowSize(window, SCREEN_WIDTH / RootDisplay::dpiScale, SCREEN_HEIGHT / RootDisplay::dpiScale);
128}
129
130RootDisplay::~RootDisplay()
131{
132 CST_DrawExit();
133
134#if defined(USE_RAMFS)
135 ramfsExit();
136#endif
137}
138
140{
141 if (nextsubscreen != subscreen)
142 {
143 delete subscreen;
144 subscreen = nextsubscreen;
145 return true;
146 }
147
148 // process either the subscreen or the children elements, always return true if "dragging"
149 // (may be a mouse cursor or wiimote pointing and moving on the screen)
150
151 if (RootDisplay::subscreen)
152 return RootDisplay::subscreen->process(event) || event->isTouchDrag();
153
154 // keep processing child elements
155 return super::process(event) || event->isTouchDrag();
156}
157
159{
160 if (RootDisplay::subscreen)
161 {
162 RootDisplay::subscreen->render(this);
163 this->update();
164 return;
165 }
166
167 // render the rest of the subelements
169
170 // commit everything to the screen
171 this->update();
172}
173
174void RootDisplay::update()
175{
176 // never exceed 60fps because there's no point
177 // commented out, as if render isn't called manually,
178 // the CST_Delay in the input processing loop should handle this
179
180 // int now = CST_GetTicks();
181 // int diff = now - this->lastFrameTime;
182
183 // if (diff < 16)
184 // return;
185
186 CST_RenderPresent(this->renderer);
187 // this->lastFrameTime = now;
188}
189
190void RootDisplay::switchSubscreen(Element* next)
191{
192 if (nextsubscreen != subscreen)
193 delete nextsubscreen;
194 nextsubscreen = next;
195}
196
197#ifdef __WIIU__
198// proc ui will block if it keeps control
199void RootDisplay::processWiiUHomeOverlay() {
200 auto status = ProcUIProcessMessages(true);
201 if (status == PROCUI_STATUS_EXITING)
202 exit(0);
203 else if (status == PROCUI_STATUS_RELEASE_FOREGROUND)
204 ProcUIDrawDoneRelease();
205}
206#endif
207
208int RootDisplay::mainLoop()
209{
210 DownloadQueue::init();
211
212#ifdef __WIIU__
213 // setup procui callback for resuming application to force a chesto render
214 // https://stackoverflow.com/a/56145528 and http://bannalia.blogspot.com/2016/07/passing-capturing-c-lambda-functions-as.html
215 auto updateDisplay = +[](void* display) -> unsigned int {
216 ((RootDisplay*)display)->futureRedrawCounter = 10;
217 return 0;
218 };
219 ProcUIRegisterCallback(PROCUI_CALLBACK_ACQUIRE, updateDisplay, this, 100);
220#endif
221
222 while (isRunning)
223 {
224 bool atLeastOneNewEvent = false;
225 bool viewChanged = false;
226
227 #ifdef __WIIU__
228 processWiiUHomeOverlay();
229 #endif
230
231 int frameStart = CST_GetTicks();
232
233 // update download queue
234 DownloadQueue::downloadQueue->process();
235
236 // get any new input events
237 while (events->update())
238 {
239 // process the inputs of the supplied event
240 viewChanged |= this->process(events);
241 atLeastOneNewEvent = true;
242
243 // if we see a minus, exit immediately!
244 if (events->pressed(SELECT_BUTTON) && this->canUseSelectToExit) {
245 if (events->quitaction != NULL) events->quitaction();
246 else isRunning = false;
247 }
248 }
249
250 // one more event update if nothing changed or there were no previous events seen
251 // needed to non-input related processing that might update the screen to take place
252 if ((!atLeastOneNewEvent && !viewChanged))
253 {
254 events->update();
255 viewChanged |= this->process(events);
256 }
257
258 // draw the display if we processed an event or the view
259 if (viewChanged)
260 this->render(NULL);
261 else
262 {
263 // delay for the remainder of the frame to keep up to 60fps
264 // (we only do this if we didn't draw to not waste energy
265 // if we did draw, then proceed immediately without waiting for smoother progress bars / scrolling)
266 int delayTime = (CST_GetTicks() - frameStart);
267 if (delayTime < 0)
268 delayTime = 0;
269 if (delayTime < 16)
270 CST_Delay(16 - delayTime);
271 }
272
273 // free up any elements that are in the trash
274 this->recycle();
275 }
276
277 delete events;
278
279 if (!isProtected) delete this;
280 DownloadQueue::quit();
281
282 return 0;
283}
284
285void RootDisplay::recycle()
286{
287 for (auto e : trash)
288 e->wipeAll(true);
289 trash.clear();
290}
int process()
process finished and queued downloads
virtual bool process(InputEvents *event)
process any input that is received for this element
Definition: Element.cpp:17
virtual void render(Element *parent)
display the current state of the display
Definition: Element.cpp:60
int width
width and height of this element (must be manually set, isn't usually calculated (but is in some case...
Definition: Element.hpp:118
Element * parent
the parent element (can sometimes be null if it isn't set)
Definition: Element.hpp:102
bool process(InputEvents *event)
process any input that is received for this element
void render(Element *parent)
display the current state of the display