I'm trying to create a libretro core. It will be a standalone game, so I'm setting RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME to true. The documentation suggests that retro_get_memory_* can be used to have data saved without needing to explicitly query RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
The save directory should be used to
store SRAM, memory cards, high scores, etc, if the libretro core
cannot use the regular memory interface (retro_get_memory_data()).
How should the core trigger the saving of data using this interface? Or am I misunderstanding the documentation?
I would expect the frontend to call retro_get_memory_{data,size}, read from the exposed buffer when stopping the core, persist the data to disk, and write it back to the exposed buffer the next time the core starts. Instead I observe:
If I don't provide a content file, the frontend never calls retro_get_memory_{data,size}.
If I provide a content file (which is unused), the frontend calls retro_get_memory_{data,size} after retro_load_game but doesn't write to disk.
Note that this question is about save files (automatically persisted data, usually capturing the player's progress), not save states (snapshots of the game state triggered by the user) which are implemented by the *serialize* methods.
Here is a simple example to reproduce the issue (based on this sample):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libretro.h"
static unsigned char c = 0;
void* retro_get_memory_data(unsigned id) {
fprintf(stderr, "retro_get_memory_data(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? &c : NULL;
}
size_t retro_get_memory_size(unsigned id) {
fprintf(stderr, "retro_get_memory_size(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? 1 : 0;
}
#define WIDTH 320
#define HEIGHT 240
static uint32_t* frame_buf;
void retro_init(void) { frame_buf = calloc(WIDTH * HEIGHT, sizeof(uint32_t)); }
void retro_deinit(void) {
free(frame_buf);
frame_buf = NULL;
}
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
void retro_get_system_info(struct retro_system_info* info) {
memset(info, 0, sizeof(*info));
info->library_name = "SaveTest";
info->library_version = "v1";
info->need_fullpath = false;
info->valid_extensions = NULL; // Anything is fine, we don't care.
}
static retro_video_refresh_t video_cb;
static retro_environment_t environ_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
void retro_set_environment(retro_environment_t cb) {
environ_cb = cb;
bool no_content = true;
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);
}
void retro_get_system_av_info(struct retro_system_av_info* info) {
float aspect = (float)WIDTH / HEIGHT;
info->timing = (struct retro_system_timing){
.fps = 60.0,
.sample_rate = 0.0,
};
info->geometry = (struct retro_game_geometry){
.base_width = WIDTH,
.base_height = HEIGHT,
.max_width = WIDTH,
.max_height = HEIGHT,
.aspect_ratio = aspect,
};
}
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
bool retro_load_game(const struct retro_game_info* info) {
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
fprintf(stderr, "XRGB8888 is not supported.\n");
return false;
}
(void)info;
return true;
}
bool button(unsigned id) {
return input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, id);
}
void retro_run(void) {
input_poll_cb();
if (button(RETRO_DEVICE_ID_JOYPAD_LEFT) && c > 0) --c;
if (button(RETRO_DEVICE_ID_JOYPAD_RIGHT) && c < 255) ++c;
uint32_t color = (255 - c) | (c << 8);
uint32_t* buf = frame_buf;
for (unsigned i = WIDTH * HEIGHT; i > 0; --i) {
*buf = color;
++buf;
}
video_cb(frame_buf, WIDTH, HEIGHT, WIDTH * sizeof(uint32_t));
}
void retro_unload_game(void) {}
size_t retro_serialize_size(void) { return 1; }
bool retro_serialize(void* data, size_t size) {
fprintf(stderr, "serialize(%p, %lu) <= %u\n", data, size, c);
*(char*)data = c;
return true;
}
bool retro_unserialize(const void* data, size_t size) {
c = *(char*)data;
fprintf(stderr, "unserialize(%p, %lu) => %u\n", data, size, c);
return true;
}
void retro_set_controller_port_device(unsigned port, unsigned device) {}
void retro_set_audio_sample(retro_audio_sample_t cb) {}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {}
void retro_reset(void) {}
bool retro_load_game_special(unsigned type, const struct retro_game_info* info,
size_t num) {
return false;
}
void retro_cheat_reset(void) {}
void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
It is undocumented but intended that auto save does not trigger for a core without content.
Moreover auto save was not triggering for cores which had content but could support no content. This was unintended and recently fixed.
Reference: https://github.com/libretro/RetroArch/issues/9300
Related
Currently, I'm working on read and write callback in open62541, I created a structure variable XV type, It's fields are X and V in XML file
X is a type of double and V is a type of float
if I write the value using the client tool it's entering into the write callback function and I'm able to print the X value alone using the print cout<<"Field value is "<< *(double*)data->value.data<<endl;, but I'm unable to fetch the second value that is V,
I took the below code as a reference, in that in the afterWriteTime callback function I tried
cout<<"Field value is "<< *(double*)data->value.data<<endl;this print statemet
it's printing only the X's value alone, how can I get two values from the callback function
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
static void
updateCurrentTime(UA_Server *server) {
UA_DateTime now = UA_DateTime_now();
UA_Variant value;
UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_Server_writeValue(server, currentNodeId, value);
}
static void
addCurrentTimeVariable(UA_Server *server) {
UA_DateTime now = 0;
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - value callback");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-value-callback");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr, NULL, NULL);
updateCurrentTime(server);
}
/**
* Variable Value Callback
* ^^^^^^^^^^^^^^^^^^^^^^^
*
* When a value changes continuously, such as the system time, updating the
* value in a tight loop would take up a lot of resources. Value callbacks allow
* to synchronize a variable value with an external representation. They attach
* callbacks to the variable that are executed before every read and after every
* write operation. */
static void
beforeReadTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeid, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
updateCurrentTime(server);
}
static void
afterWriteTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"The variable was updated");
cout<<"Field value is "<< *(double*)data->value.data<<endl;
}
static void
addValueCallbackToCurrentTimeVariable(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_ValueCallback callback ;
callback.onRead = beforeReadTime;
callback.onWrite = afterWriteTime;
UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
}
/**
* Variable Data Sources
* ^^^^^^^^^^^^^^^^^^^^^
*
* With value callbacks, the value is still stored in the variable node.
* So-called data sources go one step further. The server redirects every read
* and write request to a callback function. Upon reading, the callback provides
* a copy of the current value. Internally, the data source needs to implement
* its own memory management. */
static UA_StatusCode
readCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
UA_DataValue *dataValue) {
UA_DateTime now = UA_DateTime_now();
UA_Variant_setScalarCopy(&dataValue->value, &now,
&UA_TYPES[UA_TYPES_DATETIME]);
dataValue->hasValue = true;
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
writeCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Changing the system time is not implemented");
return UA_STATUSCODE_BADINTERNALERROR;
}
static void
addCurrentTimeDataSourceVariable(UA_Server *server) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_DataSource timeDataSource;
timeDataSource.read = readCurrentTime;
timeDataSource.write = writeCurrentTime;
UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr,
timeDataSource, NULL, NULL);
}
static UA_DataValue *externalValue;
static void
addCurrentTimeExternalDataSource(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-external-source");
UA_ValueBackend valueBackend;
valueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL;
valueBackend.backend.external.value = &externalValue;
UA_Server_setVariableNode_valueBackend(server, currentNodeId, valueBackend);
}
/** It follows the main server code, making use of the above definitions. */
static volatile UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
addCurrentTimeVariable(server);
addValueCallbackToCurrentTimeVariable(server);
addCurrentTimeDataSourceVariable(server);
addCurrentTimeExternalDataSource(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
Footer
© 2022 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Docs
Contact GitHub
Pricing
API
Training
Blog
About
open62541/tutorial_server_datasource.c at master · open62541/open62541 · GitHub
in order to set a certain variable (MyVariable) to "TRUE" I have to check that a specific function call order was respected within a system.
For example, I have different functions within the system:
uint8 myFunction1()
{
if (...)
{
return NOT_OK
}
else
{
return OK
}
}
uint8 myFunction2()
{
if (...)
{
return NOT_OK
}
else
{
return OK
}
}
uint8 myFunction3()
{
if (...)
{
return NOT_OK
}
else
{
return OK
}
}
MyVariable = TRUE only if:
OK == myFunction1
OK == myFunction2
OK == myFunction3
exactly this call order was respected.
How to check the call order in C but without touching the body of the functions (like setting some flags´etc.)?
I'm still beginner and experimenting with C :)
Thanks!
This is almost certainly an "XY problem". That is, you think saving the call order is the solution to your actual problem, but your actual problem might be to ensure that the functions can't be called in the wrong order in the first place.
So the most correct way to fix this is to remake the program design. Someone mentioned state machines as one solution. Another solution might be something like an array of function pointers (which is a common implementation of state machines).
That being said, you can do something artificial to track the call order, though I wouldn't really recommend it. Example:
#define CALL_ORDER_N 3
const char* call_order [CALL_ORDER_N] = {NULL};
size_t call_order_i = 0;
static void save_call (const char* func)
{
call_order[call_order_i] = func;
call_order_i++;
if(call_order_i == CALL_ORDER_N)
{
call_order_i = 0;
}
}
Where call_order saves the 3 last function calls as pointers to string literals. The function save_call updates this array, by passing the __func__ constant to it from each function. __func__ is guaranteed to work like a static const char[] so this is safe. You'd do something like this:
void myFunction1 (void)
{
save_call(__func__);
...
}
void myFunction2 (void)
{
save_call(__func__);
...
}
void myFunction3 (void)
{
save_call(__func__);
...
}
And then go through the calls to see if they were in the correct order:
static bool is_call_order_ok (void)
{
const char* expected_order [CALL_ORDER_N] =
{
"myFunction1",
"myFunction2",
"myFunction3"
};
size_t co_i = call_order_i;
for(size_t i=0; i<CALL_ORDER_N; i++)
{
if(strcmp(call_order[co_i], expected_order[i])==0)
{
co_i++;
if(co_i == CALL_ORDER_N)
{
co_i = 0;
}
}
else
{
return false;
}
}
return true;
}
Full example:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define CALL_ORDER_N 3
const char* call_order [CALL_ORDER_N] = {NULL};
size_t call_order_i = 0;
static void save_call (const char* func)
{
call_order[call_order_i] = func;
call_order_i++;
if(call_order_i == CALL_ORDER_N)
{
call_order_i = 0;
}
}
static bool is_call_order_ok (void)
{
const char* expected_order [CALL_ORDER_N] =
{
"myFunction1",
"myFunction2",
"myFunction3"
};
size_t co_i = call_order_i;
for(size_t i=0; i<CALL_ORDER_N; i++)
{
if(strcmp(call_order[co_i], expected_order[i])==0)
{
co_i++;
if(co_i == CALL_ORDER_N)
{
co_i = 0;
}
}
else
{
return false;
}
}
return true;
}
void myFunction1 (void)
{
save_call(__func__);
}
void myFunction2 (void)
{
save_call(__func__);
}
void myFunction3 (void)
{
save_call(__func__);
}
int main (void)
{
printf("Call 1,2,3: ");
myFunction1();
myFunction2();
myFunction3();
printf(is_call_order_ok() ? "Ok\n" : "Failed\n");
printf("Call 3,2,1: ");
myFunction3();
myFunction2();
myFunction1();
printf(is_call_order_ok() ? "Ok\n" : "Failed\n");
printf("Call 1,1,1: ");
myFunction1();
myFunction1();
myFunction1();
printf(is_call_order_ok() ? "Ok\n" : "Failed\n");
return 0;
}
The advanced, more professional version of the above, would be to cook together a mini-API with a single function, in order to give private encapsulation to every single variable. The function save_call would then be a multi-purpose function, that can be used to register expected call order, save function calls, as well as verify if the current registered order is ok.
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define CALL_ORDER_N 3
static bool save_call (const char* func, bool verify)
{
bool result;
static const char* call_order [CALL_ORDER_N] = {NULL};
static size_t call_order_i = 0;
static const char* expected_order [CALL_ORDER_N] = {NULL};
size_t i = call_order_i;
if(verify) // special case, verify the order
{
for(size_t expected=0; expected<CALL_ORDER_N; expected++)
{
if(call_order[i] == expected_order[expected])
{
i++;
if(i == CALL_ORDER_N)
{
i = 0;
}
}
else
{
return false;
}
}
return true;
}
if(expected_order[i] == NULL) // register order of calls
{
expected_order[i] = func;
result = true;
}
else // save calls
{
call_order[i] = func;
result = false;
}
call_order_i++;
if(call_order_i == CALL_ORDER_N)
{
call_order_i = 0;
}
return result;
}
void myFunction1 (void)
{
if(save_call(__func__, false))
return ;
printf("Execute stuff in %s.\n", __func__);
}
void myFunction2 (void)
{
if(save_call(__func__, false))
return ;
printf("Execute stuff in %s.\n", __func__);
}
void myFunction3 (void)
{
if(save_call(__func__, false))
return ;
printf("Execute stuff in %s.\n", __func__);
}
int main (void)
{
/* register call order: */
myFunction1();
myFunction2();
myFunction3();
printf("Call 1,2,3:\n");
myFunction1();
myFunction2();
myFunction3();
printf(save_call(NULL, true) ? "Ok\n\n" : "Failed\n\n");
printf("Call 3,2,1:\n");
myFunction3();
myFunction2();
myFunction1();
printf(save_call(NULL, true) ? "Ok\n\n" : "Failed\n\n");
printf("Call 1,1,1:\n");
myFunction1();
myFunction1();
myFunction1();
printf(save_call(NULL, true) ? "Ok\n\n" : "Failed\n\n");
return 0;
}
save_call should of course be properly placed in a .h/.c file pair of its own.
There is no direct and portable way. That being said, debuggers are great at breaking execution flow when a function is reached, so you could either use a debugger, or use debugging functions to be warned when the functions are called (unfortunately nothing portable here).
Alternatively, some linkers allow to hide some identifiers and replace them so with custom (and advanced) link options you could replace all call to those functions with calls to custom wrappers. But here again it would only makes sense for a specific implementation so it is not a C way either.
Anyway, this is such an uncommon requirement that I cannot imagine the actual reasonning behind. Maybe you could give more context about your real problem...
I have been trying to get an app working on my pebble for a while now and I finally got it to work. It's a sports counter that keeps track of the score for both teams and also has a built in stop watch in the middle. Everything works as it should but if the stopwatch runs for 42 seconds then the font for the counters become very small. I have no idea whats going on.
When I was looking around someone said it may be a memory leak, but this was not similar to my problem.
#include <pebble.h>
#define COUNTER_FONT_49 RESOURCE_ID_MACHINE_GUN_49
#define STOPWATCH_FONT_24 RESOURCE_ID_SPORTS_WORLD_24
#define HOME_AWAY_FONT_18 FONT_KEY_GOTHIC_18_BOLD
//---Counter Constants---
#define COUNTER_START 0
#define COUNTER_MAX 9999
#define COUNTER_MIN -9999
#define MAX_DIGITS 4
//---Interface Variables---
Window* window;
static Layer* layer;
static GFont counterFont;
static GFont stopwatchFont;
static TextLayer* teamAScore_layer;
static TextLayer* teamBScore_layer;
static TextLayer* big_time_layer;
static TextLayer* seconds_time_layer;
static TextLayer* home_away_layer;
//---Counter Variables---
char teamA_counter_text[MAX_DIGITS + 2 /* sign & \0 */];
char teamB_counter_text[MAX_DIGITS + 2 /* sign & \0 */];
int teamACounter;
int teamBCounter;
int singleClickIncrement;
int longClickIncrement;
int doubleClickIncrement;
//The Time
static double elapsed_time = 0;
static bool started = false;
static AppTimer* update_timer = NULL;
static double start_time = 0;
static double pause_time = 0;
time_t time_seconds();
void stop_stopwatch();
void start_stopwatch();
void handle_timer(void* data);
void update_stopwatch();
double float_time_ms() {
time_t seconds;
uint16_t milliseconds;
time_ms(&seconds, &milliseconds);
return (double)seconds + ((double)milliseconds / 1000.0);
}
void stop_stopwatch() {
started = false;
pause_time = float_time_ms();
if(update_timer != NULL) {
app_timer_cancel(update_timer);
update_timer = NULL;
}
}
void start_stopwatch() {
started = true;
if(start_time == 0) {
start_time = float_time_ms();
} else if(pause_time != 0) {
double interval = float_time_ms() - pause_time;
start_time += interval;
}
update_timer = app_timer_register(100, handle_timer, NULL);
}
void update_stopwatch() {
static char big_time[] = "00:00";
static char deciseconds_time[] = ".0";
static char seconds_time[] = ":00";
// Now convert to hours/minutes/seconds.
int tenths = (int)(elapsed_time * 10) % 10;
int seconds = (int)elapsed_time % 60;
int minutes = (int)elapsed_time / 60 % 60;
int hours = (int)elapsed_time / 3600;
// We can't fit three digit hours, so stop timing here.
if(hours > 99) {
stop_stopwatch();
return;
}
if(hours < 1) {
snprintf(big_time, 6, "%02d:%02d", minutes, seconds);
snprintf(deciseconds_time, 3, ".%d", tenths);
} else {
snprintf(big_time, 6, "%02d:%02d", hours, minutes);
snprintf(seconds_time, 4, ":%02d", seconds);
}
// Now draw the strings.
text_layer_set_text(big_time_layer, big_time);
text_layer_set_text(seconds_time_layer, hours < 1 ? deciseconds_time : seconds_time);
}
void select_click_long_handler(ClickRecognizerRef recognizer, Window *window) { //pressed SELECT LONG
bool is_running = started;
stop_stopwatch();
start_time = 0;
elapsed_time = 0;
if(is_running) start_stopwatch();
update_stopwatch();
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed SELECT
if(started) {
stop_stopwatch();
} else {
start_stopwatch();
}
}
void handle_timer(void* data) {
if(started) {
double now = float_time_ms();
elapsed_time = now - start_time;
update_timer = app_timer_register(100, handle_timer, NULL);
}
update_stopwatch();
}
static int increment_value(int team_score, const int increment){
if(team_score + increment <= COUNTER_MAX && team_score + increment >= COUNTER_MIN)
team_score = team_score + increment;
return team_score;
}
static void up_multi_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP MULTI
teamACounter = increment_value(teamACounter, doubleClickIncrement);
layer_mark_dirty(layer);
}
static void up_click_long_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP LONG
teamACounter = increment_value(teamACounter, longClickIncrement);
layer_mark_dirty(layer);
}
static void up_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP
teamACounter = increment_value(teamACounter, singleClickIncrement);
layer_mark_dirty(layer);
}
static void down_multi_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN MULTI
teamBCounter = increment_value(teamBCounter, doubleClickIncrement);
layer_mark_dirty(layer);
}
static void down_click_long_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN LONG
teamBCounter = increment_value(teamBCounter, longClickIncrement);
layer_mark_dirty(layer);
}
static void down_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN
teamBCounter = increment_value(teamBCounter, singleClickIncrement);
layer_mark_dirty(layer);
}
static void click_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_long_click_subscribe(BUTTON_ID_SELECT, 700, (ClickHandler) select_click_long_handler, NULL);
window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
window_long_click_subscribe(BUTTON_ID_UP, 700, (ClickHandler)up_click_long_handler, NULL);
window_multi_click_subscribe(BUTTON_ID_UP, 2, 10, 0, true, up_multi_click_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
window_long_click_subscribe(BUTTON_ID_DOWN, 700, (ClickHandler)down_click_long_handler, NULL);
window_multi_click_subscribe(BUTTON_ID_DOWN, 2, 10, 0, true, down_multi_click_handler);
}
static void update_layer(Layer *layer, GContext *ctx){
GRect bounds = layer_get_frame(layer);
counterFont = fonts_load_custom_font(resource_get_handle(COUNTER_FONT_49));
graphics_context_set_text_color(ctx, GColorBlack);
snprintf(teamA_counter_text, (MAX_DIGITS + 2)*sizeof(char), "%d", teamACounter);
graphics_draw_text(ctx,
teamA_counter_text,
counterFont,
GRect(0, 0, bounds.size.w - 10, 60),
GTextOverflowModeWordWrap,
GTextAlignmentCenter,
NULL);
snprintf(teamB_counter_text, (MAX_DIGITS + 2)*sizeof(char), "%d", teamBCounter);
graphics_draw_text(ctx,
teamB_counter_text,
counterFont,
GRect(0, 95, bounds.size.w - 10, 60),
GTextOverflowModeWordWrap,
GTextAlignmentCenter,
NULL);
}
static void window_load(Window *window) {
// Get the root layer
Layer *window_layer = window_get_root_layer(window);
// Get the bounds of the window for sizing the text layer
GRect bounds = layer_get_bounds(window_layer);
layer = layer_create(bounds);
layer_set_update_proc(layer, update_layer);
layer_add_child(window_layer, layer);
}
static void window_unload(Window *window) {
// Destroy TextLayer
text_layer_destroy(teamAScore_layer);
text_layer_destroy(teamBScore_layer);
text_layer_destroy(big_time_layer);
text_layer_destroy(seconds_time_layer);
}
void handle_init(void) {
window = window_create();
window_set_click_config_provider(window, click_config_provider);
window_set_window_handlers(window, (WindowHandlers) {
.load = window_load,
.unload = window_unload,
});
//Set the counters start
teamACounter = COUNTER_START;
teamBCounter = COUNTER_START;
//---------------------------------------------TEMPERARY INCREMENT
singleClickIncrement = 1;
doubleClickIncrement = 2;
longClickIncrement = 3;
Layer *root_layer = window_get_root_layer(window);
stopwatchFont = fonts_load_custom_font(resource_get_handle(STOPWATCH_FONT_24));
//-----Display Stop Watch-----
big_time_layer = text_layer_create(GRect(0, 65, 86, 35));
text_layer_set_text_alignment(big_time_layer, GTextAlignmentRight);
text_layer_set_background_color(big_time_layer, GColorClear);
text_layer_set_text(big_time_layer, "00:00");
text_layer_set_font(big_time_layer, stopwatchFont);
layer_add_child(root_layer, (Layer*)big_time_layer);
seconds_time_layer = text_layer_create(GRect(86, 65, 49, 35));
text_layer_set_text(seconds_time_layer, ".0");
text_layer_set_background_color(seconds_time_layer, GColorClear);
text_layer_set_font(seconds_time_layer, stopwatchFont);
layer_add_child(root_layer, (Layer*)seconds_time_layer);
//-----Display Home and Away-----
home_away_layer = text_layer_create(GRect(124, 0, 20, 152));
text_layer_set_text(home_away_layer, "\n H\n\n\n\n\n A");
text_layer_set_background_color(home_away_layer, GColorBlack);
text_layer_set_text_color(home_away_layer, GColorWhite);
text_layer_set_font(home_away_layer, fonts_get_system_font(HOME_AWAY_FONT_18));
layer_add_child(root_layer, (Layer*)home_away_layer);
const bool animated = true;
window_stack_push(window, animated);
}
void handle_deinit(void) {
window_destroy(window);
}
int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}
You're loading the custom font into memory each time the update_layer function is called. Eventually the app runs out of memory and can't load the font any more, which is why it uses the fallback (really small) font.
You should just load the custom font once, in your init function.
I'm currently programming a PIC in C with MPLAB X (+ compiler XC8)
In my code, I have some interruptions (inter1, inter2, ..) which are each composed of urgent and non-urgent operations (urg1, urg2, .., n_urg1, n_urg2, ..).
So I'd like a code with the following structure :
stack s; // FIFO or other
main() {
while (true) {
if (!isEmpty(s)) {
doNextFunction(s);
}
}
}
void interrupt inter1() {
urg1(); // urgent code
addStack(n_urg1);
}
void n_urg1() {
// non-urgent code
}
How can I implement that kind of stack ? Is there something in the standard library ?
If I remember correct, then that compiler is rather primitive, I don't think you can use the std library.
If you need to implement it all by yourself, you can use an array of function pointers:
#include <string.h> // for memmove()
#define STACK_MAX 10
typedef enum
{
Int,
Boolean
} VariantType;
typedef struct
{
VariantType type;
union {
int intValue;
bool booleanValue;
} value;
} Variant;
typedef bool (*FunctionPtr)(Variant data);
typedef struct
{
FunctionPtr ptr;
Variant var;
} FunctionCall;
FunctionCall functionStack[STACK_MAX];
int functionStackUse = 0;
bool addStack(FunctionPtr ptr, Variant var)
{
if (functionStackUse >= STACK_MAX)
return false; // stack full
functionStack[functionStackUse].ptr = ptr;
functionStack[functionStackUse].var = var;
functionStackUse++;
return true;
}
bool callNextFunction(void)
{
// TODO: disable inter1
if (functionStackUse > 0)
{
// get first function on stack
FunctionCall functionCall = functionStack[0];
functionStackUse--;
// remove first function from stack
memmove((void*)functionStack, (void*)(functionStack + 1), functionStackUse * sizeof(functionStack[0]));
// TODO: re-enable inter1
// call function with arguments
return (*functionCall.ptr)(functionCall.var);
}
else
{
// TODO: re-enable inter1
return false; // no more functions
}
}
void main()
{
while (1)
{
callNextFunction();
// TODO add some delay otherwise you're constantly disabling inter1 (in doNextFunction)
}
}
bool n_urg1(Variant var)
{
if (var.type == Int)
{
int i = var.value.intValue;
// do something
return true;
}
return false;
}
void inter1(void)
{
Variant var;
var.type = Int;
var.value.intValue = 45;
addStack(n_urg1, var);
}
i have an error that i dont understand why appear, the most weird is that happens sometimes but lately happens always, the code that i use is the same and i dont have make any changes to it, in fact i dont execute any code in the start of the application, i use buttons , so i dont get a reason with this error please help me, the error is a dialog that say:
Native extension Error: there are packaging errors/warning. Check each native extension in the Flex Build Packaging Page for each target platform. would you like to continue?
And when i click yes the program dont start, the debbuger show me the line:
[SWF] SerialCOMGame.swf - 2,121,630 bytes after decompression
but never start, i am using RS232 library for serial communication, before of get this error this work fine but i dont know what happen, my C code is:
/*
* NativeSerialComunication.c
*
* Created on: Jan 10, 2012
* Author: Diego Fernando
*/
#include "NativeSerialComunication.h"
int comport = 0;
int baudrate = 57600;
int buffsize = 4096;
unsigned char buffer[4096];
uint32_t comportOpened = 0;
FREObject IsSupported(FREContext ctx, void* functionData, uint32_t argc,
FREObject argv[]) {
FREObject result;
uint32_t isSuppoerted = 1;
FRENewObjectFromBool(isSuppoerted, &result);
return result;
}
int startCOMListener() {
if (!OpenComport(comport, baudrate)) {
comportOpened = 1;
return 1;
}
return 0;
}
void stopCOMListener() {
CloseComport(comport);
comportOpened = 0;
}
void COMListener(FREContext ctx) {
uint8_t compbytes = 0;
while (comportOpened) {
compbytes = PollComport(comport, buffer, buffsize);
FREDispatchStatusEventAsync(ctx, (const uint8_t *) "listening for data",(const uint8_t *)"datalistening");
if (compbytes) {
FREDispatchStatusEventAsync(ctx, (const uint8_t *) buffer,
(const uint8_t *) "datareceived");
}
Sleep(100);
}
}
FREObject startSerialListener(FREContext ctx, void* functionData, uint32_t argc,
FREObject argv[]) {
FREObject result;
if (startCOMListener()) {
CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,(LPTHREAD_START_ROUTINE) COMListener, ctx, 0, NULL);
FREDispatchStatusEventAsync(ctx, (const uint8_t *) "listener started",
(const uint8_t *) "listenerstarted");
}
FRENewObjectFromBool(comportOpened, &result);
return result;
}
FREObject stopSerialListener(FREContext ctx, void* functionData, uint32_t argc,
FREObject argv[]) {
FREObject result;
stopCOMListener();
FRENewObjectFromBool(comportOpened, &result);
return result;
}
FREObject sendDataToSerialPort(FREContext ctx, void* functionData,
uint32_t argc, FREObject argv[]) {
FREObject result;
uint32_t dataSended = 0;
uint32_t dataToSend = 0;
FREGetObjectAsUint32(argv[0],&dataToSend);
printf("data to be sended %d",dataToSend);
if (comportOpened && !SendByte(comport,dataToSend)) {
dataSended = 1;
}
FRENewObjectFromBool(dataSended, &result);
return result;
}
void MyContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToSet, const FRENamedFunction** functionsToSet) {
*numFunctionsToSet = 4;
FRENamedFunction* func = (FRENamedFunction*) malloc(
sizeof(FRENamedFunction) * 4);
func[0].name = (const uint8_t*) "isSupported";
func[0].functionData = 0;
func[0].function = &IsSupported;
func[1].name = (const uint8_t*) "sendDataToSerialPort";
func[1].functionData = 0;
func[1].function = &sendDataToSerialPort;
func[2].name = (const uint8_t*) "startSerialListener";
func[2].functionData = 0;
func[2].function = &startSerialListener;
func[3].name = (const uint8_t*) "stopSerialListener";
func[3].functionData = 0;
func[3].function = &stopSerialListener;
/*func[1].name = (const uint8_t*) "sayHelloWorld";
func[1].functionData = 0;
func[1].function = &sayHelloWorld;*/
*functionsToSet = func;
}
void MyContextFinalizer(FREContext ctx) {
return;
}
void initializer(void** extDataToSet,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet) {
extDataToSet = 0; // This example does not use any extension data.
*ctxInitializerToSet = &MyContextInitializer;
*ctxFinalizerToSet = &MyContextFinalizer;
}
void finalizer(void** extDataToSet) {
stopCOMListener();
return;
}
And this is my ActionScript code that use the native C code:
package com.nativeserialcomunication.driver
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class NativeSerialComunication extends EventDispatcher
{
private var extensionContext:ExtensionContext;
private var isSerialListenerStarted:Boolean = false;
public function NativeSerialComunication(target:IEventDispatcher=null)
{
super(target);
extensionContext =ExtensionContext.createExtensionContext("com.nativeserialcomunitacion.driver.NativeSerialComunitation","");
extensionContext.addEventListener(StatusEvent.STATUS,statusHandle);
}
public function init():void{
if(extensionContext.call("startSerialListener") as Boolean){
isSerialListenerStarted = true;
trace("serial listener started");
}
else{
trace("no serial listener started");
}
}
public function statusHandle(event:StatusEvent):void{
trace("the event ("+event.level+") received, data:"+event.code);
}
public function isSupported():Boolean{
return extensionContext.call("isSupported") as Boolean;
}
public function sendDataToSerialPort(data:uint):Boolean{
return extensionContext.call("sendDataToSerialPort",data) as Boolean;
}
}
}
The native extension package checkbox for your target platform(s) may need to be checked.
Navigate to:
Project Properties -> Flex Build Packaging -> Apple iOS (or Android, etc.)
Select the Native Extensions tab and make sure "Package" is checked.