Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/nyx/nyx_gui/frontend/gui.c
3711 views
1
/*
2
* Copyright (c) 2018-2026 CTCaer
3
*
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms and conditions of the GNU General Public License,
6
* version 2, as published by the Free Software Foundation.
7
*
8
* This program is distributed in the hope it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11
* more details.
12
*
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15
*/
16
17
#include <stdlib.h>
18
19
#include <bdk.h>
20
21
#include "gui.h"
22
#include "gui_emummc_tools.h"
23
#include "gui_tools.h"
24
#include "gui_info.h"
25
#include "gui_options.h"
26
#include <libs/lvgl/lv_themes/lv_theme_hekate.h>
27
#include <libs/lvgl/lvgl.h>
28
#include "../gfx/logos-gui.h"
29
30
#include "../config.h"
31
#include <libs/fatfs/ff.h>
32
33
extern volatile boot_cfg_t *b_cfg;
34
extern volatile nyx_storage_t *nyx_str;
35
36
extern lv_res_t launch_payload(lv_obj_t *list);
37
38
static bool disp_init_done = false;
39
static bool do_auto_reload = false;
40
41
lv_style_t hint_small_style;
42
lv_style_t hint_small_style_white;
43
lv_style_t monospace_text;
44
45
lv_obj_t *payload_list;
46
lv_obj_t *autorcm_btn;
47
lv_obj_t *close_btn;
48
49
lv_img_dsc_t *icon_switch;
50
lv_img_dsc_t *icon_payload;
51
lv_img_dsc_t *icon_lakka;
52
53
lv_img_dsc_t *hekate_bg;
54
55
lv_style_t btn_transp_rel, btn_transp_pr, btn_transp_tgl_rel, btn_transp_tgl_pr, btn_transp_ina;
56
lv_style_t ddlist_transp_bg, ddlist_transp_sel;
57
lv_style_t tabview_btn_pr, tabview_btn_tgl_pr;
58
59
lv_style_t mbox_darken;
60
61
char *text_color;
62
63
typedef struct _jc_lv_driver_t
64
{
65
lv_indev_t *indev_jc;
66
lv_indev_t *indev_touch;
67
// LV_INDEV_READ_PERIOD * JC_CAL_MAX_STEPS = 264 ms.
68
#define JC_CAL_MAX_STEPS 8
69
u32 calibration_step;
70
u16 cx_max;
71
u16 cx_min;
72
u16 cy_max;
73
u16 cy_min;
74
s16 pos_x;
75
s16 pos_y;
76
s16 pos_last_x;
77
s16 pos_last_y;
78
lv_obj_t *cursor;
79
u32 cursor_timeout;
80
bool cursor_hidden;
81
u32 console_timeout;
82
} jc_lv_driver_t;
83
84
static jc_lv_driver_t jc_drv_ctx;
85
86
gui_status_bar_ctx status_bar;
87
88
static void _nyx_disp_init()
89
{
90
vic_surface_t vic_sfc;
91
vic_sfc.src_buf = NYX_FB2_ADDRESS;
92
vic_sfc.dst_buf = NYX_FB_ADDRESS;
93
vic_sfc.width = 1280;
94
vic_sfc.height = 720;
95
vic_sfc.pix_fmt = VIC_PIX_FORMAT_X8R8G8B8;
96
vic_sfc.rotation = VIC_ROTATION_270;
97
98
// Set hardware rotation via VIC.
99
vic_init();
100
vic_set_surface(&vic_sfc);
101
102
// Turn off backlight to hide the transition.
103
display_backlight_brightness(0, 1000);
104
105
// Rotate and copy the first frame.
106
vic_compose();
107
vic_wait_idle();
108
109
// Switch to new window configuration.
110
display_init_window_a_pitch_vic();
111
112
// Enable logging on window D.
113
display_init_window_d_console();
114
115
// Switch back the backlight.
116
display_backlight_brightness(h_cfg.backlight - 20, 1000);
117
}
118
119
static void _save_log_to_bmp(char *fname)
120
{
121
u32 *fb_ptr = (u32 *)LOG_FB_ADDRESS;
122
123
// Check if there's log written.
124
bool log_changed = false;
125
for (u32 i = 0; i < 0xCD000; i++)
126
{
127
if (fb_ptr[i] != 0)
128
{
129
log_changed = true;
130
break;
131
}
132
}
133
134
if (!log_changed)
135
return;
136
137
const u32 file_size = LOG_FB_SZ + 0x36;
138
u8 *bitmap = malloc(file_size);
139
140
// Reconstruct FB for bottom-top, landscape bmp. Rotation: 656x1280 -> 1280x656.
141
u32 *fb = malloc(LOG_FB_SZ);
142
for (int x = 1279; x > - 1; x--)
143
{
144
for (int y = 655; y > -1; y--)
145
fb[y * 1280 + x] = *fb_ptr++;
146
}
147
148
manual_system_maintenance(true);
149
150
memcpy(bitmap + 0x36, fb, LOG_FB_SZ);
151
152
typedef struct _bmp_t
153
{
154
u16 magic;
155
u32 size;
156
u32 rsvd;
157
u32 data_off;
158
u32 hdr_size;
159
u32 width;
160
u32 height;
161
u16 planes;
162
u16 pxl_bits;
163
u32 comp;
164
u32 img_size;
165
u32 res_h;
166
u32 res_v;
167
u64 rsvd2;
168
} __attribute__((packed)) bmp_t;
169
170
bmp_t *bmp = (bmp_t *)bitmap;
171
172
bmp->magic = 0x4D42;
173
bmp->size = file_size;
174
bmp->rsvd = 0;
175
bmp->data_off = 0x36;
176
bmp->hdr_size = 40;
177
bmp->width = 1280;
178
bmp->height = 656;
179
bmp->planes = 1;
180
bmp->pxl_bits = 32;
181
bmp->comp = 0;
182
bmp->img_size = LOG_FB_SZ;
183
bmp->res_h = 2834;
184
bmp->res_v = 2834;
185
bmp->rsvd2 = 0;
186
187
char path[0x80];
188
strcpy(path, "bootloader/screenshots");
189
s_printf(path + strlen(path), "/nyx%s_log.bmp", fname);
190
sd_save_to_file(bitmap, file_size, path);
191
192
free(bitmap);
193
free(fb);
194
}
195
196
static void _save_fb_to_bmp()
197
{
198
// Disallow screenshots if less than 2s passed.
199
static u32 timer = 0;
200
if (get_tmr_ms() < timer)
201
return;
202
203
if (do_auto_reload)
204
goto exit;
205
206
// Invalidate data.
207
bpmp_mmu_maintenance(BPMP_MMU_MAINT_INVALID_WAY, false);
208
209
const u32 file_size = NYX_FB_SZ + 0x36;
210
u8 *bitmap = malloc(file_size);
211
u32 *fb = malloc(NYX_FB_SZ);
212
u32 *fb_ptr = (u32 *)NYX_FB2_ADDRESS;
213
u32 line_bytes = 1280 * sizeof(u32);
214
215
// Reconstruct FB for bottom-top, landscape bmp. No rotation.
216
for (int y = 719; y > -1; y--)
217
{
218
memcpy(&fb[y * 1280], fb_ptr, line_bytes);
219
fb_ptr += line_bytes / sizeof(u32);
220
}
221
222
// Create notification box.
223
lv_obj_t * mbox = lv_mbox_create(lv_layer_top(), NULL);
224
lv_mbox_set_recolor_text(mbox, true);
225
lv_mbox_set_text(mbox, SYMBOL_CAMERA" #FFDD00 Saving screenshot...#");
226
lv_obj_set_width(mbox, LV_DPI * 4);
227
lv_obj_set_top(mbox, true);
228
lv_obj_align(mbox, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
229
230
// Capture effect.
231
display_backlight_brightness(255, 100);
232
msleep(150);
233
display_backlight_brightness(h_cfg.backlight - 20, 100);
234
235
manual_system_maintenance(true);
236
237
memcpy(bitmap + 0x36, fb, NYX_FB_SZ);
238
239
typedef struct _bmp_t
240
{
241
u16 magic;
242
u32 size;
243
u32 rsvd;
244
u32 data_off;
245
u32 hdr_size;
246
u32 width;
247
u32 height;
248
u16 planes;
249
u16 pxl_bits;
250
u32 comp;
251
u32 img_size;
252
u32 res_h;
253
u32 res_v;
254
u64 rsvd2;
255
} __attribute__((packed)) bmp_t;
256
257
bmp_t *bmp = (bmp_t *)bitmap;
258
259
bmp->magic = 0x4D42;
260
bmp->size = file_size;
261
bmp->rsvd = 0;
262
bmp->data_off = 0x36;
263
bmp->hdr_size = 40;
264
bmp->width = 1280;
265
bmp->height = 720;
266
bmp->planes = 1;
267
bmp->pxl_bits = 32;
268
bmp->comp = 0;
269
bmp->img_size = NYX_FB_SZ;
270
bmp->res_h = 2834;
271
bmp->res_v = 2834;
272
bmp->rsvd2 = 0;
273
274
sd_mount();
275
276
char path[0x80];
277
278
strcpy(path, "bootloader");
279
f_mkdir(path);
280
strcat(path, "/screenshots");
281
f_mkdir(path);
282
283
// Create date/time name.
284
char fname[32];
285
rtc_time_t time;
286
max77620_rtc_get_time_adjusted(&time);
287
s_printf(fname, "%04d%02d%02d_%02d%02d%02d", time.year, time.month, time.day, time.hour, time.min, time.sec);
288
s_printf(path + strlen(path), "/nyx%s.bmp", fname);
289
290
// Save screenshot and log.
291
int res = sd_save_to_file(bitmap, file_size, path);
292
if (!res)
293
_save_log_to_bmp(fname);
294
295
sd_unmount();
296
297
free(bitmap);
298
free(fb);
299
300
if (!res)
301
lv_mbox_set_text(mbox, SYMBOL_CAMERA" #96FF00 Screenshot saved!#");
302
else
303
lv_mbox_set_text(mbox, SYMBOL_WARNING" #FFDD00 Screenshot failed!#");
304
manual_system_maintenance(true);
305
lv_mbox_start_auto_close(mbox, 4000);
306
307
exit:
308
// Set timer to 2s.
309
timer = get_tmr_ms() + 2000;
310
}
311
312
static void _disp_fb_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t *color_p)
313
{
314
// Draw to intermediate non-rotated framebuffer.
315
gfx_set_rect_pitch((u32 *)NYX_FB2_ADDRESS, (u32 *)color_p, 1280, x1, y1, x2, y2);
316
317
// Rotate and copy to visible framebuffer.
318
if (disp_init_done)
319
vic_compose();
320
321
// Check if display init was done. If it's the first big draw, init.
322
if (!disp_init_done && ((x2 - x1 + 1) > 600))
323
{
324
disp_init_done = true;
325
_nyx_disp_init();
326
}
327
328
lv_flush_ready();
329
}
330
331
static touch_event_t touchpad;
332
static bool touch_enabled;
333
static bool console_enabled = false;
334
335
static bool _fts_touch_read(lv_indev_data_t *data)
336
{
337
if (!touch_enabled)
338
return false;
339
340
int res = touch_poll(&touchpad);
341
342
// Take a screenshot if 3rd finger.
343
if (touchpad.finger > 2)
344
{
345
_save_fb_to_bmp();
346
347
data->state = LV_INDEV_STATE_REL;
348
return false;
349
}
350
351
if (console_enabled)
352
{
353
// If no event, keep last debug message.
354
if (res)
355
return false;
356
357
// Print input debugging in console.
358
gfx_con_getpos(&gfx_con.savedx, &gfx_con.savedy, &gfx_con.savedcol);
359
gfx_con_setpos(32, 638, GFX_COL_AUTO);
360
gfx_con.fntsz = 8;
361
gfx_printf("x: %4d, y: %4d | z: %3d | ", touchpad.x, touchpad.y, touchpad.z);
362
gfx_printf("0: %02X, 1: %02X, 2: %02X, ", touchpad.raw[0], touchpad.raw[1], touchpad.raw[2]);
363
gfx_printf("3: %02X, 4: %02X, 5: %02X, 6: %02X",
364
touchpad.raw[3], touchpad.raw[4], touchpad.raw[5], touchpad.raw[6]);
365
gfx_con_setpos(gfx_con.savedx, gfx_con.savedy, gfx_con.savedcol);
366
gfx_con.fntsz = 16;
367
368
return false;
369
}
370
371
// Always set touch points.
372
data->point.x = touchpad.x;
373
data->point.y = touchpad.y;
374
375
// Decide touch enable.
376
if (touchpad.touch)
377
data->state = LV_INDEV_STATE_PR;
378
else
379
data->state = LV_INDEV_STATE_REL;
380
381
return false; // No buffering so no more data read.
382
}
383
384
static bool _jc_virt_mouse_read(lv_indev_data_t *data)
385
{
386
// Poll Joy-Con.
387
jc_gamepad_rpt_t *jc_pad = joycon_poll();
388
389
if (!jc_pad)
390
{
391
data->state = LV_INDEV_STATE_REL;
392
return false;
393
}
394
395
// Take a screenshot if Capture button is pressed.
396
if (jc_pad->cap)
397
{
398
_save_fb_to_bmp();
399
400
data->state = LV_INDEV_STATE_REL;
401
return false;
402
}
403
404
// Calibrate left stick.
405
if (jc_drv_ctx.calibration_step != JC_CAL_MAX_STEPS)
406
{
407
if (n_cfg.jc_force_right)
408
{
409
if (jc_pad->conn_r
410
&& jc_pad->rstick_x > 0x400 && jc_pad->rstick_y > 0x400
411
&& jc_pad->rstick_x < 0xC00 && jc_pad->rstick_y < 0xC00)
412
{
413
jc_drv_ctx.calibration_step++;
414
jc_drv_ctx.cx_max = jc_pad->rstick_x + 0x96;
415
jc_drv_ctx.cx_min = jc_pad->rstick_x - 0x96;
416
jc_drv_ctx.cy_max = jc_pad->rstick_y + 0x96;
417
jc_drv_ctx.cy_min = jc_pad->rstick_y - 0x96;
418
jc_drv_ctx.cursor_timeout = 0;
419
}
420
}
421
else if (jc_pad->conn_l
422
&& jc_pad->lstick_x > 0x400 && jc_pad->lstick_y > 0x400
423
&& jc_pad->lstick_x < 0xC00 && jc_pad->lstick_y < 0xC00)
424
{
425
jc_drv_ctx.calibration_step++;
426
jc_drv_ctx.cx_max = jc_pad->lstick_x + 0x96;
427
jc_drv_ctx.cx_min = jc_pad->lstick_x - 0x96;
428
jc_drv_ctx.cy_max = jc_pad->lstick_y + 0x96;
429
jc_drv_ctx.cy_min = jc_pad->lstick_y - 0x96;
430
jc_drv_ctx.cursor_timeout = 0;
431
}
432
433
if (jc_drv_ctx.calibration_step != JC_CAL_MAX_STEPS)
434
{
435
if (jc_pad->plus || jc_pad->minus)
436
goto handle_console;
437
438
if (console_enabled)
439
goto console;
440
441
data->state = LV_INDEV_STATE_REL;
442
443
return false;
444
}
445
}
446
447
// Re-calibrate on disconnection.
448
if (n_cfg.jc_force_right && !jc_pad->conn_r)
449
jc_drv_ctx.calibration_step = 0;
450
else if (!n_cfg.jc_force_right && !jc_pad->conn_l)
451
jc_drv_ctx.calibration_step = 0;
452
453
// Set button presses.
454
if (jc_pad->a || jc_pad->zl || jc_pad->zr)
455
data->state = LV_INDEV_STATE_PR;
456
else
457
data->state = LV_INDEV_STATE_REL;
458
459
// Enable console.
460
if (jc_pad->plus || jc_pad->minus)
461
{
462
handle_console:
463
if (((u32)get_tmr_ms() - jc_drv_ctx.console_timeout) > 1000)
464
{
465
if (!console_enabled)
466
{
467
display_window_d_console_enable();
468
console_enabled = true;
469
gfx_con_getpos(&gfx_con.savedx, &gfx_con.savedy, &gfx_con.savedcol);
470
gfx_con_setpos(964, 630, GFX_COL_AUTO);
471
gfx_printf("Press -/+ to close");
472
gfx_con_setpos(gfx_con.savedx, gfx_con.savedy, gfx_con.savedcol);
473
}
474
else
475
{
476
display_window_d_console_disable();
477
console_enabled = false;
478
}
479
480
jc_drv_ctx.console_timeout = get_tmr_ms();
481
}
482
483
data->state = LV_INDEV_STATE_REL;
484
485
return false;
486
}
487
488
if (console_enabled)
489
{
490
console:
491
// Print input debugging in console.
492
gfx_con_getpos(&gfx_con.savedx, &gfx_con.savedy, &gfx_con.savedcol);
493
gfx_con_setpos(32, 630, GFX_COL_AUTO);
494
gfx_con.fntsz = 8;
495
gfx_printf("x: %4X, y: %4X | rx: %4X, ry: %4X | b: %06X | c: %d (%d), %d (%d)",
496
jc_pad->lstick_x, jc_pad->lstick_y, jc_pad->rstick_x, jc_pad->rstick_y,
497
jc_pad->buttons, jc_pad->batt_info_l, jc_pad->batt_chrg_l,
498
jc_pad->batt_info_r, jc_pad->batt_chrg_r);
499
gfx_con_setpos(gfx_con.savedx, gfx_con.savedy, gfx_con.savedcol);
500
gfx_con.fntsz = 16;
501
502
data->state = LV_INDEV_STATE_REL;
503
504
return false;
505
}
506
507
// Calculate new cursor position.
508
if (!n_cfg.jc_force_right)
509
{
510
// Left stick X.
511
if (jc_pad->lstick_x <= jc_drv_ctx.cx_max && jc_pad->lstick_x >= jc_drv_ctx.cx_min)
512
jc_drv_ctx.pos_x += 0;
513
else if (jc_pad->lstick_x > jc_drv_ctx.cx_max)
514
jc_drv_ctx.pos_x += ((jc_pad->lstick_x - jc_drv_ctx.cx_max) / 30);
515
else
516
jc_drv_ctx.pos_x -= ((jc_drv_ctx.cx_min - jc_pad->lstick_x) / 30);
517
518
// Left stick Y.
519
if (jc_pad->lstick_y <= jc_drv_ctx.cy_max && jc_pad->lstick_y >= jc_drv_ctx.cy_min)
520
jc_drv_ctx.pos_y += 0;
521
else if (jc_pad->lstick_y > jc_drv_ctx.cy_max)
522
{
523
s16 val = (jc_pad->lstick_y - jc_drv_ctx.cy_max) / 30;
524
// Hoag has inverted Y axis.
525
if (jc_pad->sio_mode)
526
val *= -1;
527
jc_drv_ctx.pos_y -= val;
528
}
529
else
530
{
531
s16 val = (jc_drv_ctx.cy_min - jc_pad->lstick_y) / 30;
532
// Hoag has inverted Y axis.
533
if (jc_pad->sio_mode)
534
val *= -1;
535
jc_drv_ctx.pos_y += val;
536
}
537
}
538
else
539
{
540
// Right stick X.
541
if (jc_pad->rstick_x <= jc_drv_ctx.cx_max && jc_pad->rstick_x >= jc_drv_ctx.cx_min)
542
jc_drv_ctx.pos_x += 0;
543
else if (jc_pad->rstick_x > jc_drv_ctx.cx_max)
544
jc_drv_ctx.pos_x += ((jc_pad->rstick_x - jc_drv_ctx.cx_max) / 30);
545
else
546
jc_drv_ctx.pos_x -= ((jc_drv_ctx.cx_min - jc_pad->rstick_x) / 30);
547
548
// Right stick Y.
549
if (jc_pad->rstick_y <= jc_drv_ctx.cy_max && jc_pad->rstick_y >= jc_drv_ctx.cy_min)
550
jc_drv_ctx.pos_y += 0;
551
else if (jc_pad->rstick_y > jc_drv_ctx.cy_max)
552
{
553
s16 val = (jc_pad->rstick_y - jc_drv_ctx.cy_max) / 30;
554
// Hoag has inverted Y axis.
555
if (jc_pad->sio_mode)
556
val *= -1;
557
jc_drv_ctx.pos_y -= val;
558
}
559
else
560
{
561
s16 val = (jc_drv_ctx.cy_min - jc_pad->rstick_y) / 30;
562
// Hoag has inverted Y axis.
563
if (jc_pad->sio_mode)
564
val *= -1;
565
jc_drv_ctx.pos_y += val;
566
}
567
}
568
569
// Ensure value inside screen limits.
570
if (jc_drv_ctx.pos_x < 0)
571
jc_drv_ctx.pos_x = 0;
572
else if (jc_drv_ctx.pos_x > 1279)
573
jc_drv_ctx.pos_x = 1279;
574
575
if (jc_drv_ctx.pos_y < 0)
576
jc_drv_ctx.pos_y = 0;
577
else if (jc_drv_ctx.pos_y > 719)
578
jc_drv_ctx.pos_y = 719;
579
580
// Set cursor position.
581
data->point.x = jc_drv_ctx.pos_x;
582
data->point.y = jc_drv_ctx.pos_y;
583
584
// Auto hide cursor.
585
if (jc_drv_ctx.pos_x != jc_drv_ctx.pos_last_x || jc_drv_ctx.pos_y != jc_drv_ctx.pos_last_y)
586
{
587
jc_drv_ctx.pos_last_x = jc_drv_ctx.pos_x;
588
jc_drv_ctx.pos_last_y = jc_drv_ctx.pos_y;
589
590
jc_drv_ctx.cursor_hidden = false;
591
jc_drv_ctx.cursor_timeout = get_tmr_ms();
592
lv_indev_set_cursor(jc_drv_ctx.indev_jc, jc_drv_ctx.cursor);
593
594
// Un hide cursor.
595
lv_obj_set_opa_scale_enable(jc_drv_ctx.cursor, false);
596
}
597
else
598
{
599
if (!jc_drv_ctx.cursor_hidden)
600
{
601
if (((u32)get_tmr_ms() - jc_drv_ctx.cursor_timeout) > 3000)
602
{
603
// Remove cursor and hide it.
604
lv_indev_set_cursor(jc_drv_ctx.indev_jc, NULL);
605
lv_obj_set_opa_scale_enable(jc_drv_ctx.cursor, true);
606
lv_obj_set_opa_scale(jc_drv_ctx.cursor, LV_OPA_TRANSP);
607
608
jc_drv_ctx.cursor_hidden = true;
609
}
610
}
611
else
612
data->state = LV_INDEV_STATE_REL; // Ensure that no clicks are allowed.
613
}
614
615
if (jc_pad->b && close_btn)
616
{
617
lv_action_t close_btn_action = lv_btn_get_action(close_btn, LV_BTN_ACTION_CLICK);
618
close_btn_action(close_btn);
619
close_btn = NULL;
620
}
621
622
return false; // No buffering so no more data read.
623
}
624
625
typedef struct _system_maintenance_tasks_t
626
{
627
union
628
{
629
lv_task_t *tasks[2];
630
struct
631
{
632
lv_task_t *status_bar;
633
lv_task_t *dram_periodic_comp;
634
} task;
635
};
636
} system_maintenance_tasks_t;
637
638
static system_maintenance_tasks_t system_tasks;
639
640
void manual_system_maintenance(bool refresh)
641
{
642
for (u32 task_idx = 0; task_idx < (sizeof(system_maintenance_tasks_t) / sizeof(lv_task_t *)); task_idx++)
643
{
644
lv_task_t *task = system_tasks.tasks[task_idx];
645
if (task && (lv_tick_elaps(task->last_run) >= task->period))
646
{
647
task->last_run = lv_tick_get();
648
task->task(task->param);
649
}
650
}
651
if (refresh)
652
lv_refr_now();
653
}
654
655
lv_img_dsc_t *bmp_to_lvimg_obj(const char *path)
656
{
657
u32 fsize;
658
u8 *bitmap = sd_file_read(path, &fsize);
659
if (!bitmap)
660
return NULL;
661
662
struct _bmp_data
663
{
664
u32 size;
665
u32 size_x;
666
u32 size_y;
667
u32 offset;
668
};
669
670
struct _bmp_data bmpData;
671
672
// Get values manually to avoid unaligned access.
673
bmpData.size = bitmap[2] | bitmap[3] << 8 |
674
bitmap[4] << 16 | bitmap[5] << 24;
675
bmpData.offset = bitmap[10] | bitmap[11] << 8 |
676
bitmap[12] << 16 | bitmap[13] << 24;
677
bmpData.size_x = bitmap[18] | bitmap[19] << 8 |
678
bitmap[20] << 16 | bitmap[21] << 24;
679
bmpData.size_y = bitmap[22] | bitmap[23] << 8 |
680
bitmap[24] << 16 | bitmap[25] << 24;
681
// Sanity check.
682
if (bitmap[0] == 'B' &&
683
bitmap[1] == 'M' &&
684
bitmap[28] == 32 && // Only 32 bit BMPs allowed.
685
bmpData.size <= fsize)
686
{
687
// Check if non-default Bottom-Top.
688
bool flipped = false;
689
if (bmpData.size_y & 0x80000000)
690
{
691
bmpData.size_y = ~(bmpData.size_y) + 1;
692
flipped = true;
693
}
694
695
lv_img_dsc_t *img_desc = (lv_img_dsc_t *)bitmap;
696
uptr offset_copy = ALIGN((uptr)bitmap + sizeof(lv_img_dsc_t), 0x10);
697
698
img_desc->header.always_zero = 0;
699
img_desc->header.w = bmpData.size_x;
700
img_desc->header.h = bmpData.size_y;
701
img_desc->header.cf = (bitmap[28] == 32) ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; // Only LV_IMG_CF_TRUE_COLOR_ALPHA is actually allowed.
702
img_desc->data_size = bmpData.size - bmpData.offset;
703
img_desc->data = (u8 *)offset_copy;
704
705
u32 *tmp = malloc(bmpData.size);
706
u32 *tmp2 = (u32 *)offset_copy;
707
708
// Copy the unaligned data to an aligned buffer.
709
memcpy((u8 *)tmp, bitmap + bmpData.offset, img_desc->data_size);
710
u32 j = 0;
711
712
if (!flipped)
713
{
714
for (u32 y = 0; y < bmpData.size_y; y++)
715
{
716
for (u32 x = 0; x < bmpData.size_x; x++)
717
tmp2[j++] = tmp[(bmpData.size_y - 1 - y ) * bmpData.size_x + x];
718
}
719
}
720
else
721
{
722
for (u32 y = 0; y < bmpData.size_y; y++)
723
{
724
for (u32 x = 0; x < bmpData.size_x; x++)
725
tmp2[j++] = tmp[y * bmpData.size_x + x];
726
}
727
}
728
729
free(tmp);
730
}
731
else
732
{
733
free(bitmap);
734
return NULL;
735
}
736
737
return (lv_img_dsc_t *)bitmap;
738
}
739
740
lv_res_t nyx_generic_onoff_toggle(lv_obj_t *btn)
741
{
742
lv_obj_t *label_btn = lv_obj_get_child(btn, NULL);
743
lv_obj_t *label_btn2 = lv_obj_get_child(btn, label_btn);
744
745
char label_text[64];
746
if (!label_btn2)
747
{
748
strcpy(label_text, lv_label_get_text(label_btn));
749
label_text[strlen(label_text) - 15] = 0;
750
751
if (!(lv_btn_get_state(btn) & LV_BTN_STATE_TGL_REL))
752
{
753
strcat(label_text, "#D0D0D0 OFF#");
754
lv_label_set_text(label_btn, label_text);
755
}
756
else
757
{
758
s_printf(label_text, "%s%s%s", label_text, text_color, " ON #");
759
lv_label_set_text(label_btn, label_text);
760
}
761
}
762
else
763
{
764
if (!(lv_btn_get_state(btn) & LV_BTN_STATE_TGL_REL))
765
lv_label_set_text(label_btn, "#D0D0D0 OFF#");
766
else
767
{
768
s_printf(label_text, "%s%s", text_color, " ON #");
769
lv_label_set_text(label_btn, label_text);
770
}
771
}
772
773
return LV_RES_OK;
774
}
775
776
lv_res_t nyx_mbox_action(lv_obj_t *btns, const char *txt)
777
{
778
lv_obj_t *mbox = lv_mbox_get_from_btn(btns);
779
lv_obj_t *dark_bg = lv_obj_get_parent(mbox);
780
781
lv_obj_del(dark_bg); // Deletes children also (mbox).
782
783
return LV_RES_INV;
784
}
785
786
bool nyx_emmc_check_battery_enough()
787
{
788
if (h_cfg.devmode)
789
return true;
790
791
int batt_volt = 0;
792
793
max17050_get_property(MAX17050_VCELL, &batt_volt);
794
795
if (batt_volt && batt_volt < 3650)
796
{
797
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
798
lv_obj_set_style(dark_bg, &mbox_darken);
799
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
800
801
static const char * mbox_btn_map[] = { "\251", "\222OK", "\251", "" };
802
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
803
lv_mbox_set_recolor_text(mbox, true);
804
805
lv_mbox_set_text(mbox,
806
"#FF8000 Battery Check#\n\n"
807
"#FFDD00 Battery is not enough to carry on#\n"
808
"#FFDD00 with selected operation!#\n\n"
809
"Charge to at least #C7EA46 3650 mV#, and try again!");
810
811
lv_mbox_add_btns(mbox, mbox_btn_map, nyx_mbox_action);
812
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
813
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
814
lv_obj_set_top(mbox, true);
815
816
return false;
817
}
818
819
return true;
820
}
821
822
static void _nyx_sd_card_issues_warning(void *param)
823
{
824
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
825
lv_obj_set_style(dark_bg, &mbox_darken);
826
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
827
828
static const char * mbox_btn_map[] = { "\251", "\222OK", "\251", "" };
829
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
830
lv_mbox_set_recolor_text(mbox, true);
831
832
lv_mbox_set_text(mbox,
833
"#FF8000 SD Card Issues Warning#\n\n"
834
"#FFDD00 The SD Card is initialized in 1-bit mode!#\n"
835
"#FFDD00 This might mean detached or broken connector!#\n\n"
836
"You might want to check\n#C7EA46 Console Info# -> #C7EA46 microSD#");
837
838
lv_mbox_add_btns(mbox, mbox_btn_map, nyx_mbox_action);
839
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
840
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
841
lv_obj_set_top(mbox, true);
842
}
843
844
void nyx_window_toggle_buttons(lv_obj_t *win, bool disable)
845
{
846
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
847
lv_obj_t * hbtn;
848
849
hbtn = lv_obj_get_child_back(ext->header, NULL);
850
hbtn = lv_obj_get_child_back(ext->header, hbtn); // Skip the title.
851
852
if (disable)
853
{
854
while (hbtn != NULL)
855
{
856
lv_obj_set_opa_scale(hbtn, LV_OPA_40);
857
lv_obj_set_opa_scale_enable(hbtn, true);
858
lv_obj_set_click(hbtn, false);
859
hbtn = lv_obj_get_child_back(ext->header, hbtn);
860
}
861
}
862
else
863
{
864
while (hbtn != NULL)
865
{
866
lv_obj_set_opa_scale(hbtn, LV_OPA_COVER);
867
lv_obj_set_click(hbtn, true);
868
hbtn = lv_obj_get_child_back(ext->header, hbtn);
869
}
870
}
871
}
872
873
lv_res_t nyx_win_close_action(lv_obj_t * btn)
874
{
875
close_btn = NULL;
876
877
return lv_win_close_action(btn);
878
}
879
880
lv_obj_t *nyx_create_standard_window(const char *win_title, lv_action_t close_action)
881
{
882
static lv_style_t win_bg_style;
883
884
lv_style_copy(&win_bg_style, &lv_style_plain);
885
win_bg_style.body.main_color = lv_theme_get_current()->bg->body.main_color;
886
win_bg_style.body.grad_color = win_bg_style.body.main_color;
887
888
lv_obj_t *win = lv_win_create(lv_scr_act(), NULL);
889
lv_win_set_title(win, win_title);
890
lv_win_set_style(win, LV_WIN_STYLE_BG, &win_bg_style);
891
lv_obj_set_size(win, LV_HOR_RES, LV_VER_RES);
892
893
if (!close_action)
894
close_btn = lv_win_add_btn(win, NULL, SYMBOL_CLOSE" Close", nyx_win_close_action);
895
else
896
close_btn = lv_win_add_btn(win, NULL, SYMBOL_CLOSE" Close", close_action);
897
898
return win;
899
}
900
901
static bool launch_logs_enable = false;
902
903
static void _launch_hos(u8 autoboot, u8 autoboot_list)
904
{
905
b_cfg->boot_cfg = BOOT_CFG_AUTOBOOT_EN;
906
if (launch_logs_enable)
907
b_cfg->boot_cfg |= BOOT_CFG_FROM_LAUNCH;
908
b_cfg->autoboot = autoboot;
909
b_cfg->autoboot_list = autoboot_list;
910
911
void (*main_ptr)() = (void *)nyx_str->hekate;
912
913
sd_end();
914
915
hw_deinit(false);
916
917
(*main_ptr)();
918
}
919
920
void reload_nyx(lv_obj_t *obj, bool force)
921
{
922
if (!force)
923
{
924
sd_mount();
925
926
// Check that Nyx still exists.
927
if (f_stat("bootloader/sys/nyx.bin", NULL))
928
{
929
sd_unmount();
930
931
// Remove lvgl object in case of being invoked from a window.
932
if (obj)
933
lv_obj_del(obj);
934
935
do_auto_reload = false;
936
937
return;
938
}
939
}
940
941
b_cfg->boot_cfg = BOOT_CFG_AUTOBOOT_EN;
942
b_cfg->autoboot = 0;
943
b_cfg->autoboot_list = 0;
944
b_cfg->extra_cfg = 0;
945
946
void (*main_ptr)() = (void *)nyx_str->hekate;
947
948
sd_end();
949
950
hw_deinit(false);
951
952
(*main_ptr)();
953
}
954
955
static lv_res_t reload_action(lv_obj_t *btns, const char *txt)
956
{
957
if (!lv_btnm_get_pressed(btns))
958
reload_nyx(NULL, false);
959
960
return nyx_mbox_action(btns, txt);
961
}
962
963
static lv_res_t _removed_sd_action(lv_obj_t *btns, const char *txt)
964
{
965
u32 btnidx = lv_btnm_get_pressed(btns);
966
967
switch (btnidx)
968
{
969
case 0:
970
if (h_cfg.rcm_patched)
971
power_set_state(POWER_OFF_REBOOT);
972
else
973
power_set_state(REBOOT_RCM);
974
break;
975
case 1:
976
power_set_state(POWER_OFF_RESET);
977
break;
978
case 2:
979
sd_end();
980
do_auto_reload = false;
981
break;
982
}
983
984
return nyx_mbox_action(btns, txt);
985
}
986
987
static void _check_sd_card_removed(void *params)
988
{
989
static lv_obj_t *dark_bg = NULL;
990
991
// The following checks if SDMMC_1 is initialized.
992
// If yes and card was removed, shows a message box,
993
// that will reload Nyx, when the card is inserted again.
994
if (!do_auto_reload && sd_get_card_removed())
995
{
996
dark_bg = lv_obj_create(lv_scr_act(), NULL);
997
lv_obj_set_style(dark_bg, &mbox_darken);
998
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
999
1000
static const char * mbox_btn_map[] = { "\221Reboot (RCM)", "\221Power Off", "\221Do not reload", "" };
1001
static const char * mbox_btn_map_rcm_patched[] = { "\221Reboot", "\221Power Off", "\221Do not reload", "" };
1002
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1003
lv_mbox_set_recolor_text(mbox, true);
1004
lv_obj_set_width(mbox, LV_HOR_RES * 6 / 9);
1005
1006
lv_mbox_set_text(mbox, "\n#FF8000 SD card was removed!#\n\n#96FF00 Nyx will reload after inserting it.#\n\nReminder that you can use UMS instead of removing it.\n");
1007
lv_mbox_add_btns(mbox, h_cfg.rcm_patched ? mbox_btn_map_rcm_patched : mbox_btn_map, _removed_sd_action);
1008
1009
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1010
lv_obj_set_top(mbox, true);
1011
1012
do_auto_reload = true;
1013
}
1014
1015
// If in reload state and card was inserted, reload nyx.
1016
if (do_auto_reload && !sd_get_card_removed())
1017
reload_nyx(dark_bg, false);
1018
}
1019
1020
lv_task_t *task_emmc_errors;
1021
static void _nyx_emmc_issues_warning(void *params)
1022
{
1023
if (emmc_get_mode() < EMMC_MMC_HS400)
1024
{
1025
// Remove task.
1026
lv_task_del(task_emmc_errors);
1027
1028
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1029
lv_obj_set_style(dark_bg, &mbox_darken);
1030
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1031
1032
static const char * mbox_btn_map[] = { "\251", "\222OK", "\251", "" };
1033
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
1034
lv_mbox_set_recolor_text(mbox, true);
1035
1036
lv_mbox_set_text(mbox,
1037
"#FF8000 eMMC Issues Warning#\n\n"
1038
"#FFDD00 Your eMMC is initialized in a slower mode!#\n"
1039
"#FFDD00 This might mean hardware issues!#\n\n"
1040
"You might want to check\n#C7EA46 Console Info# -> #C7EA46 eMMC#");
1041
1042
lv_mbox_add_btns(mbox, mbox_btn_map, nyx_mbox_action);
1043
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
1044
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1045
lv_obj_set_top(mbox, true);
1046
}
1047
}
1048
1049
static lv_res_t _reboot_ofw_action(lv_obj_t *btns, const char *txt)
1050
{
1051
if (!lv_btnm_get_pressed(btns))
1052
power_set_state(REBOOT_BYPASS_FUSES);
1053
1054
return nyx_mbox_action(btns, txt);
1055
}
1056
1057
static lv_res_t _create_mbox_reboot_ofw()
1058
{
1059
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1060
lv_obj_set_style(dark_bg, &mbox_darken);
1061
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1062
1063
static const char * mbox_btn_map[] = { "\221OK", "\221Cancel", "" };
1064
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1065
lv_mbox_set_recolor_text(mbox, true);
1066
lv_obj_set_width(mbox, LV_HOR_RES * 2 / 3);
1067
1068
lv_mbox_set_text(mbox,
1069
"#FF8000 Warning#\n\n"
1070
"#FFDD00 Your real DRAM ID does not match your RAM!#\n"
1071
"#FFDD00 Density and rank differ!#\n\n"
1072
"#FFDD00 Choosing to boot that way will cause #\n"
1073
"#FFDD00 performance degradation or even crashes.#\n\n"
1074
"Do you really want to boot via OFW method?#");
1075
1076
lv_mbox_add_btns(mbox, mbox_btn_map, _reboot_ofw_action);
1077
1078
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1079
lv_obj_set_top(mbox, true);
1080
1081
return LV_RES_OK;
1082
}
1083
1084
static lv_res_t _reboot_action(lv_obj_t *btns, const char *txt)
1085
{
1086
u32 btnidx = lv_btnm_get_pressed(btns);
1087
1088
u8 dram_id = fuse_read_dramid(true);
1089
u8 dram_id_adj = fuse_read_dramid(false);
1090
1091
// No OFW support if fuses burnt to 7. Custom secmon is mandatory.
1092
bool t210_8gb_dramid = !h_cfg.t210b01 && dram_id == LPDDR4_ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX;
1093
// Warn against OFW if T210B01 and dram id has the wrong density and ranks with actual ram.
1094
bool unmatched_fuses = h_cfg.t210b01 && dram_id_adj == LPDDR4X_AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL && dram_id != dram_id_adj;
1095
1096
switch (btnidx)
1097
{
1098
case 0:
1099
if (unmatched_fuses)
1100
_create_mbox_reboot_ofw();
1101
else
1102
power_set_state(REBOOT_BYPASS_FUSES);
1103
break;
1104
case 1:
1105
if (h_cfg.rcm_patched && !t210_8gb_dramid)
1106
power_set_state(POWER_OFF_REBOOT);
1107
else
1108
power_set_state(REBOOT_RCM);
1109
break;
1110
}
1111
1112
return nyx_mbox_action(btns, txt);
1113
}
1114
1115
static lv_res_t _poweroff_action(lv_obj_t *btns, const char *txt)
1116
{
1117
if (!lv_btnm_get_pressed(btns))
1118
power_set_state(POWER_OFF_RESET);
1119
1120
return nyx_mbox_action(btns, txt);
1121
}
1122
1123
static lv_res_t _create_mbox_reload(lv_obj_t *btn)
1124
{
1125
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1126
lv_obj_set_style(dark_bg, &mbox_darken);
1127
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1128
1129
static const char * mbox_btn_map[] = { "\221Reload", "\221Cancel", "" };
1130
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1131
lv_mbox_set_recolor_text(mbox, true);
1132
lv_obj_set_width(mbox, LV_HOR_RES * 4 / 10);
1133
1134
lv_mbox_set_text(mbox, "#FF8000 Do you really want#\n#FF8000 to reload hekate & Nyx?#\n\n"
1135
"This also checks\n#96FF00 bootloader/update.bin#\nfor hekate updates");
1136
1137
lv_mbox_add_btns(mbox, mbox_btn_map, reload_action);
1138
1139
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1140
lv_obj_set_top(mbox, true);
1141
1142
return LV_RES_OK;
1143
}
1144
1145
static lv_res_t _create_mbox_reboot(lv_obj_t *btn)
1146
{
1147
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1148
lv_obj_set_style(dark_bg, &mbox_darken);
1149
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1150
1151
static const char * mbox_btn_map[] = { "\221OFW", "\221RCM", "\221Cancel", "" };
1152
static const char * mbox_btn_map_autorcm[] = { "\261OFW", "\221RCM", "\221Cancel", "" };
1153
static const char * mbox_btn_map_patched[] = { "\221OFW", "\221Normal", "\221Cancel", "" };
1154
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1155
lv_mbox_set_recolor_text(mbox, true);
1156
lv_obj_set_width(mbox, LV_HOR_RES / 2);
1157
1158
lv_mbox_set_text(mbox, "#FF8000 Choose where to reboot:#");
1159
1160
// No OFW support if fuses burnt to 7. Custom secmon is mandatory.
1161
bool t210_8gb_dramid = !h_cfg.t210b01 && fuse_read_dramid(true) == LPDDR4_ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX;
1162
1163
if (h_cfg.rcm_patched && !t210_8gb_dramid)
1164
lv_mbox_add_btns(mbox, mbox_btn_map_patched, _reboot_action);
1165
else if (t210_8gb_dramid)
1166
lv_mbox_add_btns(mbox, mbox_btn_map_autorcm, _reboot_action);
1167
else
1168
lv_mbox_add_btns(mbox, !h_cfg.autorcm_enabled ? mbox_btn_map : mbox_btn_map_autorcm, _reboot_action);
1169
1170
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1171
lv_obj_set_top(mbox, true);
1172
1173
return LV_RES_OK;
1174
}
1175
1176
static lv_res_t _create_mbox_poweroff(lv_obj_t *btn)
1177
{
1178
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1179
lv_obj_set_style(dark_bg, &mbox_darken);
1180
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1181
1182
static const char * mbox_btn_map[] = { "\221Power Off", "\221Cancel", "" };
1183
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1184
lv_mbox_set_recolor_text(mbox, true);
1185
lv_obj_set_width(mbox, LV_HOR_RES * 4 / 10);
1186
1187
lv_mbox_set_text(mbox, "#FF8000 Do you really want#\n#FF8000 to power off?#");
1188
1189
lv_mbox_add_btns(mbox, mbox_btn_map, _poweroff_action);
1190
1191
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1192
lv_obj_set_top(mbox, true);
1193
1194
return LV_RES_OK;
1195
}
1196
1197
void nyx_create_onoff_button(lv_theme_t *th, lv_obj_t *parent, lv_obj_t *btn, const char *btn_name, lv_action_t action, bool transparent)
1198
{
1199
// Create buttons that are flat and text, plus On/Off switch.
1200
static lv_style_t btn_onoff_rel_hos_style, btn_onoff_pr_hos_style;
1201
lv_style_copy(&btn_onoff_rel_hos_style, th->btn.rel);
1202
btn_onoff_rel_hos_style.body.shadow.width = 0;
1203
btn_onoff_rel_hos_style.body.border.width = 0;
1204
btn_onoff_rel_hos_style.body.padding.hor = 0;
1205
btn_onoff_rel_hos_style.body.radius = 0;
1206
btn_onoff_rel_hos_style.body.empty = 1;
1207
1208
lv_style_copy(&btn_onoff_pr_hos_style, &btn_onoff_rel_hos_style);
1209
if (transparent)
1210
{
1211
btn_onoff_pr_hos_style.body.main_color = LV_COLOR_HEX(0xFFFFFF);
1212
btn_onoff_pr_hos_style.body.opa = 35;
1213
}
1214
else
1215
btn_onoff_pr_hos_style.body.main_color = LV_COLOR_HEX(theme_bg_color ? (theme_bg_color + 0x101010) : 0x2D2D2D); // COLOR_HOS_BG_LIGHT.
1216
btn_onoff_pr_hos_style.body.grad_color = btn_onoff_pr_hos_style.body.main_color;
1217
btn_onoff_pr_hos_style.text.color = th->btn.pr->text.color;
1218
btn_onoff_pr_hos_style.body.empty = 0;
1219
1220
lv_obj_t *label_btn = lv_label_create(btn, NULL);
1221
lv_obj_t *label_btnsw = NULL;
1222
1223
lv_label_set_recolor(label_btn, true);
1224
label_btnsw = lv_label_create(btn, NULL);
1225
lv_label_set_recolor(label_btnsw, true);
1226
lv_btn_set_layout(btn, LV_LAYOUT_OFF);
1227
1228
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &btn_onoff_rel_hos_style);
1229
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &btn_onoff_pr_hos_style);
1230
lv_btn_set_style(btn, LV_BTN_STYLE_TGL_REL, &btn_onoff_rel_hos_style);
1231
lv_btn_set_style(btn, LV_BTN_STYLE_TGL_PR, &btn_onoff_pr_hos_style);
1232
1233
lv_btn_set_fit(btn, false, true);
1234
lv_obj_set_width(btn, lv_obj_get_width(parent));
1235
lv_btn_set_toggle(btn, true);
1236
1237
lv_label_set_text(label_btn, btn_name);
1238
1239
lv_label_set_text(label_btnsw, "#D0D0D0 OFF#");
1240
lv_obj_align(label_btn, btn, LV_ALIGN_IN_LEFT_MID, LV_DPI / 4, 0);
1241
lv_obj_align(label_btnsw, btn, LV_ALIGN_IN_RIGHT_MID, -LV_DPI / 4, -LV_DPI / 10);
1242
1243
if (action)
1244
lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, action);
1245
}
1246
1247
static void _create_text_button(lv_theme_t *th, lv_obj_t *parent, lv_obj_t *btn, const char *btn_name, lv_action_t action)
1248
{
1249
// Create buttons that are flat and only have a text label.
1250
static lv_style_t btn_onoff_rel_hos_style, btn_onoff_pr_hos_style;
1251
lv_style_copy(&btn_onoff_rel_hos_style, th->btn.rel);
1252
btn_onoff_rel_hos_style.body.shadow.width = 0;
1253
btn_onoff_rel_hos_style.body.border.width = 0;
1254
btn_onoff_rel_hos_style.body.radius = 0;
1255
btn_onoff_rel_hos_style.body.padding.hor = LV_DPI / 4;
1256
btn_onoff_rel_hos_style.body.empty = 1;
1257
1258
lv_style_copy(&btn_onoff_pr_hos_style, &btn_onoff_rel_hos_style);
1259
if (hekate_bg)
1260
{
1261
btn_onoff_pr_hos_style.body.main_color = LV_COLOR_HEX(0xFFFFFF);
1262
btn_onoff_pr_hos_style.body.opa = 35;
1263
}
1264
else
1265
btn_onoff_pr_hos_style.body.main_color = LV_COLOR_HEX(theme_bg_color ? (theme_bg_color + 0x101010) : 0x2D2D2D); // COLOR_HOS_BG_LIGHT
1266
btn_onoff_pr_hos_style.body.grad_color = btn_onoff_pr_hos_style.body.main_color;
1267
btn_onoff_pr_hos_style.text.color = th->btn.pr->text.color;
1268
btn_onoff_pr_hos_style.body.empty = 0;
1269
1270
lv_obj_t *label_btn = lv_label_create(btn, NULL);
1271
1272
lv_label_set_recolor(label_btn, true);
1273
1274
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &btn_onoff_rel_hos_style);
1275
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &btn_onoff_pr_hos_style);
1276
lv_btn_set_style(btn, LV_BTN_STYLE_TGL_REL, &btn_onoff_rel_hos_style);
1277
lv_btn_set_style(btn, LV_BTN_STYLE_TGL_PR, &btn_onoff_pr_hos_style);
1278
1279
lv_btn_set_fit(btn, true, true);
1280
1281
lv_label_set_text(label_btn, btn_name);
1282
1283
if (action)
1284
lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, action);
1285
}
1286
1287
static void _create_tab_about(lv_theme_t * th, lv_obj_t * parent)
1288
{
1289
lv_obj_t * lbl_credits = lv_label_create(parent, NULL);
1290
1291
lv_obj_align(lbl_credits, NULL, LV_ALIGN_IN_TOP_LEFT, LV_DPI / 2, LV_DPI / 2);
1292
lv_label_set_style(lbl_credits, &monospace_text);
1293
lv_label_set_recolor(lbl_credits, true);
1294
lv_label_set_static_text(lbl_credits,
1295
"#C7EA46 hekate# (c) 2018, #C7EA46 naehrwert#, #C7EA46 st4rk#\n"
1296
" (c) 2018-2026, #C7EA46 CTCaer#\n"
1297
"\n"
1298
"#C7EA46 Nyx# (c) 2019-2026, #C7EA46 CTCaer#\n"
1299
"\n"
1300
"Thanks to: #00CCFF derrek, nedwill, plutoo, #\n"
1301
" #00CCFF shuffle2, smea, thexyz, yellows8 #\n"
1302
"\n"
1303
"Greetings to: fincs, hexkyz, SciresM,\n"
1304
" Shiny Quagsire, WinterMute\n"
1305
"\n"
1306
"Open source and free packages used: \n" // Label width alignment padding.
1307
" - Littlev Graphics Library,\n"
1308
" Copyright (c) 2016-2018, Gabor Kiss-Vamosi\n\n"
1309
" - FatFs R0.13c,\n"
1310
" Copyright (c) 2006-2018, ChaN\n"
1311
" Copyright (c) 2018-2022, CTCaer\n\n"
1312
" - bcl-1.2.0,\n"
1313
" Copyright (c) 2003-2006, Marcus Geelnard\n\n"
1314
" - blz,\n"
1315
" Copyright (c) 2018, SciresM\n\n"
1316
" - elfload,\n"
1317
" Copyright (c) 2014, Owen Shepherd\n"
1318
" Copyright (c) 2018, M4xw"
1319
);
1320
1321
lv_obj_t * lbl_octopus = lv_label_create(parent, NULL);
1322
lv_obj_align(lbl_octopus, lbl_credits, LV_ALIGN_OUT_RIGHT_TOP, -LV_DPI / 10, 0);
1323
lv_label_set_style(lbl_octopus, &monospace_text);
1324
lv_label_set_recolor(lbl_octopus, true);
1325
1326
lv_label_set_static_text(lbl_octopus,
1327
"\n#00CCFF ___#\n"
1328
"#00CCFF .-' `'.#\n"
1329
"#00CCFF / \\#\n"
1330
"#00CCFF | ;#\n"
1331
"#00CCFF | | ___.--,#\n"
1332
"#00CCFF _.._ |0) = (0) | _.---'`__.-( (_.#\n"
1333
"#00CCFF __.--'`_.. '.__.\\ '--. \\_.-' ,.--'` `\"\"`#\n"
1334
"#00CCFF ( ,.--'` ',__ /./; ;, '.__.'` __#\n"
1335
"#00CCFF _`) ) .---.__.' / | |\\ \\__..--\"\" \"\"\"--.,_#\n"
1336
"#00CCFF `---' .'.''-._.-'`_./ /\\ '. \\ _.--''````'''--._`-.__.'#\n"
1337
"#00CCFF | | .' _.-' | | \\ \\ '. `----`#\n"
1338
"#00CCFF \\ \\/ .' \\ \\ '. '-._)#\n"
1339
"#00CCFF \\/ / \\ \\ `=.__`'-.#\n"
1340
"#00CCFF / /\\ `) ) / / `\"\".`\\#\n"
1341
"#00CCFF , _.-'.'\\ \\ / / ( ( / /#\n"
1342
"#00CCFF `--'` ) ) .-'.' '.'. | (#\n"
1343
"#00CCFF (/` ( (` ) ) '-; ##00FFCC [switchbrew]#\n"
1344
"#00CCFF ` '-; (-'#"
1345
);
1346
1347
lv_obj_t *hekate_img = lv_img_create(parent, NULL);
1348
lv_img_set_src(hekate_img, &hekate_logo);
1349
lv_obj_align(hekate_img, lbl_octopus, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI * 2 / 3);
1350
1351
lv_obj_t *ctcaer_img = lv_img_create(parent, NULL);
1352
lv_img_set_src(ctcaer_img, &ctcaer_logo);
1353
lv_obj_align(ctcaer_img, lbl_octopus, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, LV_DPI * 2 / 3);
1354
1355
char version[32];
1356
s_printf(version, "Nyx %s%d.%d.%d%c", NYX_VER_RL ? "v" : "", NYX_VER_MJ, NYX_VER_MN, NYX_VER_HF, NYX_VER_RL > 'A' ? NYX_VER_RL : 0);
1357
lv_obj_t * lbl_ver = lv_label_create(parent, NULL);
1358
lv_obj_align(lbl_ver, ctcaer_img, LV_ALIGN_OUT_BOTTOM_RIGHT, -LV_DPI / 20, LV_DPI / 4);
1359
lv_label_set_style(lbl_ver, &monospace_text);
1360
lv_label_set_text(lbl_ver, version);
1361
}
1362
1363
static void _update_status_bar(void *params)
1364
{
1365
static char *label = NULL;
1366
1367
u16 soc_temp = 0;
1368
u32 batt_percent = 0;
1369
int charge_status = 0;
1370
int batt_volt = 0;
1371
int batt_curr = 0;
1372
rtc_time_t time;
1373
1374
// Get sensor data.
1375
max77620_rtc_get_time_adjusted(&time);
1376
soc_temp = tmp451_get_soc_temp(false);
1377
bq24193_get_property(BQ24193_ChargeStatus, &charge_status);
1378
max17050_get_property(MAX17050_RepSOC, (int *)&batt_percent);
1379
max17050_get_property(MAX17050_VCELL, &batt_volt);
1380
max17050_get_property(MAX17050_Current, &batt_curr);
1381
1382
// Enable fan if more than 41 oC.
1383
u32 soc_temp_dec = soc_temp >> 8;
1384
fan_set_from_temp(soc_temp_dec);
1385
1386
if (!label)
1387
label = (char *)malloc(512);
1388
1389
// Set time and SoC temperature.
1390
s_printf(label, "%02d:%02d "SYMBOL_DOT" "SYMBOL_TEMPERATURE" %02d.%d",
1391
time.hour, time.min, soc_temp_dec, (soc_temp & 0xFF) / 10);
1392
1393
lv_label_set_text(status_bar.time_temp, label);
1394
1395
lv_obj_realign(status_bar.temp_symbol);
1396
lv_obj_realign(status_bar.temp_degrees);
1397
1398
// Set battery percent and charging symbol.
1399
s_printf(label, " "SYMBOL_DOT" %d.%d%% ", (batt_percent >> 8) & 0xFF, (batt_percent & 0xFF) / 26);
1400
1401
u8 batt_level = (batt_percent >> 8) & 0xFF;
1402
if (batt_level > 80)
1403
strcat(label, SYMBOL_BATTERY_FULL);
1404
else if (batt_level > 60)
1405
strcat(label, SYMBOL_BATTERY_3);
1406
else if (batt_level > 40)
1407
strcat(label, SYMBOL_BATTERY_2);
1408
else if (batt_level > 15)
1409
strcat(label, SYMBOL_BATTERY_1);
1410
else
1411
strcat(label, "#FF3C28 "SYMBOL_BATTERY_EMPTY"#");
1412
1413
// Set charging symbol.
1414
if (charge_status)
1415
strcat(label, " #FFDD00 "SYMBOL_CHARGE"#");
1416
1417
lv_label_set_text(status_bar.battery, label);
1418
lv_obj_realign(status_bar.battery);
1419
1420
// Set battery current draw and voltage.
1421
s_printf(label, "#%s%d", batt_curr >= 0 ? "96FF00 +" : "FF3C28 ", batt_curr / 1000);
1422
1423
bool voltage_empty = batt_volt < 3200;
1424
s_printf(label + strlen(label), " mA# (%s%d mV%s)",
1425
voltage_empty ? "#FF8000 " : "", batt_volt, voltage_empty ? " "SYMBOL_WARNING"#" : "");
1426
1427
lv_label_set_text(status_bar.battery_more, label);
1428
lv_obj_realign(status_bar.battery_more);
1429
}
1430
1431
static lv_res_t _create_mbox_payloads(lv_obj_t *btn)
1432
{
1433
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
1434
lv_obj_set_style(dark_bg, &mbox_darken);
1435
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
1436
1437
static const char * mbox_btn_map[] = { "\251", "\222Cancel", "\251", "" };
1438
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
1439
lv_mbox_set_recolor_text(mbox, true);
1440
lv_obj_set_width(mbox, LV_HOR_RES * 5 / 9);
1441
1442
lv_mbox_set_text(mbox, "Select a payload to launch:");
1443
1444
// Create a list with all found payloads.
1445
//! TODO: SHould that be tabs with buttons? + Icon support?
1446
lv_obj_t *list = lv_list_create(mbox, NULL);
1447
payload_list = list;
1448
lv_obj_set_size(list, LV_HOR_RES * 3 / 7, LV_VER_RES * 3 / 7);
1449
lv_list_set_single_mode(list, true);
1450
1451
if (sd_mount())
1452
{
1453
lv_mbox_set_text(mbox, "#FFDD00 Failed to init SD!#");
1454
1455
goto out_end;
1456
}
1457
1458
dirlist_t *filelist = dirlist("bootloader/payloads", NULL, 0);
1459
sd_unmount();
1460
1461
u32 i = 0;
1462
if (filelist)
1463
{
1464
while (true)
1465
{
1466
if (!filelist->name[i])
1467
break;
1468
lv_list_add(list, NULL, filelist->name[i], launch_payload);
1469
i++;
1470
}
1471
free(filelist);
1472
}
1473
1474
out_end:
1475
lv_mbox_add_btns(mbox, mbox_btn_map, nyx_mbox_action);
1476
1477
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
1478
lv_obj_set_top(mbox, true);
1479
1480
return LV_RES_OK;
1481
}
1482
typedef struct _launch_menu_entries_t
1483
{
1484
lv_obj_t *btn[20];
1485
lv_obj_t *label[20];
1486
} launch_menu_entries_t;
1487
1488
static launch_menu_entries_t launch_ctxt;
1489
static lv_obj_t *launch_bg = NULL;
1490
static bool launch_bg_done = false;
1491
1492
static lv_res_t _launch_more_cfg_action(lv_obj_t *btn)
1493
{
1494
lv_btn_ext_t *ext = lv_obj_get_ext_attr(btn);
1495
1496
_launch_hos(ext->idx, 1);
1497
1498
return LV_RES_OK;
1499
}
1500
1501
static lv_res_t _win_launch_close_action(lv_obj_t * btn)
1502
{
1503
// Cleanup icons.
1504
for (u32 i = 0; i < (n_cfg.entries_5_col ? 10 : 8); i++)
1505
{
1506
lv_obj_t *btns = launch_ctxt.btn[i];
1507
lv_btn_ext_t *ext = lv_obj_get_ext_attr(btns);
1508
if (ext->idx)
1509
{
1510
// This gets latest object, which is the button overlay. So iterate 2 times.
1511
lv_obj_t * img = lv_obj_get_child(btns, NULL);
1512
img = lv_obj_get_child(btns, img);
1513
1514
lv_img_dsc_t *src = (lv_img_dsc_t *)lv_img_get_src(img);
1515
1516
// Avoid freeing base icons.
1517
if ((src != icon_switch) && (src != icon_payload))
1518
free(src);
1519
}
1520
}
1521
1522
lv_obj_t * win = lv_win_get_from_btn(btn);
1523
1524
lv_obj_del(win);
1525
1526
if (n_cfg.home_screen && !launch_bg_done && hekate_bg)
1527
{
1528
lv_obj_set_opa_scale_enable(launch_bg, true);
1529
lv_obj_set_opa_scale(launch_bg, LV_OPA_TRANSP);
1530
//if (launch_bg)
1531
// lv_obj_del(launch_bg); //! TODO: Find why it hangs.
1532
launch_bg_done = true;
1533
}
1534
1535
close_btn = NULL;
1536
1537
return LV_RES_INV;
1538
}
1539
1540
static lv_obj_t *create_window_launch(const char *win_title)
1541
{
1542
static lv_style_t win_bg_style, win_header;
1543
1544
lv_style_copy(&win_bg_style, &lv_style_plain);
1545
win_bg_style.body.main_color = lv_theme_get_current()->bg->body.main_color;
1546
win_bg_style.body.grad_color = win_bg_style.body.main_color;
1547
1548
if (n_cfg.home_screen && !launch_bg_done && hekate_bg)
1549
{
1550
lv_obj_t *img = lv_img_create(lv_scr_act(), NULL);
1551
lv_img_set_src(img, hekate_bg);
1552
1553
launch_bg = img;
1554
}
1555
1556
lv_obj_t *win = lv_win_create(lv_scr_act(), NULL);
1557
lv_win_set_title(win, win_title);
1558
1559
lv_obj_set_size(win, LV_HOR_RES, LV_VER_RES);
1560
1561
if (n_cfg.home_screen && !launch_bg_done && hekate_bg)
1562
{
1563
lv_style_copy(&win_header, lv_theme_get_current()->win.header);
1564
win_header.body.opa = LV_OPA_TRANSP;
1565
1566
win_bg_style.body.opa = LV_OPA_TRANSP;
1567
lv_win_set_style(win, LV_WIN_STYLE_HEADER, &win_header);
1568
}
1569
1570
lv_win_set_style(win, LV_WIN_STYLE_BG, &win_bg_style);
1571
1572
close_btn = lv_win_add_btn(win, NULL, SYMBOL_CLOSE" Close", _win_launch_close_action);
1573
1574
return win;
1575
}
1576
1577
static lv_res_t _launch_action(lv_obj_t *btn)
1578
{
1579
lv_btn_ext_t *ext = lv_obj_get_ext_attr(btn);
1580
1581
_launch_hos(ext->idx, 0);
1582
1583
return LV_RES_OK;
1584
}
1585
1586
static lv_res_t logs_onoff_toggle(lv_obj_t *btn)
1587
{
1588
launch_logs_enable = !launch_logs_enable;
1589
1590
lv_obj_t *label_btn = lv_obj_get_child(btn, NULL);
1591
1592
char label_text[64];
1593
strcpy(label_text, lv_label_get_text(label_btn));
1594
label_text[strlen(label_text) - 12] = 0;
1595
1596
if (!launch_logs_enable)
1597
{
1598
strcat(label_text, "#D0D0D0 OFF#");
1599
lv_label_set_text(label_btn, label_text);
1600
}
1601
else
1602
{
1603
s_printf(label_text, "%s%s%s", label_text, text_color, " ON #");
1604
lv_label_set_text(label_btn, label_text);
1605
}
1606
1607
return LV_RES_OK;
1608
}
1609
1610
typedef struct _launch_button_pos_t
1611
{
1612
u16 btn_x;
1613
u16 btn_y;
1614
u16 lbl_x;
1615
u16 lbl_y;
1616
} launch_button_pos_t;
1617
1618
static const launch_button_pos_t launch_button_pos8[8] = {
1619
// First row.
1620
{ 19, 36, 0, 245 },
1621
{ 340, 36, 321, 245 },
1622
{ 661, 36, 642, 245 },
1623
{ 982, 36, 963, 245 },
1624
// Second row.
1625
{ 19, 313, 0, 522 },
1626
{ 340, 313, 321, 522 },
1627
{ 661, 313, 642, 522 },
1628
{ 982, 313, 963, 522 }
1629
};
1630
1631
static const launch_button_pos_t launch_button_pos10[10] = {
1632
// First row.
1633
{ 19, 36, 0, 245},
1634
{260, 36, 241, 245},
1635
{501, 36, 482, 245},
1636
{742, 36, 723, 245},
1637
{983, 36, 964, 245},
1638
// Second row.
1639
{ 19, 313, 0, 522},
1640
{260, 313, 241, 522},
1641
{501, 313, 482, 522},
1642
{742, 313, 723, 522},
1643
{983, 313, 964, 522}
1644
};
1645
1646
static lv_res_t _create_window_home_launch(lv_obj_t *btn)
1647
{
1648
const u32 max_entries = n_cfg.entries_5_col ? 10 : 8;
1649
const launch_button_pos_t *launch_button_pos = n_cfg.entries_5_col ? launch_button_pos10 : launch_button_pos8;
1650
1651
char *icon_path;
1652
1653
static lv_style_t btn_home_noborder_rel;
1654
lv_style_copy(&btn_home_noborder_rel, lv_theme_get_current()->btn.rel);
1655
btn_home_noborder_rel.body.opa = LV_OPA_0;
1656
btn_home_noborder_rel.body.border.width = 4;
1657
btn_home_noborder_rel.body.border.opa = LV_OPA_0;
1658
1659
static lv_style_t btn_home_transp_rel;
1660
lv_style_copy(&btn_home_transp_rel, lv_theme_get_current()->btn.rel);
1661
btn_home_transp_rel.body.opa = LV_OPA_0;
1662
btn_home_transp_rel.body.border.width = 4;
1663
1664
static lv_style_t btn_home_transp_pr;
1665
lv_style_copy(&btn_home_transp_pr, lv_theme_get_current()->btn.pr);
1666
btn_home_transp_pr.body.main_color = LV_COLOR_HEX(0xFFFFFF);
1667
btn_home_transp_pr.body.grad_color = btn_home_transp_pr.body.main_color;
1668
btn_home_transp_pr.body.opa = LV_OPA_30;
1669
1670
static lv_style_t btn_label_home_transp;
1671
lv_style_copy(&btn_label_home_transp, lv_theme_get_current()->cont);
1672
btn_label_home_transp.body.opa = LV_OPA_TRANSP;
1673
1674
lv_obj_t *win;
1675
1676
bool more_cfg = false;
1677
bool combined_cfg = false;
1678
if (btn)
1679
{
1680
if (strcmp(lv_label_get_text(lv_obj_get_child(btn, NULL)) + 8,"Launch#"))
1681
more_cfg = true;
1682
}
1683
else
1684
{
1685
switch (n_cfg.home_screen)
1686
{
1687
case 1: // All configs.
1688
combined_cfg = true;
1689
break;
1690
case 3: // More configs
1691
more_cfg = true;
1692
break;
1693
}
1694
}
1695
1696
if (!btn)
1697
win = create_window_launch(SYMBOL_GPS" hekate - Launch");
1698
else if (!more_cfg)
1699
win = create_window_launch(SYMBOL_GPS" Launch");
1700
else
1701
win = create_window_launch(SYMBOL_GPS" More Configurations");
1702
1703
lv_win_add_btn(win, NULL, SYMBOL_LIST" Logs #D0D0D0 OFF#", logs_onoff_toggle);
1704
launch_logs_enable = false;
1705
1706
lv_cont_set_fit(lv_page_get_scrl(lv_win_get_content(win)), false, false);
1707
lv_page_set_scrl_height(lv_win_get_content(win), 572);
1708
1709
lv_btn_ext_t * ext;
1710
lv_obj_t *btn_boot_entry;
1711
lv_obj_t *boot_entry_lbl_cont;
1712
lv_obj_t *boot_entry_label;
1713
bool no_boot_entries = false;
1714
1715
// Create Boot Entry buttons.
1716
// Buttons are 200 x 200 with 4 pixel borders.
1717
// Icons must be <= 192 x 192.
1718
// Create first Button.
1719
btn_boot_entry = lv_btn_create(win, NULL);
1720
launch_ctxt.btn[0] = btn_boot_entry;
1721
lv_obj_set_size(btn_boot_entry, 200, 200);
1722
lv_obj_set_pos(btn_boot_entry, launch_button_pos[0].btn_x, launch_button_pos[0].btn_y);
1723
lv_obj_set_opa_scale(btn_boot_entry, LV_OPA_0);
1724
lv_obj_set_opa_scale_enable(btn_boot_entry, true);
1725
lv_btn_set_layout(btn_boot_entry, LV_LAYOUT_OFF);
1726
1727
boot_entry_lbl_cont = lv_cont_create(win, NULL);
1728
boot_entry_label = lv_label_create(boot_entry_lbl_cont, NULL);
1729
lv_obj_set_style(boot_entry_label, &hint_small_style_white);
1730
lv_label_set_text(boot_entry_label, "");
1731
launch_ctxt.label[0] = boot_entry_label;
1732
1733
lv_cont_set_fit(boot_entry_lbl_cont, false, false);
1734
lv_cont_set_layout(boot_entry_lbl_cont, LV_LAYOUT_CENTER);
1735
lv_obj_set_size(boot_entry_lbl_cont, 238, 20);
1736
lv_obj_set_pos(boot_entry_lbl_cont, launch_button_pos[0].lbl_x, launch_button_pos[0].lbl_y);
1737
lv_obj_set_style(boot_entry_lbl_cont, &btn_label_home_transp);
1738
1739
// Create the rest of the buttons.
1740
for (u32 btn_idx = 1; btn_idx < (n_cfg.entries_5_col ? 10 : 8); btn_idx++)
1741
{
1742
btn_boot_entry = lv_btn_create(win, btn_boot_entry);
1743
launch_ctxt.btn[btn_idx] = btn_boot_entry;
1744
lv_obj_set_pos(btn_boot_entry, launch_button_pos[btn_idx].btn_x, launch_button_pos[btn_idx].btn_y);
1745
1746
boot_entry_lbl_cont = lv_cont_create(win, boot_entry_lbl_cont);
1747
boot_entry_label = lv_label_create(boot_entry_lbl_cont, boot_entry_label);
1748
lv_obj_set_pos(boot_entry_lbl_cont, launch_button_pos[btn_idx].lbl_x, launch_button_pos[btn_idx].lbl_y);
1749
launch_ctxt.label[btn_idx] = boot_entry_label;
1750
}
1751
1752
// Create colorized icon style based on its parent style.
1753
static lv_style_t img_style;
1754
lv_style_copy(&img_style, &lv_style_plain);
1755
img_style.image.color = COLOR_HOS_TURQUOISE_EX(n_cfg.theme_color);
1756
img_style.image.intense = LV_OPA_COVER;
1757
1758
// Parse ini boot entries and set buttons/icons.
1759
char *tmp_path = malloc(1024);
1760
u32 curr_btn_idx = 0; // Active buttons.
1761
LIST_INIT(ini_sections);
1762
1763
if (sd_mount())
1764
goto failed_sd_mount;
1765
1766
// Check if we use custom system icons.
1767
bool icon_sw_custom = !f_stat("bootloader/res/icon_switch_custom.bmp", NULL);
1768
bool icon_pl_custom = !f_stat("bootloader/res/icon_payload_custom.bmp", NULL);
1769
1770
// Choose what to parse.
1771
bool ini_parse_success = false;
1772
if (!more_cfg)
1773
ini_parse_success = !ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false);
1774
else
1775
ini_parse_success = !ini_parse(&ini_sections, "bootloader/ini", true);
1776
1777
if (combined_cfg && !ini_parse_success)
1778
{
1779
ini_parsing:
1780
list_init(&ini_sections);
1781
ini_parse_success = !ini_parse(&ini_sections, "bootloader/ini", true);
1782
more_cfg = true;
1783
}
1784
1785
if (!ini_parse_success)
1786
goto ini_parse_failed;
1787
1788
// Iterate to all boot entries and load icons.
1789
u32 entry_idx = 1;
1790
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link)
1791
{
1792
if (!strcmp(ini_sec->name, "config") || (ini_sec->type != INI_CHOICE))
1793
continue;
1794
1795
icon_path = NULL;
1796
bool payload = false;
1797
bool img_colorize = false;
1798
bool img_noborder = false;
1799
lv_img_dsc_t *bmp = NULL;
1800
lv_obj_t *img = NULL;
1801
1802
// Check for icons.
1803
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
1804
{
1805
if (!strcmp("icon", kv->key))
1806
icon_path = kv->val;
1807
else if (!strcmp("payload", kv->key))
1808
payload = true;
1809
}
1810
1811
// If icon not found, check res folder for section_name.bmp.
1812
// If not, use defaults.
1813
if (!icon_path)
1814
{
1815
s_printf(tmp_path, "bootloader/res/%s.bmp", ini_sec->name);
1816
bmp = bmp_to_lvimg_obj(tmp_path);
1817
if (!bmp)
1818
{
1819
s_printf(tmp_path, "bootloader/res/%s_hue_nobox.bmp", ini_sec->name);
1820
bmp = bmp_to_lvimg_obj(tmp_path);
1821
if (bmp)
1822
{
1823
img_noborder = true;
1824
img_colorize = true;
1825
}
1826
if (!bmp)
1827
{
1828
s_printf(tmp_path, "bootloader/res/%s_hue.bmp", ini_sec->name);
1829
bmp = bmp_to_lvimg_obj(tmp_path);
1830
if (bmp)
1831
img_colorize = true;
1832
}
1833
if (!bmp)
1834
{
1835
s_printf(tmp_path, "bootloader/res/%s_nobox.bmp", ini_sec->name);
1836
bmp = bmp_to_lvimg_obj(tmp_path);
1837
if (bmp)
1838
img_noborder = true;
1839
}
1840
}
1841
1842
if (!bmp && payload)
1843
{
1844
bmp = icon_payload;
1845
1846
if (!icon_pl_custom)
1847
img_colorize = true;
1848
}
1849
}
1850
else
1851
{
1852
bmp = bmp_to_lvimg_obj(icon_path);
1853
1854
// Check if both colorization and border are enabled.
1855
if (bmp && strlen(icon_path) > 14 && !memcmp(icon_path + strlen(icon_path) - 14, "_hue_nobox", 10))
1856
{
1857
img_colorize = true;
1858
img_noborder = true;
1859
}
1860
else
1861
{
1862
// Check if colorization is enabled.
1863
if (bmp && strlen(icon_path) > 8 && !memcmp(icon_path + strlen(icon_path) - 8, "_hue", 4))
1864
img_colorize = true;
1865
1866
// Check if no border is enabled.
1867
if (bmp && strlen(icon_path) > 10 && !memcmp(icon_path + strlen(icon_path) - 10, "_nobox", 6))
1868
img_noborder = true;
1869
}
1870
}
1871
1872
// Enable button.
1873
lv_obj_set_opa_scale(launch_ctxt.btn[curr_btn_idx], LV_OPA_COVER);
1874
1875
// Default to switch logo if no icon found at all.
1876
if (!bmp)
1877
{
1878
bmp = icon_switch;
1879
1880
if (!icon_sw_custom)
1881
img_colorize = true;
1882
}
1883
1884
//Set icon.
1885
if (bmp)
1886
{
1887
img = lv_img_create(launch_ctxt.btn[curr_btn_idx], NULL);
1888
1889
if (img_colorize)
1890
lv_img_set_style(img, &img_style);
1891
1892
lv_img_set_src(img, bmp);
1893
}
1894
1895
// Add button mask/radius and align icon.
1896
lv_obj_t *btns = lv_btn_create(launch_ctxt.btn[curr_btn_idx], NULL);
1897
u32 btn_width = 200;
1898
u32 btn_height = 200;
1899
if (img_noborder)
1900
{
1901
btn_width = bmp->header.w + 4;
1902
btn_height = bmp->header.h + 4;
1903
1904
if (btn_width > 200)
1905
btn_width = 200;
1906
if (btn_height > 200)
1907
btn_height = 200;
1908
1909
lv_btn_set_style(launch_ctxt.btn[curr_btn_idx], LV_BTN_STYLE_REL, &btn_home_noborder_rel);
1910
lv_btn_set_style(launch_ctxt.btn[curr_btn_idx], LV_BTN_STYLE_PR, &btn_home_noborder_rel);
1911
}
1912
lv_obj_set_size(btns, btn_width, btn_height);
1913
lv_btn_set_style(btns, LV_BTN_STYLE_REL, img_noborder ? &btn_home_noborder_rel : &btn_home_transp_rel);
1914
lv_btn_set_style(btns, LV_BTN_STYLE_PR, &btn_home_transp_pr);
1915
1916
// Button transparency if custom background, but loses color.
1917
// if (!btn && hekate_bg && !img_noborder)
1918
// {
1919
// lv_btn_set_style(launch_ctxt.btn[curr_btn_idx], LV_BTN_STYLE_REL, &btn_transp_rel);
1920
// lv_btn_set_style(launch_ctxt.btn[curr_btn_idx], LV_BTN_STYLE_PR, &btn_transp_pr);
1921
// lv_btn_set_style(btns, LV_BTN_STYLE_REL, &btn_home_noborder_rel);
1922
// }
1923
1924
if (img)
1925
lv_obj_align(img, NULL, LV_ALIGN_CENTER, 0, 0);
1926
if (img_noborder)
1927
lv_obj_align(btns, NULL, LV_ALIGN_CENTER, 0, 0);
1928
1929
// Set autoboot index.
1930
ext = lv_obj_get_ext_attr(btns);
1931
ext->idx = entry_idx;
1932
ext = lv_obj_get_ext_attr(launch_ctxt.btn[curr_btn_idx]); // Redundancy.
1933
ext->idx = entry_idx;
1934
1935
// Set action.
1936
if (!more_cfg)
1937
lv_btn_set_action(btns, LV_BTN_ACTION_CLICK, _launch_action);
1938
else
1939
lv_btn_set_action(btns, LV_BTN_ACTION_CLICK, _launch_more_cfg_action);
1940
1941
// Set button's label text.
1942
lv_label_set_text(launch_ctxt.label[curr_btn_idx], ini_sec->name);
1943
lv_obj_set_opa_scale(launch_ctxt.label[curr_btn_idx], LV_OPA_COVER);
1944
1945
// Set rolling text if name is too big.
1946
int max_label_size = 238 - (n_cfg.entries_5_col ? 12 : 0);
1947
if (lv_obj_get_width(launch_ctxt.label[curr_btn_idx]) > max_label_size)
1948
{
1949
lv_label_set_long_mode(launch_ctxt.label[curr_btn_idx], LV_LABEL_LONG_ROLL);
1950
lv_obj_set_width(launch_ctxt.label[curr_btn_idx], max_label_size);
1951
}
1952
1953
entry_idx++;
1954
curr_btn_idx++;
1955
1956
// Check if we exceed max buttons.
1957
if (curr_btn_idx >= max_entries)
1958
break;
1959
}
1960
1961
ini_free(&ini_sections);
1962
1963
ini_parse_failed:
1964
// Reiterate the loop with more cfgs if combined.
1965
if (combined_cfg && (curr_btn_idx < (n_cfg.entries_5_col ? 10 : 8)) && !more_cfg)
1966
goto ini_parsing;
1967
1968
failed_sd_mount:
1969
if (curr_btn_idx < 1)
1970
no_boot_entries = true;
1971
1972
sd_unmount();
1973
1974
free(tmp_path);
1975
1976
// No boot entries found.
1977
if (no_boot_entries)
1978
{
1979
lv_obj_t *label_error = lv_label_create(win, NULL);
1980
lv_label_set_recolor(label_error, true);
1981
if (!more_cfg)
1982
{
1983
lv_label_set_static_text(label_error,
1984
"#FFDD00 No main boot entries found...#\n"
1985
"Check that #96FF00 bootloader/hekate_ipl.ini# has boot entries\n"
1986
"or use #C7EA46 More configs# button for more boot entries.");
1987
}
1988
else
1989
{
1990
lv_label_set_static_text(label_error,
1991
"#FFDD00 No .ini or boot entries found...#\n"
1992
"Check that a .ini file exists in #96FF00 bootloader/ini/#\n"
1993
"and that it contains at least one entry.");
1994
}
1995
1996
lv_obj_set_pos(label_error, 19, 0);
1997
}
1998
1999
return LV_RES_OK;
2000
}
2001
2002
static void _create_tab_home(lv_theme_t *th, lv_obj_t *parent)
2003
{
2004
lv_page_set_scrl_layout(parent, LV_LAYOUT_OFF);
2005
lv_page_set_scrl_fit(parent, false, false);
2006
lv_page_set_scrl_height(parent, 592);
2007
2008
char btn_colored_text[64];
2009
2010
// Set brand label.
2011
lv_obj_t *label_brand = lv_label_create(parent, NULL);
2012
lv_label_set_recolor(label_brand, true);
2013
s_printf(btn_colored_text, "%s%s", text_color, " hekate#");
2014
lv_label_set_text(label_brand, btn_colored_text);
2015
lv_obj_set_pos(label_brand, 50, 48);
2016
2017
// Set tagline label.
2018
lv_obj_t *label_tagline = lv_label_create(parent, NULL);
2019
lv_obj_set_style(label_tagline, &hint_small_style_white);
2020
lv_label_set_static_text(label_tagline, "THE ALL IN ONE BOOTLOADER FOR ALL YOUR NEEDS");
2021
lv_obj_set_pos(label_tagline, 50, 82);
2022
2023
static lv_style_t icons;
2024
lv_style_copy(&icons, th->label.prim);
2025
icons.text.font = &hekate_symbol_120;
2026
2027
// Launch button.
2028
lv_obj_t *btn_launch = lv_btn_create(parent, NULL);
2029
if (hekate_bg)
2030
{
2031
lv_btn_set_style(btn_launch, LV_BTN_STYLE_REL, &btn_transp_rel);
2032
lv_btn_set_style(btn_launch, LV_BTN_STYLE_PR, &btn_transp_pr);
2033
}
2034
lv_obj_t *label_btn = lv_label_create(btn_launch, NULL);
2035
lv_label_set_recolor(label_btn, true);
2036
lv_obj_set_style(label_btn, &icons);
2037
s_printf(btn_colored_text, "%s%s", text_color, " "SYMBOL_DOT"#");
2038
lv_label_set_text(label_btn, btn_colored_text);
2039
lv_btn_set_action(btn_launch, LV_BTN_ACTION_CLICK, _create_window_home_launch);
2040
lv_obj_set_size(btn_launch, 256, 256);
2041
lv_obj_set_pos(btn_launch, 50, 160);
2042
lv_btn_set_layout(btn_launch, LV_LAYOUT_OFF);
2043
lv_obj_align(label_btn, NULL, LV_ALIGN_CENTER, 0, -28);
2044
lv_obj_t *label_btn2 = lv_label_create(btn_launch, NULL);
2045
lv_label_set_recolor(label_btn2, true);
2046
s_printf(btn_colored_text, "%s%s", text_color, " Launch#");
2047
lv_label_set_text(label_btn2, btn_colored_text);
2048
lv_obj_align(label_btn2, NULL, LV_ALIGN_IN_TOP_MID, 0, 174);
2049
2050
// More Configs button.
2051
lv_obj_t *btn_more_cfg = lv_btn_create(parent, btn_launch);
2052
label_btn = lv_label_create(btn_more_cfg, label_btn);
2053
s_printf(btn_colored_text, "%s%s", text_color, " "SYMBOL_CLOCK"#");
2054
lv_label_set_text(label_btn, btn_colored_text);
2055
lv_btn_set_action(btn_more_cfg, LV_BTN_ACTION_CLICK, _create_window_home_launch);
2056
lv_btn_set_layout(btn_more_cfg, LV_LAYOUT_OFF);
2057
lv_obj_align(label_btn, NULL, LV_ALIGN_CENTER, 0, -28);
2058
label_btn2 = lv_label_create(btn_more_cfg, label_btn2);
2059
s_printf(btn_colored_text, "%s%s", text_color, " More Configs#");
2060
lv_label_set_text(label_btn2, btn_colored_text);
2061
lv_obj_set_pos(btn_more_cfg, 341, 160);
2062
lv_obj_align(label_btn2, NULL, LV_ALIGN_IN_TOP_MID, 0, 174);
2063
2064
// Quick Launch button.
2065
// lv_obj_t *btn_quick_launch = lv_btn_create(parent, NULL);
2066
// lv_obj_t *label_quick_launch = lv_label_create(btn_quick_launch, NULL);
2067
// lv_label_set_static_text(label_quick_launch, SYMBOL_EDIT" Quick Launch");
2068
// lv_obj_set_width(btn_quick_launch, 256);
2069
// lv_obj_set_pos(btn_quick_launch, 343, 448);
2070
// lv_btn_set_action(btn_quick_launch, LV_BTN_ACTION_CLICK, NULL);
2071
// if (hekate_bg)
2072
// {
2073
// lv_btn_set_style(btn_quick_launch, LV_BTN_STYLE_REL, &btn_transp_rel);
2074
// lv_btn_set_style(btn_quick_launch, LV_BTN_STYLE_PR, &btn_transp_pr);
2075
// lv_btn_set_style(btn_quick_launch, LV_BTN_STYLE_INA, &btn_transp_ina);
2076
// }
2077
2078
lv_obj_t *btn_nyx_options = lv_btn_create(parent, NULL);
2079
_create_text_button(th, NULL, btn_nyx_options, SYMBOL_SETTINGS" Nyx Settings", NULL);
2080
//lv_obj_set_width(btn_nyx_options, 256);
2081
lv_btn_set_action(btn_nyx_options, LV_BTN_ACTION_CLICK, create_win_nyx_options);
2082
lv_obj_align(btn_nyx_options, NULL, LV_ALIGN_IN_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI / 12);
2083
2084
// Payloads button.
2085
lv_obj_t *btn_payloads = lv_btn_create(parent, btn_launch);
2086
label_btn = lv_label_create(btn_payloads, label_btn);
2087
s_printf(btn_colored_text, "%s%s", text_color, " "SYMBOL_OK"#");
2088
lv_label_set_text(label_btn, btn_colored_text);
2089
lv_btn_set_action(btn_payloads, LV_BTN_ACTION_CLICK, _create_mbox_payloads);
2090
lv_btn_set_layout(btn_payloads, LV_LAYOUT_OFF);
2091
lv_obj_align(label_btn, NULL, LV_ALIGN_CENTER, 0, -28);
2092
label_btn2 = lv_label_create(btn_payloads, label_btn2);
2093
s_printf(btn_colored_text, "%s%s", text_color, " Payloads#");
2094
lv_label_set_text(label_btn2, btn_colored_text);
2095
lv_obj_set_pos(btn_payloads, 632, 160);
2096
lv_obj_align(label_btn2, NULL, LV_ALIGN_IN_TOP_MID, 0, 174);
2097
2098
lv_obj_t *line_sep = lv_line_create(parent, NULL);
2099
static const lv_point_t line_pp[] = { {0, 0}, {0, LV_DPI * 3} };
2100
lv_line_set_points(line_sep, line_pp, 2);
2101
lv_line_set_style(line_sep, th->line.decor);
2102
lv_obj_align(line_sep, btn_payloads, LV_ALIGN_OUT_RIGHT_MID, 35, 0);
2103
2104
// emuMMC manage button.
2105
lv_obj_t *btn_emummc = lv_btn_create(parent, btn_launch);
2106
label_btn = lv_label_create(btn_emummc, label_btn);
2107
s_printf(btn_colored_text, "%s%s", text_color, " "SYMBOL_LIST"#");
2108
lv_label_set_text(label_btn, btn_colored_text);
2109
lv_btn_set_action(btn_emummc, LV_BTN_ACTION_CLICK, create_win_emummc_tools);
2110
lv_btn_set_layout(btn_emummc, LV_LAYOUT_OFF);
2111
lv_obj_align(label_btn, NULL, LV_ALIGN_CENTER, 0, -28);
2112
lv_obj_set_pos(btn_emummc, 959, 160);
2113
label_btn2 = lv_label_create(btn_emummc, label_btn2);
2114
s_printf(btn_colored_text, "%s%s", text_color, " emuMMC#");
2115
lv_label_set_text(label_btn2, btn_colored_text);
2116
lv_obj_align(label_btn2, NULL, LV_ALIGN_IN_TOP_MID, 0, 174);
2117
2118
// Create bottom right power buttons.
2119
lv_obj_t *btn_reboot = lv_btn_create(parent, NULL);
2120
lv_obj_t *btn_power_off = lv_btn_create(parent, NULL);
2121
lv_obj_t *btn_reload = lv_btn_create(parent, NULL);
2122
2123
_create_text_button(th, NULL, btn_power_off, SYMBOL_POWER" Power Off", _create_mbox_poweroff);
2124
lv_obj_align(btn_power_off, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -LV_DPI / 4, -LV_DPI / 12);
2125
2126
_create_text_button(th, NULL, btn_reboot, SYMBOL_REBOOT" Reboot", _create_mbox_reboot);
2127
lv_obj_align(btn_reboot, btn_power_off, LV_ALIGN_OUT_LEFT_MID, 0, 0);
2128
2129
_create_text_button(th, NULL, btn_reload, SYMBOL_REFRESH" Reload", _create_mbox_reload);
2130
lv_obj_align(btn_reload, btn_reboot, LV_ALIGN_OUT_LEFT_MID, 0, 0);
2131
}
2132
2133
static lv_res_t _save_options_action(lv_obj_t *btn)
2134
{
2135
static const char * mbox_btn_map[] = {"\251", "\222OK!", "\251", ""};
2136
lv_obj_t * mbox = lv_mbox_create(lv_scr_act(), NULL);
2137
lv_mbox_set_recolor_text(mbox, true);
2138
2139
int res = 0;
2140
2141
if (!sd_mount())
2142
res = create_config_entry();
2143
2144
if (!res)
2145
lv_mbox_set_text(mbox, "#FF8000 hekate Configuration#\n\n#96FF00 The configuration was saved to sd card!#");
2146
else
2147
lv_mbox_set_text(mbox, "#FF8000 hekate Configuration#\n\n#FFDD00 Failed to save the configuration#\n#FFDD00 to sd card!#");
2148
lv_mbox_add_btns(mbox, mbox_btn_map, NULL);
2149
lv_obj_set_top(mbox, true);
2150
2151
nyx_options_clear_ini_changes_made();
2152
2153
sd_unmount();
2154
2155
return LV_RES_OK;
2156
}
2157
2158
static void _create_status_bar(lv_theme_t * th)
2159
{
2160
static lv_obj_t *status_bar_bg;
2161
status_bar_bg = lv_cont_create(lv_layer_top(), NULL);
2162
status_bar.bar_bg = status_bar_bg;
2163
2164
static lv_style_t status_bar_style;
2165
lv_style_copy(&status_bar_style, &lv_style_plain_color);
2166
status_bar_style.body.opa = LV_OPA_0;
2167
status_bar_style.body.shadow.width = 0;
2168
2169
lv_obj_set_style(status_bar_bg, &status_bar_style);
2170
lv_obj_set_size(status_bar_bg, LV_HOR_RES, LV_DPI * 9 / 14);
2171
lv_obj_align(status_bar_bg, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
2172
2173
// Battery percentages.
2174
lv_obj_t *lbl_battery = lv_label_create(status_bar_bg, NULL);
2175
lv_label_set_recolor(lbl_battery, true);
2176
lv_label_set_text(lbl_battery, " "SYMBOL_DOT" 00.0% "SYMBOL_BATTERY_1" #FFDD00 "SYMBOL_CHARGE"#");
2177
lv_obj_align(lbl_battery, NULL, LV_ALIGN_IN_RIGHT_MID, -LV_DPI * 6 / 11, 0);
2178
status_bar.battery = lbl_battery;
2179
2180
// Amperages, voltages.
2181
lbl_battery = lv_label_create(status_bar_bg, lbl_battery);
2182
lv_obj_set_style(lbl_battery, &hint_small_style_white);
2183
lv_label_set_text(lbl_battery, "#96FF00 +0 mA# (0 mV)");
2184
lv_obj_align(lbl_battery, status_bar.battery, LV_ALIGN_OUT_LEFT_MID, -LV_DPI / 25, -1);
2185
status_bar.battery_more = lbl_battery;
2186
2187
lv_obj_t *lbl_left = lv_label_create(status_bar_bg, NULL);
2188
lv_label_set_text(lbl_left, SYMBOL_CLOCK" ");
2189
lv_obj_align(lbl_left, NULL, LV_ALIGN_IN_LEFT_MID, LV_DPI * 6 / 11, 0);
2190
2191
// Time, temperature.
2192
lv_obj_t *lbl_time_temp = lv_label_create(status_bar_bg, NULL);
2193
lv_label_set_text(lbl_time_temp, "00:00 "SYMBOL_DOT" "SYMBOL_TEMPERATURE" 00.0");
2194
lv_obj_align(lbl_time_temp, lbl_left, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
2195
status_bar.time_temp = lbl_time_temp;
2196
2197
lbl_left = lv_label_create(status_bar_bg, NULL);
2198
lv_label_set_text(lbl_left, " "SYMBOL_DOT);
2199
lv_obj_align(lbl_left, lbl_time_temp, LV_ALIGN_OUT_RIGHT_MID, 0, -LV_DPI / 14);
2200
status_bar.temp_symbol = lbl_left;
2201
2202
lv_obj_t *lbl_degrees = lv_label_create(status_bar_bg, NULL);
2203
lv_label_set_text(lbl_degrees, "C");
2204
lv_obj_align(lbl_degrees, lbl_left, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 50, LV_DPI / 14);
2205
status_bar.temp_degrees = lbl_degrees;
2206
2207
// Middle button.
2208
//! TODO: Utilize it for more.
2209
lv_obj_t *btn_mid = lv_btn_create(status_bar_bg, NULL);
2210
lv_obj_t *lbl_mid = lv_label_create(btn_mid, NULL);
2211
lv_label_set_static_text(lbl_mid, "Save Options");
2212
lv_obj_set_size(btn_mid, LV_DPI * 17 / 8, LV_DPI / 2);
2213
lv_obj_align(btn_mid, NULL, LV_ALIGN_CENTER, 0, 0);
2214
status_bar.mid = btn_mid;
2215
lv_obj_set_opa_scale(btn_mid, LV_OPA_0);
2216
lv_obj_set_opa_scale_enable(btn_mid, true);
2217
lv_obj_set_click(btn_mid, false);
2218
lv_btn_set_action(btn_mid, LV_BTN_ACTION_CLICK, _save_options_action);
2219
}
2220
2221
static lv_res_t _create_mbox_save_changes_action(lv_obj_t *btns, const char * txt)
2222
{
2223
int btn_idx = lv_btnm_get_pressed(btns);
2224
2225
nyx_mbox_action(btns, txt);
2226
2227
if (!btn_idx)
2228
_save_options_action(NULL);
2229
2230
return LV_RES_INV;
2231
}
2232
2233
void nyx_check_ini_changes()
2234
{
2235
if (nyx_options_get_ini_changes_made())
2236
{
2237
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
2238
lv_obj_set_style(dark_bg, &mbox_darken);
2239
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
2240
2241
static const char * mbox_btn_map[] = { "\222Save", "\222Cancel", "" };
2242
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
2243
lv_mbox_set_recolor_text(mbox, true);
2244
2245
lv_mbox_set_text(mbox,
2246
"#FF8000 Main configuration#\n\n"
2247
"You changed the configuration!\n\n"
2248
"Do you want to save it?");
2249
2250
lv_mbox_add_btns(mbox, mbox_btn_map, _create_mbox_save_changes_action);
2251
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
2252
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
2253
lv_obj_set_top(mbox, true);
2254
2255
nyx_options_clear_ini_changes_made();
2256
}
2257
}
2258
2259
static lv_res_t _show_hide_save_button(lv_obj_t *tv, uint16_t tab_idx)
2260
{
2261
if (tab_idx == 4) // Options.
2262
{
2263
lv_btn_set_action(status_bar.mid, LV_BTN_ACTION_CLICK, _save_options_action);
2264
lv_obj_set_opa_scale(status_bar.mid, LV_OPA_COVER);
2265
lv_obj_set_click(status_bar.mid, true);
2266
}
2267
else
2268
{
2269
lv_obj_set_opa_scale(status_bar.mid, LV_OPA_0);
2270
lv_obj_set_click(status_bar.mid, false);
2271
}
2272
2273
nyx_check_ini_changes();
2274
2275
return LV_RES_OK;
2276
}
2277
2278
static void _nyx_set_default_styles(lv_theme_t * th)
2279
{
2280
lv_style_copy(&mbox_darken, &lv_style_plain);
2281
mbox_darken.body.main_color = LV_COLOR_BLACK;
2282
mbox_darken.body.grad_color = mbox_darken.body.main_color;
2283
mbox_darken.body.opa = LV_OPA_30;
2284
2285
lv_style_copy(&hint_small_style, th->label.hint);
2286
hint_small_style.text.letter_space = 1;
2287
hint_small_style.text.font = &interui_20;
2288
2289
lv_style_copy(&hint_small_style_white, th->label.prim);
2290
hint_small_style_white.text.letter_space = 1;
2291
hint_small_style_white.text.font = &interui_20;
2292
2293
lv_style_copy(&monospace_text, &lv_style_plain);
2294
monospace_text.body.main_color = COLOR_HOS_BG_DARKER;
2295
monospace_text.body.grad_color = COLOR_HOS_BG_DARKER;
2296
monospace_text.body.border.color = COLOR_HOS_BG_DARKER;
2297
monospace_text.body.border.width = 0;
2298
monospace_text.body.opa = LV_OPA_TRANSP;
2299
monospace_text.text.color = LV_COLOR_HEX(0xD8D8D8);
2300
monospace_text.text.font = &ubuntu_mono;
2301
monospace_text.text.letter_space = 0;
2302
monospace_text.text.line_space = 0;
2303
2304
lv_style_copy(&btn_transp_rel, th->btn.rel);
2305
btn_transp_rel.body.main_color = LV_COLOR_HEX(0x444444);
2306
btn_transp_rel.body.grad_color = btn_transp_rel.body.main_color;
2307
btn_transp_rel.body.shadow.color = LV_COLOR_HEX(0x0F0F0F);
2308
btn_transp_rel.body.opa = LV_OPA_50;
2309
2310
lv_style_copy(&btn_transp_pr, th->btn.pr);
2311
btn_transp_pr.body.main_color = LV_COLOR_HEX(0x888888);
2312
btn_transp_pr.body.grad_color = btn_transp_pr.body.main_color;
2313
btn_transp_pr.body.shadow.color = LV_COLOR_HEX(0x0F0F0F);
2314
btn_transp_pr.body.opa = LV_OPA_50;
2315
2316
lv_style_copy(&btn_transp_tgl_rel, th->btn.tgl_rel);
2317
btn_transp_tgl_rel.body.main_color = LV_COLOR_HEX(0x444444);
2318
btn_transp_tgl_rel.body.grad_color = btn_transp_tgl_rel.body.main_color;
2319
btn_transp_tgl_rel.body.shadow.color = LV_COLOR_HEX(0x0F0F0F);
2320
btn_transp_tgl_rel.body.opa = LV_OPA_50;
2321
2322
lv_style_copy(&btn_transp_tgl_pr, th->btn.tgl_pr);
2323
btn_transp_tgl_pr.body.main_color = LV_COLOR_HEX(0x888888);
2324
btn_transp_tgl_pr.body.grad_color = btn_transp_tgl_pr.body.main_color;
2325
btn_transp_tgl_pr.body.shadow.color = LV_COLOR_HEX(0x0F0F0F);
2326
btn_transp_tgl_pr.body.opa = LV_OPA_50;
2327
2328
lv_style_copy(&btn_transp_ina, th->btn.ina);
2329
btn_transp_ina.body.main_color = LV_COLOR_HEX(0x292929);
2330
btn_transp_ina.body.grad_color = btn_transp_ina.body.main_color;
2331
btn_transp_ina.body.border.color = LV_COLOR_HEX(0x444444);
2332
btn_transp_ina.body.shadow.color = LV_COLOR_HEX(0x0F0F0F);
2333
btn_transp_ina.body.opa = LV_OPA_50;
2334
2335
lv_style_copy(&ddlist_transp_bg, th->ddlist.bg);
2336
ddlist_transp_bg.body.main_color = LV_COLOR_HEX(0x2D2D2D);
2337
ddlist_transp_bg.body.grad_color = ddlist_transp_bg.body.main_color;
2338
ddlist_transp_bg.body.opa = 180; // 70.6%.
2339
2340
lv_style_copy(&ddlist_transp_sel, th->ddlist.sel);
2341
ddlist_transp_sel.body.main_color = LV_COLOR_HEX(0x4D4D4D);
2342
ddlist_transp_sel.body.grad_color = ddlist_transp_sel.body.main_color;
2343
ddlist_transp_sel.body.opa = 180; // 70.6%.
2344
2345
lv_style_copy(&tabview_btn_pr, th->tabview.btn.pr);
2346
tabview_btn_pr.body.main_color = LV_COLOR_HEX(0xFFFFFF);
2347
tabview_btn_pr.body.grad_color = tabview_btn_pr.body.main_color;
2348
tabview_btn_pr.body.opa = 35; // 13.7%.
2349
2350
lv_style_copy(&tabview_btn_tgl_pr, th->tabview.btn.tgl_pr);
2351
tabview_btn_tgl_pr.body.main_color = LV_COLOR_HEX(0xFFFFFF);
2352
tabview_btn_tgl_pr.body.grad_color = tabview_btn_tgl_pr.body.main_color;
2353
tabview_btn_tgl_pr.body.opa = 35; // 13.7%.
2354
2355
lv_color_t tmp_color = COLOR_HOS_TURQUOISE_EX(n_cfg.theme_color);
2356
text_color = malloc(32);
2357
s_printf(text_color, "#%06X", (u32)(tmp_color.full & 0xFFFFFF));
2358
}
2359
2360
lv_task_t *task_bpmp_clock;
2361
void first_time_bpmp_clock(void *param)
2362
{
2363
// Remove task.
2364
lv_task_del(task_bpmp_clock);
2365
2366
// Max clock seems fine. Save it.
2367
n_cfg.bpmp_clock = 1;
2368
create_nyx_config_entry(false);
2369
}
2370
2371
static void _nyx_main_menu(lv_theme_t * th)
2372
{
2373
static lv_style_t no_padding;
2374
lv_style_copy(&no_padding, &lv_style_transp);
2375
no_padding.body.padding.hor = 0;
2376
2377
// Initialize global styles.
2378
_nyx_set_default_styles(th);
2379
2380
// Create screen container.
2381
lv_obj_t *scr = lv_cont_create(NULL, NULL);
2382
lv_scr_load(scr);
2383
lv_cont_set_style(scr, th->bg);
2384
2385
// Create base background and add a custom one if exists.
2386
lv_obj_t *cnr = lv_cont_create(scr, NULL);
2387
static lv_style_t base_bg_style;
2388
lv_style_copy(&base_bg_style, &lv_style_plain_color);
2389
base_bg_style.body.main_color = th->bg->body.main_color;
2390
base_bg_style.body.grad_color = base_bg_style.body.main_color;
2391
lv_cont_set_style(cnr, &base_bg_style);
2392
lv_obj_set_size(cnr, LV_HOR_RES, LV_VER_RES);
2393
2394
if (hekate_bg)
2395
{
2396
lv_obj_t *img = lv_img_create(cnr, NULL);
2397
lv_img_set_src(img, hekate_bg);
2398
}
2399
2400
// Add tabview page to screen.
2401
lv_obj_t *tv = lv_tabview_create(scr, NULL);
2402
if (hekate_bg)
2403
{
2404
lv_tabview_set_style(tv, LV_TABVIEW_STYLE_BTN_PR, &tabview_btn_pr);
2405
lv_tabview_set_style(tv, LV_TABVIEW_STYLE_BTN_TGL_PR, &tabview_btn_tgl_pr);
2406
}
2407
lv_tabview_set_sliding(tv, false);
2408
lv_obj_set_size(tv, LV_HOR_RES, LV_VER_RES);
2409
2410
// Add all tabs content.
2411
char version[32];
2412
char rel = (nyx_str->version >> 24) & 0xFF;
2413
s_printf(version, "hekate %s%d.%d.%d%c%c",
2414
rel ? "v" : "", nyx_str->version & 0xFF, (nyx_str->version >> 8) & 0xFF, (nyx_str->version >> 16) & 0xFF, rel > 'a' ? rel : 0,
2415
(nyx_str->info_ex.rsvd_flags & RSVD_FLAG_DRAM_8GB) ? '*' : 0);
2416
lv_obj_t *tab_about = lv_tabview_add_tab(tv, version);
2417
2418
lv_obj_t *tab_home = lv_tabview_add_tab(tv, SYMBOL_HOME" Home");
2419
2420
lv_obj_t *tab_tools = lv_tabview_add_tab(tv, SYMBOL_TOOLS" Tools");
2421
lv_page_set_style(tab_tools, LV_PAGE_STYLE_BG, &no_padding);
2422
lv_page_set_style(tab_tools, LV_PAGE_STYLE_SCRL, &no_padding);
2423
2424
lv_obj_t *tab_info = lv_tabview_add_tab(tv, SYMBOL_INFO" Console Info");
2425
lv_page_set_style(tab_info, LV_PAGE_STYLE_BG, &no_padding);
2426
lv_page_set_style(tab_info, LV_PAGE_STYLE_SCRL, &no_padding);
2427
2428
lv_obj_t *tab_options = lv_tabview_add_tab(tv, SYMBOL_SETTINGS" Options");
2429
2430
_create_tab_about(th, tab_about);
2431
_create_tab_home(th, tab_home);
2432
create_tab_tools(th, tab_tools);
2433
create_tab_info(th, tab_info);
2434
create_tab_options(th, tab_options);
2435
2436
lv_tabview_set_tab_act(tv, 1, false);
2437
2438
// Create status bar.
2439
_create_status_bar(th);
2440
2441
// Create tasks.
2442
system_tasks.task.dram_periodic_comp = lv_task_create(minerva_periodic_training, EMC_PERIODIC_TRAIN_MS, LV_TASK_PRIO_HIGHEST, NULL);
2443
lv_task_ready(system_tasks.task.dram_periodic_comp);
2444
2445
system_tasks.task.status_bar = lv_task_create(_update_status_bar, 5000, LV_TASK_PRIO_LOW, NULL);
2446
lv_task_ready(system_tasks.task.status_bar);
2447
2448
lv_task_create(_check_sd_card_removed, 2000, LV_TASK_PRIO_LOWEST, NULL);
2449
2450
task_emmc_errors = lv_task_create(_nyx_emmc_issues_warning, 2000, LV_TASK_PRIO_LOWEST, NULL);
2451
lv_task_ready(task_emmc_errors);
2452
2453
// Create top level global line separators.
2454
lv_obj_t *line = lv_cont_create(lv_layer_top(), NULL);
2455
2456
static lv_style_t line_style;
2457
lv_style_copy(&line_style, &lv_style_plain_color);
2458
2459
line_style.body.main_color = LV_COLOR_HEX(0xDDDDDD); // 0x505050
2460
line_style.body.grad_color = line_style.body.main_color;
2461
line_style.body.shadow.width = 0;
2462
2463
lv_cont_set_style(line, &line_style);
2464
lv_obj_set_size(line, LV_HOR_RES - LV_DPI * 3 / 5, 1);
2465
lv_obj_set_pos(line, LV_DPI * 3 / 10, 63);
2466
2467
lv_obj_set_top(line, true);
2468
2469
line = lv_cont_create(lv_layer_top(), line);
2470
lv_obj_set_pos(line, LV_DPI * 3 / 10, 656);
2471
lv_obj_set_top(line, true);
2472
2473
// Option save button.
2474
lv_tabview_set_tab_load_action(tv, _show_hide_save_button);
2475
2476
// Check if Nyx was launched with a function set.
2477
if (nyx_str->cfg & NYX_CFG_UMS)
2478
{
2479
nyx_str->cfg &= ~(NYX_CFG_UMS);
2480
lv_task_t *task_run_ums = lv_task_create(nyx_run_ums, LV_TASK_ONESHOT, LV_TASK_PRIO_LOWEST, (void *)&nyx_str->cfg);
2481
lv_task_once(task_run_ums);
2482
}
2483
else if (n_cfg.home_screen)
2484
_create_window_home_launch(NULL);
2485
2486
if (!n_cfg.timeoffset)
2487
{
2488
lv_task_t *task_run_clock = lv_task_create(first_time_clock_edit, LV_TASK_ONESHOT, LV_TASK_PRIO_MID, NULL);
2489
lv_task_once(task_run_clock);
2490
}
2491
2492
if (!n_cfg.bpmp_clock)
2493
task_bpmp_clock = lv_task_create(first_time_bpmp_clock, 10000, LV_TASK_PRIO_LOWEST, NULL);
2494
}
2495
2496
void nyx_load_and_run()
2497
{
2498
memset(&system_tasks, 0, sizeof(system_maintenance_tasks_t));
2499
2500
lv_init();
2501
gfx_con.fillbg = 1;
2502
2503
// Initialize framebuffer drawing functions.
2504
lv_disp_drv_t disp_drv;
2505
lv_disp_drv_init(&disp_drv);
2506
disp_drv.disp_flush = _disp_fb_flush;
2507
lv_disp_drv_register(&disp_drv);
2508
2509
// Initialize Joy-Con.
2510
if (!n_cfg.jc_disable)
2511
{
2512
lv_task_t *task_jc_init_hw = lv_task_create(jc_init_hw, LV_TASK_ONESHOT, LV_TASK_PRIO_LOWEST, NULL);
2513
lv_task_once(task_jc_init_hw);
2514
}
2515
lv_indev_drv_t indev_drv_jc;
2516
lv_indev_drv_init(&indev_drv_jc);
2517
indev_drv_jc.type = LV_INDEV_TYPE_POINTER;
2518
indev_drv_jc.read = _jc_virt_mouse_read;
2519
memset(&jc_drv_ctx, 0, sizeof(jc_lv_driver_t));
2520
jc_drv_ctx.indev_jc = lv_indev_drv_register(&indev_drv_jc);
2521
close_btn = NULL;
2522
2523
// Initialize touch.
2524
touch_enabled = !touch_power_on();
2525
lv_indev_drv_t indev_drv_touch;
2526
lv_indev_drv_init(&indev_drv_touch);
2527
indev_drv_touch.type = LV_INDEV_TYPE_POINTER;
2528
indev_drv_touch.read = _fts_touch_read;
2529
jc_drv_ctx.indev_touch = lv_indev_drv_register(&indev_drv_touch);
2530
touchpad.touch = false;
2531
2532
// Initialize temperature sensor.
2533
tmp451_init();
2534
2535
// Set hekate theme based on chosen hue.
2536
lv_theme_t *th = lv_theme_hekate_init(n_cfg.theme_bg, n_cfg.theme_color, NULL);
2537
lv_theme_set_current(th);
2538
2539
// Create main menu
2540
_nyx_main_menu(th);
2541
2542
jc_drv_ctx.cursor = lv_img_create(lv_scr_act(), NULL);
2543
lv_img_set_src(jc_drv_ctx.cursor, &touch_cursor);
2544
lv_obj_set_opa_scale(jc_drv_ctx.cursor, LV_OPA_TRANSP);
2545
lv_obj_set_opa_scale_enable(jc_drv_ctx.cursor, true);
2546
2547
// Check if sd card issues.
2548
if (sd_get_mode() == SD_1BIT_HS25)
2549
{
2550
lv_task_t *task_run_sd_errors = lv_task_create(_nyx_sd_card_issues_warning, LV_TASK_ONESHOT, LV_TASK_PRIO_LOWEST, NULL);
2551
lv_task_once(task_run_sd_errors);
2552
}
2553
2554
// Gui loop.
2555
if (h_cfg.t210b01)
2556
{
2557
// Minerva not supported on T210B01 yet. Slight power saving via spinlock.
2558
while (true)
2559
{
2560
lv_task_handler();
2561
usleep(400);
2562
}
2563
}
2564
else
2565
{
2566
// Alternate DRAM frequencies. Total stall < 1ms. Saves 300+ mW.
2567
while (true)
2568
{
2569
minerva_change_freq(FREQ_1600); // Takes 295 us.
2570
2571
lv_task_handler();
2572
2573
minerva_change_freq(FREQ_800); // Takes 80 us.
2574
usleep(125); // Min 20us.
2575
}
2576
}
2577
}
2578
2579