Skip to content

Commit

Permalink
feat: Add toxcore FFI.
Browse files Browse the repository at this point in the history
  • Loading branch information
iphydf committed Jan 28, 2025
1 parent 2b0345b commit cf38087
Show file tree
Hide file tree
Showing 42 changed files with 851 additions and 88 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ app.*.map.json
/android/app/release

# Generated files
**/generated/*
*.freezed.dart
*.g.dart
2 changes: 2 additions & 0 deletions .restyled.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
exclude:
- "LICENSE"
# Generated files.
- "**/CMakeLists.txt"
- "linux/**/*.cc"
- "linux/**/*.h"
- "tools/include/**/*.h"
- "web/flutter_bootstrap.js"
- "windows/**/*.cc"
- "windows/**/*.h"
1 change: 1 addition & 0 deletions android/app/src/main/jniLibs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.so
6 changes: 6 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ ignorePaths:
- LICENSE*
- "*.json"
words:
- armeabi
- btox
- dylib
- ffigen
- iphydf
- kannywood
- libtoxcore
- malloc
- nospam
- robinlinden
- toxcore
- yanciman
4 changes: 4 additions & 0 deletions ios/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ Runner/GeneratedPluginRegistrant.*
!default.mode2v3
!default.pbxuser
!default.perspectivev3

# Vendored toxcore library.
/pkgconfig
*.dylib*
18 changes: 18 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
6469D7212D4729C6008E5F67 /* libtoxcore.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 6469D7202D4729C6008E5F67 /* libtoxcore.2.dylib */; };
6469D7222D4729D9008E5F67 /* libtoxcore.2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6469D7202D4729C6008E5F67 /* libtoxcore.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
Expand All @@ -35,6 +37,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
6469D7222D4729D9008E5F67 /* libtoxcore.2.dylib in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -51,6 +54,7 @@
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
5ADD64A60EF720FCF56734A0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
5BC5ED0188408D84785CE7F3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
6469D7202D4729C6008E5F67 /* libtoxcore.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libtoxcore.2.dylib; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
Expand Down Expand Up @@ -81,6 +85,7 @@
buildActionMask = 2147483647;
files = (
033C5F025F913405F7C9F291 /* Pods_Runner.framework in Frameworks */,
6469D7212D4729C6008E5F67 /* libtoxcore.2.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -113,6 +118,7 @@
children = (
AA2B125A12517F8F71057DEB /* Pods_Runner.framework */,
F10E7E459976E7D29922FCDB /* Pods_RunnerTests.framework */,
6469D7202D4729C6008E5F67 /* libtoxcore.2.dylib */,
);
name = Frameworks;
sourceTree = "<group>";
Expand Down Expand Up @@ -476,6 +482,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
PRODUCT_BUNDLE_IDENTIFIER = chat.tox.btox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
Expand Down Expand Up @@ -658,6 +668,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
PRODUCT_BUNDLE_IDENTIFIER = chat.tox.btox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
Expand All @@ -681,6 +695,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
PRODUCT_BUNDLE_IDENTIFIER = chat.tox.btox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
Expand Down
58 changes: 58 additions & 0 deletions lib/api/toxcore/tox.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
final class ApiException<T extends Enum> implements Exception {
final T error;

const ApiException(this.error);

@override
String toString() {
return 'ApiException: $error';
}
}

abstract class Tox {
String get address;

bool get isAlive;

int get iterationInterval;

set name(String value);

set statusMessage(String value);

void addTcpRelay(String host, int port, String publicKey);

void bootstrap(String host, int port, String publicKey);

List<String> iterate();

void kill();
}

abstract class ToxConstants {
const ToxConstants();

int get addressSize;
int get conferenceIdSize;
int get fileIdLength;
int get groupChatIdSize;
int get groupMaxCustomLosslessPacketLength;
int get groupMaxCustomLossyPacketLength;
int get groupMaxGroupNameLength;
int get groupMaxMessageLength;
int get groupMaxPartLength;
int get groupMaxPasswordSize;
int get groupMaxTopicLength;
int get groupPeerPublicKeySize;
int get hashLength;
int get maxCustomPacketSize;
int get maxFilenameLength;
int get maxFriendRequestLength;
int get maxHostnameLength;
int get maxMessageLength;
int get maxNameLength;
int get maxStatusMessageLength;
int get nospamSize;
int get publicKeySize;
int get secretKeySize;
}
11 changes: 11 additions & 0 deletions lib/api/toxcore/tox_options.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
final class ToxOptions {
final bool ipv6Enabled;
final bool udpEnabled;
final bool localDiscoveryEnabled;

const ToxOptions({
this.ipv6Enabled = true,
this.udpEnabled = true,
this.localDiscoveryEnabled = true,
});
}
61 changes: 42 additions & 19 deletions lib/btox_app.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import 'package:btox/api/toxcore/tox.dart';
import 'package:btox/db/database.dart';
import 'package:btox/pages/contact_list_page.dart';
import 'package:btox/pages/create_profile_page.dart';
import 'package:btox/pages/select_profile_page.dart';
import 'package:btox/providers/database.dart';
import 'package:btox/providers/tox.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'btox_app.g.dart';

@riverpod
Future<(Database, ToxConstants)> initial(Ref ref) async {
final database = await ref.watch(databaseProvider.future);
final constants = await ref.watch(toxConstantsProvider.future);
return (database, constants);
}

