rj.rs
extern mod sdl; | |
use std::libc::{c_int, c_void}; | |
use std::rt::comm; | |
use std::rt::io::extensions::*; | |
use std::rt::io::io_error; | |
use std::rt::io::net::ip::{IpAddr, Ipv4Addr, Port, SocketAddr}; | |
use std::rt::io::net::tcp::TcpStream; | |
use std::task; | |
use std::vec; | |
static DEFAULT_RJ_IP: IpAddr = Ipv4Addr(127, 0, 0, 1); | |
static DEFAULT_RJ_PORT: Port = 10004; | |
static JOY_MAGIC: u32 = 0x909ACCEF; | |
struct JoyHeader { | |
magic: u32, // JOY_MAGIC | |
mode: i32, // Image mode (0-3) | |
size: u32, // Size of payload | |
vcount: u32 // sceDisplayGetVcount | |
} | |
struct ScreenBuffer { | |
head: JoyHeader, | |
surface: ~sdl::video::Surface, | |
pixels: ~[u8], | |
width: uint, | |
height: uint | |
} | |
fn write_joyevent(wr: &mut TcpStream, t: i32, v: u32) { | |
// Write the magic | |
wr.write_le_u32(JOY_MAGIC); | |
// Next the event kind | |
wr.write_le_i32(t); | |
// And finally the value | |
wr.write_le_u32(v); | |
} | |
// Takes a TcpStream and tries to read in | |
// and populate a `JoyHeader`. | |
fn read_head(rdr: &mut TcpStream) -> JoyHeader { | |
let head = JoyHeader { | |
magic: (*rdr).read_le_u32(), | |
mode: (*rdr).read_le_i32(), | |
size: (*rdr).read_le_u32(), | |
vcount: (*rdr).read_le_u32() | |
}; | |
assert_eq!(head.magic, JOY_MAGIC); | |
head | |
} | |
// Connects to the RemoteJoy API endpoint | |
// and continuously reads in frames | |
// which it sends back through the given | |
// chan, `c`. | |
fn remote_joy(c: comm::Chan<ScreenBuffer>) { | |
let addr = SocketAddr { | |
ip: DEFAULT_RJ_IP, | |
port: DEFAULT_RJ_PORT | |
}; | |
// Connect | |
let mut sock = do io_error::cond.trap(|_| { | |
fail!("Unable to connect to %s.", addr.to_str()); | |
}).inside { | |
TcpStream::connect(addr).unwrap() | |
}; | |
// Now just perpetually read everything in! | |
loop { | |
let head = read_head(&mut sock); | |
if head.mode < 0 { | |
// Enable Screen | |
write_joyevent(&mut sock, 5, 1); | |
} else if head.mode > 3 { | |
// Something about flushing here | |
} else { | |
// Read in the next screen frame | |
let screen = sock.read_bytes(head.size as uint); | |
let width = 480u; | |
let height = 272u; | |
let (bpp, rmask, gmask, bmask) = match head.mode { | |
3 => (32, 0xFF, 0xFF00, 0xFF0000), | |
2 => (16, 0xF, 0xF0, 0xF00), | |
1 => (16, 0x1F, 0x1F << 5, 0x1F << 10), | |
0 => (16, 0x1F, 0x3F << 5, 0x1F << 11), | |
_ => fail!("Invalid mode.") | |
}; | |
// Create the SDL surface | |
let pixels = vec::raw::to_ptr(screen) as *c_void; | |
let raw = unsafe { | |
sdl::video::ll::SDL_CreateRGBSurfaceFrom( | |
pixels, width as c_int, height as c_int, | |
bpp as i32, (width * (bpp / 8)) as i32, | |
rmask, gmask, bmask, 0) | |
}; | |
let s = ScreenBuffer { | |
head: head, | |
surface: ~sdl::video::Surface { | |
raw: raw, | |
owned: true | |
}, | |
pixels: screen, | |
width: width, | |
height: height | |
}; | |
// Now send it off | |
c.send(s); | |
} | |
} | |
} | |
#[start] | |
fn start(argc: int, argv: **u8, crate_map: *u8) -> int { | |
std::rt::start_on_main_thread(argc, argv, crate_map, main) | |
} | |
fn main() { | |
do sdl::start { | |
// Init SDL | |
sdl::init([sdl::InitVideo]); | |
sdl::wm::set_caption("RemoteJoy", "RemoteJoy"); | |
// Now let's set up a surface to render to | |
let screen = match sdl::video::set_video_mode(480, 272, 32, | |
[sdl::video::HWSurface], | |
[sdl::video::DoubleBuf]) { | |
Ok(screen) => screen, | |
Err(e) => fail!("Unabled to set video mode: %s", e) | |
}; | |
// Let's create a chan/port pair to | |
// communicate with the net task | |
let (nport, nchan) = comm::stream(); | |
// Start the remote joy net task and give it its chan | |
let mut net_task = task::task(); | |
net_task.sched_mode(task::SingleThreaded); | |
net_task.spawn_with(nchan, remote_joy); | |
// Start the main loop! | |
'main: loop { | |
// And our event loop | |
'event: loop { | |
match sdl::event::poll_event() { | |
sdl::event::QuitEvent => break 'main, | |
sdl::event::NoEvent => break 'event, | |
sdl::event::KeyEvent(k, _, _, _) | |
if k == sdl::event::EscapeKey | |
=> break 'main, | |
_ => {} | |
} | |
} | |
// Anything from the net task? | |
if nport.peek() { | |
// Render the new frame | |
let frame = nport.recv(); | |
screen.blit(frame.surface); | |
} | |
screen.flip(); | |
} | |
sdl::quit(); | |
} | |
} |