Skip to content

Commit add39c3

Browse files
committed
update readme, reformat flutter-pi.c
1 parent e3e357d commit add39c3

File tree

2 files changed

+136
-126
lines changed

2 files changed

+136
-126
lines changed

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
A light-weight Flutter Engine Embedder for Raspberry Pi. Inspired by https://github.com/chinmaygarde/flutter_from_scratch.
33
Flutter-pi also runs without X11, so you don't need to boot into Raspbian Desktop & have X11 and LXDE load up; just boot into the command-line.
44

5-
Currently supported are basic, pure-dart Apps (not using any plugins), mouse input (no mouse cursor yet), touchscreen input, and the StandardMethodCodec method-channels.
5+
Currently supported are basic, pure-dart Apps (not using any plugins), mouse input (no mouse cursor yet), touchscreen input, and the StandardMethodCodec method-channels (currently needs fixing).
66
Not yet supported are JSON method-channels. Generally, flutter-pi is not yet ready to be used as a base for your project.
77

88
## Running
99
This branch (feature-v3d-anholt) doesn't support the legacy GL driver anymore. You need to activate the anholt v3d driver in raspi-config. Go to raspi-config -> Advanced -> GL Driver -> and select fake-KMS. Full-KMS is a bit buggy and doesn't work with the Raspberry Pi 7" display (or generally, any DSI display).
1010

11+
For some reason performance is much better when I gave the GPU only 16M RAM in fake-kms. I don't know why.
12+
1113
Also, you need to tell flutter-pi which input device to use and whether it's a touchscreen or mouse. Input devices are typically located at `/dev/input/...`. Just run `evtest` (`sudo apt install evtest`) to find out which exact path you should use. Currently only one input device is supported by flutter-pi.
1214