final class BtoxApp extends ConsumerWidget {
const BtoxApp({super.key});
Expand All @@ -20,7 +32,7 @@ final class BtoxApp extends ConsumerWidget {
brightness: Brightness.dark,
primarySwatch: Colors.blue,
),
home: ref.watch(databaseProvider).when(
home: ref.watch(initialProvider).when(
loading: () => const Scaffold(
body: Center(
child: CircularProgressIndicator(),
Expand All @@ -31,26 +43,37 @@ final class BtoxApp extends ConsumerWidget {
child: Text('Error: $error'),
),
),
data: (db) => StreamBuilder<List<Profile>>(
stream: db.watchProfiles(),
builder: (context, snapshot) {
final profiles = snapshot.data ?? const [];
if (profiles.isEmpty) {
return const CreateProfilePage();
}
data: (init) {
final (database, constants) = init;
return StreamBuilder<List<Profile>>(
stream: database.watchProfiles(),
builder: (context, snapshot) {
final profiles = snapshot.data ?? const [];
if (profiles.isEmpty) {
return CreateProfilePage(
constants: constants,
database: database,
);
}

final activeProfiles =
profiles.where((profile) => profile.active).toList();
if (activeProfiles.isEmpty) {
return SelectProfilePage(profiles: profiles);
}
final activeProfiles =
profiles.where((profile) => profile.active);
if (activeProfiles.isEmpty) {
return SelectProfilePage(
constants: constants,
database: database,
profiles: profiles,
);
}

return ContactListPage(
database: db,
profile: activeProfiles.first,
);
},
),
return ContactListPage(
constants: constants,
database: database,
profile: activeProfiles.first,
);
},
);
},
),
);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/ffi/tox_constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'tox_constants.native.dart'
if (dart.library.html) 'tox_constants.web.dart'
if (dart.library.js) 'tox_constants.web.dart';
56 changes: 56 additions & 0 deletions lib/ffi/tox_constants.native.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:btox/api/toxcore/tox.dart' as api;
import 'package:btox/ffi/generated/toxcore.native.dart';

final class ToxConstants extends api.ToxConstants {
@override
final int addressSize = tox_address_size();
@override
final int conferenceIdSize = tox_conference_id_size();
@override
final int fileIdLength = tox_file_id_length();
@override
final int groupChatIdSize = tox_group_chat_id_size();
@override
final int groupMaxCustomLosslessPacketLength =
tox_group_max_custom_lossless_packet_length();
@override
final int groupMaxCustomLossyPacketLength =
tox_group_max_custom_lossy_packet_length();
@override
final int groupMaxGroupNameLength = tox_group_max_group_name_length();
@override
final int groupMaxMessageLength = tox_group_max_message_length();
@override
final int groupMaxPartLength = tox_group_max_part_length();
@override
final int groupMaxPasswordSize = tox_group_max_password_size();
@override
final int groupMaxTopicLength = tox_group_max_topic_length();
@override
final int groupPeerPublicKeySize = tox_group_peer_public_key_size();
@override
final int hashLength = tox_hash_length();
@override
final int maxCustomPacketSize = tox_max_custom_packet_size();
@override
final int maxFilenameLength = tox_max_filename_length();
@override
final int maxFriendRequestLength = tox_max_friend_request_length();
@override
final int maxHostnameLength = tox_max_hostname_length();
@override
final int maxMessageLength = tox_max_message_length();
@override
final int maxNameLength = tox_max_name_length();
@override
final int maxStatusMessageLength = tox_max_status_message_length();
@override
final int nospamSize = tox_nospam_size();
@override
final int publicKeySize = tox_public_key_size();
@override
final int secretKeySize = tox_secret_key_size();

// Not const so we can have initialization calls into native code.
ToxConstants();
}
52 changes: 52 additions & 0 deletions lib/ffi/tox_constants.web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'package:btox/api/toxcore/tox.dart' as api;

final class ToxConstants extends api.ToxConstants {
@override
final int addressSize = 38;
@override
final int conferenceIdSize = 32;
@override
final int fileIdLength = 32;
@override
final int groupChatIdSize = 32;
@override
final int groupMaxCustomLosslessPacketLength = 1373;
@override
final int groupMaxCustomLossyPacketLength = 1373;
@override
final int groupMaxGroupNameLength = 48;
@override
final int groupMaxMessageLength = 1372;
@override
final int groupMaxPartLength = 128;
@override
final int groupMaxPasswordSize = 32;
@override
final int groupMaxTopicLength = 512;
@override
final int groupPeerPublicKeySize = 32;
@override
final int hashLength = 32;
@override
final int maxCustomPacketSize = 1373;
@override
final int maxFilenameLength = 255;
@override
final int maxFriendRequestLength = 921;
@override
final int maxHostnameLength = 255;
@override
final int maxMessageLength = 1372;
@override
final int maxNameLength = 128;
@override
final int maxStatusMessageLength = 1007;
@override
final int nospamSize = 4;
@override
final int publicKeySize = 32;
@override
final int secretKeySize = 32;

const ToxConstants();
}
3 changes: 3 additions & 0 deletions lib/ffi/toxcore.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'toxcore.native.dart'
if (dart.library.html) 'toxcore.web.dart'
if (dart.library.js) 'toxcore.web.dart';
Loading

0 comments on commit cf38087

Please sign in to comment.