mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 19:31:30 +11:00
Merge from mouse-support
This commit is contained in:
parent
8ba8047d11
commit
8bdbc850d5
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal 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
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "minifb"
|
||||
version = "0.2.7"
|
||||
version = "0.3.0"
|
||||
license = "MIT/Apache-2.0"
|
||||
authors = ["Daniel Collin <daniel@collin.com>"]
|
||||
description = "Cross-platform window setup for bitmap rendering"
|
||||
|
|
126
appveyor.yml
126
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
|
||||
|
|
66
appveyor_rust_install.ps1
Normal file
66
appveyor_rust_install.ps1
Normal 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
38
examples/mouse.rs
Normal 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);
|
||||
}
|
||||
}
|
79
src/lib.rs
79
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.
|
||||
///
|
||||
|
|
29
src/mouse_handler.rs
Normal file
29
src/mouse_handler.rs
Normal 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))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
|
||||
#include "OSXWindow.h"
|
||||
#include "OSXWindowFrameView.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
@public int width;
|
||||
@public int height;
|
||||
@public void* draw_buffer;
|
||||
@private NSTrackingArea* trackingArea;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
12
src/native/macosx/shared_data.h
Normal file
12
src/native/macosx/shared_data.h
Normal 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;
|
||||
|
|
@ -4,11 +4,14 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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) {
|
||||
if (info->key_callback)
|
||||
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);
|
||||
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;
|
||||
|
|
|
@ -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<Vec<Key>> {
|
||||
self.key_handler.get_keys()
|
||||
|
|
|
@ -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<Vec<Key>> {
|
||||
self.key_handler.get_keys()
|
||||
|
|
|
@ -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<u16> {
|
|||
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<HDC>,
|
||||
window: Option<HWND>,
|
||||
buffer: Vec<u32>,
|
||||
|
@ -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<Vec<Key>> {
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue