This commit is contained in:
MeexReay 2025-04-16 02:21:26 +03:00
parent b99311bd7c
commit 440d6c72d8
9 changed files with 1259 additions and 120 deletions

617
Cargo.lock generated
View File

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -77,9 +92,11 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
name = "bRAC"
version = "0.1.2+2.0"
dependencies = [
"cfg-if",
"clap",
"colored",
"crossterm",
"gtk4",
"homedir",
"lazy_static",
"native-tls",
@ -87,6 +104,22 @@ dependencies = [
"regex",
"serde",
"serde_yml",
"tokio",
]
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
@ -101,6 +134,35 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cairo-rs"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7"
dependencies = [
"bitflags",
"cairo-sys-rs",
"glib",
"libc",
]
[[package]]
name = "cairo-sys-rs"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "cc"
version = "1.2.13"
@ -110,6 +172,16 @@ dependencies = [
"shlex",
]
[[package]]
name = "cfg-expr"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -281,6 +353,16 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "field-offset"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
dependencies = [
"memoffset",
"rustc_version",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -296,6 +378,126 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gdk-pixbuf"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7563afd6ff0a221edfbb70a78add5075b8d9cb48e637a40a24c3ece3fea414d0"
dependencies = [
"gdk-pixbuf-sys",
"gio",
"glib",
"libc",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c"
dependencies = [
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "gdk4"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4850c9d9c1aecd1a3eb14fadc1cdb0ac0a2298037e116264c7473e1740a32d60"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
"gdk4-sys",
"gio",
"glib",
"libc",
"pango",
]
[[package]]
name = "gdk4-sys"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f6eb95798e2b46f279cf59005daf297d5b69555428f185650d71974a910473a"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pango-sys",
"pkg-config",
"system-deps",
]
[[package]]
name = "getrandom"
version = "0.3.1"
@ -308,6 +510,203 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gio"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f00c70f8029d84ea7572dd0e1aaa79e5329667b4c17f329d79ffb1e6277487"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys",
"glib",
"libc",
"pin-project-lite",
"smallvec",
]
[[package]]
name = "gio-sys"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"windows-sys 0.59.0",
]
[[package]]
name = "glib"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys",
"glib-macros",
"glib-sys",
"gobject-sys",
"libc",
"memchr",
"smallvec",
]
[[package]]
name = "glib-macros"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68"
dependencies = [
"heck",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "glib-sys"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "graphene-rs"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbc5911bfb32d68dcfa92c9510c462696c2f715548fcd7f3f1be424c739de19"
dependencies = [
"glib",
"graphene-sys",
"libc",
]
[[package]]
name = "graphene-sys"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a"
dependencies = [
"glib-sys",
"libc",
"pkg-config",
"system-deps",
]
[[package]]
name = "gsk4"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61f5e72f931c8c9f65fbfc89fe0ddc7746f147f822f127a53a9854666ac1f855"
dependencies = [
"cairo-rs",
"gdk4",
"glib",
"graphene-rs",
"gsk4-sys",
"libc",
"pango",
]
[[package]]
name = "gsk4-sys"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755059de55fa6f85a46bde8caf03e2184c96bfda1f6206163c72fb0ea12436dc"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
"glib-sys",
"gobject-sys",
"graphene-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "gtk4"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1c491051f030994fd0cde6f3c44f3f5640210308cff1298c7673c47408091d"
dependencies = [
"cairo-rs",
"field-offset",
"futures-channel",
"gdk-pixbuf",
"gdk4",
"gio",
"glib",
"graphene-rs",
"gsk4",
"gtk4-macros",
"gtk4-sys",
"libc",
"pango",
]
[[package]]
name = "gtk4-macros"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "gtk4-sys"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"graphene-sys",
"gsk4-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
@ -416,6 +815,24 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
@ -457,6 +874,15 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.20.3"
@ -507,6 +933,30 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "pango"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1f5dc1b8cf9bc08bfc0843a04ee0fa2e78f1e1fa4b126844a383af4f25f0ec"
dependencies = [
"gio",
"glib",
"libc",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dbb9b751673bd8fe49eb78620547973a1e719ed431372122b20abd12445bab5"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -530,6 +980,18 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.31"
@ -545,6 +1007,15 @@ dependencies = [
"zerocopy 0.7.35",
]
[[package]]
name = "proc-macro-crate"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
@ -632,6 +1103,21 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
@ -702,6 +1188,12 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
version = "1.0.219"
@ -722,6 +1214,15 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "serde_yml"
version = "0.0.12"
@ -773,12 +1274,31 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "strsim"
version = "0.11.1"
@ -796,6 +1316,25 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "system-deps"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
version = "3.16.0"
@ -810,6 +1349,69 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "tokio"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
@ -834,6 +1436,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "version_check"
version = "0.9.5"
@ -1018,6 +1626,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"

View File

@ -14,9 +14,13 @@ serde_yml = "0.0.12"
crossterm = { version = "0.29.0", optional = true }
homedir = { version = "0.3.4", optional = true }
native-tls = { version = "0.2.14", optional = true }
gtk4 = { version = "0.9.6", optional = true }
cfg-if = "1.0.0"
tokio = { version = "1.44.2", optional = true, features = [ "full" ] }
[features]
default = ["ssl", "pretty", "homedir"]
default = ["ssl", "homedir", "gtk_gui"]
ssl = ["dep:native-tls"]
pretty = ["dep:crossterm"]
homedir = ["dep:homedir"]
pretty_tui = ["dep:crossterm"]
gtk_gui = ["dep:gtk4"]
homedir = ["dep:homedir", "dep:tokio"]

BIN
brac_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -59,6 +59,11 @@
features = "default";
deps = with pkgs; [ pkg-config openssl ];
});
packages.bRAC-gtk = (rustPackage {
version = "-gtk";
features = "default gtk_gui";
deps = with pkgs; [ pkg-config openssl gtk4 pango ];
});
packages.bRAC-minimal = (rustPackage {
version = "-minimal";
features = "";

View File

@ -16,6 +16,7 @@ use super::{
use lazy_static::lazy_static;
use regex::Regex;
use cfg_if::cfg_if;
lazy_static! {
pub static ref DATE_REGEX: Regex = Regex::new(r"\[(.*?)\] (.*)").unwrap();
@ -29,15 +30,19 @@ lazy_static! {
];
}
#[cfg(not(feature = "pretty"))]
pub mod minimal_tui;
#[cfg(not(feature = "pretty"))]
pub use minimal_tui::{run_main_loop, update_console};
#[cfg(feature = "pretty")]
pub mod pretty_tui;
#[cfg(feature = "pretty")]
pub use pretty_tui::{run_main_loop, update_console};
cfg_if! {
if #[cfg(feature = "gtk_gui")] {
mod gtk_gui;
pub use gtk_gui::*;
} else if #[cfg(feature = "pretty_tui")] {
mod pretty_tui;
pub use pretty_tui::*;
} else {
mod minimal_tui;
pub use minimal_tui::*;
}
}
pub struct ChatStorage {
@ -94,11 +99,11 @@ const HELP_MESSAGE: &str = "Help message:
pub fn add_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn Error>> {
ctx.messages.append(
ctx.max_messages,
message.split("\n").map(|o| o.to_string()).collect::<Vec<String>>()
);
update_console(ctx)
for i in message.split("\n")
.map(|o| o.to_string()) {
print_message(ctx.clone(), i)?;
}
Ok(())
}
pub fn on_command(ctx: Arc<Context>, command: &str) -> Result<(), Box<dyn Error>> {
@ -130,7 +135,7 @@ pub fn on_command(ctx: Arc<Context>, command: &str) -> Result<(), Box<dyn Error>
match register_user(&mut connect(&ctx.host, ctx.enable_ssl)?, &ctx.name, pass) {
Ok(true) => {
add_message(ctx.clone(), "you was registered successfully bro")?;
*ctx.registered.write().unwrap() = Some(pass.to_string());
*ctx.chat().registered.write().unwrap() = Some(pass.to_string());
},
Ok(false) => add_message(ctx.clone(), "user with this account already exists bruh")?,
Err(e) => add_message(ctx.clone(), &format!("ERROR while registrationing: {}", e))?
@ -142,9 +147,9 @@ pub fn on_command(ctx: Arc<Context>, command: &str) -> Result<(), Box<dyn Error>
};
add_message(ctx.clone(), "ye bro you was logged in")?;
*ctx.registered.write().unwrap() = Some(pass.to_string());
*ctx.chat().registered.write().unwrap() = Some(pass.to_string());
} else if command == "ping" {
let mut before = ctx.messages.packet_size();
let mut before = ctx.chat().messages.packet_size();
let message = format!("Checking ping... {:X}", SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis());
send_message(&mut connect(&ctx.host, ctx.enable_ssl)?, &message)?;
@ -218,7 +223,7 @@ pub fn on_send_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn E
.replace("{text}", &message)
);
if let Some(password) = ctx.registered.read().unwrap().clone() {
if let Some(password) = ctx.chat().registered.read().unwrap().clone() {
send_message_auth(&mut connect(&ctx.host, ctx.enable_ssl)?, &ctx.name, &password, &message)?;
} else if ctx.enable_auth {
send_message_spoof_auth(&mut connect(&ctx.host, ctx.enable_ssl)?, &message)?;
@ -230,73 +235,70 @@ pub fn on_send_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn E
Ok(())
}
pub fn format_message(enable_ip_viewing: bool, message: String) -> Option<String> {
/// message -> (date, ip, text)
pub fn parse_message(message: String) -> Option<(String, Option<String>, String, Option<(String, Color)>)> {
let message = sanitize_text(&message);
let message = message
.trim_start_matches("(UNREGISTERED)")
.trim_start_matches("(UNAUTHORIZED)")
.trim_start_matches("(UNAUTHENTICATED)")
.trim()
.to_string()+" ";
if message.is_empty() {
None
return None
}
let date = DATE_REGEX.captures(&message)?;
let (date, message) = (
date.get(1)?.as_str().to_string(),
date.get(2)?.as_str().to_string(),
);
let (ip, message) = if let Some(message) = IP_REGEX.captures(&message) {
(Some(message.get(1)?.as_str().to_string()), message.get(2)?.as_str().to_string())
} else {
Some(
{
let message = message.clone();
move || -> Option<String> {
let message = sanitize_text(&message);
(None, message)
};
let (message, nick) = match find_username_color(&message) {
Some((name, content, color)) => (content, Some((name, color))),
None => (message, None),
};
let date = DATE_REGEX.captures(&message)?;
let (date, message) = (
date.get(1)?.as_str().to_string(),
date.get(2)?.as_str().to_string(),
);
Some((date, ip, message, nick))
}
let (ip, message) = if let Some(message) = IP_REGEX.captures(&message) {
(Some(message.get(1)?.as_str().to_string()), message.get(2)?.as_str().to_string())
pub fn format_message(enable_ip_viewing: bool, message: String) -> Option<String> {
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
Some(format!(
"{} {}{}",
if enable_ip_viewing {
if let Some(ip) = ip {
format!("{}{} [{}]", ip, " ".repeat(if 15 >= ip.chars().count() {15-ip.chars().count()} else {0}), date)
} else {
(None, message)
};
let message = message
.trim_start_matches("(UNREGISTERED)")
.trim_start_matches("(UNAUTHORIZED)")
.trim_start_matches("(UNAUTHENTICATED)")
.trim()
.to_string()+" ";
let prefix = if enable_ip_viewing {
if let Some(ip) = ip {
format!("{}{} [{}]", ip, " ".repeat(if 15 >= ip.chars().count() {15-ip.chars().count()} else {0}), date)
} else {
format!("{} [{}]", " ".repeat(15), date)
}
} else {
format!("[{}]", date)
};
Some(if let Some(captures) = find_username_color(&message) {
let nick = captures.0;
let content = captures.1;
let color = captures.2;
format!(
"{} {} {}",
prefix.white().dimmed(),
format!("<{}>", nick).color(color).bold(),
content.white().blink()
)
} else {
format!(
"{} {}",
prefix.white().dimmed(),
message.white().blink()
)
})
}()
}.unwrap_or_else(|| {
format!(
"{}",
message.bright_white()
)
}))
format!("{} [{}]", " ".repeat(15), date)
}
} else {
format!("[{}]", date)
}.white().dimmed(),
nick.map(|(name, color)|
format!("<{}> ", name)
.color(color)
.bold()
.to_string()
).unwrap_or_default(),
content.white().blink()
))
} else if !message.is_empty() {
Some(message.bright_white().to_string())
} else {
None
}
}
// message -> (nick, content, color)
pub fn find_username_color(message: &str) -> Option<(String, String, Color)> {
for (re, color) in COLORED_USERNAMES.iter() {
if let Some(captures) = re.captures(message) {
@ -304,4 +306,8 @@ pub fn find_username_color(message: &str) -> Option<(String, String, Color)> {
}
}
None
}
pub fn set_chat(ctx: Arc<Context>, chat: ChatContext) {
*ctx.chat.write().unwrap() = Some(Arc::new(chat));
}

467
src/chat/gtk_gui.rs Normal file
View File

@ -0,0 +1,467 @@
use std::sync::{Arc, RwLock};
use std::time::Duration;
use colored::{Color, Colorize};
use gtk4::gdk::Display;
use gtk4::gdk_pixbuf::PixbufLoader;
use gtk4::glib::clone::Downgrade;
use gtk4::glib::{idle_add_local, idle_add_local_once, ControlFlow, source::timeout_add_once};
use gtk4::{glib, glib::clone, Align, Box as GtkBox, Label, ScrolledWindow};
use gtk4::{CssProvider, Entry, Orientation, Overlay, Picture};
use gtk4::prelude::*;
use gtk4::{Application, ApplicationWindow, Button};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::error::Error;
use std::thread;
use std::cell::RefCell;
use crate::config::Context;
use crate::proto::{connect, read_messages};
use super::{format_message, on_send_message, parse_message, set_chat, ChatStorage};
pub struct ChatContext {
pub messages: Arc<ChatStorage>,
pub registered: Arc<RwLock<Option<String>>>,
pub sender: Sender<String>
}
struct UiModel {
chat_box: GtkBox,
chat_scrolled: ScrolledWindow
}
pub fn add_chat_message(ctx: Arc<Context>, message: String) {
let _ = ctx.chat().sender.send(message);
// MainContext::default().invoke_local(move || {
// ctx.chat().chat_box.upgrade().unwrap().append(&Label::new(Some(message.as_str())));
// });
}
pub fn print_message(ctx: Arc<Context>, message: String) -> Result<(), Box<dyn Error>> {
ctx.chat().messages.append(ctx.max_messages, vec![message.clone()]);
add_chat_message(ctx.clone(), message);
Ok(())
}
pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
match read_messages(
&mut connect(&ctx.host, ctx.enable_ssl)?,
ctx.max_messages,
ctx.chat().messages.packet_size(),
!ctx.enable_ssl,
ctx.enable_chunked
) {
Ok(Some((messages, size))) => {
let messages: Vec<String> = if ctx.disable_formatting {
messages
} else {
messages.into_iter().flat_map(|o| format_message(ctx.enable_ip_viewing, o)).collect()
};
if ctx.enable_chunked {
ctx.chat().messages.append_and_store(ctx.max_messages, messages.clone(), size);
for msg in messages {
add_chat_message(ctx.clone(), msg.clone());
}
} else {
ctx.chat().messages.update(ctx.max_messages, messages.clone(), size);
for msg in messages {
add_chat_message(ctx.clone(), msg.clone());
}
}
},
Err(e) => {
let msg = format!("Read messages error: {}", e.to_string()).bright_red().to_string();
ctx.chat().messages.append(ctx.max_messages, vec![msg.clone()]);
add_chat_message(ctx.clone(), msg.clone());
}
_ => {}
}
thread::sleep(Duration::from_millis(ctx.update_time as u64));
Ok(())
}
fn build_ui(ctx: Arc<Context>, app: &Application) {
let main_box = GtkBox::new(Orientation::Vertical, 5);
main_box.set_margin_bottom(5);
main_box.set_margin_end(5);
main_box.set_margin_start(5);
main_box.set_margin_top(5);
let chat_box = GtkBox::new(Orientation::Vertical, 2);
let chat_scrolled = ScrolledWindow::builder()
.child(&chat_box)
.vexpand(true)
.hexpand(true)
.propagate_natural_height(true)
.build();
main_box.append(&chat_scrolled);
let send_box = GtkBox::new(Orientation::Horizontal, 5);
let text_entry = Entry::builder()
.placeholder_text("Message")
.hexpand(true)
.build();
send_box.append(&text_entry);
let send_btn = Button::builder()
.label("Send")
.build();
send_btn.connect_clicked(clone!(
#[weak] text_entry,
#[weak] ctx,
move |_| {
idle_add_local_once(clone!(
#[weak] text_entry,
move || {
text_entry.set_text("");
}
));
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
let msg = format!("Send message error: {}", e.to_string()).bright_red().to_string();
add_chat_message(ctx.clone(), msg);
}
}
));
text_entry.connect_activate(clone!(
#[weak] text_entry,
#[weak] ctx,
move |_| {
idle_add_local_once(clone!(
#[weak] text_entry,
move || {
text_entry.set_text("");
}
));
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
let msg = format!("Send message error: {}", e.to_string()).bright_red().to_string();
add_chat_message(ctx.clone(), msg);
}
}
));
send_box.append(&send_btn);
main_box.append(&send_box);
let scrolled_window_weak = Downgrade::downgrade(&chat_scrolled);
idle_add_local({
let scrolled_window_weak = scrolled_window_weak.clone();
move || {
if let Some(o) = scrolled_window_weak.upgrade() {
o.vadjustment().set_value(o.vadjustment().upper() - o.vadjustment().page_size());
}
ControlFlow::Break
}
});
let overlay = Overlay::new();
overlay.set_child(Some(&main_box));
let bytes = include_bytes!("../../brac_logo.png");
let loader = PixbufLoader::new();
loader.write(bytes).unwrap();
loader.close().unwrap();
let pixbuf = loader.pixbuf().unwrap();
let logo = Picture::for_pixbuf(&pixbuf);
logo.set_size_request(500, 189);
logo.set_can_target(false);
logo.set_can_focus(false);
logo.set_halign(Align::End);
logo.set_valign(Align::Start);
overlay.add_overlay(&logo);
let window = ApplicationWindow::builder()
.application(app)
.title(format!("bRAC - Connected to {} as {}", &ctx.host, &ctx.name))
.default_width(500)
.default_height(500)
.resizable(false)
.decorated(true)
.child(&overlay)
.build();
window.connect_default_width_notify({
let scrolled_window_weak = scrolled_window_weak.clone();
move |_| {
let scrolled_window_weak = scrolled_window_weak.clone();
idle_add_local(move || {
if let Some(o) = scrolled_window_weak.upgrade() {
o.vadjustment().set_value(o.vadjustment().upper() - o.vadjustment().page_size());
}
ControlFlow::Break
});
}
});
window.show();
let ui = UiModel {
chat_scrolled,
chat_box
};
setup(ctx.clone(), ui);
load_css();
}
fn setup(ctx: Arc<Context>, ui: UiModel) {
let (sender, receiver) = channel();
set_chat(ctx.clone(), ChatContext {
messages: Arc::new(ChatStorage::new()),
registered: Arc::new(RwLock::new(None)),
sender
});
thread::spawn({
let ctx = ctx.clone();
move || {
loop {
if let Err(e) = recv_tick(ctx.clone()) {
let _ = print_message(ctx.clone(), format!("Print messages error: {}", e.to_string()).bright_red().to_string());
thread::sleep(Duration::from_secs(1));
}
}
}
});
let (tx, rx) = channel();
GLOBAL.with(|global| {
*global.borrow_mut() = Some((ui, rx));
});
thread::spawn({
let ctx = ctx.clone();
move || {
while let Ok(message) = receiver.recv() {
let _ = tx.send(message.clone());
let ctx = ctx.clone();
glib::source::timeout_add_once(Duration::ZERO, move || {
GLOBAL.with(|global| {
if let Some((ui, rx)) = &*global.borrow() {
let message: String = rx.recv().unwrap();
on_add_message(ctx.clone(), &ui, message);
}
});
});
}
}
});
}
fn load_css() {
let provider = CssProvider::new();
provider.load_from_data("
.message-content {
color: #FFFFFF;
}
.message-date {
color: #AAAAAA;
}
.message-ip {
color: #AAAAAA;
}
.message-name {
font-weight: bold;
}
.message-name-black {
color: #2E2E2E; /* Темный черный */
}
.message-name-red {
color: #8B0000; /* Темный красный */
}
.message-name-green {
color: #006400; /* Темный зеленый */
}
.message-name-yellow {
color: #8B8B00; /* Темный желтый */
}
.message-name-blue {
color: #00008B; /* Темный синий */
}
.message-name-magenta {
color: #8B008B; /* Темный пурпурный */
}
.message-name-cyan {
color: #008B8B; /* Темный бирюзовый */
}
.message-name-white {
color: #A9A9A9; /* Темный белый */
}
.message-name-bright-black {
color: #555555; /* Яркий черный */
}
.message-name-bright-red {
color: #FF0000; /* Яркий красный */
}
.message-name-bright-green {
color: #00FF00; /* Яркий зеленый */
}
.message-name-bright-yellow {
color: #FFFF00; /* Яркий желтый */
}
.message-name-bright-blue {
color: #0000FF; /* Яркий синий */
}
.message-name-bright-magenta {
color: #FF00FF; /* Яркий пурпурный */
}
.message-name-bright-cyan {
color: #00FFFF; /* Яркий бирюзовый */
}
.message-name-bright-white {
color: #FFFFFF; /* Яркий белый */
}
");
gtk4::style_context_add_provider_for_display(
&Display::default().expect("Could not connect to a display."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
let hbox = GtkBox::new(Orientation::Horizontal, 2);
if let Some(ip) = ip {
if ctx.enable_ip_viewing {
let ip = Label::builder()
.label(ip)
.margin_end(10)
.halign(Align::Start)
.css_classes(["message-ip"])
.build();
hbox.append(&ip);
}
}
let date = Label::builder()
.label(format!("[{date}]"))
.halign(Align::Start)
.css_classes(["message-date"])
.build();
hbox.append(&date);
if let Some((name, color)) = nick {
let color = match color {
Color::Black => "black",
Color::Red => "red",
Color::Green => "green",
Color::Yellow => "yellow",
Color::Blue => "blue",
Color::Magenta => "magenta",
Color::Cyan => "cyan",
Color::White => "white",
Color::BrightBlack => "bright-black",
Color::BrightRed => "bright-red",
Color::BrightGreen => "bright-green",
Color::BrightYellow => "bright-yellow",
Color::BrightBlue => "bright-blue",
Color::BrightMagenta => "bright-magenta",
Color::BrightCyan => "bright-cyan",
Color::BrightWhite => "bright-white",
_ => "unknown"
};
let name = Label::builder()
.label(format!("<{name}>"))
.halign(Align::Start)
.css_classes(["message-name", &format!("message-name-{}", color)])
.build();
hbox.append(&name);
}
let content = Label::builder()
.label(content)
.halign(Align::Start)
.css_classes(["message-content"])
.build();
hbox.append(&content);
ui.chat_box.append(&hbox);
} else {
let content = Label::builder()
.label(message)
.halign(Align::Start)
.css_classes(["message-content"])
.build();
ui.chat_box.append(&content);
}
timeout_add_once(Duration::from_millis(10), move || {
GLOBAL.with(|global| {
if let Some((ui, _)) = &*global.borrow() {
let o = &ui.chat_scrolled;
o.vadjustment().set_value(o.vadjustment().upper() - o.vadjustment().page_size());
}
});
});
}
pub fn run_main_loop(ctx: Arc<Context>) {
let application = Application::builder()
.application_id("ru.themixray.bRAC")
.build();
application.connect_activate({
let ctx = ctx.clone();
move |app| {
build_ui(ctx.clone(), app);
}
});
application.run();
}
thread_local!(
static GLOBAL: RefCell<Option<(UiModel, Receiver<String>)>> = RefCell::new(None);
);

View File

@ -13,7 +13,12 @@ use super::{
}, format_message, on_send_message
};
pub fn update_console(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
pub struct ChatContext {
pub messages: Arc<ChatStorage>,
pub registered: Arc<RwLock<Option<String>>>
}
fn update_console(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
let messages = ctx.messages.messages();
let mut out = stdout().lock();
@ -33,7 +38,17 @@ pub fn update_console(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
Ok(())
}
pub fn print_message(ctx: Arc<Context>, message: String) -> Result<(), Box<dyn Error>> {
ctx.chat().messages.append(ctx.max_messages, vec![message]);
update_console(ctx.clone())
}
pub fn run_main_loop(ctx: Arc<Context>) {
set_chat(ctx.clone(), ChatContext {
messages: Arc::new(ChatStorage::new()),
registered: Arc::new(RwLock::new(None)),
});
loop {
match connect(&ctx.host, ctx.enable_ssl) {
Ok(mut stream) => {

View File

@ -10,7 +10,7 @@ use colored::Colorize;
use std::{
cmp::{max, min},
error::Error, io::{stdout, Write},
sync::{atomic::Ordering, Arc},
sync::{atomic::{AtomicUsize, Ordering}, Arc, RwLock},
thread,
time::Duration
};
@ -20,11 +20,11 @@ use super::{
config::Context,
proto::{connect, read_messages},
util::{char_index_to_byte_index, string_chunks}
}, format_message, on_send_message
}, format_message, on_send_message, set_chat, ChatStorage
};
pub fn print_console(ctx: Arc<Context>, messages: Vec<String>, input: &str) -> Result<(), Box<dyn Error>> {
fn print_console(ctx: Arc<Context>, messages: Vec<String>, input: &str) -> Result<(), Box<dyn Error>> {
let (width, height) = terminal::size()?;
let (width, height) = (width as usize, height as usize);
@ -43,7 +43,7 @@ pub fn print_console(ctx: Arc<Context>, messages: Vec<String>, input: &str) -> R
0
};
let scroll = min(ctx.scroll.load(Ordering::SeqCst), messages_size);
let scroll = min(ctx.chat().scroll.load(Ordering::SeqCst), messages_size);
let scroll_f = ((1f64 - scroll as f64 / (messages_size+1) as f64) * (height-2) as f64).round() as usize+1;
let messages = if height < messages.len() {
@ -143,8 +143,8 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
let mut history_cursor: usize = 0;
let mut cursor: usize = 0;
let input = ctx.input.clone();
let messages = ctx.messages.clone();
let input = ctx.chat().input.clone();
let messages = ctx.chat().messages.clone();
loop {
if !event::poll(Duration::from_millis(50)).unwrap_or(false) { continue }
@ -171,8 +171,8 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
if let Err(e) = on_send_message(ctx.clone(), &message) {
let msg = format!("Send message error: {}", e.to_string()).bright_red().to_string();
ctx.messages.append(ctx.max_messages, vec![msg]);
print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap())?;
ctx.chat().messages.append(ctx.max_messages, vec![msg]);
print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap())?;
}
} else {
print_console(
@ -220,7 +220,11 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
}
KeyCode::PageUp => {
let height = terminal::size().unwrap().1 as usize;
ctx.scroll.store(min(ctx.scroll.load(Ordering::SeqCst)+height, ctx.messages.messages().len()), Ordering::SeqCst);
ctx.chat().scroll.store(min(
ctx.chat().scroll.load(Ordering::SeqCst)+height,
ctx.chat().messages.messages().len()
),
Ordering::SeqCst);
print_console(
ctx.clone(),
messages.messages(),
@ -229,7 +233,11 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
}
KeyCode::PageDown => {
let height = terminal::size().unwrap().1 as usize;
ctx.scroll.store(max(ctx.scroll.load(Ordering::SeqCst), height)-height, Ordering::SeqCst);
ctx.chat().scroll.store(max(
ctx.chat().scroll.load(Ordering::SeqCst),
height
)-height,
Ordering::SeqCst);
print_console(
ctx.clone(),
messages.messages(),
@ -289,7 +297,10 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
Event::Mouse(data) => {
match data.kind {
MouseEventKind::ScrollUp => {
ctx.scroll.store(min(ctx.scroll.load(Ordering::SeqCst)+3, ctx.messages.messages().len()), Ordering::SeqCst);
ctx.chat().scroll.store(min(
ctx.chat().scroll.load(Ordering::SeqCst)+3,
ctx.chat().messages.messages().len()
), Ordering::SeqCst);
print_console(
ctx.clone(),
messages.messages(),
@ -297,7 +308,7 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
)?;
},
MouseEventKind::ScrollDown => {
ctx.scroll.store(max(ctx.scroll.load(Ordering::SeqCst), 3)-3, Ordering::SeqCst);
ctx.chat().scroll.store(max(ctx.chat().scroll.load(Ordering::SeqCst), 3)-3, Ordering::SeqCst);
print_console(
ctx.clone(),
messages.messages(),
@ -314,11 +325,11 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
Ok(())
}
pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
match read_messages(
&mut connect(&ctx.host, ctx.enable_ssl)?,
ctx.max_messages,
ctx.messages.packet_size(),
ctx.chat().messages.packet_size(),
!ctx.enable_ssl,
ctx.enable_chunked
) {
@ -330,17 +341,17 @@ pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
};
if ctx.enable_chunked {
ctx.messages.append_and_store(ctx.max_messages, messages.clone(), size);
print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap())?;
ctx.chat().messages.append_and_store(ctx.max_messages, messages.clone(), size);
print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap())?;
} else {
ctx.messages.update(ctx.max_messages, messages.clone(), size);
print_console(ctx.clone(), messages, &ctx.input.read().unwrap())?;
ctx.chat().messages.update(ctx.max_messages, messages.clone(), size);
print_console(ctx.clone(), messages, &ctx.chat().input.read().unwrap())?;
}
},
Err(e) => {
let msg = format!("Read messages error: {}", e.to_string()).bright_red().to_string();
ctx.messages.append(ctx.max_messages, vec![msg]);
print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap())?;
ctx.chat().messages.append(ctx.max_messages, vec![msg]);
print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap())?;
}
_ => {}
}
@ -348,23 +359,39 @@ pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
Ok(())
}
pub fn on_close() {
fn on_close() {
disable_raw_mode().unwrap();
execute!(stdout(), event::DisableMouseCapture).unwrap();
}
pub fn update_console(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap())
pub struct ChatContext {
pub messages: Arc<ChatStorage>,
pub input: Arc<RwLock<String>>,
pub registered: Arc<RwLock<Option<String>>>,
pub scroll: Arc<AtomicUsize>,
}
pub fn print_message(ctx: Arc<Context>, message: String) -> Result<(), Box<dyn Error>> {
ctx.chat().messages.append(ctx.max_messages, vec![message]);
print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap())
}
pub fn run_main_loop(ctx: Arc<Context>) {
set_chat(ctx.clone(), ChatContext {
messages: Arc::new(ChatStorage::new()),
input: Arc::new(RwLock::new(String::new())),
registered: Arc::new(RwLock::new(None)),
scroll: Arc::new(AtomicUsize::new(0)),
});
enable_raw_mode().unwrap();
execute!(stdout(), event::EnableMouseCapture).unwrap();
if let Err(e) = print_console(ctx.clone(), Vec::new(), &ctx.input.read().unwrap()) {
if let Err(e) = print_console(ctx.clone(), Vec::new(), &ctx.chat().input.read().unwrap()) {
let msg = format!("Print messages error: {}", e.to_string()).bright_red().to_string();
ctx.messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap());
ctx.chat().messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap());
}
thread::spawn({
@ -374,8 +401,8 @@ pub fn run_main_loop(ctx: Arc<Context>) {
loop {
if let Err(e) = recv_tick(ctx.clone()) {
let msg = format!("Print messages error: {}", e.to_string()).bright_red().to_string();
ctx.messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap());
ctx.chat().messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap());
thread::sleep(Duration::from_secs(1));
}
}
@ -384,7 +411,7 @@ pub fn run_main_loop(ctx: Arc<Context>) {
if let Err(e) = poll_events(ctx.clone()) {
let msg = format!("Poll events error: {}", e.to_string()).bright_red().to_string();
ctx.messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.messages.messages(), &ctx.input.read().unwrap());
ctx.chat().messages.append(ctx.max_messages, vec![msg]);
let _ = print_console(ctx.clone(), ctx.chat().messages.messages(), &ctx.chat().input.read().unwrap());
}
}

View File

@ -1,4 +1,4 @@
use std::{str::FromStr, sync::{atomic::AtomicUsize, Arc, RwLock}};
use std::{str::FromStr, sync::{Arc, RwLock}};
#[allow(unused_imports)]
use std::{env, fs, path::{Path, PathBuf}, thread, time::Duration};
use colored::Colorize;
@ -6,7 +6,7 @@ use rand::random;
use serde_yml;
use clap::Parser;
use crate::chat::ChatStorage;
use crate::chat::ChatContext;
use super::util::get_input;
@ -209,11 +209,9 @@ pub struct Args {
}
pub struct Context {
pub messages: Arc<ChatStorage>,
pub input: Arc<RwLock<String>>,
pub chat: Arc<RwLock<Option<Arc<ChatContext>>>>,
pub host: String,
pub name: String,
pub registered: Arc<RwLock<Option<String>>>,
pub disable_formatting: bool,
pub disable_commands: bool,
pub disable_hiding_ip: bool,
@ -221,7 +219,6 @@ pub struct Context {
pub update_time: usize,
pub max_messages: usize,
pub enable_ip_viewing: bool,
pub scroll: Arc<AtomicUsize>,
pub enable_auth: bool,
pub enable_ssl: bool,
pub enable_chunked: bool,
@ -230,22 +227,23 @@ pub struct Context {
impl Context {
pub fn new(config: &Config, args: &Args) -> Context {
Context {
messages: Arc::new(ChatStorage::new()),
input: Arc::new(RwLock::new(String::new())),
chat: Arc::new(RwLock::new(None)),
message_format: args.message_format.clone().unwrap_or(config.message_format.clone()),
host: args.host.clone().unwrap_or(config.host.clone()),
name: args.name.clone().or(config.name.clone()).unwrap_or_else(|| ask_string("Name", format!("Anon#{:X}", random::<u16>()))),
registered: Arc::new(RwLock::new(None)),
disable_formatting: args.disable_formatting,
disable_commands: args.disable_commands,
disable_hiding_ip: args.disable_ip_hiding,
update_time: config.update_time,
max_messages: config.max_messages,
enable_ip_viewing: args.enable_users_ip_viewing || config.enable_ip_viewing,
scroll: Arc::new(AtomicUsize::new(0)),
enable_auth: args.enable_auth || config.enable_auth,
enable_ssl: args.enable_ssl || config.enable_ssl,
enable_chunked: args.enable_chunked || config.enable_chunked,
}
}
pub fn chat(&self) -> Arc<ChatContext> {
self.chat.read().unwrap().clone().unwrap()
}
}