diff --git a/Makefile b/Makefile index cf8200d9..d9303e3d 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ CC = cc LD = cc -REAL_CFLAGS = -I./include $(shell pkg-config --cflags gbm libdrm glesv2 egl) -DBUILD_ELM327_PLUGIN -DBUILD_TEST_PLUGIN -ggdb $(CFLAGS) +REAL_CFLAGS = -I./include $(shell pkg-config --cflags gbm libdrm glesv2 egl) -DBUILD_ELM327_PLUGIN -DBUILD_GPIOD_PLUGIN -DBUILD_SPIDEV_PLUGIN -DBUILD_TEST_PLUGIN -ggdb $(CFLAGS) REAL_LDFLAGS = $(shell pkg-config --libs gbm libdrm glesv2 egl) -lrt -lflutter_engine -lpthread -ldl $(LDFLAGS) SOURCES = src/flutter-pi.c src/platformchannel.c src/pluginregistry.c src/console_keyboard.c \ - src/plugins/elm327plugin.c src/plugins/services-plugin.c src/plugins/testplugin.c src/plugins/text_input.c \ - src/plugins/raw_keyboard.c + src/plugins/elm327plugin.c src/plugins/services.c src/plugins/testplugin.c src/plugins/text_input.c \ + src/plugins/raw_keyboard.c src/plugins/gpiod.c src/plugins/spidev.c OBJECTS = $(patsubst src/%.c,out/obj/%.o,$(SOURCES)) all: out/flutter-pi -out/obj/%.o: src/%.c +out/obj/%.o: src/%.c @mkdir -p $(@D) $(CC) -c $(REAL_CFLAGS) $(REAL_LDFLAGS) $< -o $@ diff --git a/include/flutter-pi.h b/include/flutter-pi.h index 4c11dddc..318e5ead 100644 --- a/include/flutter-pi.h +++ b/include/flutter-pi.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #define EGL_PLATFORM_GBM_KHR 0x31D7 @@ -34,6 +36,8 @@ typedef enum { kVBlankRequest, kVBlankReply, kUpdateOrientation, + kSendPlatformMessage, + kRespondToPlatformMessage, kFlutterTask } flutterpi_task_type; @@ -47,11 +51,26 @@ struct flutterpi_task { intptr_t baton; }; enum device_orientation orientation; + struct { + char *channel; + const FlutterPlatformMessageResponseHandle *responsehandle; + size_t message_size; + uint8_t *message; + }; }; uint64_t target_time; }; -void post_platform_task(struct flutterpi_task *task); +static inline void *memdup(const void *restrict src, const size_t n) { + void *__restrict__ dest; + + if ((src == NULL) || (n == 0)) return NULL; + + dest = malloc(n); + if (dest == NULL) return NULL; + + return memcpy(dest, src, n); +} struct drm_fb { struct gbm_bo *bo; @@ -111,8 +130,6 @@ struct mousepointer_mtslot { #define ISSET(uint32bitmap, bit) (uint32bitmap[(bit)/32] & (1 << ((bit) & 0x1F))) -#define STREQ(a, b) (strcmp(a, b) == 0) - struct input_device { char path[PATH_MAX]; char name[256]; @@ -147,4 +164,15 @@ extern struct mousepointer_mtslot mousepointer; extern FlutterEngine engine; +void post_platform_task(struct flutterpi_task *task); + +int flutterpi_send_platform_message(const char *channel, + const uint8_t *restrict message, + size_t message_size, + FlutterPlatformMessageResponseHandle *responsehandle); + +int flutterpi_respond_to_platform_message(FlutterPlatformMessageResponseHandle *handle, + const uint8_t *restrict message, + size_t message_size); + #endif \ No newline at end of file diff --git a/include/platformchannel.h b/include/platformchannel.h index 96105b30..817bfe9d 100644 --- a/include/platformchannel.h +++ b/include/platformchannel.h @@ -9,15 +9,15 @@ // andrew // only 32bit support for now. -#define __ALIGN4_REMAINING(value, remaining, ...) __align((uint32_t*) (value), 4, remaining) -#define __ALIGN8_REMAINING(value, remaining, ...) __align((uint32_t*) (value), 8, remaining) -#define align4(...) __ALIGN4_REMAINING(__VA_ARGS__, NULL) -#define align8(...) __ALIGN8_REMAINING(__VA_ARGS__, NULL) +//#define __ALIGN4_REMAINING(value, remaining, ...) __align(value, 4, remaining) +//#define __ALIGN8_REMAINING(value, remaining, ...) __align(value, 8, remaining) +//#define align4(...) __ALIGN4_REMAINING(__VA_ARGS__, NULL) +//#define align8(...) __ALIGN8_REMAINING(__VA_ARGS__, NULL) -#define alignmentDiff(value, alignment) __alignmentDiff((uint32_t) value, alignment) +//#define alignmentDiff(value, alignment) __alignmentDiff(value, alignment) -#define __ADVANCE_REMAINING(value, n, remaining, ...) __advance((uint32_t*) (value), n, remaining) -#define advance(...) __ADVANCE_REMAINING(__VA_ARGS__, NULL) +//#define __ADVANCE_REMAINING(value, n, remaining, ...) __advance(value, n, remaining) +//#define advance(...) __ADVANCE_REMAINING(__VA_ARGS__, NULL) /* @@ -34,45 +34,51 @@ * need to be rewritten every time they do. The handlers not needing to be rewritten would probably be the only advantage * of using a unified message value type. */ -enum JSONMsgCodecValueType { - kJSNull, kJSTrue, kJSFalse, kJSNumber, kJSString, kJSArray, kJSObject +enum json_value_type { + kJsonNull, + kJsonTrue, + kJsonFalse, + kJsonNumber, + kJsonString, + kJsonArray, + kJsonObject }; -struct JSONMsgCodecValue { - enum JSONMsgCodecValueType type; +struct json_value { + enum json_value_type type; union { double number_value; char *string_value; struct { size_t size; union { - struct JSONMsgCodecValue *array; + struct json_value *array; struct { char **keys; - struct JSONMsgCodecValue *values; + struct json_value *values; }; }; }; }; }; -enum StdMsgCodecValueType { - kNull = 0, - kTrue, - kFalse, - kInt32, - kInt64, - kLargeInt, // treat as kString - kFloat64, - kString, - kUInt8Array, - kInt32Array, - kInt64Array, - kFloat64Array, - kList, - kMap +enum std_value_type { + kStdNull = 0, + kStdTrue, + kStdFalse, + kStdInt32, + kStdInt64, + kStdLargeInt, // treat as kString + kStdFloat64, + kStdString, + kStdUInt8Array, + kStdInt32Array, + kStdInt64Array, + kStdFloat64Array, + kStdList, + kStdMap }; -struct StdMsgCodecValue { - enum StdMsgCodecValueType type; +struct std_value { + enum std_value_type type; union { bool bool_value; int32_t int32_value; @@ -86,20 +92,51 @@ struct StdMsgCodecValue { int32_t* int32array; int64_t* int64array; double* float64array; - struct StdMsgCodecValue* list; + struct std_value* list; struct { - struct StdMsgCodecValue* keys; - struct StdMsgCodecValue* values; + struct std_value* keys; + struct std_value* values; }; }; }; }; }; +#define STDVALUE_IS_NULL(value) ((value).type == kStdNull) +#define STDNULL ((struct std_value) {.type = kStdNull}) + +#define STDVALUE_IS_BOOL(value) (((value).type == kStdTrue) || ((value).type == kStdFalse)) +#define STDVALUE_AS_BOOL(value) ((value).type == kStdTrue) +#define STDBOOL(bool_value) ((struct std_value) {.type = (bool_value) ? kStdTrue : kStdFalse}) + +#define STDVALUE_IS_INT(value) (((value).type == kStdInt32) || ((value).type == kStdInt64)) +#define STDVALUE_AS_INT(value) ((value).type == kStdInt32 ? (int64_t) (value).int32_value : (value).int64_value) +#define STDINT32(_int32_value) ((struct std_value) {.type = kStdInt32, .int32_value = (_int32_value)}) +#define STDINT64(_int64_value) ((struct std_value) {.type = kStdInt64, .int64_value = (_int64_value)}) + +#define STDVALUE_IS_FLOAT(value) ((value).type == kStdFloat64) +#define STDVALUE_AS_FLOAT(value) ((value).float64_value) +#define STDFLOAT64(double_value) ((struct std_value) {.type = kStdFloat64, .float64_value = (double_value)}) + +#define STDVALUE_IS_NUM(value) (STDVALUE_IS_INT(value) || STDVALUE_IS_FLOAT(value)) +#define STDVALUE_AS_NUM(value) (STDVALUE_IS_INT(value) ? STDVALUE_AS_INT(value) : STDVALUE_AS_FLOAT(value)) + +#define STDVALUE_IS_STRING(value) ((value).type == kStdString) +#define STDVALUE_AS_STRING(value) ((value).string_value) +#define STDSTRING(str) ((struct std_value) {.type = kStdString, .string_value = str}) + +#define STDVALUE_IS_LIST(value) ((value).type == kStdList) +#define STDVALUE_IS_SIZE(value, _size) ((value).size == (_size)) +#define STDVALUE_IS_SIZED_LIST(value, _size) (STDVALUE_IS_LIST(value) && STDVALUE_IS_SIZE(value, _size)) + +#define STDVALUE_IS_INT_ARRAY(value) (((value).type == kStdInt32Array) || ((value).type == kStdInt64Array) || ((value).type == kStdUInt8Array)) +#define STDVALUE_IS_FLOAT_ARRAY(value) ((value).type == kStdFloat64Array) +#define STDVALUE_IS_NUM_ARRAY(value) (STDVALUE_IS_INT_ARRAY(value) || STDVALUE_IS_FLOAT_ARRAY(value)) + /// codec of an abstract channel object /// These tell this API how it should encode ChannelObjects -> platform messages /// and how to decode platform messages -> ChannelObjects. -enum ChannelCodec { +enum platch_codec { kNotImplemented, kStringCodec, kBinaryCodec, @@ -111,7 +148,7 @@ enum ChannelCodec { kJSONMethodCallResponse }; -/// Abstract Channel Object. +/// Platform Channel Object. /// Different properties are "valid" for different codecs: /// kNotImplemented: /// no values associated with this "codec". @@ -120,57 +157,58 @@ enum ChannelCodec { /// - string_value is the raw byte data of a platform message, but with an additional null-byte at the end. /// kBinaryCodec: /// - binarydata is an array of the raw byte data of a platform message, -/// - binarydata_size is the size of that byte data in uint8_t's. +/// - binarydata_size is the size of that byte data in bytes. /// kJSONMessageCodec: -/// - jsonmsgcodec_value +/// - json_value /// kStdMsgCodecValue: -/// - stdmsgcodec_value +/// - std_value /// kStandardMethodCall: /// - "method" is the method you'd like to call, or the method that was called /// by flutter. -/// - stdarg contains the argument to that method call. +/// - std_arg contains the argument to that method call. /// kJSONMethodCall: /// - "method" is the method you'd like to call, or the method that was called /// by flutter. -/// - jsarg contains the argument to that method call. +/// - json_arg contains the argument to that method call. /// kStandardMethodCallResponse or kJSONMethodCallResponse: -/// - "success" is whether the method call (send to flutter or received from flutter) -/// was succesful, i.e. no errors ocurred. +/// - "success" is whether the method call (called by flutter or by called by you) /// if success is false, -/// - errorcode must be set (by you or by flutter) to point to a valid null-terminated string, -/// - errormessage is either pointing to a valid null-terminated string or is set to NULL, +/// - error_code must be set to point to a valid null-terminated string, +/// - error_msg is either pointing to a valid null-terminated string or is set to NULL, /// - if the codec is kStandardMethodCallResponse, -/// stderrordetails may be set to any StdMsgCodecValue or NULL. +/// std_error_details must be a valid std_value +/// ({.type = kStdNull} is possible, but not NULL). /// - if the codec is kJSONMethodCallResponse, -/// jserrordetails may be set to any JSONMSgCodecValue or NULL. -struct ChannelObject { - enum ChannelCodec codec; +/// json_error_details must be a valid json_value +/// ({.type = kJsonNull} is possible, but not NULL) +struct platch_obj { + enum platch_codec codec; union { char *string_value; struct { size_t binarydata_size; uint8_t *binarydata; }; - struct JSONMsgCodecValue jsonmsgcodec_value; - struct StdMsgCodecValue stdmsgcodec_value; + struct json_value json_value; + struct std_value std_value; struct { char *method; union { - struct StdMsgCodecValue stdarg; - struct JSONMsgCodecValue jsarg; + struct std_value std_arg; + struct json_value json_arg; }; }; struct { bool success; union { - struct StdMsgCodecValue stdresult; - struct JSONMsgCodecValue jsresult; + struct std_value std_result; + struct json_value json_result; }; - char *errorcode; - char *errormessage; + char *error_code; + char *error_msg; union { - struct StdMsgCodecValue stderrordetails; - struct JSONMsgCodecValue jserrordetails; + struct std_value std_error_details; + struct json_value json_error_details; }; }; }; @@ -179,7 +217,7 @@ struct ChannelObject { /// A Callback that is called when a response to a platform message you send to flutter /// arrives. "object" is the platform message decoded using the "codec" you gave to PlatformChannel_send, /// "userdata" is the userdata you gave to PlatformChannel_send. -typedef int (*PlatformMessageResponseCallback)(struct ChannelObject *object, void *userdata); +typedef int (*platch_msg_resp_callback)(struct platch_obj *object, void *userdata); /// decodes a platform message (represented by `buffer` and `size`) as the given codec, @@ -191,16 +229,13 @@ typedef int (*PlatformMessageResponseCallback)(struct ChannelObject *object, voi /// is freed by flutter, the contents of object_out will in many cases be bogus. /// If you'd like object_out to be persistent and not depend on the lifetime of the buffer, /// you'd have to manually deep-copy it. -int PlatformChannel_decode(uint8_t *buffer, size_t size, enum ChannelCodec codec, struct ChannelObject *object_out); - -/// decodes a JSON String into a JSONMsgCodecValue -int PlatformChannel_decodeJSON(char *string, struct JSONMsgCodecValue *out); +int platch_decode(uint8_t *buffer, size_t size, enum platch_codec codec, struct platch_obj *object_out); /// Encodes a generic ChannelObject into a buffer (that is, too, allocated by PlatformChannel_encode) /// A pointer to the buffer is put into buffer_out and the size of that buffer into size_out. /// The lifetime of the buffer is independent of the ChannelObject, so contents of the ChannelObject /// can be freed after the object was encoded. -int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, size_t *size_out); +int platch_encode(struct platch_obj *object, uint8_t **buffer_out, size_t *size_out); /// Encodes a generic ChannelObject (anything, string/binary codec or Standard/JSON Method Calls and responses) as a platform message /// and sends it to flutter on channel `channel` @@ -209,24 +244,38 @@ int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, s /// Then, on_response is called with the decoded ChannelObject and the userdata as an argument. /// It's possible that flutter won't respond to your platform message, like when sending events via an EventChannel. /// userdata can be NULL. -int PlatformChannel_send(char *channel, struct ChannelObject *object, enum ChannelCodec response_codec, PlatformMessageResponseCallback on_response, void *userdata); +int platch_send(char *channel, + struct platch_obj *object, + enum platch_codec response_codec, + platch_msg_resp_callback on_response, + void *userdata); /// Encodes a StandardMethodCodec method call as a platform message and sends it to flutter /// on channel `channel`. This is just a wrapper around PlatformChannel_send /// that builds the ChannelObject for you. /// The response_codec is kStandardMethodCallResponse. userdata can be NULL. -int PlatformChannel_stdcall(char *channel, char *method, struct StdMsgCodecValue *argument, PlatformMessageResponseCallback on_response, void *userdata); - -/// Encodes a JSONMethodCodec method call as a platform message and sends it to flutter -/// on channel `channel`. This is just a wrapper around PlatformChannel_send +int platch_call_std(char *channel, + char *method, + struct std_value *argument, + platch_msg_resp_callback on_response, + void *userdata); + +/// Encodes the arguments as a JSON method call and sends it to flutter +/// on channel [channel]. This is just a wrapper around platch_send /// that builds the ChannelObject for you. -/// The response_codec is kJSONMethodCallResponse. userdata can be NULL. -int PlatformChannel_jsoncall(char *channel, char *method, struct JSONMsgCodecValue *argument, PlatformMessageResponseCallback on_response, void *userdata); +/// The response is automatically decoded as a JSON method call response and +/// supplied to [on_response] as an argument. userdata can be NULL. +int platch_call_json(char *channel, + char *method, + struct json_value *argument, + platch_msg_resp_callback on_response, + void *userdata); /// Responds to a platform message. You can (of course) only respond once to a platform message, /// i.e. a FlutterPlatformMessageResponseHandle can only be used once. /// The codec of `response` can be any of the available codecs. -int PlatformChannel_respond(FlutterPlatformMessageResponseHandle *handle, struct ChannelObject *response); +int platch_respond(FlutterPlatformMessageResponseHandle *handle, + struct platch_obj *response); /// Tells flutter that the platform message that was sent to you was not handled. /// (for example, there's no plugin that is using this channel, or there is a plugin @@ -235,126 +284,230 @@ int PlatformChannel_respond(FlutterPlatformMessageResponseHandle *handle, struct /// When flutter receives this response, it will throw a MissingPluginException. /// For most channel used by the ServicesPlugin, this is not too bad since it /// specifies many of the channels used as OptionalMethodChannels. (which will silently catch the MissingPluginException) -int PlatformChannel_respondNotImplemented(FlutterPlatformMessageResponseHandle *handle); +int platch_respond_not_implemented(FlutterPlatformMessageResponseHandle *handle); + +int platch_respond_success_std(FlutterPlatformMessageResponseHandle *handle, + struct std_value *return_value); + +int platch_respond_error_std(FlutterPlatformMessageResponseHandle *handle, + char *error_code, + char *error_msg, + struct std_value *error_details); + +int platch_respond_illegal_arg_std(FlutterPlatformMessageResponseHandle *handle, + char *error_msg); + +int platch_respond_native_error_std(FlutterPlatformMessageResponseHandle *handle, + int _errno); + + +int platch_respond_success_json(FlutterPlatformMessageResponseHandle *handle, + struct json_value *return_value); + +int platch_respond_error_json(FlutterPlatformMessageResponseHandle *handle, + char *error_code, + char *error_msg, + struct json_value *error_details); + +int platch_respond_illegal_arg_json(FlutterPlatformMessageResponseHandle *handle, + char *error_msg); + +int platch_respond_native_error_json(FlutterPlatformMessageResponseHandle *handle, + int _errno); -/// Tells flutter that the method that was called caused an error. -/// errorcode MUST be non-null, errormessage can be null. -/// when codec is one of kStandardMethodCall, kStandardMethodCallResponse, kStandardMessageCodec: -/// errordetails must either be NULL or a pointer to a struct StdMsgCodecValue. -/// when codec is one of kJSONMethodCall, kJSONMethodCallResponse or kJSONMessageCodec: -/// errordetails must either be NULL or a pointer to a struct JSONMsgCodecValue. -int PlatformChannel_respondError(FlutterPlatformMessageResponseHandle *handle, enum ChannelCodec codec, char *errorcode, char *errormessage, void *errordetails); +/// Sends a success event with value `event_value` to an event channel +/// that uses the standard method codec. +int platch_send_success_event_std(char *channel, + struct std_value *event_value); + +/// Sends an error event to an event channel that uses the standard method codec. +int platch_send_error_event_std(char *channel, + char *error_code, + char *error_msg, + struct std_value *error_details); +/// Sends a success event with value `event_value` to an event channel +/// that uses the JSON method codec. +int platch_send_success_event_json(char *channel, + struct json_value *event_value); + +/// Sends an error event to an event channel that uses the JSON method codec. +int platch_send_error_event_json(char *channel, + char *error_code, + char *error_msg, + struct json_value *error_details); /// frees a ChannelObject that was decoded using PlatformChannel_decode. /// not freeing ChannelObjects may result in a memory leak. -int PlatformChannel_free(struct ChannelObject *object); +int platch_free_obj(struct platch_obj *object); -int PlatformChannel_freeJSONMsgCodecValue(struct JSONMsgCodecValue *value, bool shallow); +int platch_free_json_value(struct json_value *value, bool shallow); /// returns true if values a and b are equal. /// for JS arrays, the order of the values is relevant /// (so two arrays are only equal if the same values appear in exactly same order) /// for objects, the order of the entries is irrelevant. -bool jsvalue_equals(struct JSONMsgCodecValue *a, struct JSONMsgCodecValue *b); +bool jsvalue_equals(struct json_value *a, struct json_value *b); /// given a JS object as an argument, it searches for an entry with key "key" /// and returns the value associated with it. /// if the key is not found, returns NULL. -struct JSONMsgCodecValue *jsobject_get(struct JSONMsgCodecValue *object, char *key); +struct json_value *jsobject_get(struct json_value *object, char *key); /// StdMsgCodecValue equivalent of jsvalue_equals. /// again, for lists, the order of values is relevant /// for maps, it's not. -bool stdvalue_equals(struct StdMsgCodecValue *a, struct StdMsgCodecValue *b); +bool stdvalue_equals(struct std_value *a, struct std_value *b); /// StdMsgCodecValue equivalent of jsobject_get, just that the key can be /// any arbitrary StdMsgCodecValue (and must not be a string as for jsobject_get) -struct StdMsgCodecValue *stdmap_get(struct StdMsgCodecValue *map, struct StdMsgCodecValue *key); +struct std_value *stdmap_get(struct std_value *map, struct std_value *key); + +struct std_value *stdmap_get_str(struct std_value *map, char *key); -/// Andrew: Definitions -static inline int __alignmentDiff(uint32_t value, int alignment) { - alignment--; - return value - (((((uint32_t) value) + alignment) | alignment) - alignment); +static inline int _advance(uintptr_t *value, int n_bytes, size_t *remaining) { + if (remaining != NULL) { + if (*remaining < n_bytes) return EBADMSG; + *remaining -= n_bytes; + } + + *value += n_bytes; + return 0; } -static inline void __align(uint32_t *value, int alignment, size_t *remaining) { - if (remaining != NULL) - *remaining -= alignmentDiff((uint32_t) *value, alignment); - alignment--; +static inline int _align(uintptr_t *value, int alignment, size_t *remaining) { + int diff; + + alignment--; + diff = ((((*value) + alignment) | alignment) - alignment) - *value; - *value = (uint32_t) (((*value + alignment) | alignment) - alignment); + return _advance(value, diff, remaining); } -static inline void __advance(uint32_t *value, int n_bytes, size_t *remaining) { - if (remaining != NULL) - *remaining -= n_bytes; - - *value += n_bytes; +static inline int _advance_size_bytes(uintptr_t *value, size_t size, size_t *remaining) { + if (size < 254) { + return _advance(value, 1, remaining); + } else if (size <= 0xFFFF) { + return _advance(value, 3, remaining); + } else { + return _advance(value, 5, remaining); + } } -static inline void write8(uint8_t **pbuffer, uint8_t value) { + +static inline int _write8(uint8_t **pbuffer, uint8_t value, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 1)) { + return EBADMSG; + } + *(uint8_t*) *pbuffer = value; + + return _advance((uintptr_t*) pbuffer, 1, remaining); } -static inline void write16(uint8_t **pbuffer, uint16_t value) { +static inline int _write16(uint8_t **pbuffer, uint16_t value, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 2)) { + return EBADMSG; + } + *(uint16_t*) *pbuffer = value; + + return _advance((uintptr_t*) pbuffer, 2, remaining); } -static inline void write32(uint8_t **pbuffer, uint32_t value) { +static inline int _write32(uint8_t **pbuffer, uint32_t value, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 4)) { + return EBADMSG; + } + *(uint32_t*) *pbuffer = value; + + return _advance((uintptr_t*) pbuffer, 4, remaining); } -static inline void write64(uint8_t **pbuffer, uint64_t value) { - *(uint64_t*) *pbuffer = value; +static inline int _write64(uint8_t **pbuffer, uint64_t value, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 8)) { + return EBADMSG; + } + + *(uint64_t*) *pbuffer = value; + + return _advance((uintptr_t*) pbuffer, 8, remaining); } -static inline uint8_t read8(uint8_t **pbuffer) { - return *(uint8_t *) *pbuffer; +static inline int _read8(uint8_t **pbuffer, uint8_t* value_out, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 1)) { + return EBADMSG; + } + + *value_out = *(uint8_t *) *pbuffer; + + return _advance((uintptr_t*) pbuffer, 1, remaining); } -static inline uint16_t read16(uint8_t **pbuffer) { - return *(uint16_t *) *pbuffer; +static inline int _read16(uint8_t **pbuffer, uint16_t *value_out, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 2)) { + return EBADMSG; + } + + *value_out = *(uint16_t *) *pbuffer; + + return _advance((uintptr_t*) pbuffer, 2, remaining); } -static inline uint32_t read32(uint8_t **pbuffer) { - return *(int32_t *) *pbuffer; +static inline int _read32(uint8_t **pbuffer, uint32_t *value_out, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 4)) { + return EBADMSG; + } + + *value_out = *(uint32_t *) *pbuffer; + + return _advance((uintptr_t*) pbuffer, 4, remaining); } -static inline uint64_t read64(uint8_t **pbuffer) { - return *(int64_t *) *pbuffer; +static inline int _read64(uint8_t **pbuffer, uint64_t *value_out, size_t *remaining) { + if ((remaining != NULL) && (*remaining < 8)) { + return EBADMSG; + } + + *value_out = *(uint64_t *) *pbuffer; + + return _advance((uintptr_t*) pbuffer, 8, remaining); } -static inline int nSizeBytes(int size) { - return (size < 254) ? 1 : (size <= 0xFFFF) ? 3 : 5; -} -static inline void writeSize(uint8_t **pbuffer, int size) { - if (size < 254) { - write8(pbuffer, (uint8_t) size); - advance(pbuffer, 1); +static inline int _writeSize(uint8_t **pbuffer, int size, size_t *remaining) { + int ok; + + if (size < 254) { + return _write8(pbuffer, (uint8_t) size, remaining); } else if (size <= 0xFFFF) { - write8(pbuffer, 0xFE); - advance(pbuffer, 1); + ok = _write8(pbuffer, 0xFE, remaining); + if (ok != 0) return ok; - write16(pbuffer, (uint16_t) size); - advance(pbuffer, 2); + ok = _write16(pbuffer, (uint16_t) size, remaining); + if (ok != 0) return ok; } else { - write8(pbuffer, 0xFF); - advance(pbuffer, 1); + ok = _write8(pbuffer, 0xFF, remaining); + if (ok != 0) return ok; - write32(pbuffer, (uint32_t) size); - advance(pbuffer, 4); - } + ok = _write32(pbuffer, (uint32_t) size, remaining); + if (ok != 0) return ok; + } } -static inline int readSize(uint8_t **pbuffer, size_t *premaining, uint32_t *psize) { - if (*premaining < 1) return EBADMSG; - - *psize = read8(pbuffer); - advance(pbuffer, 1, premaining); - - if (*psize == 254) { - if (*premaining < 2) return EBADMSG; - - *psize = read16(pbuffer); - advance(pbuffer, 2, premaining); - } else if (*psize == 255) { - if (*premaining < 4) return EBADMSG; - - *psize = read32(pbuffer); - advance(pbuffer, 4, premaining); +static inline int _readSize(uint8_t **pbuffer, uint32_t *psize, size_t *remaining) { + int ok; + uint8_t size8; + uint16_t size16; + + ok = _read8(pbuffer, &size8, remaining); + if (ok != 0) return ok; + + if (size8 <= 253) { + *psize = size8; + + return 0; + } else if (size8 == 254) { + ok = _read16(pbuffer, &size16, remaining); + if (ok != 0) return ok; + + *psize = size16; + return 0; + } else if (size8 == 255) { + return _read32(pbuffer, psize, remaining); } - return 0; + return 0; } -#endif + +#endif \ No newline at end of file diff --git a/include/pluginregistry.h b/include/pluginregistry.h index 67c32132..38e0214a 100644 --- a/include/pluginregistry.h +++ b/include/pluginregistry.h @@ -1,52 +1,54 @@ #ifndef FLUTTER_PI_REGISTRY_H_ #define FLUTTER_PI_REGISTRY_H_ 1 -#include -#include -#include +#include +#include +#include -#include "platformchannel.h" +#include + +#define STREQ(a, b) (strcmp(a, b) == 0) /// Callback for Initialization or Deinitialization. /// Return value is 0 for success, or anything else for an error /// (uses the errno error codes) -typedef int (*InitDeinitCallback)(void); +typedef int (*init_deinit_cb)(void); /// A Callback that gets called when a platform message /// arrives on a channel you registered it with. /// channel is the method channel that received a platform message, /// object is the object that is the result of automatically decoding -/// the platform message using the codec given to PluginRegistry_setReceiver. +/// the platform message using the codec given to plugin_registry_set_receiver. /// BE AWARE that object->type can be kNotImplemented, REGARDLESS of the codec -/// passed to PluginRegistry_setReceiver. -typedef int (*ChannelObjectReceiveCallback)(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle); +/// passed to plugin_registry_set_receiver. +typedef int (*platch_obj_recv_callback)(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle); /// details of a plugin for flutter-pi. /// All plugins are initialized (i.e. get their "init" callbacks called) -/// when PluginRegistry_init() is called by flutter-pi. +/// when plugin_registry_init() is called by flutter-pi. /// In the init callback, you probably want to do stuff like /// register callbacks for some method channels your plugin uses, /// or dynamically allocate memory for your plugin if you need to. -/// PluginRegistry_init() and thus every plugins init is called +/// plugin_registry_init() and thus every plugins init is called /// BEFORE the flutter engine is set up and running. The "engine" /// global may even be NULL at the time "init" is called. Sending flutter messages /// will probably cause the application to crash. /// deinit is also called AFTER the engine is shut down. -struct FlutterPiPlugin { +struct flutterpi_plugin { const char const* name; - InitDeinitCallback init; - InitDeinitCallback deinit; + init_deinit_cb init; + init_deinit_cb deinit; }; -int PluginRegistry_init(); -int PluginRegistry_onPlatformMessage(FlutterPlatformMessage *message); +int plugin_registry_init(); +int plugin_registry_on_platform_message(FlutterPlatformMessage *message); /// Sets the callback that should be called when a platform message arrives on channel "channel", /// and the codec used to automatically decode the platform message. /// Call this method with NULL as the callback parameter to remove the current listener on that channel. -int PluginRegistry_setReceiver(char *channel, enum ChannelCodec codec, ChannelObjectReceiveCallback callback); +int plugin_registry_set_receiver(char *channel, enum platch_codec codec, platch_obj_recv_callback callback); -int PluginRegistry_deinit(); +int plugin_registry_deinit(); #endif \ No newline at end of file diff --git a/src/plugins/elm327plugin.h b/include/plugins/elm327plugin.h similarity index 100% rename from src/plugins/elm327plugin.h rename to include/plugins/elm327plugin.h diff --git a/include/plugins/gpiod.h b/include/plugins/gpiod.h new file mode 100644 index 00000000..ce12b85b --- /dev/null +++ b/include/plugins/gpiod.h @@ -0,0 +1,43 @@ +#ifndef _GPIO_PLUGIN_H +#define _GPIO_PLUGIN_H + +#include + +#ifndef GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE + +# define GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE GPIOD_BIT(2) +# define GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN GPIOD_BIT(3) +# define GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP GPIOD_BIT(4) +enum { + GPIOD_LINE_BIAS_AS_IS = 1, + GPIOD_LINE_BIAS_DISABLE, + GPIOD_LINE_BIAS_PULL_UP, + GPIOD_LINE_BIAS_PULL_DOWN +}; + +#endif + +#define GPIOD_PLUGIN_METHOD_CHANNEL "plugins.flutter.io/gpiod" +#define GPIOD_PLUGIN_EVENT_CHANNEL "plugins.flutter.io/gpiod_events" + +#define GPIO_PLUGIN_MAX_CHIPS 8 + +// a basic macro that loads a symbol from libgpiod, puts it into the libgpiod struct +// and returns the errno if an error ocurred + +#define LOAD_GPIOD_PROC(name) \ + do { \ + libgpiod.name = dlsym(libgpiod.handle, "gpiod_" #name); \ + if (!libgpiod.name) {\ + perror("could not resolve libgpiod procedure gpiod_" #name); \ + return errno; \ + } \ + } while (false) + +#define LOAD_GPIOD_PROC_OPTIONAL(name) \ + libgpiod.name = dlsym(libgpiod.handle, "gpiod_" #name) + +extern int gpiodp_init(void); +extern int gpiodp_deinit(void); + +#endif \ No newline at end of file diff --git a/include/plugins/raw_keyboard.h b/include/plugins/raw_keyboard.h new file mode 100644 index 00000000..b552f9e1 --- /dev/null +++ b/include/plugins/raw_keyboard.h @@ -0,0 +1,13 @@ +#ifndef _KEY_EVENT_H +#define _KEY_EVENT_H + +#define KEY_EVENT_CHANNEL "flutter/keyevent" + +#include + +int rawkb_on_keyevent(glfw_key key, uint32_t scan_code, glfw_key_action action); + +int rawkb_init(void); +int rawkb_deinit(void); + +#endif \ No newline at end of file diff --git a/src/plugins/services-plugin.h b/include/plugins/services.h similarity index 89% rename from src/plugins/services-plugin.h rename to include/plugins/services.h index 99be5867..e5f76a26 100644 --- a/src/plugins/services-plugin.h +++ b/include/plugins/services.h @@ -10,7 +10,7 @@ strcmp(str, "DeviceOrientation.portraitDown") == 0 ? kPortraitDown :\ strcmp(str, "DeviceOrientation.landscapeRight") == 0 ? kLandscapeRight : -1) -int Services_init(void); -int Services_deinit(void); +int services_init(void); +int services_deinit(void); #endif \ No newline at end of file diff --git a/include/plugins/spidev.h b/include/plugins/spidev.h new file mode 100644 index 00000000..9e7d16d3 --- /dev/null +++ b/include/plugins/spidev.h @@ -0,0 +1,12 @@ +#ifndef _SPI_PLUGIN_H +#define _SPI_PLUGIN_H + +#include +#include + +#define SPI_PLUGIN_METHOD_CHANNEL "plugins.flutter.io/flutter_spidev" + +int spidevp_init(void); +int spidevp_deinit(void); + +#endif \ No newline at end of file diff --git a/src/plugins/testplugin.h b/include/plugins/testplugin.h similarity index 78% rename from src/plugins/testplugin.h rename to include/plugins/testplugin.h index 4ca1f0eb..d6d54ad6 100644 --- a/src/plugins/testplugin.h +++ b/include/plugins/testplugin.h @@ -8,7 +8,7 @@ #define TESTPLUGIN_CHANNEL_STD "flutter-pi/teststd" #define TESTPLUGIN_CHANNEL_PING "flutter-pi/ping" -extern int TestPlugin_init(void); -extern int TestPlugin_deinit(void); +extern int testp_init(void); +extern int testp_deinit(void); #endif \ No newline at end of file diff --git a/src/plugins/text_input.h b/include/plugins/text_input.h similarity index 67% rename from src/plugins/text_input.h rename to include/plugins/text_input.h index 01395938..d6e8f849 100644 --- a/src/plugins/text_input.h +++ b/include/plugins/text_input.h @@ -40,26 +40,26 @@ struct text_input_configuration { enum text_input_action input_action; }; -int TextInput_syncEditingState(void); -int TextInput_performAction(enum text_input_action action); -int TextInput_onConnectionClosed(void); +int textin_sync_editing_state(void); +int textin_perform_action(enum text_input_action action); +int textin_on_connection_closed(void); // TextInput model functions (updating the text editing state) -bool TextInput_deleteSelected(void); -bool TextInput_addUtf8Char(char *c); -bool TextInput_backspace(void); -bool TextInput_delete(void); -bool TextInput_moveCursorToBeginning(void); -bool TextInput_moveCursorToEnd(void); -bool TextInput_moveCursorForward(void); -bool TextInput_moveCursorBack(void); +bool textin_delete_selected(void); +bool textin_add_utf8_char(char *c); +bool textin_backspace(void); +bool textin_delete(void); +bool textin_move_cursor_to_beginning(void); +bool textin_move_cursor_to_end(void); +bool textin_move_cursor_forward(void); +bool textin_move_cursor_back(void); // parses the input string as linux terminal input and calls the TextInput model functions // accordingly. -int TextInput_onUtf8Char(char *c); -int TextInput_onKey(glfw_key key); +int textin_on_utf8_char(char *c); +int textin_on_key(glfw_key key); -int TextInput_init(void); -int TextInput_deinit(void); +int textin_init(void); +int textin_deinit(void); #endif \ No newline at end of file diff --git a/src/flutter-pi.c b/src/flutter-pi.c index 3557e700..6ef80608 100644 --- a/src/flutter-pi.c +++ b/src/flutter-pi.c @@ -36,9 +36,9 @@ #include #include #include -#include "plugins/services-plugin.h" -#include "plugins/text_input.h" -#include "plugins/raw_keyboard.h" +//#include +#include +#include char* usage ="\ @@ -440,8 +440,8 @@ void *proc_resolver(void* userdata, const char* name) { } void on_platform_message(const FlutterPlatformMessage* message, void* userdata) { int ok; - if ((ok = PluginRegistry_onPlatformMessage((FlutterPlatformMessage *)message)) != 0) - fprintf(stderr, "PluginRegistry_onPlatformMessage failed: %s\n", strerror(ok)); + if ((ok = plugin_registry_on_platform_message((FlutterPlatformMessage *)message)) != 0) + fprintf(stderr, "plugin_registry_on_platform_message failed: %s\n", strerror(ok)); } void vsync_callback(void* userdata, intptr_t baton) { post_platform_task(&(struct flutterpi_task) { @@ -561,6 +561,30 @@ bool message_loop(void) { .pixel_ratio = pixel_ratio }); + } else if (task->type == kSendPlatformMessage || task->type == kRespondToPlatformMessage) { + if (task->type == kSendPlatformMessage) { + FlutterEngineSendPlatformMessage( + engine, + &(const FlutterPlatformMessage) { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = task->channel, + .message = task->message, + .message_size = task->message_size, + .response_handle = task->responsehandle + } + ); + + free(task->channel); + } else if (task->type == kRespondToPlatformMessage) { + FlutterEngineSendPlatformMessageResponse( + engine, + task->responsehandle, + task->message, + task->message_size + ); + } + + free(task->message); } else if (FlutterEngineRunTask(engine, &task->task) != kSuccess) { fprintf(stderr, "Error running platform task\n"); return false; @@ -598,6 +622,88 @@ void flutter_post_platform_task(FlutterTask task, uint64_t target_time, void* u bool runs_platform_tasks_on_current_thread(void* userdata) { return pthread_equal(pthread_self(), platform_thread_id) != 0; } +int flutterpi_send_platform_message(const char *channel, + const uint8_t *restrict message, + size_t message_size, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct flutterpi_task *task; + int ok; + + if (runs_platform_tasks_on_current_thread(NULL)) { + ok = kSuccess == FlutterEngineSendPlatformMessage( + engine, + &(const FlutterPlatformMessage) { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = channel, + .message = message, + .message_size = message_size, + .response_handle = responsehandle + } + ); + + return ok? 0 : 1; + } else { + task = malloc(sizeof(struct flutterpi_task)); + if (task == NULL) return ENOMEM; + + task->type = kSendPlatformMessage; + task->channel = strdup(channel); + task->responsehandle = responsehandle; + + if (message && message_size) { + task->message_size = message_size; + task->message = memdup(message, message_size); + if (!task->message) { + free(task->channel); + return ENOMEM; + } + } else { + task->message_size = 0; + task->message = 0; + } + + post_platform_task(task); + } + + return 0; +} +int flutterpi_respond_to_platform_message(FlutterPlatformMessageResponseHandle *handle, + const uint8_t *restrict message, + size_t message_size) { + struct flutterpi_task *task; + int ok; + + if (runs_platform_tasks_on_current_thread(NULL)) { + ok = kSuccess == FlutterEngineSendPlatformMessageResponse( + engine, + handle, + message, + message_size + ); + + return ok? 0 : 1; + } else { + task = malloc(sizeof(struct flutterpi_task)); + if (task == NULL) return ENOMEM; + + task->type = kRespondToPlatformMessage; + task->channel = NULL; + task->responsehandle = handle; + + if (message && message_size) { + task->message_size = message_size; + task->message = memdup(message, message_size); + if (!task->message) return ENOMEM; + } else { + task->message_size = 0; + task->message = 0; + } + + post_platform_task(task); + } + + return 0; +} @@ -1096,7 +1202,7 @@ bool init_application(void) { int ok, _errno; printf("Initializing Plugin Registry...\n"); - ok = PluginRegistry_init(); + ok = plugin_registry_init(); if (ok != 0) { fprintf(stderr, "Could not initialize plugin registry: %s\n", strerror(ok)); return false; @@ -1193,7 +1299,7 @@ void destroy_application(void) { engine = NULL; } - if ((ok = PluginRegistry_deinit()) != 0) { + if ((ok = plugin_registry_deinit()) != 0) { fprintf(stderr, "Could not deinitialize plugin registry: %s\n", strerror(ok)); } } @@ -1481,7 +1587,7 @@ void on_evdev_input(fd_set fds, size_t n_ready_fds) { default: action = -1; break; } - RawKeyboard_onKeyEvent(EVDEV_KEY_TO_GLFW_KEY(e->code), 0, action); + rawkb_on_keyevent(EVDEV_KEY_TO_GLFW_KEY(e->code), 0, action); } else if (e->code != BTN_TOUCH || device->is_direct) { if (e->value == 1) device->active_buttons |= FLUTTER_BUTTON_FROM_EVENT_CODE(e->code); else device->active_buttons &= ~FLUTTER_BUTTON_FROM_EVENT_CODE(e->code); @@ -1578,9 +1684,9 @@ void on_console_input(void) { cursor = buffer; while (*cursor) { if (key = console_try_get_key(cursor, &cursor), key != GLFW_KEY_UNKNOWN) { - TextInput_onKey(key); + textin_on_key(key); } else if (c = console_try_get_utf8char(cursor, &cursor), c != NULL) { - TextInput_onUtf8Char(c); + textin_on_utf8_char(c); } else { // neither a char nor a (function) key. we don't know when // we can start parsing the buffer again, so just stop here @@ -1606,7 +1712,7 @@ void *io_loop(void *userdata) { FD_SET(drm.fd, &fds); if (drm.fd + 1 > nfds) nfds = drm.fd + 1; - FD_SET(STDIN_FILENO, &fds); + //FD_SET(STDIN_FILENO, &fds); const fd_set const_fds = fds; diff --git a/src/platformchannel.c b/src/platformchannel.c index d0522f85..c13b312e 100644 --- a/src/platformchannel.c +++ b/src/platformchannel.c @@ -13,31 +13,31 @@ #include -struct ResponseHandlerData { - enum ChannelCodec codec; - PlatformMessageResponseCallback on_response; +struct platch_msg_resp_handler_data { + enum platch_codec codec; + platch_msg_resp_callback on_response; void *userdata; }; -int PlatformChannel_freeStdMsgCodecValue(struct StdMsgCodecValue *value) { +int platch_free_value_std(struct std_value *value) { int ok; switch (value->type) { - case kString: + case kStdString: free(value->string_value); break; - case kList: + case kStdList: for (int i=0; i < value->size; i++) { - ok = PlatformChannel_freeStdMsgCodecValue(&(value->list[i])); + ok = platch_free_value_std(&(value->list[i])); if (ok != 0) return ok; } free(value->list); break; - case kMap: + case kStdMap: for (int i=0; i < value->size; i++) { - ok = PlatformChannel_freeStdMsgCodecValue(&(value->keys[i])); + ok = platch_free_value_std(&(value->keys[i])); if (ok != 0) return ok; - ok = PlatformChannel_freeStdMsgCodecValue(&(value->values[i])); + ok = platch_free_value_std(&(value->values[i])); if (ok != 0) return ok; } free(value->keys); @@ -48,24 +48,24 @@ int PlatformChannel_freeStdMsgCodecValue(struct StdMsgCodecValue *value) { return 0; } -int PlatformChannel_freeJSONMsgCodecValue(struct JSONMsgCodecValue *value, bool shallow) { +int platch_free_json_value(struct json_value *value, bool shallow) { int ok; switch (value->type) { - case kJSArray: + case kJsonArray: if (!shallow) { for (int i = 0; i < value->size; i++) { - ok = PlatformChannel_freeJSONMsgCodecValue(&(value->array[i]), false); + ok = platch_free_json_value(&(value->array[i]), false); if (ok != 0) return ok; } } free(value->array); break; - case kJSObject: + case kJsonObject: if (!shallow) { for (int i = 0; i < value->size; i++) { - ok = PlatformChannel_freeJSONMsgCodecValue(&(value->values[i]), false); + ok = platch_free_json_value(&(value->values[i]), false); if (ok != 0) return ok; } } @@ -78,7 +78,7 @@ int PlatformChannel_freeJSONMsgCodecValue(struct JSONMsgCodecValue *value, bool return 0; } -int PlatformChannel_free(struct ChannelObject *object) { +int platch_free_obj(struct platch_obj *object) { switch (object->codec) { case kStringCodec: free(object->string_value); @@ -86,92 +86,111 @@ int PlatformChannel_free(struct ChannelObject *object) { case kBinaryCodec: break; case kJSONMessageCodec: - PlatformChannel_freeJSONMsgCodecValue(&(object->jsonmsgcodec_value), false); + platch_free_json_value(&(object->json_value), false); break; case kStandardMessageCodec: - PlatformChannel_freeStdMsgCodecValue(&(object->stdmsgcodec_value)); + platch_free_value_std(&(object->std_value)); break; case kStandardMethodCall: free(object->method); - PlatformChannel_freeStdMsgCodecValue(&(object->stdarg)); + platch_free_value_std(&(object->std_arg)); break; case kJSONMethodCall: - PlatformChannel_freeJSONMsgCodecValue(&(object->jsarg), false); + platch_free_json_value(&(object->json_arg), false); + break; + default: + break; } return 0; } -int PlatformChannel_calculateStdMsgCodecValueSize(struct StdMsgCodecValue* value, size_t* psize) { - enum StdMsgCodecValueType type = value->type; - size_t size; +int platch_calc_value_size_std(struct std_value* value, size_t* size_out) { + enum std_value_type type = value->type; + uintptr_t size = (uintptr_t) *size_out; + size_t element_size, sizet_size = 0; int ok; // Type Byte - advance(psize, 1); + _advance(&size, 1, NULL); switch (type) { - case kNull: - case kTrue: - case kFalse: + case kStdNull: + case kStdTrue: + case kStdFalse: break; - case kInt32: - advance(psize, 4); + case kStdInt32: + _advance(&size, 4, NULL); break; - case kInt64: - advance(psize, 8); + case kStdInt64: + _advance(&size, 8, NULL); break; - case kFloat64: - align8 (psize); - advance(psize, 8); + case kStdFloat64: + _align (&size, 8, NULL); + _advance(&size, 8, NULL); break; - case kString: - case kLargeInt: - size = strlen(value->string_value); - advance(psize, size + nSizeBytes(size)); + case kStdString: + case kStdLargeInt: + element_size = strlen(value->string_value); + _advance_size_bytes(&size, element_size, NULL); + _advance(&size, element_size, NULL); break; - case kUInt8Array: - size = value->size; - advance(psize, size + nSizeBytes(size)); + case kStdUInt8Array: + element_size = value->size; + _advance_size_bytes(&size, element_size, NULL); + _advance(&size, element_size, NULL); break; - case kInt32Array: - size = value->size; + case kStdInt32Array: + element_size = value->size; - advance(psize, nSizeBytes(size)); - align4 (psize); - advance(psize, size*4); + _advance_size_bytes(&size, element_size, NULL); + _align (&size, 4, NULL); + _advance(&size, element_size*4, NULL); break; - case kInt64Array: - size = value->size; + case kStdInt64Array: + element_size = value->size; - advance(psize, nSizeBytes(size)); - align8 (psize); - advance(psize, size*8); + _advance_size_bytes(&size, element_size, NULL); + _align (&size, 8, NULL); + _advance(&size, element_size*8, NULL); break; - case kFloat64Array: - size = value->size; + case kStdFloat64Array: + element_size = value->size; - advance(psize, nSizeBytes(size)); - align8 (psize); - advance(psize, size*8); + _advance_size_bytes(&size, element_size, NULL); + _align (&size, 8, NULL); + _advance(&size, element_size*8, NULL); break; - case kList: - size = value->size; + case kStdList: + element_size = value->size; + + _advance_size_bytes(&size, element_size, NULL); + for (int i = 0; ilist[i]), &sizet_size); + if (ok != 0) return ok; + + size = (uintptr_t) sizet_size; + } - advance(psize, nSizeBytes(size)); - for (int i = 0; ilist[i]), psize)) != 0) return ok; - break; - case kMap: - size = value->size; + case kStdMap: + element_size = value->size; - advance(psize, nSizeBytes(size)); - for (int i = 0; ikeys[i]), psize)) != 0) return ok; - if ((ok = PlatformChannel_calculateStdMsgCodecValueSize(&(value->values[i]), psize)) != 0) return ok; + _advance_size_bytes(&size, element_size, NULL); + for (int i = 0; ikeys[i]), &sizet_size); + if (ok != 0) return ok; + + ok = platch_calc_value_size_std(&(value->values[i]), &sizet_size); + if (ok != 0) return ok; + + size = (uintptr_t) sizet_size; } break; @@ -179,98 +198,98 @@ int PlatformChannel_calculateStdMsgCodecValueSize(struct StdMsgCodecValue* value return EINVAL; } + *size_out = (size_t) size; + return 0; } -int PlatformChannel_writeStdMsgCodecValueToBuffer(struct StdMsgCodecValue* value, uint8_t **pbuffer) { +int platch_write_value_to_buffer_std(struct std_value* value, uint8_t **pbuffer) { uint8_t* byteArray; size_t size; int ok; - write8(pbuffer, value->type); - advance(pbuffer, 1); + _write8(pbuffer, value->type, NULL); switch (value->type) { - case kNull: - case kTrue: - case kFalse: - break; - case kInt32: - write32(pbuffer, value->int32_value); - advance(pbuffer, 4); - break; - case kInt64: - write64(pbuffer, value->int64_value); - advance(pbuffer, 8); - break; - case kFloat64: - align8(pbuffer); - write64(pbuffer, *((uint64_t*) &(value->float64_value))); - advance(pbuffer, 8); - break; - case kLargeInt: - case kString: - case kUInt8Array: - if ((value->type == kLargeInt) || (value->type == kString)) { + case kStdNull: + case kStdTrue: + case kStdFalse: + break; + case kStdInt32: + _write32(pbuffer, value->int32_value, NULL); + break; + case kStdInt64: + _write64(pbuffer, value->int64_value, NULL); + break; + case kStdFloat64: + _align ((uintptr_t*) pbuffer, 8, NULL); + _write64(pbuffer, *((uint64_t*) &(value->float64_value)), NULL); + break; + case kStdLargeInt: + case kStdString: + case kStdUInt8Array: + if ((value->type == kStdLargeInt) || (value->type == kStdString)) { size = strlen(value->string_value); byteArray = (uint8_t*) value->string_value; - } else if (value->type == kUInt8Array) { + } else if (value->type == kStdUInt8Array) { size = value->size; byteArray = value->uint8array; } - writeSize(pbuffer, size); + _writeSize(pbuffer, size, NULL); for (int i=0; isize; - writeSize(pbuffer, size); - align4(pbuffer); + _writeSize(pbuffer, size, NULL); + _align ((uintptr_t*) pbuffer, 4, NULL); for (int i=0; iint32array[i]); - advance(pbuffer, 4); + _write32(pbuffer, value->int32array[i], NULL); } break; - case kInt64Array: + case kStdInt64Array: size = value->size; - writeSize(pbuffer, size); - align8(pbuffer); + _writeSize(pbuffer, size, NULL); + _align((uintptr_t*) pbuffer, 8, NULL); for (int i=0; iint64array[i]); - advance(pbuffer, 8); + _write64(pbuffer, value->int64array[i], NULL); } break; - case kFloat64Array: + case kStdFloat64Array: size = value->size; - writeSize(pbuffer, size); - align8(pbuffer); + _writeSize(pbuffer, size, NULL); + _align((uintptr_t*) pbuffer, 8, NULL); for (int i=0; ifloat64array[i]); - advance(pbuffer, 8); + _write64(pbuffer, value->float64array[i], NULL); + _advance((uintptr_t*) pbuffer, 8, NULL); } break; - case kList: + case kStdList: size = value->size; - writeSize(pbuffer, size); - for (int i=0; ilist[i]), pbuffer)) != 0) return ok; - + _writeSize(pbuffer, size, NULL); + for (int i=0; i < size; i++) { + ok = platch_write_value_to_buffer_std(&value->list[i], pbuffer); + if (ok != 0) return ok; + } + break; - case kMap: + case kStdMap: size = value->size; - writeSize(pbuffer, size); + _writeSize(pbuffer, size, NULL); for (int i=0; ikeys[i]), pbuffer)) != 0) return ok; - if ((ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&(value->values[i]), pbuffer)) != 0) return ok; + ok = platch_write_value_to_buffer_std(&value->keys[i], pbuffer); + if (ok != 0) return ok; + + ok = platch_write_value_to_buffer_std(&value->values[i], pbuffer); + if (ok != 0) return ok; } break; default: @@ -279,19 +298,19 @@ int PlatformChannel_writeStdMsgCodecValueToBuffer(struct StdMsgCodecValue* value return 0; } -size_t PlatformChannel_calculateJSONMsgCodecValueSize(struct JSONMsgCodecValue *value) { +size_t platch_calc_value_size_json(struct json_value *value) { size_t size = 0; switch (value->type) { - case kJSNull: - case kJSTrue: + case kJsonNull: + case kJsonTrue: return 4; - case kJSFalse: + case kJsonFalse: return 5; - case kJSNumber: ; + case kJsonNumber: ; char numBuffer[32]; return sprintf(numBuffer, "%g", value->number_value); - case kJSString: + case kJsonString: size = 2; // we need to count how many characters we need to escape. @@ -313,17 +332,17 @@ size_t PlatformChannel_calculateJSONMsgCodecValueSize(struct JSONMsgCodecValue * } return size; - case kJSArray: + case kJsonArray: size += 2; for (int i=0; i < value->size; i++) { - size += PlatformChannel_calculateJSONMsgCodecValueSize(&(value->array[i])); + size += platch_calc_value_size_json(&(value->array[i])); if (i+1 != value->size) size += 1; } return size; - case kJSObject: + case kJsonObject: size += 2; for (int i=0; i < value->size; i++) { - size += strlen(value->keys[i]) + 3 + PlatformChannel_calculateJSONMsgCodecValueSize(&(value->values[i])); + size += strlen(value->keys[i]) + 3 + platch_calc_value_size_json(&(value->values[i])); if (i+1 != value->size) size += 1; } return size; @@ -333,21 +352,21 @@ size_t PlatformChannel_calculateJSONMsgCodecValueSize(struct JSONMsgCodecValue * return 0; } -int PlatformChannel_writeJSONMsgCodecValueToBuffer(struct JSONMsgCodecValue* value, uint8_t **pbuffer) { +int platch_write_value_to_buffer_json(struct json_value* value, uint8_t **pbuffer) { switch (value->type) { - case kJSNull: + case kJsonNull: *pbuffer += sprintf((char*) *pbuffer, "null"); break; - case kJSTrue: + case kJsonTrue: *pbuffer += sprintf((char*) *pbuffer, "true"); break; - case kJSFalse: + case kJsonFalse: *pbuffer += sprintf((char*) *pbuffer, "false"); break; - case kJSNumber: + case kJsonNumber: *pbuffer += sprintf((char*) *pbuffer, "%g", value->number_value); break; - case kJSString: + case kJsonString: *((*pbuffer)++) = '\"'; for (char *s = value->string_value; *s; s++) { @@ -389,19 +408,19 @@ int PlatformChannel_writeJSONMsgCodecValueToBuffer(struct JSONMsgCodecValue* val *((*pbuffer)++) = '\"'; break; - case kJSArray: + case kJsonArray: *pbuffer += sprintf((char*) *pbuffer, "["); for (int i=0; i < value->size; i++) { - PlatformChannel_writeJSONMsgCodecValueToBuffer(&(value->array[i]), pbuffer); + platch_write_value_to_buffer_json(&(value->array[i]), pbuffer); if (i+1 != value->size) *pbuffer += sprintf((char*) *pbuffer, ","); } *pbuffer += sprintf((char*) *pbuffer, "]"); break; - case kJSObject: + case kJsonObject: *pbuffer += sprintf((char*) *pbuffer, "{"); for (int i=0; i < value->size; i++) { *pbuffer += sprintf((char*) *pbuffer, "\"%s\":", value->keys[i]); - PlatformChannel_writeJSONMsgCodecValueToBuffer(&(value->values[i]), pbuffer); + platch_write_value_to_buffer_json(&(value->values[i]), pbuffer); if (i+1 != value->size) *pbuffer += sprintf((char*) *pbuffer, ","); } *pbuffer += sprintf((char*) *pbuffer, "}"); @@ -412,121 +431,145 @@ int PlatformChannel_writeJSONMsgCodecValueToBuffer(struct JSONMsgCodecValue* val return 0; } -int PlatformChannel_decodeStdMsgCodecValue(uint8_t **pbuffer, size_t *premaining, struct StdMsgCodecValue *value_out) { +int platch_decode_value_std(uint8_t **pbuffer, size_t *premaining, struct std_value *value_out) { + enum std_value_type type = 0; int64_t *longArray = 0; int32_t *intArray = 0; - uint8_t *byteArray = 0; + uint8_t *byteArray = 0, type_byte = 0; char *c_string = 0; size_t size = 0; int ok; - enum StdMsgCodecValueType type = read8(pbuffer); - advance(pbuffer, 1, premaining); + ok = _read8(pbuffer, &type_byte, premaining); + if (ok != 0) return ok; + type = type_byte; value_out->type = type; switch (type) { - case kNull: - case kTrue: - case kFalse: + case kStdNull: + case kStdTrue: + case kStdFalse: break; - case kInt32: - if (*premaining < 4) return EBADMSG; - - value_out->int32_value = (int32_t) read32(pbuffer); - advance(pbuffer, 4, premaining); + case kStdInt32: + ok = _read32(pbuffer, &value_out->int32_value, premaining); + if (ok != 0) return ok; break; - case kInt64: - if (*premaining < 8) return EBADMSG; - - value_out->int64_value = (int64_t) read64(pbuffer); - advance(pbuffer, 8, premaining); + case kStdInt64: + ok = _read64(pbuffer, &value_out->int64_value, premaining); + if (ok != 0) return ok; break; - case kFloat64: - if (*premaining < (8 + alignmentDiff(*pbuffer, 8))) return EBADMSG; + case kStdFloat64: + ok = _align((uintptr_t*) pbuffer, 8, premaining); + if (ok != 0) return ok; - align8(pbuffer, premaining); - uint64_t temp = read64(pbuffer); - value_out->float64_value = *((double*) (&temp)); - advance(pbuffer, 8, premaining); + ok = _read64(pbuffer, (uint64_t*) &value_out->float64_value, premaining); + if (ok != 0) return ok; break; - case kLargeInt: - case kString: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; + case kStdLargeInt: + case kStdString: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; if (*premaining < size) return EBADMSG; value_out->string_value = calloc(size+1, sizeof(char)); if (!value_out->string_value) return ENOMEM; + memcpy(value_out->string_value, *pbuffer, size); - advance(pbuffer, size, premaining); + _advance((uintptr_t*) pbuffer, size, premaining); break; - case kUInt8Array: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; + case kStdUInt8Array: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; if (*premaining < size) return EBADMSG; value_out->size = size; value_out->uint8array = *pbuffer; - advance(pbuffer, size, premaining); + + ok = _advance((uintptr_t*) pbuffer, size, premaining); + if (ok != 0) return ok; break; - case kInt32Array: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; - if (*premaining < (size*4 + alignmentDiff(*pbuffer, 4))) return EBADMSG; + case kStdInt32Array: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; + + ok = _align((uintptr_t*) pbuffer, 4, premaining); + if (ok != 0) return ok; + + if (*premaining < size*4) return EBADMSG; - align4(pbuffer, premaining); value_out->size = size; value_out->int32array = (int32_t*) *pbuffer; - advance(pbuffer, size*4, premaining); + + ok = _advance((uintptr_t*) pbuffer, size*4, premaining); + if (ok != 0) return ok; break; - case kInt64Array: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; - if (*premaining < (size*8 + alignmentDiff(*pbuffer, 8))) return EBADMSG; + case kStdInt64Array: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; + + ok = _align((uintptr_t*) pbuffer, 8, premaining); + if (ok != 0) return ok; + + if (*premaining < size*8) return EBADMSG; - align8(pbuffer, premaining); value_out->size = size; value_out->int64array = (int64_t*) *pbuffer; - advance(pbuffer, size*8, premaining); + + ok = _advance((uintptr_t*) pbuffer, size*8, premaining); + if (ok != 0) return ok; break; - case kFloat64Array: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; - if (*premaining < (size*8 + alignmentDiff(*pbuffer, 8))) return EBADMSG; + case kStdFloat64Array: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; + + ok = _align((uintptr_t*) pbuffer, 8, premaining); + if (ok != 0) return ok; + + if (*premaining < size*8) return EBADMSG; - align8(pbuffer, premaining); value_out->size = size; value_out->float64array = (double*) *pbuffer; - advance(pbuffer, size*8, premaining); + ok = _advance((uintptr_t*) pbuffer, size*8, premaining); + if (ok != 0) return ok; + break; - case kList: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; + case kStdList: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; value_out->size = size; - value_out->list = calloc(size, sizeof(struct StdMsgCodecValue)); + value_out->list = calloc(size, sizeof(struct std_value)); for (int i = 0; i < size; i++) { - ok = PlatformChannel_decodeStdMsgCodecValue(pbuffer, premaining, &(value_out->list[i])); + ok = platch_decode_value_std(pbuffer, premaining, &value_out->list[i]); if (ok != 0) return ok; } break; - case kMap: - if ((ok = readSize(pbuffer, premaining, &size)) != 0) return ok; + case kStdMap: + ok = _readSize(pbuffer, &size, premaining); + if (ok != 0) return ok; value_out->size = size; - value_out->keys = calloc(size*2, sizeof(struct StdMsgCodecValue)); + + value_out->keys = calloc(size*2, sizeof(struct std_value)); if (!value_out->keys) return ENOMEM; - value_out->values = &(value_out->keys[size]); + + value_out->values = &value_out->keys[size]; for (int i = 0; i < size; i++) { - ok = PlatformChannel_decodeStdMsgCodecValue(pbuffer, premaining, &(value_out->keys[i])); + ok = platch_decode_value_std(pbuffer, premaining, &(value_out->keys[i])); if (ok != 0) return ok; - ok = PlatformChannel_decodeStdMsgCodecValue(pbuffer, premaining, &(value_out->values[i])); + ok = platch_decode_value_std(pbuffer, premaining, &(value_out->values[i])); if (ok != 0) return ok; } @@ -537,7 +580,7 @@ int PlatformChannel_decodeStdMsgCodecValue(uint8_t **pbuffer, size_t *premaining return 0; } -int PlatformChannel_decodeJSONMsgCodecValue(char *message, size_t size, jsmntok_t **pptoken, size_t *ptokensremaining, struct JSONMsgCodecValue *value_out) { +int platch_decode_value_json(char *message, size_t size, jsmntok_t **pptoken, size_t *ptokensremaining, struct json_value *value_out) { jsmntok_t *ptoken; int result, ok; @@ -557,7 +600,7 @@ int PlatformChannel_decodeJSONMsgCodecValue(char *message, size_t size, jsmntok_ tokensremaining = (size_t) result; ptoken = tokens; - ok = PlatformChannel_decodeJSONMsgCodecValue(message, size, &ptoken, &tokensremaining, value_out); + ok = platch_decode_value_json(message, size, &ptoken, &tokensremaining, value_out); if (ok != 0) return ok; } else { // message is already tokenized @@ -572,13 +615,13 @@ int PlatformChannel_decodeJSONMsgCodecValue(char *message, size_t size, jsmntok_ return EBADMSG; case JSMN_PRIMITIVE: if (message[ptoken->start] == 'n') { - value_out->type = kJSNull; + value_out->type = kJsonNull; } else if (message[ptoken->start] == 't') { - value_out->type = kJSTrue; + value_out->type = kJsonTrue; } else if (message[ptoken->start] == 'f') { - value_out->type = kJSFalse; + value_out->type = kJsonFalse; } else { - value_out->type = kJSNumber; + value_out->type = kJsonNumber; // hacky, but should work in normal circumstances. If the platform message solely consists // of this number and nothing else, this could fail. @@ -595,42 +638,42 @@ int PlatformChannel_decodeJSONMsgCodecValue(char *message, size_t size, jsmntok_ message[ptoken->end] = '\0'; char *string = message + ptoken->start; - value_out->type = kJSString; + value_out->type = kJsonString; value_out->string_value = string; break; case JSMN_ARRAY: ; - struct JSONMsgCodecValue *array = calloc(ptoken->size, sizeof(struct JSONMsgCodecValue)); + struct json_value *array = calloc(ptoken->size, sizeof(struct json_value)); if (!array) return ENOMEM; for (int i=0; i < ptoken->size; i++) { - ok = PlatformChannel_decodeJSONMsgCodecValue(message, size, pptoken, ptokensremaining, &array[i]); + ok = platch_decode_value_json(message, size, pptoken, ptokensremaining, &array[i]); if (ok != 0) return ok; } - value_out->type = kJSArray; + value_out->type = kJsonArray; value_out->size = ptoken->size; value_out->array = array; break; case JSMN_OBJECT: ; - struct JSONMsgCodecValue key; + struct json_value key; char **keys = calloc(ptoken->size, sizeof(char *)); - struct JSONMsgCodecValue *values = calloc(ptoken->size, sizeof(struct JSONMsgCodecValue)); + struct json_value *values = calloc(ptoken->size, sizeof(struct json_value)); if ((!keys) || (!values)) return ENOMEM; for (int i=0; i < ptoken->size; i++) { - ok = PlatformChannel_decodeJSONMsgCodecValue(message, size, pptoken, ptokensremaining, &key); + ok = platch_decode_value_json(message, size, pptoken, ptokensremaining, &key); if (ok != 0) return ok; - if (key.type != kJSString) return EBADMSG; + if (key.type != kJsonString) return EBADMSG; keys[i] = key.string_value; - ok = PlatformChannel_decodeJSONMsgCodecValue(message, size, pptoken, ptokensremaining, &values[i]); + ok = platch_decode_value_json(message, size, pptoken, ptokensremaining, &values[i]); if (ok != 0) return ok; } - value_out->type = kJSObject; + value_out->type = kJsonObject; value_out->size = ptoken->size; value_out->keys = keys; value_out->values = values; @@ -644,12 +687,12 @@ int PlatformChannel_decodeJSONMsgCodecValue(char *message, size_t size, jsmntok_ return 0; } -int PlatformChannel_decodeJSON(char *string, struct JSONMsgCodecValue *out) { - return PlatformChannel_decodeJSONMsgCodecValue(string, strlen(string), NULL, NULL, out); +int platch_decode_json(char *string, struct json_value *out) { + return platch_decode_value_json(string, strlen(string), NULL, NULL, out); } -int PlatformChannel_decode(uint8_t *buffer, size_t size, enum ChannelCodec codec, struct ChannelObject *object_out) { - struct JSONMsgCodecValue root_jsvalue; +int platch_decode(uint8_t *buffer, size_t size, enum platch_codec codec, struct platch_obj *object_out) { + struct json_value root_jsvalue; uint8_t *buffer_cursor = buffer; size_t remaining = size; int ok; @@ -679,90 +722,89 @@ int PlatformChannel_decode(uint8_t *buffer, size_t size, enum ChannelCodec codec break; case kJSONMessageCodec: - ok = PlatformChannel_decodeJSONMsgCodecValue((char *) buffer, size, NULL, NULL, &(object_out->jsonmsgcodec_value)); + ok = platch_decode_value_json((char *) buffer, size, NULL, NULL, &(object_out->json_value)); if (ok != 0) return ok; break; case kJSONMethodCall: ; - ok = PlatformChannel_decodeJSONMsgCodecValue((char *) buffer, size, NULL, NULL, &root_jsvalue); + ok = platch_decode_value_json((char *) buffer, size, NULL, NULL, &root_jsvalue); if (ok != 0) return ok; - if (root_jsvalue.type != kJSObject) return EBADMSG; + if (root_jsvalue.type != kJsonObject) return EBADMSG; for (int i=0; i < root_jsvalue.size; i++) { - if ((strcmp(root_jsvalue.keys[i], "method") == 0) && (root_jsvalue.values[i].type == kJSString)) { + if ((strcmp(root_jsvalue.keys[i], "method") == 0) && (root_jsvalue.values[i].type == kJsonString)) { object_out->method = root_jsvalue.values[i].string_value; } else if (strcmp(root_jsvalue.keys[i], "args") == 0) { - object_out->jsarg = root_jsvalue.values[i]; + object_out->json_arg = root_jsvalue.values[i]; } else return EBADMSG; } - PlatformChannel_freeJSONMsgCodecValue(&root_jsvalue, true); + platch_free_json_value(&root_jsvalue, true); break; case kJSONMethodCallResponse: ; - ok = PlatformChannel_decodeJSONMsgCodecValue((char *) buffer, size, NULL, NULL, &root_jsvalue); + ok = platch_decode_value_json((char *) buffer, size, NULL, NULL, &root_jsvalue); if (ok != 0) return ok; - if (root_jsvalue.type != kJSArray) return EBADMSG; + if (root_jsvalue.type != kJsonArray) return EBADMSG; if (root_jsvalue.size == 1) { object_out->success = true; - object_out->jsresult = root_jsvalue.array[0]; - return PlatformChannel_freeJSONMsgCodecValue(&root_jsvalue, true); + object_out->json_result = root_jsvalue.array[0]; + return platch_free_json_value(&root_jsvalue, true); } else if ((root_jsvalue.size == 3) && - (root_jsvalue.array[0].type == kJSString) && - ((root_jsvalue.array[1].type == kJSString) || (root_jsvalue.array[1].type == kJSNull))) { + (root_jsvalue.array[0].type == kJsonString) && + ((root_jsvalue.array[1].type == kJsonString) || (root_jsvalue.array[1].type == kJsonNull))) { object_out->success = false; - object_out->errorcode = root_jsvalue.array[0].string_value; - object_out->errormessage = root_jsvalue.array[1].string_value; - object_out->jserrordetails = root_jsvalue.array[2]; - return PlatformChannel_freeJSONMsgCodecValue(&root_jsvalue, true); + object_out->error_code = root_jsvalue.array[0].string_value; + object_out->error_msg = root_jsvalue.array[1].string_value; + object_out->json_error_details = root_jsvalue.array[2]; + return platch_free_json_value(&root_jsvalue, true); } else return EBADMSG; break; case kStandardMessageCodec: - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &(object_out->stdmsgcodec_value)); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &object_out->std_value); if (ok != 0) return ok; break; case kStandardMethodCall: ; - struct StdMsgCodecValue methodname; + struct std_value methodname; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &methodname); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &methodname); if (ok != 0) return ok; - if (methodname.type != kString) { - PlatformChannel_freeStdMsgCodecValue(&methodname); - return EPROTO; + if (methodname.type != kStdString) { + platch_free_value_std(&methodname); + return EBADMSG; } object_out->method = methodname.string_value; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &(object_out->stdarg)); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &object_out->std_arg); if (ok != 0) return ok; break; case kStandardMethodCallResponse: ; - object_out->success = read8(&buffer_cursor) == 0; - advance(&buffer_cursor, 1, &remaining); + ok = _read8(&buffer_cursor, (uint8_t*) &object_out->success, &remaining); if (object_out->success) { - struct StdMsgCodecValue result; + struct std_value result; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &(object_out->stdresult)); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &(object_out->std_result)); if (ok != 0) return ok; } else { - struct StdMsgCodecValue errorcode, errormessage; + struct std_value error_code, error_msg; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &errorcode); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &error_code); if (ok != 0) return ok; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &errormessage); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &error_msg); if (ok != 0) return ok; - ok = PlatformChannel_decodeStdMsgCodecValue(&buffer_cursor, &remaining, &(object_out->stderrordetails)); + ok = platch_decode_value_std(&buffer_cursor, &remaining, &(object_out->std_error_details)); if (ok != 0) return ok; - if ((errorcode.type == kString) && ((errormessage.type == kString) || (errormessage.type == kNull))) { - object_out->errorcode = errorcode.string_value; - object_out->errormessage = (errormessage.type == kString) ? errormessage.string_value : NULL; + if ((error_code.type == kStdString) && ((error_msg.type == kStdString) || (error_msg.type == kStdNull))) { + object_out->error_code = error_code.string_value; + object_out->error_msg = (error_msg.type == kStdString) ? error_msg.string_value : NULL; } else { return EBADMSG; } @@ -774,9 +816,9 @@ int PlatformChannel_decode(uint8_t *buffer, size_t size, enum ChannelCodec codec return 0; } -int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, size_t *size_out) { - struct StdMsgCodecValue stdmethod, stderrcode, stderrmessage; - struct JSONMsgCodecValue jsmethod, jserrcode, jserrmessage, jsroot; +int platch_encode(struct platch_obj *object, uint8_t **buffer_out, size_t *size_out) { + struct std_value stdmethod, stderrcode, stderrmessage; + struct json_value jsmethod, jserrcode, jserrmessage, jsroot; uint8_t *buffer, *buffer_cursor; size_t size = 0; int ok = 0; @@ -797,24 +839,24 @@ int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, s *size_out = object->binarydata_size; return 0; case kJSONMessageCodec: - size = PlatformChannel_calculateJSONMsgCodecValueSize(&(object->jsonmsgcodec_value)); + size = platch_calc_value_size_json(&(object->json_value)); size += 1; // JSONMsgCodec uses sprintf, which null-terminates strings, // so lets allocate one more byte for the last null-terminator. // this is decremented again in the second switch-case, so flutter // doesn't complain about a malformed message. break; case kStandardMessageCodec: - ok = PlatformChannel_calculateStdMsgCodecValueSize(&(object->stdmsgcodec_value), &size); + ok = platch_calc_value_size_std(&(object->std_value), &size); if (ok != 0) return ok; break; case kStandardMethodCall: - stdmethod.type = kString; + stdmethod.type = kStdString; stdmethod.string_value = object->method; - ok = PlatformChannel_calculateStdMsgCodecValueSize(&stdmethod, &size); + ok = platch_calc_value_size_std(&stdmethod, &size); if (ok != 0) return ok; - ok = PlatformChannel_calculateStdMsgCodecValueSize(&(object->stdarg), &size); + ok = platch_calc_value_size_std(&(object->std_arg), &size); if (ok != 0) return ok; break; @@ -822,55 +864,55 @@ int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, s size += 1; if (object->success) { - ok = PlatformChannel_calculateStdMsgCodecValueSize(&(object->stdresult), &size); + ok = platch_calc_value_size_std(&(object->std_result), &size); if (ok != 0) return ok; } else { - stderrcode = (struct StdMsgCodecValue) { - .type = kString, - .string_value = object->errorcode + stderrcode = (struct std_value) { + .type = kStdString, + .string_value = object->error_code }; - stderrmessage = (struct StdMsgCodecValue) { - .type = kString, - .string_value = object->errormessage + stderrmessage = (struct std_value) { + .type = kStdString, + .string_value = object->error_msg }; - ok = PlatformChannel_calculateStdMsgCodecValueSize(&stderrcode, &size); + ok = platch_calc_value_size_std(&stderrcode, &size); if (ok != 0) return ok; - ok = PlatformChannel_calculateStdMsgCodecValueSize(&stderrmessage, &size); + ok = platch_calc_value_size_std(&stderrmessage, &size); if (ok != 0) return ok; - ok = PlatformChannel_calculateStdMsgCodecValueSize(&(object->stderrordetails), &size); + ok = platch_calc_value_size_std(&(object->std_error_details), &size); if (ok != 0) return ok; } break; case kJSONMethodCall: - jsroot.type = kJSObject; + jsroot.type = kJsonObject; jsroot.size = 2; jsroot.keys = (char*[]) {"method", "args"}; - jsroot.values = (struct JSONMsgCodecValue[]) { - {.type = kJSString, .string_value = object->method}, - object->jsarg + jsroot.values = (struct json_value[]) { + {.type = kJsonString, .string_value = object->method}, + object->json_arg }; - size = PlatformChannel_calculateJSONMsgCodecValueSize(&jsroot); + size = platch_calc_value_size_json(&jsroot); size += 1; break; case kJSONMethodCallResponse: - jsroot.type = kJSArray; + jsroot.type = kJsonArray; if (object->success) { jsroot.size = 1; - jsroot.array = (struct JSONMsgCodecValue[]) { - object->jsresult + jsroot.array = (struct json_value[]) { + object->json_result }; } else { jsroot.size = 3; - jsroot.array = (struct JSONMsgCodecValue[]) { - {.type = kJSString, .string_value = object->errorcode}, - {.type = (object->errormessage != NULL) ? kJSString : kJSNull, .string_value = object->errormessage}, - object->jserrordetails + jsroot.array = (struct json_value[]) { + {.type = kJsonString, .string_value = object->error_code}, + {.type = (object->error_msg != NULL) ? kJsonString : kJsonNull, .string_value = object->error_msg}, + object->json_error_details }; } - size = PlatformChannel_calculateJSONMsgCodecValueSize(&jsroot); + size = platch_calc_value_size_json(&jsroot); size += 1; break; default: @@ -885,45 +927,43 @@ int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, s memcpy(buffer, object->string_value, size); break; case kStandardMessageCodec: - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&(object->stdmsgcodec_value), &buffer_cursor); + ok = platch_write_value_to_buffer_std(&(object->std_value), &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; break; case kStandardMethodCall: - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&stdmethod, &buffer_cursor); + ok = platch_write_value_to_buffer_std(&stdmethod, &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&(object->stdarg), &buffer_cursor); + ok = platch_write_value_to_buffer_std(&(object->std_arg), &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; break; case kStandardMethodCallResponse: if (object->success) { - write8(&buffer_cursor, 0x00); - advance(&buffer_cursor, 1); + _write8(&buffer_cursor, 0x00, NULL); - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&(object->stdresult), &buffer_cursor); + ok = platch_write_value_to_buffer_std(&(object->std_result), &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; } else { - write8(&buffer_cursor, 0x01); - advance(&buffer_cursor, 1); - - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&stderrcode, &buffer_cursor); + _write8(&buffer_cursor, 0x01, NULL); + + ok = platch_write_value_to_buffer_std(&stderrcode, &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&stderrmessage, &buffer_cursor); + ok = platch_write_value_to_buffer_std(&stderrmessage, &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; - ok = PlatformChannel_writeStdMsgCodecValueToBuffer(&(object->stderrordetails), &buffer_cursor); + ok = platch_write_value_to_buffer_std(&(object->std_error_details), &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; } break; case kJSONMessageCodec: size -= 1; - ok = PlatformChannel_writeJSONMsgCodecValueToBuffer(&(object->jsonmsgcodec_value), &buffer_cursor); + ok = platch_write_value_to_buffer_json(&(object->json_value), &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; break; case kJSONMethodCall: ; size -= 1; - ok = PlatformChannel_writeJSONMsgCodecValueToBuffer(&jsroot, &buffer_cursor); + ok = platch_write_value_to_buffer_json(&jsroot, &buffer_cursor); if (ok != 0) goto free_buffer_and_return_ok; break; default: @@ -939,13 +979,13 @@ int PlatformChannel_encode(struct ChannelObject *object, uint8_t **buffer_out, s return ok; } -void PlatformChannel_internalOnResponse(const uint8_t *buffer, size_t size, void *userdata) { - struct ResponseHandlerData *handlerdata; - struct ChannelObject object; +void platch_on_response_internal(const uint8_t *buffer, size_t size, void *userdata) { + struct platch_msg_resp_handler_data *handlerdata; + struct platch_obj object; int ok; - handlerdata = (struct ResponseHandlerData *) userdata; - ok = PlatformChannel_decode((uint8_t*) buffer, size, handlerdata->codec, &object); + handlerdata = (struct platch_msg_resp_handler_data *) userdata; + ok = platch_decode((uint8_t*) buffer, size, handlerdata->codec, &object); if (ok != 0) return; ok = handlerdata->on_response(&object, handlerdata->userdata); @@ -953,29 +993,30 @@ void PlatformChannel_internalOnResponse(const uint8_t *buffer, size_t size, void free(handlerdata); - ok = PlatformChannel_free(&object); + ok = platch_free_obj(&object); if (ok != 0) return; } -int PlatformChannel_send(char *channel, struct ChannelObject *object, enum ChannelCodec response_codec, PlatformMessageResponseCallback on_response, void *userdata) { - struct ResponseHandlerData *handlerdata = NULL; + +int platch_send(char *channel, struct platch_obj *object, enum platch_codec response_codec, platch_msg_resp_callback on_response, void *userdata) { + struct platch_msg_resp_handler_data *handlerdata = NULL; FlutterPlatformMessageResponseHandle *response_handle = NULL; FlutterEngineResult result; uint8_t *buffer; size_t size; int ok; - ok = PlatformChannel_encode(object, &buffer, &size); + ok = platch_encode(object, &buffer, &size); if (ok != 0) return ok; if (on_response) { - handlerdata = malloc(sizeof(struct ResponseHandlerData)); + handlerdata = malloc(sizeof(struct platch_msg_resp_handler_data)); if (!handlerdata) return ENOMEM; handlerdata->codec = response_codec; handlerdata->on_response = on_response; handlerdata->userdata = userdata; - result = FlutterPlatformMessageCreateResponseHandle(engine, PlatformChannel_internalOnResponse, handlerdata, &response_handle); + result = FlutterPlatformMessageCreateResponseHandle(engine, platch_on_response_internal, handlerdata, &response_handle); if (result != kSuccess) return EINVAL; } @@ -1007,33 +1048,36 @@ int PlatformChannel_send(char *channel, struct ChannelObject *object, enum Chann return (result == kSuccess) ? 0 : EINVAL; } -int PlatformChannel_stdcall(char *channel, char *method, struct StdMsgCodecValue *argument, PlatformMessageResponseCallback on_response, void *userdata) { - struct ChannelObject object = { + +int platch_call_std(char *channel, char *method, struct std_value *argument, platch_msg_resp_callback on_response, void *userdata) { + struct platch_obj object = { .codec = kStandardMethodCall, .method = method, - .stdarg = *argument + .std_arg = *argument }; - return PlatformChannel_send(channel, &object, kStandardMethodCallResponse, on_response, userdata); + return platch_send(channel, &object, kStandardMethodCallResponse, on_response, userdata); } -int PlatformChannel_jsoncall(char *channel, char *method, struct JSONMsgCodecValue *argument, PlatformMessageResponseCallback on_response, void *userdata) { - return PlatformChannel_send(channel, - &(struct ChannelObject) { + +int platch_call_json(char *channel, char *method, struct json_value *argument, platch_msg_resp_callback on_response, void *userdata) { + return platch_send(channel, + &(struct platch_obj) { .codec = kJSONMethodCall, .method = method, - .jsarg = *argument + .json_arg = *argument }, kJSONMethodCallResponse, on_response, userdata); } -int PlatformChannel_respond(FlutterPlatformMessageResponseHandle *handle, struct ChannelObject *response) { + +int platch_respond(FlutterPlatformMessageResponseHandle *handle, struct platch_obj *response) { FlutterEngineResult result; uint8_t *buffer = NULL; size_t size = 0; int ok; - ok = PlatformChannel_encode(response, &buffer, &size); + ok = platch_encode(response, &buffer, &size); if (ok != 0) return ok; result = FlutterEngineSendPlatformMessageResponse(engine, (const FlutterPlatformMessageResponseHandle*) handle, (const uint8_t*) buffer, size); @@ -1042,61 +1086,188 @@ int PlatformChannel_respond(FlutterPlatformMessageResponseHandle *handle, struct return (result == kSuccess) ? 0 : EINVAL; } -int PlatformChannel_respondNotImplemented(FlutterPlatformMessageResponseHandle *handle) { - return PlatformChannel_respond( + +int platch_respond_not_implemented(FlutterPlatformMessageResponseHandle *handle) { + return platch_respond( (FlutterPlatformMessageResponseHandle *) handle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kNotImplemented }); } -int PlatformChannel_respondError(FlutterPlatformMessageResponseHandle *handle, enum ChannelCodec codec, char *errorcode, char *errormessage, void *errordetails) { - if ((codec == kStandardMessageCodec) || (codec == kStandardMethodCall) || (codec == kStandardMethodCallResponse)) { - if (errordetails == NULL) - errordetails = &(struct StdMsgCodecValue) {.type = kNull}; - - return PlatformChannel_respond(handle, &(struct ChannelObject) { + + +int platch_respond_success_std(FlutterPlatformMessageResponseHandle *handle, + struct std_value *return_value) { + return platch_respond( + handle, + &(struct platch_obj) { + .codec = kStandardMethodCallResponse, + .success = true, + .std_result = return_value? *return_value : STDNULL + } + ); +} + +int platch_respond_error_std(FlutterPlatformMessageResponseHandle *handle, + char *error_code, + char *error_msg, + struct std_value *error_details) { + return platch_respond(handle, &(struct platch_obj) { + .codec = kStandardMethodCallResponse, + .success = false, + .error_code = error_code, + .error_msg = error_msg, + .std_error_details = error_details? *error_details : STDNULL + }); +} + +/// Sends a platform message to `handle` with error code "illegalargument" +/// and error message `errmsg`. +int platch_respond_illegal_arg_std(FlutterPlatformMessageResponseHandle *handle, + char *error_msg) { + return platch_respond_error_std(handle, "illegalargument", error_msg, NULL); +} + +/// Sends a platform message to `handle` with error code "nativeerror" +/// and error messsage `strerror(_errno)` +int platch_respond_native_error_std(FlutterPlatformMessageResponseHandle *handle, + int _errno) { + return platch_respond_error_std( + handle, + "nativeerror", + strerror(_errno), + &STDINT32(_errno) + ); +} + + +int platch_respond_success_json(FlutterPlatformMessageResponseHandle *handle, + struct json_value *return_value) { + return platch_respond( + handle, + &(struct platch_obj) { + .codec = kJSONMethodCallResponse, + .success = true, + .json_result = return_value? *return_value + : (struct json_value) {.type = kJsonNull} + } + ); +} + +int platch_respond_error_json(FlutterPlatformMessageResponseHandle *handle, + char *error_code, + char *error_msg, + struct json_value *error_details) { + return platch_respond(handle, &(struct platch_obj) { + .codec = kJSONMethodCallResponse, + .success = false, + .error_code = error_code, + .error_msg = error_msg, + .json_error_details = (error_details) ? + *error_details : + (struct json_value) {.type = kJsonNull} + }); +} + +int platch_respond_illegal_arg_json(FlutterPlatformMessageResponseHandle *handle, + char *error_msg) { + return platch_respond_error_json(handle, "illegalargument", error_msg, NULL); +} + +int platch_respond_native_error_json(FlutterPlatformMessageResponseHandle *handle, + int _errno) { + return platch_respond_error_json( + handle, + "nativeerror", + strerror(_errno), + &(struct json_value) {.type = kJsonNumber, .number_value = _errno} + ); +} + + +int platch_send_success_event_std(char *channel, struct std_value *event_value) { + return platch_send( + channel, + &(struct platch_obj) { + .codec = kStandardMethodCallResponse, + .success = true, + .std_result = event_value? *event_value : STDNULL + }, + 0, NULL, NULL + ); +} + +int platch_send_error_event_std(char *channel, + char *error_code, + char *error_msg, + struct std_value *error_details) { + return platch_send( + channel, + &(struct platch_obj) { .codec = kStandardMethodCallResponse, .success = false, - .errorcode = errorcode, - .errormessage = errormessage, - .stderrordetails = *((struct StdMsgCodecValue *) errordetails) - }); - } else if ((codec == kJSONMessageCodec) || (codec == kJSONMethodCall) || (codec == kJSONMethodCallResponse)) { - if (errordetails == NULL) - errordetails = &(struct JSONMsgCodecValue) {.type = kJSNull}; - - return PlatformChannel_respond(handle, &(struct ChannelObject) { + .error_code = error_code, + .error_msg = error_msg, + .std_error_details = error_details? *error_details : STDNULL + }, + 0, NULL, NULL + ); +} + + +int platch_send_success_event_json(char *channel, struct json_value *event_value) { + return platch_send(channel, + &(struct platch_obj) { + .codec = kJSONMethodCallResponse, + .success = true, + .json_result = event_value? *event_value : (struct json_value) {.type = kJsonNull} + }, + 0, NULL, NULL + ); +} + +int platch_send_error_event_json(char *channel, + char *error_code, + char *error_msg, + struct json_value *error_details) { + return platch_send( + channel, + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = false, - .errorcode = errorcode, - .errormessage = errormessage, - .jserrordetails = *((struct JSONMsgCodecValue *) errordetails) - }); - } else return EINVAL; + .error_code = error_code, + .error_msg = error_msg, + .json_error_details = error_details? + *error_details : + (struct json_value) {.type = kJsonNull} + }, + 0, NULL, NULL + ); } -bool jsvalue_equals(struct JSONMsgCodecValue *a, struct JSONMsgCodecValue *b) { + +bool jsvalue_equals(struct json_value *a, struct json_value *b) { if (a == b) return true; if ((a == NULL) ^ (b == NULL)) return false; if (a->type != b->type) return false; switch (a->type) { - case kJSNull: - case kJSTrue: - case kJSFalse: + case kJsonNull: + case kJsonTrue: + case kJsonFalse: return true; - case kJSNumber: + case kJsonNumber: return a->number_value == b->number_value; - case kJSString: + case kJsonString: return strcmp(a->string_value, b->string_value) == 0; - case kJSArray: + case kJsonArray: if (a->size != b->size) return false; if (a->array == b->array) return true; for (int i = 0; i < a->size; i++) if (!jsvalue_equals(&a->array[i], &b->array[i])) return false; return true; - case kJSObject: + case kJsonObject: if (a->size != b->size) return false; if ((a->keys == b->keys) && (a->values == b->values)) return true; @@ -1127,7 +1298,7 @@ bool jsvalue_equals(struct JSONMsgCodecValue *a, struct JSONMsgCodecValue *b) { return true; } } -struct JSONMsgCodecValue *jsobject_get(struct JSONMsgCodecValue *object, char *key) { +struct json_value *jsobject_get(struct json_value *object, char *key) { int i; for (i=0; i < object->size; i++) if (strcmp(object->keys[i], key) == 0) break; @@ -1136,54 +1307,54 @@ struct JSONMsgCodecValue *jsobject_get(struct JSONMsgCodecValue *object, char *k if (i != object->size) return &(object->values[i]); return NULL; } -bool stdvalue_equals(struct StdMsgCodecValue *a, struct StdMsgCodecValue *b) { +bool stdvalue_equals(struct std_value *a, struct std_value *b) { if (a == b) return true; if ((a == NULL) ^ (b == NULL)) return false; if (a->type != b->type) return false; switch (a->type) { - case kNull: - case kTrue: - case kFalse: + case kStdNull: + case kStdTrue: + case kStdFalse: return true; - case kInt32: + case kStdInt32: return a->int32_value == b->int32_value; - case kInt64: + case kStdInt64: return a->int64_value == b->int64_value; - case kLargeInt: - case kString: + case kStdLargeInt: + case kStdString: return strcmp(a->string_value, b->string_value) == 0; - case kFloat64: + case kStdFloat64: return a->float64_value == b->float64_value; - case kUInt8Array: + case kStdUInt8Array: if (a->size != b->size) return false; if (a->uint8array == b->uint8array) return true; for (int i = 0; i < a->size; i++) if (a->uint8array[i] != b->uint8array[i]) return false; return true; - case kInt32Array: + case kStdInt32Array: if (a->size != b->size) return false; if (a->int32array == b->int32array) return true; for (int i = 0; i < a->size; i++) if (a->int32array[i] != b->int32array[i]) return false; return true; - case kInt64Array: + case kStdInt64Array: if (a->size != b->size) return false; if (a->int64array == b->int64array) return true; for (int i = 0; i < a->size; i++) if (a->int64array[i] != b->int64array[i]) return false; return true; - case kFloat64Array: + case kStdFloat64Array: if (a->size != b->size) return false; if (a->float64array == b->float64array) return true; for (int i = 0; i < a->size; i++) if (a->float64array[i] != b->float64array[i]) return false; return true; - case kList: + case kStdList: // the order of list elements is important if (a->size != b->size) return false; if (a->list == b->list) return true; @@ -1193,7 +1364,7 @@ bool stdvalue_equals(struct StdMsgCodecValue *a, struct StdMsgCodecValue *b) { return false; return true; - case kMap: { + case kStdMap: { // the order is not important here, which makes it a bit difficult to compare if (a->size != b->size) return false; if ((a->keys == b->keys) && (a->values == b->values)) return true; @@ -1206,7 +1377,7 @@ bool stdvalue_equals(struct StdMsgCodecValue *a, struct StdMsgCodecValue *b) { for (int i = 0; i < a->size; i++) { // The key we're searching for in b. - struct StdMsgCodecValue *key = &(a->keys[i]); + struct std_value *key = &(a->keys[i]); int j = 0; while (j < a->size) { @@ -1232,10 +1403,13 @@ bool stdvalue_equals(struct StdMsgCodecValue *a, struct StdMsgCodecValue *b) { return false; } -struct StdMsgCodecValue *stdmap_get(struct StdMsgCodecValue *map, struct StdMsgCodecValue *key) { +struct std_value *stdmap_get(struct std_value *map, struct std_value *key) { for (int i=0; i < map->size; i++) if (stdvalue_equals(&map->keys[i], key)) return &map->values[i]; return NULL; } +struct std_value *stdmap_get_str(struct std_value *map, char *key) { + return stdmap_get(map, &(struct std_value) {.type = kStdString, .string_value = key}); +} \ No newline at end of file diff --git a/src/pluginregistry.c b/src/pluginregistry.c index 806dca81..7c4f0faa 100644 --- a/src/pluginregistry.c +++ b/src/pluginregistry.c @@ -1,61 +1,87 @@ -#include +#include #include +#include #include #include -// hardcoded plugin headers -#include "plugins/services-plugin.h" -#include "plugins/text_input.h" -#include "plugins/raw_keyboard.h" +#include +#include + +#ifdef BUILD_TEXT_INPUT_PLUGIN +# include +#endif #ifdef BUILD_TEST_PLUGIN -#include "plugins/testplugin.h" +# include #endif #ifdef BUILD_ELM327_PLUGIN -#include "plugins/elm327plugin.h" +# include +#endif +#ifdef BUILD_GPIOD_PLUGIN +# include +#endif +#ifdef BUILD_SPIDEV_PLUGIN +# include #endif - -struct ChannelObjectReceiverData { +struct platch_obj_recv_data { char *channel; - enum ChannelCodec codec; - ChannelObjectReceiveCallback callback; + enum platch_codec codec; + platch_obj_recv_callback callback; }; struct { - struct FlutterPiPlugin *plugins; + struct flutterpi_plugin *plugins; size_t plugin_count; - struct ChannelObjectReceiverData *callbacks; - size_t callbacks_size; + + // platch_obj callbacks + struct platch_obj_recv_data *platch_obj_cbs; + size_t platch_obj_cbs_size; + } pluginregistry; /// array of plugins that are statically included in flutter-pi. -struct FlutterPiPlugin hardcoded_plugins[] = { - {.name = "services", .init = Services_init, .deinit = Services_deinit}, - {.name = "text_input", .init = TextInput_init, .deinit = TextInput_deinit}, - {.name = "raw_keyboard", .init = RawKeyboard_init, .deinit = RawKeyboard_deinit}, +struct flutterpi_plugin hardcoded_plugins[] = { + {.name = "services", .init = services_init, .deinit = services_deinit}, + {.name = "raw_keyboard", .init = rawkb_init, .deinit = rawkb_deinit}, + +#ifdef BUILD_TEXT_INPUT_PLUGIN + {.name = "text_input", .init = textin_init, .deinit = textin_deinit}, +#endif #ifdef BUILD_TEST_PLUGIN - {.name = "testplugin", .init = TestPlugin_init, .deinit = TestPlugin_deinit}, + {.name = "testplugin", .init = testp_init, .deinit = testp_deinit}, #endif #ifdef BUILD_ELM327_PLUGIN {.name = "elm327plugin", .init = ELM327Plugin_init, .deinit = ELM327Plugin_deinit}, #endif + +#ifdef BUILD_GPIOD_PLUGIN + {.name = "flutter_gpiod", .init = gpiodp_init, .deinit = gpiodp_deinit}, +#endif + +#ifdef BUILD_SPIDEV_PLUGIN + {.name = "flutter_spidev", .init = spidevp_init, .deinit = spidevp_deinit} +#endif }; -//size_t hardcoded_plugins_count; -int PluginRegistry_init() { +int plugin_registry_init() { int ok; memset(&pluginregistry, 0, sizeof(pluginregistry)); - pluginregistry.callbacks_size = 20; - pluginregistry.callbacks = calloc(pluginregistry.callbacks_size, sizeof(struct ChannelObjectReceiverData)); + pluginregistry.platch_obj_cbs_size = 20; + pluginregistry.platch_obj_cbs = calloc(pluginregistry.platch_obj_cbs_size, sizeof(struct platch_obj_recv_data)); + + if (!pluginregistry.platch_obj_cbs) { + fprintf(stderr, "[plugin-registry] Could not allocate memory for platform channel message callbacks.\n"); + return ENOMEM; + } pluginregistry.plugins = hardcoded_plugins; - pluginregistry.plugin_count = sizeof(hardcoded_plugins) / sizeof(struct FlutterPiPlugin); + pluginregistry.plugin_count = sizeof(hardcoded_plugins) / sizeof(struct flutterpi_plugin); // insert code for dynamically loading plugins here @@ -69,18 +95,18 @@ int PluginRegistry_init() { return 0; } -int PluginRegistry_onPlatformMessage(FlutterPlatformMessage *message) { - struct ChannelObject object; +int plugin_registry_on_platform_message(FlutterPlatformMessage *message) { + struct platch_obj object; int ok; - for (int i = 0; i < pluginregistry.callbacks_size; i++) { - if ((pluginregistry.callbacks[i].callback) && (strcmp(pluginregistry.callbacks[i].channel, message->channel) == 0)) { - ok = PlatformChannel_decode((uint8_t*) message->message, message->message_size, pluginregistry.callbacks[i].codec, &object); + for (int i = 0; i < pluginregistry.platch_obj_cbs_size; i++) { + if ((pluginregistry.platch_obj_cbs[i].callback) && (strcmp(pluginregistry.platch_obj_cbs[i].channel, message->channel) == 0)) { + ok = platch_decode((uint8_t*) message->message, message->message_size, pluginregistry.platch_obj_cbs[i].codec, &object); if (ok != 0) return ok; - pluginregistry.callbacks[i].callback((char*) message->channel, &object, (FlutterPlatformMessageResponseHandle*) message->response_handle); + pluginregistry.platch_obj_cbs[i].callback((char*) message->channel, &object, (FlutterPlatformMessageResponseHandle*) message->response_handle); - PlatformChannel_free(&object); + platch_free_obj(&object); return 0; } } @@ -89,19 +115,19 @@ int PluginRegistry_onPlatformMessage(FlutterPlatformMessage *message) { // just respond with a null buffer to tell the VM-side // that the feature is not implemented. - return PlatformChannel_respondNotImplemented((FlutterPlatformMessageResponseHandle *) message->response_handle); + return platch_respond_not_implemented((FlutterPlatformMessageResponseHandle *) message->response_handle); } -int PluginRegistry_setReceiver(char *channel, enum ChannelCodec codec, ChannelObjectReceiveCallback callback) { - /// the index in 'callback' of the ChannelObjectReceiverData that will be added / updated. +int plugin_registry_set_receiver(char *channel, enum platch_codec codec, platch_obj_recv_callback callback) { + /// the index in 'callback' of the platch_obj_recv_data that will be added / updated. int index = -1; /// find the index with channel name 'channel', or else, the first unoccupied index. - for (int i = 0; i < pluginregistry.callbacks_size; i++) { - if (pluginregistry.callbacks[i].channel == NULL) { + for (int i = 0; i < pluginregistry.platch_obj_cbs_size; i++) { + if (pluginregistry.platch_obj_cbs[i].channel == NULL) { if (index == -1) { index = i; } - } else if (strcmp(channel, pluginregistry.callbacks[i].channel) == 0) { + } else if (strcmp(channel, pluginregistry.platch_obj_cbs[i].channel) == 0) { index = i; break; } @@ -112,13 +138,13 @@ int PluginRegistry_setReceiver(char *channel, enum ChannelCodec codec, ChannelOb if (!callback) return 0; /// expand array - size_t currentsize = pluginregistry.callbacks_size * sizeof(struct ChannelObjectReceiverData); + size_t currentsize = pluginregistry.platch_obj_cbs_size * sizeof(struct platch_obj_recv_data); - pluginregistry.callbacks = realloc(pluginregistry.callbacks, 2 * currentsize); - memset(&pluginregistry.callbacks[pluginregistry.callbacks_size], currentsize, 0); + pluginregistry.platch_obj_cbs = realloc(pluginregistry.platch_obj_cbs, 2 * currentsize); + memset(&pluginregistry.platch_obj_cbs[pluginregistry.platch_obj_cbs_size], currentsize, 0); - index = pluginregistry.callbacks_size; - pluginregistry.callbacks_size = 2*pluginregistry.callbacks_size; + index = pluginregistry.platch_obj_cbs_size; + pluginregistry.platch_obj_cbs_size = 2*pluginregistry.platch_obj_cbs_size; } if (callback) { @@ -126,19 +152,19 @@ int PluginRegistry_setReceiver(char *channel, enum ChannelCodec codec, ChannelOb if (!channelCopy) return ENOMEM; strcpy(channelCopy, channel); - pluginregistry.callbacks[index].channel = channelCopy; - pluginregistry.callbacks[index].codec = codec; - pluginregistry.callbacks[index].callback = callback; - } else if (pluginregistry.callbacks[index].callback) { - free(pluginregistry.callbacks[index].channel); - pluginregistry.callbacks[index].channel = NULL; - pluginregistry.callbacks[index].callback = NULL; + pluginregistry.platch_obj_cbs[index].channel = channelCopy; + pluginregistry.platch_obj_cbs[index].codec = codec; + pluginregistry.platch_obj_cbs[index].callback = callback; + } else if (pluginregistry.platch_obj_cbs[index].callback) { + free(pluginregistry.platch_obj_cbs[index].channel); + pluginregistry.platch_obj_cbs[index].channel = NULL; + pluginregistry.platch_obj_cbs[index].callback = NULL; } return 0; } -int PluginRegistry_deinit() { +int plugin_registry_deinit() { int i, ok; /// call each plugins 'deinit' @@ -150,11 +176,11 @@ int PluginRegistry_deinit() { } /// free all the channel names from the callback list. - for (int i=0; i < pluginregistry.callbacks_size; i++) { - if (pluginregistry.callbacks[i].channel) - free(pluginregistry.callbacks[i].channel); + for (int i=0; i < pluginregistry.platch_obj_cbs_size; i++) { + if (pluginregistry.platch_obj_cbs[i].channel) + free(pluginregistry.platch_obj_cbs[i].channel); } /// free the rest - free(pluginregistry.callbacks); + free(pluginregistry.platch_obj_cbs); } diff --git a/src/plugins/elm327plugin.c b/src/plugins/elm327plugin.c index ff16047c..4eb683b3 100644 --- a/src/plugins/elm327plugin.c +++ b/src/plugins/elm327plugin.c @@ -17,7 +17,7 @@ #include #include -#include "elm327plugin.h" +#include struct elm327 elm; @@ -484,11 +484,11 @@ void *run_pidqq_processor(void* arg) { *****************/ void ELM327Plugin_onPidQueryCompletion(struct pidqq_element query, uint32_t result, enum elm327plugin_errno elm_errno) { // channel object that will be returned to flutter. - struct ChannelObject obj = { + struct platch_obj obj = { .codec = kStandardMethodCallResponse, .success = true, - .stdresult = { - .type = kFloat64, + .std_result = { + .type = kStdFloat64, .float64_value = 0.0 } }; @@ -498,40 +498,40 @@ void ELM327Plugin_onPidQueryCompletion(struct pidqq_element query, uint32_t resu switch (pid) { case OBDII_PID_ENGINE_RPM: - obj.stdresult.float64_value = (double) result / (double) 4.0; + obj.std_result.float64_value = (double) result / (double) 4.0; break; case OBDII_PID_ENGINE_LOAD: case OBDII_PID_THROTTLE_POSITION: - obj.stdresult.float64_value = result * 100.0 / 255.0; + obj.std_result.float64_value = result * 100.0 / 255.0; break; case OBDII_PID_ENGINE_COOLANT_TEMP: case OBDII_PID_INTAKE_AIR_TEMP: - obj.stdresult.type = kInt32; - obj.stdresult.int32_value = (int32_t) result - 40; + obj.std_result.type = kStdInt32; + obj.std_result.int32_value = (int32_t) result - 40; break; case OBDII_PID_VEHICLE_SPEED: - obj.stdresult.type = kInt32; - obj.stdresult.int32_value = (int32_t) result; + obj.std_result.type = kStdInt32; + obj.std_result.int32_value = (int32_t) result; break; default: break; } } else { obj.success = false; - obj.errorcode = "queryfailed"; - obj.errormessage = "ELM327 PID query failed. Reason could be a timeout on the connection between Pi and ELM or between ELM and ECU, or something else."; - obj.stderrordetails.type = kNull; + obj.error_code = "queryfailed"; + obj.error_msg = "ELM327 PID query failed. Reason could be a timeout on the connection between Pi and ELM or between ELM and ECU, or something else."; + obj.std_error_details.type = kStdNull; } - PlatformChannel_send(query.channel, &obj, kBinaryCodec, NULL, NULL); + platch_send(query.channel, &obj, kBinaryCodec, NULL, NULL); } int ELM327Plugin_onEventChannelListen(char *channel, uint8_t pid, FlutterPlatformMessageResponseHandle *handle) { printf("[elm327plugin] listener registered on event channel %s with pid 0x%02X\n", channel, pid); // check if pid is supported, if not, respond with an error envelope if (!elm_pid_supported(pid)) { - return PlatformChannel_respondError( - handle, kStandardMethodCallResponse, + return platch_respond_error_std( + handle, "notsupported", "The vehicle doesn't support the PID used for this channel.", NULL @@ -559,10 +559,10 @@ int ELM327Plugin_onEventChannelListen(char *channel, uint8_t pid, FlutterPlatfor pthread_cond_signal(&pidqq_added); // respond with null - return PlatformChannel_respond(handle, &(struct ChannelObject) { + return platch_respond(handle, &(struct platch_obj) { .codec = kStandardMethodCallResponse, .success = true, - .stdresult = {.type = kNull} + .std_result = {.type = kStdNull} }); } int ELM327Plugin_onEventChannelCancel(char *channel, uint8_t pid, FlutterPlatformMessageResponseHandle *handle) { @@ -574,13 +574,13 @@ int ELM327Plugin_onEventChannelCancel(char *channel, uint8_t pid, FlutterPlatfor pthread_mutex_unlock(&pidqq_lock); // respond with null. - return PlatformChannel_respond(handle, &(struct ChannelObject) { + return platch_respond(handle, &(struct platch_obj) { .codec = kStandardMethodCallResponse, .success = true, - .stdresult = {.type = kNull} + .std_result = {.type = kStdNull} }); } -int ELM327Plugin_onReceive(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *handle) { +int ELM327Plugin_onReceive(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *handle) { bool isListen = false; if ((object->codec == kStandardMethodCall) && ((isListen = (strcmp(object->method, "listen") == 0)) || (strcmp(object->method, "cancel") == 0))) { uint8_t pid = (strcmp(channel, ELM327PLUGIN_RPM_CHANNEL) == 0) ? OBDII_PID_ENGINE_RPM : @@ -597,13 +597,15 @@ int ELM327Plugin_onReceive(char *channel, struct ChannelObject *object, FlutterP if (isListen) ELM327Plugin_onEventChannelListen(channel, pid, handle); else ELM327Plugin_onEventChannelCancel(channel, pid, handle); } else { - return PlatformChannel_respondError( - handle, kStandardMethodCallResponse, - "noelm", "elm.is_online == false. No communication to ELM327 possible, or initialization failed.", NULL + return platch_respond_error_std( + handle, + "noelm", + "elm.is_online == false. No communication to ELM327 possible, or initialization failed.", + NULL ); } } else { - return PlatformChannel_respondNotImplemented(handle); + return platch_respond_not_implemented(handle); } } @@ -624,13 +626,13 @@ int ELM327Plugin_init(void) { pidqq_processor_shouldrun = true; pthread_create(&pidqq_processor_thread, NULL, run_pidqq_processor, NULL); - PluginRegistry_setReceiver(ELM327PLUGIN_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - PluginRegistry_setReceiver(ELM327PLUGIN_RPM_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - PluginRegistry_setReceiver(ELM327PLUGIN_ENGINELOAD_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - PluginRegistry_setReceiver(ELM327PLUGIN_COOLANTTEMP_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - PluginRegistry_setReceiver(ELM327PLUGIN_SPEED_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - PluginRegistry_setReceiver(ELM327PLUGIN_THROTTLE_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); - + plugin_registry_set_receiver(ELM327PLUGIN_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + plugin_registry_set_receiver(ELM327PLUGIN_RPM_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + plugin_registry_set_receiver(ELM327PLUGIN_ENGINELOAD_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + plugin_registry_set_receiver(ELM327PLUGIN_COOLANTTEMP_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + plugin_registry_set_receiver(ELM327PLUGIN_SPEED_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + plugin_registry_set_receiver(ELM327PLUGIN_THROTTLE_CHANNEL, kStandardMethodCall, ELM327Plugin_onReceive); + return 0; } int ELM327Plugin_deinit(void) { @@ -640,6 +642,6 @@ int ELM327Plugin_deinit(void) { elm_destroy(); free(pidqq); - + return 0; } diff --git a/src/plugins/gpiod.c b/src/plugins/gpiod.c new file mode 100644 index 00000000..deeae4e1 --- /dev/null +++ b/src/plugins/gpiod.c @@ -0,0 +1,1165 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + + +struct { + bool initialized; + bool line_event_listener_should_run; + + struct gpiod_chip *chips[GPIO_PLUGIN_MAX_CHIPS]; + size_t n_chips; + + // complete list of GPIO lines + struct gpiod_line **lines; + size_t n_lines; + + // GPIO lines that flutter is currently listening to + int epollfd; + pthread_mutex_t listening_lines_mutex; + struct gpiod_line_bulk listening_lines; + pthread_t line_event_listener_thread; + bool should_emit_events; +} gpio_plugin; + +struct { + void *handle; + + // GPIO chips + void (*chip_close)(struct gpiod_chip *chip); + const char *(*chip_name)(struct gpiod_chip *chip); + const char *(*chip_label)(struct gpiod_chip *chip); + unsigned int (*chip_num_lines)(struct gpiod_chip *chip); + + // GPIO lines + unsigned int (*line_offset)(struct gpiod_line *line); + const char *(*line_name)(struct gpiod_line *line); + const char *(*line_consumer)(struct gpiod_line *line); + int (*line_direction)(struct gpiod_line *line); + int (*line_active_state)(struct gpiod_line *line); + int (*line_bias)(struct gpiod_line *line); + bool (*line_is_used)(struct gpiod_line *line); + bool (*line_is_open_drain)(struct gpiod_line *line); + bool (*line_is_open_source)(struct gpiod_line *line); + int (*line_update)(struct gpiod_line *line); + int (*line_request)(struct gpiod_line *line, const struct gpiod_line_request_config *config, int default_val); + int (*line_release)(struct gpiod_line *line); + bool (*line_is_requested)(struct gpiod_line *line); + bool (*line_is_free)(struct gpiod_line *line); + int (*line_get_value)(struct gpiod_line *line); + int (*line_set_value)(struct gpiod_line *line, int value); + int (*line_set_config)(struct gpiod_line *line, int direction, int flags, int value); + int (*line_event_wait_bulk)(struct gpiod_line_bulk *bulk, const struct timespec *timeout, struct gpiod_line_bulk *event_bulk); + int (*line_event_read)(struct gpiod_line *line, struct gpiod_line_event *event); + int (*line_event_get_fd)(struct gpiod_line *line); + struct gpiod_chip *(*line_get_chip)(struct gpiod_line *line); + + // chip iteration + struct gpiod_chip_iter *(*chip_iter_new)(void); + void (*chip_iter_free_noclose)(struct gpiod_chip_iter *iter); + struct gpiod_chip *(*chip_iter_next_noclose)(struct gpiod_chip_iter *iter); + + // line iteration + struct gpiod_line_iter *(*line_iter_new)(struct gpiod_chip *chip); + void (*line_iter_free)(struct gpiod_line_iter *iter); + struct gpiod_line *(*line_iter_next)(struct gpiod_line_iter *iter); + + // misc + const char *(*version_string)(void); +} libgpiod; + +struct line_config { + struct gpiod_line *line; + int direction; + int request_type; + int initial_value; + uint8_t flags; +}; + +// because libgpiod doesn't provide it, but it's useful +static inline void gpiod_line_bulk_remove(struct gpiod_line_bulk *bulk, struct gpiod_line *line) { + struct gpiod_line *linetemp, **cursor; + struct gpiod_line_bulk new_bulk = GPIOD_LINE_BULK_INITIALIZER; + + gpiod_line_bulk_foreach_line(bulk, linetemp, cursor) { + if (linetemp != line) + gpiod_line_bulk_add(&new_bulk, linetemp); + } + + memcpy(bulk, &new_bulk, sizeof(struct gpiod_line_bulk)); +} + +static void *gpiodp_io_loop(void *userdata); + +/// ensures the libgpiod binding and the `gpio_plugin` chips list and line map is initialized. +static int gpiodp_ensure_gpiod_initialized(void) { + struct gpiod_chip_iter *chipiter; + struct gpiod_line_iter *lineiter; + struct gpiod_chip *chip; + struct gpiod_line *line; + int ok, i, j, fd; + + if (gpio_plugin.initialized) return 0; + + libgpiod.handle = dlopen("libgpiod.so", RTLD_LOCAL | RTLD_LAZY); + if (!libgpiod.handle) { + perror("[flutter_gpiod] could not load libgpiod.so. dlopen"); + return errno; + } + + LOAD_GPIOD_PROC(chip_close); + LOAD_GPIOD_PROC(chip_name); + LOAD_GPIOD_PROC(chip_label); + LOAD_GPIOD_PROC(chip_num_lines); + + LOAD_GPIOD_PROC(line_offset); + LOAD_GPIOD_PROC(line_name); + LOAD_GPIOD_PROC(line_consumer); + LOAD_GPIOD_PROC(line_direction); + LOAD_GPIOD_PROC(line_active_state); + LOAD_GPIOD_PROC_OPTIONAL(line_bias); + LOAD_GPIOD_PROC(line_is_used); + LOAD_GPIOD_PROC(line_is_open_drain); + LOAD_GPIOD_PROC(line_is_open_source); + LOAD_GPIOD_PROC(line_update); + LOAD_GPIOD_PROC(line_request); + LOAD_GPIOD_PROC(line_release); + LOAD_GPIOD_PROC(line_is_requested); + LOAD_GPIOD_PROC(line_is_free); + LOAD_GPIOD_PROC(line_get_value); + LOAD_GPIOD_PROC(line_set_value); + LOAD_GPIOD_PROC_OPTIONAL(line_set_config); + LOAD_GPIOD_PROC(line_event_wait_bulk); + LOAD_GPIOD_PROC(line_event_read); + LOAD_GPIOD_PROC(line_event_get_fd); + LOAD_GPIOD_PROC(line_get_chip); + + LOAD_GPIOD_PROC(chip_iter_new); + LOAD_GPIOD_PROC(chip_iter_free_noclose); + LOAD_GPIOD_PROC(chip_iter_next_noclose); + + LOAD_GPIOD_PROC(line_iter_new); + LOAD_GPIOD_PROC(line_iter_free); + LOAD_GPIOD_PROC(line_iter_next); + + LOAD_GPIOD_PROC(version_string); + + + // iterate through the GPIO chips + chipiter = libgpiod.chip_iter_new(); + if (!chipiter) { + perror("[flutter_gpiod] could not create GPIO chip iterator. gpiod_chip_iter_new"); + return errno; + } + + for (gpio_plugin.n_chips = 0, gpio_plugin.n_lines = 0, chip = libgpiod.chip_iter_next_noclose(chipiter); + chip; + gpio_plugin.n_chips++, chip = libgpiod.chip_iter_next_noclose(chipiter)) + { + gpio_plugin.chips[gpio_plugin.n_chips] = chip; + gpio_plugin.n_lines += libgpiod.chip_num_lines(chip); + } + libgpiod.chip_iter_free_noclose(chipiter); + + + // prepare the GPIO line list + gpio_plugin.lines = calloc(gpio_plugin.n_lines, sizeof(struct gpiod_line*)); + if (!gpio_plugin.lines) { + perror("could not allocate memory for GPIO line list"); + return errno; + } + + // iterate through the chips and put all lines into the list + for (i = 0, j = 0; i < gpio_plugin.n_chips; i++) { + lineiter = libgpiod.line_iter_new(gpio_plugin.chips[i]); + if (!lineiter) { + perror("could not create new GPIO line iterator"); + return errno; + } + + for (line = libgpiod.line_iter_next(lineiter); line; line = libgpiod.line_iter_next(lineiter), j++) + gpio_plugin.lines[j] = line; + + libgpiod.line_iter_free(lineiter); + } + + fd = epoll_create1(0); + if (fd == -1) { + perror("[flutter_gpiod] Could not create line event listen epoll"); + return errno; + } + + gpio_plugin.epollfd = fd; + gpio_plugin.listening_lines = (struct gpiod_line_bulk) GPIOD_LINE_BULK_INITIALIZER; + gpio_plugin.listening_lines_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; + gpio_plugin.line_event_listener_should_run = true; + + ok = pthread_create( + &gpio_plugin.line_event_listener_thread, + NULL, + gpiodp_io_loop, + NULL + ); + if (ok == -1) { + perror("[flutter_gpiod] could not create line event listener thread"); + return errno; + } + + gpio_plugin.initialized = true; + return 0; +} + +/// Sends a platform message to `handle` saying that the libgpiod binding has failed to initialize. +/// Should be called when `gpiodp_ensure_gpiod_initialized()` has failed. +static int gpiodp_respond_init_failed(FlutterPlatformMessageResponseHandle *handle) { + return platch_respond_error_std( + handle, + "couldnotinit", + "flutter_gpiod failed to initialize libgpiod bindings. See flutter-pi log for details.", + NULL + ); +} + +/// Sends a platform message to `handle` with error code "illegalargument" +/// and error messsage "supplied line handle is not valid". +static int gpiodp_respond_illegal_line_handle(FlutterPlatformMessageResponseHandle *handle) { + return platch_respond_illegal_arg_std(handle, "supplied line handle is not valid"); +} + +static int gpiodp_respond_not_supported(FlutterPlatformMessageResponseHandle *handle, char *msg) { + return platch_respond_error_std(handle, "notsupported", msg, NULL); +} + +static int gpiodp_get_config(struct std_value *value, + struct line_config *conf_out, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct std_value *temp; + unsigned int line_handle; + bool has_bias; + int ok; + + conf_out->direction = 0; + conf_out->request_type = 0; + conf_out->flags = 0; + + if ((!value) || (value->type != kStdMap)) { + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a `Map`" + ); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the line handle from the argument map + temp = stdmap_get_str(value, "lineHandle"); + if (temp && STDVALUE_IS_INT(*temp)) { + line_handle = STDVALUE_AS_INT(*temp); + } else { + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['lineHandle']` to be an integer." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the corresponding gpiod line + if (line_handle < gpio_plugin.n_lines) { + conf_out->line = gpio_plugin.lines[line_handle]; + } else { + ok = gpiodp_respond_illegal_line_handle(responsehandle); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the direction + temp = stdmap_get_str(value, "direction"); + if (temp && (temp->type == kStdString)) { + if STREQ("LineDirection.input", temp->string_value) { + conf_out->direction = GPIOD_LINE_DIRECTION_INPUT; + conf_out->request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; + } else if STREQ("LineDirection.output", temp->string_value) { + conf_out->direction = GPIOD_LINE_DIRECTION_OUTPUT; + conf_out->request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + } else { + goto invalid_direction; + } + } else { + invalid_direction: + + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['direction']` to be a string-ification of `LineDirection`." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the output mode + temp = stdmap_get_str(value, "outputMode"); + if ((!temp) || STDVALUE_IS_NULL(*temp)) { + if (conf_out->direction == GPIOD_LINE_DIRECTION_OUTPUT) { + goto invalid_output_mode; + } + } else if (temp && temp->type == kStdString) { + if (conf_out->direction == GPIOD_LINE_DIRECTION_INPUT) { + goto invalid_output_mode; + } + + if STREQ("OutputMode.pushPull", temp->string_value) { + // do nothing + } else if STREQ("OutputMode.openDrain", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; + } else if STREQ("OutputMode.openSource", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; + } else { + goto invalid_output_mode; + } + } else { + invalid_output_mode: + + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['outputMode']` to be a string-ification " + "of [OutputMode] when direction is output, " + "null when direction is input." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the bias + has_bias = false; + temp = stdmap_get_str(value, "bias"); + if ((!temp) || STDVALUE_IS_NULL(*temp)) { + // don't need to set any flags + } else if (temp && temp->type == kStdString) { + if STREQ("Bias.disable", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE; + has_bias = true; + } else if STREQ("Bias.pullUp", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; + has_bias = true; + } else if STREQ("Bias.pullDown", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; + has_bias = true; + } else { + goto invalid_bias; + } + } else { + invalid_bias: + + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['bias']` to be a stringification of [Bias] or null." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + if (has_bias && !libgpiod.line_bias) { + ok = gpiodp_respond_not_supported( + responsehandle, + "Setting line bias is not supported on this platform. " + "Expected `arg['bias']` to be null." + ); + + if (ok != 0) return ok; + return ENOTSUP; + } + + // get the initial value + conf_out->initial_value = 0; + temp = stdmap_get_str(value, "initialValue"); + if ((!temp) || STDVALUE_IS_NULL(*temp)) { + if (conf_out->direction == GPIOD_LINE_DIRECTION_INPUT) { + // do nothing. + } else if (conf_out->direction == GPIOD_LINE_DIRECTION_OUTPUT) { + goto invalid_initial_value; + } + } else if (temp && STDVALUE_IS_BOOL(*temp)) { + if (conf_out->direction == GPIOD_LINE_DIRECTION_INPUT) { + goto invalid_initial_value; + } else if (conf_out->direction == GPIOD_LINE_DIRECTION_OUTPUT) { + conf_out->initial_value = STDVALUE_AS_BOOL(*temp) ? 1 : 0; + } + } else { + invalid_initial_value: + + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['initialValue']` to be null if direction is input, " + "a bool if direction is output." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + // get the active state + temp = stdmap_get_str(value, "activeState"); + if (temp && (temp->type == kStdString)) { + if STREQ("ActiveState.low", temp->string_value) { + conf_out->flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + } else if STREQ("ActiveState.high", temp->string_value) { + // do nothing + } else { + goto invalid_active_state; + } + } else { + invalid_active_state: + + ok = platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['activeState']` to be a stringification of [ActiveState]." + ); + if (ok != 0) return ok; + + return EINVAL; + } + + return 0; +} + +/// Runs on it's own thread. Waits for events +/// on any of the lines in `gpio_plugin.listening_lines` +/// and sends them on to the event channel, if someone +/// is listening on it. +static void *gpiodp_io_loop(void *userdata) { + struct gpiod_line_event event; + struct gpiod_line *line, **cursor; + struct gpiod_chip *chip; + struct epoll_event fdevents[10]; + unsigned int line_handle; + bool is_ready; + int ok, n_fdevents; + + while (gpio_plugin.line_event_listener_should_run) { + // epoll luckily is concurrent. Other threads can add and remove fd's from this + // epollfd while we're waiting on it. + + ok = epoll_wait(gpio_plugin.epollfd, fdevents, 10, -1); + if ((ok == -1) && (errno != EINTR)) { + perror("[flutter_gpiod] error while waiting for line events, epoll"); + continue; + } else { + n_fdevents = ok; + } + + pthread_mutex_lock(&gpio_plugin.listening_lines_mutex); + + // Go through all the lines were listening to right now and find out, + // check for each line whether an event ocurred on the line's fd. + // If that's the case, read the events and send them to flutter. + gpiod_line_bulk_foreach_line(&gpio_plugin.listening_lines, line, cursor) { + is_ready = false; + for (int i = 0; i < n_fdevents; i++) { + if (fdevents[i].data.fd == libgpiod.line_event_get_fd(line)) { + is_ready = true; + break; + } + } + if (!is_ready) continue; + + // read the line events + ok = libgpiod.line_event_read(line, &event); + if (ok == -1) { + perror("[flutter_gpiod] Could not read events from GPIO line. gpiod_line_event_read"); + continue; + } + + // if currently noone's listening to the + // flutter_gpiod event channel, we don't send anything + // to flutter and discard the events. + if (!gpio_plugin.should_emit_events) continue; + + // convert the gpiod_line to a flutter_gpiod line handle. + chip = libgpiod.line_get_chip(line); + + line_handle = libgpiod.line_offset(line); + for (int i = 0; gpio_plugin.chips[i] != chip; i++) + line_handle += libgpiod.chip_num_lines(chip); + + // finally send the event to the event channel. + ok = platch_send_success_event_std( + GPIOD_PLUGIN_EVENT_CHANNEL, + &(struct std_value) { + .type = kStdList, + .size = 3, + .list = (struct std_value[3]) { + {.type = kStdInt32, .int32_value = line_handle}, + { + .type = kStdString, + .string_value = + event.event_type == GPIOD_LINE_EVENT_FALLING_EDGE? + "SignalEdge.falling" : + "SignalEdge.rising" + }, + { + .type = kStdInt64Array, // use int64's here so we don't get any unexpected overflows. + .size = 2, + .int64array = (int64_t[2]) {event.ts.tv_sec, event.ts.tv_nsec} + } + } + } + ); + } + + pthread_mutex_unlock(&gpio_plugin.listening_lines_mutex); + } + + return NULL; +} + + +static int gpiodp_get_num_chips(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + int ok; + + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + return platch_respond_success_std(responsehandle, &STDINT32(gpio_plugin.n_chips)); +} + +static int gpiodp_get_chip_details(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct gpiod_chip *chip; + unsigned int chip_index; + int ok; + + // check the argument + if (STDVALUE_IS_INT(object->std_arg)) { + chip_index = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + // init GPIO + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + // get the chip index + if (chip_index < gpio_plugin.n_chips) { + chip = gpio_plugin.chips[chip_index]; + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be valid chip index." + ); + } + + // return chip details + return platch_respond_success_std( + responsehandle, + &(struct std_value) { + .type = kStdMap, + .size = 3, + .keys = (struct std_value[3]) { + {.type = kStdString, .string_value = "name"}, + {.type = kStdString, .string_value = "label"}, + {.type = kStdString, .string_value = "numLines"}, + }, + .values = (struct std_value[3]) { + {.type = kStdString, .string_value = (char*) libgpiod.chip_name(chip)}, + {.type = kStdString, .string_value = (char*) libgpiod.chip_label(chip)}, + {.type = kStdInt32, .int32_value = libgpiod.chip_num_lines(chip)}, + } + } + ); +} + +static int gpiodp_get_line_handle(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct gpiod_chip *chip; + unsigned int chip_index, line_index; + int ok; + + // check arg + if (STDVALUE_IS_LIST(object->std_arg)) { + if (STDVALUE_IS_INT(object->std_arg.list[0])) { + chip_index = STDVALUE_AS_INT(object->std_arg.list[0]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[0]` to be an integer." + ); + } + + if (STDVALUE_IS_INT(object->std_arg.list[1])) { + line_index = STDVALUE_AS_INT(object->std_arg.list[1]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[1]` to be an integer." + ); + } + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a list with length 2." + ); + } + + // try to init GPIO + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + // try to get the chip correspondig to the chip index + if (chip_index < gpio_plugin.n_chips) { + chip = gpio_plugin.chips[chip_index]; + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[0]` to be a valid chip index." + ); + } + + // check if the line index is in range + if (line_index >= libgpiod.chip_num_lines(chip)) { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[1]` to be a valid line index." + ); + } + + // transform the line index into a line handle + for (int i = 0; i < chip_index; i++) + line_index += libgpiod.chip_num_lines(gpio_plugin.chips[i]); + + return platch_respond_success_std(responsehandle, &STDINT32(line_index)); +} + +static int gpiodp_get_line_details(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct gpiod_line *line; + unsigned int line_handle; + char *name, *consumer; + char *direction_str, *bias_str, *output_mode_str, *active_state_str; + bool open_source, open_drain; + int direction, bias; + int ok; + + // check arg + if (STDVALUE_IS_INT(object->std_arg)) { + line_handle = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + // init GPIO + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + // try to get the gpiod line corresponding to the line handle + if (line_handle < gpio_plugin.n_lines) { + line = gpio_plugin.lines[line_handle]; + } else { + return gpiodp_respond_illegal_line_handle(responsehandle); + } + + // if we don't own the line, update it + if (!libgpiod.line_is_requested(line)) { + libgpiod.line_update(line); + } + + direction = libgpiod.line_direction(line); + direction_str = direction == GPIOD_LINE_DIRECTION_INPUT ? + "LineDirection.input" : "LineDirection.output"; + + active_state_str = libgpiod.line_active_state(line) == GPIOD_LINE_ACTIVE_STATE_HIGH ? + "ActiveState.high" : "ActiveState.low"; + + bias_str = NULL; + if (libgpiod.line_bias) { + bias = libgpiod.line_bias(line); + if (bias == GPIOD_LINE_BIAS_DISABLE) { + bias_str = "Bias.disable"; + } else if (bias == GPIOD_LINE_BIAS_PULL_UP) { + bias_str = "Bias.pullUp"; + } else { + bias_str = "Bias.pullDown"; + } + } + + output_mode_str = NULL; + if (direction == GPIOD_LINE_DIRECTION_OUTPUT) { + open_source = libgpiod.line_is_open_source(line); + open_drain = libgpiod.line_is_open_drain(line); + + if (open_source) { + output_mode_str = "OutputMode.openSource"; + } else if (open_drain) { + output_mode_str = "OutputMode.openDrain"; + } else { + output_mode_str = "OutputMode.pushPull"; + } + } + + + name = (char*) libgpiod.line_name(line); + consumer = (char*) libgpiod.line_consumer(line); + + // return line details + return platch_respond_success_std( + responsehandle, + &(struct std_value) { + .type = kStdMap, + .size = 9, + .keys = (struct std_value[9]) { + {.type = kStdString, .string_value = "name"}, + {.type = kStdString, .string_value = "consumer"}, + {.type = kStdString, .string_value = "isUsed"}, + {.type = kStdString, .string_value = "isRequested"}, + {.type = kStdString, .string_value = "isFree"}, + {.type = kStdString, .string_value = "direction"}, + {.type = kStdString, .string_value = "outputMode"}, + {.type = kStdString, .string_value = "bias"}, + {.type = kStdString, .string_value = "activeState"} + }, + .values = (struct std_value[9]) { + {.type = name? kStdString : kStdNull, .string_value = name}, + {.type = consumer? kStdString : kStdNull, .string_value = consumer}, + {.type = libgpiod.line_is_used(line) ? kStdTrue : kStdFalse}, + {.type = libgpiod.line_is_requested(line) ? kStdTrue : kStdFalse}, + {.type = libgpiod.line_is_free(line) ? kStdTrue : kStdFalse}, + {.type = kStdString, .string_value = direction_str}, + { + .type = output_mode_str? kStdString : kStdNull, + .string_value = output_mode_str + }, + { + .type = bias_str? kStdString : kStdNull, + .string_value = bias_str + }, + {.type = kStdString, .string_value = active_state_str} + } + } + ); +} + +static int gpiodp_request_line(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct line_config config; + struct std_value *temp; + bool is_event_line = false; + char *consumer; + int ok, fd; + + // check that the arg is a map + if (object->std_arg.type != kStdMap) { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a `Map`" + ); + } + + // ensure GPIO is initialized + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + temp = stdmap_get_str(&object->std_arg, "consumer"); + if (!temp || STDVALUE_IS_NULL(*temp)) { + consumer = NULL; + } else if (temp && (temp->type == kStdString)) { + consumer = temp->string_value; + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['consumer']` to be a string or null." + ); + } + + // get the line config + ok = gpiodp_get_config(&object->std_arg, &config, responsehandle); + if (ok != 0) return ok; + + // get the triggers + temp = stdmap_get_str(&object->std_arg, "triggers"); + if ((!temp) || STDVALUE_IS_NULL(*temp)) { + if (config.direction == GPIOD_LINE_DIRECTION_INPUT) { + goto invalid_triggers; + } + } else if (temp && STDVALUE_IS_LIST(*temp)) { + if (config.direction == GPIOD_LINE_DIRECTION_OUTPUT) { + goto invalid_triggers; + } + + // iterate through elements in the trigger list. + for (int i = 0; i < temp->size; i++) { + if (temp->list[i].type != kStdString) { + goto invalid_triggers; + } + + // now update config.request_type accordingly. + if STREQ("SignalEdge.falling", temp->list[i].string_value) { + is_event_line = true; + switch (config.request_type) { + case GPIOD_LINE_REQUEST_DIRECTION_INPUT: + config.request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE; + break; + case GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE: + break; + case GPIOD_LINE_REQUEST_EVENT_RISING_EDGE: + case GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES: + config.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + break; + default: break; + } + } else if STREQ("SignalEdge.rising", temp->list[i].string_value) { + is_event_line = true; + switch (config.request_type) { + case GPIOD_LINE_REQUEST_DIRECTION_INPUT: + config.request_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE; + break; + case GPIOD_LINE_REQUEST_EVENT_RISING_EDGE: + break; + case GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE: + case GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES: + config.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + break; + default: break; + } + } else { + goto invalid_triggers; + } + } + } else { + invalid_triggers: + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['triggers']` to be a `List` of " + "string-ifications of [SignalEdge] when direction is input " + "(no null values in the list), null when direction is output." + ); + } + + // finally request the line + ok = libgpiod.line_request( + config.line, + &(struct gpiod_line_request_config) { + .consumer = consumer, + .request_type = config.request_type, + .flags = config.flags + }, + config.initial_value + ); + if (ok == -1) { + return platch_respond_native_error_std(responsehandle, errno); + } + + if (is_event_line) { + pthread_mutex_lock(&gpio_plugin.listening_lines_mutex); + + fd = libgpiod.line_event_get_fd(config.line); + ok = epoll_ctl(gpio_plugin.epollfd, + EPOLL_CTL_ADD, + fd, + &(struct epoll_event) {.events = EPOLLPRI | EPOLLIN, .data.fd = fd} + ); + if (ok == -1) { + perror("[flutter_gpiod] Could not add GPIO line to epollfd. epoll_ctl"); + libgpiod.line_release(config.line); + return platch_respond_native_error_std(responsehandle, errno); + } + + gpiod_line_bulk_add(&gpio_plugin.listening_lines, config.line); + + pthread_mutex_unlock(&gpio_plugin.listening_lines_mutex); + } + + return platch_respond_success_std(responsehandle, NULL); +} + +static int gpiodp_release_line(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct gpiod_line *line; + unsigned int line_handle; + int ok, fd; + + // get the line handle + if (STDVALUE_IS_INT(object->std_arg)) { + line_handle = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + // get the corresponding gpiod line + if (line_handle < gpio_plugin.n_lines) { + line = gpio_plugin.lines[line_handle]; + } else { + return gpiodp_respond_illegal_line_handle(responsehandle); + } + + // Try to get the line associated fd and remove + // it from the listening thread + fd = libgpiod.line_event_get_fd(line); + if (fd != -1) { + pthread_mutex_lock(&gpio_plugin.listening_lines_mutex); + + gpiod_line_bulk_remove(&gpio_plugin.listening_lines, line); + + ok = epoll_ctl(gpio_plugin.epollfd, EPOLL_CTL_DEL, fd, NULL); + if (ok == -1) { + perror("[flutter_gpiod] Could not remove GPIO line from epollfd. epoll_ctl"); + return platch_respond_native_error_std(responsehandle, errno); + } + + pthread_mutex_unlock(&gpio_plugin.listening_lines_mutex); + } + + ok = libgpiod.line_release(line); + if (ok == -1) { + perror("[flutter_gpiod] Could not release line. gpiod_line_release"); + return platch_respond_native_error_std(responsehandle, errno); + } + + return platch_respond_success_std(responsehandle, NULL); +} + +static int gpiodp_reconfigure_line(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct line_config config; + int ok; + + // ensure GPIO is initialized + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + ok = gpiodp_get_config(&object->std_arg, &config, responsehandle); + if (ok != 0) return ok; + + if (!libgpiod.line_set_config) { + return gpiodp_respond_not_supported( + responsehandle, + "Line reconfiguration is not supported on this platform." + ); + } + + // finally temp the line + ok = libgpiod.line_set_config( + config.line, + config.direction, + config.flags, + config.initial_value + ); + if (ok == -1) { + return platch_respond_native_error_std(responsehandle, errno); + } + + return platch_respond_success_std(responsehandle, NULL); +} + +static int gpiodp_get_line_value(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct gpiod_line *line; + unsigned int line_handle; + int ok; + + // get the line handle + if (STDVALUE_IS_INT(object->std_arg)) { + line_handle = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + // get the corresponding gpiod line + if (line_handle < gpio_plugin.n_lines) { + line = gpio_plugin.lines[line_handle]; + } else { + return gpiodp_respond_illegal_line_handle(responsehandle); + } + + // get the line value + ok = libgpiod.line_get_value(line); + if (ok == -1) { + return platch_respond_native_error_std(responsehandle, errno); + } + + return platch_respond_success_std(responsehandle, &STDBOOL(ok)); +} + +static int gpiodp_set_line_value(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + struct std_value *temp; + struct gpiod_line *line; + unsigned int line_handle; + bool value; + int ok; + + if (STDVALUE_IS_SIZED_LIST(object->std_arg, 2)) { + if (STDVALUE_IS_INT(object->std_arg.list[0])) { + line_handle = STDVALUE_AS_INT(object->std_arg.list[0]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[0]` to be an integer." + ); + } + + if (STDVALUE_IS_BOOL(object->std_arg.list[1])) { + value = STDVALUE_AS_BOOL(object->std_arg.list[1]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg[1]` to be a bool." + ); + } + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a list." + ); + } + + // get the corresponding gpiod line + if (line_handle < gpio_plugin.n_lines) { + line = gpio_plugin.lines[line_handle]; + } else { + return gpiodp_respond_illegal_line_handle(responsehandle); + } + + // get the line value + ok = libgpiod.line_set_value(line, value ? 1 : 0); + if (ok == -1) { + return platch_respond_native_error_std(responsehandle, errno); + } + + return platch_respond_success_std(responsehandle, NULL); +} + +static int gpiodp_supports_bias(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + int ok; + + // ensure GPIO is initialized + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + return platch_respond_success_std(responsehandle, &STDBOOL(libgpiod.line_bias)); +} + +static int gpiodp_supports_reconfiguration(struct platch_obj *object, + FlutterPlatformMessageResponseHandle *responsehandle) { + int ok; + + // ensure GPIO is initialized + ok = gpiodp_ensure_gpiod_initialized(); + if (ok != 0) { + return gpiodp_respond_init_failed(responsehandle); + } + + return platch_respond_success_std(responsehandle, &STDBOOL(libgpiod.line_set_config)); +} + +/// Handles incoming platform messages. Calls the above methods. +int gpiodp_on_receive(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + unsigned int chip_index, line_index; + bool is_legal_arg; + int ok; + + + if STREQ("getNumChips", object->method) { + return gpiodp_get_num_chips(object, responsehandle); + } else if STREQ("getChipDetails", object->method) { + return gpiodp_get_chip_details(object, responsehandle); + } else if STREQ("getLineHandle", object->method) { + return gpiodp_get_line_handle(object, responsehandle); + } else if STREQ("getLineDetails", object->method) { + return gpiodp_get_line_details(object, responsehandle); + } else if STREQ("requestLine", object->method) { + return gpiodp_request_line(object, responsehandle); + } else if STREQ("releaseLine", object->method) { + return gpiodp_release_line(object, responsehandle); + } else if STREQ("reconfigureLine", object->method) { + return gpiodp_reconfigure_line(object, responsehandle); + } else if STREQ("getLineValue", object->method) { + return gpiodp_get_line_value(object, responsehandle); + } else if STREQ("setLineValue", object->method) { + return gpiodp_set_line_value(object, responsehandle); + } else if STREQ("supportsBias", object->method) { + return gpiodp_supports_bias(object, responsehandle); + } else if STREQ("supportsLineReconfiguration", object->method) { + return gpiodp_supports_reconfiguration(object, responsehandle); + } + + return platch_respond_not_implemented(responsehandle); +} + +int gpiodp_on_receive_evch(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + if STREQ("listen", object->method) { + gpio_plugin.should_emit_events = true; + return platch_respond_success_std(responsehandle, NULL); + } else if STREQ("cancel", object->method) { + gpio_plugin.should_emit_events = false; + return platch_respond_success_std(responsehandle, NULL); + } + + return platch_respond_not_implemented(responsehandle); +} + + +int gpiodp_init(void) { + int ok; + + printf("[flutter_gpiod] Initializing...\n"); + + gpio_plugin.initialized = false; + + ok = plugin_registry_set_receiver(GPIOD_PLUGIN_METHOD_CHANNEL, kStandardMethodCall, gpiodp_on_receive); + if (ok != 0) return ok; + + plugin_registry_set_receiver(GPIOD_PLUGIN_EVENT_CHANNEL, kStandardMethodCall, gpiodp_on_receive_evch); + if (ok != 0) return ok; + + printf("[flutter_gpiod] Done.\n"); + + return 0; +} + +int gpiodp_deinit(void) { + printf("[flutter_gpiod] deinit.\n"); + return 0; +} \ No newline at end of file diff --git a/src/plugins/raw_keyboard.c b/src/plugins/raw_keyboard.c index 6786b27c..4983d565 100644 --- a/src/plugins/raw_keyboard.c +++ b/src/plugins/raw_keyboard.c @@ -7,7 +7,8 @@ #include #include #include -#include "raw_keyboard.h" + +#include struct { // same as mods, just that it differentiates between left and right-sided modifiers. @@ -16,26 +17,26 @@ struct { bool initialized; } raw_keyboard = {.initialized = false}; -int RawKeyboard_sendGlfwKeyEvent(uint32_t code_point, glfw_key key_code, uint32_t scan_code, glfw_keymod_map mods, bool is_down) { - return PlatformChannel_send( +int rawkb_send_glfw_keyevent(uint32_t code_point, glfw_key key_code, uint32_t scan_code, glfw_keymod_map mods, bool is_down) { + return platch_send( KEY_EVENT_CHANNEL, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMessageCodec, - .jsonmsgcodec_value = { - .type = kJSObject, + .json_value = { + .type = kJsonObject, .size = 7, .keys = (char*[7]) { "keymap", "toolkit", "unicodeScalarValues", "keyCode", "scanCode", "modifiers", "type" }, - .values = (struct JSONMsgCodecValue[7]) { - {.type = kJSString, .string_value = "linux"}, - {.type = kJSString, .string_value = "glfw"}, - {.type = kJSNumber, .number_value = code_point}, - {.type = kJSNumber, .number_value = key_code}, - {.type = kJSNumber, .number_value = scan_code}, - {.type = kJSNumber, .number_value = mods}, - {.type = kJSString, .string_value = is_down? "keydown" : "keyup"} + .values = (struct json_value[7]) { + {.type = kJsonString, .string_value = "linux"}, + {.type = kJsonString, .string_value = "glfw"}, + {.type = kJsonNumber, .number_value = code_point}, + {.type = kJsonNumber, .number_value = key_code}, + {.type = kJsonNumber, .number_value = scan_code}, + {.type = kJsonNumber, .number_value = mods}, + {.type = kJsonString, .string_value = is_down? "keydown" : "keyup"} } } }, @@ -45,7 +46,7 @@ int RawKeyboard_sendGlfwKeyEvent(uint32_t code_point, glfw_key key_code, uint32_ ); } -int RawKeyboard_onKeyEvent(glfw_key key, uint32_t scan_code, glfw_key_action action) { +int rawkb_on_keyevent(glfw_key key, uint32_t scan_code, glfw_key_action action) { glfw_keymod_map mods_after = raw_keyboard.mods; uint16_t lrmods_after = raw_keyboard.leftright_mods; glfw_keymod mod; @@ -108,23 +109,25 @@ int RawKeyboard_onKeyEvent(glfw_key key, uint32_t scan_code, glfw_key_action act } if (send) { - RawKeyboard_sendGlfwKeyEvent(0, key, scan_code, raw_keyboard.mods, action != GLFW_RELEASE); + rawkb_send_glfw_keyevent(0, key, scan_code, raw_keyboard.mods, action != GLFW_RELEASE); } raw_keyboard.leftright_mods = lrmods_after; raw_keyboard.mods = mods_after; } -int RawKeyboard_init(void) { +int rawkb_init(void) { + printf("[raw_keyboard] Initializing...\n"); + raw_keyboard.leftright_mods = 0; raw_keyboard.mods = 0; raw_keyboard.initialized = true; - printf("[raw_keyboard] init.\n"); + printf("[raw_keyboard] Done.\n"); return 0; } -int RawKeyboard_deinit(void) { +int rawkb_deinit(void) { raw_keyboard.initialized = false; printf("[raw_keyboard] deinit.\n"); diff --git a/src/plugins/raw_keyboard.h b/src/plugins/raw_keyboard.h deleted file mode 100644 index f9fb3fc8..00000000 --- a/src/plugins/raw_keyboard.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _KEY_EVENT_H -#define _KEY_EVENT_H - -#define KEY_EVENT_CHANNEL "flutter/keyevent" - -int RawKeyboard_onKeyEvent(glfw_key key, uint32_t scan_code, glfw_key_action action); - -int RawKeyboard_init(void); -int RawKeyboard_deinit(void); - -#endif \ No newline at end of file diff --git a/src/plugins/services-plugin.c b/src/plugins/services.c similarity index 67% rename from src/plugins/services-plugin.c rename to src/plugins/services.c index da7deaa5..bb9169f2 100644 --- a/src/plugins/services-plugin.c +++ b/src/plugins/services.c @@ -3,29 +3,29 @@ #include #include -#include "services-plugin.h" +#include struct { char label[256]; - uint32_t primaryColor; // ARGB8888 (blue is the lowest byte) - char isolateId[32]; + uint32_t primary_color; // ARGB8888 (blue is the lowest byte) + char isolate_id[32]; } services = {0}; -int Services_onReceiveNavigation(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - return PlatformChannel_respondNotImplemented(responsehandle); +int services_on_receive_navigation(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + return platch_respond_not_implemented(responsehandle); } -int Services_onReceiveIsolate(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - memset(&(services.isolateId), sizeof(services.isolateId), 0); - memcpy(services.isolateId, object->binarydata, object->binarydata_size); +int services_on_receive_isolate(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + memset(&(services.isolate_id), sizeof(services.isolate_id), 0); + memcpy(services.isolate_id, object->binarydata, object->binarydata_size); - return PlatformChannel_respondNotImplemented(responsehandle); + return platch_respond_not_implemented(responsehandle); } -int Services_onReceivePlatform(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - struct JSONMsgCodecValue *value; - struct JSONMsgCodecValue *arg = &(object->jsarg); +int services_on_receive_platform(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + struct json_value *value; + struct json_value *arg = &(object->json_arg); int ok; if (strcmp(object->method, "Clipboard.setData") == 0) { @@ -64,34 +64,33 @@ int Services_onReceivePlatform(char *channel, struct ChannelObject *object, Flut * } */ - value = &object->jsarg; + value = &object->json_arg; - if (value->type != kJSArray) - return PlatformChannel_respondError(responsehandle, kJSONMethodCallResponse, "illegalargument", "Expected List as argument", NULL); - - if (value->size == 0) - return PlatformChannel_respondError(responsehandle, kJSONMethodCallResponse, "illegalargument", "Argument List must have at least one value", NULL); - + if ((value->type != kJsonArray) || (value->size == 0)) { + return platch_respond_illegal_arg_json( + responsehandle, + "Expected `arg` to be an array with minimum size 1." + ); + } bool preferred_orientations[kLandscapeRight+1] = {0}; for (int i = 0; i < value->size; i++) { - if (value->array[i].type != kJSString) { - return PlatformChannel_respondError( - responsehandle, kJSONMethodCallResponse, - "illegalargument", "Argument List should only contain strings", NULL + if (value->array[i].type != kJsonString) { + return platch_respond_illegal_arg_json( + responsehandle, + "Expected `arg` to to only contain strings." ); } enum device_orientation o = ORIENTATION_FROM_STRING(value->array[i].string_value); if (o == -1) { - return PlatformChannel_respondError( - responsehandle, kJSONMethodCallResponse, - "illegalargument", - "Argument List elements should values of the DeviceOrientation enum", - NULL + return platch_respond_illegal_arg_json( + responsehandle, + "Expected `arg` to only contain stringifications of the " + "`DeviceOrientation` enum." ); } @@ -128,16 +127,10 @@ int Services_onReceivePlatform(char *channel, struct ChannelObject *object, Flut */ value = jsobject_get(arg, "label"); - if (value && (value->type == kJSString)) + if (value && (value->type == kJsonString)) snprintf(services.label, sizeof(services.label), "%s", value->string_value); - return PlatformChannel_respond(responsehandle, &(struct ChannelObject) { - .codec = kJSONMethodCallResponse, - .success = true, - .jsresult = { - .type = kNull - } - }); + return platch_respond_success_json(responsehandle, NULL); } else if (strcmp(object->method, "SystemChrome.setEnabledSystemUIOverlays") == 0) { /* * SystemChrome.setEnabledSystemUIOverlays(List overlays) @@ -172,49 +165,50 @@ int Services_onReceivePlatform(char *channel, struct ChannelObject *object, Flut printf("flutter requested application exit\n"); } - return PlatformChannel_respondNotImplemented(responsehandle); + return platch_respond_not_implemented(responsehandle); } -int Services_onReceiveAccessibility(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - return PlatformChannel_respondNotImplemented(responsehandle); +int services_on_receive_accessibility(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + return platch_respond_not_implemented(responsehandle); } -int Services_init(void) { +int services_init(void) { int ok; - printf("[services-plugin] init.\n"); + printf("[services] Initializing...\n"); - ok = PluginRegistry_setReceiver("flutter/navigation", kJSONMethodCall, Services_onReceiveNavigation); + ok = plugin_registry_set_receiver("flutter/navigation", kJSONMethodCall, services_on_receive_navigation); if (ok != 0) { fprintf(stderr, "[services-plugin] could not set \"flutter/navigation\" ChannelObject receiver: %s\n", strerror(ok)); return ok; } - ok = PluginRegistry_setReceiver("flutter/isolate", kBinaryCodec, Services_onReceiveIsolate); + ok = plugin_registry_set_receiver("flutter/isolate", kBinaryCodec, services_on_receive_isolate); if (ok != 0) { fprintf(stderr, "[services-plugin] could not set \"flutter/isolate\" ChannelObject receiver: %s\n", strerror(ok)); return ok; } - ok = PluginRegistry_setReceiver("flutter/platform", kJSONMethodCall, Services_onReceivePlatform); + ok = plugin_registry_set_receiver("flutter/platform", kJSONMethodCall, services_on_receive_platform); if (ok != 0) { fprintf(stderr, "[services-plugin] could not set \"flutter/platform\" ChannelObject receiver: %s\n", strerror(ok)); return ok; } - ok = PluginRegistry_setReceiver("flutter/accessibility", kBinaryCodec, Services_onReceiveAccessibility); + ok = plugin_registry_set_receiver("flutter/accessibility", kBinaryCodec, services_on_receive_accessibility); if (ok != 0) { fprintf(stderr, "[services-plugin] could not set \"flutter/accessibility\" ChannelObject receiver: %s\n", strerror(ok)); return ok; } - + + printf("[services] Done.\n"); + return 0; } -int Services_deinit(void) { - printf("[services-plugin] deinit.\n"); - +int services_deinit(void) { + printf("[services] deinit.\n"); return 0; } diff --git a/src/plugins/spidev.c b/src/plugins/spidev.c new file mode 100644 index 00000000..60b81c9b --- /dev/null +++ b/src/plugins/spidev.c @@ -0,0 +1,553 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +enum spidevp_task_type { + kSpiTaskClose, + kSpiTaskRdMode, + kSpiTaskWrMode, + kSpiTaskWrBitsPerWord, + kSpiTaskRdBitsPerWord, + kSpiTaskWrMaxSpeedHz, + kSpiTaskRdMaxSpeedHz, + kSpiTaskTransmit +}; + +struct spidevp_task { + enum spidevp_task_type type; + union { + uint8_t mode; + uint8_t bits; + uint64_t speed; + struct spi_ioc_transfer transfer; + }; + FlutterPlatformMessageResponseHandle *responsehandle; +}; + +struct spidevp_thread { + int fd; + bool has_task; + struct spidevp_task task; + pthread_t thread; + pthread_mutex_t task_mutex; + pthread_cond_t task_added; +}; + +#define SPI_THREAD_INITIALIZER \ + ((struct spidevp_thread) { \ + .fd = -1, .has_task = false, \ + .task_mutex = PTHREAD_MUTEX_INITIALIZER, .task_added = PTHREAD_COND_INITIALIZER \ + }) + +struct { + struct spidevp_thread **threads; + pthread_mutex_t threads_mutex; + size_t size_threads; + size_t num_threads; +} spi_plugin = { + .threads = NULL, + .threads_mutex = PTHREAD_MUTEX_INITIALIZER, + .size_threads = 0 +}; + +static void *spidevp_run_spi_thread(void *_thread) { + FlutterPlatformMessageResponseHandle *responsehandle; + struct spidevp_thread *thread = (struct spidevp_thread*) _thread; + int32_t result; + bool running = true; + int ok, err = 0, fd = thread->fd; + + while (running) { + pthread_mutex_lock(&thread->task_mutex); + while (!thread->has_task) { + pthread_cond_wait(&thread->task_added, &thread->task_mutex); + } + + responsehandle = thread->task.responsehandle; + switch (thread->task.type) { + case kSpiTaskClose: + ok = close(fd); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + running = false; + thread->has_task = false; + thread->fd = -1; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, NULL); + break; + + case kSpiTaskRdMode: + ok = ioctl(fd, SPI_IOC_RD_MODE, &thread->task.mode); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + result = thread->task.mode; + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + ok = platch_respond_success_std(responsehandle, &STDINT32(result)); + + break; + + case kSpiTaskWrMode: + ok = ioctl(fd, SPI_IOC_WR_MODE, &thread->task.mode); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, NULL); + break; + + case kSpiTaskWrBitsPerWord: + ok = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &thread->task.bits); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, NULL); + break; + + case kSpiTaskRdBitsPerWord: + ok = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &thread->task.bits); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + result = thread->task.bits; + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, &STDINT32(result)); + break; + + case kSpiTaskWrMaxSpeedHz: + ok = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &thread->task.speed); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, NULL); + break; + + case kSpiTaskRdMaxSpeedHz: + ok = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &thread->task.speed); + if (ok == -1) { + err = errno; + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + result = thread->task.speed; + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std(responsehandle, &STDINT64(result)); + break; + + case kSpiTaskTransmit: ; + size_t len = thread->task.transfer.len; + uint8_t *buf = (void*) ((uintptr_t) thread->task.transfer.rx_buf); + + ok = ioctl(fd, SPI_IOC_MESSAGE(1), &thread->task.transfer); + if (ok == -1) { + err = errno; + free(buf); + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + break; + } + + thread->has_task = false; + pthread_mutex_unlock(&thread->task_mutex); + platch_respond_success_std( + responsehandle, + &(struct std_value) { + .type = kStdUInt8Array, + .size = len, + .uint8array = buf + } + ); + + free(buf); + + break; + + default: + break; + } + + if (err != 0) { + platch_respond_native_error_std(responsehandle, err); + err = 0; + } + } + + return NULL; +} + +static struct spidevp_thread *spidevp_get_thread(const int fd) { + pthread_mutex_lock(&spi_plugin.threads_mutex); + + for (int i = 0; i < spi_plugin.size_threads; i++) { + if (spi_plugin.threads[i]->fd == fd) { + struct spidevp_thread *result = spi_plugin.threads[i]; + pthread_mutex_unlock(&spi_plugin.threads_mutex); + return result; + } + } + + pthread_mutex_unlock(&spi_plugin.threads_mutex); + return NULL; +} + +static int spidevp_new_thread(const int fd, struct spidevp_thread **thread_out) { + struct spidevp_thread *thread; + int ok; + + pthread_mutex_lock(&spi_plugin.threads_mutex); + + thread = NULL; + for (int i=0; i < spi_plugin.size_threads; i++) { + if (spi_plugin.threads[i]->fd == -1) { + thread = spi_plugin.threads[i]; + break; + } + } + + if (thread == NULL) { + size_t old = spi_plugin.size_threads; + size_t new = old*2; + + spi_plugin.threads = realloc(spi_plugin.threads, new * sizeof(struct spidevp_thread)); + spi_plugin.size_threads = new; + + for (int i=old; i < spi_plugin.size_threads; i++) { + spi_plugin.threads[i] = malloc(sizeof(struct spidevp_thread)); + *(spi_plugin.threads[i]) = SPI_THREAD_INITIALIZER; + } + + thread = spi_plugin.threads[old]; + } + + thread->fd = fd; + + pthread_mutex_unlock(&spi_plugin.threads_mutex); + + ok = pthread_create(&thread->thread, NULL, spidevp_run_spi_thread, thread); + if (ok == -1) return errno; + + *thread_out = thread; + + return 0; +} + +static int spidevp_assign_task(const int fd, const struct spidevp_task *const task) { + struct spidevp_thread *thread; + int ok; + + thread = spidevp_get_thread(fd); + if (!thread) { + return EBADF; + } + + ok = pthread_mutex_trylock(&thread->task_mutex); + if (ok == -1) { + return errno; + } else if (ok == 0) { + if (thread->fd == -1) { + return EBADF; + } else { + thread->task = *task; + thread->has_task = true; + pthread_mutex_unlock(&thread->task_mutex); + pthread_cond_signal(&thread->task_added); + } + } + + return 0; +} + +static int spidevp_open(struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + struct spidevp_thread *thread; + char *path; + int fd, ok; + + if (object->std_arg.type == kStdString) { + path = object->std_arg.string_value; + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a string." + ); + } + + fd = open(path, O_RDWR); + if (fd == -1) { + return platch_respond_native_error_std(responsehandle, errno); + } + + ok = spidevp_new_thread(fd, &thread); + if (ok != 0) { + return platch_respond_native_error_std(responsehandle, ok); + } + + return platch_respond_success_std(responsehandle, &STDINT32(fd)); +} + +static int spidevp_on_receive(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + struct std_value *temp; + struct spidevp_thread *thread; + struct spidevp_task task = {0}; + bool has_task = false; + int ok, fd; + + if STREQ("open", object->method) { + return spidevp_open(object, responsehandle); + } else if STREQ("setMode", object->method) { + if (STDVALUE_IS_SIZED_LIST(object->std_arg, 2) && STDVALUE_IS_INT(object->std_arg.list[0]) + && STDVALUE_IS_INT(object->std_arg.list[1])) { + fd = STDVALUE_AS_INT(object->std_arg.list[0]); + task.mode = STDVALUE_AS_INT(object->std_arg.list[1]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a List with size 2." + ); + } + + task.type = kSpiTaskWrMode; + has_task = true; + } else if STREQ("getMode", object->method) { + if (STDVALUE_IS_INT(object->std_arg)) { + fd = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + task.type = kSpiTaskRdMode; + has_task = true; + } else if STREQ("setMaxSpeed", object->method) { + if (STDVALUE_IS_SIZED_LIST(object->std_arg, 2) && STDVALUE_IS_INT(object->std_arg.list[0]) + && STDVALUE_IS_INT(object->std_arg.list[1])) { + fd = STDVALUE_AS_INT(object->std_arg.list[0]); + task.speed = STDVALUE_AS_INT(object->std_arg.list[1]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a List with size 2." + ); + } + + task.type = kSpiTaskWrMaxSpeedHz; + has_task = true; + } else if STREQ("getMaxSpeed", object->method) { + if (STDVALUE_IS_INT(object->std_arg)) { + fd = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + task.type = kSpiTaskRdMaxSpeedHz; + has_task = true; + } else if STREQ("setWordSize", object->method) { + if (STDVALUE_IS_SIZED_LIST(object->std_arg, 2) && STDVALUE_IS_INT(object->std_arg.list[0]) + && STDVALUE_IS_INT(object->std_arg.list[1])) { + fd = STDVALUE_AS_INT(object->std_arg.list[0]); + task.bits = STDVALUE_AS_INT(object->std_arg.list[1]); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be a List with size 2." + ); + } + + task.type = kSpiTaskWrBitsPerWord; + has_task = true; + } else if STREQ("getWordSize", object->method) { + if (STDVALUE_IS_INT(object->std_arg)) { + fd = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + task.type = kSpiTaskRdBitsPerWord; + has_task = true; + } else if STREQ("transmit", object->method) { + if (object->std_arg.type == kStdMap) { + temp = stdmap_get_str(&object->std_arg, "fd"); + if (temp && STDVALUE_IS_INT(*temp)) { + fd = STDVALUE_AS_INT(*temp); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['fd']` to be an integer." + ); + } + + temp = stdmap_get_str(&object->std_arg, "speed"); + if (temp && STDVALUE_IS_INT(*temp)) { + task.transfer.speed_hz = STDVALUE_AS_INT(*temp); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['speed']` to be an integer." + ); + } + + temp = stdmap_get_str(&object->std_arg, "delay"); + if (temp && STDVALUE_IS_INT(*temp)) { + task.transfer.delay_usecs = STDVALUE_AS_INT(*temp); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['delay']` to be an integer." + ); + } + + temp = stdmap_get_str(&object->std_arg, "wordSize"); + if (temp && STDVALUE_IS_INT(*temp)) { + task.transfer.bits_per_word = STDVALUE_AS_INT(*temp); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['wordSize']` to be an integer." + ); + } + + temp = stdmap_get_str(&object->std_arg, "csChange"); + if (temp && STDVALUE_IS_BOOL(*temp)) { + task.transfer.cs_change = STDVALUE_AS_BOOL(*temp); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['csChange']` to be an integer." + ); + } + + temp = stdmap_get_str(&object->std_arg, "buffer"); + if (temp && temp->type == kStdUInt8Array) { + task.transfer.len = temp->size; + + void *buf = malloc(temp->size); + + task.transfer.tx_buf = (__u64) ((uintptr_t) buf); + task.transfer.rx_buf = task.transfer.tx_buf; + + memcpy(buf, temp->uint8array, temp->size); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg['buffer']` to be a uint8 array." + ); + } + } + + task.type = kSpiTaskTransmit; + has_task = true; + } else if STREQ("close", object->method) { + if (STDVALUE_IS_INT(object->std_arg)) { + fd = STDVALUE_AS_INT(object->std_arg); + } else { + return platch_respond_illegal_arg_std( + responsehandle, + "Expected `arg` to be an integer." + ); + } + + task.type = kSpiTaskClose; + has_task = true; + } + + if (has_task) { + task.responsehandle = responsehandle; + + ok = spidevp_assign_task(fd, &task); + if (ok == EBUSY) { + return platch_respond_error_std( + responsehandle, + "busy", + "a different task is running on the fd already", + NULL + ); + } else if (ok != 0) { + return platch_respond_native_error_std(responsehandle, ok); + } + } else { + return platch_respond_not_implemented(responsehandle); + } + + return 0; +} + +int spidevp_init(void) { + printf("[flutter_spidev] Initializing...\n"); + + spi_plugin.size_threads = 1; + spi_plugin.threads = malloc(spi_plugin.size_threads * sizeof(struct spidevp_thread *)); + + for (int i = 0; i < spi_plugin.size_threads; i++) { + spi_plugin.threads[i] = malloc(sizeof(struct spidevp_thread)); + *(spi_plugin.threads[i]) = SPI_THREAD_INITIALIZER; + } + + plugin_registry_set_receiver(SPI_PLUGIN_METHOD_CHANNEL, kStandardMethodCall, spidevp_on_receive); + + printf("[flutter_spidev] Done.\n"); + return 0; +} + +int spidevp_deinit(void) { + printf("[flutter_spidev] deinit.\n"); + return 0; +} \ No newline at end of file diff --git a/src/plugins/testplugin.c b/src/plugins/testplugin.c index 4eb91c97..42a12cd1 100644 --- a/src/plugins/testplugin.c +++ b/src/plugins/testplugin.c @@ -1,30 +1,31 @@ #include #include +#include #include #include -#include "testplugin.h" +#include #define INDENT_STRING " " -int __printJSON(struct JSONMsgCodecValue *value, int indent) { +int __printJSON(struct json_value *value, int indent) { switch (value->type) { - case kJSNull: + case kJsonNull: printf("null"); break; - case kJSTrue: + case kJsonTrue: printf("true"); break; - case kJSFalse: + case kJsonFalse: printf("false"); break; - case kJSNumber: + case kJsonNumber: printf("%f", value->number_value); break; - case kJSString: + case kJsonString: printf("\"%s\"", value->string_value); break; - case kJSArray: + case kJsonArray: printf("[\n"); for (int i = 0; i < value->size; i++) { printf("%.*s", indent + 2, INDENT_STRING); @@ -33,7 +34,7 @@ int __printJSON(struct JSONMsgCodecValue *value, int indent) { } printf("\n%.*s]", indent, INDENT_STRING); break; - case kJSObject: + case kJsonObject: printf("{\n"); for (int i = 0; i < value->size; i++) { printf("%.*s\"%s\": ", indent + 2, INDENT_STRING, value->keys[i]); @@ -47,36 +48,36 @@ int __printJSON(struct JSONMsgCodecValue *value, int indent) { return 0; } -int printJSON(struct JSONMsgCodecValue *value, int indent) { +int printJSON(struct json_value *value, int indent) { printf("%.*s", indent, INDENT_STRING); __printJSON(value, indent); printf("\n"); } -int __printStd(struct StdMsgCodecValue *value, int indent) { +int __printStd(struct std_value *value, int indent) { switch (value->type) { - case kNull: + case kStdNull: printf("null"); break; - case kTrue: + case kStdTrue: printf("true"); break; - case kFalse: + case kStdFalse: printf("false"); break; - case kInt32: + case kStdInt32: printf("%" PRIi32, value->int32_value); break; - case kInt64: + case kStdInt64: printf("%" PRIi64, value->int64_value); break; - case kFloat64: + case kStdFloat64: printf("%lf", value->float64_value); break; - case kString: - case kLargeInt: + case kStdString: + case kStdLargeInt: printf("\"%s\"", value->string_value); break; - case kUInt8Array: + case kStdUInt8Array: printf("(uint8_t) ["); for (int i = 0; i < value->size; i++) { printf("0x%02X", value->uint8array[i]); @@ -84,7 +85,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { } printf("]"); break; - case kInt32Array: + case kStdInt32Array: printf("(int32_t) ["); for (int i = 0; i < value->size; i++) { printf("%" PRIi32, value->int32array[i]); @@ -92,7 +93,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { } printf("]"); break; - case kInt64Array: + case kStdInt64Array: printf("(int64_t) ["); for (int i = 0; i < value->size; i++) { printf("%" PRIi64, value->int64array[i]); @@ -100,7 +101,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { } printf("]"); break; - case kFloat64Array: + case kStdFloat64Array: printf("(double) ["); for (int i = 0; i < value->size; i++) { printf("%ld", value->float64array[i]); @@ -108,7 +109,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { } printf("]"); break; - case kList: + case kStdList: printf("[\n"); for (int i = 0; i < value->size; i++) { printf("%.*s", indent + 2, INDENT_STRING); @@ -117,7 +118,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { } printf("\n%.*s]", indent, INDENT_STRING); break; - case kMap: + case kStdMap: printf("{\n"); for (int i = 0; i < value->size; i++) { printf("%.*s", indent + 2, INDENT_STRING); @@ -132,7 +133,7 @@ int __printStd(struct StdMsgCodecValue *value, int indent) { break; } } -int printStd(struct StdMsgCodecValue *value, int indent) { +int printStd(struct std_value *value, int indent) { printf("%.*s", indent, INDENT_STRING); __printStd(value, indent); printf("\n"); @@ -142,7 +143,7 @@ int printStd(struct StdMsgCodecValue *value, int indent) { uint64_t testplugin_time_offset; -int TestPlugin_onReceiveResponseJSON(struct ChannelObject *object, void *userdata) { +int testp_on_response_json(struct platch_obj *object, void *userdata) { uint64_t dt = FlutterEngineGetCurrentTime() - *((uint64_t*) userdata); free(userdata); @@ -152,28 +153,28 @@ int TestPlugin_onReceiveResponseJSON(struct ChannelObject *object, void *userdat } if (object->success) { - printf("TestPlugin_onReceiveResponseJSON(dt: %lluns)\n" + printf("testp_on_response_json(dt: %lluns)\n" " success\n" " result:\n", dt); - printJSON(&object->jsresult, 4); + printJSON(&object->json_result, 4); } else { - printf("TestPlugin_onReceiveResponseJSON(dt: %lluns)\n", dt); + printf("testp_on_response_json(dt: %lluns)\n", dt); printf(" failure\n" " error code: %s\n" " error message: %s\n" - " error details:\n", object->errorcode, (object->errormessage != NULL) ? object->errormessage : "null"); - printJSON(&object->jsresult, 4); + " error details:\n", object->error_code, (object->error_msg != NULL) ? object->error_msg : "null"); + printJSON(&object->json_result, 4); } return 0; } -int TestPlugin_sendJSON() { +int testp_send_json() { uint64_t* time = malloc(sizeof(uint64_t)); *time = FlutterEngineGetCurrentTime(); char *method = "test"; - struct JSONMsgCodecValue argument = { - .type = kJSObject, + struct json_value argument = { + .type = kJsonObject, .size = 5, .keys = (char*[]) { "key1", @@ -182,24 +183,24 @@ int TestPlugin_sendJSON() { "key4", "array" }, - .values = (struct JSONMsgCodecValue[]) { - {.type = kJSString, .string_value = "value1"}, - {.type = kJSTrue}, - {.type = kJSNumber, .number_value = -1000}, - {.type = kJSNumber, .number_value = -5.0005}, - {.type = kJSArray, .size = 2, .array = (struct JSONMsgCodecValue[]) { - {.type = kJSString, .string_value = "array1"}, - {.type = kJSNumber, .number_value = 2} + .values = (struct json_value[]) { + {.type = kJsonString, .string_value = "value1"}, + {.type = kJsonTrue}, + {.type = kJsonNumber, .number_value = -1000}, + {.type = kJsonNumber, .number_value = -5.0005}, + {.type = kJsonArray, .size = 2, .array = (struct json_value[]) { + {.type = kJsonString, .string_value = "array1"}, + {.type = kJsonNumber, .number_value = 2} }} }, }; - int ok = PlatformChannel_jsoncall(TESTPLUGIN_CHANNEL_JSON, method, &argument, TestPlugin_onReceiveResponseJSON, time); + int ok = platch_call_json(TESTPLUGIN_CHANNEL_JSON, method, &argument, testp_on_response_json, time); if (ok != 0) { printf("Could not MethodCall JSON: %s\n", strerror(ok)); } } -int TestPlugin_onReceiveResponseStd(struct ChannelObject *object, void *userdata) { +int testp_on_response_std(struct platch_obj *object, void *userdata) { uint64_t dt = FlutterEngineGetCurrentTime() - *((uint64_t*) userdata); free(userdata); @@ -209,96 +210,96 @@ int TestPlugin_onReceiveResponseStd(struct ChannelObject *object, void *userdata } if (object->success) { - printf("TestPlugin_onReceiveResponseStd(dt: %lluns)\n" + printf("testp_on_response_std(dt: %lluns)\n" " success\n" " result:\n", dt); - printStd(&object->stdresult, 4); + printStd(&object->std_result, 4); } else { - printf("TestPlugin_onReceiveResponseStd(dt: %lluns)\n", dt); + printf("testp_on_response_std(dt: %lluns)\n", dt); printf(" failure\n" " error code: %s\n" " error message: %s\n" - " error details:\n", object->errorcode, (object->errormessage != NULL) ? object->errormessage : "null"); - printStd(&object->stdresult, 4); + " error details:\n", object->error_code, (object->error_msg != NULL) ? object->error_msg : "null"); + printStd(&object->std_error_details, 4); } return 0; } -int TestPlugin_sendStd() { +int testp_send_std() { uint64_t *time = malloc(sizeof(uint64_t)); *time = FlutterEngineGetCurrentTime(); char *method = "test"; - struct StdMsgCodecValue argument = { - .type = kMap, + struct std_value argument = { + .type = kStdMap, .size = 7, - .keys = (struct StdMsgCodecValue[]) { - {.type = kString, .string_value = "key1"}, - {.type = kString, .string_value = "key2"}, - {.type = kString, .string_value = "key3"}, - {.type = kString, .string_value = "key4"}, - {.type = kInt32, .int32_value = 5}, - {.type = kString, .string_value = "timestamp"}, - {.type = kString, .string_value = "array"} + .keys = (struct std_value[]) { + {.type = kStdString, .string_value = "key1"}, + {.type = kStdString, .string_value = "key2"}, + {.type = kStdString, .string_value = "key3"}, + {.type = kStdString, .string_value = "key4"}, + {.type = kStdInt32, .int32_value = 5}, + {.type = kStdString, .string_value = "timestamp"}, + {.type = kStdString, .string_value = "array"} }, - .values = (struct StdMsgCodecValue[]) { - {.type = kString, .string_value = "value1"}, - {.type = kTrue}, - {.type = kInt32, .int32_value = -1000}, - {.type = kFloat64, .float64_value = -5.0005}, - {.type = kUInt8Array, .uint8array = (uint8_t[]) {0x00, 0x01, 0x02, 0x03, 0xFF}, .size = 5}, - {.type = kInt64, .int64_value = *time & 0x7FFFFFFFFFFFFFFF}, - {.type = kList, .size = 2, .list = (struct StdMsgCodecValue[]) { - {.type = kString, .string_value = "array1"}, - {.type = kInt32, .int32_value = 2} + .values = (struct std_value[]) { + {.type = kStdString, .string_value = "value1"}, + {.type = kStdTrue}, + {.type = kStdInt32, .int32_value = -1000}, + {.type = kStdFloat64, .float64_value = -5.0005}, + {.type = kStdUInt8Array, .uint8array = (uint8_t[]) {0x00, 0x01, 0x02, 0x03, 0xFF}, .size = 5}, + {.type = kStdInt64, .int64_value = *time & 0x7FFFFFFFFFFFFFFF}, + {.type = kStdList, .size = 2, .list = (struct std_value[]) { + {.type = kStdString, .string_value = "array1"}, + {.type = kStdInt32, .int32_value = 2} }} }, }; - PlatformChannel_stdcall(TESTPLUGIN_CHANNEL_STD, method, &argument, TestPlugin_onReceiveResponseStd, time); + platch_call_std(TESTPLUGIN_CHANNEL_STD, method, &argument, testp_on_response_std, time); } -int TestPlugin_onReceiveJSON(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - printf("TestPlugin_onReceiveJSON(channel: %s)\n" +int testp_on_receive_json(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + printf("testp_on_receive_json(channel: %s)\n" " method: %s\n" " args: \n", channel, object->method); - printJSON(&(object->jsarg), 4); + printJSON(&(object->json_arg), 4); - TestPlugin_sendJSON(); + testp_send_json(); - return PlatformChannel_respond(responsehandle, &(struct ChannelObject) { + return platch_respond(responsehandle, &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = { - .type = kJSTrue + .json_result = { + .type = kJsonTrue } }); } -int TestPlugin_onReceiveStd(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { +int testp_on_receive_std(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { printf("TestPlugin_onReceiveStd(channel: %s)\n" " method: %s\n" " args: \n", channel, object->method); - printStd(&(object->stdarg), 4); + printStd(&(object->std_arg), 4); - TestPlugin_sendStd(); + testp_send_std(); - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kStandardMethodCallResponse, .success = true, - .stdresult = { - .type = kTrue + .std_result = { + .type = kStdTrue } } ); } -int TestPlugin_onReceivePing(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - return PlatformChannel_respond( +int testp_on_receive_ping(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kStringCodec, .string_value = "pong" } @@ -306,14 +307,24 @@ int TestPlugin_onReceivePing(char *channel, struct ChannelObject *object, Flutte } -int TestPlugin_init(void) { - printf("[test-plugin] init.\n"); - PluginRegistry_setReceiver(TESTPLUGIN_CHANNEL_JSON, kJSONMethodCall, TestPlugin_onReceiveJSON); - PluginRegistry_setReceiver(TESTPLUGIN_CHANNEL_STD, kStandardMethodCall, TestPlugin_onReceiveStd); - PluginRegistry_setReceiver(TESTPLUGIN_CHANNEL_PING, kStringCodec, TestPlugin_onReceivePing); +int testp_init(void) { + int ok; + + printf("[test_plugin] Initializing...\n"); + + ok = plugin_registry_set_receiver(TESTPLUGIN_CHANNEL_JSON, kJSONMethodCall, testp_on_receive_json); + if (ok != 0) return ok; + + ok = plugin_registry_set_receiver(TESTPLUGIN_CHANNEL_STD, kStandardMethodCall, testp_on_receive_std); + if (ok != 0) return ok; + + ok = plugin_registry_set_receiver(TESTPLUGIN_CHANNEL_PING, kStringCodec, testp_on_receive_ping); + if (ok != 0) return ok; + + printf("[test_plugin] Done.\n"); return 0; } -int TestPlugin_deinit(void) { - printf("[test-plugin] deinit.\n"); +int testp_deinit(void) { + printf("[test_plugin] deinit.\n"); return 0; } \ No newline at end of file diff --git a/src/plugins/text_input.c b/src/plugins/text_input.c index ac6ff83c..f0b96e55 100644 --- a/src/plugins/text_input.c +++ b/src/plugins/text_input.c @@ -6,7 +6,7 @@ #include #include -#include "text_input.h" +#include struct { int32_t transaction_id; @@ -23,12 +23,10 @@ struct { .transaction_id = -1 }; -int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlatformMessageResponseHandle *responsehandle) { - struct JSONMsgCodecValue jsvalue, *temp, *temp2, *state, *config; +int textin_on_receive(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *responsehandle) { + struct json_value jsvalue, *temp, *temp2, *state, *config; int ok; - printf("[text_input] got method call: %s\n", object->method); - if STREQ("TextInput.setClient", object->method) { /* * TextInput.setClient(List) @@ -40,45 +38,33 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat * others (except `TextInput.hide`). See [TextInput.attach]. */ - if ((object->jsarg.type != kJSArray) || (object->jsarg.size != 2)) { - return PlatformChannel_respondError( + if ((object->json_arg.type != kJsonArray) || (object->json_arg.size != 2)) { + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", - "Expected JSON Array with length 2 as the argument.", - NULL + "Expected `arg` to be an array with length 2." ); } - if (object->jsarg.array[0].type != kJSNumber) { - return PlatformChannel_respondError( + if (object->json_arg.array[0].type != kJsonNumber) { + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", - "Expected transaction id to be a number.", - NULL + "Expected transaction id to be a number." ); } - if (object->jsarg.array[1].type != kJSObject) { - return PlatformChannel_respondError( + if (object->json_arg.array[1].type != kJsonObject) { + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", - "Expected text input configuration to be a String", - NULL + "Expected text input configuration to be a String" ); } - struct JSONMsgCodecValue *config = &object->jsarg.array[1]; + struct json_value *config = &object->json_arg.array[1]; - if (config->type != kJSObject) { - return PlatformChannel_respondError( + if (config->type != kJsonObject) { + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", - "Expected decoded text input configuration to be an Object", - NULL + "Expected decoded text input configuration to be an Object" ); } @@ -88,14 +74,14 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat // AUTOCORRECT temp = jsobject_get(config, "autocorrect"); - if (!(temp && ((temp->type == kJSTrue) || (temp->type == kJSFalse)))) + if (!(temp && ((temp->type == kJsonTrue) || (temp->type == kJsonFalse)))) goto invalid_config; - autocorrect = temp->type == kJSTrue; + autocorrect = temp->type == kJsonTrue; // INPUT ACTION temp = jsobject_get(config, "inputAction"); - if (!(temp && (temp->type == kJSString))) + if (!(temp && (temp->type == kJsonString))) goto invalid_config; if STREQ("TextInputAction.none", temp->string_value) @@ -131,13 +117,13 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat // INPUT TYPE temp = jsobject_get(config, "inputType"); - if (!temp || temp->type != kJSObject) + if (!temp || temp->type != kJsonObject) goto invalid_config; temp2 = jsobject_get(temp, "name"); - if (!temp2 || temp2->type != kJSString) + if (!temp2 || temp2->type != kJsonString) goto invalid_config; if STREQ("TextInputType.text", temp2->string_value) { @@ -161,7 +147,7 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat } // TRANSACTION ID - int32_t new_id = (int32_t) object->jsarg.array[0].number_value; + int32_t new_id = (int32_t) object->json_arg.array[0].number_value; // everything okay, apply the new text editing config text_input.transaction_id = new_id; @@ -175,24 +161,21 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat text_input.warned_about_autocorrect = true; } - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = {.type = kJSNull} + .json_result = {.type = kJsonNull} } ); // invalid config given to setClient invalid_config: - return PlatformChannel_respondError( + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", "Expected decoded text input configuration to at least contain values for \"autocorrect\"" - " and \"inputAction\"", - NULL + " and \"inputAction\"" ); } else if STREQ("TextInput.show", object->method) { @@ -203,12 +186,12 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat */ // do nothing since we use a physical keyboard. - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = {.type = kJSNull} + .json_result = {.type = kJsonNull} } ); } else if STREQ("TextInput.setEditingState", object->method) { @@ -221,15 +204,12 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat * */ - state = &object->jsarg; + state = &object->json_arg; - if (state->type != kJSObject) { - return PlatformChannel_respondError( + if (state->type != kJsonObject) { + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", - "Expected decoded text editing value to be an Object", - NULL + "Expected decoded text editing value to be an Object" ); } @@ -238,19 +218,19 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat bool selection_affinity_is_downstream, selection_is_directional; temp = jsobject_get(state, "text"); - if (temp && (temp->type == kJSString)) text = temp->string_value; + if (temp && (temp->type == kJsonString)) text = temp->string_value; else goto invalid_editing_value; temp = jsobject_get(state, "selectionBase"); - if (temp && (temp->type == kJSNumber)) selection_base = (int) temp->number_value; + if (temp && (temp->type == kJsonNumber)) selection_base = (int) temp->number_value; else goto invalid_editing_value; temp = jsobject_get(state, "selectionExtent"); - if (temp && (temp->type == kJSNumber)) selection_extent = (int) temp->number_value; + if (temp && (temp->type == kJsonNumber)) selection_extent = (int) temp->number_value; else goto invalid_editing_value; temp = jsobject_get(state, "selectionAffinity"); - if (temp && (temp->type == kJSString)) { + if (temp && (temp->type == kJsonString)) { if STREQ("TextAffinity.downstream", temp->string_value) { selection_affinity_is_downstream = true; } else if STREQ("TextAffinity.upstream", temp->string_value) { @@ -263,32 +243,21 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat } temp = jsobject_get(state, "selectionIsDirectional"); - if (temp && (temp->type == kJSTrue || temp->type == kJSFalse)) { - selection_is_directional = temp->type == kJSTrue; + if (temp && (temp->type == kJsonTrue || temp->type == kJsonFalse)) { + selection_is_directional = temp->type == kJsonTrue; } else { goto invalid_editing_value; } temp = jsobject_get(state, "composingBase"); - if (temp && (temp->type == kJSNumber)) composing_base = (int) temp->number_value; + if (temp && (temp->type == kJsonNumber)) composing_base = (int) temp->number_value; else goto invalid_editing_value; temp = jsobject_get(state, "composingExtent"); - if (temp && (temp->type == kJSNumber)) composing_extent = (int) temp->number_value; + if (temp && (temp->type == kJsonNumber)) composing_extent = (int) temp->number_value; else goto invalid_editing_value; - // text editing value seems to be valid. - // apply it. - printf("[text_input] TextInput.setEditingState\n" - " text = \"%s\",\n" - " selectionBase = %i, selectionExtent = %i, selectionAffinity = %s\n" - " selectionIsDirectional = %s, composingBase = %i, composingExtent = %i\n", - text, selection_base, selection_extent, - selection_affinity_is_downstream? "downstream" : "upstream", - selection_is_directional? "true" : "false", composing_base, composing_extent - ); - snprintf(text_input.text, sizeof(text_input.text), "%s", text); text_input.selection_base = selection_base; text_input.selection_extent = selection_extent; @@ -297,23 +266,20 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat text_input.composing_base = composing_base; text_input.composing_extent = composing_extent; - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = {.type = kJSNull} + .json_result = {.type = kJsonNull} } ); invalid_editing_value: - return PlatformChannel_respondError( + return platch_respond_illegal_arg_json( responsehandle, - kJSONMethodCallResponse, - "illegalargument", "Expected decoded text editing value to be a valid" - " JSON representation of a text editing value", - NULL + " JSON representation of a text editing value" ); } else if STREQ("TextInput.clearClient", object->method) { @@ -327,12 +293,12 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat text_input.transaction_id = -1; - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = {.type = kJSNull} + .json_result = {.type = kJsonNull} } ); } else if STREQ("TextInput.hide", object->method) { @@ -344,57 +310,48 @@ int TextInput_onReceive(char *channel, struct ChannelObject *object, FlutterPlat */ // do nothing since we use a physical keyboard. - return PlatformChannel_respond( + return platch_respond( responsehandle, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCallResponse, .success = true, - .jsresult = {.type = kJSNull} + .json_result = {.type = kJsonNull} } ); } - return PlatformChannel_respondNotImplemented(responsehandle); + return platch_respond_not_implemented(responsehandle); } -int TextInput_syncEditingState() { - printf("[text_input] TextInputClient.updateEditingState\n" - " text = \"%s\",\n" - " selectionBase = %i, selectionExtent = %i, selectionAffinity = %s\n" - " selectionIsDirectional = %s, composingBase = %i, composingExtent = %i\n", - text_input.text, text_input.selection_base, text_input.selection_extent, - text_input.selection_affinity_is_downstream? "downstream" : "upstream", - text_input.selection_is_directional? "true" : "false", text_input.composing_base, text_input.composing_extent - ); - - return PlatformChannel_send( +int textin_sync_editing_state() { + return platch_send( TEXT_INPUT_CHANNEL, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCall, .method = "TextInputClient.updateEditingState", - .jsarg = { - .type = kJSArray, + .json_arg = { + .type = kJsonArray, .size = 2, - .array = (struct JSONMsgCodecValue[2]) { - {.type = kJSNumber, .number_value = text_input.transaction_id}, - {.type = kJSObject, .size = 7, + .array = (struct json_value[2]) { + {.type = kJsonNumber, .number_value = text_input.transaction_id}, + {.type = kJsonObject, .size = 7, .keys = (char*[7]) { "text", "selectionBase", "selectionExtent", "selectionAffinity", "selectionIsDirectional", "composingBase", "composingExtent" }, - .values = (struct JSONMsgCodecValue[7]) { - {.type = kJSString, .string_value = text_input.text}, - {.type = kJSNumber, .number_value = text_input.selection_base}, - {.type = kJSNumber, .number_value = text_input.selection_extent}, + .values = (struct json_value[7]) { + {.type = kJsonString, .string_value = text_input.text}, + {.type = kJsonNumber, .number_value = text_input.selection_base}, + {.type = kJsonNumber, .number_value = text_input.selection_extent}, { - .type = kJSString, + .type = kJsonString, .string_value = text_input.selection_affinity_is_downstream ? "TextAffinity.downstream" : "TextAffinity.upstream" }, - {.type = text_input.selection_is_directional? kJSTrue : kJSFalse}, - {.type = kJSNumber, .number_value = text_input.composing_base}, - {.type = kJSNumber, .number_value = text_input.composing_extent} + {.type = text_input.selection_is_directional? kJsonTrue : kJsonFalse}, + {.type = kJsonNumber, .number_value = text_input.composing_base}, + {.type = kJsonNumber, .number_value = text_input.composing_extent} } } } @@ -406,7 +363,7 @@ int TextInput_syncEditingState() { ); } -int TextInput_performAction(enum text_input_action action) { +int textin_perform_action(enum text_input_action action) { char *action_str = (action == kTextInputActionNone) ? "TextInputAction.none" : @@ -423,17 +380,17 @@ int TextInput_performAction(enum text_input_action action) { (action == kTextInputActionEmergencyCall) ? "TextInputAction.emergencyCall" : "TextInputAction.newline"; - return PlatformChannel_send( + return platch_send( TEXT_INPUT_CHANNEL, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCall, .method = "TextInputClient.performAction", - .jsarg = { - .type = kJSArray, + .json_arg = { + .type = kJsonArray, .size = 2, - .array = (struct JSONMsgCodecValue[2]) { - {.type = kJSNumber, .number_value = text_input.transaction_id}, - {.type = kJSString, .string_value = action_str} + .array = (struct json_value[2]) { + {.type = kJsonNumber, .number_value = text_input.transaction_id}, + {.type = kJsonString, .string_value = action_str} } } }, @@ -441,15 +398,15 @@ int TextInput_performAction(enum text_input_action action) { ); } -int TextInput_onConnectionClosed(void) { +int textin_on_connection_closed(void) { text_input.transaction_id = -1; - return PlatformChannel_send( + return platch_send( TEXT_INPUT_CHANNEL, - &(struct ChannelObject) { + &(struct platch_obj) { .codec = kJSONMethodCall, .method = "TextInputClient.onConnectionClosed", - .jsarg = {.type = kJSNull} + .json_arg = {.type = kJsonNull} }, kBinaryCodec, NULL, NULL ); @@ -468,7 +425,7 @@ inline int to_byte_index(unsigned int symbol_index) { } // start and end index are both inclusive. -int TextInput_erase(unsigned int start, unsigned int end) { +int textin_erase(unsigned int start, unsigned int end) { // 0 <= start <= end < len char *start_str = utf8_symbol_at(text_input.text, start); @@ -479,18 +436,18 @@ int TextInput_erase(unsigned int start, unsigned int end) { return start; } -bool TextInput_deleteSelected(void) { +bool textin_delete_selected(void) { // erase selected text - text_input.selection_base = TextInput_erase(text_input.selection_base, text_input.selection_extent-1); + text_input.selection_base = textin_erase(text_input.selection_base, text_input.selection_extent-1); text_input.selection_extent = text_input.selection_base; return true; } -bool TextInput_addUtf8Char(char *c) { +bool textin_add_utf8_char(char *c) { size_t symbol_length; char *to_move; if (text_input.selection_base != text_input.selection_extent) - TextInput_deleteSelected(); + textin_delete_selected(); // find out where in our string we need to insert the utf8 symbol @@ -516,32 +473,32 @@ bool TextInput_addUtf8Char(char *c) { return true; } -bool TextInput_backspace(void) { +bool textin_backspace(void) { if (text_input.selection_base != text_input.selection_extent) - return TextInput_deleteSelected(); + return textin_delete_selected(); if (text_input.selection_base != 0) { int base = text_input.selection_base - 1; - text_input.selection_base = TextInput_erase(base, base); + text_input.selection_base = textin_erase(base, base); text_input.selection_extent = text_input.selection_base; return true; } return false; } -bool TextInput_delete(void) { +bool textin_delete(void) { if (text_input.selection_base != text_input.selection_extent) - return TextInput_deleteSelected(); + return textin_delete_selected(); if (text_input.selection_base < strlen(text_input.text)) { - text_input.selection_base = TextInput_erase(text_input.selection_base, text_input.selection_base); + text_input.selection_base = textin_erase(text_input.selection_base, text_input.selection_base); text_input.selection_extent = text_input.selection_base; return true; } return false; } -bool TextInput_moveCursorToBeginning(void) { +bool textin_move_cursor_to_beginning(void) { if ((text_input.selection_base != 0) || (text_input.selection_extent != 0)) { text_input.selection_base = 0; text_input.selection_extent = 0; @@ -550,7 +507,7 @@ bool TextInput_moveCursorToBeginning(void) { return false; } -bool TextInput_moveCursorToEnd(void) { +bool textin_move_cursor_to_end(void) { int end = strlen(text_input.text); if (text_input.selection_base != end) { @@ -561,7 +518,7 @@ bool TextInput_moveCursorToEnd(void) { return false; } -bool TextInput_moveCursorForward(void) { +bool textin_move_cursor_forward(void) { if (text_input.selection_base != text_input.selection_extent) { text_input.selection_base = text_input.selection_extent; return true; @@ -575,7 +532,7 @@ bool TextInput_moveCursorForward(void) { return false; } -bool TextInput_moveCursorBack(void) { +bool textin_move_cursor_back(void) { if (text_input.selection_base != text_input.selection_extent) { text_input.selection_extent = text_input.selection_base; return true; @@ -592,20 +549,20 @@ bool TextInput_moveCursorBack(void) { // these two functions automatically sync the editing state with flutter if -// a change ocurred, so you don't explicitly need to call TextInput_syncEditingState(). +// a change ocurred, so you don't explicitly need to call textin_sync_editing_state(). // `c` doesn't need to be NULL-terminated, the length of the char will be calculated // using the start byte. -int TextInput_onUtf8Char(char *c) { +int textin_on_utf8_char(char *c) { if (text_input.transaction_id == -1) return 0; - if (TextInput_addUtf8Char(c)) - return TextInput_syncEditingState(); + if (textin_add_utf8_char(c)) + return textin_sync_editing_state(); return 0; } -int TextInput_onKey(glfw_key key) { +int textin_on_key(glfw_key key) { bool needs_sync = false; bool perform_action = false; int ok; @@ -615,26 +572,26 @@ int TextInput_onKey(glfw_key key) { switch (key) { case GLFW_KEY_LEFT: - needs_sync = TextInput_moveCursorBack(); + needs_sync = textin_move_cursor_back(); break; case GLFW_KEY_RIGHT: - needs_sync = TextInput_moveCursorForward(); + needs_sync = textin_move_cursor_forward(); break; case GLFW_KEY_END: - needs_sync = TextInput_moveCursorToEnd(); + needs_sync = textin_move_cursor_to_end(); break; case GLFW_KEY_HOME: - needs_sync = TextInput_moveCursorToBeginning(); + needs_sync = textin_move_cursor_to_beginning(); break; case GLFW_KEY_BACKSPACE: - needs_sync = TextInput_backspace(); + needs_sync = textin_backspace(); break; case GLFW_KEY_DELETE: - needs_sync = TextInput_delete(); + needs_sync = textin_delete(); break; case GLFW_KEY_ENTER: if (text_input.input_type == kInputTypeMultiline) - needs_sync = TextInput_addUtf8Char("\n"); + needs_sync = textin_add_utf8_char("\n"); perform_action = true; break; @@ -643,12 +600,12 @@ int TextInput_onKey(glfw_key key) { } if (needs_sync) { - ok = TextInput_syncEditingState(); + ok = textin_sync_editing_state(); if (ok != 0) return ok; } if (perform_action) { - ok = TextInput_performAction(text_input.input_action); + ok = textin_perform_action(text_input.input_action); if (ok != 0) return ok; } @@ -656,18 +613,23 @@ int TextInput_onKey(glfw_key key) { } -int TextInput_init(void) { +int textin_init(void) { + int ok; + + printf("[test_input] Initializing...\n"); + text_input.text[0] = '\0'; text_input.warned_about_autocorrect = false; - PluginRegistry_setReceiver(TEXT_INPUT_CHANNEL, kJSONMethodCall, TextInput_onReceive); + ok = plugin_registry_set_receiver(TEXT_INPUT_CHANNEL, kJSONMethodCall, textin_on_receive); + if (ok != 0) return ok; - printf("[text_input] init.\n"); + printf("[text_input] Done.\n"); return 0; } -int TextInput_deinit(void) { +int textin_deinit(void) { printf("[text_input] deinit.\n"); return 0;