1315
Run using
@@ -46,4 +48,13 @@ mkdir out
4648
cc -D_GNU_SOURCE \
4749
`pkg-config --cflags --libs dri gbm libdrm glesv2 egl` -lrt -lflutter_engine -lpthread -ldl \
4850
./src/flutter-pi.c ./src/methodchannel.c -o ./out/flutter-pi
49-
```
51+
```
52+
53+
## Performance
54+
Performance is actually better than I expected. With most of the apps inside the `flutter SDK -> examples -> catalog` directory I get smooth 50-60fps.
55+
56+
## Touchscreen Bug
57+
~~If you use the official 7 inch touchscreen, performance will feel much worse while dragging something. This seems to be some bug in the touchscreen driver. The embedder / userspace only gets around 25 touch events a second, meaning that while dragging something (like in tabbed_app_bar.dart), the position of the object being dragged is only updated 25 times a second. This results in the app looking like it runs at 25fps. The touchscreen could do up to 100 touch updates a second though.~~
58+
59+
[This has been fixed.](https://github.com/raspberrypi/linux/issues/3227) If you want to get the fix, you can run (rpi-update)[https://github.com/hexxeh/rpi-update], which will update your system to the newest version.
60+

src/flutter-pi.c

Lines changed: 123 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ uint32_t refresh_rate;
5050
// this is the pixel ratio (used by flutter) for the Official Raspberry Pi 7inch display.
5151
// if a HDMI screen is connected and being used by this application, the pixel ratio will be
5252
// computed inside init_display.
53-
// for DSI the pixel ratio can not be calculated, because there's no way to query the physical
53+
// for DSI the pixel ratio can not be calculated, because there's no (general) way to query the physical
5454
// size for DSI displays.
5555
double pixel_ratio = 1.3671;
5656

@@ -99,13 +99,14 @@ struct {
9999
} flutter = {0};
100100

101101
struct {
102-
bool is_mouse;
103102
char device_path[128];
104103
int fd;
105104
double x, y;
106105
uint8_t button;
107106
struct TouchscreenSlot ts_slots[10];
108107
struct TouchscreenSlot* ts_slot;
108+
bool is_mouse;
109+
bool is_touchscreen;
109110
} input = {0};
110111

111112
pthread_t io_thread_id;
@@ -218,7 +219,7 @@ bool present(void* userdata) {
218219
next_bo = gbm_surface_lock_front_buffer(gbm.surface);
219220
fb = drm_fb_get_from_bo(next_bo);
220221

221-
/*
222+
/* wait for vsync,
222223
ok = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &drm.waiting_for_flip);
223224
if (ok) {
224225
fprintf(stderr, "failed to queue page flip: %s\n", strerror(errno));
@@ -395,14 +396,8 @@ void on_platform_message(const FlutterPlatformMessage* message, void* user
395396
MethodChannel_freeMethodCall(&methodcall);
396397
}
397398
void vsync_callback(void* userdata, intptr_t baton) {
398-
flutter.next_vblank_baton = baton;
399-
400-
drmVBlank vbl;
401-
vbl.request.type = DRM_VBLANK_EVENT | DRM_VBLANK_RELATIVE;
402-
vbl.request.sequence = 1;
403-
vbl.request.signal = SIGUSR2;
404-
405-
drmWaitVBlank(drm.fd, &vbl);
399+
// not yet implemented
400+
fprintf(stderr, "flutter vsync callback not yet implemented\n");
406401
}
407402

408403

@@ -657,8 +652,6 @@ bool init_display(void) {
657652
return false;
658653
}
659654

660-
661-
662655
/**********************
663656
* EGL INITIALIZATION *
664657
**********************/
@@ -831,7 +824,7 @@ bool init_display(void) {
831824
return true;
832825
}
833826
void destroy_display(void) {
834-
827+
fprintf(stderr, "Deinitializing display not yet implemented\n");
835828
}
836829

837830
bool init_application(void) {
@@ -851,6 +844,14 @@ bool init_application(void) {
851844
flutter.args.struct_size = sizeof(FlutterProjectArgs);
852845
flutter.args.assets_path = flutter.asset_bundle_path;
853846
flutter.args.icu_data_path = flutter.icu_data_path;
847+
flutter.args.isolate_snapshot_data_size = 0;
848+
flutter.args.isolate_snapshot_data = NULL;
849+
flutter.args.isolate_snapshot_instructions_size = 0;
850+
flutter.args.isolate_snapshot_instructions = NULL;
851+
flutter.args.vm_snapshot_data_size = 0;
852+
flutter.args.vm_snapshot_data = NULL;
853+
flutter.args.vm_snapshot_instructions_size = 0;
854+
flutter.args.vm_snapshot_instructions = NULL;
854855
flutter.args.command_line_argc = flutter.engine_argc;
855856
flutter.args.command_line_argv = flutter.engine_argv;
856857
flutter.args.platform_message_callback = on_platform_message;
@@ -936,70 +937,68 @@ void* io_loop(void* userdata) {
936937
1
937938
) == kSuccess;
938939
if (!ok) return false;
939-
}
940940

941-
// mouse
942-
while (input.is_mouse) {
943-
// read up to 64 input events
944-
int rd = read(input.fd, &event, sizeof(struct input_event)*64);
945-
if (rd < (int) sizeof(struct input_event)) {
946-
fprintf(stderr, "Read %d bytes from input device, should have been %d; error msg: %s\n", rd, sizeof(struct input_event), strerror(errno));
947-
return false;
948-
}
941+
// mouse
942+
while (1) {
943+
// read up to 64 input events
944+
int rd = read(input.fd, &event, sizeof(struct input_event)*64);
945+
if (rd < (int) sizeof(struct input_event)) {
946+
fprintf(stderr, "Read %d bytes from input device, should have been %d; error msg: %s\n", rd, sizeof(struct input_event), strerror(errno));
947+
return false;
948+
}
949949

950-
// process the input events
951-
// TODO: instead of processing an input event, and then send the single resulting Pointer Event (i.e., one at a time) to the Flutter Engine,
952-
// process all input events, and send all resulting pointer events at once.
953-
for (int i = 0; i < rd / sizeof(struct input_event); i++) {
954-
phase = kCancel;
955-
ev = &(event[i]);
956-
957-
if (ev->type == EV_REL) {
958-
if (ev->code == REL_X) { // mouse moved in the x-direction
959-
input.x += ev->value;
960-
phase = input.button ? kMove : kHover;
961-
} else if (ev->code == REL_Y) { // mouse moved in the y-direction
962-
input.y += ev->value;
963-
phase = input.button ? kMove : kHover;
964-
}
965-
} else if (ev->type == EV_ABS) {
966-
if (ev->code == ABS_X) {
967-
input.x = ev->value;
968-
phase = input.button ? kMove : kHover;
969-
} else if (ev->code == ABS_Y) {
970-
input.y = ev->value;
971-
phase = input.button ? kMove : kHover;
950+
// process the input events
951+
// TODO: instead of processing an input event, and then send the single resulting Pointer Event (i.e., one at a time) to the Flutter Engine,
952+
// process all input events, and send all resulting pointer events at once.
953+
for (int i = 0; i < rd / sizeof(struct input_event); i++) {
954+
phase = kCancel;
955+
ev = &(event[i]);
956+
957+
if (ev->type == EV_REL) {
958+
if (ev->code == REL_X) { // mouse moved in the x-direction
959+
input.x += ev->value;
960+
phase = input.button ? kMove : kHover;
961+
} else if (ev->code == REL_Y) { // mouse moved in the y-direction
962+
input.y += ev->value;
963+
phase = input.button ? kMove : kHover;
964+
}
965+
} else if (ev->type == EV_ABS) {
966+
if (ev->code == ABS_X) {
967+
input.x = ev->value;
968+
phase = input.button ? kMove : kHover;
969+
} else if (ev->code == ABS_Y) {
970+
input.y = ev->value;
971+
phase = input.button ? kMove : kHover;
972+
}
973+
} else if ((ev->type == EV_KEY) && ((ev->code == BTN_LEFT) || (ev->code == BTN_RIGHT))) {
974+
// either the left or the right mouse button was pressed
975+
// the 1st bit in "button" is set when BTN_LEFT is down. the 2nd bit when BTN_RIGHT is down.
976+
uint8_t mask = ev->code == BTN_LEFT ? 1 : 2;
977+
if (ev->value == 1) input.button |= mask;
978+
else input.button &= ~mask;
979+
980+
phase = ev->value == 1 ? kDown : kUp;
972981
}
973-
} else if ((ev->type == EV_KEY) && ((ev->code == BTN_LEFT) || (ev->code == BTN_RIGHT))) {
974-
// either the left or the right mouse button was pressed
975-
// the 1st bit in "button" is set when BTN_LEFT is down. the 2nd bit when BTN_RIGHT is down.
976-
uint8_t mask = ev->code == BTN_LEFT ? 1 : 2;
977-
if (ev->value == 1) input.button |= mask;
978-
else input.button &= ~mask;
979982

980-
phase = ev->value == 1 ? kDown : kUp;
981-
}
982-
983-
if (phase != kCancel) {
984-
// if something changed, send the pointer event to flutter
985-
ok = FlutterEngineSendPointerEvent(
986-
engine,
987-
& (FlutterPointerEvent) {
988-
.struct_size = sizeof(FlutterPointerEvent),
989-
.timestamp = (size_t) (FlutterEngineGetCurrentTime()*1000),
990-
.phase=phase, .x=input.x, .y=input.y,
991-
.signal_kind = kFlutterPointerSignalKindNone
992-
},
993-
1
994-
) == kSuccess;
995-
if (!ok) return false;
983+
if (phase != kCancel) {
984+
// if something changed, send the pointer event to flutter
985+
ok = FlutterEngineSendPointerEvent(
986+
engine,
987+
& (FlutterPointerEvent) {
988+
.struct_size = sizeof(FlutterPointerEvent),
989+
.timestamp = (size_t) (ev->time.tv_sec * 1000000ul) + ev->time.tv_usec,
990+
.phase=phase, .x=input.x, .y=input.y,
991+
.signal_kind = kFlutterPointerSignalKindNone
992+
},
993+
1
994+
) == kSuccess;
995+
if (!ok) return false;
996+
}
996997
}
997-
}
998-
999-
printf("mouse position: %f, %f\n", input.x, input.y);
1000-
}
1001998

1002-
if (!input.is_mouse) {
999+
printf("mouse position: %f, %f\n", input.x, input.y);
1000+
}
1001+
} else if (input.is_touchscreen) {
10031002
for (int j = 0; j<10; j++) {
10041003
printf("Sending kAdd %d to Flutter Engine\n", j);
10051004
input.ts_slots[j].id = -1;
@@ -1023,65 +1022,63 @@ void* io_loop(void* userdata) {
10231022
}
10241023

10251024
input.ts_slot = &(input.ts_slots[0]);
1026-
}
1027-
1028-
// touchscreen
1029-
while (!input.is_mouse) {
1030-
int rd = read(input.fd, &event, sizeof(struct input_event)*64);
1031-
if (rd < (int) sizeof(struct input_event)) {
1032-
perror("error reading from input device");
1033-
return false;
1034-
}
1025+
while (1) {
1026+
int rd = read(input.fd, &event, sizeof(struct input_event)*64);
1027+
if (rd < (int) sizeof(struct input_event)) {
1028+
perror("error reading from input device");
1029+
return false;
1030+
}
10351031

1036-
n_pointerevents = 0;
1037-
for (int i = 0; i < rd / sizeof(struct input_event); i++) {
1038-
ev = &(event[i]);
1039-
1040-
if (ev->type == EV_ABS) {
1041-
if (ev->code == ABS_MT_SLOT) {
1042-
input.ts_slot = &(input.ts_slots[ev->value]);
1043-
} else if (ev->code == ABS_MT_TRACKING_ID) {
1044-
if (input.ts_slot->id == -1) {
1045-
input.ts_slot->id = ev->value;
1046-
input.ts_slot->phase = kDown;
1047-
} else if (ev->value == -1) {
1048-
input.ts_slot->id = ev->value;
1049-
input.ts_slot->phase = kUp;
1032+
n_pointerevents = 0;
1033+
for (int i = 0; i < rd / sizeof(struct input_event); i++) {
1034+
ev = &(event[i]);
1035+
1036+
if (ev->type == EV_ABS) {
1037+
if (ev->code == ABS_MT_SLOT) {
1038+
input.ts_slot = &(input.ts_slots[ev->value]);
1039+
} else if (ev->code == ABS_MT_TRACKING_ID) {
1040+
if (input.ts_slot->id == -1) {
1041+
input.ts_slot->id = ev->value;
1042+
input.ts_slot->phase = kDown;
1043+
} else if (ev->value == -1) {
1044+
input.ts_slot->id = ev->value;
1045+
input.ts_slot->phase = kUp;
1046+
}
1047+
} else if (ev->code == ABS_MT_POSITION_X) {
1048+
input.ts_slot->x = ev->value;
1049+
if (input.ts_slot->phase == kCancel) input.ts_slot->phase = kMove;
1050+
} else if (ev->code == ABS_MT_POSITION_Y) {
1051+
input.ts_slot->y = ev->value;
1052+
if (input.ts_slot->phase == kCancel) input.ts_slot->phase = kMove;
10501053
}
1051-
} else if (ev->code == ABS_MT_POSITION_X) {
1052-
input.ts_slot->x = ev->value;
1053-
if (input.ts_slot->phase == kCancel) input.ts_slot->phase = kMove;
1054-
} else if (ev->code == ABS_MT_POSITION_Y) {
1055-
input.ts_slot->y = ev->value;
1056-
if (input.ts_slot->phase == kCancel) input.ts_slot->phase = kMove;
1057-
}
1058-
} else if ((ev->type == EV_SYN) && (ev->code == SYN_REPORT)) {
1059-
for (int j = 0; j < 10; j++) {
1060-
if (input.ts_slots[j].phase != kCancel) {
1061-
pointer_event[n_pointerevents++] = (FlutterPointerEvent) {
1062-
.struct_size = sizeof(FlutterPointerEvent),
1063-
.phase = input.ts_slots[j].phase,
1064-
.timestamp = (size_t) (FlutterEngineGetCurrentTime()*1000),
1065-
.device = j,
1066-
.x = input.ts_slots[j].x,
1067-
.y = input.ts_slots[j].y,
1068-
.signal_kind = kFlutterPointerSignalKindNone
1069-
};
1070-
input.ts_slots[j].phase = kCancel;
1054+
} else if ((ev->type == EV_SYN) && (ev->code == SYN_REPORT)) {
1055+
for (int j = 0; j < 10; j++) {
1056+
if (input.ts_slots[j].phase != kCancel) {
1057+
pointer_event[n_pointerevents++] = (FlutterPointerEvent) {
1058+
.struct_size = sizeof(FlutterPointerEvent),
1059+
.phase = input.ts_slots[j].phase,
1060+
.timestamp = (size_t) (ev->time.tv_sec * 1000000ul) + ev->time.tv_usec,
1061+
.device = j,
1062+
.x = input.ts_slots[j].x,
1063+
.y = input.ts_slots[j].y,
1064+
.signal_kind = kFlutterPointerSignalKindNone
1065+
};
1066+
input.ts_slots[j].phase = kCancel;
1067+
}
10711068
}
10721069
}
10731070
}
1074-
}
10751071

1076-
ok = FlutterEngineSendPointerEvent(
1077-
engine,
1078-
pointer_event,
1079-
n_pointerevents
1080-
) == kSuccess;
1072+
ok = FlutterEngineSendPointerEvent(
1073+
engine,
1074+
pointer_event,
1075+
n_pointerevents
1076+
) == kSuccess;
10811077

1082-
if (!ok) {
1083-
fprintf(stderr, "Error sending pointer events to flutter\n");
1084-
return false;
1078+
if (!ok) {
1079+
fprintf(stderr, "Error sending pointer events to flutter\n");
1080+
return false;
1081+
}
10851082
}
10861083
}
10871084

@@ -1116,13 +1113,15 @@ bool parse_cmd_args(int argc, char **argv) {
11161113
printf("Using mouse input from mouse %s\n", optarg);
11171114
snprintf(input.device_path, sizeof(input.device_path), "%s", optarg);
11181115
input.is_mouse = true;
1116+
input.is_touchscreen = false;
11191117

11201118
index++;
11211119
break;
11221120
case 't':
11231121
printf("Using touchscreen input from %s\n", optarg);
11241122
snprintf(input.device_path, sizeof(input.device_path), "%s", optarg);
11251123
input.is_mouse = false;
1124+
input.is_touchscreen = true;
11261125

11271126
index++;
11281127
break;

0 commit comments

Comments
 (0)