mirror of
https://github.com/google/pebble.git
synced 2025-06-12 20:53:13 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
72
applib-targets/emscripten/emscripten_app.c
Normal file
72
applib-targets/emscripten/emscripten_app.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "emscripten_app.h"
|
||||
#include "emscripten_graphics.h"
|
||||
#include "emscripten_resources.h"
|
||||
#include "emscripten_tick_timer_service.h"
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/ui/app_window_stack.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
__attribute__((weak)) int app_main(void) {
|
||||
Window *window = window_create();
|
||||
app_window_stack_push(window, false);
|
||||
app_event_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prv_event_loop(void) {
|
||||
Window *window = app_window_stack_get_top_window();
|
||||
if (window && window->is_render_scheduled) {
|
||||
GContext *ctx = rocky_api_graphics_get_gcontext();
|
||||
layer_render_tree(&window->layer, ctx);
|
||||
window->is_render_scheduled = false;
|
||||
EM_ASM(
|
||||
// Implemented by html-binding.js:
|
||||
if (Module.frameBufferMarkDirty) {
|
||||
Module.frameBufferMarkDirty()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void emx_app_init(void) {
|
||||
emx_graphics_init();
|
||||
emx_resources_init();
|
||||
emx_tick_timer_service_init();
|
||||
}
|
||||
|
||||
void emx_app_deinit(void) {
|
||||
emx_resources_deinit();
|
||||
}
|
||||
|
||||
void emx_app_event_loop(void) {
|
||||
emscripten_set_main_loop(prv_event_loop,
|
||||
0, /* using window.requestAnimationFrame() */
|
||||
1 /* Simulate infinite loop */);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
emx_app_init();
|
||||
app_main();
|
||||
emx_app_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
21
applib-targets/emscripten/emscripten_app.h
Normal file
21
applib-targets/emscripten/emscripten_app.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void emx_app_init(void);
|
||||
|
||||
void emx_app_event_loop(void);
|
27
applib-targets/emscripten/emscripten_clock.c
Normal file
27
applib-targets/emscripten/emscripten_clock.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "services/common/clock.h"
|
||||
|
||||
static bool s_clock_is_24h_style;
|
||||
|
||||
bool clock_is_24h_style(void) {
|
||||
return s_clock_is_24h_style;
|
||||
}
|
||||
|
||||
void clock_set_24h_style(bool is_24h_style) {
|
||||
s_clock_is_24h_style = is_24h_style;
|
||||
}
|
118
applib-targets/emscripten/emscripten_graphics.c
Normal file
118
applib-targets/emscripten/emscripten_graphics.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/gdraw_command_image.h"
|
||||
#include "applib/graphics/gdraw_command_sequence.h"
|
||||
#include "applib/graphics/gdraw_command_frame.h"
|
||||
#include "applib/graphics/gbitmap_sequence.h"
|
||||
#include "applib/graphics/text.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_util.h"
|
||||
#include "applib/rockyjs/api/rocky_api_errors.h"
|
||||
|
||||
#include "jerry-api.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
static GContext s_gcontext = {};
|
||||
// FIXME: PBL-43469 Support for changing platforms will require a dynamic framebuffer.
|
||||
static FrameBuffer s_framebuffer = {};
|
||||
static TextRenderState s_text_render_state = {};
|
||||
static UnobstructedAreaState s_unobstructed_area_state = {};
|
||||
|
||||
// FIXME: Right now, rocky only supports 1 window anyways
|
||||
static Window *s_top_window;
|
||||
|
||||
GContext *emx_graphics_get_gcontext(void) {
|
||||
return &s_gcontext;
|
||||
}
|
||||
|
||||
void *emx_graphics_get_pixels(void) {
|
||||
return s_gcontext.dest_bitmap.addr;
|
||||
}
|
||||
|
||||
TextRenderState *app_state_get_text_render_state(void) {
|
||||
return &s_text_render_state;
|
||||
}
|
||||
|
||||
Layer** app_state_get_layer_tree_stack(void) {
|
||||
static Layer *layer_tree_stack[LAYER_TREE_STACK_SIZE];
|
||||
return layer_tree_stack;
|
||||
}
|
||||
|
||||
Layer** kernel_applib_get_layer_tree_stack(void) {
|
||||
PBL_ASSERT(0, "Not expected to be called when compiling to applib-emscripten...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// FIXME: Emscripten is cannot deal with two files with the same name
|
||||
// (even if the path is different) The framebuffer.c files end up not
|
||||
// getting linked in. A longer term fix would be to rename the object
|
||||
// file in WAF
|
||||
volatile const int FrameBuffer_MaxX = DISP_COLS;
|
||||
volatile const int FrameBuffer_MaxY = DISP_ROWS;
|
||||
void framebuffer_mark_dirty_rect(FrameBuffer* f, GRect rect) {
|
||||
}
|
||||
|
||||
size_t framebuffer_get_size_bytes(FrameBuffer *f) {
|
||||
return FRAMEBUFFER_SIZE_BYTES;
|
||||
}
|
||||
|
||||
Window *app_window_stack_get_top_window(void) {
|
||||
return s_top_window;
|
||||
}
|
||||
|
||||
void app_window_stack_push(Window *window, bool animated) {
|
||||
PBL_ASSERT(!s_top_window, "Already have a window");
|
||||
s_top_window = window;
|
||||
}
|
||||
|
||||
GContext *graphics_context_get_current_context(void) {
|
||||
return &s_gcontext;
|
||||
}
|
||||
|
||||
// TODO: PBL-43467 Support a user-specified unobstructed area
|
||||
UnobstructedAreaState *app_state_get_unobstructed_area_state(void) {
|
||||
return &s_unobstructed_area_state;
|
||||
}
|
||||
|
||||
void unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area_out) {
|
||||
*area_out = state->area;
|
||||
}
|
||||
|
||||
// FIXME: PBL-43496 This should take width, height, and format to dynamically
|
||||
// allocate our framebuffer GBitmap and support changing platforms.
|
||||
void emx_graphics_init(void) {
|
||||
framebuffer_init(&s_framebuffer, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
memset(s_framebuffer.buffer, 0xff, FRAMEBUFFER_SIZE_BYTES);
|
||||
framebuffer_dirty_all(&s_framebuffer);
|
||||
graphics_context_init(&s_gcontext, &s_framebuffer, GContextInitializationMode_App);
|
||||
|
||||
s_unobstructed_area_state = (UnobstructedAreaState) {
|
||||
.area = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 144, .h = 168 },
|
||||
},
|
||||
};
|
||||
}
|
26
applib-targets/emscripten/emscripten_graphics.h
Normal file
26
applib-targets/emscripten/emscripten_graphics.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
|
||||
void emx_graphics_init(void);
|
||||
GContext *emx_graphics_get_gcontext(void);
|
||||
void *emx_graphics_get_pixes(void);
|
||||
TextRenderState *app_state_get_text_render_state(void);
|
||||
void emx_graphics_call_canvas_update_proc(void);
|
720
applib-targets/emscripten/emscripten_jerry_api.c
Normal file
720
applib-targets/emscripten/emscripten_jerry_api.c
Normal file
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "jerry-api.h"
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define TYPE_ERROR \
|
||||
jerry_create_error(JERRY_ERROR_TYPE, NULL);
|
||||
|
||||
#define TYPE_ERROR_ARG \
|
||||
jerry_create_error(JERRY_ERROR_TYPE, (const jerry_char_t *)"wrong type of argument");
|
||||
|
||||
#define TYPE_ERROR_FLAG \
|
||||
jerry_create_error(JERRY_ERROR_TYPE, (const jerry_char_t *)"argument cannot have an error flag");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Parser and Executor Function
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Note that `is_strict` is currently unsupported by emscripten
|
||||
jerry_value_t jerry_eval(const jerry_char_t *source_p, size_t source_size, bool is_strict) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
// jerry_eval() uses an indirect eval() call,
|
||||
// so the global execution context is used.
|
||||
// Also see ECMA 5.1 -- 10.4.2 Entering Eval Code.
|
||||
var indirectEval = eval;
|
||||
try {
|
||||
return __jerryRefs.ref(indirectEval(Module.Pointer_stringify($0, $1)));
|
||||
} catch (e) {
|
||||
var error_ref = __jerryRefs.ref(e);
|
||||
__jerryRefs.setError(error_ref, true);
|
||||
return error_ref;
|
||||
}
|
||||
}, source_p, source_size);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_acquire_value(jerry_value_t value) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.acquire($0);
|
||||
}, value);
|
||||
}
|
||||
|
||||
void jerry_release_value(jerry_value_t value) {
|
||||
EM_ASM_INT({
|
||||
__jerryRefs.release($0);
|
||||
}, value);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Get the global context
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
jerry_value_t jerry_get_global_object(void) {
|
||||
return ((jerry_value_t)EM_ASM_INT_V({ \
|
||||
return __jerryRefs.ref(Function('return this;')()); \
|
||||
}));
|
||||
}
|
||||
|
||||
jerry_value_t jerry_get_global_builtin(const jerry_char_t *builtin_name) {
|
||||
return ((jerry_value_t)EM_ASM_INT({ \
|
||||
return __jerryRefs.ref(Function('return this;')()[Module.Pointer_stringify($0)]); \
|
||||
}, builtin_name));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jerry Value Type Checking
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define JERRY_VALUE_HAS_TYPE(ref, typename) \
|
||||
((bool)EM_ASM_INT({ \
|
||||
return typeof __jerryRefs.get($0) === (typename); \
|
||||
}, (ref)))
|
||||
|
||||
#define JERRY_VALUE_IS_INSTANCE(ref, type) \
|
||||
((bool)EM_ASM_INT({ \
|
||||
return __jerryRefs.get($0) instanceof (type); \
|
||||
}, (ref)))
|
||||
|
||||
bool jerry_value_is_array(const jerry_value_t value) {
|
||||
return JERRY_VALUE_IS_INSTANCE(value, Array);
|
||||
}
|
||||
|
||||
bool jerry_value_is_boolean(const jerry_value_t value) {
|
||||
return JERRY_VALUE_HAS_TYPE(value, 'boolean');
|
||||
}
|
||||
|
||||
bool jerry_value_is_constructor(const jerry_value_t value) {
|
||||
return jerry_value_is_function(value);
|
||||
}
|
||||
|
||||
bool jerry_value_is_function(const jerry_value_t value) {
|
||||
return JERRY_VALUE_HAS_TYPE(value, 'function');
|
||||
}
|
||||
|
||||
bool jerry_value_is_number(const jerry_value_t value) {
|
||||
return JERRY_VALUE_HAS_TYPE(value, 'number');
|
||||
}
|
||||
|
||||
bool jerry_value_is_null(const jerry_value_t value) {
|
||||
return ((bool)EM_ASM_INT({
|
||||
return __jerryRefs.get($0) === null;
|
||||
}, value));
|
||||
}
|
||||
|
||||
bool jerry_value_is_object(const jerry_value_t value) {
|
||||
return !jerry_value_is_null(value) &&
|
||||
(JERRY_VALUE_HAS_TYPE(value, 'object') || jerry_value_is_function(value));
|
||||
}
|
||||
|
||||
bool jerry_value_is_string(const jerry_value_t value) {
|
||||
return JERRY_VALUE_HAS_TYPE(value, 'string');
|
||||
}
|
||||
|
||||
bool jerry_value_is_undefined(const jerry_value_t value) {
|
||||
return JERRY_VALUE_HAS_TYPE(value, 'undefined');
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jerry Value Getter Functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool jerry_get_boolean_value(const jerry_value_t value) {
|
||||
if (!jerry_value_is_boolean(value)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)EM_ASM_INT({
|
||||
return (__jerryRefs.get($0) === true);
|
||||
}, value);
|
||||
}
|
||||
|
||||
double jerry_get_number_value(const jerry_value_t value) {
|
||||
if (!jerry_value_is_number(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return EM_ASM_DOUBLE({
|
||||
return __jerryRefs.get($0);
|
||||
}, value);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Functions for UTF-8 encoded string values
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
jerry_size_t jerry_get_utf8_string_size(const jerry_value_t value) {
|
||||
if (!jerry_value_is_string(value)) {
|
||||
return 0;
|
||||
}
|
||||
return (jerry_size_t)EM_ASM_INT({
|
||||
return Module.lengthBytesUTF8(__jerryRefs.get($0));
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_size_t jerry_string_to_utf8_char_buffer(const jerry_value_t value,
|
||||
jerry_char_t *buffer_p,
|
||||
jerry_size_t buffer_size) {
|
||||
const jerry_size_t str_size = jerry_get_utf8_string_size(value);
|
||||
if (str_size == 0 || buffer_size < str_size || buffer_p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EM_ASM_INT({
|
||||
var str = __jerryRefs.get($0);
|
||||
// Add one onto the buffer size, since Module.stringToUTF8 adds a null
|
||||
// character at the end. This will lead to truncation if we just use
|
||||
// buffer_size. Since the actual jerry-api does not do this, we are
|
||||
// always careful to allocate space for a null character at the end.
|
||||
// Allow stringToUTF8 to write that extra null beyond the passed in
|
||||
// buffer_length.
|
||||
Module.stringToUTF8(str, $1, $2 + 1);
|
||||
}, value, buffer_p, buffer_size);
|
||||
return strlen((const char *)buffer_p);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Functions for array object values
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
uint32_t jerry_get_array_length(const jerry_value_t value) {
|
||||
if (!jerry_value_is_array(value)) {
|
||||
return 0;
|
||||
}
|
||||
return (uint32_t)EM_ASM_INT({
|
||||
return __jerryRefs.get($0).length;
|
||||
}, value);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jerry Value Creation API
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define JERRY_CREATE_VALUE(value) \
|
||||
((jerry_value_t)EM_ASM_INT_V({ \
|
||||
return __jerryRefs.ref((value)); \
|
||||
}))
|
||||
|
||||
jerry_value_t jerry_create_array(uint32_t size) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(new Array($0));
|
||||
}, size);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_boolean(bool value) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(Boolean($0));
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_error(jerry_error_t error_type, const jerry_char_t *message_p) {
|
||||
return jerry_create_error_sz(error_type, message_p, strlen((const char *)message_p));
|
||||
}
|
||||
|
||||
#define JERRY_ERROR(type, msg, sz) (jerry_value_t)(EM_ASM_INT({ \
|
||||
return __jerryRefs.ref(new (type)(Module.Pointer_stringify($0, $1))) \
|
||||
}, (msg), (sz)))
|
||||
|
||||
jerry_value_t jerry_create_error_sz(jerry_error_t error_type,
|
||||
const jerry_char_t *message_p,
|
||||
jerry_size_t message_size) {
|
||||
jerry_value_t error_ref = 0;
|
||||
switch (error_type) {
|
||||
case JERRY_ERROR_COMMON:
|
||||
error_ref = JERRY_ERROR(Error, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_EVAL:
|
||||
error_ref = JERRY_ERROR(EvalError, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_RANGE:
|
||||
error_ref = JERRY_ERROR(RangeError, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_REFERENCE:
|
||||
error_ref = JERRY_ERROR(ReferenceError, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_SYNTAX:
|
||||
error_ref = JERRY_ERROR(SyntaxError, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_TYPE:
|
||||
error_ref = JERRY_ERROR(TypeError, message_p, message_size);
|
||||
break;
|
||||
case JERRY_ERROR_URI:
|
||||
error_ref = JERRY_ERROR(URIError, message_p, message_size);
|
||||
break;
|
||||
default:
|
||||
EM_ASM_INT({
|
||||
abort('Cannot create error type: ' + $0);
|
||||
}, error_type);
|
||||
break;
|
||||
}
|
||||
jerry_value_set_error_flag(&error_ref);
|
||||
return error_ref;
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_external_function(jerry_external_handler_t handler_p) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerry_create_external_function($0);
|
||||
}, handler_p);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_number(double value) {
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref($0);
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_number_infinity(bool negative) {
|
||||
if (negative) {
|
||||
return JERRY_CREATE_VALUE(-Infinity);
|
||||
} else {
|
||||
return JERRY_CREATE_VALUE(Infinity);
|
||||
}
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_number_nan(void) {
|
||||
return JERRY_CREATE_VALUE(NaN);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_null(void) {
|
||||
return JERRY_CREATE_VALUE(null);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_object(void) {
|
||||
return JERRY_CREATE_VALUE(new Object());
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_string(const jerry_char_t *str_p) {
|
||||
if (!str_p) {
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
return jerry_create_string_utf8_sz(str_p, strlen((const char *)str_p));
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_string_sz(const jerry_char_t *str_p, jerry_size_t str_size) {
|
||||
return jerry_create_string_utf8_sz(str_p, str_size);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_string_utf8(const jerry_char_t *str_p) {
|
||||
if (!str_p) {
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
return jerry_create_string_utf8_sz(str_p, strlen((const char *)str_p));
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_string_utf8_sz(const jerry_char_t *str_p, jerry_size_t str_size) {
|
||||
if (!str_p) {
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(Module.Pointer_stringify($0, $1));
|
||||
}, str_p, str_size);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_create_undefined(void) {
|
||||
return JERRY_CREATE_VALUE(undefined);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// General API Functions of JS Objects
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool jerry_has_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) {
|
||||
if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var name = __jerryRefs.get($1);
|
||||
return (name in obj);
|
||||
}, obj_val, prop_name_val);
|
||||
}
|
||||
|
||||
bool jerry_has_own_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) {
|
||||
if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var name = __jerryRefs.get($1);
|
||||
return obj.hasOwnProperty(name);
|
||||
}, obj_val, prop_name_val);
|
||||
}
|
||||
|
||||
bool jerry_delete_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) {
|
||||
if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var name = __jerryRefs.get($1);
|
||||
try {
|
||||
return delete obj[name];
|
||||
} catch (e) {
|
||||
// In strict mode, delete throws SyntaxError if the property is an
|
||||
// own non-configurable property.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, obj_val, prop_name_val);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_get_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) {
|
||||
if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var name = __jerryRefs.get($1);
|
||||
return __jerryRefs.ref(obj[name]);
|
||||
}, obj_val, prop_name_val);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_get_property_by_index(const jerry_value_t obj_val, uint32_t index) {
|
||||
if (!jerry_value_is_object(obj_val)) {
|
||||
return TYPE_ERROR;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
return __jerryRefs.ref(obj[$1]);
|
||||
}, obj_val, index);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_set_property(const jerry_value_t obj_val,
|
||||
const jerry_value_t prop_name_val,
|
||||
const jerry_value_t value_to_set) {
|
||||
if (jerry_value_has_error_flag(value_to_set) ||
|
||||
!jerry_value_is_object(obj_val) ||
|
||||
!jerry_value_is_string(prop_name_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var name = __jerryRefs.get($1);
|
||||
var to_set = __jerryRefs.get($2);
|
||||
obj[name] = to_set;
|
||||
return __jerryRefs.ref(true);
|
||||
}, obj_val, prop_name_val, value_to_set);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_set_property_by_index(const jerry_value_t obj_val,
|
||||
uint32_t index,
|
||||
const jerry_value_t value_to_set) {
|
||||
if (jerry_value_has_error_flag(value_to_set) ||
|
||||
!jerry_value_is_object(obj_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($0);
|
||||
var to_set = __jerryRefs.get($2);
|
||||
obj[$1] = to_set;
|
||||
return __jerryRefs.ref(true);
|
||||
}, obj_val, index, value_to_set);
|
||||
}
|
||||
|
||||
void jerry_init_property_descriptor_fields(jerry_property_descriptor_t *prop_desc_p) {
|
||||
*prop_desc_p = (jerry_property_descriptor_t) {
|
||||
.value = jerry_create_undefined(),
|
||||
.getter = jerry_create_undefined(),
|
||||
.setter = jerry_create_undefined(),
|
||||
};
|
||||
}
|
||||
|
||||
jerry_value_t jerry_define_own_property(const jerry_value_t obj_val,
|
||||
const jerry_value_t prop_name_val,
|
||||
const jerry_property_descriptor_t *pdp) {
|
||||
if (!jerry_value_is_object(obj_val) && !jerry_value_is_string(obj_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
if ((pdp->is_writable_defined || pdp->is_value_defined)
|
||||
&& (pdp->is_get_defined || pdp->is_set_defined)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
if (pdp->is_get_defined && !jerry_value_is_function(pdp->getter)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
if (pdp->is_set_defined && !jerry_value_is_function(pdp->setter)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
|
||||
return (jerry_value_t)(EM_ASM_INT({
|
||||
var obj = __jerryRefs.get($12 /* obj_val */);
|
||||
var name = __jerryRefs.get($13 /* prop_name_val */);
|
||||
var desc = {};
|
||||
if ($0 /* is_value_defined */) {
|
||||
desc.value = __jerryRefs.get($9);
|
||||
}
|
||||
if ($1 /* is_get_defined */) {
|
||||
desc.get = __jerryRefs.get($10);
|
||||
}
|
||||
if ($2 /* is_set_defined */) {
|
||||
desc.set = __jerryRefs.get($11);
|
||||
}
|
||||
if ($3 /* is_writable_defined */) {
|
||||
desc.writable = Boolean($4 /* is_writable */);
|
||||
}
|
||||
if ($5 /* is_enumerable_defined */) {
|
||||
desc.enumerable = Boolean($6 /* is_enumerable */);
|
||||
}
|
||||
if ($7 /* is_configurable */) {
|
||||
desc.configurable = Boolean($8 /* is_configurable */);
|
||||
}
|
||||
|
||||
Object.defineProperty(obj, name, desc);
|
||||
return __jerryRefs.ref(Boolean(true));
|
||||
}, pdp->is_value_defined, /* $0 */
|
||||
pdp->is_get_defined, /* $1 */
|
||||
pdp->is_set_defined, /* $2 */
|
||||
pdp->is_writable_defined, /* $3 */
|
||||
pdp->is_writable, /* $4 */
|
||||
pdp->is_enumerable_defined, /* $5 */
|
||||
pdp->is_enumerable, /* $6 */
|
||||
pdp->is_configurable_defined, /* $7 */
|
||||
pdp->is_configurable, /* $8 */
|
||||
pdp->value, /* $9 */
|
||||
pdp->getter, /* $10 */
|
||||
pdp->setter, /* $11 */
|
||||
obj_val, /* $12 */
|
||||
prop_name_val /* $13 */
|
||||
));
|
||||
}
|
||||
|
||||
jerry_value_t emscripten_call_jerry_function(jerry_external_handler_t func_obj_p,
|
||||
const jerry_value_t func_obj_val,
|
||||
const jerry_value_t this_val,
|
||||
const jerry_value_t args_p[],
|
||||
jerry_size_t args_count) {
|
||||
return (func_obj_p)(func_obj_val, this_val, args_p, args_count);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_call_function(const jerry_value_t func_obj_val,
|
||||
const jerry_value_t this_val,
|
||||
const jerry_value_t args_p[],
|
||||
jerry_size_t args_count) {
|
||||
if (!jerry_value_is_function(func_obj_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var func_obj = __jerryRefs.get($0);
|
||||
var this_val = __jerryRefs.get($1);
|
||||
var args = [];
|
||||
for (var i = 0; i < $3; ++i) {
|
||||
args.push(__jerryRefs.get(getValue($2 + i*4, 'i32')));
|
||||
}
|
||||
try {
|
||||
var rv = func_obj.apply(this_val, args);
|
||||
} catch (e) {
|
||||
var error_ref = __jerryRefs.ref(e);
|
||||
__jerryRefs.setError(error_ref, true);
|
||||
return error_ref;
|
||||
}
|
||||
return __jerryRefs.ref(rv);
|
||||
}, func_obj_val, this_val, args_p, args_count);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_construct_object(const jerry_value_t func_obj_val,
|
||||
const jerry_value_t args_p[],
|
||||
jerry_size_t args_count) {
|
||||
if (!jerry_value_is_constructor(func_obj_val)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var func_obj = __jerryRefs.get($0);
|
||||
var args = [];
|
||||
for (var i = 0; i < $2; ++i) {
|
||||
args.push(__jerryRefs.get(getValue($1 + i*4, 'i32')));
|
||||
}
|
||||
// Call the constructor with new object as `this`
|
||||
var bindArgs = [null].concat(args);
|
||||
var boundConstructor = func_obj.bind.apply(func_obj, bindArgs);
|
||||
var rv = new boundConstructor();
|
||||
return __jerryRefs.ref(rv);
|
||||
}, func_obj_val, args_p, args_count);
|
||||
}
|
||||
|
||||
jerry_size_t jerry_string_to_char_buffer(const jerry_value_t value,
|
||||
jerry_char_t *buffer_p,
|
||||
jerry_size_t buffer_size) {
|
||||
return jerry_string_to_utf8_char_buffer(value, buffer_p, buffer_size);
|
||||
}
|
||||
|
||||
jerry_size_t jerry_object_to_string_to_utf8_char_buffer(const jerry_value_t object,
|
||||
jerry_char_t *buffer_p,
|
||||
jerry_size_t buffer_size) {
|
||||
jerry_value_t str_ref = (jerry_value_t)EM_ASM_INT({
|
||||
var str = __jerryRefs.ref(String(__jerryRefs.get($0)));
|
||||
return str;
|
||||
}, object);
|
||||
jerry_size_t len = jerry_string_to_utf8_char_buffer(str_ref, buffer_p, buffer_size);
|
||||
jerry_release_value(str_ref);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// FIXME: PBL-43551 Propery CESU-8 => UTF-8 conversion.
|
||||
jerry_size_t jerry_object_to_string_to_char_buffer(const jerry_value_t object,
|
||||
jerry_char_t *buffer_p,
|
||||
jerry_size_t buffer_size) {
|
||||
return jerry_object_to_string_to_utf8_char_buffer(object,
|
||||
buffer_p,
|
||||
buffer_size);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_get_object_keys(const jerry_value_t value) {
|
||||
if (!jerry_value_is_object(value)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(Object.keys(__jerryRefs.get($0)));
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_get_prototype(const jerry_value_t value) {
|
||||
if (!jerry_value_is_object(value)) {
|
||||
return TYPE_ERROR_ARG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(__jerryRefs.get($0).prototype);
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_set_prototype(const jerry_value_t obj_val, const jerry_value_t proto_obj_val) {
|
||||
return 0; // FIXME: Not sure what to do here
|
||||
}
|
||||
|
||||
bool jerry_get_object_native_handle(const jerry_value_t obj_val, uintptr_t *out_handle_p) {
|
||||
return EM_ASM_INT({
|
||||
var ptr = __jerryRefs.getNativeHandle($0);
|
||||
if (ptr === undefined) {
|
||||
return false;
|
||||
}
|
||||
Module.setValue($1, ptr, '*');
|
||||
return true;
|
||||
}, obj_val, out_handle_p);
|
||||
}
|
||||
|
||||
void jerry_set_object_native_handle(const jerry_value_t obj_val, uintptr_t handle_p,
|
||||
jerry_object_free_callback_t freecb_p) {
|
||||
EM_ASM_INT({
|
||||
__jerryRefs.setNativeHandle($0, $1, $2);
|
||||
}, obj_val, handle_p, freecb_p);
|
||||
}
|
||||
|
||||
void emscripten_call_jerry_object_free_callback(jerry_object_free_callback_t freecb_p,
|
||||
uintptr_t handle_p) {
|
||||
if (freecb_p) {
|
||||
freecb_p(handle_p);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Error flag manipulation functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The error flag is stored alongside the value in __jerryRefs.
|
||||
// This allows for us to keep a valid value, like jerryscript does, and be able
|
||||
// to add / remove a flag specifying whether there was an error or not.
|
||||
|
||||
bool jerry_value_has_error_flag(const jerry_value_t value) {
|
||||
return (bool)(EM_ASM_INT({
|
||||
return __jerryRefs.getError($0);
|
||||
}, value));
|
||||
}
|
||||
|
||||
void jerry_value_clear_error_flag(jerry_value_t *value_p) {
|
||||
EM_ASM_INT({
|
||||
return __jerryRefs.setError($0, false);
|
||||
}, *value_p);
|
||||
}
|
||||
|
||||
void jerry_value_set_error_flag(jerry_value_t *value_p) {
|
||||
EM_ASM_INT({
|
||||
return __jerryRefs.setError($0, true);
|
||||
}, *value_p);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Converters of `jerry_value_t`
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool jerry_value_to_boolean(const jerry_value_t value) {
|
||||
if (jerry_value_has_error_flag(value)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)EM_ASM_INT({
|
||||
return Boolean(__jerryRefs.get($0));
|
||||
}, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
jerry_value_t jerry_value_to_number(const jerry_value_t value) {
|
||||
if (jerry_value_has_error_flag(value)) {
|
||||
return TYPE_ERROR_FLAG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(Number(__jerryRefs.get($0)));
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_value_to_object(const jerry_value_t value) {
|
||||
if (jerry_value_has_error_flag(value)) {
|
||||
return TYPE_ERROR_FLAG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(new Object(__jerryRefs.get($0)));
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_value_to_primitive(const jerry_value_t value) {
|
||||
if (jerry_value_has_error_flag(value)) {
|
||||
return TYPE_ERROR_FLAG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
var val = __jerryRefs.get($0);
|
||||
var rv;
|
||||
if ((typeof val === 'object' && val != null)
|
||||
|| (typeof val === 'function')) {
|
||||
rv = val.valueOf(); // unbox
|
||||
} else {
|
||||
rv = val; // already a primitive
|
||||
}
|
||||
return __jerryRefs.ref(rv);
|
||||
}, value);
|
||||
}
|
||||
|
||||
jerry_value_t jerry_value_to_string(const jerry_value_t value) {
|
||||
if (jerry_value_has_error_flag(value)) {
|
||||
return TYPE_ERROR_FLAG;
|
||||
}
|
||||
return (jerry_value_t)EM_ASM_INT({
|
||||
return __jerryRefs.ref(String(__jerryRefs.get($0)));
|
||||
}, value);
|
||||
}
|
||||
|
||||
int jerry_obj_refcount(jerry_value_t o) {
|
||||
return EM_ASM_INT({
|
||||
try {
|
||||
return __jerryRefs.getRefCount($0);
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}, o);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void jerry_init(jerry_init_flag_t flags) {
|
||||
EM_ASM(__jerryRefs.reset());
|
||||
}
|
||||
|
||||
void jerry_cleanup(void) {
|
||||
}
|
21
applib-targets/emscripten/emscripten_jerry_port.c
Normal file
21
applib-targets/emscripten/emscripten_jerry_port.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
void rocky_runtime_context_init(void) {
|
||||
}
|
||||
|
||||
void rocky_runtime_context_deinit(void) {
|
||||
}
|
249
applib-targets/emscripten/emscripten_resources.c
Normal file
249
applib-targets/emscripten/emscripten_resources.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "emscripten_resources.h"
|
||||
|
||||
#include "resource/resource_storage_impl.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
} Resource;
|
||||
|
||||
#define MANIFEST_SIZE (sizeof(ResourceManifest))
|
||||
#define TABLE_ENTRY_SIZE (sizeof(ResTableEntry))
|
||||
#define MAX_RESOURCES_FOR_SYSTEM_STORE 512
|
||||
#define SYSTEM_STORE_METADATA_BYTES \
|
||||
(MANIFEST_SIZE + MAX_RESOURCES_FOR_SYSTEM_STORE * TABLE_ENTRY_SIZE)
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Custom Resources
|
||||
//
|
||||
// Custom resources in Rocky.js are implemented with a set of callbacks
|
||||
// on the javascript side which implement resource APIs
|
||||
//
|
||||
// We store those callbacks in an array and return a resource ID which
|
||||
// can then be used as if it was a valid resource
|
||||
//
|
||||
// Under the hood, we just lookup & call the initially provided callbacks
|
||||
|
||||
typedef struct {
|
||||
uint32_t resource_id;
|
||||
ResourceReadCb read;
|
||||
ResourceGetSizeCb get_size;
|
||||
} EmxCustomResource;
|
||||
|
||||
typedef struct {
|
||||
EmxCustomResource *custom_resources;
|
||||
uint32_t last_id;
|
||||
int array_size;
|
||||
int next_index;
|
||||
} CustomResList;
|
||||
|
||||
static CustomResList s_custom_res_list = {0};
|
||||
|
||||
static int prv_custom_resource_get_index(uint32_t resource_id) {
|
||||
int index;
|
||||
for (index = 0; index < s_custom_res_list.next_index; ++index) {
|
||||
if (s_custom_res_list.custom_resources[index].resource_id == resource_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static EmxCustomResource *prv_custom_resource_get(uint32_t resource_id) {
|
||||
int index = prv_custom_resource_get_index(resource_id);
|
||||
if (index < 0) {
|
||||
return NULL;
|
||||
} else {
|
||||
return &s_custom_res_list.custom_resources[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prv_custom_resource_add(EmxCustomResource *res) {
|
||||
if (s_custom_res_list.array_size <= s_custom_res_list.next_index) {
|
||||
// grow the list
|
||||
int new_size = (s_custom_res_list.array_size + 1) * 2;
|
||||
s_custom_res_list.custom_resources = realloc(s_custom_res_list.custom_resources, new_size);
|
||||
s_custom_res_list.array_size = new_size;
|
||||
}
|
||||
|
||||
s_custom_res_list.custom_resources[s_custom_res_list.next_index] = *res;
|
||||
++s_custom_res_list.next_index;
|
||||
}
|
||||
|
||||
static void prv_custom_resource_remove(uint32_t resource_id) {
|
||||
int index = prv_custom_resource_get_index(resource_id);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
--s_custom_res_list.next_index;
|
||||
if (s_custom_res_list.next_index == index) {
|
||||
// we had the last resource, we're done
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(&s_custom_res_list.custom_resources[index],
|
||||
&s_custom_res_list.custom_resources[index + 1],
|
||||
(s_custom_res_list.next_index - index) * sizeof(EmxCustomResource));
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// System Resources
|
||||
//
|
||||
// In this case, we shove the pbpack in an emscripten "file" (baked into the resulting JS
|
||||
// and reimplement system resource APIs using standard C file I/O
|
||||
|
||||
static FILE *s_resource_file = NULL;
|
||||
|
||||
static uint32_t prv_read(uint32_t offset, void *data, size_t num_bytes) {
|
||||
if (!s_resource_file || fseek(s_resource_file, offset, SEEK_SET)) {
|
||||
printf("%s: Couldn't seek to %d\n", __FILE__, offset);
|
||||
return 0;
|
||||
}
|
||||
return fread(data, 1, num_bytes, s_resource_file);
|
||||
}
|
||||
|
||||
static ResourceManifest prv_get_manifest(void) {
|
||||
ResourceManifest manifest = {};
|
||||
prv_read(0, &manifest, sizeof(ResourceManifest));
|
||||
return manifest;
|
||||
}
|
||||
|
||||
static bool prv_get_table_entry(ResTableEntry *entry, uint32_t index) {
|
||||
uint32_t addr = sizeof(ResourceManifest) + index * sizeof(ResTableEntry);
|
||||
return prv_read(addr, entry, sizeof(ResTableEntry));
|
||||
}
|
||||
|
||||
static bool prv_get_resource(uint32_t resource_id, Resource *res) {
|
||||
*res = (Resource){
|
||||
.length = 0,
|
||||
.offset = 0,
|
||||
};
|
||||
|
||||
ResourceManifest manifest = prv_get_manifest();
|
||||
|
||||
if (resource_id > manifest.num_resources) {
|
||||
printf("%s: resource id %d > %d is out of range\n", __FILE__,
|
||||
resource_id,
|
||||
manifest.num_resources);
|
||||
return false;
|
||||
}
|
||||
|
||||
ResTableEntry entry;
|
||||
if (!prv_get_table_entry(&entry, resource_id - 1)) {
|
||||
printf("%s: Failed to read table entry for %d\n", __FILE__, resource_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((entry.resource_id != resource_id) ||
|
||||
(entry.length == 0)) {
|
||||
// empty resource
|
||||
printf("%s: Invalid resourcel for %d\n", __FILE__, resource_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
res->offset = SYSTEM_STORE_METADATA_BYTES + entry.offset;
|
||||
res->length = entry.length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// API
|
||||
|
||||
size_t emx_resources_read(ResAppNum app_num,
|
||||
uint32_t resource_id,
|
||||
uint32_t offset,
|
||||
uint8_t *buf,
|
||||
size_t num_bytes) {
|
||||
if (offset > INT_MAX || num_bytes > INT_MAX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EmxCustomResource *custom_res = NULL;
|
||||
if (app_num != SYSTEM_APP && (custom_res = prv_custom_resource_get(resource_id))) {
|
||||
return custom_res->read(offset, buf, num_bytes);
|
||||
}
|
||||
|
||||
Resource resource = {};
|
||||
if (!prv_get_resource(resource_id, &resource)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset + num_bytes > resource.length) {
|
||||
if (offset >= resource.length) {
|
||||
// Can't recover from trying to read from beyond the resource. Read nothing.
|
||||
printf("%s: Reading past the end of the resource!\n", __FILE__);
|
||||
return 0;
|
||||
}
|
||||
num_bytes = resource.length - offset;
|
||||
}
|
||||
|
||||
return prv_read(offset + resource.offset, buf, num_bytes);
|
||||
}
|
||||
|
||||
size_t emx_resources_get_size(ResAppNum app_num, uint32_t resource_id) {
|
||||
EmxCustomResource *custom_res = NULL;
|
||||
if (app_num != SYSTEM_APP && (custom_res = prv_custom_resource_get(resource_id))) {
|
||||
return custom_res->get_size();
|
||||
}
|
||||
|
||||
Resource resource = {};
|
||||
if (!prv_get_resource(resource_id, &resource)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return resource.length;
|
||||
}
|
||||
|
||||
bool emx_resources_init(void) {
|
||||
s_resource_file = fopen("system_resources.pbpack", "r");
|
||||
|
||||
if (!s_resource_file) {
|
||||
printf("Error: Failed to open resources file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void emx_resources_deinit(void) {
|
||||
fclose(s_resource_file);
|
||||
}
|
||||
|
||||
uint32_t emx_resources_register_custom(ResourceReadCb read_cb, ResourceGetSizeCb get_size_cb) {
|
||||
EmxCustomResource custom_res = {
|
||||
.resource_id = ++s_custom_res_list.last_id,
|
||||
.read = read_cb,
|
||||
.get_size = get_size_cb,
|
||||
};
|
||||
prv_custom_resource_add(&custom_res);
|
||||
|
||||
return custom_res.resource_id;
|
||||
}
|
||||
|
||||
void emx_resources_remove_custom(uint32_t resource_id) {
|
||||
prv_custom_resource_remove(resource_id);
|
||||
}
|
38
applib-targets/emscripten/emscripten_resources.h
Normal file
38
applib-targets/emscripten/emscripten_resources.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "resource/resource.h"
|
||||
|
||||
// transformed to int to avoid surpises between C->JS
|
||||
typedef int (*ResourceReadCb)(int offset, uint8_t *buf, int num_bytes);
|
||||
typedef int (*ResourceGetSizeCb)(void);
|
||||
|
||||
bool emx_resources_init(void);
|
||||
void emx_resources_deinit(void);
|
||||
size_t emx_resources_get_size(ResAppNum app_num, uint32_t resource_id);
|
||||
size_t emx_resources_read(ResAppNum app_num,
|
||||
uint32_t resource_id,
|
||||
uint32_t offset,
|
||||
uint8_t *buf,
|
||||
size_t num_bytes);
|
||||
uint32_t emx_resources_register_custom(ResourceReadCb read_cb, ResourceGetSizeCb get_size_cb);
|
||||
void emx_resources_remove_custom(uint32_t resource_id);
|
108
applib-targets/emscripten/emscripten_tick_timer_service.c
Normal file
108
applib-targets/emscripten/emscripten_tick_timer_service.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "applib/app_logging.h"
|
||||
#include "applib/tick_timer_service_private.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
time_t time(time_t *time);
|
||||
|
||||
static TickTimerServiceState s_state;
|
||||
|
||||
static void prv_schedule_next_update(void);
|
||||
|
||||
static void prv_do_update(void *data) {
|
||||
if (!s_state.handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The data pointer value is used to pass a boolean value directly:
|
||||
const bool is_update_due_to_time_change = (uintptr_t)data;
|
||||
|
||||
struct tm currtime;
|
||||
time_t t = time(NULL);
|
||||
localtime_r(&t, &currtime);
|
||||
|
||||
TimeUnits units_changed;
|
||||
if (is_update_due_to_time_change) {
|
||||
units_changed = (SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | YEAR_UNIT);
|
||||
} else {
|
||||
prv_schedule_next_update();
|
||||
units_changed = 0;
|
||||
if (!s_state.first_tick) {
|
||||
if (s_state.last_time.tm_sec != currtime.tm_sec) {
|
||||
units_changed |= SECOND_UNIT;
|
||||
}
|
||||
if (s_state.last_time.tm_min != currtime.tm_min) {
|
||||
units_changed |= MINUTE_UNIT;
|
||||
}
|
||||
if (s_state.last_time.tm_hour != currtime.tm_hour) {
|
||||
units_changed |= HOUR_UNIT;
|
||||
}
|
||||
if (s_state.last_time.tm_mday != currtime.tm_mday) {
|
||||
units_changed |= DAY_UNIT;
|
||||
}
|
||||
if (s_state.last_time.tm_mon != currtime.tm_mon) {
|
||||
units_changed |= MONTH_UNIT;
|
||||
}
|
||||
if (s_state.last_time.tm_year != currtime.tm_year) {
|
||||
units_changed |= YEAR_UNIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_state.last_time = currtime;
|
||||
s_state.first_tick = false;
|
||||
|
||||
if ((s_state.tick_units & units_changed) || (units_changed == 0)) {
|
||||
s_state.handler(&currtime, units_changed);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_schedule_next_update(void) {
|
||||
// Schedule this to fire again at the top of the next second
|
||||
const int ms_into_s = EM_ASM_INT_V({
|
||||
return Date.now().getMilliseconds;
|
||||
});
|
||||
const double wait_ms = 1000 - ms_into_s;
|
||||
const bool is_update_due_to_time_change = false;
|
||||
emscripten_async_call(prv_do_update, (void *)(uintptr_t)is_update_due_to_time_change, wait_ms);
|
||||
}
|
||||
|
||||
void tick_timer_service_handle_time_change(void) {
|
||||
const bool is_update_due_to_time_change = true;
|
||||
prv_do_update((void *)(uintptr_t)is_update_due_to_time_change);
|
||||
}
|
||||
|
||||
void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) {
|
||||
const bool first = (s_state.handler == NULL);
|
||||
s_state = (TickTimerServiceState) {
|
||||
.handler = handler,
|
||||
.tick_units = tick_units,
|
||||
.first_tick = true,
|
||||
};
|
||||
|
||||
if (first && handler != NULL) {
|
||||
prv_schedule_next_update();
|
||||
}
|
||||
}
|
||||
|
||||
void emx_tick_timer_service_init(void) {
|
||||
s_state = (TickTimerServiceState){};
|
||||
}
|
19
applib-targets/emscripten/emscripten_tick_timer_service.h
Normal file
19
applib-targets/emscripten/emscripten_tick_timer_service.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void emx_tick_timer_service_init(void);
|
62
applib-targets/emscripten/exported_functions.json
Normal file
62
applib-targets/emscripten/exported_functions.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
["_main",
|
||||
"_emx_graphics_get_pixels",
|
||||
"_graphics_context_set_stroke_width",
|
||||
"_graphics_context_set_antialiased",
|
||||
"_emx_resources_read",
|
||||
"_emx_resources_get_size",
|
||||
"_app_state_get_graphics_context",
|
||||
"_gdraw_command_image_create_with_resource",
|
||||
"_gdraw_command_image_get_command_list",
|
||||
"_gdraw_command_list_get_command",
|
||||
"_gdraw_command_list_get_num_commands",
|
||||
"_gdraw_command_sequence_create_with_resource",
|
||||
"_gdraw_command_sequence_get_frame_by_elapsed",
|
||||
"_gdraw_command_sequence_get_total_duration",
|
||||
"_gdraw_command_get_type",
|
||||
"_gdraw_command_get_stroke_width",
|
||||
"_gdraw_command_set_stroke_width",
|
||||
"_gdraw_command_set_path_open",
|
||||
"_gdraw_command_get_path_open",
|
||||
"_gdraw_command_set_hidden",
|
||||
"_gdraw_command_get_hidden",
|
||||
"_gdraw_command_get_num_points",
|
||||
"_gdraw_command_set_radius",
|
||||
"_gdraw_command_get_radius",
|
||||
"_gdraw_command_sequence_get_play_count",
|
||||
"_gdraw_command_sequence_set_play_count",
|
||||
"_gdraw_command_sequence_get_total_duration",
|
||||
"_gdraw_command_sequence_get_num_frames",
|
||||
"_gdraw_command_sequence_get_frame_by_elapsed",
|
||||
"_gdraw_command_sequence_get_frame_by_index",
|
||||
"_gdraw_command_frame_set_duration",
|
||||
"_gdraw_command_frame_get_duration",
|
||||
"_gdraw_command_frame_get_command_list",
|
||||
"_fonts_get_system_font",
|
||||
"_fonts_load_custom_font",
|
||||
"_fonts_unload_custom_font",
|
||||
"_gbitmap_create_with_data",
|
||||
"_gbitmap_create_from_png_data",
|
||||
"_gbitmap_destroy",
|
||||
"_gbitmap_get_format",
|
||||
"_graphics_context_set_compositing_mode",
|
||||
"_emx_resources_register_custom",
|
||||
"_emx_resources_remove_custom",
|
||||
"_gpath_draw_filled",
|
||||
"_gpath_draw_outline",
|
||||
"_gpath_draw_outline_open",
|
||||
"_graphics_draw_text",
|
||||
"_gbitmap_sequence_create_with_resource",
|
||||
"_gbitmap_sequence_destroy",
|
||||
"_gbitmap_sequence_get_current_frame_delay_ms",
|
||||
"_gbitmap_sequence_get_total_num_frames",
|
||||
"_gbitmap_sequence_get_current_frame_idx",
|
||||
"_gbitmap_sequence_get_play_count",
|
||||
"_gbitmap_sequence_set_play_count",
|
||||
|
||||
"_rocky_api_watchface_init",
|
||||
"_jerry_call_function",
|
||||
"_emscripten_call_jerry_function",
|
||||
"_emscripten_call_jerry_object_free_callback",
|
||||
"_tick_timer_service_handle_time_change",
|
||||
"_clock_set_24h_style"
|
||||
]
|
83
applib-targets/emscripten/html-binding.js
Normal file
83
applib-targets/emscripten/html-binding.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
|
||||
Copyright © 2015-2016 Pebble Technology Corp.,
|
||||
All Rights Reserved. http://pebble.github.io/rockyjs/LICENSE
|
||||
|
||||
This describes functionality to bind the Rocky Simulator to an HTML canvas
|
||||
element. This file is included into the Emscripten output using --post-js,
|
||||
as such it will end up in body of the RockySimulator(options) constructor.
|
||||
|
||||
*/
|
||||
|
||||
if (typeof(Module) === 'undefined') {
|
||||
var Module = {};
|
||||
}
|
||||
|
||||
Module.bindCanvas = function(canvas) {
|
||||
// in a future version, these values should adapt automatically
|
||||
// also, we want the ability to create framebuffers of larger sizes
|
||||
var canvasW = canvas.width;
|
||||
var canvasH = canvas.height;
|
||||
var framebufferW = 144;
|
||||
var framebufferH = 168;
|
||||
|
||||
// scale gives us the ability to do a nearest-neighbor scaling
|
||||
var scale = options.scale ||
|
||||
Math.min(canvasW / framebufferW, canvasH / framebufferH);
|
||||
|
||||
// pixel access to read (framebuffer) and write to (canvas)
|
||||
var canvasCtx = canvas.getContext('2d');
|
||||
var canvasPixelData = canvasCtx.createImageData(canvasW, canvasH);
|
||||
var canvasPixels = canvasPixelData.data;
|
||||
var framebufferPixelPTR = Module.ccall(
|
||||
'emx_graphics_get_pixels', 'number', []
|
||||
);
|
||||
|
||||
var isRenderRequested = false;
|
||||
var copyFrameBufferToCanvas = function(timestamp) {
|
||||
console.log('copying pixels...');
|
||||
isRenderRequested = false;
|
||||
var framebufferPixels = new Uint8Array(Module.HEAPU8.buffer,
|
||||
framebufferPixelPTR,
|
||||
framebufferW * framebufferH);
|
||||
// renders current state of the framebuffer to the bound canvas
|
||||
// respecting the passed scale
|
||||
for (var y = 0; y < canvasH; y++) {
|
||||
var pebbleY = (y / scale) >> 0;
|
||||
if (pebbleY >= framebufferH) {
|
||||
break;
|
||||
}
|
||||
for (var x = 0; x < canvasW; x++) {
|
||||
var pebbleX = (x / scale) >> 0;
|
||||
if (pebbleX >= framebufferW) {
|
||||
break;
|
||||
}
|
||||
var pebbleOffset = pebbleY * framebufferW + pebbleX;
|
||||
var in_values = framebufferPixels[pebbleOffset];
|
||||
var r = ((in_values >> 4) & 0x3) * 85;
|
||||
var g = ((in_values >> 2) & 0x3) * 85;
|
||||
var b = ((in_values >> 0) & 0x3) * 85;
|
||||
var canvasOffset = (y * canvasW + x) * 4;
|
||||
canvasPixels[canvasOffset + 0] = r;
|
||||
canvasPixels[canvasOffset + 1] = g;
|
||||
canvasPixels[canvasOffset + 2] = b;
|
||||
canvasPixels[canvasOffset + 3] = 255;
|
||||
}
|
||||
}
|
||||
canvasCtx.putImageData(canvasPixelData, 0, 0);
|
||||
};
|
||||
|
||||
Module.frameBufferMarkDirty = function() {
|
||||
if (isRenderRequested) {
|
||||
return;
|
||||
}
|
||||
console.log('request render');
|
||||
isRenderRequested = true;
|
||||
window.requestAnimationFrame(copyFrameBufferToCanvas);
|
||||
}
|
||||
};
|
||||
|
||||
// Apply `options` from the RockySimulator(options) constructor:
|
||||
if (typeof(options) !== 'undefined' && options.canvas) {
|
||||
Module.bindCanvas(options.canvas);
|
||||
}
|
116
applib-targets/emscripten/html/css/style.css
Normal file
116
applib-targets/emscripten/html/css/style.css
Normal file
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Bowlby+One+SC|Roboto:400,700,400italic);
|
||||
|
||||
body {
|
||||
font: 16px/1.5 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
body > .container:first-child > .row:first-child > div > p:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
h1 + blockquote {
|
||||
border: 0 none;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
margin: 0 auto 3em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-family: "Bowlby One SC";
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
margin: 2em 0 1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1:before, h1:after, h2:before, h2:after {
|
||||
background-color: #ddd;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
h1:before, h2:before {
|
||||
right: 0.5em;
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
h1:after, h2:after {
|
||||
left: 0.5em;
|
||||
margin-right: -50%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0 0.5em;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#githubForkLink {
|
||||
background: url(../img/forkBanner.png) no-repeat top right;
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 149px;
|
||||
height: 149px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
/* Hide the text. */
|
||||
text-indent: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.table-compatibility>tbody>tr.standard {
|
||||
background-color: #d9edf7;
|
||||
}
|
||||
|
||||
table.table-compatibility>tbody>tr.implemented {
|
||||
background-color: #dff0db;
|
||||
}
|
||||
|
||||
table.table-compatibility>tbody>tr.partial {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
table.table-compatibility>tbody>tr.planned {
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
table.table-compatibility>tbody>tr.not-planned {
|
||||
background-color: #f2dede
|
||||
}
|
94
applib-targets/emscripten/html/index.html
Normal file
94
applib-targets/emscripten/html/index.html
Normal file
|
@ -0,0 +1,94 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright 2024 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>Simple Rocky Simulator</title>
|
||||
|
||||
<link rel="stylesheet" href="css/bootstrap.css"/>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script type="text/javascript" src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script type="text/javascript" src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript" src="rocky.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>[Simple Rocky Simulator]</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<canvas id="pebble" class="rocky" width="432" height="504"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h3>Controls</h3>
|
||||
<p><form id="controls-form">
|
||||
<label for="timestamp-input">Set Time (Epoch timestamp)</label>
|
||||
<input id="timestamp-input" type="text" />
|
||||
<label for="timezone-offset-input">Timezone Offset (<span id="timezone-offset-span"></span>)</label>
|
||||
<input id="timezone-offset-input" type="range" min="-720" max="840" step="15" value="0" />
|
||||
<label>24h Style</label>
|
||||
<input id="24h-style-input" type="checkbox" />
|
||||
</form></p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// Create a new simulator and bind it to the canvas:
|
||||
var rockySimulator = new RockySimulator({
|
||||
canvas: document.getElementById("pebble")
|
||||
});
|
||||
|
||||
// in the future, we will replace the singleton
|
||||
// `rocky` as well as the namespace `Rocky`, e.g.
|
||||
// `Rocky.tween` and `Rocky.WatchfaceHelper` with modules
|
||||
var rocky = _rocky;
|
||||
|
||||
function getQueryVariable(variable) {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split('&');
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split('=');
|
||||
if (decodeURIComponent(pair[0]) == variable) {
|
||||
return decodeURIComponent(pair[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load app source from 'src' query param (URL / data URI):
|
||||
var src = getQueryVariable('src');
|
||||
// If 'src' is set to empty string, don't load anything (for testing):
|
||||
if (src !== '') {
|
||||
src = src || 'js/tictoc.js';
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
document.getElementsByTagName('html')[0].appendChild(script);
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="js/controls.js"></script>
|
||||
</body>
|
||||
</html>
|
62
applib-targets/emscripten/html/js/controls.js
vendored
Normal file
62
applib-targets/emscripten/html/js/controls.js
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var form = document.getElementById("controls-form");
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
var timestampInput = document.getElementById("timestamp-input");
|
||||
timestampInput.addEventListener('change', function(e) {
|
||||
rockySimulator.setTime(Number(e.target.value) * 1000);
|
||||
});
|
||||
|
||||
var timezoneSlider = document.getElementById("timezone-offset-input");
|
||||
var timezoneSpan = document.getElementById("timezone-offset-span");
|
||||
var timezoneSliderOnChange = function() {
|
||||
var offset = timezoneSlider.value;
|
||||
rockySimulator.setTimezoneOffset(offset);
|
||||
var sign;
|
||||
if (offset > 0) {
|
||||
sign = '+';
|
||||
} else if (offset < 0) {
|
||||
sign = '-';
|
||||
} else {
|
||||
sign = '';
|
||||
}
|
||||
var offsetMinutes = (offset % 60);
|
||||
var absOffsetHours = Math.abs((offset - offsetMinutes) / 60);
|
||||
var gmtText = 'GMT ' + sign + absOffsetHours;
|
||||
if (offsetMinutes) {
|
||||
var absOffsetMinutes = Math.abs(offsetMinutes);
|
||||
gmtText += ':' + (absOffsetMinutes < 10 ? '0' : '') + absOffsetMinutes;
|
||||
}
|
||||
timezoneSpan.innerText = gmtText;
|
||||
};
|
||||
timezoneSlider.addEventListener('change', timezoneSliderOnChange);
|
||||
timezoneSlider.addEventListener('input', timezoneSliderOnChange);
|
||||
|
||||
// After loading the page, set the slider to the local TZ:
|
||||
var localTimezoneOffset = new Date().getTimezoneOffset();
|
||||
timezoneSlider.value = localTimezoneOffset;
|
||||
timezoneSliderOnChange();
|
||||
|
||||
var time24hStyle = document.getElementById("24h-style-input");
|
||||
time24hStyle.addEventListener('change', function(e) {
|
||||
rockySimulator.set24hStyle(e.target.checked);
|
||||
});
|
||||
});
|
91
applib-targets/emscripten/html/js/tictoc.js
Normal file
91
applib-targets/emscripten/html/js/tictoc.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var WatchfaceHelper = function(date) {
|
||||
function clockwiseRad(fraction) {
|
||||
// TODO: figure out if this is actually correct orientation for Canvas APIs
|
||||
return (1.5 - fraction) * 2 * Math.PI;
|
||||
}
|
||||
|
||||
date = date || new Date();
|
||||
var secondFraction = date.getSeconds() / 60;
|
||||
var minuteFraction = (date.getMinutes()) / 60;
|
||||
var hourFraction = (date.getHours() % 12 + minuteFraction) / 12;
|
||||
this.secondAngle = clockwiseRad(secondFraction);
|
||||
this.minuteAngle = clockwiseRad(minuteFraction);
|
||||
this.hourAngle = clockwiseRad(hourFraction);
|
||||
};
|
||||
|
||||
// book keeping so that we can easily animate the two hands for the watchface
|
||||
// .scale/.angle are updated by tween/event handler (see below)
|
||||
var renderState = {
|
||||
minute: {style: 'white', scale: 0.80, angle: 0},
|
||||
hour: {style: 'red', scale: 0.51, angle: 0}
|
||||
};
|
||||
|
||||
// helper function for the draw function (see below)
|
||||
// extracted as a standalone function to satisfy common believe in efficient JS code
|
||||
// TODO: verify that this has actually any effect on byte code level
|
||||
var drawHand = function(handState, ctx, cx, cy, maxRadius) {
|
||||
ctx.lineWidth = 8;
|
||||
ctx.strokeStyle = handState.style;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, cy);
|
||||
ctx.lineTo(cx + Math.sin(handState.angle) * handState.scale * maxRadius,
|
||||
cy + Math.cos(handState.angle) * handState.scale * maxRadius);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
// the 'draw' event is being emitted after each call to rocky.requestDraw() but
|
||||
// at most once for each screen update, even if .requestDraw() is called frequently
|
||||
// the 'draw' event might also fire at other meaningful times (e.g. upon launch)
|
||||
rocky.on('draw', function(drawEvent) {
|
||||
var ctx = drawEvent.context;
|
||||
var w = ctx.canvas.unobstructedWidth;
|
||||
var h = ctx.canvas.unobstructedHeight;
|
||||
|
||||
// clear canvas on each render
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
|
||||
|
||||
// center point
|
||||
var cx = w / 2;
|
||||
var cy = h / 2;
|
||||
var maxRadius = Math.min(w, h - 2 * 10) / 2;
|
||||
drawHand(renderState.minute, ctx, cx, cy, maxRadius);
|
||||
drawHand(renderState.hour, ctx, cx, cy, maxRadius);
|
||||
|
||||
// Draw a 12 o clock indicator
|
||||
drawHand({style: 'white', scale: 0, angle: 0}, ctx, cx, 8, 0);
|
||||
// overdraw center so that no white part of the minute hand is visible
|
||||
drawHand({style: 'red', scale: 0, angle: 0}, ctx, cx, cy, 0);
|
||||
});
|
||||
|
||||
// listener is called on each full minute and once immediately after registration
|
||||
rocky.on('minutechange', function(e) {
|
||||
// WatchfaceHelper will later be extracted as npm module
|
||||
var wfh = new WatchfaceHelper(e.date);
|
||||
renderState.minute.angle = wfh.minuteAngle;
|
||||
renderState.hour.angle = wfh.hourAngle;
|
||||
rocky.requestDraw();
|
||||
});
|
||||
|
||||
rocky.on('secondchange', function(e) {
|
||||
console.log(e.date.toLocaleTimeString() + ' ' + e.date.toLocaleDateString() +
|
||||
' ' + e.date.toLocaleString());
|
||||
});
|
||||
|
||||
console.log('TicToc launched');
|
1
applib-targets/emscripten/integration_tests/.gitignore
vendored
Normal file
1
applib-targets/emscripten/integration_tests/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
6
applib-targets/emscripten/integration_tests/wscript
Normal file
6
applib-targets/emscripten/integration_tests/wscript
Normal file
|
@ -0,0 +1,6 @@
|
|||
def configure(conf):
|
||||
pass
|
||||
|
||||
|
||||
def build(bld):
|
||||
pass
|
192
applib-targets/emscripten/jerry_api.js
Normal file
192
applib-targets/emscripten/jerry_api.js
Normal file
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Polyfill Number.isNaN
|
||||
if (Number.isNaN === undefined) {
|
||||
Number.isNaN = Number.isNaN || function(value) {
|
||||
return value !== value;
|
||||
}
|
||||
}
|
||||
|
||||
var __jerryRefs = {
|
||||
|
||||
// Jerryscript values (jerry_value_t) are integers which contain information
|
||||
// about some javascript value that has been internally created.
|
||||
// Create _objMap which will allow us to store and retrieve javascript values
|
||||
// from a jerry_value_t, and perform refcounts on values that we still need an
|
||||
// internal reference to, and avoid them from being garbage collected.
|
||||
|
||||
_objMap : {},
|
||||
_nextObjectRef : 1,
|
||||
_findValue : function(value) {
|
||||
if (Number.isNaN(value)) {
|
||||
// Special case to find NaN
|
||||
for (var jerry_val in this._objMap) {
|
||||
if (Number.isNaN(this._objMap[jerry_val].value)) {
|
||||
return jerry_val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var jerry_val in this._objMap) {
|
||||
if (this._objMap[jerry_val].value === value) {
|
||||
return jerry_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
_getEntry : function(jerry_value) {
|
||||
var entry = this._objMap[jerry_value];
|
||||
if (!entry) {
|
||||
throw new Error('Entry at ' + jerry_value + ' does not exist');
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._objMap = {};
|
||||
this._nextObjectRef = 1;
|
||||
},
|
||||
|
||||
// Given a jerry value, return the stored javascript value.
|
||||
get : function(jerry_value) {
|
||||
return this._getEntry(jerry_value).value;
|
||||
},
|
||||
|
||||
// Given a javascript value, return a jerry value that refers to it.
|
||||
// If the value already exists in the map, increment its refcount and return
|
||||
// the jerry value.
|
||||
// Otherwise, create a new entry and return the jerry value.
|
||||
ref : function(value) {
|
||||
var jerry_value = this._findValue(value);
|
||||
if (jerry_value) {
|
||||
this._getEntry(jerry_value).refCount++;
|
||||
return jerry_value;
|
||||
}
|
||||
|
||||
jerry_value = this._nextObjectRef++;
|
||||
this._objMap[jerry_value] = {
|
||||
refCount : 1,
|
||||
value : value,
|
||||
error : false,
|
||||
};
|
||||
// console.log('created entry ' + jerry_value + ' for ' + value + ' at ' + stackTrace());
|
||||
return jerry_value;
|
||||
},
|
||||
|
||||
getRefCount : function(jerry_value) {
|
||||
return this._getEntry(jerry_value).refCount;
|
||||
},
|
||||
|
||||
// Increase the reference count of the given jerry value
|
||||
acquire : function(jerry_value) {
|
||||
this._getEntry(jerry_value).refCount++;
|
||||
return jerry_value;
|
||||
},
|
||||
|
||||
// Decrease the reference count of the given jerry value and delete it if
|
||||
// there are no more internal references.
|
||||
release : function(ref) {
|
||||
var entry = this._getEntry(ref);
|
||||
entry.refCount--;
|
||||
|
||||
if (entry.refCount <= 0) {
|
||||
if (entry.freeCallbackPtr) {
|
||||
Module.ccall(
|
||||
'emscripten_call_jerry_object_free_callback',
|
||||
null,
|
||||
['number', 'number'],
|
||||
[entry.freeCallbackPtr, entry.nativeHandlePtr]);
|
||||
}
|
||||
// console.log('deleting ' + ref + ' at ' + stackTrace());
|
||||
delete this._objMap[ref];
|
||||
}
|
||||
},
|
||||
|
||||
setError : function(ref, state) {
|
||||
this._getEntry(ref).error = state;
|
||||
},
|
||||
|
||||
getError : function(ref) {
|
||||
return this._getEntry(ref).error;
|
||||
},
|
||||
|
||||
setNativeHandle : function(jerryValue, nativeHandlePtr, freeCallbackPtr) {
|
||||
var entry = this._getEntry(jerryValue);
|
||||
entry.nativeHandlePtr = nativeHandlePtr;
|
||||
entry.freeCallbackPtr = freeCallbackPtr;
|
||||
},
|
||||
|
||||
getNativeHandle : function(jerryValue) {
|
||||
return this._getEntry(jerryValue).nativeHandlePtr;
|
||||
}
|
||||
};
|
||||
|
||||
function __jerry_create_external_function(function_ptr) {
|
||||
var f = function() {
|
||||
var nativeHandlerArgs = [
|
||||
function_ptr, /* the function pointer for us to call */
|
||||
__jerryRefs.ref(f), /* ref to the actual js function */
|
||||
__jerryRefs.ref(this) /* our this object */
|
||||
];
|
||||
|
||||
var numArgs = arguments.length;
|
||||
var jsRefs = [];
|
||||
for (var i = 0; i < numArgs; i++) {
|
||||
jsRefs.push(__jerryRefs.ref(arguments[i]));
|
||||
}
|
||||
|
||||
// Arg 4 is a uint32 array of jerry_value_t arguments
|
||||
var jsArgs = Module._malloc(numArgs * 4);
|
||||
for (var i = 0; i < numArgs; i++) {
|
||||
Module.setValue(jsArgs + i*4, jsRefs[i], 'i32');
|
||||
}
|
||||
nativeHandlerArgs.push(jsArgs);
|
||||
nativeHandlerArgs.push(numArgs);
|
||||
|
||||
// this is just the classy Emscripten calling. function_ptr is a C-pointer here
|
||||
// and we know the signature of the C function as it needs to follow
|
||||
var result_ref = Module.ccall('emscripten_call_jerry_function',
|
||||
'number',
|
||||
['number', 'number', 'number', 'number', 'number'],
|
||||
nativeHandlerArgs);
|
||||
|
||||
// Free and release all js args
|
||||
Module._free(jsArgs);
|
||||
while (jsRefs.length > 0) {
|
||||
__jerryRefs.release(jsRefs.pop());
|
||||
}
|
||||
|
||||
// decrease refcount of native handler arguments
|
||||
__jerryRefs.release(nativeHandlerArgs[1]); // jsFunctionRef
|
||||
__jerryRefs.release(nativeHandlerArgs[2]); // our this object
|
||||
|
||||
// delete native handler arguments
|
||||
nativeHandlerArgs.length = 0;
|
||||
|
||||
var result_val = __jerryRefs.get(result_ref);
|
||||
var has_error = __jerryRefs.getError(result_ref);
|
||||
__jerryRefs.release(result_ref);
|
||||
|
||||
if (has_error) {
|
||||
throw result_val;
|
||||
}
|
||||
|
||||
return result_val;
|
||||
};
|
||||
|
||||
return __jerryRefs.ref(f);
|
||||
}
|
224
applib-targets/emscripten/shims.c
Normal file
224
applib-targets/emscripten/shims.c
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/heap.h"
|
||||
#include "util/size.h"
|
||||
#include "applib/app_logging.h"
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "applib/fonts/fonts_private.h"
|
||||
#include "applib/graphics/text_resources.h"
|
||||
#include "applib/rockyjs/api/rocky_api.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "font_resource_keys.auto.h"
|
||||
#include "font_resource_table.auto.h"
|
||||
|
||||
#include "emscripten_app.h"
|
||||
#include "emscripten_graphics.h"
|
||||
#include "emscripten_resources.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
#define NUM_SYSTEM_FONTS (ARRAY_LENGTH(s_font_resource_keys))
|
||||
|
||||
void *task_malloc(size_t bytes) {
|
||||
return malloc(bytes);
|
||||
}
|
||||
|
||||
void *task_zalloc(size_t bytes) {
|
||||
void *ptr = malloc(bytes);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, bytes);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *task_zalloc_check(size_t bytes) {
|
||||
void *ptr = task_zalloc(bytes);
|
||||
if (!ptr) {
|
||||
wtf();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *task_realloc(void *ptr, size_t bytes) {
|
||||
return realloc(ptr, bytes);
|
||||
}
|
||||
|
||||
void task_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void app_log(uint8_t log_level, const char* src_filename,
|
||||
int src_line_number, const char* fmt, ...) {
|
||||
printf("%s:%d", src_filename, src_line_number);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
GContext* app_state_get_graphics_context() {
|
||||
return emx_graphics_get_gcontext();
|
||||
}
|
||||
|
||||
bool app_state_get_text_perimeter_debugging_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Heap *app_state_get_heap(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBitmap* app_state_legacy2_get_2bit_framebuffer(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool heap_is_allocated(Heap* const heap, void* ptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void passert_failed(const char* filename, int line_number, const char* message, ...) {
|
||||
APP_LOG(LOG_LEVEL_ERROR, "ASSERTION FAILED: %s:%d", filename, line_number);
|
||||
EM_ASM_INT_V({ abort(); });
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
void passert_failed_no_message(const char* filename, int line_number) {
|
||||
passert_failed(filename, line_number, NULL);
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
void passert_failed_hashed_no_message(void) {
|
||||
EM_ASM_INT_V({ abort(); });
|
||||
while (1);
|
||||
}
|
||||
|
||||
void passert_failed_hashed(uint32_t packed_loghash, ...) {
|
||||
EM_ASM_INT_V({ abort(); });
|
||||
while (1);
|
||||
}
|
||||
|
||||
void pbl_log(uint8_t log_level, const char* src_filename,
|
||||
int src_line_number, const char* fmt, ...) {
|
||||
printf("%s:%d ", src_filename, src_line_number);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool process_manager_compiled_with_legacy2_sdk(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResAppNum sys_get_current_resource_num(void) {
|
||||
return 1; // 0 is system
|
||||
}
|
||||
|
||||
size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes,
|
||||
uint8_t *buffer, size_t num_bytes) {
|
||||
return emx_resources_read(app_num, id, start_bytes, buffer, num_bytes);
|
||||
}
|
||||
|
||||
size_t sys_resource_size(ResAppNum app_num, uint32_t handle) {
|
||||
return emx_resources_get_size(app_num, handle);
|
||||
}
|
||||
|
||||
GFont sys_font_get_system_font(const char *font_key) {
|
||||
static FontInfo s_system_fonts_info_table[NUM_SYSTEM_FONTS + 1] = {};
|
||||
|
||||
for (int i = 0; i < (int) NUM_SYSTEM_FONTS; ++i) {
|
||||
if (0 == strcmp(font_key, s_font_resource_keys[i].key_name)) {
|
||||
FontInfo *fontinfo = &s_system_fonts_info_table[i];
|
||||
uint32_t resource = s_font_resource_keys[i].resource_id;
|
||||
// if the font has not been initialized yet
|
||||
if (!fontinfo->loaded) {
|
||||
if (!text_resources_init_font(SYSTEM_APP,
|
||||
resource, 0, &s_system_fonts_info_table[i])) {
|
||||
// Can't initialize the font for some reason
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return &s_system_fonts_info_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find the given font, invalid key.
|
||||
return (GFont)NULL;
|
||||
}
|
||||
|
||||
void sys_font_reload_font(FontInfo *fontinfo) {
|
||||
text_resources_init_font(fontinfo->base.app_num, fontinfo->base.resource_id,
|
||||
fontinfo->extension.resource_id, fontinfo);
|
||||
}
|
||||
|
||||
uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id) {
|
||||
return resource_id;
|
||||
}
|
||||
|
||||
bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceCallbackHandle resource_watch(ResAppNum app_num,
|
||||
uint32_t resource_id,
|
||||
ResourceChangedCallback callback,
|
||||
void *data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void applib_resource_munmap_or_free(void *bytes) {
|
||||
free(bytes);
|
||||
}
|
||||
|
||||
void *applib_resource_mmap_or_load(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t offset, size_t num_bytes, bool used_aligned) {
|
||||
if (num_bytes == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *result = malloc(num_bytes + (used_aligned ? 7 :0));
|
||||
if (!result
|
||||
|| sys_resource_load_range(app_num, resource_id, offset, result, num_bytes) != num_bytes) {
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void wtf(void) {
|
||||
printf(">>>> WTF\n");
|
||||
EM_ASM_INT_V({ abort(); });
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
PebbleTask pebble_task_get_current(void) {
|
||||
return PebbleTask_App;
|
||||
}
|
||||
|
||||
void app_event_loop(void) {
|
||||
// FIXME: PBL-43469 will need to remove this init from here when multiple
|
||||
// platform support is implemented.
|
||||
rocky_api_watchface_init();
|
||||
emx_app_event_loop();
|
||||
}
|
132
applib-targets/emscripten/tests/test_custom_resources.c
Normal file
132
applib-targets/emscripten/tests/test_custom_resources.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "../../emscripten_resources.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ASSERT(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
printf("%s:%d " #expr " false\n", __FILE__, __LINE__); \
|
||||
exit(-1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define CUSTOM_RES_GEN(x) \
|
||||
static uint32_t s_read_##x##_called = 0; \
|
||||
static uint32_t s_size_##x##_called = 0; \
|
||||
int custom_res_read_##x(int offset, uint8_t *buf, int num_bytes) { \
|
||||
uint32_t *buf_ptr = (uint32_t *)buf; \
|
||||
*buf_ptr = x; \
|
||||
s_read_##x##_called++; \
|
||||
return 4; \
|
||||
} \
|
||||
int custom_res_size_##x(void) { \
|
||||
s_size_##x##_called++; \
|
||||
return 4; \
|
||||
}
|
||||
|
||||
CUSTOM_RES_GEN(1);
|
||||
CUSTOM_RES_GEN(2);
|
||||
CUSTOM_RES_GEN(3);
|
||||
CUSTOM_RES_GEN(4);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// 1 res
|
||||
uint32_t id_1 = emx_resources_register_custom(custom_res_read_1, custom_res_size_1);
|
||||
ASSERT(emx_resources_get_size(1, id_1) == 4);
|
||||
uint32_t buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_1, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 1);
|
||||
ASSERT(s_read_1_called == 1);
|
||||
ASSERT(s_size_1_called == 1);
|
||||
|
||||
// 2nd res
|
||||
uint32_t id_2 = emx_resources_register_custom(custom_res_read_2, custom_res_size_2);
|
||||
ASSERT(emx_resources_get_size(1, id_2) == 4);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_2, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 2);
|
||||
ASSERT(s_read_2_called == 1);
|
||||
ASSERT(s_size_2_called == 1);
|
||||
|
||||
// 3rd res
|
||||
uint32_t id_3 = emx_resources_register_custom(custom_res_read_3, custom_res_size_3);
|
||||
ASSERT(emx_resources_get_size(1, id_3) == 4);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_3, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 3);
|
||||
ASSERT(s_read_3_called == 1);
|
||||
ASSERT(s_size_3_called == 1);
|
||||
|
||||
// remove 2
|
||||
emx_resources_remove_custom(id_2);
|
||||
ASSERT(emx_resources_get_size(1, id_2) == 0);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_2, 0, (uint8_t *)&buf, 1) == 0);
|
||||
ASSERT(buf == 0);
|
||||
// verify 1 & 3 are OK
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_3, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 3);
|
||||
ASSERT(s_read_3_called == 2);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_1, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 1);
|
||||
ASSERT(s_read_1_called == 2);
|
||||
|
||||
// add 4
|
||||
uint32_t id_4 = emx_resources_register_custom(custom_res_read_4, custom_res_size_4);
|
||||
ASSERT(emx_resources_get_size(1, id_4) == 4);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 4);
|
||||
ASSERT(s_read_4_called == 1);
|
||||
ASSERT(s_size_4_called == 1);
|
||||
|
||||
// remove 1 & 3
|
||||
emx_resources_remove_custom(id_1);
|
||||
ASSERT(emx_resources_get_size(1, id_1) == 0);
|
||||
emx_resources_remove_custom(id_3);
|
||||
ASSERT(emx_resources_get_size(1, id_3) == 0);
|
||||
// verify 4 is ok
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 4);
|
||||
ASSERT(s_read_4_called == 2);
|
||||
|
||||
// remove 4
|
||||
emx_resources_remove_custom(id_4);
|
||||
ASSERT(emx_resources_get_size(1, id_4) == 0);
|
||||
ASSERT(s_size_4_called == 1);
|
||||
|
||||
// add 4 again
|
||||
id_4 = emx_resources_register_custom(custom_res_read_4, custom_res_size_4);
|
||||
ASSERT(emx_resources_get_size(1, id_4) == 4);
|
||||
buf = 0;
|
||||
ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4);
|
||||
ASSERT(buf == 4);
|
||||
ASSERT(s_read_4_called == 3);
|
||||
ASSERT(s_size_4_called == 2);
|
||||
|
||||
// remove 4 again
|
||||
emx_resources_remove_custom(id_4);
|
||||
ASSERT(emx_resources_get_size(1, id_4) == 0);
|
||||
ASSERT(s_size_4_called == 2);
|
||||
}
|
42
applib-targets/emscripten/tests/wscript
Normal file
42
applib-targets/emscripten/tests/wscript
Normal file
|
@ -0,0 +1,42 @@
|
|||
from waflib import Logs
|
||||
|
||||
|
||||
def configure(conf):
|
||||
conf.load('gcc waf_unit_test')
|
||||
pass
|
||||
|
||||
|
||||
def show_results(bld):
|
||||
lst = getattr(bld, 'utest_results', [])
|
||||
if lst:
|
||||
Logs.pprint('CYAN', 'execution summary')
|
||||
|
||||
total = len(lst)
|
||||
tfail = len([x for x in lst if x[1]])
|
||||
|
||||
Logs.pprint('CYAN', ' tests that pass %d/%d' % (total-tfail, total))
|
||||
for (f, code, out, err) in lst:
|
||||
if not code:
|
||||
Logs.pprint('CYAN', ' %s' % f)
|
||||
|
||||
if (tfail):
|
||||
Logs.pprint('RED', ' tests that fail %d/%d' % (tfail, total))
|
||||
for (f, code, out, err) in lst:
|
||||
if code:
|
||||
Logs.pprint('CYAN', ' %s' % f)
|
||||
Logs.pprint('WHITE', ' %s' % out)
|
||||
|
||||
|
||||
def build(bld):
|
||||
includes = ['.', '../', '../../../src/fw/']
|
||||
sources = bld.path.ant_glob('*.c')
|
||||
sources.append(bld.path.parent.find_node('emscripten_resources.c'))
|
||||
bld.program(features='test',
|
||||
source=sources,
|
||||
target='test',
|
||||
cflags='-g',
|
||||
includes=includes)
|
||||
|
||||
bld.add_post_fun(show_results)
|
||||
|
||||
# vim:filetype=python
|
134
applib-targets/emscripten/transform_js.py
Executable file
134
applib-targets/emscripten/transform_js.py
Executable file
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('js_file')
|
||||
parser.add_argument('--unittest', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
# load file to be processed
|
||||
with open(args.js_file, "r") as f:
|
||||
source = f.read()
|
||||
|
||||
# remove all known functions for memory access
|
||||
# note: this implementation uses a weak heuristic: only the closing } of a
|
||||
# given function has no indentation
|
||||
for func in ["SAFE_HEAP_LOAD", "SAFE_HEAP_LOAD_D", "SAFE_HEAP_STORE", "SAFE_HEAP_STORE_D"]:
|
||||
source = re.sub("function %s\([^\)]*\)\s*{(.*\n)+?}" % func, "", source)
|
||||
|
||||
# applies the same patch as seen at
|
||||
# https://github.com/kripken/emscripten/commit/bc11547fbf446993ee0f6f30a0deb3f80f205c35
|
||||
# which is part of the fix for https://github.com/kripken/emscripten/issues/3945
|
||||
# TODO: fix after PBL-32521 is done
|
||||
orig_source = source
|
||||
source = source.replace("funcstr += arg + '=' + convertCode.returnValue + ';';",
|
||||
"funcstr += arg + '=(' + convertCode.returnValue + ');';")
|
||||
# assert source != orig_source, "Emscripten output does not match expected output of 1.35.0"
|
||||
|
||||
# we're not using emscripten's --pre-js and --post-js as it interferes
|
||||
# with --embed-file
|
||||
with open(args.js_file, "w") as f:
|
||||
f.write(PROLOGUE)
|
||||
if args.unittest:
|
||||
f.write(UNITTEST_PROLOGUE)
|
||||
f.write(source)
|
||||
f.write(EPILOGUE)
|
||||
if args.unittest:
|
||||
f.write("new RockySimulator();\n")
|
||||
|
||||
PROLOGUE = """
|
||||
RockySimulator = function(options) {
|
||||
options = options || {};
|
||||
|
||||
var Module = {
|
||||
print: function(text) {
|
||||
console.log(text);
|
||||
},
|
||||
printErr: function(text) {
|
||||
console.error(text);
|
||||
},
|
||||
};
|
||||
|
||||
"""
|
||||
|
||||
EPILOGUE = """
|
||||
// support non-aligned memory access
|
||||
function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
|
||||
if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest);
|
||||
if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP);
|
||||
assert(DYNAMICTOP <= TOTAL_MEMORY);
|
||||
if (dest % bytes !== 0) {
|
||||
for (var i = 0; i < bytes; i++) {
|
||||
HEAPU8[dest + i >> 0] = (value >> (8 * i)) & 0xff;
|
||||
}
|
||||
} else {
|
||||
setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
|
||||
}
|
||||
}
|
||||
|
||||
function SAFE_HEAP_STORE_D(dest, value, bytes) {
|
||||
SAFE_HEAP_STORE(dest, value, bytes, true);
|
||||
}
|
||||
|
||||
function SAFE_HEAP_LOAD(dest, bytes, unsigned, isFloat) {
|
||||
// overrule
|
||||
if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest);
|
||||
if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP);
|
||||
assert(DYNAMICTOP <= TOTAL_MEMORY);
|
||||
var type = getSafeHeapType(bytes, isFloat);
|
||||
var ret;
|
||||
if (dest % bytes !== 0) {
|
||||
for (var i = 0; i < bytes; i++) {
|
||||
ret |= HEAPU8[dest + i >> 0] << (8 * i);
|
||||
}
|
||||
} else {
|
||||
ret = getValue(dest, type, 1);
|
||||
}
|
||||
if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1);
|
||||
return ret;
|
||||
}
|
||||
function SAFE_HEAP_LOAD_D(dest, bytes, unsigned) {
|
||||
return SAFE_HEAP_LOAD(dest, bytes, unsigned, true);
|
||||
}
|
||||
|
||||
return Module;
|
||||
};
|
||||
|
||||
if (typeof(module) !== "undefined") {
|
||||
module.exports = RockySimulator;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
UNITTEST_PROLOGUE = """
|
||||
var defaultPreRun = Module.preRun;
|
||||
Module.preRun = function() {
|
||||
if (defaultPreRun) {
|
||||
defaultPreRun();
|
||||
}
|
||||
// Mount the host filesystem to make the fixture files accessible:
|
||||
FS.mkdir('/node_fs');
|
||||
FS.mount(NODEFS, { root: '/' }, '/node_fs');
|
||||
}
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
137
applib-targets/emscripten/wscript
Normal file
137
applib-targets/emscripten/wscript
Normal file
|
@ -0,0 +1,137 @@
|
|||
from waflib import Logs
|
||||
|
||||
|
||||
def em_resource(task):
|
||||
packager = task.env.EMSCRIPTEN_ROOT + '/tools/file_packager.py'
|
||||
task.exec_command(['python',
|
||||
packager,
|
||||
task.outputs[0].abspath(),
|
||||
'--embed',
|
||||
task.inputs[0].abspath(),
|
||||
'--js-output=' + task.outputs[0].abspath()])
|
||||
|
||||
|
||||
def configure(conf):
|
||||
conf.find_program('node', var='NODE', errmsg='node is not installed')
|
||||
|
||||
# Make sure we're about to modify the 'local' conf:
|
||||
prev_env = conf.env
|
||||
conf.set_env(conf.all_envs['local'])
|
||||
|
||||
conf.load('emscripten')
|
||||
|
||||
# The standard lib emscripten bundles uses a different format
|
||||
# for its stack guard!!
|
||||
conf.env.CFLAGS.append('-D_TIME_H')
|
||||
|
||||
# For unit tests: DUMA depends on pthread,
|
||||
# which I didn't get to work with emscripten.
|
||||
conf.env.DEFINES.append('DUMA_DISABLED')
|
||||
|
||||
# Flags that emcc doesn't support, just remove them:
|
||||
unwanted_cflags = ['-gdwarf-4']
|
||||
|
||||
if 'RELEASE' not in conf.env.DEFINES:
|
||||
conf.env.EMCC_DEBUG = 2
|
||||
conf.env['CFLAGS'].extend(['-g4'])
|
||||
unwanted_cflags.extend(['-g3', '-g'])
|
||||
|
||||
conf.env['CFLAGS'] = filter(
|
||||
lambda flag: flag not in unwanted_cflags,
|
||||
conf.env['CFLAGS']
|
||||
)
|
||||
|
||||
conf.env.EMX_OTHER_SETTINGS = [
|
||||
'SAFE_HEAP=1',
|
||||
# absurdly large value so we don't worry:
|
||||
'RESERVED_FUNCTION_POINTERS=1000',
|
||||
'ERROR_ON_UNDEFINED_SYMBOLS=1'
|
||||
]
|
||||
|
||||
conf.add_platform_defines(conf.env)
|
||||
|
||||
conf.recurse('integration_tests')
|
||||
|
||||
conf.setenv('emscripten', conf.env)
|
||||
|
||||
conf.set_env(prev_env)
|
||||
|
||||
|
||||
def apply_config_for_applib_and_test_rocky_emx_builds(bld):
|
||||
bld.env.DEFINES.append("APPLIB_EMSCRIPTEN=1")
|
||||
|
||||
# __builtin_return_address() doesn't seem to be supported by Emscripten,
|
||||
# it fail at runtime due to a missing `llvm_return_address` function.
|
||||
bld.env.CFLAGS.extend(['-D__builtin_return_address(level)=(0)'])
|
||||
|
||||
jerry_api_js = bld.path.make_node('jerry_api.js')
|
||||
timeshift_js = bld.path.find_node('timeshift-js/timeshift.js')
|
||||
html_binding_js = bld.path.make_node('html-binding.js')
|
||||
bld.env.EMX_PRE_JS_FILES = [jerry_api_js, timeshift_js]
|
||||
bld.env.EMX_POST_JS_FILES = [html_binding_js]
|
||||
# use external transformation script instead of --pre-js and --post-js so
|
||||
# we can replace functions and wrap entire file without interfering with
|
||||
# --embed-file
|
||||
transform_js_node_and_args = [bld.path.make_node('transform_js.py')]
|
||||
if bld.variant == 'test_rocky_emx':
|
||||
transform_js_node_and_args.append(' --unittest')
|
||||
bld.env.EMX_TRANSFORM_JS_NODE_AND_ARGS = transform_js_node_and_args
|
||||
|
||||
|
||||
def build(bld):
|
||||
if bld.variant == 'test':
|
||||
bld.recurse('tests')
|
||||
return
|
||||
|
||||
# Extend waf's 'cprogram' feature with Emscripten-specific things:
|
||||
bld.load('emscripten')
|
||||
|
||||
apply_config_for_applib_and_test_rocky_emx_builds(bld)
|
||||
|
||||
# Fine to use 'stlib' here vs emscripten_program, because we're only
|
||||
# invoking emcc to generate an archive file, so only 'standard' compiler
|
||||
# flags need to be passed.
|
||||
bld.objects(source=['emscripten_jerry_api.c'],
|
||||
target='emscripten_jerry_api',
|
||||
use=['jerry_port_includes'])
|
||||
|
||||
if bld.variant == 'test_rocky_emx':
|
||||
return
|
||||
|
||||
# Copy stuff from html folder:
|
||||
html_node = bld.path.find_dir('html')
|
||||
html_bld_node = bld.path.get_bld().make_node('html')
|
||||
for file in html_node.ant_glob('**/*'):
|
||||
bld(rule="cp ${SRC} ${TGT}",
|
||||
source=file,
|
||||
target=html_bld_node.make_node(file.path_from(html_node)))
|
||||
|
||||
pbpack = bld.path.parent.parent.get_bld().make_node('system_resources.pbpack')
|
||||
exported_functions = bld.path.make_node('exported_functions.json')
|
||||
sources = bld.path.ant_glob('*.c', excl='emscripten_jerry_api.c')
|
||||
rockyjs_node = html_bld_node.make_node('rocky.js')
|
||||
bld.program(source=sources,
|
||||
target=rockyjs_node,
|
||||
emx_pre_js_files=[],
|
||||
emx_post_js_files=[],
|
||||
emx_exported_functions=exported_functions,
|
||||
emx_other_settings=[],
|
||||
emx_embed_files=[pbpack],
|
||||
use=['emscripten_jerry_api',
|
||||
'applib',
|
||||
'applib_includes',
|
||||
'nanopb',
|
||||
'fw_includes',
|
||||
'libutil',
|
||||
'upng'])
|
||||
|
||||
bld.recurse('integration_tests')
|
||||
|
||||
def print_index_html_path(bld):
|
||||
index_html_path = html_bld_node.find_node('index.html').abspath()
|
||||
Logs.pprint('PINK',
|
||||
'Built Rocky Simulator: file://{}'.format(index_html_path))
|
||||
bld.add_post_fun(print_index_html_path)
|
||||
|
||||
|
||||
# vim:filetype=python
|
19
applib-targets/overrides/FreeRTOS.h
Normal file
19
applib-targets/overrides/FreeRTOS.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef long BaseType_t;
|
34
applib-targets/overrides/applib/applib_malloc.auto.h
Normal file
34
applib-targets/overrides/applib/applib_malloc.auto.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void* applib_zalloc(size_t size) {
|
||||
void* result = malloc(size);
|
||||
if (result) {
|
||||
memset(result, 0, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define applib_type_zalloc(Type) applib_zalloc(sizeof(Type))
|
||||
#define applib_type_malloc(Type) malloc(sizeof(Type))
|
||||
#define applib_type_size(Type) sizeof(Type)
|
||||
#define applib_malloc(size) malloc(size)
|
||||
#define applib_free(ptr) free(ptr)
|
22
applib-targets/overrides/os/mutex.h
Normal file
22
applib-targets/overrides/os/mutex.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct pebble_mutex_t;
|
||||
typedef struct pebble_mutex_t PebbleMutex;
|
||||
struct pebble_recursive_mutex_t;
|
||||
typedef struct pebble_recursive_mutex_t PebbleRecursiveMutex;
|
19
applib-targets/overrides/portmacro.h
Normal file
19
applib-targets/overrides/portmacro.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef long BaseType_t;
|
19
applib-targets/overrides/queue.h
Normal file
19
applib-targets/overrides/queue.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef void * QueueHandle_t;
|
17
applib-targets/overrides/semphr.h
Normal file
17
applib-targets/overrides/semphr.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
22
applib-targets/overrides/task.h
Normal file
22
applib-targets/overrides/task.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef void * TaskHandle_t;
|
||||
|
||||
typedef struct {
|
||||
} TaskParameters_t;
|
40
applib-targets/sdl/examples/main.c
Normal file
40
applib-targets/sdl/examples/main.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sdl_graphics.h"
|
||||
#include "sdl_app.h"
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_line.h"
|
||||
|
||||
|
||||
int main(void) {
|
||||
GContext *context = sdl_graphics_get_gcontext();
|
||||
graphics_context_set_stroke_color(context, GColorBrightGreen);
|
||||
graphics_context_set_stroke_width(context, 2);
|
||||
graphics_draw_line(context, (GPoint){0, 0}, (GPoint){100, 100});
|
||||
graphics_draw_line(context, (GPoint){0, 10}, (GPoint){100, 10});
|
||||
graphics_draw_line(context, (GPoint){0, 20}, (GPoint){100, 20});
|
||||
graphics_draw_line(context, (GPoint){0, 30}, (GPoint){100, 30});
|
||||
graphics_draw_circle(context, (GPoint){50, 50}, 20);
|
||||
app_event_loop();
|
||||
|
||||
return 0;
|
||||
}
|
12
applib-targets/sdl/examples/wscript
Normal file
12
applib-targets/sdl/examples/wscript
Normal file
|
@ -0,0 +1,12 @@
|
|||
from waflib import Task
|
||||
from waflib.TaskGen import feature, before_method
|
||||
|
||||
|
||||
def build(bld):
|
||||
sources = bld.path.ant_glob('*.c')
|
||||
bld.program(source=sources,
|
||||
target='sdl-example',
|
||||
defines=['main=app_main'],
|
||||
use=['applib_sdl', 'fw_includes'])
|
||||
|
||||
# vim:filetype=python
|
72
applib-targets/sdl/sdl_app.c
Normal file
72
applib-targets/sdl/sdl_app.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "sdl_app.h"
|
||||
#include "sdl_graphics.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern int app_main(void);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (!sdl_app_init()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
app_main();
|
||||
sdl_app_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
bool sdl_app_init(void) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
printf("Error: Failed to init SDL\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sdl_graphics_init()) {
|
||||
printf("Error: Failed to init graphics\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sdl_app_deinit(void) {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void sdl_app_event_loop(void) {
|
||||
SDL_Event event;
|
||||
int keypress = 0;
|
||||
|
||||
while (!keypress) {
|
||||
sdl_graphics_render();
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
keypress = 1;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
keypress = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
applib-targets/sdl/sdl_app.h
Normal file
23
applib-targets/sdl/sdl_app.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool sdl_app_init(void);
|
||||
void sdl_app_deinit(void);
|
||||
void sdl_app_event_loop(void);
|
61
applib-targets/sdl/sdl_graphics.c
Normal file
61
applib-targets/sdl/sdl_graphics.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "sdl_graphics.h"
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/8_bit/framebuffer.h"
|
||||
#include "util/circular_cache.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
|
||||
static GContext s_gcontext = {};
|
||||
static FrameBuffer fb = {};
|
||||
static SDL_Surface *screen = NULL;
|
||||
|
||||
bool sdl_graphics_init(void) {
|
||||
if (!(screen = SDL_SetVideoMode(DISP_COLS, DISP_ROWS, 8 /* bits/pixel */, SDL_HWSURFACE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
framebuffer_init(&fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
s_gcontext = (GContext) {
|
||||
.dest_bitmap = (GBitmap) {
|
||||
.addr = screen->pixels,
|
||||
.row_size_bytes = DISP_COLS,
|
||||
.info = (BitmapInfo) {.format = GBITMAP_NATIVE_FORMAT },
|
||||
.bounds = (GRect) { { 0, 0 }, { DISP_COLS, DISP_ROWS } },
|
||||
.data_row_infos = NULL,
|
||||
},
|
||||
.parent_framebuffer = &fb,
|
||||
.parent_framebuffer_vertical_offset = 0,
|
||||
.lock = false
|
||||
};
|
||||
|
||||
graphics_context_set_default_drawing_state(&s_gcontext, GContextInitializationMode_App);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GContext *sdl_graphics_get_gcontext(void) {
|
||||
return &s_gcontext;
|
||||
}
|
||||
|
||||
void sdl_graphics_render(void) {
|
||||
SDL_Flip(screen);
|
||||
}
|
24
applib-targets/sdl/sdl_graphics.h
Normal file
24
applib-targets/sdl/sdl_graphics.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
bool sdl_graphics_init(void);
|
||||
void sdl_graphics_render(void);
|
||||
GContext *sdl_graphics_get_gcontext(void);
|
105
applib-targets/sdl/shims.c
Normal file
105
applib-targets/sdl/shims.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/heap.h"
|
||||
#include "util/circular_cache.h"
|
||||
|
||||
#include "sdl_app.h"
|
||||
#include "sdl_graphics.h"
|
||||
|
||||
void *task_malloc(size_t bytes) {
|
||||
return malloc(bytes);
|
||||
}
|
||||
|
||||
void task_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void app_log(uint8_t log_level, const char* src_filename,
|
||||
int src_line_number, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
GContext* app_state_get_graphics_context() {
|
||||
return sdl_graphics_get_gcontext();
|
||||
}
|
||||
|
||||
Heap *app_state_get_heap(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBitmap* app_state_legacy2_get_2bit_framebuffer(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void circular_cache_init(CircularCache* c, uint8_t* buffer, size_t item_size,
|
||||
int total_items, Comparator compare_cb) {
|
||||
}
|
||||
|
||||
bool heap_is_allocated(Heap* const heap, void* ptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void passert_failed(const char* filename, int line_number, const char* message, ...) {
|
||||
}
|
||||
|
||||
void passert_failed_no_message(const char* filename, int line_number) {
|
||||
}
|
||||
|
||||
void pbl_log(uint8_t log_level, const char* src_filename,
|
||||
int src_line_number, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
bool process_manager_compiled_with_legacy2_sdk(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResAppNum sys_get_current_resource_num(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t * sys_resource_builtin_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
uint32_t *num_bytes_out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes,
|
||||
uint8_t *buffer, size_t num_bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sys_resource_size(ResAppNum app_num, uint32_t handle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_event_loop(void) {
|
||||
sdl_app_event_loop();
|
||||
}
|
||||
|
||||
void wtf(void) {
|
||||
printf(">>> WTF\n");
|
||||
}
|
27
applib-targets/sdl/wscript
Normal file
27
applib-targets/sdl/wscript
Normal file
|
@ -0,0 +1,27 @@
|
|||
import waflib
|
||||
|
||||
|
||||
def configure(conf):
|
||||
conf.check_cfg(msg='Checking for sdl-config',
|
||||
path='sdl-config',
|
||||
package='',
|
||||
args='--cflags --libs',
|
||||
uselib_store='SDL')
|
||||
|
||||
conf.find_program('objcopy gobjcopy', var='OBJCOPY')
|
||||
|
||||
# We are overriding the gcc toolchain include/time.h with our own
|
||||
# just to check/force our version of <time.h>
|
||||
conf.env.CFLAGS.append('-D_TIME_H_')
|
||||
|
||||
|
||||
def build(bld):
|
||||
sources = bld.path.ant_glob('*.c')
|
||||
bld.stlib(source=sources,
|
||||
target='applib_sdl',
|
||||
includes='.',
|
||||
export_includes='.',
|
||||
use=['applib', 'fw_includes', 'libutil', 'upng', 'SDL'])
|
||||
bld.recurse('examples')
|
||||
|
||||
# vim:filetype=python
|
33
applib-targets/wscript
Normal file
33
applib-targets/wscript
Normal file
|
@ -0,0 +1,33 @@
|
|||
def options(opt):
|
||||
opt.add_option('--target', action='store',
|
||||
choices=['sdl', 'emscripten'],
|
||||
help='What backend we are compiling applib against (#rockyJS)')
|
||||
|
||||
|
||||
def configure(conf):
|
||||
if conf.options.target is None:
|
||||
return
|
||||
else:
|
||||
conf.env.APPLIB_TARGET = conf.options.target
|
||||
conf.recurse(conf.options.target)
|
||||
|
||||
|
||||
def build(bld):
|
||||
if bld.variant == 'test':
|
||||
bld.recurse('emscripten')
|
||||
return
|
||||
|
||||
if bld.env.APPLIB_TARGET is None:
|
||||
bld(export_includes=[], name='target_includes')
|
||||
return
|
||||
|
||||
bld.set_env(bld.all_envs['local'])
|
||||
|
||||
# time_t is defined in sys/types in newlib, and time.h on recent Linux
|
||||
# so just force the defined type for testing time
|
||||
bld.env.CFLAGS.append('-Dtime_t=__SYSCALL_SLONG_TYPE')
|
||||
|
||||
bld(export_includes=['overrides'], name='target_includes')
|
||||
bld.recurse(bld.env.APPLIB_TARGET)
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue