Merge from mouse-support

This commit is contained in:
Daniel Collin 2016-01-29 20:16:00 +01:00
parent 8ba8047d11
commit 8bdbc850d5
16 changed files with 763 additions and 29 deletions

11
CHANGELOG.md Normal file
View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "minifb" name = "minifb"
version = "0.2.7" version = "0.3.0"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
authors = ["Daniel Collin <daniel@collin.com>"] authors = ["Daniel Collin <daniel@collin.com>"]
description = "Cross-platform window setup for bitmap rendering" description = "Cross-platform window setup for bitmap rendering"

View file

@ -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: environment:
matrix: 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: test_script:
- cargo test --verbose - cmd: cargo test --verbose

66
appveyor_rust_install.ps1 Normal file
View file

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

38
examples/mouse.rs Normal file
View file

@ -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<u32> = 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);
}
}

View file

@ -30,6 +30,18 @@ pub enum KeyRepeat {
No, 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 /// Key is used by the get key functions to check if some keys on the keyboard has been pressed
#[derive(PartialEq, Clone, Copy)] #[derive(PartialEq, Clone, Copy)]
pub enum Key { pub enum Key {
@ -158,9 +170,22 @@ pub enum Key {
Count = 107, 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; extern crate libc;
#[doc(hidden)]
pub mod os; pub mod os;
mod mouse_handler;
mod key_handler; mod key_handler;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -258,7 +283,6 @@ impl Window {
/// ```ignore /// ```ignore
/// // Moves the window to pixel postion 20, 20 on the screen /// // Moves the window to pixel postion 20, 20 on the screen
/// window.set_position(20, 20); /// window.set_position(20, 20);
/// }
/// ``` /// ```
/// ///
#[inline] #[inline]
@ -266,6 +290,59 @@ impl Window {
self.0.set_position(x, y) 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. /// Get the current keys that are down.
/// ///

29
src/mouse_handler.rs Normal file
View file

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

View file

@ -1,10 +1,16 @@
#include "OSXWindow.h" #include "OSXWindow.h"
#include "OSXWindowFrameView.h"
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
#include <unistd.h> #include <unistd.h>
static bool s_init = false; 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->height = height;
window->scale = scale; window->scale = scale;
window->key_callback = 0; window->key_callback = 0;
window->shared_data = 0;
[window updateSize]; [window updateSize];
[window setTitle:[NSString stringWithUTF8String:name]]; [window setTitle:[NSString stringWithUTF8String:name]];
[window setReleasedWhenClosed:NO]; [window setReleasedWhenClosed:NO];
[window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; [window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
[window setAcceptsMouseMovedEvents:YES];
[window center]; [window center];
@ -85,8 +93,16 @@ int mfb_update(void* window, void* buffer)
OSXWindow* win = (OSXWindow*)window; OSXWindow* win = (OSXWindow*)window;
memcpy(win->draw_buffer, buffer, win->width * win->height * 4); memcpy(win->draw_buffer, buffer, win->width * win->height * 4);
//g_updateBuffer = buffer;
int state = update_events(); 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]; [[win contentView] setNeedsDisplay:YES];
return state; 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; win->rust_data = rust_data;
} }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_set_mouse_data(void* window, SharedData* shared_data)
{
OSXWindow* win = (OSXWindow*)window;
win->shared_data = shared_data;
}

View file

@ -1,4 +1,5 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "shared_data.h"
@interface OSXWindow : NSWindow @interface OSXWindow : NSWindow
{ {
@ -9,6 +10,7 @@
@public int scale; @public int scale;
@public void* draw_buffer; @public void* draw_buffer;
@public void* rust_data; @public void* rust_data;
@public SharedData* shared_data;
@public bool should_close; @public bool should_close;
} }

View file

@ -6,6 +6,7 @@
@public int width; @public int width;
@public int height; @public int height;
@public void* draw_buffer; @public void* draw_buffer;
@private NSTrackingArea* trackingArea;
} }
@end @end

View file

@ -1,9 +1,27 @@
#import "OSXWindowFrameView.h" #import "OSXWindowFrameView.h"
#import "OSXWindow.h"
@implementation OSXWindowFrameView @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 - (void)drawRect:(NSRect)rect
{ {
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
@ -22,5 +40,60 @@
CGImageRelease(img); 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 @end

View file

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

View file

@ -4,11 +4,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define KEY_FUNCTION 0xFF #define KEY_FUNCTION 0xFF
#define KEY_ESC 0x1B #define KEY_ESC 0x1B
#define Button6 6
#define Button7 7
void mfb_close(void* window_info); 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 { typedef struct WindowInfo {
void (*key_callback)(void* user_data, int key, int state); void (*key_callback)(void* user_data, int key, int state);
void* rust_data; void* rust_data;
SharedData* shared_data;
Window window; Window window;
XImage* ximage; XImage* ximage;
void* draw_buffer; void* draw_buffer;
@ -91,8 +108,6 @@ static int setup_display() {
s_setup_done = 1; s_setup_done = 1;
printf("setup done\n");
return 1; return 1;
} }
@ -139,7 +154,7 @@ void* mfb_open(const char* title, int width, int height, int scale)
sizeHints.min_height = height; sizeHints.min_height = height;
sizeHints.max_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); XSetWMNormalHints(s_display, window, &sizeHints);
XClearWindow(s_display, window); XClearWindow(s_display, window);
XMapRaised(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) { switch (event->type)
int sym = XLookupKeysym(&event->xkey, 0); {
case KeyPress:
{
sym = XLookupKeysym(&event->xkey, 0);
if (event->type == KeyPress) { if (info->key_callback)
info->key_callback(info->rust_data, sym, 1); info->key_callback(info->rust_data, sym, 1);
} else if (event->type == KeyRelease) {
break;
}
case KeyRelease:
{
sym = XLookupKeysym(&event->xkey, 0);
if (info->key_callback)
info->key_callback(info->rust_data, sym, 0); 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() static int process_events()
{ {
int count; 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) { static void scale_4x(unsigned int* dest, unsigned int* source, int width, int height, int scale) {
int x, y; int x, y;
for (y = 0; y < height; y += scale) { for (y = 0; y < height; y += scale) {
@ -319,6 +404,14 @@ void mfb_update(void* window_info, void* buffer)
XFlush(s_display); 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(); 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) { int mfb_should_close(void* window) {
WindowInfo* win = (WindowInfo*)window; WindowInfo* win = (WindowInfo*)window;
return !!win->update; return !!win->update;

View file

@ -1,7 +1,8 @@
#![cfg(target_os = "macos")] #![cfg(target_os = "macos")]
use {Scale, Key, KeyRepeat}; use {MouseButton, MouseMode, Scale, Key, KeyRepeat};
use key_handler::KeyHandler; use key_handler::KeyHandler;
use mouse_handler;
use libc::{c_void, c_char, c_uchar}; use libc::{c_void, c_char, c_uchar};
use std::ffi::{CString}; use std::ffi::{CString};
@ -148,13 +149,29 @@ extern {
fn mfb_update(window: *mut c_void, buffer: *const c_uchar); 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_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_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_should_close(window: *mut c_void) -> i32;
fn mfb_get_screen_size() -> u32; 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 { pub struct Window {
window_handle: *mut c_void, window_handle: *mut c_void,
scale_factor: usize,
pub shared_data: SharedData,
key_handler: KeyHandler, key_handler: KeyHandler,
pub has_set_data: bool,
} }
unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) { unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
@ -180,7 +197,8 @@ impl Window {
}; };
unsafe { 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() { if handle == ptr::null_mut() {
return Err("Unable to open Window"); return Err("Unable to open Window");
@ -188,16 +206,28 @@ impl Window {
Ok(Window { Ok(Window {
window_handle: handle, 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(), 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]) { pub fn update(&mut self, buffer: &[u32]) {
self.key_handler.update(); self.key_handler.update();
unsafe { unsafe {
mfb_update(self.window_handle, buffer.as_ptr() as *const u8); 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); 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) } 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] #[inline]
pub fn get_keys(&self) -> Option<Vec<Key>> { pub fn get_keys(&self) -> Option<Vec<Key>> {
self.key_handler.get_keys() self.key_handler.get_keys()

View file

@ -6,7 +6,7 @@
extern crate x11_dl; extern crate x11_dl;
use {Scale, Key, KeyRepeat}; use {MouseMode, MouseButton, Scale, Key, KeyRepeat};
use key_handler::KeyHandler; use key_handler::KeyHandler;
use self::x11_dl::keysym::*; use self::x11_dl::keysym::*;
@ -14,6 +14,7 @@ use libc::{c_void, c_char, c_uchar};
use std::ffi::{CString}; use std::ffi::{CString};
use std::ptr; use std::ptr;
use std::mem; use std::mem;
use mouse_handler;
#[link(name = "X11")] #[link(name = "X11")]
extern { extern {
@ -22,12 +23,27 @@ extern {
fn mfb_update(window: *mut c_void, buffer: *const c_uchar); 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_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_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_should_close(window: *mut c_void) -> i32;
fn mfb_get_screen_size() -> u32; 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 { pub struct Window {
window_handle: *mut c_void, window_handle: *mut c_void,
shared_data: SharedData,
key_handler: KeyHandler, key_handler: KeyHandler,
} }
@ -154,7 +170,8 @@ impl Window {
}; };
unsafe { 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() { if handle == ptr::null_mut() {
return Err("Unable to open Window"); return Err("Unable to open Window");
@ -162,15 +179,24 @@ impl Window {
Ok(Window { Ok(Window {
window_handle: handle, window_handle: handle,
shared_data: SharedData {
scale: scale as f32,
.. SharedData::default()
},
key_handler: KeyHandler::new(), 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]) { pub fn update(&mut self, buffer: &[u32]) {
self.key_handler.update(); self.key_handler.update();
unsafe { unsafe {
Self::set_shared_data(self);
mfb_update(self.window_handle, buffer.as_ptr() as *const u8); mfb_update(self.window_handle, buffer.as_ptr() as *const u8);
mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback); 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) } 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] #[inline]
pub fn get_keys(&self) -> Option<Vec<Key>> { pub fn get_keys(&self) -> Option<Vec<Key>> {
self.key_handler.get_keys() self.key_handler.get_keys()

View file

@ -6,7 +6,7 @@ extern crate winapi;
extern crate gdi32; extern crate gdi32;
extern crate time; extern crate time;
use {Scale, Key, KeyRepeat}; use {Scale, Key, KeyRepeat, MouseButton, MouseMode};
use key_handler::KeyHandler; use key_handler::KeyHandler;
@ -14,6 +14,7 @@ use std::ptr;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::mem; use std::mem;
use mouse_handler;
use self::winapi::windef::HWND; use self::winapi::windef::HWND;
use self::winapi::windef::HDC; 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); let mut wnd: &mut Window = mem::transmute(user_data);
match msg { 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 => { winapi::winuser::WM_KEYDOWN => {
update_key_state(wnd, (lparam as u32) >> 16, true); update_key_state(wnd, (lparam as u32) >> 16, true);
return 0; 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 => { winapi::winuser::WM_CLOSE => {
wnd.is_open = false; wnd.is_open = false;
} }
@ -233,7 +273,16 @@ fn to_wstring(str: &str) -> Vec<u16> {
v v
} }
#[derive(Default)]
struct MouseData {
pub x: f32,
pub y: f32,
pub state: [bool; 8],
pub scroll: f32,
}
pub struct Window { pub struct Window {
mouse: MouseData,
dc: Option<HDC>, dc: Option<HDC>,
window: Option<HWND>, window: Option<HWND>,
buffer: Vec<u32>, buffer: Vec<u32>,
@ -328,6 +377,7 @@ impl Window {
} }
let window = Window { let window = Window {
mouse: MouseData::default(),
dc: Some(user32::GetDC(handle.unwrap())), dc: Some(user32::GetDC(handle.unwrap())),
window: Some(handle.unwrap()), window: Some(handle.unwrap()),
buffer: Vec::new(), 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] #[inline]
pub fn get_keys(&self) -> Option<Vec<Key>> { pub fn get_keys(&self) -> Option<Vec<Key>> {
self.key_handler.get_keys() self.key_handler.get_keys()
@ -391,6 +466,18 @@ impl Window {
let mut msg = mem::uninitialized(); let mut msg = mem::uninitialized();
let window = self.window.unwrap(); 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(); self.key_handler.update();
// TODO: Optimize // TODO: Optimize