Skip to content

Commit

Permalink
charset_converter plugin (ardera#361)
Browse files Browse the repository at this point in the history
Adds platform-side implementation of charset_converter plugin.

See: https://pub.dev/packages/charset_converter
  • Loading branch information
bojidartonchev authored Oct 16, 2023
1 parent 8f0b9fe commit 6a28738
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ option(BUILD_GSTREAMER_VIDEO_PLAYER_PLUGIN "Include the gstreamer based video pl
option(BUILD_GSTREAMER_AUDIO_PLAYER_PLUGIN "Include the gstreamer based audio plugins in the finished binary." ON)
option(TRY_BUILD_GSTREAMER_VIDEO_PLAYER_PLUGIN "Don't throw an error if the gstreamer libs aren't found, instead just don't build the gstreamer video player plugin in that case." ON)
option(TRY_BUILD_GSTREAMER_AUDIO_PLAYER_PLUGIN "Don't throw an error if the gstreamer libs aren't found, instead just don't build gstreamer audio plugin." ON)
option(BUILD_CHARSET_CONVERTER_PLUGIN "Include the charset converter plugin in the finished binary." OFF)
option(ENABLE_OPENGL "Build with EGL/OpenGL rendering support." ON)
option(TRY_ENABLE_OPENGL "Don't throw an error if EGL/OpenGL aren't found, instead just build without EGL/OpenGL support in that case." ON)
option(ENABLE_VULKAN "Build with Vulkan rendering support." OFF)
Expand Down Expand Up @@ -338,6 +339,10 @@ if (BUILD_GSTREAMER_AUDIO_PLAYER_PLUGIN)
endif()
endif()

if (BUILD_CHARSET_CONVERTER_PLUGIN)
target_sources(flutterpi_module PRIVATE src/plugins/charset_converter.c)
endif()

# Needed so dart VM can actually resolve symbols in the same
# executable. (For dart:ffi DynamicLibrary.executable / DynamicLibrary.process)
target_link_options(flutterpi_module PUBLIC -rdynamic)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ This is why I created my own (userspace) touchscreen driver, for improved latenc
| linux_spidev ([package](https://pub.dev/packages/linux_spidev/)) ([repo](https://github.com/ardera/flutter_packages/tree/main/packages/linux_spidev)) | 🖨 peripherals | Hannes Winkler | SPI bus support for dart/flutter, uses kernel interfaces directly for more performance. |
| dart_periphery ([package](https://pub.dev/packages/dart_periphery)) ([repo](https://github.com/pezi/dart_periphery)) | 🖨 peripherals | [Peter Sauer](https://github.com/pezi/) | All-in-one package GPIO, I2C, SPI, Serial, PWM, Led, MMIO support using c-periphery. |
| flutterpi_gstreamer_video_player ([package](https://pub.dev/packages/flutterpi_gstreamer_video_player)) ([repo](https://github.com/ardera/flutter_packages/tree/main/packages/flutterpi_gstreamer_video_player)) | ⏯️ multimedia | Hannes Winkler | Official video player implementation for flutter-pi. See [GStreamer video player](#gstreamer-video-player) section above. |
| charset_converter ([package](https://pub.dev/packages/charset_converter)) ([repo](https://github.com/pr0gramista/charset_converter)) | 🗚 encoding | Bartosz Wiśniewski | Encode and decode charsets using platform built-in converter. |
## 💬 Discord
There a `#custom-embedders` channel on the [flutter discord](https://github.com/flutter/flutter/wiki/Chat) which you can use if you have any questions regarding flutter-pi or generally, anything related to embedding the engine for which you don't want to open issue about or write an email.
249 changes: 249 additions & 0 deletions src/plugins/charset_converter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#include "plugins/charset_converter.h"
#include "flutter-pi.h"
#include "pluginregistry.h"
#include "util/logging.h"
#include <iconv.h>

static bool convert(char *inbuf, char *outbuf, size_t len, const char *from, const char *to)
{
iconv_t iconv_cd = iconv_open(to, from);
if (iconv_cd == (iconv_t) -1) {
LOG_ERROR("Conversion from charset \"%s\" to charset \"%s\" is not supported. iconv_open: %s\n", from, to, strerror(errno));
return false;
}

size_t inlen = len;
size_t outlen = len;
size_t res = 0;

while (inlen > 0 && outlen > 0) {
res = iconv(iconv_cd, &inbuf, &inlen, &outbuf, &outlen);
if (res == 0) {
break;
}

if (res == (size_t) (-1)) {
iconv_close(iconv_cd);
*outbuf = '\0';

return false;
}
}

iconv_close(iconv_cd);
*outbuf = '\0';

return true;
}

static int on_encode(struct platch_obj *object, FlutterPlatformMessageResponseHandle *response_handle) {
struct std_value *args, *tmp;
char *charset, *input, *output;

args = &object->std_arg;

if (args == NULL || !STDVALUE_IS_MAP(*args)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg` to be a map.");
}

tmp = stdmap_get_str(&object->std_arg, "charset");
if (tmp == NULL || !STDVALUE_IS_STRING(*tmp)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg['charset'] to be a string.");
}

charset = STDVALUE_AS_STRING(*tmp);

tmp = stdmap_get_str(&object->std_arg, "data");
if (tmp == NULL || !STDVALUE_IS_STRING(*tmp)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg['data'] to be a string.");
}

input = STDVALUE_AS_STRING(*tmp);
output = malloc(strlen(input) + 1);

bool res = convert(input, output, strlen(input) + 1, "UTF-8", charset);
if(!res) {
free(output);
return platch_respond_error_std(response_handle, "error_id", "charset_name_unrecognized", NULL);
}

int ok = platch_respond_success_std(
response_handle,
&(struct std_value) {
.type = kStdUInt8Array,
.size = strlen(output),
.uint8array = (uint8_t*) output,
}
);

free(output);

return ok;
}

static int on_decode(struct platch_obj *object, FlutterPlatformMessageResponseHandle *response_handle) {
struct std_value *args, *tmp;
char *charset, *output;
const uint8_t *input;

args = &object->std_arg;

if (args == NULL || !STDVALUE_IS_MAP(*args)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg` to be a map.");
}

tmp = stdmap_get_str(&object->std_arg, "charset");
if (tmp == NULL || !STDVALUE_IS_STRING(*tmp)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg['charset'] to be a string.");
}

charset = STDVALUE_AS_STRING(*tmp);

tmp = stdmap_get_str(&object->std_arg, "data");
if (tmp == NULL || (*tmp).type != kStdUInt8Array ) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg['data'] to be a uint8_t list.");
}

input = tmp->uint8array;
output = malloc(strlen((char*) input) + 1);

bool res = convert((char*) input, output, strlen((char*) input) + 1, "UTF-8", charset);
if(!res) {
free(output);
return platch_respond_error_std(response_handle, "error_id", "charset_name_unrecognized", NULL);
}

int ok = platch_respond_success_std(
response_handle,
&(struct std_value) {
.type = kStdUInt8Array,
.size = strlen(output),
.uint8array = (uint8_t*) output,
}
);

free(output);

return ok;
}

static int on_available_charsets(struct platch_obj *object, FlutterPlatformMessageResponseHandle *response_handle) {
(void) object;

char* output;
size_t length, count;
FILE *fp;

// Count the available charsets
fp = popen("iconv --list | wc -w", "r");
if(!fp) {
return platch_respond_error_std(response_handle, "error_id", "charsets_not_available", NULL);
}

while (getline(&output, &length, fp) >= 0) {
count = atoi(output);
}

pclose(fp);

fp = popen("iconv --list", "r");
if(!fp) {
return platch_respond_error_std(response_handle, "error_id", "charsets_not_available", NULL);
}

struct std_value values;

values.type = kStdList;
values.size = count;
values.list = alloca(sizeof(struct std_value) * count);

for (int index = 0; index < count; index++) {
if(getline(&output, &length, fp) < 0) {
break;
}

strtok(output, "/");

values.list[index].type = kStdString;
values.list[index].string_value = strdup(output);
}

pclose(fp);

return platch_respond_success_std(response_handle, &values);
}

static int on_check(struct platch_obj *object, FlutterPlatformMessageResponseHandle *response_handle) {
struct std_value *args, *tmp;
char *charset;

args = &object->std_arg;

if (args == NULL || !STDVALUE_IS_MAP(*args)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg` to be a map.");
}

tmp = stdmap_get_str(&object->std_arg, "charset");
if (tmp == NULL || !STDVALUE_IS_STRING(*tmp)) {
return platch_respond_illegal_arg_std(response_handle, "Expected `arg['charset'] to be a string.");
}

charset = STDVALUE_AS_STRING(*tmp);

iconv_t iconv_cd = iconv_open("UTF-8", charset);
if (iconv_cd == (iconv_t) -1) {
return platch_respond(
response_handle,
&(struct platch_obj){ .codec = kStandardMethodCallResponse, .success = true, .std_result = { .type = kStdFalse } }
);
}

iconv_close(iconv_cd);

return platch_respond(
response_handle,
&(struct platch_obj){ .codec = kStandardMethodCallResponse, .success = true, .std_result = { .type = kStdTrue } }
);
}

static int on_receive(char *channel, struct platch_obj *object, FlutterPlatformMessageResponseHandle *response_handle) {
(void) channel;

const char *method;
method = object->method;

if (streq(method, "encode")) {
return on_encode(object, response_handle);
} else if (streq(method, "decode")) {
return on_decode(object, response_handle);
} else if (streq(method, "availableCharsets")) {
return on_available_charsets(object, response_handle);
} else if (streq(method, "check")) {
return on_check(object, response_handle);
}

return platch_respond_not_implemented(response_handle);
}

enum plugin_init_result charset_converter_init(struct flutterpi *flutterpi, void **userdata_out) {
(void) flutterpi;

int ok;

ok = plugin_registry_set_receiver_locked(CHARSET_CONVERTER_CHANNEL, kStandardMethodCall, on_receive);
if (ok != 0) {
return PLUGIN_INIT_RESULT_ERROR;
}

*userdata_out = NULL;

return PLUGIN_INIT_RESULT_INITIALIZED;
}

void charset_converter_deinit(struct flutterpi *flutterpi, void *userdata) {
(void) userdata;

plugin_registry_remove_receiver_v2_locked(flutterpi_get_plugin_registry(flutterpi), CHARSET_CONVERTER_CHANNEL);
}

FLUTTERPI_PLUGIN("charset converter plugin", charset_converter_plugin, charset_converter_init, charset_converter_deinit)
9 changes: 9 additions & 0 deletions src/plugins/charset_converter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef _CHARSET_CONVERTER_PLUGIN_H
#define _CHARSET_CONVERTER_PLUGIN_H

#include <stdio.h>
#include <string.h>

#define CHARSET_CONVERTER_CHANNEL "charset_converter"

#endif

0 comments on commit 6a28738

Please sign in to comment.