diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2c20ac4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +This project follows semantic versioning. + +### v0.3.0 (2016-01-29) + +- [added] get_mouse_pos +- [added] get_mouse_down +- [added] get_scroll_wheel + +This relase adds support for mouse input. See the documentation and the examples for usage diff --git a/Cargo.toml b/Cargo.toml index 6b8033e..cb0f1e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minifb" -version = "0.2.7" +version = "0.3.0" license = "MIT/Apache-2.0" authors = ["Daniel Collin "] description = "Cross-platform window setup for bitmap rendering" diff --git a/appveyor.yml b/appveyor.yml index b6bba13..48bb88f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,16 +1,118 @@ +## Operating System (VM environment) ## + +# Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. +os: Visual Studio 2015 + +## Build Matrix ## + +# This configuration will setup a build for each channel & target combination (12 windows +# combinations in all). +# +# There are 3 channels: stable, beta, and nightly. +# +# The values for target are the set of windows Rust build targets. Each value is of the form +# +# ARCH-pc-windows-TOOLCHAIN +# +# Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker +# toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for +# a description of the toolchain differences. +# +# Comment out channel/target combos you do not wish to build in CI. environment: matrix: - - TARGET: x86_64-pc-windows-msvc - - TARGET: i686-pc-windows-msvc - - TARGET: i686-pc-windows-gnu -install: - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" - - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin - - SET PATH=%PATH%;C:\MinGW\bin - - rustc -V - - cargo -V -build: false +### MSVC Toolchains ### + + # Stable 64-bit MSVC + - channel: stable + target: x86_64-pc-windows-msvc + # Stable 32-bit MSVC + - channel: stable + target: i686-pc-windows-msvc + # Beta 64-bit MSVC + - channel: beta + target: x86_64-pc-windows-msvc + # Beta 32-bit MSVC + - channel: beta + target: i686-pc-windows-msvc + # Nightly 64-bit MSVC + - channel: nightly + target: x86_64-pc-windows-msvc + # Nightly 32-bit MSVC + - channel: nightly + target: i686-pc-windows-msvc + +### GNU Toolchains ### + + # Stable 64-bit GNU + - channel: stable + target: x86_64-pc-windows-gnu + # Stable 32-bit GNU + - channel: stable + target: i686-pc-windows-gnu + # Beta 64-bit GNU + - channel: beta + target: x86_64-pc-windows-gnu + # Beta 32-bit GNU + - channel: beta + target: i686-pc-windows-gnu + # Nightly 64-bit GNU + - channel: nightly + target: x86_64-pc-windows-gnu + # Nightly 32-bit GNU + - channel: nightly + target: i686-pc-windows-gnu + +### Allowed failures ### + +# See Appveyor documentation for specific details. In short, place any channel or targets you wish +# to allow build failures on (usually nightly at least is a wise choice). This will prevent a build +# or test failure in the matching channels/targets from failing the entire build. +matrix: + allow_failures: + - channel: nightly + +# If you only care about stable channel build failures, uncomment the following line: + #- channel: beta + +# 32-bit MSVC isn't stablized yet, so you may optionally allow failures there (uncomment line): + #- target: i686-pc-windows-msvc + +## Install Script ## + +# This is the most important part of the Appveyor configuration. This installs the version of Rust +# specified by the 'channel' and 'target' environment variables from the build matrix. By default, +# Rust will be installed to C:\Rust for easy usage, but this path can be overridden by setting the +# RUST_INSTALL_DIR environment variable. The URL to download rust distributions defaults to +# https://static.rust-lang.org/dist/ but can overridden by setting the RUST_DOWNLOAD_URL environment +# variable. +# +# For simple configurations, instead of using the build matrix, you can override the channel and +# target environment variables with the -channel and -target script arguments. +# +# If no channel or target arguments or environment variables are specified, will default to stable +# channel and x86_64-pc-windows-msvc target. +# +# The file appveyor_rust_install.ps1 must exist in the root directory of the repository. +install: +- ps: .\appveyor_rust_install.ps1 + +# Alternative install command for simple configurations without build matrix (uncomment line and +# comment above line): +#- ps: .\appveyor_rust_install.ps1 -channel stable -target x86_64-pc-windows-msvc + +## Build Script ## + +# Uses 'cargo build' to build. Alternatively, the project may call rustc directly or perform other +# build commands. Rust will automatically be placed in the PATH environment variable. +build_script: +- cmd: cargo build --verbose + +## Build Script ## + +# Uses 'cargo test' to run tests. Alternatively, the project may call compiled programs directly or +# perform other testing commands. Rust will automatically be placed in the PATH environment +# variable. test_script: - - cargo test --verbose +- cmd: cargo test --verbose diff --git a/appveyor_rust_install.ps1 b/appveyor_rust_install.ps1 new file mode 100644 index 0000000..8e12c2e --- /dev/null +++ b/appveyor_rust_install.ps1 @@ -0,0 +1,66 @@ +##### Appveyor Rust Install Script ##### + +# This is the most important part of the Appveyor configuration. This installs the version of Rust +# specified by the "channel" and "target" environment variables from the build matrix. By default, +# Rust will be installed to C:\Rust for easy usage, but this path can be overridden by setting the +# RUST_INSTALL_DIR environment variable. The URL to download rust distributions defaults to +# https://static.rust-lang.org/dist/ but can overridden by setting the RUST_DOWNLOAD_URL environment +# variable. +# +# For simple configurations, instead of using the build matrix, you can override the channel and +# target environment variables with the --channel and --target script arguments. +# +# If no channel or target arguments or environment variables are specified, will default to stable +# channel and x86_64-pc-windows-msvc target. + +param([string]$channel=${env:channel}, [string]$target=${env:target}) + +# Initialize our parameters from arguments and environment variables, falling back to defaults +if (!$channel) { + $channel = "stable" +} +if (!$target) { + $target = "x86_64-pc-windows-msvc" +} + +$downloadUrl = "https://static.rust-lang.org/dist/" +if ($env:RUST_DOWNLOAD_URL) { + $downloadUrl = $env:RUST_DOWNLOAD_URL +} + +$installDir = "C:\Rust" +if ($env:RUST_INSTALL_DIR) { + $installUrl = $env:RUST_INSTALL_DIR +} + +# Download manifest so we can find actual filename of installer to download. Needed mostly for +# stable channel. +echo "Downloading $channel channel manifest" +$manifest = "${env:Temp}\channel-rust-${channel}" +Start-FileDownload "${downloadUrl}channel-rust-${channel}" -FileName "$manifest" + +# Search the manifest lines for the correct filename based on target +$match = Get-Content "$manifest" | Select-String -pattern "${target}.exe" -simplematch + +if (!$match -or !$match.line) { + throw "Could not find $target in $channel channel manifest" +} + +$installer = $match.line + +# Download installer +echo "Downloading ${downloadUrl}$installer" +Start-FileDownload "${downloadUrl}$installer" -FileName "${env:Temp}\$installer" + +# Execute installer and wait for it to finish +echo "Installing $installer to $installDir" +&"${env:Temp}\$installer" /VERYSILENT /NORESTART /DIR="$installDir" | Write-Output + +# Add Rust to the path. +$env:Path += ";${installDir}\bin;C:\MinGW\bin" + +echo "Installation of $channel Rust $target completed" + +# Test and display installed version information for rustc and cargo +rustc -V +cargo -V \ No newline at end of file diff --git a/examples/mouse.rs b/examples/mouse.rs new file mode 100644 index 0000000..5c42878 --- /dev/null +++ b/examples/mouse.rs @@ -0,0 +1,38 @@ +extern crate minifb; + +use minifb::{MouseButton, MouseMode, Window, Key, Scale}; + +const WIDTH: usize = 640; +const HEIGHT: usize = 360; + +fn main() { + let mut buffer: Vec = vec![0; WIDTH * HEIGHT]; + + let mut window = match Window::new("Mouse Draw - 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) { + window.get_mouse_pos(MouseMode::Discard).map(|mouse| { + let screen_pos = ((mouse.1 as usize) * WIDTH) + mouse.0 as usize; + + if window.get_mouse_down(MouseButton::Left) { + buffer[screen_pos] = 0x00ffffff; + } + + if window.get_mouse_down(MouseButton::Right) { + buffer[screen_pos] = 0; + } + }); + + window.get_scroll_wheel().map(|scroll| { + println!("Scrolling {} - {}", scroll.0, scroll.1); + }); + + window.update(&buffer); + } +} diff --git a/src/lib.rs b/src/lib.rs index bbc2b73..60136d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,18 @@ pub enum KeyRepeat { No, } +/// The various mouse buttons that are availible +#[derive(PartialEq, Clone, Copy)] +pub enum MouseButton +{ + /// Left mouse button + Left, + /// Middle mouse button + Middle, + /// Right mouse button + Right, +} + /// 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 { @@ -158,9 +170,22 @@ pub enum Key { Count = 107, } +/// 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 MouseMode { + /// Return mouse coords from outside of the window (may be negative) + Pass, + /// Clamp the mouse coordinates within the window + Clamp, + /// Discared if the mouse is outside the window + Discard, +} + extern crate libc; +#[doc(hidden)] pub mod os; +mod mouse_handler; mod key_handler; #[cfg(target_os = "macos")] @@ -258,7 +283,6 @@ impl Window { /// ```ignore /// // Moves the window to pixel postion 20, 20 on the screen /// window.set_position(20, 20); - /// } /// ``` /// #[inline] @@ -266,6 +290,59 @@ impl Window { self.0.set_position(x, y) } + /// + /// Get the current position of the mouse relative to the current window + /// The coordinate system is as 0, 0 as the upper left corner + /// + /// # Examples + /// + /// ```ignore + /// window.get_mouse_pos(MouseMode::Clamp).map(|mouse| { + /// println!("x {} y {}", mouse.0, mouse.1); + /// }); + /// ``` + /// + #[inline] + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + self.0.get_mouse_pos(mode) + } + + /// + /// Check if a mouse button is down or not + /// + /// # Examples + /// + /// ```ignore + /// let left_down = window.get_mouse_down(MouseButton::Left); + /// println!("is left down? {}", left_down) + /// ``` + /// + #[inline] + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + self.0.get_mouse_down(button) + } + + /// + /// Get the current movement of the scroll wheel. + /// Scroll wheel can mean different thing depending on the device attach. + /// For example on Mac with trackpad "scroll wheel" means two finger + /// swiping up/down (y axis) and to the sides (x-axis) + /// When using a mouse this assumes the scroll wheel which often is only y direction. + /// + /// # Examples + /// + /// ```ignore + /// window.get_scroll_wheel().map(|scroll| { + /// println!("scrolling - x {} y {}", scroll.0, scroll.1); + /// }); + /// ``` + /// + /// + #[inline] + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + self.0.get_scroll_wheel() + } + /// /// Get the current keys that are down. /// diff --git a/src/mouse_handler.rs b/src/mouse_handler.rs new file mode 100644 index 0000000..f401379 --- /dev/null +++ b/src/mouse_handler.rs @@ -0,0 +1,29 @@ +use MouseMode; + +fn clamp(v: f32, lb: f32, ub: f32) -> f32 { + f32::min(f32::max(v, lb), ub) +} + +pub fn get_pos(mode: MouseMode, mx: f32, my: f32, scale: f32, width: f32, height: f32) -> Option<(f32, f32)> { + let s = 1.0 / scale as f32; + let x = mx * s; + let y = my * s; + let window_width = width * s; + let window_height = height * s; + + match mode { + MouseMode::Pass => Some((x, y)), + MouseMode::Clamp => { + Some((clamp(x, 0.0, window_width), + clamp(y, 0.0, window_height))) + }, + MouseMode::Discard => { + if x < 0.0 || y < 0.0 || x >= window_width || y >= window_height { + None + } else { + Some((x, y)) + } + }, + } +} + diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index cc4ddc0..cdb6829 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -1,10 +1,16 @@ #include "OSXWindow.h" +#include "OSXWindowFrameView.h" #include #include static bool s_init = false; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wobjc-method-access" // [window updateSize]; +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -35,12 +41,14 @@ void* mfb_open(const char* name, int width, int height, int scale) window->height = height; window->scale = scale; window->key_callback = 0; + window->shared_data = 0; [window updateSize]; [window setTitle:[NSString stringWithUTF8String:name]]; [window setReleasedWhenClosed:NO]; [window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + [window setAcceptsMouseMovedEvents:YES]; [window center]; @@ -85,8 +93,16 @@ int mfb_update(void* window, void* buffer) OSXWindow* win = (OSXWindow*)window; memcpy(win->draw_buffer, buffer, win->width * win->height * 4); - //g_updateBuffer = buffer; int state = update_events(); + + if (win->shared_data) { + NSPoint p = [win mouseLocationOutsideOfEventStream]; + NSRect originalFrame = [win frame]; + NSRect contentRect = [NSWindow contentRectForFrameRect: originalFrame styleMask: NSTitledWindowMask]; + win->shared_data->mouse_x = p.x; + win->shared_data->mouse_y = contentRect.size.height - p.y; + } + [[win contentView] setNeedsDisplay:YES]; return state; } @@ -138,3 +154,12 @@ void mfb_set_key_callback(void* window, void* rust_data, void (*key_callback)(vo win->rust_data = rust_data; } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mfb_set_mouse_data(void* window, SharedData* shared_data) +{ + OSXWindow* win = (OSXWindow*)window; + win->shared_data = shared_data; +} + + diff --git a/src/native/macosx/OSXWindow.h b/src/native/macosx/OSXWindow.h index 48b7532..ced6eec 100644 --- a/src/native/macosx/OSXWindow.h +++ b/src/native/macosx/OSXWindow.h @@ -1,4 +1,5 @@ #import +#include "shared_data.h" @interface OSXWindow : NSWindow { @@ -9,6 +10,7 @@ @public int scale; @public void* draw_buffer; @public void* rust_data; + @public SharedData* shared_data; @public bool should_close; } diff --git a/src/native/macosx/OSXWindowFrameView.h b/src/native/macosx/OSXWindowFrameView.h index c12bc52..d07d6a5 100644 --- a/src/native/macosx/OSXWindowFrameView.h +++ b/src/native/macosx/OSXWindowFrameView.h @@ -6,6 +6,7 @@ @public int width; @public int height; @public void* draw_buffer; + @private NSTrackingArea* trackingArea; } @end diff --git a/src/native/macosx/OSXWindowFrameView.m b/src/native/macosx/OSXWindowFrameView.m index b8e590d..7af8079 100644 --- a/src/native/macosx/OSXWindowFrameView.m +++ b/src/native/macosx/OSXWindowFrameView.m @@ -1,9 +1,27 @@ #import "OSXWindowFrameView.h" +#import "OSXWindow.h" @implementation OSXWindowFrameView /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +-(void)updateTrackingAreas +{ + if(trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); + trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + - (void)drawRect:(NSRect)rect { CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; @@ -22,5 +40,60 @@ CGImageRelease(img); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseDown:(NSEvent*)event +{ + OSXWindow* window = (OSXWindow*)[self window]; + window->shared_data->mouse_state[0] = 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseUp:(NSEvent*)event +{ + OSXWindow* window = (OSXWindow*)[self window]; + window->shared_data->mouse_state[0] = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)rightMouseDown:(NSEvent*)event +{ + OSXWindow* window = (OSXWindow*)[self window]; + window->shared_data->mouse_state[2] = 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)rightMouseUp:(NSEvent*)event +{ + OSXWindow* window = (OSXWindow*)[self window]; + window->shared_data->mouse_state[2] = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)scrollWheel:(NSEvent *)event +{ + OSXWindow* window = (OSXWindow*)[self window]; + window->shared_data->scroll_x = [event deltaX]; + window->shared_data->scroll_y = [event deltaY]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + @end diff --git a/src/native/macosx/shared_data.h b/src/native/macosx/shared_data.h new file mode 100644 index 0000000..b869f91 --- /dev/null +++ b/src/native/macosx/shared_data.h @@ -0,0 +1,12 @@ +#pragma once + +typedef struct SharedData { + unsigned int width; + unsigned int height; + float mouse_x; + float mouse_y; + float scroll_x; + float scroll_y; + unsigned char mouse_state[8]; +} SharedData; + diff --git a/src/native/x11/X11MiniFB.c b/src/native/x11/X11MiniFB.c index d4b3fcb..c69d393 100644 --- a/src/native/x11/X11MiniFB.c +++ b/src/native/x11/X11MiniFB.c @@ -4,11 +4,14 @@ #include #include #include +#include /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define KEY_FUNCTION 0xFF #define KEY_ESC 0x1B +#define Button6 6 +#define Button7 7 void mfb_close(void* window_info); @@ -28,9 +31,23 @@ static Atom s_wm_delete_window; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef struct SharedData { + uint32_t width; + uint32_t height; + float scale; + float mouse_x; + float mouse_y; + float scroll_x; + float scroll_y; + uint8_t state[3]; +} SharedData; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef struct WindowInfo { void (*key_callback)(void* user_data, int key, int state); void* rust_data; + SharedData* shared_data; Window window; XImage* ximage; void* draw_buffer; @@ -91,8 +108,6 @@ static int setup_display() { s_setup_done = 1; - printf("setup done\n"); - return 1; } @@ -139,7 +154,7 @@ void* mfb_open(const char* title, int width, int height, int scale) sizeHints.min_height = height; sizeHints.max_height = height; - XSelectInput(s_display, window, KeyPressMask | KeyReleaseMask); + XSelectInput(s_display, window, ButtonPressMask | KeyPressMask | KeyReleaseMask | ButtonReleaseMask); XSetWMNormalHints(s_display, window, &sizeHints); XClearWindow(s_display, window); XMapRaised(s_display, window); @@ -207,13 +222,63 @@ static int process_event(XEvent* event) { } } - if ((event->type == KeyPress) || (event->type == KeyRelease) && info->key_callback) { - int sym = XLookupKeysym(&event->xkey, 0); + switch (event->type) + { + case KeyPress: + { + sym = XLookupKeysym(&event->xkey, 0); - if (event->type == KeyPress) { - info->key_callback(info->rust_data, sym, 1); - } else if (event->type == KeyRelease) { - info->key_callback(info->rust_data, sym, 0); + if (info->key_callback) + info->key_callback(info->rust_data, sym, 1); + + break; + } + + case KeyRelease: + { + sym = XLookupKeysym(&event->xkey, 0); + + if (info->key_callback) + info->key_callback(info->rust_data, sym, 0); + break; + } + + case ButtonPress: + { + if (!info->shared_data) + break; + + if (event->xbutton.button == Button1) + info->shared_data->state[0] = 1; + else if (event->xbutton.button == Button2) + info->shared_data->state[1] = 1; + else if (event->xbutton.button == Button3) + info->shared_data->state[2] = 1; + else if (event->xbutton.button == Button4) + info->shared_data->scroll_y = 10.0f; + else if (event->xbutton.button == Button5) + info->shared_data->scroll_y = -10.0f; + else if (event->xbutton.button == Button6) + info->shared_data->scroll_x = 10.0f; + else if (event->xbutton.button == Button7) + info->shared_data->scroll_y = -10.0f; + + break; + } + + case ButtonRelease: + { + if (!info->shared_data) + break; + + if (event->xbutton.button == Button1) + info->shared_data->state[0] = 0; + else if (event->xbutton.button == Button2) + info->shared_data->state[1] = 0; + else if (event->xbutton.button == Button3) + info->shared_data->state[2] = 0; + + break; } } @@ -222,6 +287,24 @@ static int process_event(XEvent* event) { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void get_mouse_pos(WindowInfo* info) { + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer(s_display, info->window, + &root, &child, + &rootX, &rootY, &childX, &childY, + &mask); + + if (info->shared_data) { + info->shared_data->mouse_x = (float)childX; + info->shared_data->mouse_y = (float)childY; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static int process_events() { int count; @@ -261,6 +344,8 @@ static void scale_2x(unsigned int* dest, unsigned int* source, int width, int he } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static void scale_4x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { int x, y; for (y = 0; y < height; y += scale) { @@ -319,6 +404,14 @@ void mfb_update(void* window_info, void* buffer) XFlush(s_display); } + // clear before processing new events + + if (info->shared_data) { + info->shared_data->scroll_x = 0.0f; + info->shared_data->scroll_y = 0.0f; + } + + get_mouse_pos(info); process_events(); } @@ -362,6 +455,16 @@ void mfb_set_key_callback(void* window, void* rust_data, void (*key_callback)(vo /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void mfb_set_shared_data(void* window, SharedData* data) +{ + WindowInfo* win = (WindowInfo*)window; + win->shared_data = data; + win->shared_data->width = win->width; + win->shared_data->height = win->height; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int mfb_should_close(void* window) { WindowInfo* win = (WindowInfo*)window; return !!win->update; diff --git a/src/os/macos/mod.rs b/src/os/macos/mod.rs index c82b862..93965f5 100644 --- a/src/os/macos/mod.rs +++ b/src/os/macos/mod.rs @@ -1,7 +1,8 @@ #![cfg(target_os = "macos")] -use {Scale, Key, KeyRepeat}; +use {MouseButton, MouseMode, Scale, Key, KeyRepeat}; use key_handler::KeyHandler; +use mouse_handler; use libc::{c_void, c_char, c_uchar}; use std::ffi::{CString}; @@ -148,13 +149,29 @@ extern { fn mfb_update(window: *mut c_void, buffer: *const c_uchar); fn mfb_set_position(window: *mut c_void, x: i32, y: i32); fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void, cb: unsafe extern fn(*mut c_void, i32, i32)); + fn mfb_set_mouse_data(window_handle: *mut c_void, shared_data: *mut SharedData); fn mfb_should_close(window: *mut c_void) -> i32; fn mfb_get_screen_size() -> u32; } +#[derive(Default)] +#[repr(C)] +pub struct SharedData { + pub width: u32, + pub height: u32, + pub mouse_x: f32, + pub mouse_y: f32, + pub scroll_x: f32, + pub scroll_y: f32, + pub state: [u8; 8], +} + pub struct Window { window_handle: *mut c_void, + scale_factor: usize, + pub shared_data: SharedData, key_handler: KeyHandler, + pub has_set_data: bool, } unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) { @@ -180,7 +197,8 @@ impl Window { }; unsafe { - let handle = mfb_open(n.as_ptr(), width as u32, height as u32, Self::get_scale_factor(width, height, scale)); + let scale_factor = Self::get_scale_factor(width, height, scale) as usize; + let handle = mfb_open(n.as_ptr(), width as u32, height as u32, scale_factor as i32); if handle == ptr::null_mut() { return Err("Unable to open Window"); @@ -188,16 +206,28 @@ impl Window { Ok(Window { window_handle: handle, + scale_factor: scale_factor, + shared_data: SharedData { + width: width as u32 * scale_factor as u32, + height: height as u32 * scale_factor as u32, + .. SharedData::default() + }, key_handler: KeyHandler::new(), + has_set_data: false, }) } } + unsafe fn set_mouse_data(&mut self) { + mfb_set_mouse_data(self.window_handle, &mut self.shared_data); + } + pub fn update(&mut self, buffer: &[u32]) { self.key_handler.update(); unsafe { mfb_update(self.window_handle, buffer.as_ptr() as *const u8); + Self::set_mouse_data(self); mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback); } } @@ -207,6 +237,33 @@ impl Window { unsafe { mfb_set_position(self.window_handle, x as i32, y as i32) } } + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + let sx = self.shared_data.scroll_x; + let sy = self.shared_data.scroll_y; + + if sx.abs() > 0.0001 || sy.abs() > 0.0001 { + Some((sx, sy)) + } else { + None + } + } + + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + match button { + MouseButton::Left => self.shared_data.state[0] > 0, + MouseButton::Middle => self.shared_data.state[1] > 0, + MouseButton::Right => self.shared_data.state[2] > 0, + } + } + + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + let s = self.scale_factor as f32; + let w = self.shared_data.width as f32; + let h = self.shared_data.height as f32; + + mouse_handler::get_pos(mode, self.shared_data.mouse_x, self.shared_data.mouse_y, s, w, h) + } + #[inline] pub fn get_keys(&self) -> Option> { self.key_handler.get_keys() diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 4964bc9..8fe070a 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -6,7 +6,7 @@ extern crate x11_dl; -use {Scale, Key, KeyRepeat}; +use {MouseMode, MouseButton, Scale, Key, KeyRepeat}; use key_handler::KeyHandler; use self::x11_dl::keysym::*; @@ -14,6 +14,7 @@ use libc::{c_void, c_char, c_uchar}; use std::ffi::{CString}; use std::ptr; use std::mem; +use mouse_handler; #[link(name = "X11")] extern { @@ -22,12 +23,27 @@ extern { fn mfb_update(window: *mut c_void, buffer: *const c_uchar); fn mfb_set_position(window: *mut c_void, x: i32, y: i32); fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void, cb: unsafe extern fn(*mut c_void, i32, i32)); + fn mfb_set_shared_data(window: *mut c_void, target: *mut SharedData); fn mfb_should_close(window: *mut c_void) -> i32; fn mfb_get_screen_size() -> u32; } +#[derive(Default)] +#[repr(C)] +pub struct SharedData { + pub width: u32, + pub height: u32, + pub scale: f32, + pub mouse_x: f32, + pub mouse_y: f32, + pub scroll_x: f32, + pub scroll_y: f32, + pub state: [u8; 3], +} + pub struct Window { window_handle: *mut c_void, + shared_data: SharedData, key_handler: KeyHandler, } @@ -154,7 +170,8 @@ impl Window { }; unsafe { - let handle = mfb_open(n.as_ptr(), width as u32, height as u32, Self::get_scale_factor(width, height, scale)); + let scale = Self::get_scale_factor(width, height, scale); + let handle = mfb_open(n.as_ptr(), width as u32, height as u32, scale); if handle == ptr::null_mut() { return Err("Unable to open Window"); @@ -162,15 +179,24 @@ impl Window { Ok(Window { window_handle: handle, + shared_data: SharedData { + scale: scale as f32, + .. SharedData::default() + }, key_handler: KeyHandler::new(), }) } } + unsafe fn set_shared_data(&mut self) { + mfb_set_shared_data(self.window_handle, &mut self.shared_data); + } + pub fn update(&mut self, buffer: &[u32]) { self.key_handler.update(); unsafe { + Self::set_shared_data(self); mfb_update(self.window_handle, buffer.as_ptr() as *const u8); mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback); } @@ -181,6 +207,31 @@ impl Window { unsafe { mfb_set_position(self.window_handle, x as i32, y as i32) } } + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + let s = self.shared_data.scale as f32; + let w = self.shared_data.width as f32; + let h = self.shared_data.height as f32; + + mouse_handler::get_pos(mode, self.shared_data.mouse_x, self.shared_data.mouse_y, s, w, h) + } + + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + match button { + MouseButton::Left => self.shared_data.state[0] > 0, + MouseButton::Middle => self.shared_data.state[1] > 0, + MouseButton::Right => self.shared_data.state[2] > 0, + } + } + + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + if self.shared_data.scroll_x.abs() > 0.0 || + self.shared_data.scroll_y.abs() > 0.0 { + Some((self.shared_data.scroll_x, self.shared_data.scroll_y)) + } else { + None + } + } + #[inline] pub fn get_keys(&self) -> Option> { self.key_handler.get_keys() diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index e9a5e8f..a2dc7ce 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -6,7 +6,7 @@ extern crate winapi; extern crate gdi32; extern crate time; -use {Scale, Key, KeyRepeat}; +use {Scale, Key, KeyRepeat, MouseButton, MouseMode}; use key_handler::KeyHandler; @@ -14,6 +14,7 @@ use std::ptr; use std::os::windows::ffi::OsStrExt; use std::ffi::OsStr; use std::mem; +use mouse_handler; use self::winapi::windef::HWND; use self::winapi::windef::HDC; @@ -171,11 +172,50 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND, let mut wnd: &mut Window = mem::transmute(user_data); match msg { + /* + winapi::winuser::WM_MOUSEMOVE => { + let mouse_coords = lparam as u32; + let scale = user_data.scale as f32; + user_data.mouse.local_x = (((mouse_coords >> 16) & 0xffff) as f32) / scale; + user_data.mouse.local_y = ((mouse_coords & 0xffff) as f32) / scale; + + return 0; + } + */ + winapi::winuser::WM_MOUSEWHEEL => { + let scroll = ((((wparam as u32) >> 16) & 0xffff) as i16) as f32 * 0.1; + wnd.mouse.scroll = scroll; + } + winapi::winuser::WM_KEYDOWN => { update_key_state(wnd, (lparam as u32) >> 16, true); return 0; } + winapi::winuser::WM_LBUTTONDOWN => { + wnd.mouse.state[0] = true + } + + winapi::winuser::WM_LBUTTONUP => { + wnd.mouse.state[0] = false + } + + winapi::winuser::WM_MBUTTONDOWN => { + wnd.mouse.state[1] = true + } + + winapi::winuser::WM_MBUTTONUP => { + wnd.mouse.state[1] = false + } + + winapi::winuser::WM_RBUTTONDOWN => { + wnd.mouse.state[2] = true + } + + winapi::winuser::WM_RBUTTONUP => { + wnd.mouse.state[2] = false + } + winapi::winuser::WM_CLOSE => { wnd.is_open = false; } @@ -233,7 +273,16 @@ fn to_wstring(str: &str) -> Vec { v } +#[derive(Default)] +struct MouseData { + pub x: f32, + pub y: f32, + pub state: [bool; 8], + pub scroll: f32, +} + pub struct Window { + mouse: MouseData, dc: Option, window: Option, buffer: Vec, @@ -328,6 +377,7 @@ impl Window { } let window = Window { + mouse: MouseData::default(), dc: Some(user32::GetDC(handle.unwrap())), window: Some(handle.unwrap()), buffer: Vec::new(), @@ -350,6 +400,31 @@ impl Window { } } + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + let s = self.scale_factor as f32; + let w = self.width as f32; + let h = self.height as f32; + + // TODO: Needs to be fixed with resize support + mouse_handler::get_pos(mode, self.mouse.x, self.mouse.y, s, w * s, h * s) + } + + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + match button { + MouseButton::Left => self.mouse.state[0], + MouseButton::Middle => self.mouse.state[1], + MouseButton::Right => self.mouse.state[2], + } + } + + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + if self.mouse.scroll.abs() > 0.0 { + Some((0.0, self.mouse.scroll)) + } else { + None + } + } + #[inline] pub fn get_keys(&self) -> Option> { self.key_handler.get_keys() @@ -391,6 +466,18 @@ impl Window { let mut msg = mem::uninitialized(); let window = self.window.unwrap(); + let mut point: winapi::POINT = mem::uninitialized(); + user32::GetCursorPos(&mut point); + user32::ScreenToClient(window, &mut point); + + self.mouse.x = point.x as f32; + self.mouse.y = point.y as f32; + self.mouse.scroll = 0.0; + + //self.mouse_data.x + + //println!("{} {}", point.x, point.y); + self.key_handler.update(); // TODO: Optimize