Tizen native web view: How to store and restore cookies in a native web view? - webview

I am trying to develop a native web view app and it is working good.
But every time I close and restart the app all login/cookie information is lost.
How can I store and restore cookies in a native web view?
What is very weird: The HTML5 Web Storage (local db) gets restored, but the cookies aren't available anymore!?
Do you have any information for me?
Here is the basic code that I am using (from a Samsung example):
#include "webviewexample.h"
// Header files needed for EWK Webkit
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Ecore_Getopt.h>
#include <Eet.h>
#include <Eina.h>
#include <Elementary.h>
#include <Evas.h>
#include <EWebKit.h>
#include <app.h>
typedef struct appdata {
Evas_Object *win;
Evas_Object *conform;
Evas_Object *label;
Evas_Object *entry;
Evas_Object *web_view;
Evas_Object *back_button;
Evas_Object *forward_button;
} appdata_s;
static void
win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s *ad = data;
/* Let window go to hide state. */
elm_win_iconified_set(ad->win, EINA_TRUE);
}
static void
btn_go_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s* ad = data;
ewk_view_url_set(ad->web_view, elm_object_text_get(ad->entry) );
}
static void
btn_prev_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s* ad = data;
if( ewk_view_back_possible( ad->web_view ) == EINA_TRUE )
ewk_view_back( ad->web_view );
}
static void
btn_next_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s* ad = data;
if( ewk_view_forward_possible( ad->web_view ) == EINA_TRUE )
ewk_view_forward( ad->web_view );
}
static void
my_table_pack(Evas_Object *table, Evas_Object *child, int x, int y, int w, int h)
{
evas_object_size_hint_align_set(child, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(child, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_table_pack(table, child, x, y, w, h);
evas_object_show(child);
}
static void
create_base_gui(appdata_s *ad)
{
/* set up policy to exit when last window is closed */
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
/* Window */
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
elm_win_autodel_set(ad->win, EINA_TRUE);
int rots[4] = { 0, 90, 180, 270 };
elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
{
/* Box to put the table in so we can bottom-align the table
* window will stretch all resize object content to win size */
Evas_Object *box = elm_box_add(ad->win);
evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, box);
evas_object_show(box);
/* Table */
Evas_Object *table = elm_table_add(ad->win);
/* Make table homogenous - every cell will be the same size */
elm_table_homogeneous_set(table, EINA_TRUE);
/* Set padding of 10 pixels multiplied by scale factor of UI */
elm_table_padding_set(table, 5 * elm_config_scale_get(), 10 * elm_config_scale_get());
/* Let the table child allocation area expand within in the box */
evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
/* Set table to fiill width but align to bottom of box */
evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(box, table);
evas_object_show(table);
{
/* Entry */
ad->entry = elm_entry_add(ad->win);
elm_entry_scrollable_set(ad->entry, EINA_TRUE);
eext_entry_selection_back_event_allow_set(ad->entry, EINA_TRUE);
elm_object_text_set(ad->entry, "http://www.tizen.org");
my_table_pack(table, ad->entry, 0, 0, 3, 1);
/* Button-1 */
Evas_Object *btn = elm_button_add(ad->win);
elm_object_text_set(btn, "Prev");
evas_object_smart_callback_add(btn, "clicked", btn_prev_cb, ad);
my_table_pack(table, btn, 0, 1, 1, 1);
/* Button-2 */
btn = elm_button_add(ad->win);
elm_object_text_set(btn, "Go");
evas_object_smart_callback_add(btn, "clicked", btn_go_cb, ad);
my_table_pack(table, btn, 1, 1, 1, 1);
/* Button-3 */
btn = elm_button_add(ad->win);
elm_object_text_set(btn, "Next");
evas_object_smart_callback_add(btn, "clicked", btn_next_cb, ad);
my_table_pack(table, btn, 2, 1, 1, 1);
/* WebView */
Evas *evas = evas_object_evas_get(ad->win);
ad->web_view = ewk_view_add(evas);
ewk_view_url_set(ad->web_view, elm_object_text_get(ad->entry) );
my_table_pack(table, ad->web_view, 0, 2, 3, 8);
}
}
/* Show window after base gui is set up */
evas_object_show(ad->win);
}
static bool
app_create(void *data)
{
/* Hook to take necessary actions before main event loop starts
Initialize UI resources and application's data
If this function returns true, the main loop of application starts
If this function returns false, the application is terminated */
appdata_s *ad = data;
create_base_gui(ad);
return true;
}
static void
app_control(app_control_h app_control, void *data)
{
/* Handle the launch request. */
}
static void
app_pause(void *data)
{
/* Take necessary actions when application becomes invisible. */
}
static void
app_resume(void *data)
{
/* Take necessary actions when application becomes visible. */
}
static void
app_terminate(void *data)
{
/* Release all resources. */
ewk_shutdown();
}
static void
ui_app_lang_changed(app_event_info_h event_info, void *user_data)
{
/*APP_EVENT_LANGUAGE_CHANGED*/
char *locale = NULL;
system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &locale);
elm_language_set(locale);
free(locale);
return;
}
int
main(int argc, char *argv[])
{
appdata_s ad = {0,};
memset(&ad, 0x00, sizeof(appdata_s));
int ret = 0;
ui_app_lifecycle_callback_s event_callback = {0,};
memset(&event_callback, 0x00, sizeof(ui_app_lifecycle_callback_s));
app_event_handler_h handlers[5] = {NULL, };
event_callback.create = app_create;
event_callback.terminate = app_terminate;
event_callback.pause = app_pause;
event_callback.resume = app_resume;
event_callback.app_control = app_control;
ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, ui_app_lang_changed, &ad);
ret = ui_app_main(argc, argv, &event_callback, &ad);
if (ret != APP_ERROR_NONE) {
dlog_print(DLOG_ERROR, LOG_TAG, "app_main() is failed. err = %d", ret);
}
return ret;
}
The manifest file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<manifest xmlns="http://tizen.org/ns/packages" api-version="2.4" package="org.example.webviewexample" version="1.0.0">
<profile name="mobile"/>
<ui-application appid="org.example.webviewexample" exec="webviewexample" launch_mode="single" multiple="false" nodisplay="false" taskmanage="true" type="capp">
<label>webviewexample</label>
<icon>webviewexample.png</icon>
</ui-application>
<privileges>
<privilege>http://tizen.org/privilege/network.get</privilege>
<privilege>http://tizen.org/privilege/appmanager.launch</privilege>
<privilege>http://tizen.org/privilege/location</privilege>
<privilege>http://tizen.org/privilege/internet</privilege>
<privilege>http://tizen.org/privilege/notification</privilege>
<privilege>http://tizen.org/privilege/display</privilege>
<privilege>http://tizen.org/privilege/camera</privilege>
<privilege>http://tizen.org/privilege/externalstorage</privilege>
<privilege>http://tizen.org/privilege/content.write</privilege>
</privileges>
</manifest>

