Create tcp server
This commit is contained in:
		
							parent
							
								
									1a54c2e6fc
								
							
						
					
					
						commit
						47a05968fb
					
				| 
						 | 
					@ -28,6 +28,15 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "addr2line"
 | 
				
			||||||
 | 
					version = "0.24.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "gimli",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "adler2"
 | 
					name = "adler2"
 | 
				
			||||||
version = "2.0.0"
 | 
					version = "2.0.0"
 | 
				
			||||||
| 
						 | 
					@ -400,6 +409,21 @@ version = "1.4.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 | 
					checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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 0.52.6",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "bindgen"
 | 
					name = "bindgen"
 | 
				
			||||||
version = "0.70.1"
 | 
					version = "0.70.1"
 | 
				
			||||||
| 
						 | 
					@ -1306,6 +1330,12 @@ dependencies = [
 | 
				
			||||||
 "windows 0.58.0",
 | 
					 "windows 0.58.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "gimli"
 | 
				
			||||||
 | 
					version = "0.31.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "gl_generator"
 | 
					name = "gl_generator"
 | 
				
			||||||
version = "0.14.0"
 | 
					version = "0.14.0"
 | 
				
			||||||
| 
						 | 
					@ -1736,6 +1766,7 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "thread-priority",
 | 
					 "thread-priority",
 | 
				
			||||||
 | 
					 "tokio",
 | 
				
			||||||
 "wgpu",
 | 
					 "wgpu",
 | 
				
			||||||
 "windows 0.58.0",
 | 
					 "windows 0.58.0",
 | 
				
			||||||
 "winit",
 | 
					 "winit",
 | 
				
			||||||
| 
						 | 
					@ -1902,6 +1933,17 @@ dependencies = [
 | 
				
			||||||
 "simd-adler32",
 | 
					 "simd-adler32",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "mio"
 | 
				
			||||||
 | 
					version = "1.0.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "wasi",
 | 
				
			||||||
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "naga"
 | 
					name = "naga"
 | 
				
			||||||
version = "23.1.0"
 | 
					version = "23.1.0"
 | 
				
			||||||
| 
						 | 
					@ -2276,6 +2318,15 @@ dependencies = [
 | 
				
			||||||
 "objc2-foundation",
 | 
					 "objc2-foundation",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "object"
 | 
				
			||||||
 | 
					version = "0.36.7"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "oboe"
 | 
					name = "oboe"
 | 
				
			||||||
version = "0.6.1"
 | 
					version = "0.6.1"
 | 
				
			||||||
| 
						 | 
					@ -2696,6 +2747,12 @@ dependencies = [
 | 
				
			||||||
 "realfft",
 | 
					 "realfft",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rustc-demangle"
 | 
				
			||||||
 | 
					version = "0.1.24"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rustc-hash"
 | 
					name = "rustc-hash"
 | 
				
			||||||
version = "1.1.0"
 | 
					version = "1.1.0"
 | 
				
			||||||
| 
						 | 
					@ -2918,6 +2975,16 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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]]
 | 
					[[package]]
 | 
				
			||||||
name = "spirv"
 | 
					name = "spirv"
 | 
				
			||||||
version = "0.3.0+sdk-1.3.268.0"
 | 
					version = "0.3.0+sdk-1.3.268.0"
 | 
				
			||||||
| 
						 | 
					@ -3070,6 +3137,33 @@ dependencies = [
 | 
				
			||||||
 "zerovec",
 | 
					 "zerovec",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "tokio"
 | 
				
			||||||
 | 
					version = "1.42.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "backtrace",
 | 
				
			||||||
 | 
					 "bytes",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "mio",
 | 
				
			||||||
 | 
					 "pin-project-lite",
 | 
				
			||||||
 | 
					 "socket2",
 | 
				
			||||||
 | 
					 "tokio-macros",
 | 
				
			||||||
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "tokio-macros"
 | 
				
			||||||
 | 
					version = "2.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "toml"
 | 
					name = "toml"
 | 
				
			||||||
version = "0.8.19"
 | 
					version = "0.8.19"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ rubato = "0.16"
 | 
				
			||||||
serde = { version = "1", features = ["derive"] }
 | 
					serde = { version = "1", features = ["derive"] }
 | 
				
			||||||
serde_json = "1"
 | 
					serde_json = "1"
 | 
				
			||||||
thread-priority = "1"
 | 
					thread-priority = "1"
 | 
				
			||||||
 | 
					tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync"] }
 | 
				
			||||||
wgpu = "23"
 | 
					wgpu = "23"
 | 
				
			||||||
winit = { version = "0.30", features = ["serde"] }
 | 
					winit = { version = "0.30", features = ["serde"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ use crate::{
 | 
				
			||||||
    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
					    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
				
			||||||
    input::MappingProvider,
 | 
					    input::MappingProvider,
 | 
				
			||||||
    persistence::Persistence,
 | 
					    persistence::Persistence,
 | 
				
			||||||
    window::{AboutWindow, AppWindow, GameWindow, InputWindow},
 | 
					    window::{AboutWindow, AppWindow, GameWindow, GdbServerWindow, InputWindow},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn load_icon() -> anyhow::Result<IconData> {
 | 
					fn load_icon() -> anyhow::Result<IconData> {
 | 
				
			||||||
| 
						 | 
					@ -183,6 +183,10 @@ impl ApplicationHandler<UserEvent> for Application {
 | 
				
			||||||
                let about = AboutWindow;
 | 
					                let about = AboutWindow;
 | 
				
			||||||
                self.open(event_loop, Box::new(about));
 | 
					                self.open(event_loop, Box::new(about));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            UserEvent::OpenDebugger(sim_id) => {
 | 
				
			||||||
 | 
					                let debugger = GdbServerWindow::new(sim_id, self.client.clone());
 | 
				
			||||||
 | 
					                self.open(event_loop, Box::new(debugger));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenInput => {
 | 
					            UserEvent::OpenInput => {
 | 
				
			||||||
                let input = InputWindow::new(self.mappings.clone());
 | 
					                let input = InputWindow::new(self.mappings.clone());
 | 
				
			||||||
                self.open(event_loop, Box::new(input));
 | 
					                self.open(event_loop, Box::new(input));
 | 
				
			||||||
| 
						 | 
					@ -374,6 +378,7 @@ impl Drop for Viewport {
 | 
				
			||||||
pub enum UserEvent {
 | 
					pub enum UserEvent {
 | 
				
			||||||
    GamepadEvent(gilrs::Event),
 | 
					    GamepadEvent(gilrs::Event),
 | 
				
			||||||
    OpenAbout,
 | 
					    OpenAbout,
 | 
				
			||||||
 | 
					    OpenDebugger(SimId),
 | 
				
			||||||
    OpenInput,
 | 
					    OpenInput,
 | 
				
			||||||
    OpenPlayer2,
 | 
					    OpenPlayer2,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    collections::HashMap,
 | 
					    collections::HashMap,
 | 
				
			||||||
 | 
					    fmt::Display,
 | 
				
			||||||
    fs::{self, File},
 | 
					    fs::{self, File},
 | 
				
			||||||
    io::{Read, Seek, SeekFrom, Write},
 | 
					    io::{Read, Seek, SeekFrom, Write},
 | 
				
			||||||
    path::{Path, PathBuf},
 | 
					    path::{Path, PathBuf},
 | 
				
			||||||
| 
						 | 
					@ -45,6 +46,14 @@ impl SimId {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					impl Display for SimId {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        f.write_str(match self {
 | 
				
			||||||
 | 
					            Self::Player1 => "Player 1",
 | 
				
			||||||
 | 
					            Self::Player2 => "Player 2",
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Cart {
 | 
					struct Cart {
 | 
				
			||||||
    rom_path: PathBuf,
 | 
					    rom_path: PathBuf,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,154 @@
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    error::Error,
 | 
				
			||||||
 | 
					    sync::{Arc, Mutex},
 | 
				
			||||||
 | 
					    thread,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use tokio::{
 | 
				
			||||||
 | 
					    io::{AsyncReadExt, AsyncWriteExt as _, BufReader, BufWriter},
 | 
				
			||||||
 | 
					    net::{
 | 
				
			||||||
 | 
					        tcp::{OwnedReadHalf, OwnedWriteHalf},
 | 
				
			||||||
 | 
					        TcpListener, TcpStream,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    select,
 | 
				
			||||||
 | 
					    sync::oneshot,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct GdbServer {
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    status: Arc<Mutex<GdbServerStatus>>,
 | 
				
			||||||
 | 
					    killer: Option<oneshot::Sender<()>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GdbServer {
 | 
				
			||||||
 | 
					    pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            client,
 | 
				
			||||||
 | 
					            status: Arc::new(Mutex::new(GdbServerStatus::Stopped)),
 | 
				
			||||||
 | 
					            killer: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn status(&self) -> GdbServerStatus {
 | 
				
			||||||
 | 
					        self.status.lock().unwrap().clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start(&mut self, port: u16) {
 | 
				
			||||||
 | 
					        let sim_id = self.sim_id;
 | 
				
			||||||
 | 
					        let client = self.client.clone();
 | 
				
			||||||
 | 
					        let status = self.status.clone();
 | 
				
			||||||
 | 
					        let (tx, rx) = oneshot::channel();
 | 
				
			||||||
 | 
					        self.killer = Some(tx);
 | 
				
			||||||
 | 
					        thread::spawn(move || {
 | 
				
			||||||
 | 
					            tokio::runtime::Builder::new_current_thread()
 | 
				
			||||||
 | 
					                .enable_all()
 | 
				
			||||||
 | 
					                .build()
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .block_on(async move {
 | 
				
			||||||
 | 
					                    select! {
 | 
				
			||||||
 | 
					                        _ = run_server(sim_id, client, port, &status) => {}
 | 
				
			||||||
 | 
					                        _ = rx => {
 | 
				
			||||||
 | 
					                            *status.lock().unwrap() = GdbServerStatus::Stopped;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn stop(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(killer) = self.killer.take() {
 | 
				
			||||||
 | 
					            let _ = killer.send(());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn run_server(
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    port: u16,
 | 
				
			||||||
 | 
					    status: &Mutex<GdbServerStatus>,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    client.send_command(EmulatorCommand::Pause);
 | 
				
			||||||
 | 
					    let Some(stream) = try_connect(port, status).await else {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let connection = GdbConnection::new(sim_id, client, stream);
 | 
				
			||||||
 | 
					    match connection.run().await {
 | 
				
			||||||
 | 
					        Ok(()) => {
 | 
				
			||||||
 | 
					            *status.lock().unwrap() = GdbServerStatus::Stopped;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Err(error) => {
 | 
				
			||||||
 | 
					            *status.lock().unwrap() = GdbServerStatus::Error(error.to_string());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn try_connect(port: u16, status: &Mutex<GdbServerStatus>) -> Option<TcpStream> {
 | 
				
			||||||
 | 
					    *status.lock().unwrap() = GdbServerStatus::Connecting;
 | 
				
			||||||
 | 
					    let listener = match TcpListener::bind(("127.0.0.1", port)).await {
 | 
				
			||||||
 | 
					        Ok(l) => l,
 | 
				
			||||||
 | 
					        Err(err) => {
 | 
				
			||||||
 | 
					            *status.lock().unwrap() = GdbServerStatus::Error(err.to_string());
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    match listener.accept().await {
 | 
				
			||||||
 | 
					        Ok((stream, _)) => {
 | 
				
			||||||
 | 
					            *status.lock().unwrap() = GdbServerStatus::Running;
 | 
				
			||||||
 | 
					            Some(stream)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Err(err) => {
 | 
				
			||||||
 | 
					            *status.lock().unwrap() = GdbServerStatus::Error(err.to_string());
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					pub enum GdbServerStatus {
 | 
				
			||||||
 | 
					    Stopped,
 | 
				
			||||||
 | 
					    Connecting,
 | 
				
			||||||
 | 
					    Running,
 | 
				
			||||||
 | 
					    Error(String),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GdbServerStatus {
 | 
				
			||||||
 | 
					    pub fn running(&self) -> bool {
 | 
				
			||||||
 | 
					        matches!(self, Self::Connecting | Self::Running)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GdbConnection {
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    stream_in: BufReader<OwnedReadHalf>,
 | 
				
			||||||
 | 
					    stream_out: BufWriter<OwnedWriteHalf>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GdbConnection {
 | 
				
			||||||
 | 
					    fn new(sim_id: SimId, client: EmulatorClient, stream: TcpStream) -> Self {
 | 
				
			||||||
 | 
					        let (rx, tx) = stream.into_split();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            client,
 | 
				
			||||||
 | 
					            stream_in: BufReader::new(rx),
 | 
				
			||||||
 | 
					            stream_out: BufWriter::new(tx),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    async fn run(mut self) -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
 | 
					        println!("Connected for {}", self.sim_id);
 | 
				
			||||||
 | 
					        self.client.send_command(EmulatorCommand::Resume);
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let byte = self.read_byte().await?;
 | 
				
			||||||
 | 
					            println!("{byte}");
 | 
				
			||||||
 | 
					            self.stream_out.write_u8(byte).await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn read_byte(&mut self) -> std::io::Result<u8> {
 | 
				
			||||||
 | 
					        self.stream_in.read_u8().await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ mod app;
 | 
				
			||||||
mod audio;
 | 
					mod audio;
 | 
				
			||||||
mod controller;
 | 
					mod controller;
 | 
				
			||||||
mod emulator;
 | 
					mod emulator;
 | 
				
			||||||
 | 
					mod gdbserver;
 | 
				
			||||||
mod graphics;
 | 
					mod graphics;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
mod persistence;
 | 
					mod persistence;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,14 @@
 | 
				
			||||||
pub use about::AboutWindow;
 | 
					pub use about::AboutWindow;
 | 
				
			||||||
use egui::{Context, ViewportBuilder, ViewportId};
 | 
					use egui::{Context, ViewportBuilder, ViewportId};
 | 
				
			||||||
pub use game::GameWindow;
 | 
					pub use game::GameWindow;
 | 
				
			||||||
 | 
					pub use gdb::GdbServerWindow;
 | 
				
			||||||
pub use input::InputWindow;
 | 
					pub use input::InputWindow;
 | 
				
			||||||
use winit::event::KeyEvent;
 | 
					use winit::event::KeyEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod about;
 | 
					mod about;
 | 
				
			||||||
mod game;
 | 
					mod game;
 | 
				
			||||||
mod game_screen;
 | 
					mod game_screen;
 | 
				
			||||||
 | 
					mod gdb;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait AppWindow {
 | 
					pub trait AppWindow {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,6 +121,14 @@ impl GameWindow {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        ui.menu_button("Tools", |ui| {
 | 
				
			||||||
 | 
					            if ui.button("GDB Server").clicked() {
 | 
				
			||||||
 | 
					                self.proxy
 | 
				
			||||||
 | 
					                    .send_event(UserEvent::OpenDebugger(self.sim_id))
 | 
				
			||||||
 | 
					                    .unwrap();
 | 
				
			||||||
 | 
					                ui.close_menu();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        ui.menu_button("About", |ui| {
 | 
					        ui.menu_button("About", |ui| {
 | 
				
			||||||
            self.proxy.send_event(UserEvent::OpenAbout).unwrap();
 | 
					            self.proxy.send_event(UserEvent::OpenAbout).unwrap();
 | 
				
			||||||
            ui.close_menu();
 | 
					            ui.close_menu();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,80 @@
 | 
				
			||||||
 | 
					use egui::{Button, CentralPanel, TextEdit, ViewportBuilder, ViewportId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    emulator::{EmulatorClient, SimId},
 | 
				
			||||||
 | 
					    gdbserver::{GdbServer, GdbServerStatus},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::AppWindow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct GdbServerWindow {
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    port_str: String,
 | 
				
			||||||
 | 
					    server: GdbServer,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GdbServerWindow {
 | 
				
			||||||
 | 
					    pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            port_str: (8080 + sim_id.to_index()).to_string(),
 | 
				
			||||||
 | 
					            server: GdbServer::new(sim_id, client),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AppWindow for GdbServerWindow {
 | 
				
			||||||
 | 
					    fn viewport_id(&self) -> ViewportId {
 | 
				
			||||||
 | 
					        ViewportId::from_hash_of(format!("Debugger-{}", self.sim_id))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initial_viewport(&self) -> ViewportBuilder {
 | 
				
			||||||
 | 
					        ViewportBuilder::default()
 | 
				
			||||||
 | 
					            .with_title(format!("GDB Server ({})", self.sim_id))
 | 
				
			||||||
 | 
					            .with_inner_size((300.0, 200.0))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn show(&mut self, ctx: &egui::Context) {
 | 
				
			||||||
 | 
					        let port_num: Option<u16> = self.port_str.parse().ok();
 | 
				
			||||||
 | 
					        let status = self.server.status();
 | 
				
			||||||
 | 
					        CentralPanel::default().show(ctx, |ui| {
 | 
				
			||||||
 | 
					            ui.horizontal(|ui| {
 | 
				
			||||||
 | 
					                if port_num.is_none() {
 | 
				
			||||||
 | 
					                    let style = ui.style_mut();
 | 
				
			||||||
 | 
					                    let error = style.visuals.error_fg_color;
 | 
				
			||||||
 | 
					                    style.visuals.widgets.active.bg_stroke.color = error;
 | 
				
			||||||
 | 
					                    style.visuals.widgets.hovered.bg_stroke.color = error;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ui.label("Port");
 | 
				
			||||||
 | 
					                let port_editor = TextEdit::singleline(&mut self.port_str).desired_width(100.0);
 | 
				
			||||||
 | 
					                ui.add_enabled(!status.running(), port_editor);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if !status.running() {
 | 
				
			||||||
 | 
					                let start_button = Button::new("Start");
 | 
				
			||||||
 | 
					                if ui.add_enabled(port_num.is_some(), start_button).clicked() {
 | 
				
			||||||
 | 
					                    let port = port_num.unwrap();
 | 
				
			||||||
 | 
					                    self.server.start(port);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                let stop_button = Button::new("Stop");
 | 
				
			||||||
 | 
					                if ui.add(stop_button).clicked() {
 | 
				
			||||||
 | 
					                    self.server.stop();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            match &status {
 | 
				
			||||||
 | 
					                GdbServerStatus::Stopped => {}
 | 
				
			||||||
 | 
					                GdbServerStatus::Connecting => {
 | 
				
			||||||
 | 
					                    ui.label("Connecting...");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                GdbServerStatus::Running => {
 | 
				
			||||||
 | 
					                    ui.label("Running");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                GdbServerStatus::Error(message) => {
 | 
				
			||||||
 | 
					                    ui.label(message);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue