mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-24 03:41:29 +11:00
WIP on Windows version
This commit is contained in:
parent
e83c66b8dd
commit
c8b45d8577
218
src/lib.rs
218
src/lib.rs
|
@ -1,80 +1,152 @@
|
||||||
extern crate libc;
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use windows::*;
|
pub use windows::*;
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[link(name = "Cocoa", kind = "framework")]
|
pub mod macos;
|
||||||
extern {
|
#[cfg(target_os = "macos")]
|
||||||
fn mfb_open(name: *const c_char, width: c_int, height: c_int) -> c_int;
|
pub use macos::*;
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
159
src/windows.rs
159
src/windows.rs
|
@ -1,8 +1,14 @@
|
||||||
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
extern crate user32;
|
extern crate user32;
|
||||||
extern crate kernel32;
|
extern crate kernel32;
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
extern crate gdi32;
|
extern crate gdi32;
|
||||||
|
|
||||||
|
use Scale;
|
||||||
|
use Vsync;
|
||||||
|
use Key;
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
@ -24,21 +30,120 @@ struct BitmapInfo {
|
||||||
pub bmi_colors: [RGBQUAD; 3],
|
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,
|
unsafe extern "system" fn wnd_proc(window: winapi::HWND,
|
||||||
msg: winapi::UINT,
|
msg: winapi::UINT,
|
||||||
wparam: winapi::WPARAM,
|
wparam: winapi::WPARAM,
|
||||||
lparam: winapi::LPARAM)
|
lparam: winapi::LPARAM)
|
||||||
-> winapi::LRESULT {
|
-> 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 {
|
match msg {
|
||||||
winapi::winuser::WM_KEYDOWN => {
|
winapi::winuser::WM_KEYDOWN => {
|
||||||
if (wparam & 0xff) == 27 {
|
update_key_state(self, wparam as u32, true);
|
||||||
|
if (wparam & 0x1ff) == 27 {
|
||||||
CLOSE_APP = true;
|
CLOSE_APP = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winapi::winuser::WM_KEYUP => {
|
||||||
|
update_key_state(self, wparam as u32, false);
|
||||||
|
}
|
||||||
|
|
||||||
winapi::winuser::WM_PAINT => {
|
winapi::winuser::WM_PAINT => {
|
||||||
let mut rect: winapi::RECT = mem::uninitialized();
|
let mut rect: winapi::RECT = mem::uninitialized();
|
||||||
let buffer = user32::GetWindowLongPtrW(window, winapi::winuser::GWLP_USERDATA);
|
|
||||||
|
|
||||||
user32::GetClientRect(window, &mut rect);
|
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[1].rgbGreen = 0xff;
|
||||||
bitmap_info.bmi_colors[2].rgbBlue = 0xff;
|
bitmap_info.bmi_colors[2].rgbBlue = 0xff;
|
||||||
|
|
||||||
let dc = user32::GetDC(window);
|
gdi32::StretchDIBits(self.dc,
|
||||||
|
|
||||||
gdi32::StretchDIBits(dc,
|
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
|
@ -67,14 +170,12 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND,
|
||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
mem::transmute(buffer),
|
mem::transmute(self.buffer),
|
||||||
mem::transmute(&bitmap_info),
|
mem::transmute(&bitmap_info),
|
||||||
winapi::wingdi::DIB_RGB_COLORS,
|
winapi::wingdi::DIB_RGB_COLORS,
|
||||||
winapi::wingdi::SRCCOPY);
|
winapi::wingdi::SRCCOPY);
|
||||||
|
|
||||||
user32::ValidateRect(window, ptr::null_mut());
|
user32::ValidateRect(window, ptr::null_mut());
|
||||||
|
|
||||||
user32::ReleaseDC(window, dc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -92,12 +193,15 @@ fn to_wstring(str: &str) -> *const u16 {
|
||||||
v.as_ptr()
|
v.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Minifb {
|
pub struct Window {
|
||||||
|
dc: DC,
|
||||||
window: HWND,
|
window: HWND,
|
||||||
|
bool keys_down: [bool; 512],
|
||||||
|
buffer: &[u32],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Minifb {
|
impl Window {
|
||||||
fn open_window(name: &str, width: usize, height: usize) -> HWND {
|
fn open_window(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result<Window, &str> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let class_name = to_wstring("minifb_window");
|
let class_name = to_wstring("minifb_window");
|
||||||
let s = CString::new(name).unwrap();
|
let s = CString::new(name).unwrap();
|
||||||
|
@ -163,13 +267,26 @@ impl Minifb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_keys(&self -> Vec<Key> {
|
||||||
|
let index = 0;
|
||||||
|
let mut keys: Vec<Key> = 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 {
|
pub fn update(&mut self, buffer: &[u32]) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut msg = mem::uninitialized();
|
let mut msg = mem::uninitialized();
|
||||||
|
|
||||||
user32::SetWindowLongPtrW(self.window,
|
user32::SetWindowLongPtrW(self.window, winapi::winuser::GWLP_USERDATA, self as i64);
|
||||||
winapi::winuser::GWLP_USERDATA,
|
|
||||||
buffer.as_ptr() as i64);
|
|
||||||
user32::InvalidateRect(self.window, ptr::null_mut(), winapi::TRUE);
|
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 {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue