Skip to content

Commit 2d32e3a

Browse files
authored
Merge pull request #16 from Goddchen/feature/clipboard
feat: implement clipboard support
2 parents 3e80175 + 66a1b60 commit 2d32e3a

9 files changed

+507
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,7 @@
3636
## 0.5.0
3737

3838
- Add key event support
39+
40+
## 0.6.0
41+
42+
- Add clipboard support

lib/src/client/remote_frame_buffer_client.dart

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:dart_rfb/src/constants.dart';
1313
import 'package:dart_rfb/src/extensions/byte_data_extensions.dart';
1414
import 'package:dart_rfb/src/extensions/int_extensions.dart';
1515
import 'package:dart_rfb/src/extensions/raw_socket_extensions.dart';
16+
import 'package:dart_rfb/src/protocol/client_cut_text_message.dart';
1617
import 'package:dart_rfb/src/protocol/client_init_message.dart';
1718
import 'package:dart_rfb/src/protocol/encoding_type.dart';
1819
import 'package:dart_rfb/src/protocol/frame_buffer_update_message.dart';
@@ -24,6 +25,7 @@ import 'package:dart_rfb/src/protocol/protocol_version_handshake_message.dart';
2425
import 'package:dart_rfb/src/protocol/security_handshake_message.dart';
2526
import 'package:dart_rfb/src/protocol/security_result_handshake_message.dart';
2627
import 'package:dart_rfb/src/protocol/security_type.dart';
28+
import 'package:dart_rfb/src/protocol/server_cut_text_message.dart';
2729
import 'package:dart_rfb/src/protocol/server_init_message.dart';
2830
import 'package:dart_rfb/src/protocol/set_encodings_message.dart';
2931
import 'package:dart_rfb/src/protocol/set_pixel_format_message.dart';
@@ -35,25 +37,32 @@ import 'package:logging/logging.dart';
3537
class RemoteFrameBufferClient {
3638
static final Logger logger = Logger('RemoteFrameBufferClient');
3739

38-
final StreamController<RemoteFrameBufferClientUpdate>
39-
_updateStreamController =
40-
StreamController<RemoteFrameBufferClientUpdate>.broadcast();
41-
4240
Option<Config> _config = none();
4341

44-
Option<RawSocket> _socket = none();
42+
Option<String> _password = none();
4543

4644
bool _readLoopRunning = false;
4745

48-
Option<String> _password = none();
49-
5046
Option<RemoteFrameBufferProtocolVersion> _selectedProtocolVersion = none();
5147

5248
Option<RemoteFrameBufferSecurityType> _selectedSecurityType = none();
5349

50+
Option<RawSocket> _socket = none();
51+
52+
final StreamController<String> _serverClipBoardStreamController =
53+
StreamController<String>();
54+
55+
final StreamController<RemoteFrameBufferClientUpdate>
56+
_updateStreamController =
57+
StreamController<RemoteFrameBufferClientUpdate>.broadcast();
58+
5459
/// The config used by the underlying session.
5560
Option<Config> get config => _config;
5661

62+
/// A [Stream] that will give access to the server's clipboard updates.
63+
Stream<String> get serverClipBoardStream =>
64+
_serverClipBoardStreamController.stream;
65+
5766
/// A [Stream] that will give access to all incoming framebuffer updates.
5867
Stream<RemoteFrameBufferClientUpdate> get updateStream =>
5968
_updateStreamController.stream;
@@ -116,6 +125,19 @@ class RemoteFrameBufferClient {
116125
),
117126
);
118127

