mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 19:31:30 +11:00
Merge branch 'wip'
# Conflicts: # README.md
This commit is contained in:
commit
894da55768
50
Cargo.toml
50
Cargo.toml
|
@ -1,12 +1,58 @@
|
|||
[package]
|
||||
name = "minifb"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Daniel Collin <daniel@collin.com>"]
|
||||
description = "Cross-platform window setup for bitmap rendering"
|
||||
keywords = ["windowing", "framebuffer"]
|
||||
repository = "https://github.com/emoon/rust_minifb"
|
||||
documentation = "http://prodbg.com/minifb/minifb/index.html"
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gcc = "0.3.19"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.1.10"
|
||||
libc = "0.2"
|
||||
time = "0.1.34"
|
||||
|
||||
[target.x86_64-pc-windows-msvc.dependencies]
|
||||
user32-sys = "0.1.2"
|
||||
winapi = "0.2.4"
|
||||
kernel32-sys = "0.1.4"
|
||||
gdi32-sys = "0.1.1"
|
||||
|
||||
[target.x86_64-pc-windows-gnu.dependencies]
|
||||
user32-sys = "0.1.2"
|
||||
winapi = "0.2.4"
|
||||
kernel32-sys = "0.1.4"
|
||||
gdi32-sys = "0.1.1"
|
||||
|
||||
[target.i686-pc-windows-msvc.dependencies]
|
||||
user32-sys = "0.1.2"
|
||||
winapi = "0.2.4"
|
||||
kernel32-sys = "0.1.4"
|
||||
gdi32-sys = "0.1.1"
|
||||
|
||||
[target.i686-pc-windows-gnu.dependencies]
|
||||
user32-sys = "0.1.2"
|
||||
winapi = "0.2.4"
|
||||
kernel32-sys = "0.1.4"
|
||||
gdi32-sys = "0.1.1"
|
||||
|
||||
[target.i686-unknown-linux-gnu.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
||||
[target.x86_64-unknown-linux-gnu.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
||||
[target.arm-unknown-linux-gnueabihf.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
||||
[target.aarch64-unknown-linux-gnu.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
||||
[target.x86_64-unknown-dragonfly.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
||||
[target.x86_64-unknown-freebsd.dependencies]
|
||||
x11-dl = "~2.2"
|
||||
|
|
29
README.md
29
README.md
|
@ -7,32 +7,37 @@ rust_minifb (Mini FrameBuffer) is a small cross platform library written in [Rus
|
|||
```rust
|
||||
extern crate minifb;
|
||||
|
||||
const WIDTH: usize = 1280;
|
||||
const HEIGHT: usize = 720;
|
||||
use minifb::*;
|
||||
|
||||
const WIDTH: usize = 640;
|
||||
const HEIGHT: usize = 360;
|
||||
|
||||
fn main() {
|
||||
let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
||||
|
||||
if !(minifb::open("TestWindow", WIDTH, HEIGHT)) {
|
||||
return;
|
||||
}
|
||||
let mut window = Window::new("Noise Test - Press ESC to exit",
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
Scale::X1,
|
||||
Vsync::No)
|
||||
.unwrap();
|
||||
|
||||
while minifb::update(&buffer) {
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
for i in buffer.iter_mut() {
|
||||
*i = ... // write something here
|
||||
}
|
||||
*i = 0; // write something more funny here!
|
||||
}
|
||||
|
||||
minifb::close();
|
||||
window.update(&buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status
|
||||
------
|
||||
Currently Mac, Windows and Linux has been tested which are the supported platforms for now.
|
||||
Currently Windows and Mac are the current supported platforms. X11 (Linux/FreeBSD/etc) support is coming soon.
|
||||
|
||||
|
||||
Build instructions
|
||||
Build instruction
|
||||
------------------
|
||||
|
||||
```
|
||||
|
@ -40,6 +45,6 @@ cargo build
|
|||
cargo run --example noise
|
||||
```
|
||||
|
||||
This will run the [noise example](https://github.com/emoon/rust_minifb/blob/master/examples/noise.rs) which should look something like this (Mac screenshot)
|
||||
This will run the [noise example](https://github.com/emoon/rust_minifb/blob/windows-rs/examples/noise.rs) which should look something like this (Mac screenshot)
|
||||
|
||||
![mac_screenshot](https://dl.dropboxusercontent.com/u/5205843/rust_minifb/noise_screen.png)
|
||||
|
|
4
build.rs
4
build.rs
|
@ -8,9 +8,7 @@ fn main() {
|
|||
&["src/native/macosx/MacMiniFB.m",
|
||||
"src/native/macosx/OSXWindow.m",
|
||||
"src/native/macosx/OSXWindowFrameView.m"]); // MacOS
|
||||
} else if env.contains("windows") {
|
||||
gcc::compile_library("libminifb_native.a", &["src/native/windows/WinMiniFB.c"]); // Windows
|
||||
} else {
|
||||
} else if env.contains("linux") {
|
||||
gcc::compile_library("libminifb_native.a", &["src/native/x11/X11MiniFB.c"]); // Unix
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
extern crate minifb;
|
||||
|
||||
use minifb::*;
|
||||
|
||||
const WIDTH: usize = 640;
|
||||
const HEIGHT: usize = 360;
|
||||
|
||||
|
@ -10,11 +12,15 @@ fn main() {
|
|||
|
||||
let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
||||
|
||||
if !(minifb::open("Noise Test - Press ESC to exit", WIDTH, HEIGHT)) {
|
||||
let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X2) {
|
||||
Ok(win) => win,
|
||||
Err(err) => {
|
||||
println!("Unable to create window {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
while minifb::update(&buffer) {
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
for i in buffer.iter_mut() {
|
||||
noise = seed;
|
||||
noise >>= 3;
|
||||
|
@ -26,7 +32,17 @@ fn main() {
|
|||
noise &= 0xFF;
|
||||
*i = (noise << 16) | (noise << 8) | noise;
|
||||
}
|
||||
}
|
||||
|
||||
minifb::close();
|
||||
window.get_keys().map(|keys| {
|
||||
for t in keys {
|
||||
match t {
|
||||
Key::W => println!("holding w!"),
|
||||
Key::T => println!("holding t!"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.update(&buffer);
|
||||
}
|
||||
}
|
||||
|
|
123
src/key_handler.rs
Normal file
123
src/key_handler.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
extern crate time;
|
||||
|
||||
use std::mem;
|
||||
use {Key, KeyRepeat};
|
||||
|
||||
pub struct KeyHandler {
|
||||
prev_time: f64,
|
||||
delta_time: f32,
|
||||
keys: [bool; 512],
|
||||
keys_down_duration: [f32; 512],
|
||||
key_repeat_delay: f32,
|
||||
key_repeat_rate: f32,
|
||||
}
|
||||
|
||||
impl KeyHandler {
|
||||
pub fn new() -> KeyHandler {
|
||||
KeyHandler {
|
||||
keys: [false; 512],
|
||||
keys_down_duration: [-1.0; 512],
|
||||
prev_time: time::precise_time_s(),
|
||||
delta_time: 0.0,
|
||||
key_repeat_delay: 0.250,
|
||||
key_repeat_rate: 0.050,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_state(&mut self, key: Key, state: bool) {
|
||||
self.keys[key as usize] = state;
|
||||
}
|
||||
|
||||
pub fn get_keys(&self) -> Option<Vec<Key>> {
|
||||
let mut index: u16 = 0;
|
||||
let mut keys: Vec<Key> = Vec::new();
|
||||
|
||||
for i in self.keys.iter() {
|
||||
if *i {
|
||||
unsafe {
|
||||
keys.push(mem::transmute(index as u8));
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
Some(keys)
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
let current_time = time::precise_time_s();
|
||||
let delta_time = (current_time - self.prev_time) as f32;
|
||||
self.prev_time = current_time;
|
||||
self.delta_time = delta_time;
|
||||
|
||||
for i in 0..self.keys.len() {
|
||||
if self.keys[i] {
|
||||
if self.keys_down_duration[i] < 0.0 {
|
||||
self.keys_down_duration[i] = 0.0;
|
||||
} else {
|
||||
self.keys_down_duration[i] += delta_time;
|
||||
}
|
||||
} else {
|
||||
self.keys_down_duration[i] = -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option<Vec<Key>> {
|
||||
let mut index: u16 = 0;
|
||||
let mut keys: Vec<Key> = Vec::new();
|
||||
|
||||
for i in self.keys.iter() {
|
||||
if *i {
|
||||
unsafe {
|
||||
if Self::key_pressed(self, index as usize, repeat) {
|
||||
keys.push(mem::transmute(index as u8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
Some(keys)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, key: Key) -> bool {
|
||||
return self.keys[key as usize];
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_delay(&mut self, delay: f32) {
|
||||
self.key_repeat_delay = delay;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||
self.key_repeat_rate = rate;
|
||||
}
|
||||
|
||||
pub fn key_pressed(&self, index: usize, repeat: KeyRepeat) -> bool {
|
||||
let t = self.keys_down_duration[index];
|
||||
|
||||
if t == 0.0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
if repeat == KeyRepeat::Yes && t > self.key_repeat_delay {
|
||||
let delay = self.key_repeat_delay;
|
||||
let rate = self.key_repeat_rate;
|
||||
if ((((t - delay) % rate) > rate * 0.5)) != (((t - delay - self.delta_time) % rate) > rate * 0.5) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
|
||||
return Self::key_pressed(self, key as usize, repeat);
|
||||
}
|
||||
}
|
383
src/lib.rs
383
src/lib.rs
|
@ -1,70 +1,351 @@
|
|||
/// Scale will scale the frame buffer and the window that is being sent in when calling the update
|
||||
/// function. This is useful if you for example want to display a 320 x 256 window on a screen with
|
||||
/// much higher resolution which would result in that the window is very small.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Scale {
|
||||
/// This mode checks your current screen resolution and will caluclate the largest window size
|
||||
/// that can be used within that limit and resize it. Useful if you have a small buffer to
|
||||
/// display on a high resolution screen.
|
||||
FitScreen,
|
||||
/// 1X scale (which means leave the corrdinates sent into Window::new untouched)
|
||||
X1,
|
||||
/// 2X window scale (Example: 320 x 200 -> 640 x 400)
|
||||
X2,
|
||||
/// 4X window scale (Example: 320 x 200 -> 1280 x 800)
|
||||
X4,
|
||||
/// 8X window scale (Example: 320 x 200 -> 2560 x 1600)
|
||||
X8,
|
||||
/// 16X window scale (Example: 320 x 200 -> 5120 x 3200)
|
||||
X16,
|
||||
/// 32 window scale (Example: 320 x 200 -> 10240 x 6400)
|
||||
X32,
|
||||
}
|
||||
|
||||
/// Used for is_key_pressed and get_keys_pressed() to indicated if repeat of presses is wanted
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum KeyRepeat {
|
||||
/// Use repeat
|
||||
Yes,
|
||||
/// Don't use repeat
|
||||
No,
|
||||
}
|
||||
|
||||
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum Key {
|
||||
Key0 = 0,
|
||||
Key1 = 1,
|
||||
Key2 = 2,
|
||||
Key3 = 3,
|
||||
Key4 = 4,
|
||||
Key5 = 5,
|
||||
Key6 = 6,
|
||||
Key7 = 7,
|
||||
Key8 = 8,
|
||||
Key9 = 9,
|
||||
|
||||
A = 10,
|
||||
B = 11,
|
||||
C = 12,
|
||||
D = 13,
|
||||
E = 14,
|
||||
F = 15,
|
||||
G = 16,
|
||||
H = 17,
|
||||
I = 18,
|
||||
J = 19,
|
||||
K = 20,
|
||||
L = 21,
|
||||
M = 22,
|
||||
N = 23,
|
||||
O = 24,
|
||||
P = 25,
|
||||
Q = 26,
|
||||
R = 27,
|
||||
S = 28,
|
||||
T = 29,
|
||||
U = 30,
|
||||
V = 31,
|
||||
W = 32,
|
||||
X = 33,
|
||||
Y = 34,
|
||||
Z = 35,
|
||||
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Apostrophe,
|
||||
Backquote,
|
||||
|
||||
Backslash,
|
||||
Comma,
|
||||
Equal,
|
||||
LeftBracket,
|
||||
Minus,
|
||||
Period,
|
||||
RightBracket,
|
||||
Semicolon,
|
||||
|
||||
Slash,
|
||||
Backspace,
|
||||
Delete,
|
||||
End,
|
||||
Enter,
|
||||
|
||||
Escape,
|
||||
|
||||
Home,
|
||||
Insert,
|
||||
Menu,
|
||||
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
Pause,
|
||||
Space,
|
||||
Tab,
|
||||
NumLock,
|
||||
CapsLock,
|
||||
ScrollLock,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
LeftCtrl,
|
||||
RightCtrl,
|
||||
|
||||
NumPad0,
|
||||
NumPad1,
|
||||
NumPad2,
|
||||
NumPad3,
|
||||
NumPad4,
|
||||
NumPad5,
|
||||
NumPad6,
|
||||
NumPad7,
|
||||
NumPad8,
|
||||
NumPad9,
|
||||
NumPadDot,
|
||||
NumPadSlash,
|
||||
NumPadAsterisk,
|
||||
NumPadMinus,
|
||||
NumPadPlus,
|
||||
NumPadEnter,
|
||||
|
||||
LeftAlt,
|
||||
RightAlt,
|
||||
|
||||
LeftSuper,
|
||||
RightSuper,
|
||||
|
||||
/// Used when an Unknown key has been pressed
|
||||
Unknown,
|
||||
|
||||
Count = 107,
|
||||
}
|
||||
|
||||
extern crate libc;
|
||||
use std::ffi::CString;
|
||||
use std::mem::transmute;
|
||||
use libc::{c_char, c_int, c_void};
|
||||
|
||||
pub mod os;
|
||||
mod key_handler;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[link(name = "Cocoa", kind = "framework")]
|
||||
extern {
|
||||
fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int;
|
||||
fn mfb_update(buffer: *mut c_void) -> c_int;
|
||||
fn mfb_close();
|
||||
}
|
||||
|
||||
use self::os::macos as imp;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[link(name = "gdi32")]
|
||||
extern {
|
||||
fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int;
|
||||
fn mfb_update(buffer: *mut c_void) -> c_int;
|
||||
fn mfb_close();
|
||||
}
|
||||
use self::os::windows as imp;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[link(name = "X11")]
|
||||
extern {
|
||||
fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int;
|
||||
fn mfb_update(buffer: *mut c_void) -> c_int;
|
||||
fn mfb_close();
|
||||
///
|
||||
/// Window used for displaying a 32-bit RGB buffer. Here is a small example on how to use it:
|
||||
/// (without error checking
|
||||
///
|
||||
/// ```ignore
|
||||
///
|
||||
/// const WIDTH: usize = 640;
|
||||
/// const HEIGHT: usize = 360;
|
||||
///
|
||||
/// let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
||||
///
|
||||
/// let mut window = match Window::new("Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X1).unwrap()
|
||||
///
|
||||
/// while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
/// for i in buffer.iter_mut() {
|
||||
/// *i = 0; // write something interesting here
|
||||
/// }
|
||||
/// window.update(&buffer);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
||||
pub struct Window(imp::Window);
|
||||
|
||||
impl Window {
|
||||
///
|
||||
/// Opens up a new window
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut window = match Window::new("Test", 640, 400, Scale::X1) {
|
||||
/// Ok(win) => win,
|
||||
/// Err(err) => {
|
||||
/// println!("Unable to create window {}", err);
|
||||
/// return;
|
||||
/// }
|
||||
///};
|
||||
/// ```
|
||||
pub fn new(name: &str, width: usize, height: usize, scale: Scale) -> Result<imp::Window, &str> {
|
||||
imp::Window::new(name, width, height, scale)
|
||||
}
|
||||
|
||||
///
|
||||
/// Open up a window
|
||||
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
|
||||
/// the size of the created window
|
||||
///
|
||||
pub fn open(name: &str, width: usize, height: usize) -> bool {
|
||||
let s = CString::new(name).unwrap();
|
||||
let ret;
|
||||
|
||||
unsafe {
|
||||
ret = mfb_open(s.as_ptr(), width as c_int, height as c_int);
|
||||
}
|
||||
|
||||
match ret {
|
||||
0 => false,
|
||||
_ => true,
|
||||
}
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut buffer: [u32; 640 * 400] = [0; 640 * 400];
|
||||
///
|
||||
/// let mut window = match Window::new("Test", 640, 400, Scale::X1).unwrap();
|
||||
///
|
||||
/// window.update(&buffer);
|
||||
/// ```
|
||||
pub fn update(&mut self, buffer: &[u32]) {
|
||||
self.0.update(buffer)
|
||||
}
|
||||
|
||||
///
|
||||
/// Update
|
||||
/// Checks if the window is still open. A window can be closed by the user (by for example
|
||||
/// pressing the close button on the window) It's up to the user to make sure that this is
|
||||
/// being checked and take action depending on the state.
|
||||
///
|
||||
pub fn update(buffer: &[u32]) -> bool {
|
||||
let ret;
|
||||
unsafe {
|
||||
ret = mfb_update(transmute(buffer.as_ptr()));
|
||||
}
|
||||
|
||||
if ret < 0 {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// while window.is_open() {
|
||||
/// window.update(...)
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.0.is_open()
|
||||
}
|
||||
|
||||
///
|
||||
/// Close
|
||||
/// Get the current keys that are down.
|
||||
///
|
||||
pub fn close() {
|
||||
unsafe {
|
||||
mfb_close();
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// window.get_keys().map(|keys| {
|
||||
/// for t in keys {
|
||||
/// match t {
|
||||
/// Key::W => println!("holding w"),
|
||||
/// Key::T => println!("holding t"),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_keys(&self) -> Option<Vec<Key>> {
|
||||
self.0.get_keys()
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the current pressed keys. Repeat can be used to control if keys should
|
||||
/// be repeated if down or not.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// window.get_keys_pressed(KeyRepeat::No).map(|keys| {
|
||||
/// for t in keys {
|
||||
/// match t {
|
||||
/// Key::W => println!("pressed w"),
|
||||
/// Key::T => println!("pressed t"),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option<Vec<Key>> {
|
||||
self.0.get_keys_pressed(repeat)
|
||||
}
|
||||
|
||||
///
|
||||
/// Check if a single key is down.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if window.is_key_down(Key::A) {
|
||||
/// println!("Key A is down");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, key: Key) -> bool {
|
||||
self.0.is_key_down(key)
|
||||
}
|
||||
|
||||
///
|
||||
/// Check if a single key is pressed. KeyRepeat will control if the key should be repeated or
|
||||
/// not while being pressed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if window.is_key_pressed(KeyRepeat::No) {
|
||||
/// println!("Key A is down");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
|
||||
self.0.is_key_pressed(key, repeat)
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the delay for when a key is being held before it starts being repeated the default
|
||||
/// value is 0.25 sec
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// window.set_key_repeat_delay(0.5) // 0.5 sec before repeat starts
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn set_key_repeat_delay(&mut self, delay: f32) {
|
||||
self.0.set_key_repeat_delay(delay)
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the rate in between when the keys has passed the intital repeat_delay. The default
|
||||
/// value is 0.05 sec
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// window.set_key_repeat_rate(0.01) // 0.01 sec between keys
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||
self.0.set_key_repeat_rate(rate)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,83 +3,76 @@
|
|||
#include <Cocoa/Cocoa.h>
|
||||
#include <unistd.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static bool s_init = false;
|
||||
|
||||
void* g_updateBuffer = 0;
|
||||
int g_width = 0;
|
||||
int g_height = 0;
|
||||
static NSWindow* window_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int mfb_open(const char* name, int width, int height)
|
||||
void* mfb_open(const char* name, int width, int height, int scale)
|
||||
{
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
g_width = width;
|
||||
g_height = height;
|
||||
|
||||
if (!s_init) {
|
||||
[NSApplication sharedApplication];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
s_init = true;
|
||||
}
|
||||
|
||||
unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask;
|
||||
|
||||
NSRect rectangle = NSMakeRect(0, 0, width, height);
|
||||
window_ = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
|
||||
NSRect rectangle = NSMakeRect(0, 0, width * scale, height * scale);
|
||||
OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
|
||||
|
||||
if (!window_)
|
||||
if (!window)
|
||||
return 0;
|
||||
|
||||
[window_ setTitle:[NSString stringWithUTF8String:name]];
|
||||
[window_ setReleasedWhenClosed:NO];
|
||||
[window_ performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
|
||||
window->draw_buffer = malloc(width * height * 4);
|
||||
|
||||
[window_ center];
|
||||
if (!window->draw_buffer)
|
||||
return 0;
|
||||
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
window->scale = scale;
|
||||
window->key_callback = 0;
|
||||
|
||||
[window updateSize];
|
||||
|
||||
[window setTitle:[NSString stringWithUTF8String:name]];
|
||||
[window setReleasedWhenClosed:NO];
|
||||
[window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
|
||||
|
||||
[window center];
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
[pool drain];
|
||||
|
||||
return 1;
|
||||
return window;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_close()
|
||||
void mfb_close(void* win)
|
||||
{
|
||||
NSWindow* window = (NSWindow*)win;
|
||||
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
if (window_)
|
||||
[window_ close];
|
||||
if (window)
|
||||
[window close];
|
||||
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int updateEvents()
|
||||
static int update_events()
|
||||
{
|
||||
int state = 0;
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
if (event)
|
||||
{
|
||||
switch ([event type])
|
||||
{
|
||||
case NSKeyDown:
|
||||
case NSKeyUp:
|
||||
{
|
||||
state = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
default :
|
||||
{
|
||||
[NSApp sendEvent:event];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
[pool release];
|
||||
|
||||
return state;
|
||||
|
@ -87,10 +80,41 @@ static int updateEvents()
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int mfb_update(void* buffer)
|
||||
int mfb_update(void* window, void* buffer)
|
||||
{
|
||||
g_updateBuffer = buffer;
|
||||
int state = updateEvents();
|
||||
[[window_ contentView] setNeedsDisplay:YES];
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
memcpy(win->draw_buffer, buffer, win->width * win->height * 4);
|
||||
|
||||
//g_updateBuffer = buffer;
|
||||
int state = update_events();
|
||||
[[win contentView] setNeedsDisplay:YES];
|
||||
return state;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int mfb_should_close(void* window)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
return win->should_close;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t mfb_get_screen_size()
|
||||
{
|
||||
NSRect e = [[NSScreen mainScreen] frame];
|
||||
uint32_t w = (uint32_t)e.size.width;
|
||||
uint32_t h = (uint32_t)e.size.height;
|
||||
return (w << 16) | h;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_set_key_callback(void* window, void* rust_data, void (*key_callback)(void* user_data, int key, int state))
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
win->key_callback = key_callback;
|
||||
win->rust_data = rust_data;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// @class OSXWindowFrameView;
|
||||
|
||||
@interface OSXWindow : NSWindow
|
||||
{
|
||||
NSView* childContentView;
|
||||
@public void (*key_callback)(void* user_data, int key, int state);
|
||||
@public int width;
|
||||
@public int height;
|
||||
@public int scale;
|
||||
@public void* draw_buffer;
|
||||
@public void* rust_data;
|
||||
@public bool should_close;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -63,12 +63,82 @@
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
-(void)flagsChanged:(NSEvent *)event
|
||||
{
|
||||
const uint32_t flags = [event modifierFlags];
|
||||
|
||||
// Left Shift
|
||||
key_callback(rust_data, 0x38, flags == 0x20102 ? 1 : 0);
|
||||
|
||||
// RightShift
|
||||
key_callback(rust_data, 0x3c, flags == 0x20104 ? 1 : 0);
|
||||
|
||||
// Left Ctrl
|
||||
key_callback(rust_data, 0x3b, flags == 0x40101 ? 1 : 0);
|
||||
|
||||
// Right Ctrl
|
||||
key_callback(rust_data, 0x3b, flags == 0x42101 ? 1 : 0);
|
||||
|
||||
// Left Alt
|
||||
key_callback(rust_data, 0x3a, flags == 0x80120 ? 1 : 0);
|
||||
|
||||
// Right Super
|
||||
key_callback(rust_data, 0x3d, flags == 0x80140 ? 1 : 0);
|
||||
|
||||
// Left Super
|
||||
key_callback(rust_data, 0x37, flags == 0x100108 ? 1 : 0);
|
||||
|
||||
// Right Super
|
||||
key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
// Cmd+Q always closes app
|
||||
if ([event.characters.uppercaseString isEqualToString:@"Q"] && ([event modifierFlags] & NSCommandKeyMask)) {
|
||||
[self performClose:self];
|
||||
return;
|
||||
}
|
||||
|
||||
if (key_callback) {
|
||||
key_callback(rust_data, [event keyCode], 1);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
if (key_callback) {
|
||||
key_callback(rust_data, [event keyCode], 0);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)mainWindowChanged:(NSNotification *)aNotification
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
should_close = true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender
|
||||
{
|
||||
should_close = true;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)setContentView:(NSView *)aView
|
||||
{
|
||||
if ([childContentView isEqualTo:aView])
|
||||
|
@ -83,14 +153,16 @@
|
|||
if (!frameView)
|
||||
{
|
||||
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
|
||||
|
||||
frameView->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->scale = scale;
|
||||
[super setContentView:frameView];
|
||||
}
|
||||
|
||||
if (childContentView)
|
||||
{
|
||||
[childContentView removeFromSuperview];
|
||||
}
|
||||
|
||||
childContentView = aView;
|
||||
[childContentView setFrame:[self contentRectForFrameRect:bounds]];
|
||||
[childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
@ -133,4 +205,18 @@
|
|||
return NSInsetRect(windowContentRect, 0, 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)updateSize
|
||||
{
|
||||
OSXWindowFrameView* frameView = [super contentView];
|
||||
if (frameView)
|
||||
{
|
||||
frameView->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
@interface OSXWindowFrameView : NSView
|
||||
{
|
||||
@public int scale;
|
||||
@public int width;
|
||||
@public int height;
|
||||
@public void* draw_buffer;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
@implementation OSXWindowFrameView
|
||||
|
||||
extern void* g_updateBuffer;
|
||||
extern int g_width;
|
||||
extern int g_height;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSRect)resizeRect
|
||||
|
@ -27,21 +23,18 @@ extern int g_height;
|
|||
|
||||
- (void)drawRect:(NSRect)rect
|
||||
{
|
||||
if (!g_updateBuffer)
|
||||
return;
|
||||
|
||||
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_updateBuffer, g_width * g_height * 4, NULL);
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, draw_buffer, width * height * 4, NULL);
|
||||
|
||||
CGImageRef img = CGImageCreate(g_width, g_height, 8, 32, g_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
|
||||
CGImageRef img = CGImageCreate(width, height, 8, 32, width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
|
||||
provider, NULL, false, kCGRenderingIntentDefault);
|
||||
|
||||
CGColorSpaceRelease(space);
|
||||
CGDataProviderRelease(provider);
|
||||
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img);
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width * scale, height * scale), img);
|
||||
|
||||
CGImageRelease(img);
|
||||
}
|
||||
|
|
280
src/os/macos/mod.rs
Normal file
280
src/os/macos/mod.rs
Normal file
|
@ -0,0 +1,280 @@
|
|||
#![cfg(target_os = "macos")]
|
||||
|
||||
use {Scale, Key, KeyRepeat};
|
||||
use key_handler::KeyHandler;
|
||||
|
||||
use libc::{c_void, c_char, c_uchar};
|
||||
use std::ffi::{CString};
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
|
||||
// Table taken from GLFW and slightly modified
|
||||
|
||||
static KEY_MAPPINGS: [Key; 128] = [
|
||||
/* 00 */ Key::A,
|
||||
/* 01 */ Key::S,
|
||||
/* 02 */ Key::D,
|
||||
/* 03 */ Key::F,
|
||||
/* 04 */ Key::H,
|
||||
/* 05 */ Key::G,
|
||||
/* 06 */ Key::Z,
|
||||
/* 07 */ Key::X,
|
||||
/* 08 */ Key::C,
|
||||
/* 09 */ Key::V,
|
||||
/* 0a */ Key::Unknown, // GraveAccent
|
||||
/* 0b */ Key::B,
|
||||
/* 0c */ Key::Q,
|
||||
/* 0d */ Key::W,
|
||||
/* 0e */ Key::E,
|
||||
/* 0f */ Key::R,
|
||||
/* 10 */ Key::Y,
|
||||
/* 11 */ Key::T,
|
||||
/* 12 */ Key::Key1,
|
||||
/* 13 */ Key::Key2,
|
||||
/* 14 */ Key::Key3,
|
||||
/* 15 */ Key::Key4,
|
||||
/* 16 */ Key::Key6,
|
||||
/* 17 */ Key::Key5,
|
||||
/* 18 */ Key::Equal,
|
||||
/* 19 */ Key::Key9,
|
||||
/* 1a */ Key::Key7,
|
||||
/* 1b */ Key::Minus,
|
||||
/* 1c */ Key::Key8,
|
||||
/* 1d */ Key::Key0,
|
||||
/* 1e */ Key::RightBracket,
|
||||
/* 1f */ Key::O,
|
||||
/* 20 */ Key::U,
|
||||
/* 21 */ Key::LeftBracket,
|
||||
/* 22 */ Key::I,
|
||||
/* 23 */ Key::P,
|
||||
/* 24 */ Key::Enter,
|
||||
/* 25 */ Key::L,
|
||||
/* 26 */ Key::J,
|
||||
/* 27 */ Key::Apostrophe,
|
||||
/* 28 */ Key::K,
|
||||
/* 29 */ Key::Semicolon,
|
||||
/* 2a */ Key::Backslash,
|
||||
/* 2b */ Key::Comma,
|
||||
/* 2c */ Key::Slash,
|
||||
/* 2d */ Key::N,
|
||||
/* 2e */ Key::M,
|
||||
/* 2f */ Key::Period,
|
||||
/* 30 */ Key::Tab,
|
||||
/* 31 */ Key::Space,
|
||||
/* 32 */ Key::Unknown, // World1
|
||||
/* 33 */ Key::Backspace,
|
||||
/* 34 */ Key::Unknown,
|
||||
/* 35 */ Key::Escape,
|
||||
/* 36 */ Key::RightSuper,
|
||||
/* 37 */ Key::LeftSuper,
|
||||
/* 38 */ Key::LeftShift,
|
||||
/* 39 */ Key::CapsLock,
|
||||
/* 3a */ Key::LeftAlt,
|
||||
/* 3b */ Key::LeftCtrl,
|
||||
/* 3c */ Key::RightShift,
|
||||
/* 3d */ Key::RightAlt,
|
||||
/* 3e */ Key::RightCtrl,
|
||||
/* 3f */ Key::Unknown, // Function
|
||||
/* 40 */ Key::Unknown, // F17
|
||||
/* 41 */ Key::Unknown, // Decimal
|
||||
/* 42 */ Key::Unknown,
|
||||
/* 43 */ Key::Unknown, // Multiply
|
||||
/* 44 */ Key::Unknown,
|
||||
/* 45 */ Key::Unknown, // Add
|
||||
/* 46 */ Key::Unknown,
|
||||
/* 47 */ Key::NumLock, // Really KeypadClear...
|
||||
/* 48 */ Key::Unknown, // VolumeUp
|
||||
/* 49 */ Key::Unknown, // VolumeDown
|
||||
/* 4a */ Key::Unknown, // Mute
|
||||
/* 4b */ Key::Unknown,
|
||||
/* 4c */ Key::Enter,
|
||||
/* 4d */ Key::Unknown,
|
||||
/* 4e */ Key::Unknown, // Subtrackt
|
||||
/* 4f */ Key::Unknown, // F18
|
||||
/* 50 */ Key::Unknown, // F19
|
||||
/* 51 */ Key::Equal,
|
||||
/* 52 */ Key::NumPad0,
|
||||
/* 53 */ Key::NumPad1,
|
||||
/* 54 */ Key::NumPad2,
|
||||
/* 55 */ Key::NumPad3,
|
||||
/* 56 */ Key::NumPad4,
|
||||
/* 57 */ Key::NumPad5,
|
||||
/* 58 */ Key::NumPad6,
|
||||
/* 59 */ Key::NumPad7,
|
||||
/* 5a */ Key::Unknown, // F20
|
||||
/* 5b */ Key::NumPad8,
|
||||
/* 5c */ Key::NumPad9,
|
||||
/* 5d */ Key::Unknown,
|
||||
/* 5e */ Key::Unknown,
|
||||
/* 5f */ Key::Unknown,
|
||||
/* 60 */ Key::F5,
|
||||
/* 61 */ Key::F6,
|
||||
/* 62 */ Key::F7,
|
||||
/* 63 */ Key::F3,
|
||||
/* 64 */ Key::F8,
|
||||
/* 65 */ Key::F9,
|
||||
/* 66 */ Key::Unknown,
|
||||
/* 67 */ Key::F11,
|
||||
/* 68 */ Key::Unknown,
|
||||
/* 69 */ Key::Unknown, // PrintScreen
|
||||
/* 6a */ Key::Unknown, // F16
|
||||
/* 6b */ Key::F14,
|
||||
/* 6c */ Key::Unknown,
|
||||
/* 6d */ Key::F10,
|
||||
/* 6e */ Key::Unknown,
|
||||
/* 6f */ Key::F12,
|
||||
/* 70 */ Key::Unknown,
|
||||
/* 71 */ Key::F15,
|
||||
/* 72 */ Key::Insert, /* Really Help... */
|
||||
/* 73 */ Key::Home,
|
||||
/* 74 */ Key::PageUp,
|
||||
/* 75 */ Key::Delete,
|
||||
/* 76 */ Key::F4,
|
||||
/* 77 */ Key::End,
|
||||
/* 78 */ Key::F2,
|
||||
/* 79 */ Key::PageDown,
|
||||
/* 7a */ Key::F1,
|
||||
/* 7b */ Key::Left,
|
||||
/* 7c */ Key::Right,
|
||||
/* 7d */ Key::Down,
|
||||
/* 7e */ Key::Up,
|
||||
/* 7f */ Key::Unknown,
|
||||
];
|
||||
|
||||
#[link(name = "Cocoa", kind = "framework")]
|
||||
extern {
|
||||
fn mfb_open(name: *const c_char, width: u32, height: u32, scale: i32) -> *mut c_void;
|
||||
fn mfb_close(window: *mut c_void);
|
||||
fn mfb_update(window: *mut c_void, buffer: *const c_uchar);
|
||||
fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void, cb: unsafe extern fn(*mut c_void, i32, i32));
|
||||
fn mfb_should_close(window: *mut c_void) -> i32;
|
||||
fn mfb_get_screen_size() -> u32;
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
window_handle: *mut c_void,
|
||||
key_handler: KeyHandler,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
|
||||
let win: *mut Window = mem::transmute(window);
|
||||
|
||||
let s = state == 1;
|
||||
|
||||
if key > 128 {
|
||||
(*win).key_handler.set_key_state(Key::Unknown, s);
|
||||
} else {
|
||||
(*win).key_handler.set_key_state(KEY_MAPPINGS[key as usize], s);
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(name: &str, width: usize, height: usize, scale: Scale) -> Result<Window, &str> {
|
||||
let n = match CString::new(name) {
|
||||
Err(_) => {
|
||||
println!("Unable to convert {} to c_string", name);
|
||||
return Err("Unable to set correct name");
|
||||
}
|
||||
Ok(n) => n,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let handle = mfb_open(n.as_ptr(), width as u32, height as u32, Self::get_scale_factor(width, height, scale));
|
||||
|
||||
if handle == ptr::null_mut() {
|
||||
return Err("Unable to open Window");
|
||||
}
|
||||
|
||||
Ok(Window {
|
||||
window_handle: handle,
|
||||
key_handler: KeyHandler::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, buffer: &[u32]) {
|
||||
self.key_handler.update();
|
||||
|
||||
unsafe {
|
||||
mfb_update(self.window_handle, buffer.as_ptr() as *const u8);
|
||||
mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_keys(&self) -> Option<Vec<Key>> {
|
||||
self.key_handler.get_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option<Vec<Key>> {
|
||||
self.key_handler.get_keys_pressed(repeat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, key: Key) -> bool {
|
||||
self.key_handler.is_key_down(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_delay(&mut self, delay: f32) {
|
||||
self.key_handler.set_key_repeat_delay(delay)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||
self.key_handler.set_key_repeat_rate(rate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
|
||||
self.key_handler.is_key_pressed(key, repeat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_open(&self) -> bool {
|
||||
unsafe { mfb_should_close(self.window_handle) == 0 }
|
||||
}
|
||||
|
||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||
let factor: i32 = match scale {
|
||||
Scale::X1 => 1,
|
||||
Scale::X2 => 2,
|
||||
Scale::X4 => 4,
|
||||
Scale::X8 => 8,
|
||||
Scale::X16 => 16,
|
||||
Scale::X32 => 32,
|
||||
Scale::FitScreen => {
|
||||
let wh: u32 = mfb_get_screen_size();
|
||||
let screen_x = (wh >> 16) as i32;
|
||||
let screen_y = (wh & 0xffff) as i32;
|
||||
|
||||
let mut scale = 1i32;
|
||||
|
||||
loop {
|
||||
let w = width as i32 * (scale + 1);
|
||||
let h = height as i32 * (scale + 1);
|
||||
|
||||
if w > screen_x || h > screen_y {
|
||||
break;
|
||||
}
|
||||
|
||||
scale *= 2;
|
||||
}
|
||||
|
||||
scale
|
||||
}
|
||||
};
|
||||
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
mfb_close(self.window_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
src/os/mod.rs
Normal file
4
src/os/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
428
src/os/windows/mod.rs
Normal file
428
src/os/windows/mod.rs
Normal file
|
@ -0,0 +1,428 @@
|
|||
#![cfg(target_os = "windows")]
|
||||
|
||||
extern crate user32;
|
||||
extern crate kernel32;
|
||||
extern crate winapi;
|
||||
extern crate gdi32;
|
||||
extern crate time;
|
||||
|
||||
use {Scale, Key, KeyRepeat};
|
||||
|
||||
use key_handler::KeyHandler;
|
||||
|
||||
use std::ptr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ffi::OsStr;
|
||||
use std::mem;
|
||||
|
||||
use self::winapi::windef::HWND;
|
||||
use self::winapi::windef::HDC;
|
||||
use self::winapi::winuser::WS_OVERLAPPEDWINDOW;
|
||||
use self::winapi::winuser::WNDCLASSW;
|
||||
use self::winapi::wingdi::BITMAPINFOHEADER;
|
||||
use self::winapi::wingdi::RGBQUAD;
|
||||
|
||||
// Wrap this so we can have a proper numbef of bmiColors to write in
|
||||
#[repr(C)]
|
||||
struct BitmapInfo {
|
||||
pub bmi_header: BITMAPINFOHEADER,
|
||||
pub bmi_colors: [RGBQUAD; 3],
|
||||
}
|
||||
|
||||
fn update_key_state(window: &mut Window, wparam: u32, state: bool) {
|
||||
match wparam & 0x1ff {
|
||||
0x00B => window.key_handler.set_key_state(Key::Key0, state),
|
||||
0x002 => window.key_handler.set_key_state(Key::Key1, state),
|
||||
0x003 => window.key_handler.set_key_state(Key::Key2, state),
|
||||
0x004 => window.key_handler.set_key_state(Key::Key3, state),
|
||||
0x005 => window.key_handler.set_key_state(Key::Key4, state),
|
||||
0x006 => window.key_handler.set_key_state(Key::Key5, state),
|
||||
0x007 => window.key_handler.set_key_state(Key::Key6, state),
|
||||
0x008 => window.key_handler.set_key_state(Key::Key7, state),
|
||||
0x009 => window.key_handler.set_key_state(Key::Key8, state),
|
||||
0x00A => window.key_handler.set_key_state(Key::Key9, state),
|
||||
0x01E => window.key_handler.set_key_state(Key::A, state),
|
||||
0x030 => window.key_handler.set_key_state(Key::B, state),
|
||||
0x02E => window.key_handler.set_key_state(Key::C, state),
|
||||
0x020 => window.key_handler.set_key_state(Key::D, state),
|
||||
0x012 => window.key_handler.set_key_state(Key::E, state),
|
||||
0x021 => window.key_handler.set_key_state(Key::F, state),
|
||||
0x022 => window.key_handler.set_key_state(Key::G, state),
|
||||
0x023 => window.key_handler.set_key_state(Key::H, state),
|
||||
0x017 => window.key_handler.set_key_state(Key::I, state),
|
||||
0x024 => window.key_handler.set_key_state(Key::J, state),
|
||||
0x025 => window.key_handler.set_key_state(Key::K, state),
|
||||
0x026 => window.key_handler.set_key_state(Key::L, state),
|
||||
0x032 => window.key_handler.set_key_state(Key::M, state),
|
||||
0x031 => window.key_handler.set_key_state(Key::N, state),
|
||||
0x018 => window.key_handler.set_key_state(Key::O, state),
|
||||
0x019 => window.key_handler.set_key_state(Key::P, state),
|
||||
0x010 => window.key_handler.set_key_state(Key::Q, state),
|
||||
0x013 => window.key_handler.set_key_state(Key::R, state),
|
||||
0x01F => window.key_handler.set_key_state(Key::S, state),
|
||||
0x014 => window.key_handler.set_key_state(Key::T, state),
|
||||
0x016 => window.key_handler.set_key_state(Key::U, state),
|
||||
0x02F => window.key_handler.set_key_state(Key::V, state),
|
||||
0x011 => window.key_handler.set_key_state(Key::W, state),
|
||||
0x02D => window.key_handler.set_key_state(Key::X, state),
|
||||
0x015 => window.key_handler.set_key_state(Key::Y, state),
|
||||
0x02C => window.key_handler.set_key_state(Key::Z, state),
|
||||
0x03B => window.key_handler.set_key_state(Key::F1, state),
|
||||
0x03C => window.key_handler.set_key_state(Key::F2, state),
|
||||
0x03D => window.key_handler.set_key_state(Key::F3, state),
|
||||
0x03E => window.key_handler.set_key_state(Key::F4, state),
|
||||
0x03F => window.key_handler.set_key_state(Key::F5, state),
|
||||
0x040 => window.key_handler.set_key_state(Key::F6, state),
|
||||
0x041 => window.key_handler.set_key_state(Key::F7, state),
|
||||
0x042 => window.key_handler.set_key_state(Key::F8, state),
|
||||
0x043 => window.key_handler.set_key_state(Key::F9, state),
|
||||
0x044 => window.key_handler.set_key_state(Key::F10, state),
|
||||
0x057 => window.key_handler.set_key_state(Key::F11, state),
|
||||
0x058 => window.key_handler.set_key_state(Key::F12, state),
|
||||
0x150 => window.key_handler.set_key_state(Key::Down, state),
|
||||
0x14B => window.key_handler.set_key_state(Key::Left, state),
|
||||
0x14D => window.key_handler.set_key_state(Key::Right, state),
|
||||
0x148 => window.key_handler.set_key_state(Key::Up, state),
|
||||
0x028 => window.key_handler.set_key_state(Key::Apostrophe, state),
|
||||
0x029 => window.key_handler.set_key_state(Key::Backquote, state),
|
||||
0x02B => window.key_handler.set_key_state(Key::Backslash, state),
|
||||
0x033 => window.key_handler.set_key_state(Key::Comma, state),
|
||||
0x00D => window.key_handler.set_key_state(Key::Equal, state),
|
||||
0x01A => window.key_handler.set_key_state(Key::LeftBracket, state),
|
||||
0x00C => window.key_handler.set_key_state(Key::Minus, state),
|
||||
0x034 => window.key_handler.set_key_state(Key::Period, state),
|
||||
0x01B => window.key_handler.set_key_state(Key::RightBracket, state),
|
||||
0x027 => window.key_handler.set_key_state(Key::Semicolon, state),
|
||||
0x035 => window.key_handler.set_key_state(Key::Slash, state),
|
||||
0x00E => window.key_handler.set_key_state(Key::Backspace, state),
|
||||
0x153 => window.key_handler.set_key_state(Key::Delete, state),
|
||||
0x14F => window.key_handler.set_key_state(Key::End, state),
|
||||
0x01C => window.key_handler.set_key_state(Key::Enter, state),
|
||||
0x001 => window.key_handler.set_key_state(Key::Escape, state),
|
||||
0x147 => window.key_handler.set_key_state(Key::Home, state),
|
||||
0x152 => window.key_handler.set_key_state(Key::Insert, state),
|
||||
0x15D => window.key_handler.set_key_state(Key::Menu, state),
|
||||
0x151 => window.key_handler.set_key_state(Key::PageDown, state),
|
||||
0x149 => window.key_handler.set_key_state(Key::PageUp, state),
|
||||
0x045 => window.key_handler.set_key_state(Key::Pause, state),
|
||||
0x039 => window.key_handler.set_key_state(Key::Space, state),
|
||||
0x00F => window.key_handler.set_key_state(Key::Tab, state),
|
||||
0x145 => window.key_handler.set_key_state(Key::NumLock, state),
|
||||
0x03A => window.key_handler.set_key_state(Key::CapsLock, state),
|
||||
0x046 => window.key_handler.set_key_state(Key::ScrollLock, state),
|
||||
0x02A => window.key_handler.set_key_state(Key::LeftShift, state),
|
||||
0x036 => window.key_handler.set_key_state(Key::RightShift, state),
|
||||
0x01D => window.key_handler.set_key_state(Key::LeftCtrl, state),
|
||||
0x11D => window.key_handler.set_key_state(Key::RightCtrl, state),
|
||||
0x052 => window.key_handler.set_key_state(Key::NumPad0, state),
|
||||
0x04F => window.key_handler.set_key_state(Key::NumPad1, state),
|
||||
0x050 => window.key_handler.set_key_state(Key::NumPad2, state),
|
||||
0x051 => window.key_handler.set_key_state(Key::NumPad3, state),
|
||||
0x04B => window.key_handler.set_key_state(Key::NumPad4, state),
|
||||
0x04C => window.key_handler.set_key_state(Key::NumPad5, state),
|
||||
0x04D => window.key_handler.set_key_state(Key::NumPad6, state),
|
||||
0x047 => window.key_handler.set_key_state(Key::NumPad7, state),
|
||||
0x048 => window.key_handler.set_key_state(Key::NumPad8, state),
|
||||
0x049 => window.key_handler.set_key_state(Key::NumPad9, state),
|
||||
0x053 => window.key_handler.set_key_state(Key::NumPadDot, state),
|
||||
0x135 => window.key_handler.set_key_state(Key::NumPadSlash, state),
|
||||
0x037 => window.key_handler.set_key_state(Key::NumPadAsterisk, state),
|
||||
0x04A => window.key_handler.set_key_state(Key::NumPadMinus, state),
|
||||
0x04E => window.key_handler.set_key_state(Key::NumPadPlus, state),
|
||||
0x11C => window.key_handler.set_key_state(Key::NumPadEnter, state),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsafe extern "system" fn wnd_proc(window: winapi::HWND,
|
||||
msg: winapi::UINT,
|
||||
wparam: winapi::WPARAM,
|
||||
lparam: winapi::LPARAM)
|
||||
-> winapi::LRESULT {
|
||||
// This make sure we actually don't do anything before the user data has been setup for the
|
||||
// window
|
||||
|
||||
let user_data = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA);
|
||||
|
||||
if user_data == 0 {
|
||||
return user32::DefWindowProcW(window, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
let mut wnd: &mut Window = mem::transmute(user_data);
|
||||
|
||||
match msg {
|
||||
winapi::winuser::WM_KEYDOWN => {
|
||||
update_key_state(wnd, (lparam as u32) >> 16, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
winapi::winuser::WM_CLOSE => {
|
||||
wnd.is_open = false;
|
||||
}
|
||||
|
||||
winapi::winuser::WM_KEYUP => {
|
||||
update_key_state(wnd, (lparam as u32) >> 16, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
winapi::winuser::WM_PAINT => {
|
||||
let mut bitmap_info: BitmapInfo = mem::zeroed();
|
||||
|
||||
bitmap_info.bmi_header.biSize = mem::size_of::<BITMAPINFOHEADER>() as u32;
|
||||
bitmap_info.bmi_header.biPlanes = 1;
|
||||
bitmap_info.bmi_header.biBitCount = 32;
|
||||
bitmap_info.bmi_header.biCompression = winapi::wingdi::BI_BITFIELDS;
|
||||
bitmap_info.bmi_header.biWidth = wnd.width;
|
||||
bitmap_info.bmi_header.biHeight = -wnd.height;
|
||||
bitmap_info.bmi_colors[0].rgbRed = 0xff;
|
||||
bitmap_info.bmi_colors[1].rgbGreen = 0xff;
|
||||
bitmap_info.bmi_colors[2].rgbBlue = 0xff;
|
||||
|
||||
gdi32::StretchDIBits(wnd.dc.unwrap(),
|
||||
0,
|
||||
0,
|
||||
wnd.width * wnd.scale_factor,
|
||||
wnd.height * wnd.scale_factor,
|
||||
0,
|
||||
0,
|
||||
wnd.width,
|
||||
wnd.height,
|
||||
mem::transmute(wnd.buffer.as_ptr()),
|
||||
mem::transmute(&bitmap_info),
|
||||
winapi::wingdi::DIB_RGB_COLORS,
|
||||
winapi::wingdi::SRCCOPY);
|
||||
|
||||
user32::ValidateRect(window, ptr::null_mut());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
return user32::DefWindowProcW(window, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
pub enum MinifbError {
|
||||
UnableToCreateWindow,
|
||||
}
|
||||
|
||||
fn to_wstring(str: &str) -> Vec<u16> {
|
||||
let mut v: Vec<u16> = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
|
||||
v.push(0u16);
|
||||
v
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
dc: Option<HDC>,
|
||||
window: Option<HWND>,
|
||||
buffer: Vec<u32>,
|
||||
is_open : bool,
|
||||
scale_factor: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
key_handler: KeyHandler,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
fn open_window(name: &str, width: usize, height: usize, scale_factor: i32) -> Option<HWND> {
|
||||
unsafe {
|
||||
let class_name = to_wstring("minifb_window");
|
||||
let class = WNDCLASSW {
|
||||
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
|
||||
lpfnWndProc: Some(wnd_proc),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: kernel32::GetModuleHandleA(ptr::null()),
|
||||
hIcon: ptr::null_mut(),
|
||||
hCursor: ptr::null_mut(),
|
||||
hbrBackground: ptr::null_mut(),
|
||||
lpszMenuName: ptr::null(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
};
|
||||
|
||||
if user32::RegisterClassW(&class) == 0 {
|
||||
// ignore the "Class already exists" error for multiple windows
|
||||
if kernel32::GetLastError() as u32 != 1410 {
|
||||
println!("Unable to register class, error {}", kernel32::GetLastError() as u32);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let new_width = width * scale_factor as usize;
|
||||
let new_height = height * scale_factor as usize;
|
||||
|
||||
let mut rect = winapi::RECT {
|
||||
left: 0,
|
||||
right: new_width as winapi::LONG,
|
||||
top: 0,
|
||||
bottom: new_height as winapi::LONG,
|
||||
};
|
||||
|
||||
user32::AdjustWindowRect(&mut rect,
|
||||
winapi::WS_POPUP | winapi::WS_SYSMENU | winapi::WS_CAPTION,
|
||||
0);
|
||||
|
||||
rect.right -= rect.left;
|
||||
rect.bottom -= rect.top;
|
||||
|
||||
let window_name = to_wstring(name);
|
||||
|
||||
let handle = user32::CreateWindowExW(0,
|
||||
class_name.as_ptr(),
|
||||
window_name.as_ptr(),
|
||||
winapi::WS_OVERLAPPEDWINDOW &
|
||||
!winapi::WS_MAXIMIZEBOX &
|
||||
!winapi::WS_THICKFRAME,
|
||||
winapi::CW_USEDEFAULT,
|
||||
winapi::CW_USEDEFAULT,
|
||||
rect.right,
|
||||
rect.bottom,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut());
|
||||
if handle.is_null() {
|
||||
println!("Unable to create window, error {}", kernel32::GetLastError() as u32);
|
||||
return None;
|
||||
}
|
||||
|
||||
user32::ShowWindow(handle, winapi::SW_NORMAL);
|
||||
|
||||
return Some(handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
scale: Scale)
|
||||
-> Result<Window, &str> {
|
||||
unsafe {
|
||||
let scale_factor = Self::get_scale_factor(width, height, scale);
|
||||
|
||||
let handle = Self::open_window(name, width, height, scale_factor);
|
||||
|
||||
if handle.is_none() {
|
||||
return Err("Unable to create Window");
|
||||
}
|
||||
|
||||
let window = Window {
|
||||
dc: Some(user32::GetDC(handle.unwrap())),
|
||||
window: Some(handle.unwrap()),
|
||||
buffer: Vec::new(),
|
||||
key_handler: KeyHandler::new(),
|
||||
is_open: true,
|
||||
scale_factor: scale_factor,
|
||||
width: width as i32,
|
||||
height: height as i32,
|
||||
};
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_keys(&self) -> Option<Vec<Key>> {
|
||||
self.key_handler.get_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option<Vec<Key>> {
|
||||
self.key_handler.get_keys_pressed(repeat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, key: Key) -> bool {
|
||||
self.key_handler.is_key_down(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_delay(&mut self, delay: f32) {
|
||||
self.key_handler.set_key_repeat_delay(delay)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||
self.key_handler.set_key_repeat_rate(rate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
|
||||
self.key_handler.is_key_pressed(key, repeat)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn is_open(&self) -> bool {
|
||||
return self.is_open
|
||||
}
|
||||
|
||||
pub fn update(&mut self, buffer: &[u32]) {
|
||||
unsafe {
|
||||
let mut msg = mem::uninitialized();
|
||||
let window = self.window.unwrap();
|
||||
|
||||
self.key_handler.update();
|
||||
|
||||
// TODO: Optimize
|
||||
|
||||
self.buffer = buffer.iter().cloned().collect();
|
||||
|
||||
user32::SetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA, mem::transmute(self));
|
||||
user32::InvalidateRect(window, ptr::null_mut(), winapi::TRUE);
|
||||
|
||||
while user32::PeekMessageW(&mut msg, window, 0, 0, winapi::winuser::PM_REMOVE) != 0 {
|
||||
user32::TranslateMessage(&mut msg);
|
||||
user32::DispatchMessageW(&mut msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||
let factor: i32 = match scale {
|
||||
Scale::X1 => 1,
|
||||
Scale::X2 => 2,
|
||||
Scale::X4 => 4,
|
||||
Scale::X8 => 8,
|
||||
Scale::X16 => 16,
|
||||
Scale::X32 => 32,
|
||||
Scale::FitScreen => {
|
||||
let screen_x = user32::GetSystemMetrics(winapi::winuser::SM_CXSCREEN) as i32;
|
||||
let screen_y = user32::GetSystemMetrics(winapi::winuser::SM_CYSCREEN) as i32;
|
||||
|
||||
let mut scale = 1i32;
|
||||
|
||||
loop {
|
||||
let w = width as i32 * (scale + 1);
|
||||
let h = height as i32 * (scale + 1);
|
||||
|
||||
if w > screen_x || h > screen_y {
|
||||
break;
|
||||
}
|
||||
|
||||
scale *= 2;
|
||||
}
|
||||
|
||||
scale
|
||||
}
|
||||
};
|
||||
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.dc.is_some() {
|
||||
user32::ReleaseDC(self.window.unwrap(), self.dc.unwrap());
|
||||
}
|
||||
|
||||
if self.window.is_some() {
|
||||
user32::CloseWindow(self.window.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue