Merge branch 'wip'

# Conflicts:
#	README.md
This commit is contained in:
Daniel Collin 2016-01-02 21:46:28 +01:00
commit 894da55768
14 changed files with 1432 additions and 139 deletions

View file

@ -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"

View file

@ -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)

View file

@ -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
} }
} }

View file

@ -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) {
Ok(win) => win,
Err(err) => {
println!("Unable to create window {}", err);
return; 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
View 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);
}
}

View file

@ -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)
} }
} }

View file

@ -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]; [NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
s_init = true;
}
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)
{
switch ([event type])
{
case NSKeyDown:
case NSKeyUp:
{
state = -1;
break;
}
default :
{
[NSApp sendEvent:event]; [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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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
View 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());
}
}
}
}