128+
void sendClientCutText({
129+
required final String text,
130+
}) =>
131+
_socket.match(
132+
() {},
133+
(final RawSocket socket) {
134+
final RemoteFrameBufferClientCutTextMessage message =
135+
RemoteFrameBufferClientCutTextMessage(text: text);
136+
logger.info('> $message');
137+
socket.write(message.toBytes().buffer.asUint8List());
138+
},
139+
);
140+
119141
void sendKeyEvent({
120142
required final RemoteFrameBufferClientKeyEvent keyEvent,
121143
}) =>
@@ -234,13 +256,20 @@ class RemoteFrameBufferClient {
234256
// no data, just ignore for now
235257
break;
236258
case 3: // ServerCutText
237-
final int length =
238-
(await socket.readSync(length: 7).run()).getUint32(3);
239-
socket.readSync(length: length);
259+
final RemoteFrameBufferServerCutTextMessage message =
260+
(await RemoteFrameBufferServerCutTextMessage
261+
.readFromSocket(socket: socket)
262+
.run())
263+
.getOrElse(
264+
(final Object error) => throw Exception(
265+
'Error reading server cut text: $error',
266+
),
267+
);
268+
logger.info('< $message');
269+
_serverClipBoardStreamController.add(message.text);
240270
break;
241271
default:
242-
logger.log(
243-
Level.INFO,
272+
logger.info(
244273
'Receive unsupported message type: $messageType',
245274
);
246275
break;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'dart:convert';
2+
import 'dart:typed_data';
3+
4+
import 'package:freezed_annotation/freezed_annotation.dart';
5+
6+
part 'client_cut_text_message.freezed.dart';
7+
8+
@freezed
9+
class RemoteFrameBufferClientCutTextMessage
10+
with _$RemoteFrameBufferClientCutTextMessage {
11+
const factory RemoteFrameBufferClientCutTextMessage({
12+
required final String text,
13+
}) = _RemoteFrameBufferClientCutTextMessage;
14+
15+
const RemoteFrameBufferClientCutTextMessage._();
16+
17+
ByteData toBytes() {
18+
final String text = this.text.replaceAll('\r', '');
19+
return ByteData.sublistView(
20+
(BytesBuilder()
21+
..add(
22+
(ByteData(8)
23+
..setUint8(0, 6)
24+
..setUint32(4, text.length))
25+
.buffer
26+
.asUint8List(),
27+
)
28+
..add(latin1.encode(text)))
29+
.toBytes(),
30+
);
31+
}
32+
}

lib/src/protocol/client_cut_text_message.freezed.dart

Lines changed: 147 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
import 'dart:typed_data';
4+
5+
import 'package:dart_rfb/src/extensions/raw_socket_extensions.dart';
6+
import 'package:fpdart/fpdart.dart';
7+
import 'package:freezed_annotation/freezed_annotation.dart';
8+
9+
part 'server_cut_text_message.freezed.dart';
10+
11+
@freezed
12+
class RemoteFrameBufferServerCutTextMessage
13+
with _$RemoteFrameBufferServerCutTextMessage {
14+
const factory RemoteFrameBufferServerCutTextMessage({
15+
required final String text,
16+
}) = _RemoteFrameBufferServerCutTextMessage;
17+
18+
const RemoteFrameBufferServerCutTextMessage._();
19+
20+
ByteData toBytes() => ByteData.sublistView(
21+
(BytesBuilder()
22+
..add(
23+
(ByteData(8)
24+
..setUint8(0, 3)
25+
..setUint32(4, text.length))
26+
.buffer
27+
.asUint8List(),
28+
)
29+
..add(latin1.encode(text.replaceAll('\r', ''))))
30+
.toBytes(),
31+
);
32+
33+
static TaskEither<Object, RemoteFrameBufferServerCutTextMessage>
34+
readFromSocket({
35+
required final RawSocket socket,
36+
}) =>
37+
TaskEither<Object, RemoteFrameBufferServerCutTextMessage>.tryCatch(
38+
() async {
39+
final int length =
40+
(await socket.readSync(length: 7).run()).getUint32(3);
41+
final String text = latin1.decode(
42+
(await socket.readSync(length: length).run())
43+
.buffer
44+
.asUint8List(),
45+
);
46+
return RemoteFrameBufferServerCutTextMessage(text: text);
47+
},
48+
(final Object error, final _) => error,
49+
);
50+
}

0 commit comments

Comments
 (0)