You may Checkout the EWebKit: WebView API References for API functions related to Cookies.
relevant terms/functions on API references:
Ewk Cookie Manager,
Ewk_Cookie_Persistent_Storage,
ewk_cookie_manager_persistent_storage_set (),
Ewk_Cookie_Accept_Policy

Thank you for the answer, but I have not been able to solve it.
I have now solved it via a workaround:
1. via Javascript I save the cookies in the LocalStorage.
2. when starting the app I read the data from the LocalStorage and create the cookies again.
That's how it works for me.

Related

How to get memory rd/wr trace for a specific function call using PIN tools

I am trying to dump mem rd/wr trace for a specific function call from my application and after researching a bit I came across a solution to do so.
But since I am very new to PIN usage, I am not sure how to pass routine names (refer to Routine(RTN rtn, VOID *v)) from application to pin tools so that the right callback function gets trigerred. Can someone please help?
As of now If I run the given pin tools, my trace.out is empty because "!isROI" is always set to false.
#include <stdio.h>
#include "pin.H"
#include <string>
const CHAR * ROI_BEGIN = "__parsec_roi_begin";
const CHAR * ROI_END = "__parsec_roi_end";
FILE * trace;
bool isROI = false;
// Print a memory read record
VOID RecordMemRead(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,R,%p,%s\n", ip, addr, rtn);
}
// Print a memory write record
VOID RecordMemWrite(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,W,%p,%s\n", ip, addr, rtn);
}
// Set ROI flag
VOID StartROI()
{
isROI = true;
}
// Set ROI flag
VOID StopROI()
{
isROI = false;
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
// Instruments memory accesses using a predicated call, i.e.
// the instrumentation is called iff the instruction will actually be executed.
//
// On the IA-32 and Intel(R) 64 architectures conditional moves and REP
// prefixed instructions appear as predicated instructions in Pin.
UINT32 memOperands = INS_MemoryOperandCount(ins);
// Iterate over each memory operand of the instruction.
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
// Get routine name if valid
const CHAR * name = "invalid";
if(RTN_Valid(INS_Rtn(ins)))
{
name = RTN_Name(INS_Rtn(ins)).c_str();
}
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
}
}
// Pin calls this function every time a new rtn is executed
VOID Routine(RTN rtn, VOID *v)
{
// Get routine name
const CHAR * name = RTN_Name(rtn).c_str();
if(strcmp(name,ROI_BEGIN) == 0) {
// Start tracing after ROI begin exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)StartROI, IARG_END);
RTN_Close(rtn);
} else if (strcmp(name,ROI_END) == 0) {
// Stop tracing before ROI end exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)StopROI, IARG_END);
RTN_Close(rtn);
}
}
// Pin calls this function at the end
VOID Fini(INT32 code, VOID *v)
{
fclose(trace);
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
PIN_ERROR( "This Pintool prints a trace of memory addresses\n"
+ KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char *argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
// Usage
if (PIN_Init(argc, argv)) return Usage();
// Open trace file and write header
trace = fopen("roitrace.csv", "w");
fprintf(trace,"pc,rw,addr,rtn\n");
// Add instrument functions
RTN_AddInstrumentFunction(Routine, 0);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Never returns
PIN_StartProgram();
return 0;
}

IOS development how to solve the capture signal handler by covering problems

I want to make both the existing singal handler in the project and my own singal handler co-exist so that both can properly capture singal.
typedef void (*signalHandler)(int signo, siginfo_t *info, void *context);
static signalHandler previousSignalHandler = NULL;
void signalExceptionHandler(int signal, siginfo_t* info, void* context)
{
NSMutableString *mstr = [[NSMutableString alloc] init];
[mstr appendString:#"Stack:\n"];
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i <frames; ++i) {
[mstr appendFormat:#"%s\n", strs[i]];
}
BYLOGI(#"%#",mstr);
[BYCrashCacheHelper saveAppCrashDataWithCrashInfo:mstr];
if (previousSignalHandler) {
previousSignalHandler(signal, info, context);
}
}
void signalRegister(int signal)
{
struct sigaction action;
action.sa_sigaction = signalExceptionHandler;
action.sa_flags = SA_NODEFER | SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(signal, &action, 0);
}
void judgePreviousSignalHandlerAndSignalRegister(int signal)
{
struct sigaction old_action;
sigaction(signal, NULL, &old_action);
if (old_action.sa_flags & SA_SIGINFO) {
previousSignalHandler = old_action.sa_sigaction;
}
signalRegister(signal);
}
void installSignalHandler(void)
{
judgePreviousSignalHandlerAndSignalRegister(SIGHUP);
judgePreviousSignalHandlerAndSignalRegister(SIGINT);
judgePreviousSignalHandlerAndSignalRegister(SIGQUIT);
judgePreviousSignalHandlerAndSignalRegister(SIGABRT);
judgePreviousSignalHandlerAndSignalRegister(SIGILL);
judgePreviousSignalHandlerAndSignalRegister(SIGSEGV);
judgePreviousSignalHandlerAndSignalRegister(SIGFPE);
judgePreviousSignalHandlerAndSignalRegister(SIGBUS);
judgePreviousSignalHandlerAndSignalRegister(SIGPIPE);
}
I found the above method in some blogs, but after debugging, I found that it can not get to the previous handler.
Excuse me, how to get the previous handler correctly, and avoid the handler coverage problem?

How to reset / restart a Urho3D application scene to its initial state?

I want to reset my scene to its original state when the user presses a key, to facilitate testing or allow users to restart the game.
More precisely, how to organize my code, and what do I have to put into:
void HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData) {
using namespace KeyDown;
int key = eventData[P_KEY].GetInt();
if (key == KEY_R) {
// TODO
}
}
to restart the application?
Here is a full runnable example, where I try to create a pong-like game:
#include <iostream>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/Object.h>
#include <Urho3D/Engine/Application.h>
#include <Urho3D/Engine/Engine.h>
#include <Urho3D/Engine/EngineDefs.h>
#include <Urho3D/Graphics/Camera.h>
#include <Urho3D/Graphics/DebugRenderer.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/Graphics/Octree.h>
#include <Urho3D/Graphics/Renderer.h>
#include <Urho3D/IO/File.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/MemoryBuffer.h>
#include <Urho3D/Input/Input.h>
#include <Urho3D/Input/InputEvents.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Scene/Node.h>
#include <Urho3D/Scene/Scene.h>
#include <Urho3D/Scene/SceneEvents.h>
#include <Urho3D/UI/Font.h>
#include <Urho3D/UI/Text.h>
#include <Urho3D/UI/UI.h>
#include <Urho3D/Urho2D/CollisionBox2D.h>
#include <Urho3D/Urho2D/CollisionCircle2D.h>
#include <Urho3D/Urho2D/ConstraintPrismatic2D.h>
#include <Urho3D/Urho2D/PhysicsEvents2D.h>
#include <Urho3D/Urho2D/PhysicsWorld2D.h>
#include <Urho3D/Urho2D/RigidBody2D.h>
using namespace Urho3D;
class Main : public Application {
URHO3D_OBJECT(Main, Application);
public:
Main(Context* context) : Application(context) {}
virtual void Setup() override {
engineParameters_[EP_FULL_SCREEN] = false;
engineParameters_[EP_WINDOW_TITLE] = __FILE__;
engineParameters_[EP_WINDOW_HEIGHT] = 512;
engineParameters_[EP_WINDOW_WIDTH] = 512;
}
void Start() {
auto windowHeight = this->windowWidth;
auto wallLength = this->windowWidth;
auto wallWidth = this->windowWidth / 20.0f;
auto ballRadius = this->windowWidth / 20.0f;
auto ballRestitution = 1.0f;
auto playerLength = windowHeight / 4.0f;
this->score = 0;
this->steps = 0;
this->input = this->GetSubsystem<Input>();
// Events
SubscribeToEvent(E_KEYDOWN, URHO3D_HANDLER(Main, HandleKeyDown));
SubscribeToEvent(E_PHYSICSBEGINCONTACT2D, URHO3D_HANDLER(Main, HandlePhysicsBeginContact2D));
SubscribeToEvent(E_POSTRENDERUPDATE, URHO3D_HANDLER(Main, HandlePostRenderUpdate));
SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(Main, HandleUpdate));
// Scene
this->scene = new Scene(this->context_);
this->scene->CreateComponent<Octree>();
this->scene->CreateComponent<DebugRenderer>();
this->scene->CreateComponent<PhysicsWorld2D>();
auto physicsWorld = scene->GetComponent<PhysicsWorld2D>();
physicsWorld->SetGravity(Vector2(0.0f, 0.0f));
this->physicsWorld = this->scene->GetComponent<PhysicsWorld2D>();
// Graphics
auto cameraNode = this->scene->CreateChild("Camera");
cameraNode->SetPosition(Vector3(windowWidth / 2.0f, windowHeight / 2.0f, -1.0f));
auto camera = cameraNode->CreateComponent<Camera>();
camera->SetOrthographic(true);
camera->SetOrthoSize(windowWidth);
auto renderer = GetSubsystem<Renderer>();
SharedPtr<Viewport> viewport(new Viewport(context_, this->scene, cameraNode->GetComponent<Camera>()));
renderer->SetViewport(0, viewport);
// Score
ResourceCache *cache = this->GetSubsystem<ResourceCache>();
auto ui = this->GetSubsystem<UI>();
this->text = ui->GetRoot()->CreateChild<Text>();
this->text->SetText(std::to_string(this->score).c_str());
this->text->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
this->text->SetHorizontalAlignment(HA_CENTER);
this->text->SetVerticalAlignment(VA_CENTER);
Node *wallNode;
RigidBody2D *wallBody;
{
wallNode = this->scene->CreateChild("BottomWall");
wallNode->SetPosition(Vector3(this->windowWidth / 2.0, wallWidth / 2.0f, 0.0f));
wallBody = wallNode->CreateComponent<RigidBody2D>();
auto box = wallNode->CreateComponent<CollisionBox2D>();
box->SetSize(Vector2(wallLength, wallWidth));
box->SetRestitution(0.0);
}
{
auto node = wallNode->Clone();
node->SetName("TopWall");
node->SetPosition(Vector3(this->windowWidth / 2.0f, windowHeight - (wallWidth / 2.0f), 0.0f));
}
{
auto& node = this->rightWallNode;
node = wallNode->Clone();
node->SetName("RightWall");
node->SetRotation(Quaternion(0.0f, 0.0f, 90.0f));
node->SetPosition(Vector3(this->windowWidth - (wallWidth / 2.0f), windowHeight / 2.0f, 0.0f));
}
{
auto& node = this->leftWallNode;
node = wallNode->Clone();
node->SetName("LeftWall");
node->SetRotation(Quaternion(0.0f, 0.0f, 90.0f));
node->SetPosition(Vector3(-wallWidth / 2.0f, windowHeight / 2.0f, 0.0f));
}
{
auto& node = this->playerNode;
node = wallNode->Clone();
node->SetName("Player");
node->SetRotation(Quaternion(0.0f, 0.0f, 90.0f));
node->SetPosition(Vector3(wallWidth / 2.0f, windowHeight / 2.0f, 0.0f));
auto body = node->GetComponent<RigidBody2D>();
body->SetBodyType(BT_DYNAMIC);
body->SetFixedRotation(true);
body->SetLinearDamping(4.0);
//auto constraint = node->CreateComponent<ConstraintPrismatic2D>();
//constraint->SetOtherBody(wallBody);
//constraint->SetAxis(Vector2(0.0f, 1.0f));
auto shape = node->GetComponent<CollisionBox2D>();
shape->SetDensity(this->playerDensity);
shape->SetFriction(1.0f);
shape->SetRestitution(0.0);
shape->SetSize(Vector2(playerLength, wallWidth));
}
{
this->ballNode = this->scene->CreateChild("Ball");
this->ballNode->SetPosition(Vector3(this->windowWidth / 4.0f, windowHeight / 2.0f, 0.0f));
auto body = this->ballNode->CreateComponent<RigidBody2D>();
body->SetBodyType(BT_DYNAMIC);
body->SetLinearVelocity(Vector2(2 * this->windowWidth, -windowHeight / 2.0f));
auto shape = this->ballNode->CreateComponent<CollisionCircle2D>();
shape->SetDensity(1.0f);
shape->SetFriction(1.0f);
shape->SetRadius(ballRadius);
shape->SetRestitution(ballRestitution);
}
}
void Stop() {}
private:
SharedPtr<Scene> scene;
Node *ballNode, *playerNode, *leftWallNode, *rightWallNode;
Text *text;
Input *input;
PhysicsWorld2D *physicsWorld;
uint64_t score, steps;
/// Larger means that the controls will react faster.
static constexpr float playerDensity = 10.0f;
static constexpr float windowWidth = 1.0f;
void HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData) {
using namespace KeyDown;
int key = eventData[P_KEY].GetInt();
if (key == KEY_ESCAPE) {
engine_->Exit();
} else if (key == KEY_R) {
this->Start();
}
}
void HandlePhysicsBeginContact2D(StringHash eventType, VariantMap& eventData) {
using namespace PhysicsBeginContact2D;
auto nodea = static_cast<Node*>(eventData[P_NODEA].GetPtr());
auto nodeb = static_cast<Node*>(eventData[P_NODEB].GetPtr());
Node *otherNode;
bool isBall = false;
if (nodea == this->ballNode) {
otherNode = nodeb;
isBall = true;
}
if (nodeb == this->ballNode) {
otherNode = nodea;
isBall = true;
}
if (otherNode == this->rightWallNode) {
this->score++;
} else if (otherNode == this->leftWallNode) {
this->score = 0;
}
this->text->SetText(std::to_string(this->score).c_str());
File saveFile(this->context_, GetSubsystem<FileSystem>()->GetProgramDir() + String(this->steps) + ".xml", FILE_WRITE);
this->scene->SaveXML(saveFile);
}
void HandleUpdate(StringHash eventType, VariantMap& eventData) {
using namespace Update;
auto timeStep = eventData[P_TIMESTEP].GetFloat();
auto impuseMagnitude = this->windowWidth * this->playerDensity * timeStep * 10.0;
auto body = this->playerNode->GetComponent<RigidBody2D>();
if (this->input->GetKeyDown(KEY_DOWN)) {
body->ApplyForceToCenter(Vector2(0.0f, -impuseMagnitude), true);
} else if (this->input->GetKeyDown(KEY_UP)) {
body->ApplyForceToCenter(Vector2(0.0f, impuseMagnitude), true);
} else if (this->input->GetKeyDown(KEY_RIGHT)) {
body->ApplyForceToCenter(Vector2(impuseMagnitude, 0.0f), true);
} else if (input->GetKeyDown(KEY_LEFT)) {
body->ApplyForceToCenter(Vector2(-impuseMagnitude, 0.0f), true);
}
this->steps++;
}
void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData) {
this->physicsWorld->DrawDebugGeometry();
}
};
URHO3D_DEFINE_APPLICATION_MAIN(Main);
In my naive attempt, I've tried to simply call this->Start() from the callback, but it did not clear up everything: the UI still shows the old score, in addition to a new score superposed to it.
GitHub upstream: https://github.com/cirosantilli/Urho3D-cheat/blob/e58a643ccf855d659ee292aeb513e191e0c5bb95/pong.cpp
Also asked at: https://discourse.urho3d.io/t/how-to-reset-restart-a-urho3d-application-scene-to-its-initial-state/3831
To empty a Scene, call Scene::Clear() on it. This removes all the Scene's Components and child Nodes. After this you can call a function that rebuilds the scene.
Alternately you could create a custom reset event that all Objects that require some form of resetting subscribe their HandleReset methods to. To restart the game you would then call SendEvent(E_RESET).
Based on #Frode's answer, I've reorganized my code along:
void Start() override {
if (this->scene) {
this->scene->Clear();
} else {
this->scene = new Scene(this->context_);
// Setup text.
this->text = this->GetSubsystem<UI>()->GetRoot()->CreateChild<Text>();
// Other things that only need to be done once.
}
// Application state.
this->score = 0;
// Scene setup
{
this->scene->CreateComponent<Octree>();
this->scene->CreateChild("BottomWall");
}
}
and then:
void HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData) {
using namespace KeyDown;
int key = eventData[P_KEY].GetInt();
if (key == KEY_R) {
this->Start();
}
}
I was kind of hoping that there would be a Clear() like method for the whole application (in particular, the UI does not get reset with Scene->Clear, but this solution is acceptable to me.
Updated working code at: https://github.com/cirosantilli/Urho3D-cheat/blob/cda28fea8028f4c7a5ebb7c4d7bace4a7d78d8e8/pong.cpp

Create C timer in macOS

I would like to know how to create a timer in macOS / iOS. In linux you can create it using timer_create() function of time.h class but in macOS / iOS this function don't exist.
Something like NSTimer (objective-c) but in C.
Thanks
After updating the link for Grand Central Dispatch timers (Apple's page) on a previous answer, I created some example code for two timers. It should be noted that this works on FreeBSD and MacOS, but not Linux (without GCD support). The example creates two event timers, one that fires 0.2sec and one that fires 0.5sec over a total of 20 times. There is a 1 second delay that exists before execution starts. The sleep() functions are not used.
#include <dispatch/dispatch.h>
#include <stdio.h>
#include <stdlib.h>
int i = 0;
dispatch_queue_t queue;
dispatch_source_t timer1;
dispatch_source_t timer2;
void sigtrap(int sig)
{
dispatch_source_cancel(timer1);
dispatch_source_cancel(timer2);
printf("CTRL-C received, exiting program\n");
exit(EXIT_SUCCESS);
}
void vector1(dispatch_source_t timer)
{
printf("a: %d\n", i);
i++;
if (i >= 20)
{
dispatch_source_cancel(timer);
}
}
void vector2(dispatch_source_t timer)
{
printf("b: %d\n", i);
i++;
if (i >= 20) //at 20 count cancel the
{
dispatch_source_cancel(timer);
}
}
int main(int argc, const char* argv[]) {
signal(SIGINT, &sigtrap); //catch the cntl-c
queue = dispatch_queue_create("timerQueue", 0);
// Create dispatch timer source
timer1 = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
timer2 = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// Set block for dispatch source when catched events
dispatch_source_set_event_handler(timer1, ^{vector1(timer1);});
dispatch_source_set_event_handler(timer2, ^{vector2(timer2);});
// Set block for dispatch source when cancel source
dispatch_source_set_cancel_handler(timer1, ^{
dispatch_release(timer1);
dispatch_release(queue);
printf("end\n");
exit(0);
});
dispatch_source_set_cancel_handler(timer2, ^{
dispatch_release(timer2);
dispatch_release(queue);
printf("end\n");
exit(0);
});
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC); // after 1 sec
// Set timer
dispatch_source_set_timer(timer1, start, NSEC_PER_SEC / 5, 0); // 0.2 sec
dispatch_source_set_timer(timer2, start, NSEC_PER_SEC / 2, 0); // 0.5 sec
printf("start\n");
dispatch_resume(timer1);
dispatch_resume(timer2);
while(1)
{
;;
}
return 0;
}
You can use pthreads for macOS, with some combination of sleep and time
typedef struct Timer {
void (*fn)(void);
bool (*timer_delegate)(pthread_t, unsigned int, unsigned int);
unsigned int seconds;
} Timer;
void* timer_run(void *t) {
unsigned int start_time = time(NULL);
while(1) {
Timer tmr = *((Timer *) t);
bool should_kill_thread = tmr.timer_delegate(pthread_self(), start_time, time(NULL));
if (should_kill_thread) pthread_cancel(pthread_self());
tmr.fn();
sleep(tmr.seconds);
}
}
bool should_kill_thread(pthread_t t, unsigned int start_time, unsigned int utime_new) {
printf("the start time was %d and the new time is %d \n", start_time, utime_new);
if (utime_new - start_time >= 9) {
return true;
}
return false;
}
void hello_world() {
printf("%s\n", "Hello, World!");
}
int main(int argc, char const *argv[])
{
pthread_t t1;
Timer args;
args.fn = &hello_world;
args.timer_delegate = should_kill_thread;
args.seconds = 1; // call every 1 second
int id = pthread_create(&t1, NULL, timer_run, &args);
if (id) {
printf("ERROR; return code from pthread_create() is %d\n", id);
exit(EXIT_FAILURE);
}
pthread_join(t1, NULL); // blocks main thread
printf("%s\n", "DONE"); // never reached until t1 is killed
return 0;
}
Expanding on b_degnan's answer above (click that one), we had to (partially) support Linux code using timers, so we wrote a time.h that we could use directly.
#include <sys/stdtypes.h>
#include <stdbool.h>
#include <mach/boolean.h>
#include <sys/errno.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
#if !defined(MAC_OS_X_VERSION_10_12) || \
(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12)
typedef int clockid_t;
#endif
struct itimerspec {
struct timespec it_interval; /* timer period */
struct timespec it_value; /* timer expiration */
};
struct sigevent;
/* If used a lot, queue should probably be outside of this struct */
struct macos_timer {
dispatch_queue_t tim_queue;
dispatch_source_t tim_timer;
void (*tim_func)(union sigval);
void *tim_arg;
};
typedef struct macos_timer *timer_t;
static inline void
_timer_cancel(void *arg)
{
struct macos_timer *tim = (struct macos_timer *)arg;
dispatch_release(tim->tim_timer);
dispatch_release(tim->tim_queue);
tim->tim_timer = NULL;
tim->tim_queue = NULL;
free(tim);
}
static inline void
_timer_handler(void *arg)
{
struct macos_timer *tim = (struct macos_timer *)arg;
union sigval sv;
sv.sival_ptr = tim->tim_arg;
if (tim->tim_func != NULL)
tim->tim_func(sv);
}
static inline int
timer_create(clockid_t clockid, struct sigevent *sevp,
timer_t *timerid)
{
struct macos_timer *tim;
*timerid = NULL;
switch (clockid) {
case CLOCK_REALTIME:
/* What is implemented so far */
if (sevp->sigev_notify != SIGEV_THREAD) {
errno = ENOTSUP;
return (-1);
}
tim = (struct macos_timer *)
malloc(sizeof (struct macos_timer));
if (tim == NULL) {
errno = ENOMEM;
return (-1);
}
tim->tim_queue =
dispatch_queue_create("timerqueue",
0);
tim->tim_timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0, 0, tim->tim_queue);
tim->tim_func = sevp->sigev_notify_function;
tim->tim_arg = sevp->sigev_value.sival_ptr;
*timerid = tim;
/* Opting to use pure C instead of Block versions */
dispatch_set_context(tim->tim_timer, tim);
dispatch_source_set_event_handler_f(tim->tim_timer,
_timer_handler);
dispatch_source_set_cancel_handler_f(tim->tim_timer,
_timer_cancel);
return (0);
default:
break;
}
errno = EINVAL;
return (-1);
}
static inline int
timer_settime(timer_t tim, int flags,
const struct itimerspec *its, struct itimerspec *remainvalue)
{
if (tim != NULL) {
/* Both zero, is disarm */
if (its->it_value.tv_sec == 0 &&
its->it_value.tv_nsec == 0) {
/* There's a comment about suspend count in Apple docs */
dispatch_suspend(tim->tim_timer);
return (0);
}
dispatch_time_t start;
start = dispatch_time(DISPATCH_TIME_NOW,
NSEC_PER_SEC * its->it_value.tv_sec +
its->it_value.tv_nsec);
dispatch_source_set_timer(tim->tim_timer, start,
NSEC_PER_SEC * its->it_value.tv_sec +
its->it_value.tv_nsec,
0);
dispatch_resume(tim->tim_timer);
}
return (0);
}
static inline int
timer_delete(timer_t tim)
{
/* Calls _timer_cancel() */
if (tim != NULL)
dispatch_source_cancel(tim->tim_timer);
return (0);
}
Or, as a gist:
https://gist.github.com/lundman/731d0d7d09eca072cd1224adb00d9b9e
I'd be happy to receive updates, if you enhance it further with more types. It can clearly be improved, but does just enough for our needs.
Went with straight C here, since it is a port of upstream source.

