this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: send mouse position from server to clients

dusk 6c582687 1639a2c6

+409 -96
+263 -34
Cargo.lock
··· 82 82 "anyhow", 83 83 "bincode", 84 84 "console_error_panic_hook", 85 + "enigo", 85 86 "fastrand", 86 87 "futures-util", 87 88 "quanta", ··· 198 199 source = "registry+https://github.com/rust-lang/crates.io-index" 199 200 checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" 200 201 dependencies = [ 201 - "objc2", 202 + "objc2 0.5.2", 202 203 ] 203 204 204 205 [[package]] ··· 402 403 ] 403 404 404 405 [[package]] 406 + name = "core-graphics" 407 + version = "0.25.0" 408 + source = "registry+https://github.com/rust-lang/crates.io-index" 409 + checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" 410 + dependencies = [ 411 + "bitflags 2.9.3", 412 + "core-foundation 0.10.1", 413 + "core-graphics-types 0.2.0", 414 + "foreign-types", 415 + "libc", 416 + ] 417 + 418 + [[package]] 405 419 name = "core-graphics-types" 406 420 version = "0.1.3" 407 421 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 514 528 checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 515 529 516 530 [[package]] 531 + name = "dispatch2" 532 + version = "0.3.0" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" 535 + dependencies = [ 536 + "bitflags 2.9.3", 537 + "objc2 0.6.2", 538 + ] 539 + 540 + [[package]] 517 541 name = "dlib" 518 542 version = "0.5.2" 519 543 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 580 604 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 581 605 582 606 [[package]] 607 + name = "enigo" 608 + version = "0.5.0" 609 + source = "registry+https://github.com/rust-lang/crates.io-index" 610 + checksum = "71744ff36f35a4276e8827add8102d0e792378c574fd93cb4e1c8e0505f96b7c" 611 + dependencies = [ 612 + "core-foundation 0.10.1", 613 + "core-graphics 0.25.0", 614 + "foreign-types-shared", 615 + "libc", 616 + "log", 617 + "nom", 618 + "objc2 0.6.2", 619 + "objc2-app-kit 0.3.1", 620 + "objc2-foundation 0.3.1", 621 + "tempfile", 622 + "wayland-client", 623 + "wayland-protocols-misc", 624 + "wayland-protocols-wlr", 625 + "windows", 626 + "x11rb", 627 + "xkbcommon", 628 + "xkeysym", 629 + ] 630 + 631 + [[package]] 583 632 name = "equivalent" 584 633 version = "1.0.2" 585 634 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1009 1058 ] 1010 1059 1011 1060 [[package]] 1061 + name = "nom" 1062 + version = "8.0.0" 1063 + source = "registry+https://github.com/rust-lang/crates.io-index" 1064 + checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" 1065 + dependencies = [ 1066 + "memchr", 1067 + ] 1068 + 1069 + [[package]] 1012 1070 name = "num_enum" 1013 1071 version = "0.7.4" 1014 1072 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1047 1105 ] 1048 1106 1049 1107 [[package]] 1108 + name = "objc2" 1109 + version = "0.6.2" 1110 + source = "registry+https://github.com/rust-lang/crates.io-index" 1111 + checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" 1112 + dependencies = [ 1113 + "objc2-encode", 1114 + ] 1115 + 1116 + [[package]] 1050 1117 name = "objc2-app-kit" 1051 1118 version = "0.2.2" 1052 1119 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1055 1122 "bitflags 2.9.3", 1056 1123 "block2", 1057 1124 "libc", 1058 - "objc2", 1125 + "objc2 0.5.2", 1059 1126 "objc2-core-data", 1060 1127 "objc2-core-image", 1061 - "objc2-foundation", 1128 + "objc2-foundation 0.2.2", 1062 1129 "objc2-quartz-core", 1063 1130 ] 1064 1131 1065 1132 [[package]] 1133 + name = "objc2-app-kit" 1134 + version = "0.3.1" 1135 + source = "registry+https://github.com/rust-lang/crates.io-index" 1136 + checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" 1137 + dependencies = [ 1138 + "bitflags 2.9.3", 1139 + "objc2 0.6.2", 1140 + "objc2-foundation 0.3.1", 1141 + ] 1142 + 1143 + [[package]] 1066 1144 name = "objc2-cloud-kit" 1067 1145 version = "0.2.2" 1068 1146 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1070 1148 dependencies = [ 1071 1149 "bitflags 2.9.3", 1072 1150 "block2", 1073 - "objc2", 1151 + "objc2 0.5.2", 1074 1152 "objc2-core-location", 1075 - "objc2-foundation", 1153 + "objc2-foundation 0.2.2", 1076 1154 ] 1077 1155 1078 1156 [[package]] ··· 1082 1160 checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" 1083 1161 dependencies = [ 1084 1162 "block2", 1085 - "objc2", 1086 - "objc2-foundation", 1163 + "objc2 0.5.2", 1164 + "objc2-foundation 0.2.2", 1087 1165 ] 1088 1166 1089 1167 [[package]] ··· 1094 1172 dependencies = [ 1095 1173 "bitflags 2.9.3", 1096 1174 "block2", 1097 - "objc2", 1098 - "objc2-foundation", 1175 + "objc2 0.5.2", 1176 + "objc2-foundation 0.2.2", 1177 + ] 1178 + 1179 + [[package]] 1180 + name = "objc2-core-foundation" 1181 + version = "0.3.1" 1182 + source = "registry+https://github.com/rust-lang/crates.io-index" 1183 + checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" 1184 + dependencies = [ 1185 + "bitflags 2.9.3", 1186 + "dispatch2", 1187 + "objc2 0.6.2", 1099 1188 ] 1100 1189 1101 1190 [[package]] ··· 1105 1194 checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" 1106 1195 dependencies = [ 1107 1196 "block2", 1108 - "objc2", 1109 - "objc2-foundation", 1197 + "objc2 0.5.2", 1198 + "objc2-foundation 0.2.2", 1110 1199 "objc2-metal", 1111 1200 ] 1112 1201 ··· 1117 1206 checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" 1118 1207 dependencies = [ 1119 1208 "block2", 1120 - "objc2", 1209 + "objc2 0.5.2", 1121 1210 "objc2-contacts", 1122 - "objc2-foundation", 1211 + "objc2-foundation 0.2.2", 1123 1212 ] 1124 1213 1125 1214 [[package]] ··· 1138 1227 "block2", 1139 1228 "dispatch", 1140 1229 "libc", 1141 - "objc2", 1230 + "objc2 0.5.2", 1231 + ] 1232 + 1233 + [[package]] 1234 + name = "objc2-foundation" 1235 + version = "0.3.1" 1236 + source = "registry+https://github.com/rust-lang/crates.io-index" 1237 + checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" 1238 + dependencies = [ 1239 + "bitflags 2.9.3", 1240 + "objc2 0.6.2", 1241 + "objc2-core-foundation", 1142 1242 ] 1143 1243 1144 1244 [[package]] ··· 1148 1248 checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" 1149 1249 dependencies = [ 1150 1250 "block2", 1151 - "objc2", 1152 - "objc2-app-kit", 1153 - "objc2-foundation", 1251 + "objc2 0.5.2", 1252 + "objc2-app-kit 0.2.2", 1253 + "objc2-foundation 0.2.2", 1154 1254 ] 1155 1255 1156 1256 [[package]] ··· 1161 1261 dependencies = [ 1162 1262 "bitflags 2.9.3", 1163 1263 "block2", 1164 - "objc2", 1165 - "objc2-foundation", 1264 + "objc2 0.5.2", 1265 + "objc2-foundation 0.2.2", 1166 1266 ] 1167 1267 1168 1268 [[package]] ··· 1173 1273 dependencies = [ 1174 1274 "bitflags 2.9.3", 1175 1275 "block2", 1176 - "objc2", 1177 - "objc2-foundation", 1276 + "objc2 0.5.2", 1277 + "objc2-foundation 0.2.2", 1178 1278 "objc2-metal", 1179 1279 ] 1180 1280 ··· 1184 1284 source = "registry+https://github.com/rust-lang/crates.io-index" 1185 1285 checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" 1186 1286 dependencies = [ 1187 - "objc2", 1188 - "objc2-foundation", 1287 + "objc2 0.5.2", 1288 + "objc2-foundation 0.2.2", 1189 1289 ] 1190 1290 1191 1291 [[package]] ··· 1196 1296 dependencies = [ 1197 1297 "bitflags 2.9.3", 1198 1298 "block2", 1199 - "objc2", 1299 + "objc2 0.5.2", 1200 1300 "objc2-cloud-kit", 1201 1301 "objc2-core-data", 1202 1302 "objc2-core-image", 1203 1303 "objc2-core-location", 1204 - "objc2-foundation", 1304 + "objc2-foundation 0.2.2", 1205 1305 "objc2-link-presentation", 1206 1306 "objc2-quartz-core", 1207 1307 "objc2-symbols", ··· 1216 1316 checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" 1217 1317 dependencies = [ 1218 1318 "block2", 1219 - "objc2", 1220 - "objc2-foundation", 1319 + "objc2 0.5.2", 1320 + "objc2-foundation 0.2.2", 1221 1321 ] 1222 1322 1223 1323 [[package]] ··· 1228 1328 dependencies = [ 1229 1329 "bitflags 2.9.3", 1230 1330 "block2", 1231 - "objc2", 1331 + "objc2 0.5.2", 1232 1332 "objc2-core-location", 1233 - "objc2-foundation", 1333 + "objc2-foundation 0.2.2", 1234 1334 ] 1235 1335 1236 1336 [[package]] ··· 1743 1843 "js-sys", 1744 1844 "log", 1745 1845 "memmap2", 1746 - "objc2", 1747 - "objc2-foundation", 1846 + "objc2 0.5.2", 1847 + "objc2-foundation 0.2.2", 1748 1848 "objc2-quartz-core", 1749 1849 "raw-window-handle", 1750 1850 "redox_syscall 0.5.17", ··· 2389 2489 ] 2390 2490 2391 2491 [[package]] 2492 + name = "wayland-protocols-misc" 2493 + version = "0.3.9" 2494 + source = "registry+https://github.com/rust-lang/crates.io-index" 2495 + checksum = "2dfe33d551eb8bffd03ff067a8b44bb963919157841a99957151299a6307d19c" 2496 + dependencies = [ 2497 + "bitflags 2.9.3", 2498 + "wayland-backend", 2499 + "wayland-client", 2500 + "wayland-protocols", 2501 + "wayland-scanner", 2502 + ] 2503 + 2504 + [[package]] 2392 2505 name = "wayland-protocols-plasma" 2393 2506 version = "0.3.9" 2394 2507 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2507 2620 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2508 2621 2509 2622 [[package]] 2623 + name = "windows" 2624 + version = "0.61.3" 2625 + source = "registry+https://github.com/rust-lang/crates.io-index" 2626 + checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" 2627 + dependencies = [ 2628 + "windows-collections", 2629 + "windows-core", 2630 + "windows-future", 2631 + "windows-link", 2632 + "windows-numerics", 2633 + ] 2634 + 2635 + [[package]] 2636 + name = "windows-collections" 2637 + version = "0.2.0" 2638 + source = "registry+https://github.com/rust-lang/crates.io-index" 2639 + checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 2640 + dependencies = [ 2641 + "windows-core", 2642 + ] 2643 + 2644 + [[package]] 2645 + name = "windows-core" 2646 + version = "0.61.2" 2647 + source = "registry+https://github.com/rust-lang/crates.io-index" 2648 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 2649 + dependencies = [ 2650 + "windows-implement", 2651 + "windows-interface", 2652 + "windows-link", 2653 + "windows-result", 2654 + "windows-strings", 2655 + ] 2656 + 2657 + [[package]] 2658 + name = "windows-future" 2659 + version = "0.2.1" 2660 + source = "registry+https://github.com/rust-lang/crates.io-index" 2661 + checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" 2662 + dependencies = [ 2663 + "windows-core", 2664 + "windows-link", 2665 + "windows-threading", 2666 + ] 2667 + 2668 + [[package]] 2669 + name = "windows-implement" 2670 + version = "0.60.0" 2671 + source = "registry+https://github.com/rust-lang/crates.io-index" 2672 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2673 + dependencies = [ 2674 + "proc-macro2", 2675 + "quote", 2676 + "syn", 2677 + ] 2678 + 2679 + [[package]] 2680 + name = "windows-interface" 2681 + version = "0.59.1" 2682 + source = "registry+https://github.com/rust-lang/crates.io-index" 2683 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2684 + dependencies = [ 2685 + "proc-macro2", 2686 + "quote", 2687 + "syn", 2688 + ] 2689 + 2690 + [[package]] 2510 2691 name = "windows-link" 2511 2692 version = "0.1.3" 2512 2693 source = "registry+https://github.com/rust-lang/crates.io-index" 2513 2694 checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 2514 2695 2515 2696 [[package]] 2697 + name = "windows-numerics" 2698 + version = "0.2.0" 2699 + source = "registry+https://github.com/rust-lang/crates.io-index" 2700 + checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 2701 + dependencies = [ 2702 + "windows-core", 2703 + "windows-link", 2704 + ] 2705 + 2706 + [[package]] 2707 + name = "windows-result" 2708 + version = "0.3.4" 2709 + source = "registry+https://github.com/rust-lang/crates.io-index" 2710 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 2711 + dependencies = [ 2712 + "windows-link", 2713 + ] 2714 + 2715 + [[package]] 2716 + name = "windows-strings" 2717 + version = "0.4.2" 2718 + source = "registry+https://github.com/rust-lang/crates.io-index" 2719 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 2720 + dependencies = [ 2721 + "windows-link", 2722 + ] 2723 + 2724 + [[package]] 2516 2725 name = "windows-sys" 2517 2726 version = "0.45.0" 2518 2727 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2612 2821 ] 2613 2822 2614 2823 [[package]] 2824 + name = "windows-threading" 2825 + version = "0.1.0" 2826 + source = "registry+https://github.com/rust-lang/crates.io-index" 2827 + checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" 2828 + dependencies = [ 2829 + "windows-link", 2830 + ] 2831 + 2832 + [[package]] 2615 2833 name = "windows_aarch64_gnullvm" 2616 2834 version = "0.42.2" 2617 2835 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2814 3032 "libc", 2815 3033 "memmap2", 2816 3034 "ndk", 2817 - "objc2", 2818 - "objc2-app-kit", 2819 - "objc2-foundation", 3035 + "objc2 0.5.2", 3036 + "objc2-app-kit 0.2.2", 3037 + "objc2-foundation 0.2.2", 2820 3038 "objc2-ui-kit", 2821 3039 "orbclient", 2822 3040 "percent-encoding", ··· 2898 3116 version = "0.3.10" 2899 3117 source = "registry+https://github.com/rust-lang/crates.io-index" 2900 3118 checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" 3119 + 3120 + [[package]] 3121 + name = "xkbcommon" 3122 + version = "0.8.0" 3123 + source = "registry+https://github.com/rust-lang/crates.io-index" 3124 + checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" 3125 + dependencies = [ 3126 + "libc", 3127 + "memmap2", 3128 + "xkeysym", 3129 + ] 2901 3130 2902 3131 [[package]] 2903 3132 name = "xkbcommon-dl"
+2
Cargo.toml
··· 11 11 server = [ 12 12 "tokio/net", 13 13 "futures-util/std", 14 + "dep:enigo", 14 15 "dep:tokio-websockets", 15 16 ] 16 17 client = [ ··· 37 38 wasm-bindgen-futures = "0.4" 38 39 39 40 [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 41 + enigo = { optional = true, version = "0.5.0", features = ["wayland"] } 40 42 tokio = { version = "1", default-features = false, features = ["rt", "rt-multi-thread", "sync"] } 41 43 ahash = { version = "0.8", default-features = false, features = ["runtime-rng"] } 42 44
+100 -35
src/main.rs
··· 14 14 window::{Window, WindowAttributes, WindowId}, 15 15 }; 16 16 17 - use crate::renderer::{Renderer, skia_rgba_to_bgra_u32}; 18 17 use crate::ws::LaserMessage; 18 + use crate::{ 19 + renderer::{Renderer, skia_rgba_to_bgra_u32}, 20 + ws::WsMessage, 21 + }; 19 22 20 23 mod renderer; 21 24 mod utils; ··· 71 74 pub struct LaserOverlay { 72 75 gfx: Option<Graphics>, 73 76 window: WindowHandle, 77 + server_mouse_pos: (f32, f32), 74 78 laser_points: HashMap<(u64, u8), Vec<LaserPoint>, ahash::RandomState>, 75 - in_chan: (mpsc::Sender<LaserMessage>, mpsc::Receiver<LaserMessage>), 76 - out_tx: mpsc::Sender<LaserMessage>, 79 + in_chan: (mpsc::Sender<WsMessage>, mpsc::Receiver<WsMessage>), 80 + out_tx: mpsc::Sender<WsMessage>, 77 81 last_render: Instant, 78 82 last_cleanup: Instant, 79 83 clock: Clock, ··· 87 91 } 88 92 89 93 impl LaserOverlay { 90 - pub fn new() -> ( 91 - mpsc::Sender<LaserMessage>, 92 - mpsc::Receiver<LaserMessage>, 93 - Self, 94 - ) { 94 + pub fn new() -> (mpsc::Sender<WsMessage>, mpsc::Receiver<WsMessage>, Self) { 95 95 let in_chan = mpsc::channel(1024); 96 96 let (out_tx, out_rx) = mpsc::channel(512); 97 97 ··· 101 101 let this = Self { 102 102 gfx: None, 103 103 window: WindowHandle::default(), 104 + server_mouse_pos: (0.0, 0.0), 104 105 laser_points: Default::default(), 105 106 in_chan, 106 107 out_tx, ··· 115 116 needs_redraw: false, 116 117 has_any_points: false, 117 118 }; 119 + 118 120 (this.in_chan.0.clone(), out_rx, this) 119 121 } 120 122 123 + #[cfg(feature = "server")] 124 + pub fn start_mouse_listener(send_tx: tokio::sync::broadcast::Sender<(u64, WsMessage)>) { 125 + std::thread::spawn({ 126 + use enigo::{Enigo, Mouse, Settings}; 127 + 128 + move || { 129 + let enigo = Enigo::new(&Settings::default()).unwrap(); 130 + loop { 131 + let res = enigo.location(); 132 + let Ok(pos) = res else { 133 + eprintln!("failed to get mouse position: {res:?}"); 134 + continue; 135 + }; 136 + let msg = WsMessage::Mouse(ws::MouseMessage { 137 + x: pos.0 as u32, 138 + y: pos.1 as u32, 139 + }); 140 + let _ = send_tx.send((0, msg)); 141 + std::thread::sleep(Duration::from_millis(1000 / 30)); 142 + } 143 + } 144 + }); 145 + } 146 + 121 147 pub fn window_handle(&self) -> WindowHandle { 122 148 self.window.clone() 123 149 } ··· 154 180 self.next_line_id = self.next_line_id.wrapping_add(1); 155 181 self.needs_redraw = true; 156 182 157 - let msg = LaserMessage { 183 + let msg = WsMessage::Laser(LaserMessage { 158 184 x: position.0 as u32, 159 185 y: position.1 as u32, 160 186 id: self.client_id, 161 187 line_id: self.current_line_id, 162 - }; 188 + }); 163 189 let _ = self.in_chan.0.try_send(msg); 164 190 let _ = self.out_tx.try_send(msg); 165 191 } 166 192 167 193 fn handle_mouse_move(&mut self, position: (f32, f32)) { 168 - if self.mouse_pressed { 169 - let dx = position.0 - self.mouse_pos.0; 170 - let dy = position.1 - self.mouse_pos.1; 171 - let distance = (dx * dx + dy * dy).sqrt(); 194 + if !self.mouse_pressed { 195 + return; 196 + } 172 197 173 - if distance > 3.0 { 174 - self.mouse_pos = position; 175 - self.needs_redraw = true; 176 - let msg = LaserMessage { 177 - x: position.0 as u32, 178 - y: position.1 as u32, 179 - id: self.client_id, 180 - line_id: self.current_line_id, 181 - }; 182 - let _ = self.in_chan.0.try_send(msg); 183 - let _ = self.out_tx.try_send(msg); 184 - } 198 + let dx = position.0 - self.mouse_pos.0; 199 + let dy = position.1 - self.mouse_pos.1; 200 + let distance = (dx * dx + dy * dy).sqrt(); 201 + 202 + if distance > 3.0 { 203 + self.mouse_pos = position; 204 + self.needs_redraw = true; 205 + let msg = WsMessage::Laser(LaserMessage { 206 + x: position.0 as u32, 207 + y: position.1 as u32, 208 + id: self.client_id, 209 + line_id: self.current_line_id, 210 + }); 211 + let _ = self.in_chan.0.try_send(msg); 212 + let _ = self.out_tx.try_send(msg); 185 213 } 186 214 } 187 215 ··· 248 276 } 249 277 250 278 while let Ok(msg) = self.in_chan.1.try_recv() { 251 - self.laser_points 252 - .entry((msg.id, msg.line_id)) 253 - .or_default() 254 - .push(LaserPoint::new(msg, self.clock.now())); 255 - has_any_points = true; 279 + match msg { 280 + WsMessage::Laser(msg) => { 281 + self.laser_points 282 + .entry((msg.id, msg.line_id)) 283 + .or_default() 284 + .push(LaserPoint::new(msg, self.clock.now())); 285 + has_any_points = true; 286 + } 287 + WsMessage::Mouse(msg) => { 288 + self.server_mouse_pos = (msg.x as f32, msg.y as f32); 289 + self.needs_redraw = true; 290 + } 291 + } 256 292 } 257 293 258 294 self.has_any_points = has_any_points; 259 295 } 260 296 297 + #[cfg(feature = "client")] 298 + #[inline(always)] 299 + fn draw_server_mouse(mut pixmap: PixmapMut, mouse_pos: (f32, f32)) { 300 + let (x, y) = mouse_pos; 301 + let radius = 10.0; 302 + let color = Color::WHITE; 303 + 304 + let mut pb = PathBuilder::new(); 305 + pb.push_circle(x, y, radius); 306 + 307 + if let Some(path) = pb.finish() { 308 + let mut paint = Paint::default(); 309 + paint.set_color(color); 310 + paint.anti_alias = true; 311 + paint.blend_mode = BlendMode::Source; 312 + 313 + let mut stroke = Stroke::default(); 314 + stroke.width = radius * 2.0; 315 + stroke.line_cap = LineCap::Round; 316 + stroke.line_join = LineJoin::Round; 317 + 318 + pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None); 319 + } 320 + } 321 + 261 322 #[inline(always)] 262 323 fn draw_tapering_laser_line(mut pixmap: PixmapMut, points: &[LaserPoint], now: Instant) { 263 324 if points.len() < 2 { ··· 351 412 for points in self.laser_points.values() { 352 413 Self::draw_tapering_laser_line(gfx.pixmap.as_mut(), points, self.clock.now()); 353 414 } 415 + #[cfg(feature = "client")] 416 + Self::draw_server_mouse(gfx.pixmap.as_mut(), self.server_mouse_pos); 354 417 skia_rgba_to_bgra_u32(gfx.pixmap.data(), frame.deref_mut()); 355 418 356 419 gfx.window.pre_present_notify(); ··· 420 483 WindowEvent::CursorMoved { position, .. } => { 421 484 let pos = (position.x as f32, position.y as f32); 422 485 self.handle_mouse_move(pos); 423 - if !self.mouse_pressed { 424 - self.mouse_pos = pos; 425 - } 486 + self.mouse_pos = pos; 426 487 } 427 488 WindowEvent::Resized(size) if let Some(gfx) = self.gfx.as_mut() => { 428 489 gfx.resize(size.width, size.height).unwrap(); ··· 474 535 tokio::spawn({ 475 536 let window = window.clone(); 476 537 let tx = _tx.clone(); 477 - async move { ws::server::listen(3111, window, tx).await.unwrap() } 538 + async move { 539 + let (server, send_tx) = ws::server::listen(3111, window, tx).await.unwrap(); 540 + LaserOverlay::start_mouse_listener(send_tx); 541 + server.await; 542 + } 478 543 }); 479 544 #[cfg(feature = "client")] 480 545 {
+44 -27
src/ws.rs
··· 4 4 const BINCODE_CFG: bincode::config::Configuration = bincode::config::standard(); 5 5 6 6 #[derive(Debug, Clone, Copy, Encode, Decode)] 7 + pub enum WsMessage { 8 + Laser(LaserMessage), 9 + Mouse(MouseMessage), 10 + } 11 + 12 + #[derive(Debug, Clone, Copy, Encode, Decode)] 7 13 pub struct LaserMessage { 8 14 pub x: u32, 9 15 pub y: u32, ··· 11 17 pub line_id: u8, 12 18 } 13 19 20 + #[derive(Debug, Clone, Copy, Encode, Decode)] 21 + pub struct MouseMessage { 22 + pub x: u32, 23 + pub y: u32, 24 + } 25 + 14 26 #[cfg(feature = "client")] 15 27 pub mod client { 16 28 use futures_util::{SinkExt, StreamExt}; ··· 19 31 20 32 use crate::{ 21 33 AppResult, WindowHandle, 22 - ws::{BINCODE_CFG, LaserMessage}, 34 + ws::{BINCODE_CFG, WsMessage}, 23 35 }; 24 36 25 37 pub async fn connect( 26 38 server_url: &str, 27 39 window: WindowHandle, 28 - mut overlay_rx: mpsc::Receiver<LaserMessage>, 29 - overlay_tx: mpsc::Sender<LaserMessage>, 40 + mut overlay_rx: mpsc::Receiver<WsMessage>, 41 + overlay_tx: mpsc::Sender<WsMessage>, 30 42 id: u64, 31 43 ) -> AppResult<()> { 32 44 let (mut tx, mut rx) = tokio_tungstenite_wasm::connect(server_url).await?.split(); ··· 42 54 while let Some(ev) = rx.next().await { 43 55 match ev { 44 56 Ok(Message::Binary(payload)) => { 45 - let Ok((decoded, _)): Result<(LaserMessage, _), _> = 57 + let Ok((decoded, _)): Result<(WsMessage, _), _> = 46 58 bincode::decode_from_slice(&payload, BINCODE_CFG) 47 59 else { 48 60 continue; 49 61 }; 50 - // don't send messages from the client we are on 51 - if decoded.id == id { 52 - continue; 53 - } 54 62 let _ = overlay_tx.send(decoded).await; 55 63 if let Some(window) = window.get() { 56 64 window.request_redraw(); ··· 58 66 } 59 67 Ok(Message::Close(_)) => { 60 68 eprintln!("server closed connection"); 69 + if let Some(window) = window.get() { 70 + window.set_title("offline"); 71 + } 61 72 break; 62 73 } 63 74 Err(err) => { 64 75 eprintln!("error receiving message: {}", err); 76 + if let Some(window) = window.get() { 77 + window.set_title(&format!("error: {}", err)); 78 + } 65 79 } 66 80 _ => {} 67 81 } ··· 85 99 86 100 use crate::{ 87 101 AppResult, WindowHandle, 88 - ws::{BINCODE_CFG, LaserMessage}, 102 + ws::{BINCODE_CFG, WsMessage}, 89 103 }; 90 104 91 105 pub async fn listen( 92 106 port: u16, 93 107 window: WindowHandle, 94 - overlay_tx: mpsc::Sender<LaserMessage>, 95 - ) -> AppResult<impl Future> { 108 + overlay_tx: mpsc::Sender<WsMessage>, 109 + ) -> AppResult<(impl Future, broadcast::Sender<(u64, WsMessage)>)> { 96 110 let addr = SocketAddr::from(([0, 0, 0, 0], port)); 97 111 let listener = TcpListener::bind(&addr).await?; 98 112 println!("listening on {}", addr); 99 113 100 114 let (tx, mut rx) = broadcast::channel(1024); 101 115 102 - let server_task = tokio::spawn(async move { 103 - loop { 104 - let conn = match listener.accept().await { 105 - Ok((conn, addr)) => { 106 - println!("accepted connection from {}", addr); 107 - conn 108 - } 109 - Err(err) => { 110 - eprintln!("error accepting connection: {}", err); 111 - continue; 112 - } 113 - }; 114 - tokio::spawn(handle_server_conn(conn, tx.clone(), tx.subscribe())); 116 + let server_task = tokio::spawn({ 117 + let tx = tx.clone(); 118 + async move { 119 + loop { 120 + let conn = match listener.accept().await { 121 + Ok((conn, addr)) => { 122 + println!("accepted connection from {}", addr); 123 + conn 124 + } 125 + Err(err) => { 126 + eprintln!("error accepting connection: {}", err); 127 + continue; 128 + } 129 + }; 130 + tokio::spawn(handle_server_conn(conn, tx.clone(), tx.subscribe())); 131 + } 115 132 } 116 133 }); 117 134 let overlay_task = tokio::spawn(async move { ··· 123 140 } 124 141 }); 125 142 126 - Ok(futures_util::future::join(server_task, overlay_task)) 143 + Ok((futures_util::future::join(server_task, overlay_task), tx)) 127 144 } 128 145 129 146 async fn handle_server_conn( 130 147 conn: TcpStream, 131 - msg_tx: broadcast::Sender<(u64, LaserMessage)>, 132 - mut msg_rx: broadcast::Receiver<(u64, LaserMessage)>, 148 + msg_tx: broadcast::Sender<(u64, WsMessage)>, 149 + mut msg_rx: broadcast::Receiver<(u64, WsMessage)>, 133 150 ) -> impl Future { 134 151 let (_, server) = tokio_websockets::ServerBuilder::new() 135 152 .accept(conn)