mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-24 03:41:29 +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]
|
[package]
|
||||||
name = "minifb"
|
name = "minifb"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Daniel Collin <daniel@collin.com>"]
|
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 = "build.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gcc = "0.3.19"
|
gcc = "0.3.19"
|
||||||
|
|
||||||
[dependencies]
|
[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
|
```rust
|
||||||
extern crate minifb;
|
extern crate minifb;
|
||||||
|
|
||||||
const WIDTH: usize = 1280;
|
use minifb::*;
|
||||||
const HEIGHT: usize = 720;
|
|
||||||
|
const WIDTH: usize = 640;
|
||||||
|
const HEIGHT: usize = 360;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
||||||
|
|
||||||
if !(minifb::open("TestWindow", WIDTH, HEIGHT)) {
|
let mut window = Window::new("Noise Test - Press ESC to exit",
|
||||||
return;
|
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() {
|
for i in buffer.iter_mut() {
|
||||||
*i = ... // write something here
|
*i = 0; // write something more funny here!
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
minifb::close();
|
window.update(&buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Status
|
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
|
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)
|
![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/MacMiniFB.m",
|
||||||
"src/native/macosx/OSXWindow.m",
|
"src/native/macosx/OSXWindow.m",
|
||||||
"src/native/macosx/OSXWindowFrameView.m"]); // MacOS
|
"src/native/macosx/OSXWindowFrameView.m"]); // MacOS
|
||||||
} else if env.contains("windows") {
|
} else if env.contains("linux") {
|
||||||
gcc::compile_library("libminifb_native.a", &["src/native/windows/WinMiniFB.c"]); // Windows
|
|
||||||
} else {
|
|
||||||
gcc::compile_library("libminifb_native.a", &["src/native/x11/X11MiniFB.c"]); // Unix
|
gcc::compile_library("libminifb_native.a", &["src/native/x11/X11MiniFB.c"]); // Unix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
extern crate minifb;
|
extern crate minifb;
|
||||||
|
|
||||||
|
use minifb::*;
|
||||||
|
|
||||||
const WIDTH: usize = 640;
|
const WIDTH: usize = 640;
|
||||||
const HEIGHT: usize = 360;
|
const HEIGHT: usize = 360;
|
||||||
|
|
||||||
|
@ -10,11 +12,15 @@ fn main() {
|
||||||
|
|
||||||
let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
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) {
|
||||||
return;
|
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() {
|
for i in buffer.iter_mut() {
|
||||||
noise = seed;
|
noise = seed;
|
||||||
noise >>= 3;
|
noise >>= 3;
|
||||||
|
@ -26,7 +32,17 @@ fn main() {
|
||||||
noise &= 0xFF;
|
noise &= 0xFF;
|
||||||
*i = (noise << 16) | (noise << 8) | noise;
|
*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);
|
||||||
|
}
|
||||||
|
}
|
389
src/lib.rs
389
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;
|
extern crate libc;
|
||||||
use std::ffi::CString;
|
|
||||||
use std::mem::transmute;
|
pub mod os;
|
||||||
use libc::{c_char, c_int, c_void};
|
mod key_handler;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[link(name = "Cocoa", kind = "framework")]
|
use self::os::macos as imp;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
#[link(name = "gdi32")]
|
use self::os::windows as imp;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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();
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Open up a window
|
/// 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 fn open(name: &str, width: usize, height: usize) -> bool {
|
|
||||||
let s = CString::new(name).unwrap();
|
|
||||||
let ret;
|
|
||||||
|
|
||||||
unsafe {
|
pub struct Window(imp::Window);
|
||||||
ret = mfb_open(s.as_ptr(), width as c_int, height as c_int);
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
match ret {
|
///
|
||||||
0 => false,
|
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
|
||||||
_ => true,
|
/// the size of the created window
|
||||||
}
|
///
|
||||||
}
|
/// # Examples
|
||||||
|
///
|
||||||
///
|
/// ```ignore
|
||||||
/// Update
|
/// let mut buffer: [u32; 640 * 400] = [0; 640 * 400];
|
||||||
///
|
///
|
||||||
pub fn update(buffer: &[u32]) -> bool {
|
/// let mut window = match Window::new("Test", 640, 400, Scale::X1).unwrap();
|
||||||
let ret;
|
///
|
||||||
unsafe {
|
/// window.update(&buffer);
|
||||||
ret = mfb_update(transmute(buffer.as_ptr()));
|
/// ```
|
||||||
|
pub fn update(&mut self, buffer: &[u32]) {
|
||||||
|
self.0.update(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ret < 0 {
|
///
|
||||||
return false;
|
/// Checks if the window is still open. A window can be closed by the user (by for example
|
||||||
} else {
|
/// pressing the close button on the window) It's up to the user to make sure that this is
|
||||||
return true;
|
/// being checked and take action depending on the state.
|
||||||
|
///
|
||||||
|
/// # 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() {
|
/// # Examples
|
||||||
unsafe {
|
///
|
||||||
mfb_close();
|
/// ```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 <Cocoa/Cocoa.h>
|
||||||
#include <unistd.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];
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
g_width = width;
|
if (!s_init) {
|
||||||
g_height = height;
|
[NSApplication sharedApplication];
|
||||||
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
[NSApplication sharedApplication];
|
s_init = true;
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
}
|
||||||
|
|
||||||
unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask;
|
unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask;
|
||||||
|
|
||||||
NSRect rectangle = NSMakeRect(0, 0, width, height);
|
NSRect rectangle = NSMakeRect(0, 0, width * scale, height * scale);
|
||||||
window_ = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
|
OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
|
||||||
|
|
||||||
if (!window_)
|
if (!window)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
[window_ setTitle:[NSString stringWithUTF8String:name]];
|
window->draw_buffer = malloc(width * height * 4);
|
||||||
[window_ setReleasedWhenClosed:NO];
|
|
||||||
[window_ performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
|
|
||||||
|
|
||||||
[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];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
|
||||||
[pool drain];
|
[pool drain];
|
||||||
|
|
||||||
return 1;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void mfb_close()
|
void mfb_close(void* win)
|
||||||
{
|
{
|
||||||
|
NSWindow* window = (NSWindow*)win;
|
||||||
|
|
||||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
if (window_)
|
if (window)
|
||||||
[window_ close];
|
[window close];
|
||||||
|
|
||||||
[pool drain];
|
[pool drain];
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static int updateEvents()
|
static int update_events()
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||||
if (event)
|
[NSApp sendEvent:event];
|
||||||
{
|
|
||||||
switch ([event type])
|
|
||||||
{
|
|
||||||
case NSKeyDown:
|
|
||||||
case NSKeyUp:
|
|
||||||
{
|
|
||||||
state = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
[NSApp sendEvent:event];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[pool release];
|
[pool release];
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
@ -87,10 +80,41 @@ static int updateEvents()
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
int mfb_update(void* buffer)
|
int mfb_update(void* window, void* buffer)
|
||||||
{
|
{
|
||||||
g_updateBuffer = buffer;
|
OSXWindow* win = (OSXWindow*)window;
|
||||||
int state = updateEvents();
|
memcpy(win->draw_buffer, buffer, win->width * win->height * 4);
|
||||||
[[window_ contentView] setNeedsDisplay:YES];
|
|
||||||
|
//g_updateBuffer = buffer;
|
||||||
|
int state = update_events();
|
||||||
|
[[win contentView] setNeedsDisplay:YES];
|
||||||
return state;
|
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>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
// @class OSXWindowFrameView;
|
|
||||||
|
|
||||||
@interface OSXWindow : NSWindow
|
@interface OSXWindow : NSWindow
|
||||||
{
|
{
|
||||||
NSView* childContentView;
|
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
|
@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)mainWindowChanged:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
- (void)windowWillClose:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
should_close = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
- (BOOL)windowShouldClose:(id)sender
|
||||||
|
{
|
||||||
|
should_close = true;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
- (void)setContentView:(NSView *)aView
|
- (void)setContentView:(NSView *)aView
|
||||||
{
|
{
|
||||||
if ([childContentView isEqualTo:aView])
|
if ([childContentView isEqualTo:aView])
|
||||||
|
@ -79,18 +149,20 @@
|
||||||
NSRect bounds = [self frame];
|
NSRect bounds = [self frame];
|
||||||
bounds.origin = NSZeroPoint;
|
bounds.origin = NSZeroPoint;
|
||||||
|
|
||||||
OSXWindowFrameView *frameView = [super contentView];
|
OSXWindowFrameView* frameView = [super contentView];
|
||||||
if (!frameView)
|
if (!frameView)
|
||||||
{
|
{
|
||||||
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
|
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
|
||||||
|
frameView->width = width;
|
||||||
|
frameView->height = height;
|
||||||
|
frameView->draw_buffer = draw_buffer;
|
||||||
|
frameView->scale = scale;
|
||||||
[super setContentView:frameView];
|
[super setContentView:frameView];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childContentView)
|
if (childContentView)
|
||||||
{
|
|
||||||
[childContentView removeFromSuperview];
|
[childContentView removeFromSuperview];
|
||||||
}
|
|
||||||
childContentView = aView;
|
childContentView = aView;
|
||||||
[childContentView setFrame:[self contentRectForFrameRect:bounds]];
|
[childContentView setFrame:[self contentRectForFrameRect:bounds]];
|
||||||
[childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
[childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||||
|
@ -133,4 +205,18 @@
|
||||||
return NSInsetRect(windowContentRect, 0, 0);
|
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
|
@end
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
@interface OSXWindowFrameView : NSView
|
@interface OSXWindowFrameView : NSView
|
||||||
{
|
{
|
||||||
|
@public int scale;
|
||||||
|
@public int width;
|
||||||
|
@public int height;
|
||||||
|
@public void* draw_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
@implementation OSXWindowFrameView
|
@implementation OSXWindowFrameView
|
||||||
|
|
||||||
extern void* g_updateBuffer;
|
|
||||||
extern int g_width;
|
|
||||||
extern int g_height;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
- (NSRect)resizeRect
|
- (NSRect)resizeRect
|
||||||
|
@ -27,21 +23,18 @@ extern int g_height;
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)rect
|
- (void)drawRect:(NSRect)rect
|
||||||
{
|
{
|
||||||
if (!g_updateBuffer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
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);
|
provider, NULL, false, kCGRenderingIntentDefault);
|
||||||
|
|
||||||
CGColorSpaceRelease(space);
|
CGColorSpaceRelease(space);
|
||||||
CGDataProviderRelease(provider);
|
CGDataProviderRelease(provider);
|
||||||
|
|
||||||
CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img);
|
CGContextDrawImage(context, CGRectMake(0, 0, width * scale, height * scale), img);
|
||||||
|
|
||||||
CGImageRelease(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