A question of libevent example code: how is invoked?

I'm learning libev however the code is so hard to understand, so I choose to learn libevent first whose code is relatively clearer. But I encounter a problem when try the example (http://www.wangafu.net/~nickm/libevent-book/01_intro.html).
How is the code event_add(state->write_event, NULL) in do_read() make do_write() function invoked?
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
#include <event2/event.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAX_LINE 16384
void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);
char
rot13_char(char c)
{
return c;
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}
struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;
size_t n_written;
size_t write_upto;
struct event *read_event;
struct event *write_event;
};
struct fd_state *
alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
if (!state->read_event) {
free(state);
return NULL;
}
state->write_event =
event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
if (!state->write_event) {
event_free(state->read_event);
free(state);
return NULL;
}
state->buffer_used = state->n_written = state->write_upto = 0;
assert(state->write_event);
return state;
}
void
free_fd_state(struct fd_state *state)
{
event_free(state->read_event);
event_free(state->write_event);
free(state);
}
void
do_read(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;
char buf[1024];
int i;
ssize_t result;
while (1) {
assert(state->write_event);
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;
for (i=0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
if (buf[i] == '\n') {
assert(state->write_event);
**event_add(state->write_event, NULL);**
state->write_upto = state->buffer_used;
}
}
}
if (result == 0) {
free_fd_state(state);
} else if (result < 0) {
if (errno == EAGAIN) // XXXX use evutil macro
return;
perror("recv");
free_fd_state(state);
}
}
void
**do_write(evutil_socket_t fd, short events, void *arg)**
{
struct fd_state *state = arg;
while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN) // XXX use evutil macro
return;
free_fd_state(state);
return;
}
assert(result != 0);
state->n_written += result;
}
if (state->n_written == state->buffer_used)
state->n_written = state->write_upto = state->buffer_used = 1;
event_del(state->write_event);
}
void
do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = arg;
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) { // XXXX eagain??
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
} else {
struct fd_state *state;
evutil_make_socket_nonblocking(fd);
state = alloc_fd_state(base, fd);
assert(state); /*XXX err*/
assert(state->write_event);
event_add(state->read_event, NULL);
}
}
void
run(void)
{
evutil_socket_t listener;
struct sockaddr_in sin;
struct event_base *base;
struct event *listener_event;
base = event_base_new();
if (!base)
return; /*XXXerr*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);
#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif
if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}
if (listen(listener, 16)<0) {
perror("listen");
return;
}
listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
/*XXX check it */
event_add(listener_event, NULL);
event_base_dispatch(base);
}
int
main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);
run();
return 0;
}
I'm not sure if I'm answering the same question you asked - I understand it as:
How does calling event_add(state->write_event, NULL) in do_read() lead to do_write() being invoked?
The key to figuring this out is understanding what the do_read() function is actually doing. do_read() is a callback function associated with a socket which has data to be read: this is set up with allocate_fd_state():
struct fd_state *
alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
/*
* Allocate a new fd_state structure, which will hold our read and write events
* /
struct fd_state *state = malloc(sizeof(struct fd_state));
[...]
/*
* Initialize a read event on the given file descriptor: associate the event with
* the given base, and set up the do_read callback to be invoked whenever
* data is available to be read on the file descriptor.
* /
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
[...]
/*
* Set up another event on the same file descriptor and base, which invoked the
* do_write callback anytime the file descriptor is ready to be written to.
*/
state->write_event =
event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
[...]
return state;
}
At this point, though, neither of these events have been event_add()'ed to the event_base base. The instructions for what to do are all written out, but no one is looking at them. So how does anything get read? state->read_event is event_add()'ed to the base after an incoming connection is made. Look at do_accept():
void
do_accept(evutil_socket_t listener, short event, void *arg)
{
[ ... accept a new connection and give it a file descriptor fd ... ]
/*
* If the file descriptor is invalid, close it.
*/
if (fd < 0) { // XXXX eagain??
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
/*
* Otherwise, if the connection was successfully accepted...
*/
} else {
[ ... allocate a new fd_state structure, and make the file descriptor non-blocking ...]
/*
* Here's where the magic happens. The read_event created back in alloc_fd_state()
* is finally added to the base associated with it.
*/
event_add(state->read_event, NULL);
}
}
So right after accepting a new connection, the program tells libevent to wait until there's data available on the connection, and then run the do_read() callback. At this point, it's still impossible for do_write() to be called. It needs to be event_add()'ed. This happens in do_read():
void
do_read(evutil_socket_t fd, short events, void *arg)
{
/* Create a temporary buffer to receive some data */
char buf[1024];
while (1) {
[ ... Receive the data, copying it into buf ... ]
[ ... if there is no more data to receive, or there was an error, exit this loop... ]
[ ... else, result = number of bytes received ... ]
for (i=0; i < result; ++i) {
[ ... if there's room in the buffer, copy in the rot13() encoded
version of the received data ... ]
/*
* Boom, headshot. If we've reached the end of the incoming data
* (assumed to be a newline), then ...
*/
if (buf[i] == '\n') {
[...]
/*
* Have libevent start monitoring the write_event, which calls do_write
* as soon as the file descriptor is ready to be written to.
*/
event_add(state->write_event, NULL);
[...]
}
}
}
[...]
}
So, after reading in some data from a file descriptor, the program starts waiting until
the file descriptor is ready to be written to, and then invokes do_write(). Program
flow looks like this:
[ set up an event_base and start waiting for events ]
[ if someone tries to connect ]
[ accept the connection ]
[ ... wait until there is data to read on the connection ... ]
[ read in data from the connection until there is no more left ]
[ ....wait until the connection is ready to be written to ... ]
[ write out our rot13() encoded response ]
I hope that a) that was the correct interpretation of your question, and b) this was a helpful answer.

Resources