Skip to content

Commit 59b222c

Browse files
committed
Add sprintf implementation and watchface stuff
1 parent 2bf5a2d commit 59b222c

File tree

3 files changed

+327
-146
lines changed

3 files changed

+327
-146
lines changed

src/displayapp/screens/Pawn.cpp

Lines changed: 139 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Pawn.h"
22
#include <stdio.h>
3+
#include <charconv>
34

45
using namespace Pinetime::Applications::Screens;
56

@@ -81,7 +82,7 @@ static cell AMX_NATIVE_CALL F_lv_label_set_text(AMX* amx, const cell* params) {
8182
if (text != NULL)
8283
lv_label_set_text(label, text);
8384
else
84-
lv_label_set_text(label, "<invalid>");
85+
lv_label_set_text_static(label, "<invalid>");
8586

8687
return 0;
8788
}
@@ -151,66 +152,154 @@ static cell AMX_NATIVE_CALL F_lv_obj_align(AMX* amx, const cell* params) {
151152
return 0;
152153
}
153154

155+
static cell AMX_NATIVE_CALL F_lv_obj_realign(AMX* amx, const cell* params) {
156+
ASSERT_PARAMS(1)
157+
lv_obj_realign(PARAMS_OBJ(1));
158+
return 0;
159+
}
160+
161+
/**
162+
* Hand-written implementation of sprintf with limited functionality in order to support reading strings from parameters.
163+
* Supported interpolations:
164+
* %%
165+
* %d and %x, including padding flags
166+
* %s with packed strings
167+
*/
154168
static cell AMX_NATIVE_CALL F_sprintf(AMX* amx, const cell* params) {
155-
// param[0] is the number of total parameter bytes, divide it by cell size and subtract 3 to account for the fixed parameters
156-
int args_count = params[0] / sizeof(cell) - 3;
169+
// param[0] is the number of total parameter bytes, divide it by cell size to get the parameter count
170+
int args_count = params[0] / sizeof(cell);
171+
if (args_count < 4) {
172+
amx_RaiseError(amx, AMX_ERR_PARAMCOUNT);
173+
return 0;
174+
}
157175

158176
cell* output = amx_Address(amx, params[1]);
159-
cell output_size = params[2] * sizeof(cell); // We assume the output array is packed, TODO: add a separate sprintf_unpacked function?
177+
cell max_size = params[2] * sizeof(cell); // We assume the output array is packed so each cell contains one character per byte
178+
// TODO: add a separate sprintf_unpacked function?
160179

161-
char buf[output_size];
180+
char buf[max_size];
181+
char* bufc = buf;
182+
char* bufmax = buf + max_size - 1;
162183

163184
char* fmt;
164185
amx_StrParam_Type(amx, params[3], fmt, char*);
165186
if (fmt == NULL)
166187
return 0;
167188

168-
cell ret = 0;
169-
170-
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
171-
switch (args_count) {
172-
case 0:
173-
strcpy(buf, fmt);
174-
ret = strlen(fmt) + 1;
175-
break;
176-
case 1:
177-
ret = snprintf(buf, output_size, fmt, *amx_Address(amx, params[4]));
178-
break;
179-
case 2:
180-
ret = snprintf(buf, output_size, fmt, *amx_Address(amx, params[4]), *amx_Address(amx, params[5]));
181-
break;
182-
case 3:
183-
ret = snprintf(buf, output_size, fmt, *amx_Address(amx, params[4]), *amx_Address(amx, params[5]), *amx_Address(amx, params[6]));
184-
break;
185-
case 4:
186-
ret = snprintf(buf,
187-
output_size,
188-
fmt,
189-
*amx_Address(amx, params[4]),
190-
*amx_Address(amx, params[5]),
191-
*amx_Address(amx, params[6]),
192-
*amx_Address(amx, params[7]));
193-
break;
194-
case 5:
195-
ret = snprintf(buf,
196-
output_size,
197-
fmt,
198-
*amx_Address(amx, params[4]),
199-
*amx_Address(amx, params[5]),
200-
*amx_Address(amx, params[6]),
201-
*amx_Address(amx, params[7]),
202-
*amx_Address(amx, params[8]));
203-
break;
204-
default:
205-
return 0;
189+
size_t paramc = 4;
190+
191+
size_t fmt_len = strlen(fmt);
192+
bool in_pc = false;
193+
194+
char flags[4];
195+
size_t flagc = 0;
196+
197+
for (size_t i = 0; i < fmt_len; i++) {
198+
char c = fmt[i];
199+
200+
if (c == '%') {
201+
if (in_pc) {
202+
*bufc++ = '%';
203+
in_pc = false;
204+
} else {
205+
flagc = 0;
206+
in_pc = true;
207+
}
208+
} else if (in_pc) {
209+
switch (c) {
210+
case 'x':
211+
case 'd': {
212+
int padding = 0;
213+
char pad = ' ';
214+
215+
if (flagc == 1) {
216+
padding = flags[0] - '0';
217+
} else if (flagc == 2) {
218+
pad = flags[0];
219+
padding = flags[1] - '0';
220+
}
221+
222+
std::to_chars_result result = std::to_chars(bufc, bufmax, (int) *amx_Address(amx, params[paramc++]), c == 'x' ? 16 : 10);
223+
224+
int padlen = padding - (result.ptr - bufc);
225+
for (int n = 0; n < padlen && bufc < bufmax; n++) {
226+
bufc[1] = bufc[0];
227+
bufc[0] = pad;
228+
bufc++;
229+
}
230+
231+
bufc = result.ptr + (padlen > 0 ? padlen : 0);
232+
in_pc = false;
233+
break;
234+
}
235+
236+
case 's': {
237+
cell param = params[paramc++];
238+
int len;
239+
amx_StrLen(amx_Address(amx, param), &len);
240+
241+
if (len > 0 && bufc + len < bufmax - 1) {
242+
amx_GetString(bufc, amx_Address(amx, param), 0, len + 1);
243+
bufc += len;
244+
}
245+
in_pc = false;
246+
break;
247+
}
248+
249+
default:
250+
if (flagc < sizeof(flags))
251+
flags[flagc++] = c;
252+
break;
253+
}
254+
} else {
255+
*bufc++ = c;
256+
}
206257
}
207-
#pragma GCC diagnostic warning "-Wformat-nonliteral"
258+
*bufc = 0;
208259

209-
amx_SetString(output, buf, 1, 0, output_size);
260+
amx_SetString(output, buf, 1, 0, max_size);
210261

211-
return ret;
262+
return bufc - buf;
212263
}
213264

265+
static cell AMX_NATIVE_CALL F_get_datetime(AMX* amx, const cell* params) {
266+
ASSERT_PARAMS(1);
267+
268+
Pawn* pawn = (Pawn*) amx->userdata[0];
269+
270+
pawn->currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(pawn->dateTimeController.CurrentDateTime());
271+
272+
cell* ret = amx_Address(amx, params[1]);
273+
274+
ret[0] = pawn->currentDateTime.IsUpdated();
275+
ret[1] = pawn->dateTimeController.Seconds();
276+
ret[2] = pawn->dateTimeController.Minutes();
277+
ret[3] = pawn->dateTimeController.Hours();
278+
ret[4] = pawn->dateTimeController.Day();
279+
ret[5] = pawn->dateTimeController.Year();
280+
281+
return 0;
282+
}
283+
284+
static cell AMX_NATIVE_CALL F_get_datetime_short_str(AMX* amx, const cell* params) {
285+
ASSERT_PARAMS(2);
286+
287+
Pawn* pawn = (Pawn*) amx->userdata[0];
288+
289+
cell* ret_day = amx_Address(amx, params[1]);
290+
cell* ret_month = amx_Address(amx, params[2]);
291+
292+
if (ret_day != NULL) {
293+
const char* day = pawn->dateTimeController.DayOfWeekShortToString();
294+
amx_SetString(ret_day, day, true, false, 4);
295+
}
296+
if (ret_month != NULL) {
297+
const char* month = pawn->dateTimeController.MonthShortToString();
298+
amx_SetString(ret_month, month, true, false, 4);
299+
}
300+
301+
return 0;
302+
}
214303

215304
static int load_program(AMX* amx, const uint8_t* data) {
216305
AMX_HEADER hdr;
@@ -236,9 +325,9 @@ static int load_program(AMX* amx, const uint8_t* data) {
236325
return result;
237326
}
238327

239-
Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateTimeController) {
240328
#include "program.h"
241329

330+
Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateTimeController) {
242331
load_program(&amx, program);
243332
(void) program_len;
244333

@@ -252,6 +341,8 @@ Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateT
252341

253342
const AMX_NATIVE_INFO natives[] = {
254343
{"sprintf", F_sprintf},
344+
{"get_datetime", F_get_datetime},
345+
{"get_datetime_short_str", F_get_datetime_short_str},
255346
{"lv_scr_act", F_lv_scr_act},
256347
{"lv_label_create", F_lv_label_create},
257348
{"lv_btn_create", F_lv_btn_create},
@@ -264,6 +355,7 @@ Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateT
264355
{"lv_obj_set_style_local_opa", F_lv_obj_set_style_local_opa},
265356
{"lv_obj_set_style_local_ptr", F_lv_obj_set_style_local_ptr},
266357
{"lv_obj_align", F_lv_obj_align},
358+
{"lv_obj_realign", F_lv_obj_realign},
267359
{0, 0} /* terminator */
268360
};
269361
amx_Register(&amx, natives, -1);

src/displayapp/screens/Pawn.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include "displayapp/apps/Apps.h"
44
#include "displayapp/screens/Screen.h"
55
#include "displayapp/Controllers.h"
6+
#include "components/datetime/DateTimeController.h"
7+
#include "utility/DirtyValue.h"
8+
#include <chrono>
69

710
#include "pawn/amx.h"
811

@@ -12,15 +15,18 @@ namespace Pinetime {
1215

1316
class Pawn : public Screen {
1417
public:
15-
Pawn();
18+
Pawn(Controllers::DateTime& dateTimeController);
1619
~Pawn() override;
1720

1821
void Refresh() override;
1922

23+
24+
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
25+
Controllers::DateTime& dateTimeController;
26+
2027
private:
2128
AMX amx;
2229
int refresh_index;
23-
2430
lv_task_t* taskRefresh = 0;
2531
};
2632
}
@@ -30,8 +36,8 @@ namespace Pinetime {
3036
static constexpr Apps app = Apps::Pawn;
3137
static constexpr const char* icon = "P";
3238

33-
static Screens::Screen* Create(AppControllers& /*controllers*/) {
34-
return new Screens::Pawn();
39+
static Screens::Screen* Create(AppControllers& controllers) {
40+
return new Screens::Pawn(controllers.dateTimeController);
3541
};
3642

3743
static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {

0 commit comments

Comments
 (0)