From 298e535de22782a8585f76c8c002f68b94e89c4d Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 24 Nov 2015 21:46:52 +0100 Subject: [PATCH 01/50] Started to work on Rust only version for Windows --- Cargo.toml | 1 + build.rs | 6 +++--- src/lib.rs | 8 ++++++++ src/windows.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/windows.rs diff --git a/Cargo.toml b/Cargo.toml index 86c6be6..9d79e7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ gcc = "0.3.19" [dependencies] libc = "0.1.10" +user32-sys = "0.1.2" diff --git a/build.rs b/build.rs index 308dd66..d8317c4 100644 --- a/build.rs +++ b/build.rs @@ -8,9 +8,9 @@ fn main() { &["src/native/macosx/MacMiniFB.m", "src/native/macosx/OSXWindow.m", "src/native/macosx/OSXWindowFrameView.m"]); // MacOS - } else if env.contains("windows") { - gcc::compile_library("libminifb_native.a", &["src/native/windows/WinMiniFB.c"]); // Windows - } else { + // } else if env.contains("windows") { + // gcc::compile_library("libminifb_native.a", &["src/native/windows/WinMiniFB.c"]); // Windows + } else if env.contains("linux") { gcc::compile_library("libminifb_native.a", &["src/native/x11/X11MiniFB.c"]); // Unix } } diff --git a/src/lib.rs b/src/lib.rs index 63dc6a0..72aa947 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,9 @@ use std::ffi::CString; use std::mem::transmute; use libc::{c_char, c_int, c_void}; +#[cfg(target_os = "windows")] +pub mod windows; + #[cfg(target_os = "macos")] #[link(name = "Cocoa", kind = "framework")] extern { @@ -11,6 +14,7 @@ extern { fn mfb_close(); } +/* #[cfg(target_os = "windows")] #[link(name = "gdi32")] extern { @@ -18,6 +22,7 @@ extern { fn mfb_update(buffer: *mut c_void) -> c_int; fn mfb_close(); } +*/ #[cfg(target_os = "linux")] #[link(name = "X11")] @@ -30,6 +35,7 @@ extern { /// /// 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; @@ -47,6 +53,7 @@ pub fn open(name: &str, width: usize, height: usize) -> bool { /// /// Update /// +#[cfg(any(target_os = "linux", target_os = "mac"))] pub fn update(buffer: &[u32]) -> bool { let ret; unsafe { @@ -63,6 +70,7 @@ pub fn update(buffer: &[u32]) -> bool { /// /// 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 new file mode 100644 index 0000000..dcd69e9 --- /dev/null +++ b/src/windows.rs @@ -0,0 +1,47 @@ +extern crate user; + +pub fn open(name: &str, width: usize, height: usize) -> bool { + let s = CString::new(name).unwrap(); + + unsafe { + let class = winapi::WNDCLASSEXA { + cbSize: mem::size_of::() as winapi::UINT, + style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC, + lpfnWndProc: Some(callback::callback), + 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: s.as_ptr(), + hIconSm: ptr::null_mut(), + }; + + user32::RegisterClassExA(&class); + + let mut rect = winapi::RECT { + left: 0, right: width as winapi::LONG, + top: 0, bottom: 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 handle = user32::CreateWindowExA(0, + s.as_ptr(), s.as_ptr(), + winapi::WS_OVERLAPPEDWINDOW & ~winapi::WS_MAXIMIZEBOX & ~winapi::WS_TICKFRAME, + 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() { + return false; + } + + user32::ShowWindow(handle, winapi::SW_NORMAL); +} + From ccfeeb24da7730cd648ee5c8979a68b2cf30946b Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 27 Nov 2015 19:14:24 +0100 Subject: [PATCH 02/50] WIP on Windows version --- src/windows.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index dcd69e9..63ab418 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,6 +1,22 @@ extern crate user; +static winapi::HANDLE HWND_HANDLE = 0; +static bool CLOSE_APP = false; + +unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, + wparam: winapi::WPARAM, lparam: winapi::LPARAM) + -> winapi::LRESULT +{ + match msg { + winapi::WM_PAINT => { + + } + } +} + + pub fn open(name: &str, width: usize, height: usize) -> bool { + let class_name = CString::new("minifb_window").unwrap(); let s = CString::new(name).unwrap(); unsafe { @@ -15,7 +31,7 @@ pub fn open(name: &str, width: usize, height: usize) -> bool { hCursor: ptr::null_mut(), hbrBackground: ptr::null_mut(), lpszMenuName: ptr::null(), - lpszClassName: s.as_ptr(), + lpszClassName: class_name.as_ptr(), hIconSm: ptr::null_mut(), }; @@ -32,7 +48,7 @@ pub fn open(name: &str, width: usize, height: usize) -> bool { rect.bottom -= rect.top; let handle = user32::CreateWindowExA(0, - s.as_ptr(), s.as_ptr(), + class_name.as_ptr(), s.as_ptr(), winapi::WS_OVERLAPPEDWINDOW & ~winapi::WS_MAXIMIZEBOX & ~winapi::WS_TICKFRAME, winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, rect.right, rect.bottom, @@ -43,5 +59,21 @@ pub fn open(name: &str, width: usize, height: usize) -> bool { } user32::ShowWindow(handle, winapi::SW_NORMAL); + + HWND_HANDLE = handle; +} + +fn update() -> bool { + + let mut msg = mem::uninitialized(); + + user32::InvalidateRect(HWND_HANDLE, ptr::null_mut(), winapi::TRUE); + + while user32::PeekMessage(&mut msg, HWND_HANDLE, 0, 0, winapi::PM_REMOTE) { + user32::TranslateMessage(&mut msg); + user32::DispatchMessage(&mut msg); + } + + CLOSE_APP, } From a9b0a118534c6b07fac7d773ee05ce3a1f933866 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 27 Nov 2015 21:25:50 +0100 Subject: [PATCH 03/50] Window gets created --- Cargo.toml | 3 +- examples/noise.rs | 22 +++--- src/lib.rs | 10 +-- src/windows.rs | 173 ++++++++++++++++++++++++++++------------------ 4 files changed, 129 insertions(+), 79 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9d79e7c..c2e89df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ gcc = "0.3.19" [dependencies] libc = "0.1.10" user32-sys = "0.1.2" - +winapi = "0.2.4" +kernel32-sys = "0.1.4" diff --git a/examples/noise.rs b/examples/noise.rs index d1bf858..98029b1 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -1,20 +1,25 @@ extern crate minifb; +use minifb::*; + const WIDTH: usize = 640; const HEIGHT: usize = 360; fn main() { - let mut noise; - let mut carry; - let mut seed = 0xbeefu32; + //let mut noise; + //let mut carry; + //let mut seed = 0xbeefu32; - 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)) { - return; + let mut mfb = Minifb::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT).unwrap(); + + while mfb.update() { } - while minifb::update(&buffer) { + /* + while mfb.update() { + for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -27,6 +32,7 @@ fn main() { *i = (noise << 16) | (noise << 8) | noise; } } + */ - minifb::close(); + //minifb::close(); } diff --git a/src/lib.rs b/src/lib.rs index 72aa947..3aed2ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ extern crate libc; -use std::ffi::CString; -use std::mem::transmute; -use libc::{c_char, c_int, c_void}; - #[cfg(target_os = "windows")] pub mod windows; +pub use windows::*; + +/* + #[cfg(target_os = "macos")] #[link(name = "Cocoa", kind = "framework")] extern { @@ -76,3 +76,5 @@ pub fn close() { mfb_close(); } } + +*/ diff --git a/src/windows.rs b/src/windows.rs index 63ab418..fbf6fd7 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,79 +1,120 @@ -extern crate user; +extern crate user32; +extern crate kernel32; +extern crate winapi; +use std::ffi::CString; +use std::ptr; +use std::os::windows::ffi::OsStrExt; +use std::ffi::OsStr; +use std::mem; -static winapi::HANDLE HWND_HANDLE = 0; -static bool CLOSE_APP = false; +use self::winapi::windef::HWND; +use self::winapi::winuser::WS_OVERLAPPEDWINDOW; +use self::winapi::winuser::WNDCLASSW; -unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, +static mut CLOSE_APP: bool = false; + +unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> winapi::LRESULT { match msg { - winapi::WM_PAINT => { + winapi::winuser::WM_KEYDOWN => { + if (wparam & 0xff) == 27 { + CLOSE_APP = true; + } + } + _ => (), + } + + return user32::DefWindowProcW(window, msg, wparam, lparam); +} + +pub enum MinifbError { + UnableToCreateWindow, +} + +fn to_wstring(str : &str) -> *const u16 { + let v : Vec = OsStr::new(str).encode_wide(). chain(Some(0).into_iter()).collect(); + v.as_ptr() +} + +pub struct Minifb { + window: HWND, +} + +impl Minifb { + fn open_window(name: &str, width: usize, height: usize) -> HWND { + 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, + 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, + }; + + user32::RegisterClassW(&class); + + let mut rect = winapi::RECT { + left: 0, right: width as winapi::LONG, + top: 0, bottom: 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 handle = user32::CreateWindowExA(0, + "minifb_window".as_ptr() as *mut _, s.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() { + 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); + + match handle.is_null() { + true => None, + false => Some(Minifb { window : handle }), + } + } + + pub fn update(&mut self) -> bool { + unsafe { + let mut msg = mem::uninitialized(); + + user32::InvalidateRect(self.window, ptr::null_mut(), winapi::TRUE); + + while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != 0 { + user32::TranslateMessage(&mut msg); + user32::DispatchMessageW(&mut msg); + } + } + + unsafe { + return !CLOSE_APP; } } } -pub fn open(name: &str, width: usize, height: usize) -> bool { - let class_name = CString::new("minifb_window").unwrap(); - let s = CString::new(name).unwrap(); - - unsafe { - let class = winapi::WNDCLASSEXA { - cbSize: mem::size_of::() as winapi::UINT, - style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC, - lpfnWndProc: Some(callback::callback), - 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(), - hIconSm: ptr::null_mut(), - }; - - user32::RegisterClassExA(&class); - - let mut rect = winapi::RECT { - left: 0, right: width as winapi::LONG, - top: 0, bottom: 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 handle = user32::CreateWindowExA(0, - class_name.as_ptr(), s.as_ptr(), - winapi::WS_OVERLAPPEDWINDOW & ~winapi::WS_MAXIMIZEBOX & ~winapi::WS_TICKFRAME, - 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() { - return false; - } - - user32::ShowWindow(handle, winapi::SW_NORMAL); - - HWND_HANDLE = handle; -} - -fn update() -> bool { - - let mut msg = mem::uninitialized(); - - user32::InvalidateRect(HWND_HANDLE, ptr::null_mut(), winapi::TRUE); - - while user32::PeekMessage(&mut msg, HWND_HANDLE, 0, 0, winapi::PM_REMOTE) { - user32::TranslateMessage(&mut msg); - user32::DispatchMessage(&mut msg); - } - - CLOSE_APP, -} From 15ec55aae2b9d36dc283a74fffe2d92d9be19a91 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 27 Nov 2015 23:42:28 +0100 Subject: [PATCH 04/50] Working Win32 version in pure Rust --- Cargo.toml | 1 + examples/noise.rs | 18 +++++------------- src/windows.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c2e89df..6333080 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ libc = "0.1.10" user32-sys = "0.1.2" winapi = "0.2.4" kernel32-sys = "0.1.4" +gdi32-sys = "0.1.1" diff --git a/examples/noise.rs b/examples/noise.rs index 98029b1..28a3155 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -6,20 +6,15 @@ const WIDTH: usize = 640; const HEIGHT: usize = 360; fn main() { - //let mut noise; - //let mut carry; - //let mut seed = 0xbeefu32; + let mut noise; + let mut carry; + let mut seed = 0xbeefu32; - //let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; + let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; let mut mfb = Minifb::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT).unwrap(); - while mfb.update() { - } - - /* - while mfb.update() { - + while mfb.update(&buffer) { for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -32,7 +27,4 @@ fn main() { *i = (noise << 16) | (noise << 8) | noise; } } - */ - - //minifb::close(); } diff --git a/src/windows.rs b/src/windows.rs index fbf6fd7..df4b2af 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,6 +1,8 @@ extern crate user32; extern crate kernel32; extern crate winapi; +extern crate gdi32; + use std::ffi::CString; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -10,9 +12,18 @@ use std::mem; use self::winapi::windef::HWND; use self::winapi::winuser::WS_OVERLAPPEDWINDOW; 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 { + pub bmi_header: BITMAPINFOHEADER, + pub bmi_colors: [RGBQUAD; 3], +} + unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> winapi::LRESULT @@ -24,6 +35,39 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, } } + 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); + + 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_colors[0].rgbRed = 0xff; + bitmap_info.bmi_colors[1].rgbGreen = 0xff; + bitmap_info.bmi_colors[2].rgbBlue = 0xff; + + let dc = user32::GetDC(window); + + gdi32::StretchDIBits(dc, 0, 0, width, height, 0, 0, width, height, + mem::transmute(buffer), + mem::transmute(&bitmap_info), + winapi::wingdi::DIB_RGB_COLORS, + winapi::wingdi::SRCCOPY); + + user32::ValidateRect(window, ptr::null_mut()); + + user32::ReleaseDC(window, dc); + } + _ => (), } @@ -98,10 +142,11 @@ impl Minifb { } } - pub fn update(&mut self) -> bool { + 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::InvalidateRect(self.window, ptr::null_mut(), winapi::TRUE); while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != 0 { From 860f8b4f3d761dacc5c256c882aad7d0442f5658 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 27 Nov 2015 23:47:15 +0100 Subject: [PATCH 05/50] Ran rustfmt --- src/windows.rs | 87 +++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index df4b2af..1d787a3 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -20,14 +20,15 @@ static mut CLOSE_APP: bool = false; // 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], + pub bmi_header: BITMAPINFOHEADER, + pub bmi_colors: [RGBQUAD; 3], } -unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, - wparam: winapi::WPARAM, lparam: winapi::LPARAM) - -> winapi::LRESULT -{ +unsafe extern "system" fn wnd_proc(window: winapi::HWND, + msg: winapi::UINT, + wparam: winapi::WPARAM, + lparam: winapi::LPARAM) + -> winapi::LRESULT { match msg { winapi::winuser::WM_KEYDOWN => { if (wparam & 0xff) == 27 { @@ -37,7 +38,7 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, winapi::winuser::WM_PAINT => { let mut rect: winapi::RECT = mem::uninitialized(); - let buffer = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA); + let buffer = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA); user32::GetClientRect(window, &mut rect); @@ -51,17 +52,25 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, msg: winapi::UINT, bitmap_info.bmi_header.biCompression = winapi::wingdi::BI_BITFIELDS; bitmap_info.bmi_header.biWidth = width; bitmap_info.bmi_header.biHeight = -height; - bitmap_info.bmi_colors[0].rgbRed = 0xff; - bitmap_info.bmi_colors[1].rgbGreen = 0xff; - bitmap_info.bmi_colors[2].rgbBlue = 0xff; + bitmap_info.bmi_colors[0].rgbRed = 0xff; + bitmap_info.bmi_colors[1].rgbGreen = 0xff; + bitmap_info.bmi_colors[2].rgbBlue = 0xff; let dc = user32::GetDC(window); - gdi32::StretchDIBits(dc, 0, 0, width, height, 0, 0, width, height, - mem::transmute(buffer), - mem::transmute(&bitmap_info), - winapi::wingdi::DIB_RGB_COLORS, - winapi::wingdi::SRCCOPY); + gdi32::StretchDIBits(dc, + 0, + 0, + width, + height, + 0, + 0, + width, + height, + mem::transmute(buffer), + mem::transmute(&bitmap_info), + winapi::wingdi::DIB_RGB_COLORS, + winapi::wingdi::SRCCOPY); user32::ValidateRect(window, ptr::null_mut()); @@ -78,8 +87,8 @@ pub enum MinifbError { UnableToCreateWindow, } -fn to_wstring(str : &str) -> *const u16 { - let v : Vec = OsStr::new(str).encode_wide(). chain(Some(0).into_iter()).collect(); +fn to_wstring(str: &str) -> *const u16 { + let v: Vec = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect(); v.as_ptr() } @@ -90,9 +99,9 @@ pub struct Minifb { impl Minifb { fn open_window(name: &str, width: usize, height: usize) -> HWND { unsafe { - let class_name = to_wstring("minifb_window"); + 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, lpfnWndProc: Some(wnd_proc), @@ -109,21 +118,33 @@ impl Minifb { user32::RegisterClassW(&class); let mut rect = winapi::RECT { - left: 0, right: width as winapi::LONG, - top: 0, bottom: height as winapi::LONG, + left: 0, + right: width as winapi::LONG, + top: 0, + bottom: height as winapi::LONG, }; - user32::AdjustWindowRect(&mut rect, winapi::WS_POPUP | winapi::WS_SYSMENU | winapi::WS_CAPTION, 0); + user32::AdjustWindowRect(&mut rect, + winapi::WS_POPUP | winapi::WS_SYSMENU | winapi::WS_CAPTION, + 0); rect.right -= rect.left; rect.bottom -= rect.top; let handle = user32::CreateWindowExA(0, - "minifb_window".as_ptr() as *mut _, s.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()); + "minifb_window".as_ptr() as *mut _, + s.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() { user32::ShowWindow(handle, winapi::SW_NORMAL); @@ -138,7 +159,7 @@ impl Minifb { match handle.is_null() { true => None, - false => Some(Minifb { window : handle }), + false => Some(Minifb { window: handle }), } } @@ -146,10 +167,13 @@ impl Minifb { 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, + buffer.as_ptr() 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 { + while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != + 0 { user32::TranslateMessage(&mut msg); user32::DispatchMessageW(&mut msg); } @@ -160,6 +184,3 @@ impl Minifb { } } } - - - From e83c66b8ddc85aff70bc2a233e17c9ca344cd8f9 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 27 Nov 2015 23:49:24 +0100 Subject: [PATCH 06/50] Minor fixup --- src/windows.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 1d787a3..d4e5e1c 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -172,8 +172,7 @@ impl Minifb { buffer.as_ptr() 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 { + while user32::PeekMessageW(&mut msg, self.window, 0, 0, winapi::winuser::PM_REMOVE) != 0 { user32::TranslateMessage(&mut msg); user32::DispatchMessageW(&mut msg); } From 29e744bc197d24e17b785df21a357c8f418e5546 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Fri, 4 Dec 2015 20:35:35 +0100 Subject: [PATCH 07/50] WIP on pure Rust version of the Mac backend --- Cargo.toml | 22 +++++++++++++ src/lib.rs | 36 ++++++++++++++++++++- src/macos.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/windows.rs | 1 - 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/macos.rs diff --git a/Cargo.toml b/Cargo.toml index 6333080..9256621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,25 @@ user32-sys = "0.1.2" winapi = "0.2.4" kernel32-sys = "0.1.4" gdi32-sys = "0.1.1" + +[target.x86_64-apple-darwin.dependencies] +objc = "0.1.8" +cgl = "0.1" +cocoa = "0.1.4" +core-foundation = "0" +core-graphics = "0" + +[target.i686-pc-windows-gnu.dependencies] +winapi = "0.2.4" +user32-sys = "0.1.2" +kernel32-sys = "0.1.4" +gdi32-sys = "0.1.1" + +[target.i686-pc-windows-msvc.dependencies] +winapi = "0.2.4" +user32-sys = "0.1.2" +kernel32-sys = "0.1.4" +gdi32-sys = "0.1.1" + + + diff --git a/src/lib.rs b/src/lib.rs index 3aed2ca..afe32cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,43 @@ 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")] + +/// Error that can happen while creating a window or a headless renderer. +#[derive(Debug)] +pub enum CreationError { + OsError(String), + NotSupported, +} + +impl CreationError { + fn to_string(&self) -> &str { + match *self { + CreationError::OsError(ref text) => &text, + CreationError::NotSupported => "Some of the requested attributes are not supported", + } + } +} + #[cfg(target_os = "windows")] pub mod windows; - +#[cfg(target_os = "windows")] pub use windows::*; +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "mac")] +pub use macos::*; + + /* #[cfg(target_os = "macos")] diff --git a/src/macos.rs b/src/macos.rs new file mode 100644 index 0000000..d1a6596 --- /dev/null +++ b/src/macos.rs @@ -0,0 +1,85 @@ +#![cfg(target_os = "macos")] + +use CreationError; +use CreationError::OsError; + +use libc; +use cocoa::appkit; +use cocoa::appkit::*; +use cocoa::appkit::NSEventSubtype::*; + +use cocoa::base::{id, nil}; +use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; + +use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, + NSString, NSUInteger}; + + +struct Minifb { + temp: isize, +} + +impl Minifb { + pub unsafe fn new(name: &str, width: isize, height: isize) -> Result { + let app = match Self::create_app() { + Some(app) => app, + None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, + }; + + let masks = 0u64; + + //let masks = NSResizableWindowMask as NSUInteger | + // NSClosableWindowMask as NSUInteger | + // NSTitledWindowMaskas as NSUInteger; + + let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)); + + let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( + frame, + masks, + NSBackingStoreBuffered, + NO, + )); + + if window.is_nil() { + return Err(OsError(format!("Unable to create window"))); + } + + } + + fn create_app() -> Option { + unsafe { + let app = NSApp(); + if app == nil { + None + } else { + app.setActivationPolicy_(NSApplicationActivationPolicyRegular); + Some(app) + } + } + } + + +} + +struct IdRef(id); + +impl IdRef { + fn new(i: id) -> IdRef { + IdRef(i) + } + + #[allow(dead_code)] + fn retain(i: id) -> IdRef { + if i != nil { + let _: id = unsafe { msg_send![i, retain] }; + } + IdRef(i) + } + + fn non_nil(self) -> Option { + if self.0 == nil { None } else { Some(self) } + } +} + + diff --git a/src/windows.rs b/src/windows.rs index d4e5e1c..c7d3a5c 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -76,7 +76,6 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, user32::ReleaseDC(window, dc); } - _ => (), } From 99e106fe931011e5c80ebc632c8c129839765f95 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sun, 6 Dec 2015 19:14:34 +0100 Subject: [PATCH 08/50] WIP on latest Mac version --- examples/noise.rs | 24 ++++-- src/lib.rs | 197 ++++++++++++++++++++++++++-------------------- src/macos.rs | 124 +++++++++++++++++++++++------ 3 files changed, 231 insertions(+), 114 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index 28a3155..55131b0 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -6,15 +6,26 @@ const WIDTH: usize = 640; const HEIGHT: usize = 360; fn main() { - let mut noise; - let mut carry; - let mut seed = 0xbeefu32; + //let mut noise; + //let mut carry; + //let mut seed = 0xbeefu32; - let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; + //let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; - let mut mfb = Minifb::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT).unwrap(); + let window = match macos::Window::new("Noise Test - Press ESC to exit", + WIDTH, + HEIGHT, + Scale::X1, + Vsync::No) { + Ok(window) => window, + Err(info) => { + println!("{}", info); + return; + } + }; - while mfb.update(&buffer) { + loop { + /* for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -26,5 +37,6 @@ fn main() { noise &= 0xFF; *i = (noise << 16) | (noise << 8) | noise; } + */ } } diff --git a/src/lib.rs b/src/lib.rs index afe32cc..2713803 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,20 +11,119 @@ extern crate cocoa; extern crate core_foundation; #[cfg(target_os = "macos")] -/// Error that can happen while creating a window or a headless renderer. -#[derive(Debug)] -pub enum CreationError { - OsError(String), - NotSupported, +/// 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, } -impl CreationError { - fn to_string(&self) -> &str { - match *self { - CreationError::OsError(ref text) => &text, - CreationError::NotSupported => "Some of the requested attributes are not supported", - } - } +/// Vsync will allow syncronized rendering with the screen refresh rate. +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 { + Space, + /// The '1' key over the letters. + Key1, + /// The '2' key over the letters. + Key2, + /// The '3' key over the letters. + Key3, + /// The '4' key over the letters. + Key4, + /// The '5' key over the letters. + Key5, + /// The '6' key over the letters. + Key6, + /// The '7' key over the letters. + Key7, + /// The '8' key over the letters. + Key8, + /// The '9' key over the letters. + Key9, + /// The '0' key over the 'O' and 'P' keys. + 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, + + Escape, + + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + + ArrowLeft, + ArrowRight, + ArrowUp, + ArrowDown, + + Backspace, + Delete, + Comma, + Semicollon } #[cfg(target_os = "windows")] @@ -38,77 +137,3 @@ pub mod macos; pub use macos::*; -/* - -#[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(); -} - -/* -#[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/macos.rs b/src/macos.rs index d1a6596..b634b84 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,36 +1,69 @@ #![cfg(target_os = "macos")] -use CreationError; -use CreationError::OsError; +use Scale; +use Vsync; +use Key; -use libc; -use cocoa::appkit; +//use libc; +//use cocoa::appkit; use cocoa::appkit::*; -use cocoa::appkit::NSEventSubtype::*; +//use cocoa::appkit::NSEventSubtype::*; +#[allow(unused_imports)] use cocoa::base::{id, nil}; +#[allow(unused_imports)] use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; +#[allow(unused_imports)] use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, NSString, NSUInteger}; +use std::ops::Deref; -struct Minifb { - temp: isize, +pub struct Window { + view: IdRef, + window: IdRef, } -impl Minifb { - pub unsafe fn new(name: &str, width: isize, height: isize) -> Result { - let app = match Self::create_app() { - Some(app) => app, - None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, - }; +impl Window { + pub fn new(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { + unsafe { + let app = match Self::create_app() { + Some(app) => app, + None => { + return Err("Couldn't create NSApplication"); + }, + }; - let masks = 0u64; + let window = match Self::create_window(name, width, height) { + Some(window) => window, + None => { + return Err("Unable to create NSWindow"); + } + }; - //let masks = NSResizableWindowMask as NSUInteger | - // NSClosableWindowMask as NSUInteger | - // NSTitledWindowMaskas as NSUInteger; + let view = match Self::create_view(*window) { + Some(view) => view, + None => { + return Err("Unable to create NSView"); + } + }; + + app.activateIgnoringOtherApps_(YES); + + println!("Created window and view"); + + return Ok(Window { + window: window, + view: view + }); + } + } + + unsafe fn create_window(name: &str, width: usize, height: usize) -> Option { + let masks = NSResizableWindowMask as NSUInteger | + NSClosableWindowMask as NSUInteger | + NSTitledWindowMask as NSUInteger; let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)); @@ -41,9 +74,34 @@ impl Minifb { NO, )); - if window.is_nil() { - return Err(OsError(format!("Unable to create window"))); - } + window.non_nil().map(|window| { + let title = IdRef::new(NSString::alloc(nil).init_str(name)); + window.setTitle_(*title); + window.center(); + window + }) + } + + unsafe fn create_view(window: id) -> Option { + let view = IdRef::new(NSView::alloc(nil).init()); + view.non_nil().map(|view| { + window.setContentView_(*view); + view + }) + } + + pub fn update(_: &[u32]) { + } + + pub fn get_keys() -> Option> { + return None; + } + + pub fn is_esc_pressed() -> bool { + false + } + + pub fn close() { } @@ -58,10 +116,10 @@ impl Minifb { } } } - - } + + struct IdRef(id); impl IdRef { @@ -82,4 +140,26 @@ impl IdRef { } } +impl Drop for IdRef { + fn drop(&mut self) { + if self.0 != nil { + let _: () = unsafe { msg_send![self.0, release] }; + } + } +} +impl Deref for IdRef { + type Target = id; + fn deref<'a>(&'a self) -> &'a id { + &self.0 + } +} + +impl Clone for IdRef { + fn clone(&self) -> IdRef { + if self.0 != nil { + let _: id = unsafe { msg_send![self.0, retain] }; + } + IdRef(self.0) + } +} From c8b45d85776505eef39cd544845e0bd51fecba50 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 18:49:03 +0100 Subject: [PATCH 09/50] 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 10/50] 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 11/50] 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 12/50] 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 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 19/50] 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 20/50] 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 21/50] 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 22/50] 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 23/50] 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 24/50] 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 25/50] 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 26/50] 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 08028c1e542db816280ac333d5a609f6ff0b8cf0 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 19 Dec 2015 08:25:04 +0100 Subject: [PATCH 27/50] WIP on Mac version --- src/macos.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/src/macos.rs b/src/macos.rs index b634b84..7c1150f 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -4,7 +4,7 @@ use Scale; use Vsync; use Key; -//use libc; +use libc; //use cocoa::appkit; use cocoa::appkit::*; //use cocoa::appkit::NSEventSubtype::*; @@ -13,6 +13,7 @@ use cocoa::appkit::*; use cocoa::base::{id, nil}; #[allow(unused_imports)] use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; +use objc::declare::ClassDecl; #[allow(unused_imports)] use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, @@ -23,8 +24,126 @@ use std::ops::Deref; pub struct Window { view: IdRef, window: IdRef, + delegate: WindowDelegate, } +struct DelegateState { + view: IdRef, + window: IdRef, + resize_handler: Option, +} + +struct WindowDelegate { + state: Box, + _this: IdRef, +} + +//sthou + +impl WindowDelegate { + /// Get the delegate class, initiailizing it neccessary + fn class() -> *const Class { + use std::sync::{Once, ONCE_INIT}; + + extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = state as *mut DelegateState; + //(*state).pending_events.lock().unwrap().push_back(Closed); + } + YES + } + + extern fn window_did_resize(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + + //let _: () = msg_send![*state.context, update]; + + if let Some(handler) = state.resize_handler { + let rect = NSView::frame(*state.view); + let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32; + (handler)((scale_factor * rect.size.width as f32) as u32, + (scale_factor * rect.size.height as f32) as u32); + } + } + } + + extern fn window_did_become_key(this: &Object, _: Sel, _: id) { + unsafe { + // TODO: center the cursor if the window had mouse grab when it + // lost focus + + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = state as *mut DelegateState; + //(*state).pending_events.lock().unwrap().push_back(Focused(true)); + } + } + + extern fn window_did_resign_key(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = state as *mut DelegateState; + //(*state).pending_events.lock().unwrap().push_back(Focused(false)); + } + } + + static mut delegate_class: *const Class = 0 as *const Class; + static INIT: Once = ONCE_INIT; + + INIT.call_once(|| unsafe { + // Create new NSWindowDelegate + let superclass = Class::get("NSObject").unwrap(); + let mut decl = ClassDecl::new(superclass, "GlutinWindowDelegate").unwrap(); + + // Add callback methods + decl.add_method(sel!(windowShouldClose:), + window_should_close as extern fn(&Object, Sel, id) -> BOOL); + decl.add_method(sel!(windowDidResize:), + window_did_resize as extern fn(&Object, Sel, id)); + + decl.add_method(sel!(windowDidBecomeKey:), + window_did_become_key as extern fn(&Object, Sel, id)); + decl.add_method(sel!(windowDidResignKey:), + window_did_resign_key as extern fn(&Object, Sel, id)); + + // Store internal state as user data + decl.add_ivar::<*mut libc::c_void>("glutinState"); + + delegate_class = decl.register(); + }); + + unsafe { + delegate_class + } + } + + fn new(state: DelegateState) -> WindowDelegate { + // Box the state so we can give a pointer to it + let mut state = Box::new(state); + let state_ptr: *mut DelegateState = &mut *state; + unsafe { + let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]); + + (&mut **delegate).set_ivar("glutinState", state_ptr as *mut libc::c_void); + let _: () = msg_send![*state.window, setDelegate:*delegate]; + + WindowDelegate { state: state, _this: delegate } + } + } +} + +impl Drop for WindowDelegate { + fn drop(&mut self) { + unsafe { + // Nil the window's delegate so it doesn't still reference us + let _: () = msg_send![*self.state.window, setDelegate:nil]; + } + } +} + + impl Window { pub fn new(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { unsafe { @@ -51,11 +170,18 @@ impl Window { app.activateIgnoringOtherApps_(YES); + let ds = DelegateState { + view: view.clone(), + window: window.clone(), + resize_handler: None, + }; + println!("Created window and view"); return Ok(Window { window: window, - view: view + view: view, + delegate: WindowDelegate::new(ds), }); } } From 026be10b8be3b6dc6583732e6528710d991c9537 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 19 Dec 2015 11:44:36 +0100 Subject: [PATCH 28/50] Finally a visible window! --- examples/noise.rs | 3 ++- src/macos.rs | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index 55131b0..e0286db 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -10,7 +10,7 @@ fn main() { //let mut carry; //let mut seed = 0xbeefu32; - //let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; + let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; let window = match macos::Window::new("Noise Test - Press ESC to exit", WIDTH, @@ -25,6 +25,7 @@ fn main() { }; loop { + window.update(&buffer); /* for i in buffer.iter_mut() { noise = seed; diff --git a/src/macos.rs b/src/macos.rs index 7c1150f..d315991 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -59,6 +59,8 @@ impl WindowDelegate { let state: *mut libc::c_void = *this.get_ivar("glutinState"); let state = &mut *(state as *mut DelegateState); + println!("did_resize"); + //let _: () = msg_send![*state.context, update]; if let Some(handler) = state.resize_handler { @@ -75,6 +77,7 @@ impl WindowDelegate { // TODO: center the cursor if the window had mouse grab when it // lost focus + println!("became key window"); let state: *mut libc::c_void = *this.get_ivar("glutinState"); let state = state as *mut DelegateState; //(*state).pending_events.lock().unwrap().push_back(Focused(true)); @@ -93,7 +96,7 @@ impl WindowDelegate { static INIT: Once = ONCE_INIT; INIT.call_once(|| unsafe { - // Create new NSWindowDelegate + println!("Create new NSWindowDelegate"); let superclass = Class::get("NSObject").unwrap(); let mut decl = ClassDecl::new(superclass, "GlutinWindowDelegate").unwrap(); @@ -167,8 +170,9 @@ impl Window { return Err("Unable to create NSView"); } }; - + app.activateIgnoringOtherApps_(YES); + window.makeKeyAndOrderFront_(nil); let ds = DelegateState { view: view.clone(), @@ -202,6 +206,7 @@ impl Window { window.non_nil().map(|window| { let title = IdRef::new(NSString::alloc(nil).init_str(name)); + window.setLevel_(NSMainMenuWindowLevel as i64 + 1); window.setTitle_(*title); window.center(); window @@ -216,7 +221,14 @@ impl Window { }) } - pub fn update(_: &[u32]) { + pub fn update(&self, _: &[u32]) { + unsafe { + NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + NSAnyEventMask.bits(), + NSDate::distantPast(nil), + NSDefaultRunLoopMode, + YES); + } } pub fn get_keys() -> Option> { From 1455bd375e34f17d1dcdb72ef5a66de6d2d27f1e Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 19 Dec 2015 14:47:34 +0100 Subject: [PATCH 29/50] WIP on Mac version --- src/macos.rs | 126 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 100 insertions(+), 26 deletions(-) diff --git a/src/macos.rs b/src/macos.rs index d315991..03d6177 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -17,7 +17,7 @@ use objc::declare::ClassDecl; #[allow(unused_imports)] use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, - NSString, NSUInteger}; + NSString, NSUInteger}; use std::ops::Deref; @@ -25,6 +25,7 @@ pub struct Window { view: IdRef, window: IdRef, delegate: WindowDelegate, + view_delegate: ViewDelegate, } struct DelegateState { @@ -38,7 +39,14 @@ struct WindowDelegate { _this: IdRef, } -//sthou +struct ViewDelegateState { + view: IdRef, +} + +struct ViewDelegate { + state: Box, + _this: IdRef, +} impl WindowDelegate { /// Get the delegate class, initiailizing it neccessary @@ -46,9 +54,10 @@ impl WindowDelegate { use std::sync::{Once, ONCE_INIT}; extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { + println!("Should close"); unsafe { - let state: *mut libc::c_void = *this.get_ivar("glutinState"); - let state = state as *mut DelegateState; + //let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); + //let state = state as *mut DelegateState; //(*state).pending_events.lock().unwrap().push_back(Closed); } YES @@ -56,7 +65,7 @@ impl WindowDelegate { extern fn window_did_resize(this: &Object, _: Sel, _: id) { unsafe { - let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); let state = &mut *(state as *mut DelegateState); println!("did_resize"); @@ -67,7 +76,7 @@ impl WindowDelegate { let rect = NSView::frame(*state.view); let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32; (handler)((scale_factor * rect.size.width as f32) as u32, - (scale_factor * rect.size.height as f32) as u32); + (scale_factor * rect.size.height as f32) as u32); } } } @@ -78,7 +87,7 @@ impl WindowDelegate { // lost focus println!("became key window"); - let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); let state = state as *mut DelegateState; //(*state).pending_events.lock().unwrap().push_back(Focused(true)); } @@ -86,7 +95,7 @@ impl WindowDelegate { extern fn window_did_resign_key(this: &Object, _: Sel, _: id) { unsafe { - let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); let state = state as *mut DelegateState; //(*state).pending_events.lock().unwrap().push_back(Focused(false)); } @@ -98,21 +107,21 @@ impl WindowDelegate { INIT.call_once(|| unsafe { println!("Create new NSWindowDelegate"); let superclass = Class::get("NSObject").unwrap(); - let mut decl = ClassDecl::new(superclass, "GlutinWindowDelegate").unwrap(); - + let mut decl = ClassDecl::new(superclass, "minifb_window_delegate").unwrap(); + // Add callback methods decl.add_method(sel!(windowShouldClose:), - window_should_close as extern fn(&Object, Sel, id) -> BOOL); + window_should_close as extern fn(&Object, Sel, id) -> BOOL); decl.add_method(sel!(windowDidResize:), - window_did_resize as extern fn(&Object, Sel, id)); - + window_did_resize as extern fn(&Object, Sel, id)); + decl.add_method(sel!(windowDidBecomeKey:), - window_did_become_key as extern fn(&Object, Sel, id)); + window_did_become_key as extern fn(&Object, Sel, id)); decl.add_method(sel!(windowDidResignKey:), - window_did_resign_key as extern fn(&Object, Sel, id)); + window_did_resign_key as extern fn(&Object, Sel, id)); // Store internal state as user data - decl.add_ivar::<*mut libc::c_void>("glutinState"); + decl.add_ivar::<*mut libc::c_void>("minifb_window_state"); delegate_class = decl.register(); }); @@ -129,9 +138,11 @@ impl WindowDelegate { unsafe { let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]); - (&mut **delegate).set_ivar("glutinState", state_ptr as *mut libc::c_void); + (&mut **delegate).set_ivar("minifb_window_state", state_ptr as *mut libc::c_void); let _: () = msg_send![*state.window, setDelegate:*delegate]; + println!("Setup delegate"); + WindowDelegate { state: state, _this: delegate } } } @@ -146,6 +157,60 @@ impl Drop for WindowDelegate { } } +impl ViewDelegate { + fn class() -> *const Class { + use std::sync::{Once, ONCE_INIT}; + + extern fn draw_rect(this: &Object, _: Sel, _: id) { + println!("draw_rect"); + } + + static mut delegate_class: *const Class = 0 as *const Class; + static INIT: Once = ONCE_INIT; + + INIT.call_once(|| unsafe { + println!("Create new ViewDelegate"); + let superclass = Class::get("NSObject").unwrap(); + let mut decl = ClassDecl::new(superclass, "minifb_view_delegate").unwrap(); + + // Add callback methods + decl.add_method(sel!(drawRect:), + draw_rect as extern fn(&Object, Sel, id)); + // Store internal state as user data + decl.add_ivar::<*mut libc::c_void>("minifb_view_state"); + + delegate_class = decl.register(); + }); + + unsafe { + delegate_class + } + } + + fn new(state: ViewDelegateState) -> ViewDelegate { + let mut state = Box::new(state); + let state_ptr: *mut ViewDelegateState = &mut *state; + unsafe { + let delegate = IdRef::new(msg_send![ViewDelegate::class(), new]); + + (&mut **delegate).set_ivar("minifb_view_state", state_ptr as *mut libc::c_void); + let _: () = msg_send![*state.view, setDelegate:*delegate]; + + ViewDelegate { state: state, _this: delegate } + } + } +} + +impl Drop for ViewDelegate { + fn drop(&mut self) { + unsafe { + // Nil the views's delegate so it doesn't still reference us + let _: () = msg_send![*self.state.view, setDelegate:nil]; + } + } +} + + impl Window { pub fn new(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { @@ -170,7 +235,7 @@ impl Window { return Err("Unable to create NSView"); } }; - + app.activateIgnoringOtherApps_(YES); window.makeKeyAndOrderFront_(nil); @@ -180,29 +245,34 @@ impl Window { resize_handler: None, }; + let vs = ViewDelegateState { + view: view.clone(), + }; + println!("Created window and view"); return Ok(Window { window: window, view: view, delegate: WindowDelegate::new(ds), + view_delegate: ViewDelegate::new(vs), }); } } unsafe fn create_window(name: &str, width: usize, height: usize) -> Option { let masks = NSResizableWindowMask as NSUInteger | - NSClosableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger; + NSClosableWindowMask as NSUInteger | + NSTitledWindowMask as NSUInteger; let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)); let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( - frame, - masks, - NSBackingStoreBuffered, - NO, - )); + frame, + masks, + NSBackingStoreBuffered, + NO, + )); window.non_nil().map(|window| { let title = IdRef::new(NSString::alloc(nil).init_str(name)); @@ -223,11 +293,15 @@ impl Window { pub fn update(&self, _: &[u32]) { unsafe { - NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( NSAnyEventMask.bits(), NSDate::distantPast(nil), NSDefaultRunLoopMode, YES); + + if event != nil { + NSApp().sendEvent_(event); + } } } From 6fd476783ce69c23feb0d7b32e15d4ed256cce1f Mon Sep 17 00:00:00 2001 From: Krzysztof Kondrak Date: Tue, 22 Dec 2015 12:54:06 +0100 Subject: [PATCH 30/50] 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 31/50] 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 32/50] 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 33/50] 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; } From 3415eb269d7769af8d077bfc2baf98cd32a38865 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Wed, 2 Dec 2015 11:44:00 +0100 Subject: [PATCH 34/50] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1441c4b..f3654d0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ fn main() { Status ------ -Currently Mac, Windows has been Linux has been tested which are the supported platforms for now. +Currently Mac, Windows and Linux has been tested which are the supported platforms for now. Build instructions From b66fbe709a5dd8c6f54b8daae18503fdd9b7581d Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 18:49:03 +0100 Subject: [PATCH 35/50] WIP on Windows version --- src/lib.rs | 57 +++++++++++------- src/windows.rs | 159 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 180 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2713803..1d322d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ extern crate libc; - #[cfg(target_os = "macos")] #[macro_use] extern crate objc; @@ -34,6 +33,7 @@ pub enum Scale { } /// 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, @@ -48,26 +48,15 @@ pub enum Vsync { /// pub enum Key { - Space, - /// The '1' key over the letters. Key1, - /// The '2' key over the letters. Key2, - /// The '3' key over the letters. Key3, - /// The '4' key over the letters. Key4, - /// The '5' key over the letters. Key5, - /// The '6' key over the letters. Key6, - /// The '7' key over the letters. Key7, - /// The '8' key over the letters. Key8, - /// The '9' key over the letters. Key9, - /// The '0' key over the 'O' and 'P' keys. Key0, A, @@ -97,8 +86,6 @@ pub enum Key { Y, Z, - Escape, - F1, F2, F3, @@ -115,15 +102,42 @@ pub enum Key { F14, F15, - ArrowLeft, - ArrowRight, - ArrowUp, - ArrowDown, + Down, + Left, + Right, + Up, + Apostrophe, + Backslash, + Comma, + Equal, + LeftBracket, + Minus, + Period, + RightBracket, + Semicolon, + + Slash, Backspace, Delete, - Comma, - Semicollon + End, + Enter, + + Escape, + + Home, + Insert, + Menu, + + PageDown, + PageUp, + + Pause, + Space, + Tab, + CapsLock, + + Count = 80, } #[cfg(target_os = "windows")] @@ -133,7 +147,6 @@ pub use windows::*; #[cfg(target_os = "macos")] pub mod macos; -#[cfg(target_os = "mac")] +#[cfg(target_os = "macos")] pub use macos::*; - diff --git a/src/windows.rs b/src/windows.rs index c7d3a5c..8ecce9d 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); } _ => (), } @@ -91,12 +192,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(); @@ -162,13 +266,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 { @@ -182,3 +299,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 60e28c4a741c26a25e3490509b70aaee73dc0045 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 18:53:26 +0100 Subject: [PATCH 36/50] Removed double Key0 --- src/windows.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/windows.rs b/src/windows.rs index 8ecce9d..2b555fd 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 d4ca056adc9101e8c7eb86c0b691f085423d50c7 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 22:57:14 +0100 Subject: [PATCH 37/50] New version running with keyboard support --- examples/noise.rs | 31 +++--- src/lib.rs | 82 ++++++++-------- src/windows.rs | 233 +++++++++++++++++++++++++--------------------- 3 files changed, 180 insertions(+), 166 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index e0286db..523a02a 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -6,27 +6,15 @@ const WIDTH: usize = 640; const HEIGHT: usize = 360; fn main() { - //let mut noise; - //let mut carry; - //let mut seed = 0xbeefu32; + let mut noise; + let mut carry; + let mut seed = 0xbeefu32; let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; - let window = match macos::Window::new("Noise Test - Press ESC to exit", - WIDTH, - HEIGHT, - Scale::X1, - Vsync::No) { - Ok(window) => window, - Err(info) => { - println!("{}", info); - return; - } - }; + let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X1, Vsync::No).unwrap(); - loop { - window.update(&buffer); - /* + while window.update(&buffer) { for i in buffer.iter_mut() { noise = seed; noise >>= 3; @@ -38,6 +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 2b555fd..6ad1a13 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); @@ -192,14 +195,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(); @@ -252,26 +255,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; @@ -283,11 +299,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); } @@ -302,12 +323,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 1e477cd47f2e872dae79cf5292226f842c1e3c07 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Tue, 8 Dec 2015 23:08:52 +0100 Subject: [PATCH 38/50] 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 6ad1a13..60868d8 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; @@ -260,7 +258,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); @@ -269,7 +272,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 3e9ebe119bc79d9506901586aa975684c2a4c7f6 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 11:59:59 +0100 Subject: [PATCH 39/50] Updated various things * Only depend on Win32 APIs on Windows * Only depend on Cocoa/etc APIs on Mac * Bump to 0.2.0 * Added description, keywords, etc --- Cargo.toml | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 066f0ca..549cc2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "minifb" -version = "0.1.0" +version = "0.2.0" authors = ["Daniel Collin "] +description = "Cross-platform window setup for bitmap rendering" +keywords = ["windowing", "framebuffer"] +repository = "https://github.com/emoon/rust_minifb" build = "build.rs" [build-dependencies] @@ -9,8 +12,51 @@ gcc = "0.3.19" [dependencies] libc = "0.2" +time = "0.1.34" + +[target.x86_64-apple-darwin.dependencies] +cocoa = "0.2" +core-foundation = "0" +core-graphics = "0" + +[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" -time = "0.1.34" + +[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" From a996f9b865fd53f369257f6f29682c5a18a6ff73 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 12:00:19 +0100 Subject: [PATCH 40/50] Removed Windows part as it's fully in Rust now --- build.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.rs b/build.rs index d8317c4..d5ed662 100644 --- a/build.rs +++ b/build.rs @@ -8,8 +8,6 @@ fn main() { &["src/native/macosx/MacMiniFB.m", "src/native/macosx/OSXWindow.m", "src/native/macosx/OSXWindowFrameView.m"]); // MacOS - // } else if env.contains("windows") { - // gcc::compile_library("libminifb_native.a", &["src/native/windows/WinMiniFB.c"]); // Windows } else if env.contains("linux") { gcc::compile_library("libminifb_native.a", &["src/native/x11/X11MiniFB.c"]); // Unix } From 092965cb49d5fa57bcaa95369dc3a917ac39dd95 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 15:36:08 +0100 Subject: [PATCH 41/50] Removed VSync flag --- src/windows.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 58ce1a9..eb90143 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -7,7 +7,6 @@ extern crate gdi32; extern crate time; use Scale; -use Vsync; use Key; use KeyRepeat; @@ -232,7 +231,7 @@ pub struct Window { } impl Window { - fn open_window(name: &str, width: usize, height: usize, scale_factor: i32, _: Vsync) -> Option { + fn open_window(name: &str, width: usize, height: usize, scale_factor: i32) -> Option { unsafe { let class_name = to_wstring("minifb_window"); let class = WNDCLASSW { @@ -303,8 +302,7 @@ impl Window { pub fn new(name: &str, width: usize, height: usize, - scale: Scale, - vsync: Vsync) + scale: Scale) -> Result { unsafe { let scale_factor = Self::get_scale_factor(width, height, scale); From 946aa5ec1454312cf8dda20d7c95e5286fe42a00 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 15:37:50 +0100 Subject: [PATCH 42/50] Mac fixes * Removed hard coded global values for width, height, buffer - These are now tracked on a per window basis. * Fixed multi-window support * Various cleanup and fixes --- examples/noise.rs | 3 +- src/lib.rs | 35 +-- src/macos.rs | 414 ++++--------------------- src/native/macosx/MacMiniFB.m | 84 +++-- src/native/macosx/OSXWindow.h | 6 +- src/native/macosx/OSXWindow.m | 30 +- src/native/macosx/OSXWindowFrameView.h | 4 + src/native/macosx/OSXWindowFrameView.m | 13 +- 8 files changed, 148 insertions(+), 441 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index fd9c4a7..cecd3c3 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -15,8 +15,7 @@ fn main() { let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, - Scale::X1, - Vsync::No) + Scale::X1) .unwrap(); while window.is_open() && !window.is_key_down(Key::Escape) { diff --git a/src/lib.rs b/src/lib.rs index 7881404..82c8bbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,3 @@ -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; - /// 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. @@ -32,20 +21,6 @@ pub enum Scale { 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, -} - /// Used for is_key_pressed and get_keys_pressed() to indicated if repeat of presses is wanted #[derive(PartialEq, Clone, Copy)] pub enum KeyRepeat { @@ -172,13 +147,15 @@ pub enum Key { Count = 103, } +extern crate libc; + #[cfg(target_os = "windows")] 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/macos.rs b/src/macos.rs index 03d6177..eb34d18 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,377 +1,93 @@ #![cfg(target_os = "macos")] -use Scale; -use Vsync; -use Key; +use {Scale, Key, KeyRepeat}; -use libc; -//use cocoa::appkit; -use cocoa::appkit::*; -//use cocoa::appkit::NSEventSubtype::*; +use libc::{c_void, c_char, c_uchar}; +use std::ffi::{CString}; +use std::ptr; -#[allow(unused_imports)] -use cocoa::base::{id, nil}; -#[allow(unused_imports)] -use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; -use objc::declare::ClassDecl; - -#[allow(unused_imports)] -use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, - NSString, NSUInteger}; - -use std::ops::Deref; +#[link(name = "Cocoa", kind = "framework")] +extern { + fn mfb_open(name: *const c_char, width: u32, height: u32, scale: u32) -> *mut c_void; + fn mfb_close(window: *mut c_void); + fn mfb_update(window: *mut c_void, buffer: *const c_uchar); +} pub struct Window { - view: IdRef, - window: IdRef, - delegate: WindowDelegate, - view_delegate: ViewDelegate, + window_handle: *mut c_void, } -struct DelegateState { - view: IdRef, - window: IdRef, - resize_handler: Option, -} - -struct WindowDelegate { - state: Box, - _this: IdRef, -} - -struct ViewDelegateState { - view: IdRef, -} - -struct ViewDelegate { - state: Box, - _this: IdRef, -} - -impl WindowDelegate { - /// Get the delegate class, initiailizing it neccessary - fn class() -> *const Class { - use std::sync::{Once, ONCE_INIT}; - - extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { - println!("Should close"); - unsafe { - //let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - //let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Closed); - } - YES - } - - extern fn window_did_resize(this: &Object, _: Sel, _: id) { - unsafe { - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = &mut *(state as *mut DelegateState); - - println!("did_resize"); - - //let _: () = msg_send![*state.context, update]; - - if let Some(handler) = state.resize_handler { - let rect = NSView::frame(*state.view); - let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32; - (handler)((scale_factor * rect.size.width as f32) as u32, - (scale_factor * rect.size.height as f32) as u32); - } - } - } - - extern fn window_did_become_key(this: &Object, _: Sel, _: id) { - unsafe { - // TODO: center the cursor if the window had mouse grab when it - // lost focus - - println!("became key window"); - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Focused(true)); - } - } - - extern fn window_did_resign_key(this: &Object, _: Sel, _: id) { - unsafe { - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Focused(false)); - } - } - - static mut delegate_class: *const Class = 0 as *const Class; - static INIT: Once = ONCE_INIT; - - INIT.call_once(|| unsafe { - println!("Create new NSWindowDelegate"); - let superclass = Class::get("NSObject").unwrap(); - let mut decl = ClassDecl::new(superclass, "minifb_window_delegate").unwrap(); - - // Add callback methods - decl.add_method(sel!(windowShouldClose:), - window_should_close as extern fn(&Object, Sel, id) -> BOOL); - decl.add_method(sel!(windowDidResize:), - window_did_resize as extern fn(&Object, Sel, id)); - - decl.add_method(sel!(windowDidBecomeKey:), - window_did_become_key as extern fn(&Object, Sel, id)); - decl.add_method(sel!(windowDidResignKey:), - window_did_resign_key as extern fn(&Object, Sel, id)); - - // Store internal state as user data - decl.add_ivar::<*mut libc::c_void>("minifb_window_state"); - - delegate_class = decl.register(); - }); - - unsafe { - delegate_class - } - } - - fn new(state: DelegateState) -> WindowDelegate { - // Box the state so we can give a pointer to it - let mut state = Box::new(state); - let state_ptr: *mut DelegateState = &mut *state; - unsafe { - let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]); - - (&mut **delegate).set_ivar("minifb_window_state", state_ptr as *mut libc::c_void); - let _: () = msg_send![*state.window, setDelegate:*delegate]; - - println!("Setup delegate"); - - WindowDelegate { state: state, _this: delegate } - } - } -} - -impl Drop for WindowDelegate { - fn drop(&mut self) { - unsafe { - // Nil the window's delegate so it doesn't still reference us - let _: () = msg_send![*self.state.window, setDelegate:nil]; - } - } -} - -impl ViewDelegate { - fn class() -> *const Class { - use std::sync::{Once, ONCE_INIT}; - - extern fn draw_rect(this: &Object, _: Sel, _: id) { - println!("draw_rect"); - } - - static mut delegate_class: *const Class = 0 as *const Class; - static INIT: Once = ONCE_INIT; - - INIT.call_once(|| unsafe { - println!("Create new ViewDelegate"); - let superclass = Class::get("NSObject").unwrap(); - let mut decl = ClassDecl::new(superclass, "minifb_view_delegate").unwrap(); - - // Add callback methods - decl.add_method(sel!(drawRect:), - draw_rect as extern fn(&Object, Sel, id)); - // Store internal state as user data - decl.add_ivar::<*mut libc::c_void>("minifb_view_state"); - - delegate_class = decl.register(); - }); - - unsafe { - delegate_class - } - } - - fn new(state: ViewDelegateState) -> ViewDelegate { - let mut state = Box::new(state); - let state_ptr: *mut ViewDelegateState = &mut *state; - unsafe { - let delegate = IdRef::new(msg_send![ViewDelegate::class(), new]); - - (&mut **delegate).set_ivar("minifb_view_state", state_ptr as *mut libc::c_void); - let _: () = msg_send![*state.view, setDelegate:*delegate]; - - ViewDelegate { state: state, _this: delegate } - } - } -} - -impl Drop for ViewDelegate { - fn drop(&mut self) { - unsafe { - // Nil the views's delegate so it doesn't still reference us - let _: () = msg_send![*self.state.view, setDelegate:nil]; - } - } -} - - - impl Window { - pub fn new(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { - unsafe { - let app = match Self::create_app() { - Some(app) => app, - None => { - return Err("Couldn't create NSApplication"); - }, - }; - - let window = match Self::create_window(name, width, height) { - Some(window) => window, - None => { - return Err("Unable to create NSWindow"); - } - }; - - let view = match Self::create_view(*window) { - Some(view) => view, - None => { - return Err("Unable to create NSView"); - } - }; - - app.activateIgnoringOtherApps_(YES); - window.makeKeyAndOrderFront_(nil); - - let ds = DelegateState { - view: view.clone(), - window: window.clone(), - resize_handler: None, - }; - - let vs = ViewDelegateState { - view: view.clone(), - }; - - println!("Created window and view"); - - return Ok(Window { - window: window, - view: view, - delegate: WindowDelegate::new(ds), - view_delegate: ViewDelegate::new(vs), - }); - } - } - - unsafe fn create_window(name: &str, width: usize, height: usize) -> Option { - let masks = NSResizableWindowMask as NSUInteger | - NSClosableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger; - - let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)); - - let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( - frame, - masks, - NSBackingStoreBuffered, - NO, - )); - - window.non_nil().map(|window| { - let title = IdRef::new(NSString::alloc(nil).init_str(name)); - window.setLevel_(NSMainMenuWindowLevel as i64 + 1); - window.setTitle_(*title); - window.center(); - window - }) - } - - unsafe fn create_view(window: id) -> Option { - let view = IdRef::new(NSView::alloc(nil).init()); - view.non_nil().map(|view| { - window.setContentView_(*view); - view - }) - } - - pub fn update(&self, _: &[u32]) { - unsafe { - let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits(), - NSDate::distantPast(nil), - NSDefaultRunLoopMode, - YES); - - if event != nil { - NSApp().sendEvent_(event); + pub fn new(name: &str, + width: usize, + height: usize, + scale: Scale) + -> Result { + 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, scale as u32); + + if handle == ptr::null_mut() { + return Err("Unable to open Window"); + } + + Ok(Window { window_handle: handle }) } } - pub fn get_keys() -> Option> { - return None; + pub fn update(&mut self, buffer: &[u32]) { + unsafe { + mfb_update(self.window_handle, buffer.as_ptr() as *const u8); + } } - pub fn is_esc_pressed() -> bool { + pub fn get_keys(&self) -> Option> { + None + } + + pub fn get_keys_pressed(&self, _: KeyRepeat) -> Option> { + None + } + + #[inline] + pub fn is_key_down(&self, _: Key) -> bool { false } - pub fn close() { - + #[inline] + pub fn set_key_repeat_delay(&mut self, _: f32) { } - fn create_app() -> Option { - unsafe { - let app = NSApp(); - if app == nil { - None - } else { - app.setActivationPolicy_(NSApplicationActivationPolicyRegular); - Some(app) - } - } + #[inline] + pub fn set_key_repeat_rate(&mut self, _: f32) { + } + + pub fn key_pressed(&self, _: usize, _: KeyRepeat) -> bool { + false + } + + pub fn is_key_pressed(&self, _: Key, _: KeyRepeat) -> bool { + false + } + + #[inline] + pub fn is_open(&self) -> bool { + true } } - - -struct IdRef(id); - -impl IdRef { - fn new(i: id) -> IdRef { - IdRef(i) - } - - #[allow(dead_code)] - fn retain(i: id) -> IdRef { - if i != nil { - let _: id = unsafe { msg_send![i, retain] }; - } - IdRef(i) - } - - fn non_nil(self) -> Option { - if self.0 == nil { None } else { Some(self) } - } -} - -impl Drop for IdRef { +impl Drop for Window { fn drop(&mut self) { - if self.0 != nil { - let _: () = unsafe { msg_send![self.0, release] }; + unsafe { + mfb_close(self.window_handle); } } } -impl Deref for IdRef { - type Target = id; - fn deref<'a>(&'a self) -> &'a id { - &self.0 - } -} - -impl Clone for IdRef { - fn clone(&self) -> IdRef { - if self.0 != nil { - let _: id = unsafe { msg_send![self.0, retain] }; - } - IdRef(self.0) - } -} diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index 83742d1..ae2a60c 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -3,83 +3,74 @@ #include #include -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void* g_updateBuffer = 0; -int g_width = 0; -int g_height = 0; -static NSWindow* window_; +static bool s_init = false; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_open(const char* name, int width, int height) +void* mfb_open(const char* name, int width, int height, int scale) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - g_width = width; - g_height = height; - - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + if (!s_init) { + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + s_init = true; + } unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask; NSRect rectangle = NSMakeRect(0, 0, width, height); - window_ = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; + OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; - if (!window_) + if (!window) return 0; - [window_ setTitle:[NSString stringWithUTF8String:name]]; - [window_ setReleasedWhenClosed:NO]; - [window_ performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + window->draw_buffer = malloc(width * height * 4); - [window_ center]; + if (!window->draw_buffer) + return 0; + + window->width = width; + window->height = height; + window->scale = scale; + + [window updateSize]; + + [window setTitle:[NSString stringWithUTF8String:name]]; + [window setReleasedWhenClosed:NO]; + [window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + + [window center]; [NSApp activateIgnoringOtherApps:YES]; [pool drain]; - return 1; + return window; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mfb_close() +void mfb_close(void* win) { + NSWindow* window = (NSWindow*)win; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - if (window_) - [window_ close]; + if (window) + [window close]; [pool drain]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static int updateEvents() +static int update_events() { int state = 0; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event) - { - switch ([event type]) - { - case NSKeyDown: - case NSKeyUp: - { - state = -1; - break; - } - - default : - { - [NSApp sendEvent:event]; - break; - } - } - } + [NSApp sendEvent:event]; [pool release]; return state; @@ -87,10 +78,13 @@ static int updateEvents() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_update(void* buffer) +int mfb_update(void* window, void* buffer) { - g_updateBuffer = buffer; - int state = updateEvents(); - [[window_ contentView] setNeedsDisplay:YES]; + OSXWindow* win = (OSXWindow*)window; + memcpy(win->draw_buffer, buffer, win->width * win->height * 4); + + //g_updateBuffer = buffer; + int state = update_events(); + [[win contentView] setNeedsDisplay:YES]; return state; } diff --git a/src/native/macosx/OSXWindow.h b/src/native/macosx/OSXWindow.h index 5314f9c..2541897 100644 --- a/src/native/macosx/OSXWindow.h +++ b/src/native/macosx/OSXWindow.h @@ -1,10 +1,12 @@ #import -// @class OSXWindowFrameView; - @interface OSXWindow : NSWindow { NSView* childContentView; + @public int width; + @public int height; + @public int scale; + @public void* draw_buffer; } @end diff --git a/src/native/macosx/OSXWindow.m b/src/native/macosx/OSXWindow.m index a910e10..0938eba 100644 --- a/src/native/macosx/OSXWindow.m +++ b/src/native/macosx/OSXWindow.m @@ -57,6 +57,8 @@ NSSize newFrameSize = [frameView bounds].size; newFrameSize.width += sizeDelta.width; newFrameSize.height += sizeDelta.height; + + printf("conten size\n"); [super setContentSize:newFrameSize]; } @@ -79,18 +81,22 @@ NSRect bounds = [self frame]; bounds.origin = NSZeroPoint; - OSXWindowFrameView *frameView = [super contentView]; + printf("view size\n"); + + OSXWindowFrameView* frameView = [super contentView]; if (!frameView) { frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease]; - + frameView->width = width; + frameView->height = height; + frameView->draw_buffer = draw_buffer; + frameView->scale = scale; [super setContentView:frameView]; } if (childContentView) - { [childContentView removeFromSuperview]; - } + childContentView = aView; [childContentView setFrame:[self contentRectForFrameRect:bounds]]; [childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; @@ -133,4 +139,20 @@ 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; + } + + printf("UpdateSize %d %d - %d\n", width, height, scale); +} + @end diff --git a/src/native/macosx/OSXWindowFrameView.h b/src/native/macosx/OSXWindowFrameView.h index 96408e9..c12bc52 100644 --- a/src/native/macosx/OSXWindowFrameView.h +++ b/src/native/macosx/OSXWindowFrameView.h @@ -2,6 +2,10 @@ @interface OSXWindowFrameView : NSView { + @public int scale; + @public int width; + @public int height; + @public void* draw_buffer; } @end diff --git a/src/native/macosx/OSXWindowFrameView.m b/src/native/macosx/OSXWindowFrameView.m index 1f01a41..0f0d67b 100644 --- a/src/native/macosx/OSXWindowFrameView.m +++ b/src/native/macosx/OSXWindowFrameView.m @@ -2,10 +2,6 @@ @implementation OSXWindowFrameView -extern void* g_updateBuffer; -extern int g_width; -extern int g_height; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (NSRect)resizeRect @@ -27,21 +23,18 @@ extern int g_height; - (void)drawRect:(NSRect)rect { - if (!g_updateBuffer) - return; - CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_updateBuffer, g_width * g_height * 4, NULL); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, draw_buffer, width * height * 4, NULL); - CGImageRef img = CGImageCreate(g_width, g_height, 8, 32, g_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + CGImageRef img = CGImageCreate(width, height, 8, 32, width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, provider, NULL, false, kCGRenderingIntentDefault); CGColorSpaceRelease(space); CGDataProviderRelease(provider); - CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img); + CGContextDrawImage(context, CGRectMake(0, 0, width, height), img); CGImageRelease(img); } From 8e9f8d33a1bde3cf0fbbbdcd58b55a04cf333508 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 15:49:53 +0100 Subject: [PATCH 43/50] Add support for scaling --- examples/noise.rs | 3 +-- src/macos.rs | 28 ++++++++++++++++++-------- src/native/macosx/MacMiniFB.m | 2 +- src/native/macosx/OSXWindowFrameView.m | 2 +- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index cecd3c3..c536b94 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -15,7 +15,7 @@ fn main() { let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, - Scale::X1) + Scale::X2) .unwrap(); while window.is_open() && !window.is_key_down(Key::Escape) { @@ -41,7 +41,6 @@ fn main() { } }); - window.update(&buffer); } } diff --git a/src/macos.rs b/src/macos.rs index eb34d18..e2ecf3a 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -8,7 +8,7 @@ use std::ptr; #[link(name = "Cocoa", kind = "framework")] extern { - fn mfb_open(name: *const c_char, width: u32, height: u32, scale: u32) -> *mut c_void; + 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); } @@ -18,11 +18,7 @@ pub struct Window { } impl Window { - pub fn new(name: &str, - width: usize, - height: usize, - scale: Scale) - -> Result { + pub fn new(name: &str, width: usize, height: usize, scale: Scale) -> Result { let n = match CString::new(name) { Err(_) => { println!("Unable to convert {} to c_string", name); @@ -32,12 +28,12 @@ impl Window { }; unsafe { - let handle = mfb_open(n.as_ptr(), width as u32, height as u32, scale as u32); + 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 }) } } @@ -81,6 +77,22 @@ impl Window { pub fn is_open(&self) -> bool { true } + + unsafe fn get_scale_factor(_: usize, _: 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 => { + 1 + } + }; + + return factor; + } } impl Drop for Window { diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index ae2a60c..8b0ab3f 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -19,7 +19,7 @@ void* mfb_open(const char* name, int width, int height, int scale) unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask; - NSRect rectangle = NSMakeRect(0, 0, width, height); + NSRect rectangle = NSMakeRect(0, 0, width * scale, height * scale); OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; if (!window) diff --git a/src/native/macosx/OSXWindowFrameView.m b/src/native/macosx/OSXWindowFrameView.m index 0f0d67b..09b0b65 100644 --- a/src/native/macosx/OSXWindowFrameView.m +++ b/src/native/macosx/OSXWindowFrameView.m @@ -34,7 +34,7 @@ CGColorSpaceRelease(space); CGDataProviderRelease(provider); - CGContextDrawImage(context, CGRectMake(0, 0, width, height), img); + CGContextDrawImage(context, CGRectMake(0, 0, width * scale, height * scale), img); CGImageRelease(img); } From 867daf1ca9ccb3517b9b5020248a203fb261cf7d Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 18:55:46 +0100 Subject: [PATCH 44/50] Added working keyboard support for Mac Also moved all code out from windows.rs to a shared key_handler impl --- src/key_handler.rs | 123 ++++++++++++++++++++ src/lib.rs | 14 ++- src/macos.rs | 209 +++++++++++++++++++++++++++++++--- src/native/macosx/MacMiniFB.m | 36 ++++++ src/native/macosx/OSXWindow.h | 3 + src/native/macosx/OSXWindow.m | 72 +++++++++++- src/windows.rs | 35 +----- 7 files changed, 440 insertions(+), 52 deletions(-) create mode 100644 src/key_handler.rs diff --git a/src/key_handler.rs b/src/key_handler.rs new file mode 100644 index 0000000..1f6233c --- /dev/null +++ b/src/key_handler.rs @@ -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> { + 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) + } + + 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> { + 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); + } +} diff --git a/src/lib.rs b/src/lib.rs index 82c8bbb..eb2ef5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub enum KeyRepeat { No, } +#[derive(PartialEq, Clone, Copy)] pub enum Key { Key0 = 0, Key1 = 1, @@ -144,11 +145,22 @@ pub enum Key { NumPadPlus, NumPadEnter, - Count = 103, + LeftAlt, + RightAlt, + + LeftSuper, + RightSuper, + + /// Used when an Unknown key has been pressed + Unknown, + + Count = 107, } extern crate libc; +pub mod key_handler; + #[cfg(target_os = "windows")] pub mod windows; #[cfg(target_os = "windows")] diff --git a/src/macos.rs b/src/macos.rs index e2ecf3a..afa6de7 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,20 +1,171 @@ #![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 { @@ -34,51 +185,58 @@ impl Window { return Err("Unable to open Window"); } - Ok(Window { window_handle: handle }) + 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> { - None - } - - pub fn get_keys_pressed(&self, _: KeyRepeat) -> Option> { - None + self.key_handler.get_keys() } #[inline] - pub fn is_key_down(&self, _: Key) -> bool { - false + pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { + self.key_handler.get_keys_pressed(repeat) } #[inline] - pub fn set_key_repeat_delay(&mut self, _: f32) { + pub fn is_key_down(&self, key: Key) -> bool { + self.key_handler.is_key_down(key) } #[inline] - pub fn set_key_repeat_rate(&mut self, _: f32) { + pub fn set_key_repeat_delay(&mut self, delay: f32) { + self.key_handler.set_key_repeat_delay(delay) } - pub fn key_pressed(&self, _: usize, _: KeyRepeat) -> bool { - false + #[inline] + pub fn set_key_repeat_rate(&mut self, rate: f32) { + self.key_handler.set_key_repeat_rate(rate) } - pub fn is_key_pressed(&self, _: Key, _: KeyRepeat) -> bool { - false + #[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 { - true + unsafe { mfb_should_close(self.window_handle) == 0 } } - unsafe fn get_scale_factor(_: usize, _: usize, scale: Scale) -> i32 { + unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 { let factor: i32 = match scale { Scale::X1 => 1, Scale::X2 => 2, @@ -87,7 +245,24 @@ impl Window { Scale::X16 => 16, Scale::X32 => 32, Scale::FitScreen => { - 1 + 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 } }; diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index 8b0ab3f..4117534 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -5,6 +5,7 @@ static bool s_init = false; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void* mfb_open(const char* name, int width, int height, int scale) @@ -25,6 +26,12 @@ void* mfb_open(const char* name, int width, int height, int scale) if (!window) return 0; + NSRect e = [[NSScreen mainScreen] frame]; + int H = (int)e.size.height; + int W = (int)e.size.width; + + printf("H %d W %d\n", W, H); + window->draw_buffer = malloc(width * height * 4); if (!window->draw_buffer) @@ -33,6 +40,7 @@ void* mfb_open(const char* name, int width, int height, int scale) window->width = width; window->height = height; window->scale = scale; + window->key_callback = 0; [window updateSize]; @@ -88,3 +96,31 @@ int mfb_update(void* window, void* buffer) [[win contentView] setNeedsDisplay:YES]; return state; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_should_close(void* window) +{ + OSXWindow* win = (OSXWindow*)window; + return win->should_close; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +uint32_t mfb_get_screen_size() +{ + NSRect e = [[NSScreen mainScreen] frame]; + uint32_t w = (uint32_t)e.size.width; + uint32_t h = (uint32_t)e.size.height; + return (w << 16) | h; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mfb_set_key_callback(void* window, void* rust_data, void (*key_callback)(void* user_data, int key, int state)) +{ + OSXWindow* win = (OSXWindow*)window; + win->key_callback = key_callback; + win->rust_data = rust_data; +} + diff --git a/src/native/macosx/OSXWindow.h b/src/native/macosx/OSXWindow.h index 2541897..48b7532 100644 --- a/src/native/macosx/OSXWindow.h +++ b/src/native/macosx/OSXWindow.h @@ -3,10 +3,13 @@ @interface OSXWindow : NSWindow { NSView* childContentView; + @public void (*key_callback)(void* user_data, int key, int state); @public int width; @public int height; @public int scale; @public void* draw_buffer; + @public void* rust_data; + @public bool should_close; } @end diff --git a/src/native/macosx/OSXWindow.m b/src/native/macosx/OSXWindow.m index 0938eba..c406f55 100644 --- a/src/native/macosx/OSXWindow.m +++ b/src/native/macosx/OSXWindow.m @@ -58,19 +58,87 @@ newFrameSize.width += sizeDelta.width; newFrameSize.height += sizeDelta.height; - printf("conten size\n"); - [super setContentSize:newFrameSize]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +-(void)flagsChanged:(NSEvent *)event +{ + const uint32_t flags = [event modifierFlags]; + + // Left Shift + key_callback(rust_data, 0x38, flags == 0x20102 ? 1 : 0); + + // RightShift + key_callback(rust_data, 0x3c, flags == 0x20104 ? 1 : 0); + + // Left Ctrl + key_callback(rust_data, 0x3b, flags == 0x40101 ? 1 : 0); + + // Right Ctrl + key_callback(rust_data, 0x3b, flags == 0x42101 ? 1 : 0); + + // Left Alt + key_callback(rust_data, 0x3a, flags == 0x80120 ? 1 : 0); + + // Right Super + key_callback(rust_data, 0x3d, flags == 0x80140 ? 1 : 0); + + // Left Super + key_callback(rust_data, 0x37, flags == 0x100108 ? 1 : 0); + + // Right Super + key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)keyDown:(NSEvent *)event +{ + // Cmd+Q always closes app + if ([event.characters.uppercaseString isEqualToString:@"Q"] && ([event modifierFlags] & NSCommandKeyMask)) { + [self performClose:self]; + return; + } + + if (key_callback) { + key_callback(rust_data, [event keyCode], 1); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)keyUp:(NSEvent *)event +{ + if (key_callback) { + key_callback(rust_data, [event keyCode], 0); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + - (void)mainWindowChanged:(NSNotification *)aNotification { } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)windowWillClose:(NSNotification *)notification +{ + should_close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)windowShouldClose:(id)sender +{ + should_close = true; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + - (void)setContentView:(NSView *)aView { if ([childContentView isEqualTo:aView]) diff --git a/src/windows.rs b/src/windows.rs index eb90143..257f2c6 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -6,9 +6,7 @@ extern crate winapi; extern crate gdi32; extern crate time; -use Scale; -use Key; -use KeyRepeat; +use {Scale, Key, KeyRepeat, KeyHandler}; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -217,17 +215,12 @@ fn to_wstring(str: &str) -> Vec { 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, + key_handler: KeyHandler, } impl Window { @@ -316,13 +309,8 @@ impl Window { let window = 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(), + key_handler: KeyHandler::new(), is_open: true, scale_factor: scale_factor, width: width as i32, @@ -415,23 +403,6 @@ 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(); From 66c36fb6c6a0c475a8fd93919cf8239c53ca8dc7 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 19:40:41 +0100 Subject: [PATCH 45/50] Proper error handling if window create fail --- examples/noise.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index c536b94..4279f15 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -12,11 +12,13 @@ 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::X2) - .unwrap(); + let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X2) { + Ok(win) => win, + Err(err) => { + println!("Unable to create window {}", err); + return; + } + }; while window.is_open() && !window.is_key_down(Key::Escape) { for i in buffer.iter_mut() { From a7e1df51ad6d782a72d902f609a3f1c5271e81aa Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 19:40:56 +0100 Subject: [PATCH 46/50] Use the new key_handler code --- src/windows.rs | 267 +++++++++++++++++++++---------------------------- 1 file changed, 115 insertions(+), 152 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 257f2c6..876427a 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -6,7 +6,9 @@ extern crate winapi; extern crate gdi32; extern crate time; -use {Scale, Key, KeyRepeat, KeyHandler}; +use {Scale, Key, KeyRepeat}; + +use key_handler::KeyHandler; use std::ptr; use std::os::windows::ffi::OsStrExt; @@ -29,105 +31,105 @@ struct BitmapInfo { fn update_key_state(window: &mut Window, wparam: u32, state: bool) { match wparam & 0x1ff { - 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, - 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, - 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, - 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, + 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), _ => (), } } @@ -300,7 +302,7 @@ impl Window { unsafe { let scale_factor = Self::get_scale_factor(width, height, scale); - let handle = Self::open_window(name, width, height, scale_factor, vsync); + let handle = Self::open_window(name, width, height, scale_factor); if handle.is_none() { return Err("Unable to create Window"); @@ -321,78 +323,37 @@ impl Window { } } + #[inline] 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) + self.key_handler.get_keys() } + #[inline] 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) + self.key_handler.get_keys_pressed(repeat) } #[inline] pub fn is_key_down(&self, key: Key) -> bool { - return self.keys[key as usize]; + self.key_handler.is_key_down(key) } #[inline] pub fn set_key_repeat_delay(&mut self, delay: f32) { - self.key_repeat_delay = delay; + self.key_handler.set_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; + self.key_handler.set_key_repeat_rate(rate) } + #[inline] pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool { - return Self::key_pressed(self, key as usize, repeat); + self.key_handler.is_key_pressed(key, repeat) } + #[inline] pub fn is_open(&self) -> bool { return self.is_open @@ -403,6 +364,8 @@ impl Window { let mut msg = mem::uninitialized(); let window = self.window.unwrap(); + self.key_handler.update(); + // TODO: Optimize self.buffer = buffer.iter().cloned().collect(); From 4b87f681257dff9573b6da4008449d51771dc822 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 21:26:08 +0100 Subject: [PATCH 47/50] Moved stuff around + documentation --- Cargo.toml | 5 - examples/noise.rs | 6 +- src/lib.rs | 174 ++++++++++++++++++++++++-- src/native/macosx/MacMiniFB.m | 6 - src/native/macosx/OSXWindow.m | 4 - src/{macos.rs => os/macos/mod.rs} | 0 src/os/mod.rs | 4 + src/{windows.rs => os/windows/mod.rs} | 0 8 files changed, 172 insertions(+), 27 deletions(-) rename src/{macos.rs => os/macos/mod.rs} (100%) create mode 100644 src/os/mod.rs rename src/{windows.rs => os/windows/mod.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 549cc2a..108fc04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,6 @@ gcc = "0.3.19" libc = "0.2" time = "0.1.34" -[target.x86_64-apple-darwin.dependencies] -cocoa = "0.2" -core-foundation = "0" -core-graphics = "0" - [target.x86_64-pc-windows-msvc.dependencies] user32-sys = "0.1.2" winapi = "0.2.4" diff --git a/examples/noise.rs b/examples/noise.rs index 4279f15..ab39f11 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -33,9 +33,9 @@ fn main() { *i = (noise << 16) | (noise << 8) | noise; } - window.get_keys_pressed(KeyRepeat::No).map(|keys| { - for t in keys.iter() { - match *t { + window.get_keys().map(|keys| { + for t in keys { + match t { Key::W => println!("holding w!"), Key::T => println!("holding t!"), _ => (), diff --git a/src/lib.rs b/src/lib.rs index eb2ef5f..31dc551 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,15 +159,171 @@ pub enum Key { extern crate libc; -pub mod key_handler; - -#[cfg(target_os = "windows")] -pub mod windows; -#[cfg(target_os = "windows")] -pub use windows::*; +pub mod os; +mod key_handler; #[cfg(target_os = "macos")] -pub mod macos; -#[cfg(target_os = "macos")] -pub use macos::*; +use self::os::macos as imp; +#[cfg(target_os = "windows")] +use self::os::windows as imp; +/// Window used for displaying a 32-bit RGB buffer +pub struct Window(imp::Window); + +impl Window { + /// + /// Opens up a new window + /// + /// ```ignore + /// let mut window = match Window::new("Test", 640, 400, Scale::X1) { + /// Ok(win) => win, + /// Err(err) => { + /// println!("Unable to create window {}", err); + /// return; + /// } + ///}; + /// ``` + pub fn new(name: &str, width: usize, height: usize, scale: Scale) -> Result { + imp::Window::new(name, width, height, scale) + } + + /// + /// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least + /// the size of the created window + /// + /// # Examples + /// + /// ```ignore + /// let mut buffer: [u32; 640 * 400] = [0; 640 * 400]; + /// + /// let mut window = match Window::new("Test", 640, 400, Scale::X1).unwrap(); + /// + /// window.update(&buffer); + /// ``` + pub fn update(&mut self, buffer: &[u32]) { + self.0.update(buffer) + } + + /// + /// Checks if the window is still open. A window can be closed by the user (by for example + /// pressing the close button on the window) It's up to the user to make sure that this is + /// being checked and take action depending on the state. + /// + /// # Examples + /// + /// ```ignore + /// while window.is_open() { + /// window.update(...) + /// } + /// ``` + #[inline] + pub fn is_open(&self) -> bool { + self.0.is_open() + } + + /// + /// Get the current keys that are down. + /// + /// # Examples + /// + /// ```ignore + /// window.get_keys().map(|keys| { + /// for t in keys { + /// match t { + /// Key::W => println!("holding w"), + /// Key::T => println!("holding t"), + /// _ => (), + /// } + /// } + /// }); + /// ``` + #[inline] + pub fn get_keys(&self) -> Option> { + 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> { + 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) + } +} diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index 4117534..c761c42 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -26,12 +26,6 @@ void* mfb_open(const char* name, int width, int height, int scale) if (!window) return 0; - NSRect e = [[NSScreen mainScreen] frame]; - int H = (int)e.size.height; - int W = (int)e.size.width; - - printf("H %d W %d\n", W, H); - window->draw_buffer = malloc(width * height * 4); if (!window->draw_buffer) diff --git a/src/native/macosx/OSXWindow.m b/src/native/macosx/OSXWindow.m index c406f55..fb5f196 100644 --- a/src/native/macosx/OSXWindow.m +++ b/src/native/macosx/OSXWindow.m @@ -149,8 +149,6 @@ NSRect bounds = [self frame]; bounds.origin = NSZeroPoint; - printf("view size\n"); - OSXWindowFrameView* frameView = [super contentView]; if (!frameView) { @@ -219,8 +217,6 @@ frameView->draw_buffer = draw_buffer; frameView->scale = scale; } - - printf("UpdateSize %d %d - %d\n", width, height, scale); } @end diff --git a/src/macos.rs b/src/os/macos/mod.rs similarity index 100% rename from src/macos.rs rename to src/os/macos/mod.rs diff --git a/src/os/mod.rs b/src/os/mod.rs new file mode 100644 index 0000000..0669925 --- /dev/null +++ b/src/os/mod.rs @@ -0,0 +1,4 @@ +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "windows")] +pub mod windows; diff --git a/src/windows.rs b/src/os/windows/mod.rs similarity index 100% rename from src/windows.rs rename to src/os/windows/mod.rs From c5f45664ef2d8c90885754db4ac7f7d8aabc4713 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 21:42:37 +0100 Subject: [PATCH 48/50] More documentation --- src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 31dc551..2eb82b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub enum KeyRepeat { 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, @@ -167,7 +168,28 @@ use self::os::macos as imp; #[cfg(target_os = "windows")] use self::os::windows as imp; -/// Window used for displaying a 32-bit RGB buffer +/// +/// Window used for displaying a 32-bit RGB buffer. Here is a small example on how to use it: +/// (without error checking +/// +/// ```ignore +/// +/// const WIDTH: usize = 640; +/// const HEIGHT: usize = 360; +/// +/// let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT]; +/// +/// let mut window = match Window::new("Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X1).unwrap() +/// +/// while window.is_open() && !window.is_key_down(Key::Escape) { +/// for i in buffer.iter_mut() { +/// *i = 0; // write something interesting here +/// } +/// window.update(&buffer); +/// } +/// ``` +/// + pub struct Window(imp::Window); impl Window { From 46982b4795917d0d5692cd4c5e4cca9eb484b00b Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 21:42:50 +0100 Subject: [PATCH 49/50] Added documentation link --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 108fc04..0cd0fb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Daniel Collin "] description = "Cross-platform window setup for bitmap rendering" keywords = ["windowing", "framebuffer"] repository = "https://github.com/emoon/rust_minifb" +documentation = "http://prodbg.com/minifb/minifb/index.html" build = "build.rs" [build-dependencies] From a90edcafb5ba7db4da155b56d00724f18c218a92 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 21:45:57 +0100 Subject: [PATCH 50/50] Updated current status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0d461b..49aa93d 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ fn main() { Status ------ -Currently Windows is the supported platform. +Currently Windows and Mac are the current supported platforms. X11 (Linux/FreeBSD/etc) support is coming soon. Build instruction