From c8b45d85776505eef39cd544845e0bd51fecba50 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 18:49:03 +0100 Subject: [PATCH 01/22] WIP on Windows version --- src/lib.rs | 218 ++++++++++++++++++++++++++++++++----------------- src/windows.rs | 159 ++++++++++++++++++++++++++++++++---- 2 files changed, 290 insertions(+), 87 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3aed2ca..1d322d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,80 +1,152 @@ extern crate libc; +#[cfg(target_os = "macos")] +#[macro_use] +extern crate objc; +#[cfg(target_os = "macos")] +extern crate cgl; +#[cfg(target_os = "macos")] +extern crate cocoa; +#[cfg(target_os = "macos")] +extern crate core_foundation; +#[cfg(target_os = "macos")] + +/// 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. +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, +} + +/// Vsync will allow syncronized rendering with the screen refresh rate. +/// Currently Vsync isn't implemented so nothing will change regardless of given value right now +pub enum Vsync { + /// No vsync + No, + /// Require accurate vsync. Notice that if the library is unable to to setup an accurate + /// syncing the window creation will fail. + Accurate, + /// Setup a best guess syncing with the screen. This will always succesed but may not be + /// accurate. What this means is if the lib is unable to create a accurate syncing approach + /// a 'emulated' one will be used (for example using a timer to approximate syncing) + BestGuess, +} + +/// +pub enum Key { + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + Key0, + + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + + Down, + Left, + Right, + Up, + Apostrophe, + + Backslash, + Comma, + Equal, + LeftBracket, + Minus, + Period, + RightBracket, + Semicolon, + + Slash, + Backspace, + Delete, + End, + Enter, + + Escape, + + Home, + Insert, + Menu, + + PageDown, + PageUp, + + Pause, + Space, + Tab, + CapsLock, + + Count = 80, +} + #[cfg(target_os = "windows")] pub mod windows; - +#[cfg(target_os = "windows")] pub use windows::*; -/* - #[cfg(target_os = "macos")] -#[link(name = "Cocoa", kind = "framework")] -extern { - fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int; - fn mfb_update(buffer: *mut c_void) -> c_int; - fn mfb_close(); -} +pub mod macos; +#[cfg(target_os = "macos")] +pub use macos::*; -/* -#[cfg(target_os = "windows")] -#[link(name = "gdi32")] -extern { - fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int; - fn mfb_update(buffer: *mut c_void) -> c_int; - fn mfb_close(); -} -*/ - -#[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 -/// -#[cfg(any(target_os = "linux", target_os = "mac"))] -pub fn open(name: &str, width: usize, height: usize) -> bool { - let s = CString::new(name).unwrap(); - let ret; - - unsafe { - ret = mfb_open(s.as_ptr(), width as c_int, height as c_int); - } - - match ret { - 0 => false, - _ => true, - } -} - -/// -/// Update -/// -#[cfg(any(target_os = "linux", target_os = "mac"))] -pub fn update(buffer: &[u32]) -> bool { - let ret; - unsafe { - ret = mfb_update(transmute(buffer.as_ptr())); - } - - if ret < 0 { - return false; - } else { - return true; - } -} - -/// -/// Close -/// -#[cfg(any(target_os = "linux", target_os = "mac"))] -pub fn close() { - unsafe { - mfb_close(); - } -} - -*/ diff --git a/src/windows.rs b/src/windows.rs index d4e5e1c..d16ee8b 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,8 +1,14 @@ +#![cfg(target_os = "windows")] + extern crate user32; extern crate kernel32; extern crate winapi; extern crate gdi32; +use Scale; +use Vsync; +use Key; + use std::ffi::CString; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -24,21 +30,120 @@ struct BitmapInfo { pub bmi_colors: [RGBQUAD; 3], } +fn update_key_state(window: &mut Window, wparam: u32, bool state) { + match wparam & 0x1ff { + 0x00B => window.keys[Key::Key0] = state, + 0x00B => window.keys[Key::Key0] = state, + 0x002 => window.keys[Key::Key1] = state, + 0x003 => window.keys[Key::Key2] = state, + 0x004 => window.keys[Key::Key3] = state, + 0x005 => window.keys[Key::Key4] = state, + 0x006 => window.keys[Key::Key5] = state, + 0x007 => window.keys[Key::Key6] = state, + 0x008 => window.keys[Key::Key7] = state, + 0x009 => window.keys[Key::Key8] = state, + 0x00A => window.keys[Key::Key9] = state, + 0x01E => window.keys[Key::A] = state, + 0x030 => window.keys[Key::B] = state, + 0x02E => window.keys[Key::C] = state, + 0x020 => window.keys[Key::D] = state, + 0x012 => window.keys[Key::E] = state, + 0x021 => window.keys[Key::F] = state, + 0x022 => window.keys[Key::G] = state, + 0x023 => window.keys[Key::H] = state, + 0x017 => window.keys[Key::I] = state, + 0x024 => window.keys[Key::J] = state, + 0x025 => window.keys[Key::K] = state, + 0x026 => window.keys[Key::L] = state, + 0x032 => window.keys[Key::M] = state, + 0x031 => window.keys[Key::N] = state, + 0x018 => window.keys[Key::O] = state, + 0x019 => window.keys[Key::P] = state, + 0x010 => window.keys[Key::Q] = state, + 0x013 => window.keys[Key::R] = state, + 0x01F => window.keys[Key::S] = state, + 0x014 => window.keys[Key::T] = state, + 0x016 => window.keys[Key::U] = state, + 0x02F => window.keys[Key::V] = state, + 0x011 => window.keys[Key::W] = state, + 0x02D => window.keys[Key::X] = state, + 0x015 => window.keys[Key::Y] = state, + 0x02C => window.keys[Key::Z] = state, + 0x03B => window.keys[Key::F1] = state, + 0x03C => window.keys[Key::F2] = state, + 0x03D => window.keys[Key::F3] = state, + 0x03E => window.keys[Key::F4] = state, + 0x03F => window.keys[Key::F5] = state, + 0x040 => window.keys[Key::F6] = state, + 0x041 => window.keys[Key::F7] = state, + 0x042 => window.keys[Key::F8] = state, + 0x043 => window.keys[Key::F9] = state, + 0x042 => window.keys[Key::F8] = state, + 0x043 => window.keys[Key::F9] = state, + 0x044 => window.keys[Key::F10] = state, + 0x057 => window.keys[Key::F11] = state, + 0x058 => window.keys[Key::F12] = state, + 0x150 => window.keys[Key::Down] = state, + 0x14B => window.keys[Key::Left] = state, + 0x14D => window.keys[Key::Right] = state, + 0x148 => window.keys[Key::Up] = state, + 0x028 => window.keys[Key::Apostrophe] = state, + 0x02B => window.keys[Key::Backslash] = state, + 0x033 => window.keys[Key::Comma] = state, + 0x00D => window.keys[Key::Equal] = state, + 0x01A => window.keys[Key::LeftBracket] = state, + 0x00C => window.keys[Key::Minus] = state, + 0x034 => window.keys[Key::Period] = state, + 0x01B => window.keys[Key::RightBracket] = state, + 0x027 => window.keys[Key::Semicolon] = state, + 0x035 => window.keys[Key::Slash] = state, + 0x00E => window.keys[Key::Backspace] = state, + 0x153 => window.keys[Key::Delete] = state, + 0x14F => window.keys[Key::End] = state, + 0x01C => window.keys[Key::Enter] = state, + 0x001 => window.keys[Key::Escape] = state, + 0x147 => window.keys[Key::Home] = state, + 0x152 => window.keys[Key::Insert] = state, + 0x15D => window.keys[Key::Menu] = state, + 0x151 => window.keys[Key::PageDown] = state, + 0x149 => window.keys[Key::PageUp] = state, + 0x045 => window.keys[Key::Pause] = state, + 0x039 => window.keys[Key::Space] = state, + 0x00F => window.keys[Key::Tab] = state, + 0x03A => window.keys[Key::CapsLock] = 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 self = user_data as &mut Window; + match msg { winapi::winuser::WM_KEYDOWN => { - if (wparam & 0xff) == 27 { + update_key_state(self, wparam as u32, true); + if (wparam & 0x1ff) == 27 { CLOSE_APP = true; } } + winapi::winuser::WM_KEYUP => { + update_key_state(self, wparam as u32, false); + } + winapi::winuser::WM_PAINT => { let mut rect: winapi::RECT = mem::uninitialized(); - let buffer = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA); user32::GetClientRect(window, &mut rect); @@ -56,9 +161,7 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, bitmap_info.bmi_colors[1].rgbGreen = 0xff; bitmap_info.bmi_colors[2].rgbBlue = 0xff; - let dc = user32::GetDC(window); - - gdi32::StretchDIBits(dc, + gdi32::StretchDIBits(self.dc, 0, 0, width, @@ -67,14 +170,12 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, 0, width, height, - mem::transmute(buffer), + mem::transmute(self.buffer), mem::transmute(&bitmap_info), winapi::wingdi::DIB_RGB_COLORS, winapi::wingdi::SRCCOPY); user32::ValidateRect(window, ptr::null_mut()); - - user32::ReleaseDC(window, dc); } _ => (), @@ -92,12 +193,15 @@ fn to_wstring(str: &str) -> *const u16 { v.as_ptr() } -pub struct Minifb { +pub struct Window { + dc: DC, window: HWND, + bool keys_down: [bool; 512], + buffer: &[u32], } -impl Minifb { - fn open_window(name: &str, width: usize, height: usize) -> HWND { +impl Window { + fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { unsafe { let class_name = to_wstring("minifb_window"); let s = CString::new(name).unwrap(); @@ -163,13 +267,26 @@ impl Minifb { } } + pub fn get_keys(&self -> Vec { + let index = 0; + let mut keys: Vec = Vec::new(); + + for i in self.keys { + if *i { + keys.push(index as Key); + } + + index += 1; + } + + keys + } + pub fn update(&mut self, buffer: &[u32]) -> bool { unsafe { let mut msg = mem::uninitialized(); - user32::SetWindowLongPtrW(self.window, - winapi::winuser::GWLP_USERDATA, - buffer.as_ptr() as i64); + user32::SetWindowLongPtrW(self.window, winapi::winuser::GWLP_USERDATA, self as i64); user32::InvalidateRect(self.window, ptr::null_mut(), winapi::TRUE); while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != 0 { @@ -183,3 +300,17 @@ impl Minifb { } } } + +impl Drop for Window { + fn drop(&mut self) { + unsafe { + if self.dc.is_valid() { + user32::ReleaseDC(self.window, self.dc); + } + + if self.hwnd.is_valid() { + user32::CloseWindow(self.hwnd); + } + } + } +} From 6bb0f1f47954f1449f24de8d3fbc9d042fed31e8 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 18:53:26 +0100 Subject: [PATCH 02/22] Removed double Key0 --- src/windows.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/windows.rs b/src/windows.rs index d16ee8b..10c8cd6 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -32,7 +32,6 @@ struct BitmapInfo { fn update_key_state(window: &mut Window, wparam: u32, bool state) { match wparam & 0x1ff { - 0x00B => window.keys[Key::Key0] = state, 0x00B => window.keys[Key::Key0] = state, 0x002 => window.keys[Key::Key1] = state, 0x003 => window.keys[Key::Key2] = state, From 0b2911ca302020a6e365eec60fa0494f56ef70e1 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 22:57:14 +0100 Subject: [PATCH 03/22] New version running with keyboard support --- examples/noise.rs | 12 ++- src/lib.rs | 82 ++++++++-------- src/windows.rs | 233 +++++++++++++++++++++++++--------------------- 3 files changed, 177 insertions(+), 150 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index 28a3155..523a02a 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -12,9 +12,9 @@ fn main() { let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; - let mut mfb = Minifb::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT).unwrap(); + let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X1, Vsync::No).unwrap(); - while mfb.update(&buffer) { + while window.update(&buffer) { for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -26,5 +26,13 @@ fn main() { noise &= 0xFF; *i = (noise << 16) | (noise << 8) | noise; } + + for key in window.get_keys().iter() { + match *key { + Key::A => println!("Pressed A"), + Key::B => println!("Pressed B"), + _ => (), + } + } } } diff --git a/src/lib.rs b/src/lib.rs index 1d322d4..cab0b79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ extern crate cgl; extern crate cocoa; #[cfg(target_os = "macos")] extern crate core_foundation; -#[cfg(target_os = "macos")] /// 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 @@ -46,45 +45,44 @@ pub enum Vsync { BestGuess, } -/// pub enum Key { - Key1, - Key2, - Key3, - Key4, - Key5, - Key6, - Key7, - Key8, - Key9, - Key0, + Key0 = 0, + Key1 = 1, + Key2 = 2, + Key3 = 3, + Key4 = 4, + Key5 = 5, + Key6 = 6, + Key7 = 7, + Key8 = 8, + Key9 = 9, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, + 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, @@ -145,8 +143,8 @@ pub mod windows; #[cfg(target_os = "windows")] pub use windows::*; -#[cfg(target_os = "macos")] -pub mod macos; -#[cfg(target_os = "macos")] -pub use macos::*; +//#[cfg(target_os = "macos")] +//pub mod macos; +//#[cfg(target_os = "macos")] +//pub use macos::*; diff --git a/src/windows.rs b/src/windows.rs index 10c8cd6..6df07a2 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -9,6 +9,8 @@ use Scale; use Vsync; use Key; +//use Keys; + use std::ffi::CString; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -16,6 +18,7 @@ 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; @@ -30,86 +33,85 @@ struct BitmapInfo { pub bmi_colors: [RGBQUAD; 3], } -fn update_key_state(window: &mut Window, wparam: u32, bool state) { +fn update_key_state(window: &mut Window, wparam: u32, state: bool) { match wparam & 0x1ff { - 0x00B => window.keys[Key::Key0] = state, - 0x002 => window.keys[Key::Key1] = state, - 0x003 => window.keys[Key::Key2] = state, - 0x004 => window.keys[Key::Key3] = state, - 0x005 => window.keys[Key::Key4] = state, - 0x006 => window.keys[Key::Key5] = state, - 0x007 => window.keys[Key::Key6] = state, - 0x008 => window.keys[Key::Key7] = state, - 0x009 => window.keys[Key::Key8] = state, - 0x00A => window.keys[Key::Key9] = state, - 0x01E => window.keys[Key::A] = state, - 0x030 => window.keys[Key::B] = state, - 0x02E => window.keys[Key::C] = state, - 0x020 => window.keys[Key::D] = state, - 0x012 => window.keys[Key::E] = state, - 0x021 => window.keys[Key::F] = state, - 0x022 => window.keys[Key::G] = state, - 0x023 => window.keys[Key::H] = state, - 0x017 => window.keys[Key::I] = state, - 0x024 => window.keys[Key::J] = state, - 0x025 => window.keys[Key::K] = state, - 0x026 => window.keys[Key::L] = state, - 0x032 => window.keys[Key::M] = state, - 0x031 => window.keys[Key::N] = state, - 0x018 => window.keys[Key::O] = state, - 0x019 => window.keys[Key::P] = state, - 0x010 => window.keys[Key::Q] = state, - 0x013 => window.keys[Key::R] = state, - 0x01F => window.keys[Key::S] = state, - 0x014 => window.keys[Key::T] = state, - 0x016 => window.keys[Key::U] = state, - 0x02F => window.keys[Key::V] = state, - 0x011 => window.keys[Key::W] = state, - 0x02D => window.keys[Key::X] = state, - 0x015 => window.keys[Key::Y] = state, - 0x02C => window.keys[Key::Z] = state, - 0x03B => window.keys[Key::F1] = state, - 0x03C => window.keys[Key::F2] = state, - 0x03D => window.keys[Key::F3] = state, - 0x03E => window.keys[Key::F4] = state, - 0x03F => window.keys[Key::F5] = state, - 0x040 => window.keys[Key::F6] = state, - 0x041 => window.keys[Key::F7] = state, - 0x042 => window.keys[Key::F8] = state, - 0x043 => window.keys[Key::F9] = state, - 0x042 => window.keys[Key::F8] = state, - 0x043 => window.keys[Key::F9] = state, - 0x044 => window.keys[Key::F10] = state, - 0x057 => window.keys[Key::F11] = state, - 0x058 => window.keys[Key::F12] = state, - 0x150 => window.keys[Key::Down] = state, - 0x14B => window.keys[Key::Left] = state, - 0x14D => window.keys[Key::Right] = state, - 0x148 => window.keys[Key::Up] = state, - 0x028 => window.keys[Key::Apostrophe] = state, - 0x02B => window.keys[Key::Backslash] = state, - 0x033 => window.keys[Key::Comma] = state, - 0x00D => window.keys[Key::Equal] = state, - 0x01A => window.keys[Key::LeftBracket] = state, - 0x00C => window.keys[Key::Minus] = state, - 0x034 => window.keys[Key::Period] = state, - 0x01B => window.keys[Key::RightBracket] = state, - 0x027 => window.keys[Key::Semicolon] = state, - 0x035 => window.keys[Key::Slash] = state, - 0x00E => window.keys[Key::Backspace] = state, - 0x153 => window.keys[Key::Delete] = state, - 0x14F => window.keys[Key::End] = state, - 0x01C => window.keys[Key::Enter] = state, - 0x001 => window.keys[Key::Escape] = state, - 0x147 => window.keys[Key::Home] = state, - 0x152 => window.keys[Key::Insert] = state, - 0x15D => window.keys[Key::Menu] = state, - 0x151 => window.keys[Key::PageDown] = state, - 0x149 => window.keys[Key::PageUp] = state, - 0x045 => window.keys[Key::Pause] = state, - 0x039 => window.keys[Key::Space] = state, - 0x00F => window.keys[Key::Tab] = state, - 0x03A => window.keys[Key::CapsLock] = state, + 0x00B => window.keys[Key::Key0 as usize] = state, + 0x002 => window.keys[Key::Key1 as usize] = state, + 0x003 => window.keys[Key::Key2 as usize] = state, + 0x004 => window.keys[Key::Key3 as usize] = state, + 0x005 => window.keys[Key::Key4 as usize] = state, + 0x006 => window.keys[Key::Key5 as usize] = state, + 0x007 => window.keys[Key::Key6 as usize] = state, + 0x008 => window.keys[Key::Key7 as usize] = state, + 0x009 => window.keys[Key::Key8 as usize] = state, + 0x00A => window.keys[Key::Key9 as usize] = state, + 0x01E => window.keys[Key::A as usize] = state, + 0x030 => window.keys[Key::B as usize] = state, + 0x02E => window.keys[Key::C as usize] = state, + 0x020 => window.keys[Key::D as usize] = state, + 0x012 => window.keys[Key::E as usize] = state, + 0x021 => window.keys[Key::F as usize] = state, + 0x022 => window.keys[Key::G as usize] = state, + 0x023 => window.keys[Key::H as usize] = state, + 0x017 => window.keys[Key::I as usize] = state, + 0x024 => window.keys[Key::J as usize] = state, + 0x025 => window.keys[Key::K as usize] = state, + 0x026 => window.keys[Key::L as usize] = state, + 0x032 => window.keys[Key::M as usize] = state, + 0x031 => window.keys[Key::N as usize] = state, + 0x018 => window.keys[Key::O as usize] = state, + 0x019 => window.keys[Key::P as usize] = state, + 0x010 => window.keys[Key::Q as usize] = state, + 0x013 => window.keys[Key::R as usize] = state, + 0x01F => window.keys[Key::S as usize] = state, + 0x014 => window.keys[Key::T as usize] = state, + 0x016 => window.keys[Key::U as usize] = state, + 0x02F => window.keys[Key::V as usize] = state, + 0x011 => window.keys[Key::W as usize] = state, + 0x02D => window.keys[Key::X as usize] = state, + 0x015 => window.keys[Key::Y as usize] = state, + 0x02C => window.keys[Key::Z as usize] = state, + 0x03B => window.keys[Key::F1 as usize] = state, + 0x03C => window.keys[Key::F2 as usize] = state, + 0x03D => window.keys[Key::F3 as usize] = state, + 0x03E => window.keys[Key::F4 as usize] = state, + 0x03F => window.keys[Key::F5 as usize] = state, + 0x040 => window.keys[Key::F6 as usize] = state, + 0x041 => window.keys[Key::F7 as usize] = state, + 0x042 => window.keys[Key::F8 as usize] = state, + 0x043 => window.keys[Key::F9 as usize] = state, + 0x044 => window.keys[Key::F10 as usize] = state, + 0x057 => window.keys[Key::F11 as usize] = state, + 0x058 => window.keys[Key::F12 as usize] = state, + 0x150 => window.keys[Key::Down as usize] = state, + 0x14B => window.keys[Key::Left as usize] = state, + 0x14D => window.keys[Key::Right as usize] = state, + 0x148 => window.keys[Key::Up as usize] = state, + 0x028 => window.keys[Key::Apostrophe as usize] = state, + 0x02B => window.keys[Key::Backslash as usize] = state, + 0x033 => window.keys[Key::Comma as usize] = state, + 0x00D => window.keys[Key::Equal as usize] = state, + 0x01A => window.keys[Key::LeftBracket as usize] = state, + 0x00C => window.keys[Key::Minus as usize] = state, + 0x034 => window.keys[Key::Period as usize] = state, + 0x01B => window.keys[Key::RightBracket as usize] = state, + 0x027 => window.keys[Key::Semicolon as usize] = state, + 0x035 => window.keys[Key::Slash as usize] = state, + 0x00E => window.keys[Key::Backspace as usize] = state, + 0x153 => window.keys[Key::Delete as usize] = state, + 0x14F => window.keys[Key::End as usize] = state, + 0x01C => window.keys[Key::Enter as usize] = state, + 0x001 => window.keys[Key::Escape as usize] = state, + 0x147 => window.keys[Key::Home as usize] = state, + 0x152 => window.keys[Key::Insert as usize] = state, + 0x15D => window.keys[Key::Menu as usize] = state, + 0x151 => window.keys[Key::PageDown as usize] = state, + 0x149 => window.keys[Key::PageUp as usize] = state, + 0x045 => window.keys[Key::Pause as usize] = state, + 0x039 => window.keys[Key::Space as usize] = state, + 0x00F => window.keys[Key::Tab as usize] = state, + 0x03A => window.keys[Key::CapsLock as usize] = state, + _ => (), } } @@ -124,21 +126,22 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, let user_data = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA); - if user_data == 0 + if user_data == 0 { return user32::DefWindowProcW(window, msg, wparam, lparam); + } - let mut self = user_data as &mut Window; + let mut wnd: &mut Window = mem::transmute(user_data); match msg { winapi::winuser::WM_KEYDOWN => { - update_key_state(self, wparam as u32, true); + update_key_state(wnd, (lparam as u32) >> 16, true); if (wparam & 0x1ff) == 27 { CLOSE_APP = true; } } winapi::winuser::WM_KEYUP => { - update_key_state(self, wparam as u32, false); + update_key_state(wnd, (lparam as u32) >> 16, false); } winapi::winuser::WM_PAINT => { @@ -160,7 +163,7 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, bitmap_info.bmi_colors[1].rgbGreen = 0xff; bitmap_info.bmi_colors[2].rgbBlue = 0xff; - gdi32::StretchDIBits(self.dc, + gdi32::StretchDIBits(wnd.dc.unwrap(), 0, 0, width, @@ -169,7 +172,7 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, 0, width, height, - mem::transmute(self.buffer), + mem::transmute(wnd.buffer.as_ptr()), mem::transmute(&bitmap_info), winapi::wingdi::DIB_RGB_COLORS, winapi::wingdi::SRCCOPY); @@ -193,14 +196,14 @@ fn to_wstring(str: &str) -> *const u16 { } pub struct Window { - dc: DC, - window: HWND, - bool keys_down: [bool; 512], - buffer: &[u32], + dc: Option, + window: Option, + keys: [bool; 512], + buffer: Vec, } impl Window { - fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { + fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> HWND { unsafe { let class_name = to_wstring("minifb_window"); let s = CString::new(name).unwrap(); @@ -253,26 +256,39 @@ impl Window { user32::ShowWindow(handle, winapi::SW_NORMAL); } + return handle; } } - pub fn new(name: &str, width: usize, height: usize) -> Option { - let handle = Minifb::open_window(name, width, height); + pub fn new(name: &str, width: usize, height: usize, scale: Scale, vsync: Vsync) -> Result { + unsafe { + let handle = Self::open_window(name, width, height, scale, vsync); - match handle.is_null() { - true => None, - false => Some(Minifb { window: handle }), + if handle.is_null() { + return Err("Unable to create Window"); + } + + let window = Window { + dc: Some(user32::GetDC(handle)), + window: Some(handle), + keys: [false; 512], + buffer: Vec::new(), + }; + + Ok(window) } } - pub fn get_keys(&self -> Vec { - let index = 0; + pub fn get_keys(&self) -> Vec { + let mut index: u8 = 0; let mut keys: Vec = Vec::new(); - for i in self.keys { + for i in self.keys.iter() { if *i { - keys.push(index as Key); + unsafe { + keys.push(mem::transmute(index)); + } } index += 1; @@ -284,11 +300,16 @@ impl Window { pub fn update(&mut self, buffer: &[u32]) -> bool { unsafe { let mut msg = mem::uninitialized(); + let window = self.window.unwrap(); - user32::SetWindowLongPtrW(self.window, winapi::winuser::GWLP_USERDATA, self as i64); - user32::InvalidateRect(self.window, ptr::null_mut(), winapi::TRUE); + // TODO: Optimize - while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != 0 { + 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); } @@ -303,12 +324,12 @@ impl Window { impl Drop for Window { fn drop(&mut self) { unsafe { - if self.dc.is_valid() { - user32::ReleaseDC(self.window, self.dc); + if self.dc.is_some() { + user32::ReleaseDC(self.window.unwrap(), self.dc.unwrap()); } - if self.hwnd.is_valid() { - user32::CloseWindow(self.hwnd); + if self.window.is_some() { + user32::CloseWindow(self.window.unwrap()); } } } From e89cb041899214df31bc3f5e32cfe90948f80551 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 23:08:52 +0100 Subject: [PATCH 04/22] Rust format --- examples/noise.rs | 7 ++++++- src/windows.rs | 11 +++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index 523a02a..c860419 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -12,7 +12,12 @@ fn main() { let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; - let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X1, Vsync::No).unwrap(); + let mut window = Window::new("Noise Test - Press ESC to exit", + WIDTH, + HEIGHT, + Scale::X1, + Vsync::No) + .unwrap(); while window.update(&buffer) { for i in buffer.iter_mut() { diff --git a/src/windows.rs b/src/windows.rs index 6df07a2..c97ee1e 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -9,8 +9,6 @@ use Scale; use Vsync; use Key; -//use Keys; - use std::ffi::CString; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -261,7 +259,12 @@ impl Window { } } - pub fn new(name: &str, width: usize, height: usize, scale: Scale, vsync: Vsync) -> Result { + pub fn new(name: &str, + width: usize, + height: usize, + scale: Scale, + vsync: Vsync) + -> Result { unsafe { let handle = Self::open_window(name, width, height, scale, vsync); @@ -270,7 +273,7 @@ impl Window { } let window = Window { - dc: Some(user32::GetDC(handle)), + dc: Some(user32::GetDC(handle)), window: Some(handle), keys: [false; 512], buffer: Vec::new(), From b46868121ed12560bc7b725ee8b7c4c141c64b41 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Wed, 9 Dec 2015 13:04:33 +0100 Subject: [PATCH 05/22] Fixed overflow on windows --- src/windows.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index c97ee1e..b14f01e 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -284,13 +284,13 @@ impl Window { } pub fn get_keys(&self) -> Vec { - let mut index: u8 = 0; + let mut index: u16 = 0; let mut keys: Vec = Vec::new(); for i in self.keys.iter() { if *i { unsafe { - keys.push(mem::transmute(index)); + keys.push(mem::transmute(index as u8)); } } From d8c2a38f3956990cdc8fcb83ebb6b743e9d611e2 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Wed, 9 Dec 2015 15:44:34 +0100 Subject: [PATCH 06/22] Changed keys api and added check if window is open Also removed ugly CLOSE_APP global --- examples/noise.rs | 11 ++--------- src/windows.rs | 40 +++++++++++++++------------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index c860419..7142140 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -19,7 +19,7 @@ fn main() { Vsync::No) .unwrap(); - while window.update(&buffer) { + while window.is_open() && !window.is_key_down(Key::Escape) { for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -31,13 +31,6 @@ fn main() { noise &= 0xFF; *i = (noise << 16) | (noise << 8) | noise; } - - for key in window.get_keys().iter() { - match *key { - Key::A => println!("Pressed A"), - Key::B => println!("Pressed B"), - _ => (), - } - } + window.update(&buffer); } } diff --git a/src/windows.rs b/src/windows.rs index b14f01e..df593b5 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -22,8 +22,6 @@ use self::winapi::winuser::WNDCLASSW; use self::winapi::wingdi::BITMAPINFOHEADER; use self::winapi::wingdi::RGBQUAD; -static mut CLOSE_APP: bool = false; - // Wrap this so we can have a proper numbef of bmiColors to write in #[repr(C)] struct BitmapInfo { @@ -133,9 +131,10 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, match msg { winapi::winuser::WM_KEYDOWN => { update_key_state(wnd, (lparam as u32) >> 16, true); - if (wparam & 0x1ff) == 27 { - CLOSE_APP = true; - } + } + + winapi::winuser::WM_CLOSE => { + wnd.is_open = false; } winapi::winuser::WM_KEYUP => { @@ -198,6 +197,7 @@ pub struct Window { window: Option, keys: [bool; 512], buffer: Vec, + is_open : bool, } impl Window { @@ -277,30 +277,24 @@ impl Window { window: Some(handle), keys: [false; 512], buffer: Vec::new(), + is_open: true, }; Ok(window) } } - pub fn get_keys(&self) -> Vec { - let mut index: u16 = 0; - let mut keys: Vec = Vec::new(); - - for i in self.keys.iter() { - if *i { - unsafe { - keys.push(mem::transmute(index as u8)); - } - } - - index += 1; - } - - keys + #[inline] + pub fn is_key_down(&self, key: Key) -> bool { + return self.keys[key as usize]; } - pub fn update(&mut self, buffer: &[u32]) -> bool { + #[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(); @@ -317,10 +311,6 @@ impl Window { user32::DispatchMessageW(&mut msg); } } - - unsafe { - return !CLOSE_APP; - } } } From 58565b9812b5faa38bc497c434af13039d732e12 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Thu, 10 Dec 2015 21:27:18 +0100 Subject: [PATCH 07/22] Added more error checking when creating window --- src/windows.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index df593b5..448e9d5 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -201,7 +201,7 @@ pub struct Window { } impl Window { - fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> HWND { + fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Option { unsafe { let class_name = to_wstring("minifb_window"); let s = CString::new(name).unwrap(); @@ -219,7 +219,10 @@ impl Window { lpszClassName: class_name, }; - user32::RegisterClassW(&class); + if user32::RegisterClassW(&class) == 0 { + println!("Unable to register class, error {}", kernel32::GetLastError() as u32); + return None; + } let mut rect = winapi::RECT { left: 0, @@ -249,13 +252,14 @@ impl Window { ptr::null_mut(), ptr::null_mut(), ptr::null_mut()); - - if !handle.is_null() { - user32::ShowWindow(handle, winapi::SW_NORMAL); + if handle.is_null() { + println!("Unable to register create window, error {}", kernel32::GetLastError() as u32); + return None; } + user32::ShowWindow(handle, winapi::SW_NORMAL); - return handle; + return Some(handle); } } @@ -268,13 +272,13 @@ impl Window { unsafe { let handle = Self::open_window(name, width, height, scale, vsync); - if handle.is_null() { + if handle.is_none() { return Err("Unable to create Window"); } let window = Window { - dc: Some(user32::GetDC(handle)), - window: Some(handle), + dc: Some(user32::GetDC(handle.unwrap())), + window: Some(handle.unwrap()), keys: [false; 512], buffer: Vec::new(), is_open: true, From b05fb33fb32e05d3f17c3e29c02a43c8c45d26df Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 08:52:38 +0100 Subject: [PATCH 08/22] Use CreateWindowExW instead of A --- src/windows.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 448e9d5..d501d66 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -9,7 +9,6 @@ use Scale; use Vsync; use Key; -use std::ffi::CString; use std::ptr; use std::os::windows::ffi::OsStrExt; use std::ffi::OsStr; @@ -204,7 +203,6 @@ impl Window { fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Option { unsafe { let class_name = to_wstring("minifb_window"); - let s = CString::new(name).unwrap(); let class = WNDCLASSW { style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC, @@ -238,9 +236,9 @@ impl Window { rect.right -= rect.left; rect.bottom -= rect.top; - let handle = user32::CreateWindowExA(0, - "minifb_window".as_ptr() as *mut _, - s.as_ptr(), + let handle = user32::CreateWindowExW(0, + class_name, + to_wstring(name), winapi::WS_OVERLAPPEDWINDOW & !winapi::WS_MAXIMIZEBOX & !winapi::WS_THICKFRAME, @@ -253,7 +251,7 @@ impl Window { ptr::null_mut(), ptr::null_mut()); if handle.is_null() { - println!("Unable to register create window, error {}", kernel32::GetLastError() as u32); + println!("Unable to create window, error {}", kernel32::GetLastError() as u32); return None; } From 474e6db31dcde546041856560c03e15e419fbec3 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 09:25:06 +0100 Subject: [PATCH 09/22] Null terminate wchar string --- src/windows.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index d501d66..c48ae7f 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -187,7 +187,8 @@ pub enum MinifbError { } fn to_wstring(str: &str) -> *const u16 { - let v: Vec = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect(); + let mut v: Vec = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect(); + v.push(0u16); v.as_ptr() } @@ -203,7 +204,6 @@ impl Window { fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Option { 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), From 2f5503d115da5b42b6abf42939f49385b7e24d03 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 09:46:10 +0100 Subject: [PATCH 10/22] Make sure to keep wchars alive --- src/windows.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index c48ae7f..3b16d72 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -186,10 +186,10 @@ pub enum MinifbError { UnableToCreateWindow, } -fn to_wstring(str: &str) -> *const u16 { +fn to_wstring(str: &str) -> Vec { let mut v: Vec = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect(); v.push(0u16); - v.as_ptr() + v } pub struct Window { @@ -214,7 +214,7 @@ impl Window { hCursor: ptr::null_mut(), hbrBackground: ptr::null_mut(), lpszMenuName: ptr::null(), - lpszClassName: class_name, + lpszClassName: class_name.as_ptr(), }; if user32::RegisterClassW(&class) == 0 { @@ -236,9 +236,11 @@ impl Window { rect.right -= rect.left; rect.bottom -= rect.top; + let window_name = to_wstring(name); + let handle = user32::CreateWindowExW(0, - class_name, - to_wstring(name), + class_name.as_ptr(), + window_name.as_ptr(), winapi::WS_OVERLAPPEDWINDOW & !winapi::WS_MAXIMIZEBOX & !winapi::WS_THICKFRAME, From 6f47463a2e27c4332d4acec3b0dd59c2569d1129 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 10:33:20 +0100 Subject: [PATCH 11/22] Implemented scaling BestFit isn't implemented yet though --- src/lib.rs | 1 + src/windows.rs | 31 ++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cab0b79..a6548e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ extern crate core_foundation; /// 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 diff --git a/src/windows.rs b/src/windows.rs index 3b16d72..7ffe293 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -162,8 +162,8 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, gdi32::StretchDIBits(wnd.dc.unwrap(), 0, 0, - width, - height, + width * wnd.scale_factor, + height * wnd.scale_factor, 0, 0, width, @@ -198,10 +198,11 @@ pub struct Window { keys: [bool; 512], buffer: Vec, is_open : bool, + scale_factor: i32, } impl Window { - fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Option { + fn open_window(name: &str, width: usize, height: usize, scale: Scale, _: Vsync) -> Option { unsafe { let class_name = to_wstring("minifb_window"); let class = WNDCLASSW { @@ -222,11 +223,15 @@ impl Window { return None; } + let scale_factor = Self::get_scale_factor(scale); + let new_width = width * scale_factor as usize; + let new_height = height * scale_factor as usize; + let mut rect = winapi::RECT { left: 0, - right: width as winapi::LONG, + right: new_width as winapi::LONG, top: 0, - bottom: height as winapi::LONG, + bottom: new_height as winapi::LONG, }; user32::AdjustWindowRect(&mut rect, @@ -282,6 +287,7 @@ impl Window { keys: [false; 512], buffer: Vec::new(), is_open: true, + scale_factor: Self::get_scale_factor(scale), }; Ok(window) @@ -316,6 +322,21 @@ impl Window { } } } + + fn get_scale_factor(scale: Scale) -> i32 { + // TODO: Implement best fit + let factor: i32 = match scale { + Scale::X1 => 1, + Scale::X2 => 2, + Scale::X4 => 4, + Scale::X8 => 8, + Scale::X16 => 16, + Scale::X32 => 32, + _ => 1, + }; + + return factor; + } } impl Drop for Window { From ef9ac3e88cb4f296a7c6db750dee015721948af8 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 11:26:00 +0100 Subject: [PATCH 12/22] Fixed broken scaling --- src/windows.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 7ffe293..f59bb02 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -130,6 +130,7 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, match msg { winapi::winuser::WM_KEYDOWN => { update_key_state(wnd, (lparam as u32) >> 16, true); + return 0; } winapi::winuser::WM_CLOSE => { @@ -138,23 +139,18 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, winapi::winuser::WM_KEYUP => { update_key_state(wnd, (lparam as u32) >> 16, false); + return 0; } winapi::winuser::WM_PAINT => { - let mut rect: winapi::RECT = mem::uninitialized(); - - user32::GetClientRect(window, &mut rect); - let mut bitmap_info: BitmapInfo = mem::zeroed(); - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; bitmap_info.bmi_header.biSize = mem::size_of::() 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 = width; - bitmap_info.bmi_header.biHeight = -height; + 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; @@ -162,18 +158,20 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, gdi32::StretchDIBits(wnd.dc.unwrap(), 0, 0, - width * wnd.scale_factor, - height * wnd.scale_factor, + wnd.width * wnd.scale_factor, + wnd.height * wnd.scale_factor, 0, 0, - width, - height, + 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; } _ => (), @@ -199,6 +197,8 @@ pub struct Window { buffer: Vec, is_open : bool, scale_factor: i32, + width: i32, + height: i32, } impl Window { @@ -288,6 +288,8 @@ impl Window { buffer: Vec::new(), is_open: true, scale_factor: Self::get_scale_factor(scale), + width: width as i32, + height: height as i32, }; Ok(window) From 03e8075157abb127bb58ad98c7371442ea62dca7 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 11 Dec 2015 14:12:29 +0100 Subject: [PATCH 13/22] Implemented FitScreen --- src/windows.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index f59bb02..8800c96 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -202,7 +202,7 @@ pub struct Window { } impl Window { - fn open_window(name: &str, width: usize, height: usize, scale: Scale, _: Vsync) -> Option { + fn open_window(name: &str, width: usize, height: usize, scale_factor: i32, _: Vsync) -> Option { unsafe { let class_name = to_wstring("minifb_window"); let class = WNDCLASSW { @@ -223,7 +223,6 @@ impl Window { return None; } - let scale_factor = Self::get_scale_factor(scale); let new_width = width * scale_factor as usize; let new_height = height * scale_factor as usize; @@ -275,7 +274,9 @@ impl Window { vsync: Vsync) -> Result { unsafe { - let handle = Self::open_window(name, width, height, scale, vsync); + let scale_factor = Self::get_scale_factor(width, height, scale); + + let handle = Self::open_window(name, width, height, scale_factor, vsync); if handle.is_none() { return Err("Unable to create Window"); @@ -287,7 +288,7 @@ impl Window { keys: [false; 512], buffer: Vec::new(), is_open: true, - scale_factor: Self::get_scale_factor(scale), + scale_factor: scale_factor, width: width as i32, height: height as i32, }; @@ -325,7 +326,7 @@ impl Window { } } - fn get_scale_factor(scale: Scale) -> i32 { + unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 { // TODO: Implement best fit let factor: i32 = match scale { Scale::X1 => 1, @@ -334,7 +335,25 @@ impl Window { Scale::X8 => 8, Scale::X16 => 16, Scale::X32 => 32, - _ => 1, + 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; From 0c2e4cee7c03db5c5c0f0067710c47ede5c5a195 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 15 Dec 2015 14:20:04 +0100 Subject: [PATCH 14/22] Added get_keys() function --- src/windows.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/windows.rs b/src/windows.rs index 8800c96..52fa9ef 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -297,6 +297,23 @@ impl Window { } } + pub fn get_keys(&self) -> Option> { + let mut index: u16 = 0; + let mut keys: Vec = Vec::new(); + + for i in self.keys.iter() { + if *i { + unsafe { + 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]; From 3d54525d46eba76379a67ea93bad87fad03cbb46 Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Wed, 16 Dec 2015 09:31:14 +0100 Subject: [PATCH 15/22] added support for Ctrl and Shift keys --- src/lib.rs | 6 +++++- src/windows.rs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a6548e1..0f5b33c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,8 +135,12 @@ pub enum Key { Space, Tab, CapsLock, + LeftShift, + RightShift, + LeftCtrl, + RightCtrl, - Count = 80, + Count = 84, } #[cfg(target_os = "windows")] diff --git a/src/windows.rs b/src/windows.rs index 52fa9ef..5340c33 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -106,6 +106,10 @@ fn update_key_state(window: &mut Window, wparam: u32, state: bool) { 0x039 => window.keys[Key::Space as usize] = state, 0x00F => window.keys[Key::Tab as usize] = state, 0x03A => window.keys[Key::CapsLock as usize] = state, + 0x02A => window.keys[Key::LeftShift as usize] = state, + 0x036 => window.keys[Key::RightShift as usize] = state, + 0x01D => window.keys[Key::LeftCtrl as usize] = state, + 0x11D => window.keys[Key::RightCtrl as usize] = state, _ => (), } } From 2f91a754d510518e8355a21c762dfa05b4f78e59 Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Thu, 17 Dec 2015 12:27:46 +0100 Subject: [PATCH 16/22] added ScrollLock and numeric keypad --- src/lib.rs | 21 ++++++++++++++++++++- src/windows.rs | 20 +++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0f5b33c..b7ba5a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,13 +134,32 @@ pub enum Key { Pause, Space, Tab, + NumLock, CapsLock, + ScrollLock, LeftShift, RightShift, LeftCtrl, RightCtrl, - Count = 84, + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + NumPadDot, + NumPadSlash, + NumPadAsterisk, + NumPadMinus, + NumPadPlus, + NumPadEnter, + + Count = 102, } #[cfg(target_os = "windows")] diff --git a/src/windows.rs b/src/windows.rs index 5340c33..3abf836 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -105,12 +105,30 @@ fn update_key_state(window: &mut Window, wparam: u32, state: bool) { 0x045 => window.keys[Key::Pause as usize] = state, 0x039 => window.keys[Key::Space as usize] = state, 0x00F => window.keys[Key::Tab as usize] = state, + 0x145 => window.keys[Key::NumLock as usize] = state, 0x03A => window.keys[Key::CapsLock as usize] = state, + 0x046 => window.keys[Key::ScrollLock as usize] = state, 0x02A => window.keys[Key::LeftShift as usize] = state, 0x036 => window.keys[Key::RightShift as usize] = state, 0x01D => window.keys[Key::LeftCtrl as usize] = state, 0x11D => window.keys[Key::RightCtrl as usize] = state, - _ => (), + 0x052 => window.keys[Key::NumPad0 as usize] = state, + 0x04F => window.keys[Key::NumPad1 as usize] = state, + 0x050 => window.keys[Key::NumPad2 as usize] = state, + 0x051 => window.keys[Key::NumPad3 as usize] = state, + 0x04B => window.keys[Key::NumPad4 as usize] = state, + 0x04C => window.keys[Key::NumPad5 as usize] = state, + 0x04D => window.keys[Key::NumPad6 as usize] = state, + 0x047 => window.keys[Key::NumPad7 as usize] = state, + 0x048 => window.keys[Key::NumPad8 as usize] = state, + 0x049 => window.keys[Key::NumPad9 as usize] = state, + 0x053 => window.keys[Key::NumPadDot as usize] = state, + 0x135 => window.keys[Key::NumPadSlash as usize] = state, + 0x037 => window.keys[Key::NumPadAsterisk as usize] = state, + 0x04A => window.keys[Key::NumPadMinus as usize] = state, + 0x04E => window.keys[Key::NumPadPlus as usize] = state, + 0x11C => window.keys[Key::NumPadEnter as usize] = state, + _ => {println!("key {:04X}", wparam & 0x1ff)}, } } From 3bbbd46dd95a11608c4d7f90ec901ab03500764e Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Thu, 17 Dec 2015 12:33:21 +0100 Subject: [PATCH 17/22] remove println\ --- src/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows.rs b/src/windows.rs index 3abf836..edd0ffa 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -128,7 +128,7 @@ fn update_key_state(window: &mut Window, wparam: u32, state: bool) { 0x04A => window.keys[Key::NumPadMinus as usize] = state, 0x04E => window.keys[Key::NumPadPlus as usize] = state, 0x11C => window.keys[Key::NumPadEnter as usize] = state, - _ => {println!("key {:04X}", wparam & 0x1ff)}, + _ => (), } } From aa5d84eb2be71e674f65fa635304147563d51886 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 18 Dec 2015 20:21:47 +0100 Subject: [PATCH 18/22] Added support for key repeat * Added get_keys_pressed(KeyRepeat::Yes/No) in order for the user to decide if repeat should be used or not * added is_key_pressed(key, KeyRepeat::Yes/No) * Added set_key_repeat_delay/rate in order to control the rate/delays of keys --- Cargo.toml | 3 +- examples/noise.rs | 12 +++++++ src/lib.rs | 9 ++++++ src/windows.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6333080..066f0ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,9 @@ build = "build.rs" gcc = "0.3.19" [dependencies] -libc = "0.1.10" +libc = "0.2" user32-sys = "0.1.2" winapi = "0.2.4" kernel32-sys = "0.1.4" gdi32-sys = "0.1.1" +time = "0.1.34" diff --git a/examples/noise.rs b/examples/noise.rs index 7142140..fd9c4a7 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -31,6 +31,18 @@ fn main() { noise &= 0xFF; *i = (noise << 16) | (noise << 8) | noise; } + + window.get_keys_pressed(KeyRepeat::No).map(|keys| { + for t in keys.iter() { + match *t { + Key::W => println!("holding w!"), + Key::T => println!("holding t!"), + _ => (), + } + } + }); + + window.update(&buffer); } } diff --git a/src/lib.rs b/src/lib.rs index b7ba5a1..d93872f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,15 @@ pub enum Vsync { BestGuess, } +/// 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, +} + pub enum Key { Key0 = 0, Key1 = 1, diff --git a/src/windows.rs b/src/windows.rs index edd0ffa..dd8893d 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -4,10 +4,12 @@ extern crate user32; extern crate kernel32; extern crate winapi; extern crate gdi32; +extern crate time; use Scale; use Vsync; use Key; +use KeyRepeat; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -216,11 +218,16 @@ pub struct Window { dc: Option, window: Option, keys: [bool; 512], + keys_down_duration: [f32; 512], buffer: Vec, is_open : bool, scale_factor: i32, width: i32, height: i32, + prev_time: f64, + delta_time: f32, + key_repeat_delay: f32, + key_repeat_rate: f32, } impl Window { @@ -308,6 +315,11 @@ impl Window { dc: Some(user32::GetDC(handle.unwrap())), window: Some(handle.unwrap()), 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, buffer: Vec::new(), is_open: true, scale_factor: scale_factor, @@ -336,11 +348,61 @@ impl Window { Some(keys) } + pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { + let mut index: u16 = 0; + let mut keys: Vec = 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); + } + #[inline] pub fn is_open(&self) -> bool { return self.is_open @@ -351,6 +413,23 @@ impl Window { let mut msg = mem::uninitialized(); let window = self.window.unwrap(); + 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; + } + } + // TODO: Optimize self.buffer = buffer.iter().cloned().collect(); @@ -366,7 +445,6 @@ impl Window { } unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 { - // TODO: Implement best fit let factor: i32 = match scale { Scale::X1 => 1, Scale::X2 => 2, From 6fd476783ce69c23feb0d7b32e15d4ed256cce1f Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Tue, 22 Dec 2015 12:54:06 +0100 Subject: [PATCH 19/22] Added backquote/tilde key --- src/lib.rs | 3 ++- src/windows.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d93872f..7881404 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,6 +115,7 @@ pub enum Key { Right, Up, Apostrophe, + Backquote, Backslash, Comma, @@ -168,7 +169,7 @@ pub enum Key { NumPadPlus, NumPadEnter, - Count = 102, + Count = 103, } #[cfg(target_os = "windows")] diff --git a/src/windows.rs b/src/windows.rs index dd8893d..9444f89 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -85,6 +85,7 @@ fn update_key_state(window: &mut Window, wparam: u32, state: bool) { 0x14D => window.keys[Key::Right as usize] = state, 0x148 => window.keys[Key::Up as usize] = state, 0x028 => window.keys[Key::Apostrophe as usize] = state, + 0x029 => window.keys[Key::Backquote as usize] = state, 0x02B => window.keys[Key::Backslash as usize] = state, 0x033 => window.keys[Key::Comma as usize] = state, 0x00D => window.keys[Key::Equal as usize] = state, From c5aca23e78d194787655044daa3788f32422458b Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 25 Dec 2015 10:37:13 +0100 Subject: [PATCH 20/22] Updated README with correct example --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1441c4b..a0d461b 100644 --- a/README.md +++ b/README.md @@ -7,32 +7,37 @@ rust_minifb (Mini FrameBuffer) is a small cross platform library written in [Rus ```rust extern crate minifb; -const WIDTH: usize = 1280; -const HEIGHT: usize = 720; +use minifb::*; + +const WIDTH: usize = 640; +const HEIGHT: usize = 360; fn main() { let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; - if !(minifb::open("TestWindow", WIDTH, HEIGHT)) { - return; - } + let mut window = Window::new("Noise Test - Press ESC to exit", + WIDTH, + HEIGHT, + Scale::X1, + Vsync::No) + .unwrap(); - while minifb::update(&buffer) { + while window.is_open() && !window.is_key_down(Key::Escape) { for i in buffer.iter_mut() { - *i = ... // write something here + *i = 0; // write something more funny here! } - } - minifb::close(); + window.update(&buffer); + } } ``` Status ------ -Currently Mac, Windows has been Linux has been tested which are the supported platforms for now. +Currently Windows is the supported platform. -Build instructions +Build instruction ------------------ ``` @@ -40,6 +45,6 @@ cargo build cargo run --example noise ``` -This will run the [noise example](https://github.com/emoon/rust_minifb/blob/master/examples/noise.rs) which should look something like this (Mac screenshot) +This will run the [noise example](https://github.com/emoon/rust_minifb/blob/windows-rs/examples/noise.rs) which should look something like this (Mac screenshot) ![mac_screenshot](https://dl.dropboxusercontent.com/u/5205843/rust_minifb/noise_screen.png) From de47861fe373e553beacf579652b18c77510f0cb Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Mon, 28 Dec 2015 16:50:51 +0100 Subject: [PATCH 21/22] Ignore the "Class already exists" error in multiple-window application --- src/windows.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 9444f89..0a4afca 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -249,8 +249,14 @@ impl Window { }; if user32::RegisterClassW(&class) == 0 { - println!("Unable to register class, error {}", kernel32::GetLastError() as u32); - return None; + let err_code = kernel32::GetLastError() as u32; + + // ignore the "Class already exists" error for multiple windows + if err_code != 1410 + { + println!("Unable to register class, error {}", kernel32::GetLastError() as u32); + return None; + } } let new_width = width * scale_factor as usize; From bd5ac066a2bf66524713b38b1eb7fd2aaec94f6d Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Mon, 28 Dec 2015 17:17:23 +0100 Subject: [PATCH 22/22] fixed braces formatting + dropped err_code variable --- src/windows.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 0a4afca..58ce1a9 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -249,11 +249,8 @@ impl Window { }; if user32::RegisterClassW(&class) == 0 { - let err_code = kernel32::GetLastError() as u32; - // ignore the "Class already exists" error for multiple windows - if err_code != 1410 - { + if kernel32::GetLastError() as u32 != 1410 { println!("Unable to register class, error {}", kernel32::GetLastError() as u32); return None; }