main.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. // Use these to incrementally fix warnings with cargo fix
  19. #![allow(warnings, unused)]
  20. //#![deny(unused_imports)]
  21. use async_lock::{Mutex as AsyncMutex, RwLock as AsyncRwLock};
  22. use darkfi::system::CondVar;
  23. use darkfi_serial::{deserialize, Decodable, Encodable};
  24. use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
  25. use std::{
  26. io::Cursor,
  27. sync::{mpsc, Arc, OnceLock},
  28. };
  29. #[macro_use]
  30. extern crate log;
  31. #[allow(unused_imports)]
  32. use log::LevelFilter;
  33. #[derive(Debug)]
  34. pub enum AndroidSuggestEvent {
  35. Init,
  36. CreateInputConnect,
  37. Compose { text: String, cursor_pos: i32, is_commit: bool },
  38. ComposeRegion { start: usize, end: usize },
  39. FinishCompose,
  40. DeleteSurroundingText { left: usize, right: usize },
  41. }
  42. #[cfg(target_os = "android")]
  43. mod android;
  44. mod app;
  45. mod build_info;
  46. mod error;
  47. mod expr;
  48. mod gfx;
  49. mod logger;
  50. mod mesh;
  51. mod net;
  52. mod plugin;
  53. mod prop;
  54. mod pubsub;
  55. //mod py;
  56. mod ringbuf;
  57. mod scene;
  58. mod shape;
  59. mod text;
  60. mod text2;
  61. mod ui;
  62. mod util;
  63. use crate::{
  64. app::{App, AppPtr},
  65. gfx::EpochIndex,
  66. net::ZeroMQAdapter,
  67. prop::{
  68. Property, PropertyAtomicGuard, PropertyBool, PropertyStr, PropertySubType, PropertyType,
  69. Role,
  70. },
  71. scene::{CallArgType, SceneNode, SceneNodePtr, SceneNodeType, Slot},
  72. text::TextShaper,
  73. ui::chatview,
  74. util::AsyncRuntime,
  75. };
  76. // This is historical, but ideally we can fix the entire project and remove this import.
  77. pub use util::ExecutorPtr;
  78. macro_rules! d { ($($arg:tt)*) => { debug!(target: "main", $($arg)*); } }
  79. macro_rules! t { ($($arg:tt)*) => { trace!(target: "main", $($arg)*); } }
  80. macro_rules! i { ($($arg:tt)*) => { info!(target: "main", $($arg)*); } }
  81. macro_rules! w { ($($arg:tt)*) => { warn!(target: "main", $($arg)*); } }
  82. macro_rules! e { ($($arg:tt)*) => { error!(target: "main", $($arg)*); } }
  83. // Hides the cmd.exe terminal on Windows.
  84. // Enable this when making release builds.
  85. //#![windows_subsystem = "windows"]
  86. fn panic_hook(panic_info: &std::panic::PanicHookInfo) {
  87. error!("panic occurred: {panic_info}");
  88. error!("{}", std::backtrace::Backtrace::force_capture().to_string());
  89. std::process::abort()
  90. }
  91. /// Contains values which persist between app restarts. For example on Android, we are
  92. /// running a foreground service. Everytime the UI restarts main() is called again.
  93. /// However the global state remains intact.
  94. struct God {
  95. bg_runtime: AsyncRuntime,
  96. bg_ex: ExecutorPtr,
  97. fg_runtime: AsyncRuntime,
  98. fg_ex: ExecutorPtr,
  99. /// App must fully finish setup() before start() is allowed to begin.
  100. cv_app_is_setup: Arc<CondVar>,
  101. app: AppPtr,
  102. /// This is the main rendering API used to send commands to the gfx subsystem.
  103. /// We have a ref here so the gfx subsystem can increment the epoch counter.
  104. render_api: gfx::RenderApi,
  105. /// This is how the gfx subsystem receives messages from the render API.
  106. method_rep: async_channel::Receiver<(gfx::EpochIndex, gfx::GraphicsMethod)>,
  107. /// Publisher to send input and window events to subscribers.
  108. event_pub: gfx::GraphicsEventPublisherPtr,
  109. }
  110. impl God {
  111. fn new() -> Self {
  112. info!(target: "main", "Creating the app");
  113. // Abort the application on panic right away
  114. std::panic::set_hook(Box::new(panic_hook));
  115. text2::init_txt_ctx();
  116. logger::setup_logging();
  117. #[cfg(target_os = "android")]
  118. {
  119. use crate::android::get_appdata_path;
  120. // Workaround for this bug
  121. // https://gitlab.torproject.org/tpo/core/arti/-/issues/999
  122. unsafe {
  123. std::env::set_var("HOME", get_appdata_path().as_os_str());
  124. }
  125. }
  126. let exe_path = std::env::current_exe().unwrap();
  127. let basename = exe_path.parent().unwrap();
  128. std::env::set_current_dir(basename);
  129. let bg_ex = Arc::new(smol::Executor::new());
  130. let fg_ex = Arc::new(smol::Executor::new());
  131. let sg_root = SceneNode::root();
  132. let bg_runtime = AsyncRuntime::new(bg_ex.clone(), "bg");
  133. bg_runtime.start();
  134. #[cfg(feature = "enable-netdebug")]
  135. {
  136. let sg_root = sg_root.clone();
  137. let ex = bg_ex.clone();
  138. let zmq_task = bg_ex.spawn(async {
  139. let zmq_rpc = ZeroMQAdapter::new(sg_root, ex).await;
  140. zmq_rpc.run().await;
  141. });
  142. bg_runtime.push_task(zmq_task);
  143. }
  144. let fg_runtime = AsyncRuntime::new(fg_ex.clone(), "fg");
  145. let (method_req, method_rep) = async_channel::unbounded();
  146. // The UI actually needs to be running for this to reply back.
  147. // Otherwise calls will just hang.
  148. let render_api = gfx::RenderApi::new(method_req);
  149. let event_pub = gfx::GraphicsEventPublisher::new();
  150. let text_shaper = TextShaper::new();
  151. let app = App::new(sg_root.clone(), render_api.clone(), text_shaper, fg_ex.clone());
  152. let app2 = app.clone();
  153. let cv_app_is_setup = Arc::new(CondVar::new());
  154. let cv = cv_app_is_setup.clone();
  155. let app_task = fg_ex.spawn(async move {
  156. app2.setup().await;
  157. cv.notify();
  158. });
  159. fg_runtime.push_task(app_task);
  160. #[cfg(feature = "enable-plugins")]
  161. {
  162. let ex = bg_ex.clone();
  163. let cv = cv_app_is_setup.clone();
  164. let plug_task = bg_ex.spawn(async move {
  165. load_plugins(ex, sg_root, cv).await;
  166. });
  167. bg_runtime.push_task(plug_task);
  168. }
  169. #[cfg(not(feature = "enable-plugins"))]
  170. warn!(target: "main", "Plugins are disabled in this build");
  171. Self {
  172. bg_runtime,
  173. bg_ex,
  174. fg_runtime,
  175. fg_ex,
  176. cv_app_is_setup,
  177. app,
  178. render_api,
  179. method_rep,
  180. event_pub,
  181. }
  182. }
  183. /// Start the app. Can only happen once the window is ready.
  184. pub fn start_app(&self, epoch: EpochIndex) {
  185. info!(target: "main", "Starting the app");
  186. #[cfg(target_os = "android")]
  187. {
  188. use crate::android::{get_appdata_path, get_external_storage_path};
  189. info!("App internal data path: {:?}", get_appdata_path());
  190. info!("App external storage path: {:?}", get_external_storage_path());
  191. //let paths = std::fs::read_dir("/data/data/darkfi.darkfi/").unwrap();
  192. //for path in paths {
  193. // debug!("{}", path.unwrap().path().display())
  194. //}
  195. }
  196. info!("Target OS: {}", build_info::TARGET_OS);
  197. info!("Target arch: {}", build_info::TARGET_ARCH);
  198. let cwd = std::env::current_dir().unwrap();
  199. info!("Current dir: {}", cwd.display());
  200. self.fg_runtime.start_with_count(2);
  201. let app = self.app.clone();
  202. let cv = self.cv_app_is_setup.clone();
  203. let event_pub = self.event_pub.clone();
  204. smol::block_on(async move {
  205. cv.wait().await;
  206. app.start(event_pub, epoch).await;
  207. });
  208. }
  209. /// Put the app to sleep until the next restart.
  210. pub fn stop_app(&self) {
  211. self.fg_runtime.stop();
  212. self.app.stop();
  213. info!(target: "main", "App stopped");
  214. }
  215. }
  216. impl std::fmt::Debug for God {
  217. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  218. write!(f, "God")
  219. }
  220. }
  221. pub static GOD: OnceLock<God> = OnceLock::new();
  222. #[cfg(feature = "enable-plugins")]
  223. async fn load_plugins(ex: ExecutorPtr, sg_root: SceneNodePtr, cv: Arc<CondVar>) {
  224. let plugin = SceneNode::new("plugin", SceneNodeType::PluginRoot);
  225. let plugin = plugin.setup_null();
  226. sg_root.clone().link(plugin.clone());
  227. let darkirc = create_darkirc("darkirc");
  228. let darkirc = darkirc
  229. .setup(|me| async {
  230. plugin::DarkIrc::new(me, ex.clone()).await.expect("DarkIrc pimpl setup")
  231. })
  232. .await;
  233. let (slot, recvr) = Slot::new("recvmsg");
  234. darkirc.register("recv", slot).unwrap();
  235. let sg_root2 = sg_root.clone();
  236. let darkirc_nick = PropertyStr::wrap(&darkirc, Role::App, "nick", 0).unwrap();
  237. let listen_recv = ex.spawn(async move {
  238. while let Ok(data) = recvr.recv().await {
  239. let atom = &mut PropertyAtomicGuard::new();
  240. let mut cur = Cursor::new(&data);
  241. let channel = String::decode(&mut cur).unwrap();
  242. let timestamp = chatview::Timestamp::decode(&mut cur).unwrap();
  243. let id = chatview::MessageId::decode(&mut cur).unwrap();
  244. let nick = String::decode(&mut cur).unwrap();
  245. let msg = String::decode(&mut cur).unwrap();
  246. let node_path = format!("/window/{channel}_chat_layer/content/chatty");
  247. t!("Attempting to relay message to {node_path}");
  248. let Some(chatview) = sg_root2.clone().lookup_node(&node_path) else {
  249. d!("Ignoring message since {node_path} doesn't exist");
  250. continue
  251. };
  252. // I prefer to just re-encode because the code is clearer.
  253. let mut data = vec![];
  254. timestamp.encode(&mut data).unwrap();
  255. id.encode(&mut data).unwrap();
  256. nick.encode(&mut data).unwrap();
  257. msg.encode(&mut data).unwrap();
  258. if let Err(err) = chatview.call_method("insert_line", data).await {
  259. error!(
  260. target: "app",
  261. "Call method {node_path}::insert_line({timestamp}, {id}, {nick}, '{msg}'): {err:?}"
  262. );
  263. }
  264. // Apply coloring when you get a message
  265. let chat_path = format!("/window/{channel}_chat_layer");
  266. let chat_layer = sg_root2.clone().lookup_node(chat_path).unwrap();
  267. if chat_layer.get_property_bool("is_visible").unwrap() {
  268. continue
  269. }
  270. let node_path = format!("/window/menu_layer/{channel}_channel_label");
  271. let menu_label = sg_root2.clone().lookup_node(&node_path).unwrap();
  272. let prop = menu_label.get_property("text_color").unwrap();
  273. if msg.contains(&darkirc_nick.get()) {
  274. // Nick highlight
  275. prop.clone().set_f32(atom, Role::App, 0, 0.56).unwrap();
  276. prop.clone().set_f32(atom, Role::App, 1, 0.61).unwrap();
  277. prop.clone().set_f32(atom, Role::App, 2, 1.).unwrap();
  278. prop.clone().set_f32(atom, Role::App, 3, 1.).unwrap();
  279. } else {
  280. // Normal channel activity
  281. prop.clone().set_f32(atom, Role::App, 0, 0.36).unwrap();
  282. prop.clone().set_f32(atom, Role::App, 1, 1.).unwrap();
  283. prop.clone().set_f32(atom, Role::App, 2, 0.51).unwrap();
  284. prop.clone().set_f32(atom, Role::App, 3, 1.).unwrap();
  285. }
  286. }
  287. });
  288. let (slot, recvr) = Slot::new("connect");
  289. darkirc.register("connect", slot).unwrap();
  290. let sg_root2 = sg_root.clone();
  291. let listen_connect = ex.spawn(async move {
  292. cv.wait().await;
  293. let net0 = sg_root2.clone().lookup_node("/window/netstatus_layer/net0").unwrap();
  294. let net1 = sg_root2.clone().lookup_node("/window/netstatus_layer/net1").unwrap();
  295. let net2 = sg_root2.clone().lookup_node("/window/netstatus_layer/net2").unwrap();
  296. let net3 = sg_root2.clone().lookup_node("/window/netstatus_layer/net3").unwrap();
  297. let net0_is_visible = PropertyBool::wrap(&net0, Role::App, "is_visible", 0).unwrap();
  298. let net1_is_visible = PropertyBool::wrap(&net1, Role::App, "is_visible", 0).unwrap();
  299. let net2_is_visible = PropertyBool::wrap(&net2, Role::App, "is_visible", 0).unwrap();
  300. let net3_is_visible = PropertyBool::wrap(&net3, Role::App, "is_visible", 0).unwrap();
  301. while let Ok(data) = recvr.recv().await {
  302. let (peers_count, is_dag_synced): (u32, bool) = deserialize(&data).unwrap();
  303. let atom = &mut PropertyAtomicGuard::new();
  304. if peers_count == 0 {
  305. net0_is_visible.set(atom, true);
  306. net1_is_visible.set(atom, false);
  307. net2_is_visible.set(atom, false);
  308. net3_is_visible.set(atom, false);
  309. continue
  310. }
  311. assert!(peers_count > 0);
  312. if !is_dag_synced {
  313. net0_is_visible.set(atom, false);
  314. net1_is_visible.set(atom, true);
  315. net2_is_visible.set(atom, false);
  316. net3_is_visible.set(atom, false);
  317. continue
  318. }
  319. assert!(peers_count > 0 && is_dag_synced);
  320. if peers_count == 1 {
  321. net0_is_visible.set(atom, false);
  322. net1_is_visible.set(atom, false);
  323. net2_is_visible.set(atom, true);
  324. net3_is_visible.set(atom, false);
  325. continue
  326. }
  327. net0_is_visible.set(atom, false);
  328. net1_is_visible.set(atom, false);
  329. net2_is_visible.set(atom, false);
  330. net3_is_visible.set(atom, true);
  331. }
  332. });
  333. plugin.link(darkirc);
  334. i!("Plugins loaded");
  335. futures::join!(listen_recv, listen_connect);
  336. }
  337. pub fn create_darkirc(name: &str) -> SceneNode {
  338. t!("create_darkirc({name})");
  339. let mut node = SceneNode::new(name, SceneNodeType::Plugin);
  340. let mut prop = Property::new("nick", PropertyType::Str, PropertySubType::Null);
  341. prop.set_ui_text("Nick", "Nickname");
  342. prop.set_defaults_str(vec!["anon".to_string()]).unwrap();
  343. node.add_property(prop).unwrap();
  344. node.add_signal(
  345. "recv",
  346. "Message received",
  347. vec![
  348. ("channel", "Channel", CallArgType::Str),
  349. ("timestamp", "Timestamp", CallArgType::Uint64),
  350. ("id", "ID", CallArgType::Hash),
  351. ("nick", "Nick", CallArgType::Str),
  352. ("msg", "Message", CallArgType::Str),
  353. ],
  354. )
  355. .unwrap();
  356. node.add_signal(
  357. "connect",
  358. "Connections and disconnects",
  359. vec![
  360. ("peers_count", "Peers Count", CallArgType::Uint32),
  361. ("dag_synced", "Is DAG Synced", CallArgType::Bool),
  362. ],
  363. )
  364. .unwrap();
  365. node.add_method(
  366. "send",
  367. vec![("channel", "Channel", CallArgType::Str), ("msg", "Message", CallArgType::Str)],
  368. None,
  369. )
  370. .unwrap();
  371. node
  372. }
  373. fn main() {
  374. GOD.get_or_init(God::new);
  375. // Reuse render_api, event_pub and text_shaper
  376. // No need for setup(), just wait for gfx start then call .start()
  377. // ZMQ, darkirc stay running
  378. gfx::run_gui();
  379. debug!(target: "main", "Started GFX backend");
  380. }
  381. /*
  382. use rustpython_vm::{self as pyvm, convert::ToPyObject};
  383. fn main() {
  384. let module = pyvm::Interpreter::without_stdlib(Default::default()).enter(|vm| {
  385. let source = r#"
  386. def foo():
  387. open("hihi", "w")
  388. return 110
  389. #max(1 + lw/3, 4*10) + foo(2, True)
  390. "#;
  391. //let code_obj = vm
  392. // .compile(source, pyvm::compiler::Mode::Exec, "<embedded>".to_owned())
  393. // .map_err(|err| vm.new_syntax_error(&err, Some(source))).unwrap();
  394. //code_obj
  395. pyvm::import::import_source(vm, "lain", source).unwrap()
  396. });
  397. fn foo(x: u32, y: bool) -> u32 {
  398. if y {
  399. 2 * x
  400. } else {
  401. x
  402. }
  403. }
  404. let res = pyvm::Interpreter::without_stdlib(Default::default()).enter(|vm| {
  405. let globals = vm.ctx.new_dict();
  406. globals.set_item("lw", vm.ctx.new_int(110).to_pyobject(vm), vm).unwrap();
  407. globals.set_item("lh", vm.ctx.new_int(4).to_pyobject(vm), vm).unwrap();
  408. globals.set_item("foo", vm.new_function("foo", foo).into(), vm).unwrap();
  409. let scope = pyvm::scope::Scope::new(None, globals);
  410. let foo_fn = module.get_attr("foo", vm).unwrap();
  411. foo_fn.call((), vm).unwrap()
  412. //vm.run_code_obj(code_obj, scope).unwrap()
  413. });
  414. println!("{:?}", res);
  415. }
  416. */