Skip to content

Instantly share code, notes, and snippets.

@luqmana
Last active December 21, 2015 06:28
Show Gist options
  • Save luqmana/6264106 to your computer and use it in GitHub Desktop.
Save luqmana/6264106 to your computer and use it in GitHub Desktop.
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();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment