Skip to content

Commit 4add19e

Browse files
committed
fixes GLFW Plugin thread safety
Using HandleFuncSync instead of HandleFunc to ensure the plugin method handler is called from the go-flutter main thread. fixes go-flutter-desktop/go-flutter#214
1 parent 9bf03bd commit 4add19e

File tree

2 files changed

+42
-30
lines changed

2 files changed

+42
-30
lines changed

draggable_borderless/go/cmd/options.go

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ var options = []flutter.Option{
1616
type AppBarDraggable struct {
1717
window *glfw.Window
1818
windowDragActive chan bool
19+
cursorPosY int
20+
cursorPosX int
1921
}
2022

2123
var _ flutter.Plugin = &AppBarDraggable{} // compile-time type check
@@ -27,7 +29,8 @@ func (p *AppBarDraggable) InitPlugin(messenger plugin.BinaryMessenger) error {
2729
p.windowDragActive = make(chan bool)
2830
channel := plugin.NewMethodChannel(messenger, "samples.go-flutter.dev/draggable", plugin.StandardMethodCodec{})
2931
channel.HandleFunc("onPanStart", p.onPanStart)
30-
channel.HandleFunc("onPanEnd", p.onPanEnd)
32+
channel.HandleFuncSync("onPanUpdate", p.onPanUpdate) // MUST RUN ON THE MAIN THREAD (use of HandleFuncSync)
33+
channel.HandleFunc("onClose", p.onClose)
3134
return nil
3235
}
3336

@@ -37,31 +40,31 @@ func (p *AppBarDraggable) InitPluginGLFW(window *glfw.Window) error {
3740
return nil
3841
}
3942

40-
// onPanStart a golang / flutter implemantation of:
43+
// onPanStart/onPanUpdate a golang / flutter implemantation of:
4144
// "GLFW how to drag undecorated window without lag"
4245
// https://stackoverflow.com/a/46205940
4346
func (p *AppBarDraggable) onPanStart(arguments interface{}) (reply interface{}, err error) {
4447
argumentsMap := arguments.(map[interface{}]interface{})
45-
cursorPosX := int(argumentsMap["dx"].(float64))
46-
cursorPosY := int(argumentsMap["dy"].(float64))
47-
for {
48-
select {
49-
case <-p.windowDragActive:
50-
return
51-
default:
52-
xpos, ypos := p.window.GetCursorPos()
53-
deltaX := int(xpos) - cursorPosX
54-
deltaY := int(ypos) - cursorPosY
48+
p.cursorPosX = int(argumentsMap["dx"].(float64))
49+
p.cursorPosY = int(argumentsMap["dy"].(float64))
50+
return nil, nil
51+
}
52+
53+
// onPanUpdate calls GLFW functions that aren't thread safe.
54+
// to run function on the main go-flutter thread, use HandleFuncSync instead of HandleFunc!
55+
func (p *AppBarDraggable) onPanUpdate(arguments interface{}) (reply interface{}, err error) {
56+
xpos, ypos := p.window.GetCursorPos() // This function must only be called from the main thread.
57+
deltaX := int(xpos) - p.cursorPosX
58+
deltaY := int(ypos) - p.cursorPosY
5559

56-
x, y := p.window.GetPos()
57-
p.window.SetPos(x+deltaX, y+deltaY)
58-
}
60+
x, y := p.window.GetPos() // This function must only be called from the main thread.
61+
p.window.SetPos(x+deltaX, y+deltaY) // This function must only be called from the main thread.
5962

60-
}
6163
return nil, nil
6264
}
6365

64-
func (p *AppBarDraggable) onPanEnd(arguments interface{}) (reply interface{}, err error) {
65-
p.windowDragActive <- false
66+
func (p *AppBarDraggable) onClose(arguments interface{}) (reply interface{}, err error) {
67+
// This function may be called from any thread. Access is not synchronized.
68+
p.window.SetShouldClose(true)
6669
return nil, nil
6770
}

draggable_borderless/lib/main_desktop.dart

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class MyApp extends StatelessWidget {
1414
@override
1515
Widget build(BuildContext context) {
1616
return MaterialApp(
17+
debugShowCheckedModeBanner: false,
1718
title: 'Flutter Code Sample for widgets.Listener',
1819
theme: ThemeData(
1920
// If the host is missing some fonts, it can cause the
@@ -22,11 +23,7 @@ class MyApp extends StatelessWidget {
2223
primarySwatch: Colors.blue,
2324
),
2425
home: Scaffold(
25-
appBar: DraggebleAppBar(
26-
appBar: AppBar(
27-
title: Text("Draggable borderless"),
28-
),
29-
),
26+
appBar: DraggebleAppBar(title: "Draggable borderless"),
3027
body: Center(
3128
child: Column(
3229
mainAxisAlignment: MainAxisAlignment.center,
@@ -47,25 +44,37 @@ class DraggebleAppBar extends StatelessWidget implements PreferredSizeWidget {
4744
static const platform_channel_draggable =
4845
MethodChannel('samples.go-flutter.dev/draggable');
4946

50-
final AppBar appBar;
47+
AppBar appBar;
5148

52-
const DraggebleAppBar({Key key, this.appBar}) : super(key: key);
49+
DraggebleAppBar({@required String title}) {
50+
this.appBar = AppBar(
51+
title: Text(title),
52+
actions: <Widget>[
53+
new IconButton(
54+
icon: new Icon(Icons.close),
55+
onPressed: () async =>
56+
await platform_channel_draggable.invokeMethod("onClose"),
57+
),
58+
],
59+
leading: new Container(),
60+
);
61+
}
5362

5463
@override
5564
Widget build(BuildContext context) {
5665
return GestureDetector(
57-
child: appBar, onPanStart: onPanStart, onPanEnd: onPanEnd);
66+
child: appBar, onPanStart: onPanStart, onPanUpdate: onPanUpdate);
5867
}
5968

6069
@override
6170
Size get preferredSize => new Size.fromHeight(kToolbarHeight);
6271

72+
void onPanUpdate(DragUpdateDetails details) async {
73+
await platform_channel_draggable.invokeMethod('onPanUpdate');
74+
}
75+
6376
void onPanStart(DragStartDetails details) async {
6477
await platform_channel_draggable.invokeMethod('onPanStart',
6578
{"dx": details.globalPosition.dx, "dy": details.globalPosition.dy});
6679
}
67-
68-
void onPanEnd(DragEndDetails details) async {
69-
await platform_channel_draggable.invokeMethod('onPanEnd');
70-
}
7180
}

0 commit comments

Comments
 (0)