Merge work from menu branch

This commit is contained in:
Daniel Collin 2016-03-04 17:36:28 +01:00
parent 73813b63f4
commit 5d9ca9e585
15 changed files with 1622 additions and 232 deletions

View file

@ -2,6 +2,13 @@
This project follows semantic versioning.
### v0.5.0 (2016-03-04)
- [changed] - Proper Errors which uses ```std::Error``` as base. ```Window::new``` uses this but the API itself hasn't changed.
- [added] - Menu support on Mac and Windows. See the Menu API functions [here](http://prodbg.com/minifb/minifb/struct.Window.html#method.add_menu)
- [known issue] - ```remove_menu``` doesn't work on Windows [issue](https://github.com/emoon/rust_minifb/issues/16)
- [known issue] - On Mac when running an application from terminal on has to switch to another application and back to get menu focus. [issue](https://github.com/emoon/rust_minifb/issues/17)
### v0.4.0 (2016-01-31)
This release breaks some of the API by changing names and parameters to some functions.

View file

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

View file

@ -14,7 +14,7 @@ Usage
```toml
# Cargo.toml
[dependencies]
minifb = "0.4.0"
minifb = "0.5.0"
```
Example
@ -31,7 +31,7 @@ const HEIGHT: usize = 360;
fn main() {
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
let mut window = match minifb::Window::new("Test - ESC to exit", WIDTH, HEIGHT,
let mut window = match minifb::Window::new("Test - ESC to exit", WIDTH, HEIGHT,
WindowOptions::default()) {
Ok(win) => win,
Err(err) => {
@ -52,7 +52,7 @@ fn main() {
Status
------
Currently Mac, Linux and Windows (64-bit and 32-bit) are the current supported platforms. X11 (Linux/FreeBSD/etc) support has been tested on Ubuntu (x64). Bug report(s) for other OSes/CPUs are welcome!
Currently Mac, Linux and Windows (64-bit and 32-bit) are the current supported platforms. X11 (Linux/FreeBSD/etc) support has been tested on Ubuntu (x64). Bug report(s) for other OSes/CPUs are welcome!
Build instructions
@ -60,7 +60,7 @@ Build instructions
```
cargo build
cargo run --example noise
cargo run --example noise
```
This will run the [noise example](https://github.com/emoon/rust_minifb/blob/master/examples/noise.rs) which should look something like this (Mac screenshot)

126
examples/menu.rs Normal file
View file

@ -0,0 +1,126 @@
extern crate minifb;
use minifb::{Window, Key, Scale, WindowOptions, Menu};
use minifb::{MENU_KEY_CTRL, MENU_KEY_COMMAND};
const WIDTH: usize = 640;
const HEIGHT: usize = 360;
const MENU_TEST_ID: usize = 1;
const OTHER_MENU_ID: usize = 2;
const COLOR_0_ID: usize = 3;
const COLOR_1_ID: usize = 4;
const COLOR_2_ID: usize = 5;
const CLOSE_MENU_ID: usize = 6;
fn main() {
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
let mut window = Window::new("Noise Test - Press ESC to exit",
WIDTH,
HEIGHT,
WindowOptions {
resize: true,
scale: Scale::X2,
..WindowOptions::default()
})
.expect("Unable to Open Window");
// Setup a sub menu
let sub_menu = vec![
Menu {
name: "Color 0",
id: COLOR_0_ID,
..Menu::default()
},
Menu {
name: "Color 1",
id: COLOR_1_ID,
..Menu::default()
},
Menu {
name: "Color 2",
id: COLOR_2_ID,
..Menu::default()
},
];
// Main menu
let menu = vec![
Menu {
name: "Menu Test",
key: Key::W,
id: MENU_TEST_ID,
modifier: MENU_KEY_CTRL,
mac_mod: MENU_KEY_COMMAND,
..Menu::default()
},
Menu::separotor(),
Menu {
name: "Other menu!",
key: Key::S,
modifier: MENU_KEY_CTRL,
mac_mod: MENU_KEY_CTRL,
id: OTHER_MENU_ID,
..Menu::default()
},
Menu {
name: "Remove Menu",
key: Key::R,
id: CLOSE_MENU_ID,
..Menu::default()
},
Menu {
name: "Select Color",
sub_menu: Some(&sub_menu),
..Menu::default()
}
];
window.add_menu("Test", &menu).expect("Unable to add menu");
let mut color_mul = 1;
while window.is_open() && !window.is_key_down(Key::Escape) {
for y in 0..HEIGHT {
for x in 0..WIDTH {
buffer[(y * WIDTH) + x] = (((x ^ y) & 0xff) * color_mul) as u32;
}
}
window.is_menu_pressed().map(|menu_id| {
match menu_id {
COLOR_0_ID => {
color_mul = 0xfe0000;
}
COLOR_1_ID => {
color_mul = 0xff00;
}
COLOR_2_ID => {
color_mul = 1;
}
CLOSE_MENU_ID => {
println!("remove menu");
window.remove_menu("Test").expect("Unable to remove menu");
}
_ => (),
}
println!("Menu id {} pressed", menu_id);
});
window.get_keys().map(|keys| {
for t in keys {
match t {
Key::W => println!("holding w!"),
Key::T => println!("holding t!"),
_ => (),
}
}
});
window.update_with_buffer(&buffer);
}
}

View file

@ -12,8 +12,8 @@ fn main() {
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT,
WindowOptions {
let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT,
WindowOptions {
resize: true,
scale: Scale::X2,
..WindowOptions::default()

48
src/error.rs Normal file
View file

@ -0,0 +1,48 @@
use std::error::Error as StdError;
use std::fmt;
/// Errors that can be return from various operatiors
///
#[derive(Debug)]
pub enum Error {
/// Returned if menu Menu function isn't supported
MenusNotSupported,
/// Menu already exists
MenuExists(String),
/// Menu already exists
WindowCreate(String),
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::MenusNotSupported => "Menus not supported",
Error::MenuExists(_) => "Menu already exists",
Error::WindowCreate(_) => "Failed to create Window",
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
Error::MenusNotSupported => None,
Error::MenuExists(_) => None,
Error::WindowCreate(_) => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::MenusNotSupported => {
write!(fmt, "{}", self.description())
},
Error::MenuExists(ref e) => {
write!(fmt, "{} {:?}", self.description(), e)
},
Error::WindowCreate(ref e) => {
write!(fmt, "{} {:?}", self.description(), e)
}
}
}
}

128
src/key.rs Normal file
View file

@ -0,0 +1,128 @@
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
#[derive(PartialEq, Clone, Copy)]
pub enum Key {
Key0 = 0,
Key1 = 1,
Key2 = 2,
Key3 = 3,
Key4 = 4,
Key5 = 5,
Key6 = 6,
Key7 = 7,
Key8 = 8,
Key9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15,
G = 16,
H = 17,
I = 18,
J = 19,
K = 20,
L = 21,
M = 22,
N = 23,
O = 24,
P = 25,
Q = 26,
R = 27,
S = 28,
T = 29,
U = 30,
V = 31,
W = 32,
X = 33,
Y = 34,
Z = 35,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
Down,
Left,
Right,
Up,
Apostrophe,
Backquote,
Backslash,
Comma,
Equal,
LeftBracket,
Minus,
Period,
RightBracket,
Semicolon,
Slash,
Backspace,
Delete,
End,
Enter,
Escape,
Home,
Insert,
Menu,
PageDown,
PageUp,
Pause,
Space,
Tab,
NumLock,
CapsLock,
ScrollLock,
LeftShift,
RightShift,
LeftCtrl,
RightCtrl,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadDot,
NumPadSlash,
NumPadAsterisk,
NumPadMinus,
NumPadPlus,
NumPadEnter,
LeftAlt,
RightAlt,
LeftSuper,
RightSuper,
/// Used when an Unknown key has been pressed
Unknown,
Count = 107,
}

View file

@ -42,133 +42,6 @@ pub enum MouseButton
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 {
Key0 = 0,
Key1 = 1,
Key2 = 2,
Key3 = 3,
Key4 = 4,
Key5 = 5,
Key6 = 6,
Key7 = 7,
Key8 = 8,
Key9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15,
G = 16,
H = 17,
I = 18,
J = 19,
K = 20,
L = 21,
M = 22,
N = 23,
O = 24,
P = 25,
Q = 26,
R = 27,
S = 28,
T = 29,
U = 30,
V = 31,
W = 32,
X = 33,
Y = 34,
Z = 35,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
Down,
Left,
Right,
Up,
Apostrophe,
Backquote,
Backslash,
Comma,
Equal,
LeftBracket,
Minus,
Period,
RightBracket,
Semicolon,
Slash,
Backspace,
Delete,
End,
Enter,
Escape,
Home,
Insert,
Menu,
PageDown,
PageUp,
Pause,
Space,
Tab,
NumLock,
CapsLock,
ScrollLock,
LeftShift,
RightShift,
LeftCtrl,
RightCtrl,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadDot,
NumPadSlash,
NumPadAsterisk,
NumPadMinus,
NumPadPlus,
NumPadEnter,
LeftAlt,
RightAlt,
LeftSuper,
RightSuper,
/// Used when an Unknown key has been pressed
Unknown,
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)]
@ -186,10 +59,24 @@ extern crate libc;
use std::os::raw;
#[doc(hidden)]
mod error;
pub use self::error::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub mod key;
pub use key::Key as Key;
pub mod os;
mod mouse_handler;
mod key_handler;
mod window_flags;
mod menu;
pub use menu::Menu as Menu;
pub use menu::MENU_KEY_COMMAND;
pub use menu::MENU_KEY_WIN;
pub use menu::MENU_KEY_SHIFT;
pub use menu::MENU_KEY_CTRL;
pub use menu::MENU_KEY_ALT;
#[cfg(target_os = "macos")]
use self::os::macos as imp;
@ -206,14 +93,14 @@ pub struct Window(imp::Window);
///
/// WindowOptions is creation settings for the window. By default the settings are defined for
/// displayng a 32-bit buffer (no scaling of window is possible)
/// displayng a 32-bit buffer (no scaling of window is possible)
///
pub struct WindowOptions {
/// If the window should be borderless (default: false)
pub borderless: bool,
/// If the window should have a title (default: true)
pub title: bool,
/// If it should be possible to resize the window (default: false)
/// If it should be possible to resize the window (default: false)
pub resize: bool,
/// Scale of the window that used in conjunction with update_with_buffer (default: X1)
pub scale: Scale
@ -265,10 +152,10 @@ impl Window {
/// Open up a window that is resizeable
///
/// ```ignore
/// let mut window = match Window::new("Test", 640, 400,
/// let mut window = match Window::new("Test", 640, 400,
/// WindowOptions {
/// resize: true,
/// ..WindowOptions::default()
/// ..WindowOptions::default()
/// }) {
/// Ok(win) => win,
/// Err(err) => {
@ -277,7 +164,7 @@ impl Window {
/// }
///};
/// ```
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
imp::Window::new(name, width, height, opts).map(Window)
}
@ -297,7 +184,7 @@ impl Window {
}
///
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
/// the size of the created window
///
/// # Examples
@ -315,7 +202,7 @@ impl Window {
}
///
/// Updates the window (this is required to call in order to get keyboard/mouse input, etc)
/// Updates the window (this is required to call in order to get keyboard/mouse input, etc)
///
/// # Examples
///
@ -334,7 +221,7 @@ impl Window {
///
/// Checks if the window is still open. A window can be closed by the user (by for example
/// pressing the close button on the window) It's up to the user to make sure that this is
/// being checked and take action depending on the state.
/// being checked and take action depending on the state.
///
/// # Examples
///
@ -375,31 +262,31 @@ impl Window {
/// 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
/// 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)
/// println!("is left down? {}", left_down)
/// ```
///
///
#[inline]
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
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
/// 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.
///
@ -418,7 +305,7 @@ impl Window {
}
///
/// Get the current keys that are down.
/// Get the current keys that are down.
///
/// # Examples
///
@ -460,7 +347,7 @@ impl Window {
self.0.get_keys_pressed(repeat)
}
///
///
/// Check if a single key is down.
///
/// # Examples
@ -476,7 +363,7 @@ impl Window {
self.0.is_key_down(key)
}
///
///
/// Check if a single key is pressed. KeyRepeat will control if the key should be repeated or
/// not while being pressed.
///
@ -515,13 +402,65 @@ impl Window {
/// # Examples
///
/// ```ignore
/// window.set_key_repeat_rate(0.01) // 0.01 sec between keys
/// window.set_key_repeat_rate(0.01) // 0.01 sec between keys
/// ```
///
#[inline]
pub fn set_key_repeat_rate(&mut self, rate: f32) {
self.0.set_key_repeat_rate(rate)
}
///
/// Returns if this windows is the current active one
///
#[inline]
pub fn is_active(&mut self) -> bool {
self.0.is_active()
}
///
/// This allows adding menus to your windows. As menus behaves a bit diffrently depending on
/// Operating system here is how it works. See [Menu] for description on each field.
///
/// ```ignore
/// Windows:
/// Each window has their own menu and shortcuts are active depending on active window.
/// Mac:
/// As Mac uses one menu for the whole program the menu will change depending
/// on which window you have active.
/// Linux/BSD/etc:
/// Menus aren't supported as they depend on each WindowManager and is outside of the
/// scope for this library to support.
/// ```
///
#[inline]
pub fn add_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
self.0.add_menu(menu_name, menu)
}
///
/// Updates an existing menu created with [add_menu]
///
#[inline]
pub fn update_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
self.0.update_menu(menu_name, menu)
}
///
/// Remove a menu that has been added with [add_menu]
///
#[inline]
pub fn remove_menu(&mut self, menu_name: &str) -> Result<()> {
self.0.remove_menu(menu_name)
}
///
/// Check if a menu item has been pressed
///
#[inline]
pub fn is_menu_pressed(&mut self) -> Option<usize> {
self.0.is_menu_pressed()
}
}
// Impl for WindowOptions

59
src/menu.rs Normal file
View file

@ -0,0 +1,59 @@
use Key;
/// Command key on Mac OS
pub const MENU_KEY_COMMAND: usize = 1;
/// Windows key on Windows
pub const MENU_KEY_WIN: usize = 2;
/// Shift key
pub const MENU_KEY_SHIFT: usize = 4;
/// Control key
pub const MENU_KEY_CTRL: usize = 8;
/// Alt key
pub const MENU_KEY_ALT: usize = 16;
const MENU_ID_SEPARATOR:usize = 0xffffffff;
///
/// Used to hold the data for creating menus for the Application
///
pub struct Menu<'a> {
/// Name of the menu item
pub name: &'a str,
/// User-defined Id thot will be sent back to the application in [get_menu_event]
pub id: usize,
/// Shortcut key for the menu item
pub key: Key,
/// Modifier on Windows for the menu
pub modifier: usize,
/// Modifier on Mac OS
pub mac_mod: usize,
/// Menu item should be enabled on grayed out
pub enabled: bool,
/// Sub-menu. Vector of a sub-menu, otherwise None if no sub-menu
pub sub_menu: Option<&'a Vec<Menu<'a>>>,
}
impl<'a> Menu<'a> {
pub fn separotor() -> Menu<'a> {
Menu {
id: MENU_ID_SEPARATOR,
.. Self::default()
}
}
}
impl<'a> Default for Menu<'a> {
fn default() -> Menu<'a> {
Menu {
name: "",
id: 0,
key: Key::Unknown,
modifier: 0,
mac_mod: 0,
enabled: true,
sub_menu: None,
}
}
}

View file

@ -7,9 +7,11 @@
static bool s_init = false;
// window_handler.rs
const uint32_t WINDOW_BORDERLESS = 1 << 1;
const uint32_t WINDOW_RESIZE = 1 << 2;
const uint32_t WINDOW_TITLE = 1 << 3;
const uint32_t WINDOW_BORDERLESS = 1 << 1;
const uint32_t WINDOW_RESIZE = 1 << 2;
const uint32_t WINDOW_TITLE = 1 << 3;
static void create_standard_menu();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -21,27 +23,30 @@ const uint32_t WINDOW_TITLE = 1 << 3;
void* mfb_open(const char* name, int width, int height, uint32_t flags, int scale)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
bool prev_init = s_init;
//NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
if (!s_init) {
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
create_standard_menu();
s_init = true;
}
uint32_t styles = NSClosableWindowMask | NSMiniaturizableWindowMask;
if (flags & WINDOW_BORDERLESS)
styles |= NSBorderlessWindowMask;
styles |= NSBorderlessWindowMask;
if (flags & WINDOW_RESIZE)
styles |= NSResizableWindowMask;
styles |= NSResizableWindowMask;
if (flags & WINDOW_TITLE)
styles |= NSTitledWindowMask;
styles |= NSTitledWindowMask;
NSRect rectangle = NSMakeRect(0, 0, width * scale, (height * scale));
OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
if (!window)
@ -57,6 +62,10 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
window->scale = scale;
window->key_callback = 0;
window->shared_data = 0;
window->active_menu_id = -1;
window->menu_data = malloc(sizeof(MenuData));
memset(window->menu_data, 0, sizeof(MenuData));
[window updateSize];
@ -69,11 +78,124 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
[NSApp activateIgnoringOtherApps:YES];
[pool drain];
if (!prev_init)
[NSApp finishLaunching];
//[pool drain];
return window;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static NSString* findAppName(void)
{
size_t i;
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
// Keys to search for as potential application names
NSString* GLFWNameKeys[] =
{
@"CFBundleDisplayName",
@"CFBundleName",
@"CFBundleExecutable",
};
for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++)
{
id name = [infoDictionary objectForKey:GLFWNameKeys[i]];
if (name &&
[name isKindOfClass:[NSString class]] &&
![name isEqualToString:@""])
{
return name;
}
}
extern char** _NSGetProgname();
char* progname = *_NSGetProgname();
if (progname)
return [NSString stringWithUTF8String:progname];
// Really shouldn't get here
return @"Unknown";
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void create_standard_menu(void)
{
NSString* appName = findAppName();
NSMenu* bar = [[NSMenu alloc] init];
[NSApp setMainMenu:bar];
NSMenuItem* appMenuItem =
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
NSMenu* appMenu = [[NSMenu alloc] init];
[appMenuItem setSubmenu:appMenu];
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
NSMenu* servicesMenu = [[NSMenu alloc] init];
[NSApp setServicesMenu:servicesMenu];
[[appMenu addItemWithTitle:@"Services"
action:NULL
keyEquivalent:@""] setSubmenu:servicesMenu];
[servicesMenu release];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
action:@selector(hide:)
keyEquivalent:@"h"];
[[appMenu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"]
setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
[appMenu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
action:@selector(terminate:)
keyEquivalent:@"q"];
/*
NSMenuItem* windowMenuItem =
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
[bar release];
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
[NSApp setWindowsMenu:windowMenu];
[windowMenuItem setSubmenu:windowMenu];
[windowMenu addItemWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[windowMenu addItemWithTitle:@"Zoom"
action:@selector(performZoom:)
keyEquivalent:@""];
[windowMenu addItem:[NSMenuItem separatorItem]];
[windowMenu addItemWithTitle:@"Bring All to Front"
action:@selector(arrangeInFront:)
keyEquivalent:@""];
// TODO: Make this appear at the bottom of the menu (for consistency)
[windowMenu addItem:[NSMenuItem separatorItem]];
[[windowMenu addItemWithTitle:@"Enter Full Screen"
action:@selector(toggleFullScreen:)
keyEquivalent:@"f"]
setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
*/
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
// to get the application menu working properly.
//SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
//[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_close(void* win)
@ -83,7 +205,7 @@ void mfb_close(void* win)
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
if (window)
[window close];
[window close];
[pool drain];
}
@ -92,13 +214,28 @@ void mfb_close(void* win)
static int update_events()
{
int state = 0;
NSEvent* event;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
[NSApp sendEvent:event];
do
{
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event) {
[NSApp sendEvent:event];
}
}
while (event);
[pool release];
return state;
/*
int state = 0;
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
[NSApp sendEvent:event];
*/
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -142,14 +279,14 @@ int mfb_update_with_buffer(void* window, void* buffer)
static float transformY(float y)
{
float b = CGDisplayBounds(CGMainDisplayID()).size.height;
float b = CGDisplayBounds(CGMainDisplayID()).size.height;
float t = b - y;
return t;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_set_position(void* window, int x, int y)
void mfb_set_position(void* window, int x, int y)
{
OSXWindow* win = (OSXWindow*)window;
const NSRect contentRect = [[win contentView] frame];
@ -160,7 +297,7 @@ void mfb_set_position(void* window, int x, int y)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int mfb_should_close(void* window)
int mfb_should_close(void* window)
{
OSXWindow* win = (OSXWindow*)window;
return win->should_close;
@ -168,7 +305,7 @@ int mfb_should_close(void* window)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t mfb_get_screen_size()
uint32_t mfb_get_screen_size()
{
NSRect e = [[NSScreen mainScreen] frame];
uint32_t w = (uint32_t)e.size.width;
@ -193,4 +330,104 @@ void mfb_set_mouse_data(void* window, SharedData* shared_data)
win->shared_data = shared_data;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t mfb_is_active(void* window)
{
OSXWindow* win = (OSXWindow*)window;
return win->is_active;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_remove_menu(void* window, const char* name)
{
OSXWindow* win = (OSXWindow*)window;
NSString* ns_name = [NSString stringWithUTF8String: name];
NSMenu* main_menu = [NSApp mainMenu];
int len = win->menu_data->menu_count;
for (int i = 0; i < len; ++i)
{
Menu* menu = &win->menu_data->menus[i];
if (strcmp(menu->name, name))
continue;
Menu* menu_end = &win->menu_data->menus[len - 1];
[main_menu removeItem:menu->menu_item];
// swap remove
menu->name = menu_end->name;
menu->menu = menu_end->menu;
menu->menu_item = menu_end->menu_item;
win->menu_data->menu_count--;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int mfb_active_menu(void* window) {
OSXWindow* win = (OSXWindow*)window;
int active_menu_id = win->active_menu_id;
win->active_menu_id = -1;
return active_menu_id;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_add_menu(void* window, const char* name, void* m)
{
OSXWindow* win = (OSXWindow*)window;
const char* n = strdup(name);
NSString* ns_name = [NSString stringWithUTF8String: n];
NSMenu* main_menu = [NSApp mainMenu];
NSMenuItem* windowMenuItem = [main_menu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:ns_name];
[NSApp setWindowsMenu:windowMenu];
[windowMenuItem setSubmenu:windowMenu];
MenuDesc* menu_desc = (MenuDesc*)m;
[windowMenu setAutoenablesItems:NO];
build_submenu(windowMenu, menu_desc);
Menu* menu = &win->menu_data->menus[win->menu_data->menu_count++];
menu->name = n;
menu->menu = windowMenu;
menu->menu_item = windowMenuItem;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mfb_update_menu(void* window, const char* name, void* m)
{
OSXWindow* win = (OSXWindow*)window;
NSString* ns_name = [NSString stringWithUTF8String: name];
NSMenu* main_menu = [NSApp mainMenu];
int len = win->menu_data->menu_count;
for (int i = 0; i < len; ++i)
{
Menu* menu = &win->menu_data->menus[i];
if (!strcmp(menu->name, name)) {
[menu->menu removeAllItems];
build_submenu(menu->menu, (MenuDesc*)m);
return;
}
}
}

View file

@ -1,6 +1,42 @@
#import <Cocoa/Cocoa.h>
#include "shared_data.h"
#define MAX_MENUS 512
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct Menu
{
const char* name;
NSMenu* menu;
NSMenuItem* menu_item;
} Menu;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct MenuData
{
Menu menus[MAX_MENUS];
int menu_count;
} MenuData;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct MenuDesc {
char name[512];
struct MenuDesc* sub_menu;
int menu_id;
int key;
int special_key;
int modifier;
int modifier_mac;
int enabled;
} MenuDesc;
void build_submenu(NSMenu* menu, MenuDesc* desc);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface OSXWindow : NSWindow
{
NSView* childContentView;
@ -12,6 +48,11 @@
@public void* rust_data;
@public SharedData* shared_data;
@public bool should_close;
@public bool is_active;
@public int active_menu_id;
@public MenuData* menu_data;
}
@end

View file

@ -1,5 +1,6 @@
#import "OSXWindow.h"
#import "OSXWindowFrameView.h"
#include <Carbon/Carbon.h>
@implementation OSXWindow
@ -20,7 +21,7 @@
NSSize childBoundsSize = [childContentView bounds].size;
sizeDelta.width -= childBoundsSize.width;
sizeDelta.height -= childBoundsSize.height;
OSXWindowFrameView *frameView = [super contentView];
NSSize newFrameSize = [frameView bounds].size;
newFrameSize.width += sizeDelta.width;
@ -37,7 +38,7 @@
// Left Shift
key_callback(rust_data, 0x38, flags == 0x20102 ? 1 : 0);
// RightShift
key_callback(rust_data, 0x3c, flags == 0x20104 ? 1 : 0);
@ -58,21 +59,19 @@
// Right Super
key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0);
[super flagsChanged:event];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyDown:(NSEvent *)event
{
// Cmd+Q always closes app
if ([event.characters.uppercaseString isEqualToString:@"Q"] && ([event modifierFlags] & NSCommandKeyMask)) {
[self performClose:self];
return;
}
if (key_callback) {
key_callback(rust_data, [event keyCode], 1);
}
[super keyDown:event];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -82,17 +81,26 @@
if (key_callback) {
key_callback(rust_data, [event keyCode], 0);
}
[super keyDown:event];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mainWindowChanged:(NSNotification *)aNotification
- (void)mainWindowChanged:(NSNotification *)note
{
void* window = [note object];
if (window == self) {
self->is_active = true;
} else {
self->is_active = false;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)windowWillClose:(NSNotification *)notification
- (void)windowWillClose:(NSNotification *)notification
{
should_close = true;
}
@ -111,21 +119,33 @@
{
if ([childContentView isEqualTo:aView])
return;
NSRect bounds = [self frame];
bounds.origin = NSZeroPoint;
OSXWindowFrameView* frameView = [super contentView];
if (!frameView)
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(mainWindowChanged:)
name:NSWindowDidBecomeMainNotification
object:self];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(mainWindowChanged:)
name:NSWindowDidResignMainNotification
object:self];
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
frameView->width = width;
frameView->height = height;
frameView->draw_buffer = draw_buffer;
frameView->width = width;
frameView->height = height;
frameView->draw_buffer = draw_buffer;
frameView->scale = scale;
[super setContentView:frameView];
}
if (childContentView)
[childContentView removeFromSuperview];
@ -168,16 +188,149 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)updateSize
- (void)updateSize
{
OSXWindowFrameView* frameView = [super contentView];
if (frameView)
{
frameView->width = width;
frameView->height = height;
frameView->draw_buffer = draw_buffer;
frameView->width = width;
frameView->height = height;
frameView->draw_buffer = draw_buffer;
frameView->scale = scale;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)onMenuPress:(id)sender
{
int menu_id = (int)((NSButton*)sender).tag;
self->active_menu_id = menu_id;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static CFStringRef create_string_for_key(CGKeyCode keyCode)
{
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
if (!layoutData)
return 0;
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
UInt32 keysDown = 0;
UniChar chars[4];
UniCharCount realLength;
UCKeyTranslate(keyboardLayout,
keyCode,
kUCKeyActionDisplay,
0,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&keysDown,
sizeof(chars) / sizeof(chars[0]),
&realLength,
chars);
CFRelease(currentKeyboard);
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static NSString* convert_key_code_to_string(int key)
{
if (key < 128)
{
NSString* charName = (NSString*)create_string_for_key(key);
if (charName)
return charName;
return [NSString stringWithFormat:@"%c", (char)key];
}
return [NSString stringWithFormat:@"%C", (uint16_t)key];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const uint32_t MENU_KEY_COMMAND = 1;
const uint32_t MENU_KEY_WIN = 2;
const uint32_t MENU_KEY_SHIFT= 4;
const uint32_t MENU_KEY_CTRL = 8;
const uint32_t MENU_KEY_ALT = 16;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void build_submenu(NSMenu* menu, MenuDesc* desc)
{
[menu removeAllItems];
while (desc->menu_id != -2)
{
NSString* name = [NSString stringWithUTF8String: desc->name];
if (desc->menu_id == -1)
{
[menu addItem:[NSMenuItem separatorItem]];
}
else if (desc->sub_menu)
{
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:NULL keyEquivalent:@""];
NSMenu* newMenu = [[NSMenu alloc] initWithTitle:name];
[newItem setSubmenu:newMenu];
build_submenu(newMenu, desc->sub_menu);
[newMenu release];
[menu addItem:newItem];
[newItem release];
}
else
{
int mask = 0;
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:@selector(onMenuPress:) keyEquivalent:@""];
[newItem setTag:desc->menu_id];
if (desc->modifier_mac & MENU_KEY_COMMAND) {
mask |= NSCommandKeyMask;
}
if (desc->modifier_mac & MENU_KEY_SHIFT) {
mask |= NSShiftKeyMask;
}
if (desc->modifier_mac & MENU_KEY_CTRL) {
mask |= NSControlKeyMask;
}
if (desc->modifier_mac & MENU_KEY_ALT) {
mask |= NSAlternateKeyMask;
}
if (desc->key != 0x7f) {
NSString* key = convert_key_code_to_string(desc->key);
if (key) {
[newItem setKeyEquivalentModifierMask: mask];
[newItem setKeyEquivalent:key];
}
}
if (desc->enabled) {
[newItem setEnabled:YES];
} else {
[newItem setEnabled:NO];
}
[newItem setOnStateImage: newItem.offStateImage];
[menu addItem:newItem];
[newItem release];
}
desc++;
}
}
@end

View file

@ -2,8 +2,11 @@
use {MouseButton, MouseMode, Scale, Key, KeyRepeat, WindowOptions};
use key_handler::KeyHandler;
use error::Error;
use Result;
use mouse_handler;
use window_flags;
use menu::Menu;
use libc::{c_void, c_char, c_uchar};
use std::ffi::{CString};
@ -13,7 +16,7 @@ use std::os::raw;
// Table taken from GLFW and slightly modified
static KEY_MAPPINGS: [Key; 128] = [
static KEY_MAPPINGS: [Key; 128] = [
/* 00 */ Key::A,
/* 01 */ Key::S,
/* 02 */ Key::D,
@ -80,7 +83,7 @@ static KEY_MAPPINGS: [Key; 128] = [
/* 3f */ Key::Unknown, // Function
/* 40 */ Key::Unknown, // F17
/* 41 */ Key::Unknown, // Decimal
/* 42 */ Key::Unknown,
/* 42 */ Key::Unknown,
/* 43 */ Key::Unknown, // Multiply
/* 44 */ Key::Unknown,
/* 45 */ Key::Unknown, // Add
@ -89,7 +92,7 @@ static KEY_MAPPINGS: [Key; 128] = [
/* 48 */ Key::Unknown, // VolumeUp
/* 49 */ Key::Unknown, // VolumeDown
/* 4a */ Key::Unknown, // Mute
/* 4b */ Key::Unknown,
/* 4b */ Key::Unknown,
/* 4c */ Key::Enter,
/* 4d */ Key::Unknown,
/* 4e */ Key::Unknown, // Subtrackt
@ -144,7 +147,23 @@ static KEY_MAPPINGS: [Key; 128] = [
/* 7f */ Key::Unknown,
];
const STRING_SIZE: usize = 512;
#[repr(C)]
struct CMenu {
name: [i8; STRING_SIZE],
sub_menu: *mut raw::c_void,
id: raw::c_int,
key: raw::c_int,
special_key: raw::c_int,
modifier: raw::c_int,
mac_mod: raw::c_int,
enabled: raw::c_int,
}
#[link(name = "Cocoa", kind = "framework")]
#[link(name = "Carbon", kind = "framework")]
extern {
fn mfb_open(name: *const c_char, width: u32, height: u32, flags: u32, scale: i32) -> *mut c_void;
fn mfb_close(window: *mut c_void);
@ -155,6 +174,11 @@ extern {
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;
fn mfb_is_active(window: *mut c_void) -> u32;
fn mfb_add_menu(window: *mut c_void, name: *const c_char, menu: *mut c_void);
fn mfb_remove_menu(window: *mut c_void, name: *const c_char);
fn mfb_update_menu(window: *mut c_void, name: *const c_char, menu: *mut c_void);
fn mfb_active_menu(window: *mut c_void) -> i32;
}
#[derive(Default)]
@ -171,7 +195,7 @@ pub struct SharedData {
pub struct Window {
window_handle: *mut c_void,
scale_factor: usize,
scale_factor: usize,
pub shared_data: SharedData,
key_handler: KeyHandler,
pub has_set_data: bool,
@ -190,11 +214,11 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
}
impl Window {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
let n = match CString::new(name) {
Err(_) => {
Err(_) => {
println!("Unable to convert {} to c_string", name);
return Err("Unable to set correct name");
return Err(Error::WindowCreate("Unable to set correct name".to_owned()));
}
Ok(n) => n,
};
@ -204,13 +228,13 @@ impl Window {
let handle = mfb_open(n.as_ptr(), width as u32, height as u32, window_flags::get_flags(opts), scale_factor as i32);
if handle == ptr::null_mut() {
return Err("Unable to open Window");
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
}
Ok(Window {
Ok(Window {
window_handle: handle,
scale_factor: scale_factor,
shared_data: SharedData {
shared_data: SharedData {
width: width as u32 * scale_factor as u32,
height: height as u32 * scale_factor as u32,
.. SharedData::default()
@ -223,7 +247,7 @@ impl Window {
#[inline]
pub fn get_window_handle(&self) -> *mut raw::c_void {
self.window_handle as *mut raw::c_void
self.window_handle as *mut raw::c_void
}
#[inline]
@ -313,11 +337,62 @@ impl Window {
self.key_handler.is_key_pressed(key, repeat)
}
pub fn is_menu_pressed(&mut self) -> Option<usize> {
let menu_id = unsafe { mfb_active_menu(self.window_handle) };
if menu_id < 0 {
None
} else {
Some(menu_id as usize)
}
}
pub fn add_menu(&mut self, name: &str, menu: &Vec<Menu>) -> Result<()> {
let mut build_menu = Vec::<Vec<CMenu>>::new();
unsafe {
Self::recursive_convert(&mut build_menu, &Some(menu));
let menu_len = build_menu.len();
mfb_add_menu(self.window_handle,
CString::new(name).unwrap().as_ptr(),
build_menu[menu_len - 1].as_mut_ptr() as *mut c_void);
}
Ok(())
}
pub fn update_menu(&mut self, name: &str, menu: &Vec<Menu>) -> Result<()> {
let mut build_menu = Vec::<Vec<CMenu>>::new();
unsafe {
Self::recursive_convert(&mut build_menu, &Some(menu));
let menu_len = build_menu.len();
mfb_update_menu(self.window_handle,
CString::new(name).unwrap().as_ptr(),
build_menu[menu_len - 1].as_mut_ptr() as *mut c_void);
}
Ok(())
}
pub fn remove_menu(&mut self, name: &str) -> Result<()> {
unsafe {
mfb_remove_menu(self.window_handle, CString::new(name).unwrap().as_ptr());
}
Ok(())
}
#[inline]
pub fn is_open(&self) -> bool {
unsafe { mfb_should_close(self.window_handle) == 0 }
}
#[inline]
pub fn is_active(&mut self) -> bool {
unsafe { mfb_is_active(self.window_handle) == 0 }
}
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
let factor: i32 = match scale {
Scale::X1 => 1,
@ -328,8 +403,8 @@ impl Window {
Scale::X32 => 32,
Scale::FitScreen => {
let wh: u32 = mfb_get_screen_size();
let screen_x = (wh >> 16) as i32;
let screen_y = (wh & 0xffff) as i32;
let screen_x = (wh >> 16) as i32;
let screen_y = (wh & 0xffff) as i32;
let mut scale = 1i32;
@ -350,6 +425,168 @@ impl Window {
return factor;
}
unsafe fn map_key_to_menu_key(key: Key) -> i32 {
match key {
Key::A => 0x00,
Key::S => 0x01,
Key::D => 0x02,
Key::F => 0x03,
Key::H => 0x04,
Key::G => 0x05,
Key::Z => 0x06,
Key::X => 0x07,
Key::C => 0x08,
Key::V => 0x09,
Key::B => 0x0b,
Key::Q => 0x0c,
Key::W => 0x0d,
Key::E => 0x0e,
Key::R => 0x0f,
Key::Y => 0x10,
Key::T => 0x11,
Key::Key1 => 0x12,
Key::Key2 => 0x13,
Key::Key3 => 0x14,
Key::Key4 => 0x15,
Key::Key6 => 0x16,
Key::Key5 => 0x17,
Key::Equal => 0x18,
Key::Key9 => 0x19,
Key::Key7 => 0x1a,
Key::Minus => 0x1b,
Key::Key8 => 0x1c,
Key::Key0 => 0x1d,
Key::RightBracket => 0x1e,
Key::O => 0x1f,
Key::U => 0x20,
Key::LeftBracket => 0x21,
Key::I => 0x22,
Key::P => 0x23,
Key::Enter => 0x24,
Key::L => 0x25,
Key::J => 0x26,
Key::Apostrophe => 0x27,
Key::K => 0x28,
Key::Semicolon => 0x29,
Key::Backslash => 0x2a,
Key::Comma => 0x2b,
Key::Slash => 0x2c,
Key::N => 0x2d,
Key::M => 0x2e,
Key::Period => 0x2f,
//Key::Tab => 0x30,
Key::Space => 0x31,
//Key::Backspace => 0x33,
//Key::Escape => 0x35,
Key::RightSuper => 0x36,
Key::LeftSuper => 0x37,
Key::LeftShift => 0x38,
Key::CapsLock => 0x39,
Key::LeftAlt => 0x3a,
Key::LeftCtrl => 0x3b,
Key::RightShift => 0x3c,
Key::RightAlt => 0x3d,
Key::RightCtrl => 0x3e,
//Key::Equal => 0x51,
Key::NumPad0 => 0x52,
Key::NumPad1 => 0x53,
Key::NumPad2 => 0x54,
Key::NumPad3 => 0x55,
Key::NumPad4 => 0x56,
Key::NumPad5 => 0x57,
Key::NumPad6 => 0x58,
Key::NumPad7 => 0x59,
Key::NumPad8 => 0x5b,
Key::NumPad9 => 0x5c,
Key::F5 => 0x60,
Key::F6 => 0x61,
Key::F7 => 0x62,
Key::F3 => 0x63,
Key::F8 => 0x64,
Key::F9 => 0x65,
Key::F11 => 0x67,
Key::F14 => 0x6b,
Key::F10 => 0x6d,
Key::F12 => 0x6f,
Key::F15 => 0x71,
Key::Insert => 0x72, /* Really Help... */
Key::Home => 0x73,
//Key::PageUp => 0x74,
Key::Delete => 0x75,
Key::F4 => 0x76,
Key::End => 0x77,
Key::F2 => 0x78,
//Key::PageDown => 0x79,
Key::F1 => 0x7a,
//Key::Left => 0x7b,
//Key::Right => 0x7c,
//Key::Down => 0x7d,
//Key::Up => 0x7e,
Key::Left => 0x2190,
Key::Up => 0x2191,
Key::Down => 0x2193,
Key::Right => 0x2192,
Key::Escape => 0x238b,
//Key::Enter => 0x000d,
Key::Backspace => 0x232b,
Key::Tab => 0x21e4,
Key::PageUp => 0x21de,
Key::PageDown => 0x21df,
_ => 0x7f,
}
}
unsafe fn recursive_convert(menu_build_vec: &mut Vec<Vec<CMenu>>, in_menu: &Option<&Vec<Menu>>) -> *mut raw::c_void {
if in_menu.is_none() {
return ptr::null_mut();
}
let mut menu_build = Vec::<CMenu>::new();
let menu_vec = in_menu.as_ref().unwrap();
for m in menu_vec.iter() {
let key_map = Self::map_key_to_menu_key(m.key);
let mut menu = CMenu {
name: mem::uninitialized(),
id: m.id as raw::c_int,
key: key_map as raw::c_int,
special_key: 0,
modifier: m.modifier as raw::c_int,
mac_mod: m.mac_mod as raw::c_int,
enabled: m.enabled as raw::c_int,
sub_menu : Self::recursive_convert(menu_build_vec, &m.sub_menu),
};
let name = CString::new(m.name).unwrap();
let name_len = m.name.len();
ptr::copy_nonoverlapping(name.as_ptr(),
menu.name.as_mut_ptr() as *mut i8,
name_len);
menu.name[name_len] = 0;
menu_build.push(menu);
}
// end marker
menu_build.push(CMenu {
name: [0; STRING_SIZE],
id: -2,
key: 0,
special_key: 0,
modifier: 0,
mac_mod: 0,
enabled: 0,
sub_menu : ptr::null_mut(),
});
let ptr = menu_build.as_mut_ptr() as *mut raw::c_void ;
menu_build_vec.push(menu_build);
ptr
}
}
impl Drop for Window {

View file

@ -8,7 +8,10 @@ extern crate x11_dl;
use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions};
use key_handler::KeyHandler;
use menu::Menu;
use self::x11_dl::keysym::*;
use error::Error;
use Result;
use libc::{c_void, c_char, c_uchar};
use std::ffi::{CString};
@ -163,31 +166,31 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) {
}
impl Window {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
let n = match CString::new(name) {
Err(_) => {
Err(_) => {
println!("Unable to convert {} to c_string", name);
return Err("Unable to set correct name");
return Err(Error::WindowCreate("Unable to set correct name".to_owned()));
}
Ok(n) => n,
};
unsafe {
let scale = Self::get_scale_factor(width, height, opts.scale);
let handle = mfb_open(n.as_ptr(),
width as u32,
height as u32,
window_flags::get_flags(opts),
let handle = mfb_open(n.as_ptr(),
width as u32,
height as u32,
window_flags::get_flags(opts),
scale);
if handle == ptr::null_mut() {
return Err("Unable to open Window");
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
}
Ok(Window {
Ok(Window {
window_handle: handle,
shared_data: SharedData {
scale: scale as f32,
shared_data: SharedData {
scale: scale as f32,
.. SharedData::default()
},
key_handler: KeyHandler::new(),
@ -238,7 +241,7 @@ impl Window {
}
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
match button {
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,
@ -246,7 +249,7 @@ impl Window {
}
pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> {
if self.shared_data.scroll_x.abs() > 0.0 ||
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 {
@ -289,6 +292,12 @@ impl Window {
unsafe { mfb_should_close(self.window_handle) == 1 }
}
#[inline]
pub fn is_active(&mut self) -> bool {
// TODO: Proper implementation
true
}
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
let factor: i32 = match scale {
Scale::X1 => 1,
@ -296,8 +305,8 @@ impl Window {
Scale::X4 => 4,
Scale::FitScreen => {
let wh: u32 = mfb_get_screen_size();
let screen_x = (wh >> 16) as i32;
let screen_y = (wh & 0xffff) as i32;
let screen_x = (wh >> 16) as i32;
let screen_y = (wh & 0xffff) as i32;
println!("{} - {}", screen_x, screen_y);
@ -329,6 +338,19 @@ impl Window {
return factor;
}
pub fn add_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> Result<()> {
Err(Error::MenusNotSupported)
}
pub fn update_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> Result<()> {
Err(Error::MenusNotSupported)
}
pub fn remove_menu(&mut self, _menu_name: &str) -> Result<()> {
Err(Error::MenusNotSupported)
}
pub fn is_menu_pressed(&mut self) -> Option<usize> {
None
}
}
impl Drop for Window {

View file

@ -6,9 +6,15 @@ extern crate winapi;
extern crate gdi32;
extern crate time;
const INVALID_ACCEL: usize = 0xffffffff;
use {Scale, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions};
use key_handler::KeyHandler;
use menu::Menu;
use error::Error;
use Result;
use menu::{MENU_KEY_WIN, MENU_KEY_SHIFT, MENU_KEY_CTRL, MENU_KEY_ALT};
use std::ptr;
use std::os::windows::ffi::OsStrExt;
@ -17,11 +23,16 @@ use std::mem;
use std::os::raw;
use mouse_handler;
use self::winapi::windef::HWND;
use self::winapi::windef::HDC;
use self::winapi::winuser::WNDCLASSW;
use self::winapi::wingdi::BITMAPINFOHEADER;
use self::winapi::wingdi::RGBQUAD;
//use self::winapi::windef::HWND;
//use self::winapi::windef::HDC;
//use self::winapi::windef::HMENU;
//use self::winapi::wingdi::BITMAPINFOHEADER;
//use self::winapi::wingdi::RGBQUAD;
//use self::winapi::winuser::WNDCLASSW;
//use self::winapi::winuser::ACCEL;
//use self::winapi::basetsd::UINT_PTR;
use self::winapi::*;
//use self::winapi::winmindefs::BYTE;
// Wrap this so we can have a proper numbef of bmiColors to write in
#[repr(C)]
@ -226,6 +237,12 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND,
return 0;
}
winapi::winuser::WM_COMMAND => {
if lparam == 0 {
wnd.accel_key = (wparam & 0xffff) as usize;
}
}
winapi::winuser::WM_PAINT => {
// if we have nothing to draw here we return the default function
@ -288,6 +305,12 @@ struct MouseData {
pub scroll: f32,
}
struct MenuStore {
name: String,
menu: HMENU,
accel_items: Vec<ACCEL>,
}
pub struct Window {
mouse: MouseData,
dc: Option<HDC>,
@ -297,7 +320,18 @@ pub struct Window {
scale_factor: i32,
width: i32,
height: i32,
menus: Vec<MenuStore>,
key_handler: KeyHandler,
accel_table: HACCEL,
accel_key: usize,
}
// TranslateAccelerator is currently missing in win-rs
#[cfg(target_family = "windows")]
#[link(name = "user32")]
#[allow(non_snake_case)]
extern "system" {
fn TranslateAcceleratorW(hWnd: HWND, accel: *const ACCEL, pmsg: *const MSG) -> INT;
}
impl Window {
@ -347,7 +381,7 @@ impl Window {
let mut flags = 0;
if opts.title {
flags |= winapi::WS_OVERLAPPEDWINDOW as u32;
flags |= winapi::WS_OVERLAPPEDWINDOW as u32;
}
if opts.resize {
@ -363,7 +397,7 @@ impl Window {
}
let handle = user32::CreateWindowExW(0,
class_name.as_ptr(),
class_name.as_ptr(),
window_name.as_ptr(),
flags,
winapi::CW_USEDEFAULT,
@ -389,14 +423,14 @@ impl Window {
width: usize,
height: usize,
opts: WindowOptions)
-> Result<Window, &str> {
-> Result<Window> {
unsafe {
let scale_factor = Self::get_scale_factor(width, height, opts.scale);
let handle = Self::open_window(name, width, height, opts, scale_factor);
if handle.is_none() {
return Err("Unable to create Window");
return Err(Error::WindowCreate("Unable to create Window".to_owned()));
}
let window = Window {
@ -409,6 +443,9 @@ impl Window {
scale_factor: scale_factor,
width: width as i32,
height: height as i32,
menus: Vec::new(),
accel_table: ptr::null_mut(),
accel_key: INVALID_ACCEL,
};
Ok(window)
@ -423,7 +460,7 @@ impl Window {
#[inline]
pub fn set_position(&mut self, x: isize, y: isize) {
unsafe {
user32::SetWindowPos(self.window.unwrap(), ptr::null_mut(), x as i32, y as i32,
user32::SetWindowPos(self.window.unwrap(), ptr::null_mut(), x as i32, y as i32,
0, 0, winapi::SWP_SHOWWINDOW | winapi::SWP_NOSIZE);
}
}
@ -491,7 +528,6 @@ impl Window {
fn generic_update(&mut self, window: HWND) {
unsafe {
let mut point: winapi::POINT = mem::uninitialized();
user32::GetCursorPos(&mut point);
user32::ScreenToClient(window, &mut point);
@ -506,13 +542,21 @@ impl Window {
}
}
fn message_loop(&mut self, window: HWND) {
fn message_loop(&self, window: HWND) {
unsafe {
let mut msg = mem::uninitialized();
while user32::PeekMessageW(&mut msg, window, 0, 0, winapi::winuser::PM_REMOVE) != 0 {
user32::TranslateMessage(&mut msg);
user32::DispatchMessageW(&mut msg);
// Make this code a bit nicer
if self.accel_table == ptr::null_mut() {
user32::TranslateMessage(&mut msg);
user32::DispatchMessageW(&mut msg);
} else {
if TranslateAcceleratorW(msg.hwnd, mem::transmute(self.accel_table), &mut msg) == 0 {
user32::TranslateMessage(&mut msg);
user32::DispatchMessageW(&mut msg);
}
}
}
}
}
@ -537,6 +581,12 @@ impl Window {
Self::message_loop(self, window);
}
#[inline]
pub fn is_active(&mut self) -> bool {
// TODO: Proper implementation
true
}
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
let factor: i32 = match scale {
Scale::X1 => 1,
@ -568,6 +618,349 @@ impl Window {
return factor;
}
fn map_key_to_vk_accel(key: Key) -> (raw::c_int, &'static str) {
match key {
Key::Key0 => (0x30, "0"),
Key::Key1 => (0x31, "1"),
Key::Key2 => (0x32, "2"),
Key::Key3 => (0x33, "3"),
Key::Key4 => (0x34, "4"),
Key::Key5 => (0x35, "5"),
Key::Key6 => (0x36, "6"),
Key::Key7 => (0x37, "7"),
Key::Key8 => (0x38, "8"),
Key::Key9 => (0x39, "9"),
Key::A => (0x41, "a"),
Key::B => (0x42, "b"),
Key::C => (0x43, "c"),
Key::D => (0x44, "d"),
Key::E => (0x45, "e"),
Key::F => (0x46, "f"),
Key::G => (0x47, "g"),
Key::H => (0x48, "h"),
Key::I => (0x49, "i"),
Key::J => (0x4a, "j"),
Key::K => (0x4b, "k"),
Key::L => (0x4c, "l"),
Key::M => (0x4d, "m"),
Key::N => (0x4e, "n"),
Key::O => (0x4f, "o"),
Key::P => (0x50, "p"),
Key::Q => (0x51, "q"),
Key::R => (0x52, "r"),
Key::S => (0x53, "s"),
Key::T => (0x54, "t"),
Key::U => (0x55, "u"),
Key::V => (0x56, "v"),
Key::W => (0x57, "w"),
Key::X => (0x58, "x"),
Key::Y => (0x59, "y"),
Key::Z => (0x5a, "z"),
Key::F1 => (winapi::winuser::VK_F1, "F1"),
Key::F2 => (winapi::winuser::VK_F2, "F2"),
Key::F3 => (winapi::winuser::VK_F3, "F3"),
Key::F4 => (winapi::winuser::VK_F4, "F4"),
Key::F5 => (winapi::winuser::VK_F5, "F5"),
Key::F6 => (winapi::winuser::VK_F6, "F6"),
Key::F7 => (winapi::winuser::VK_F7, "F7"),
Key::F8 => (winapi::winuser::VK_F8, "F8"),
Key::F9 => (winapi::winuser::VK_F9, "F9"),
Key::F10 => (winapi::winuser::VK_F10, "F10"),
Key::F11 => (winapi::winuser::VK_F11, "F11"),
Key::F12 => (winapi::winuser::VK_F12, "F12"),
Key::F13 => (winapi::winuser::VK_F13, "F14"),
Key::F14 => (winapi::winuser::VK_F14, "F14"),
Key::F15 => (winapi::winuser::VK_F15, "F15"),
Key::Down => (winapi::winuser::VK_DOWN, "Down"),
Key::Left => (winapi::winuser::VK_LEFT, "Left"),
Key::Right => (winapi::winuser::VK_RIGHT, "Right"),
Key::Up => (winapi::winuser::VK_UP, "Up"),
Key::Backslash => (winapi::winuser::VK_OEM_102, "Backslash"),
Key::Comma => (winapi::winuser::VK_OEM_COMMA, ","),
Key::Minus => (winapi::winuser::VK_OEM_MINUS, "-"),
Key::Period => (winapi::winuser::VK_OEM_PERIOD, "."),
Key::Backspace => (winapi::winuser::VK_BACK, "Back"),
Key::Delete => (winapi::winuser::VK_DELETE, "Delete"),
Key::End => (winapi::winuser::VK_END, "End"),
Key::Enter => (winapi::winuser::VK_RETURN, "Enter"),
Key::Escape => (winapi::winuser::VK_ESCAPE, "Esc"),
Key::Home => (winapi::winuser::VK_HOME, "Home"),
Key::Insert => (winapi::winuser::VK_INSERT, "Insert"),
Key::Menu => (winapi::winuser::VK_MENU, "Menu"),
Key::PageDown => (winapi::winuser::VK_NEXT, "PageDown"),
Key::PageUp => (winapi::winuser::VK_PRIOR, "PageUp"),
Key::Pause => (winapi::winuser::VK_PAUSE, "Pause"),
Key::Space => (winapi::winuser::VK_SPACE, "Space"),
Key::Tab => (winapi::winuser::VK_TAB, "Tab"),
Key::NumLock => (winapi::winuser::VK_NUMLOCK, "NumLock"),
Key::CapsLock => (winapi::winuser::VK_CAPITAL, "CapsLock"),
Key::ScrollLock => (winapi::winuser::VK_SCROLL, "Scroll"),
Key::LeftShift => (winapi::winuser::VK_LSHIFT, "LeftShift"),
Key::RightShift => (winapi::winuser::VK_RSHIFT, "RightShift"),
Key::LeftCtrl => (winapi::winuser::VK_CONTROL, "Ctrl"),
Key::RightCtrl => (winapi::winuser::VK_CONTROL, "Ctrl"),
Key::NumPad0 => (winapi::winuser::VK_NUMPAD0, "NumPad0"),
Key::NumPad1 => (winapi::winuser::VK_NUMPAD1, "NumPad1"),
Key::NumPad2 => (winapi::winuser::VK_NUMPAD2, "NumPad2"),
Key::NumPad3 => (winapi::winuser::VK_NUMPAD3, "NumPad3"),
Key::NumPad4 => (winapi::winuser::VK_NUMPAD4, "NumPad4"),
Key::NumPad5 => (winapi::winuser::VK_NUMPAD5, "NumPad5"),
Key::NumPad6 => (winapi::winuser::VK_NUMPAD6, "NumPad6"),
Key::NumPad7 => (winapi::winuser::VK_NUMPAD7, "NumPad7"),
Key::NumPad8 => (winapi::winuser::VK_NUMPAD8, "NumPad8"),
Key::NumPad9 => (winapi::winuser::VK_NUMPAD9, "NumPad9"),
Key::LeftAlt => (winapi::winuser::VK_MENU, "Alt"),
Key::RightAlt => (winapi::winuser::VK_MENU, "Alt"),
Key::LeftSuper => (winapi::winuser::VK_LWIN, "LeftWin"),
Key::RightSuper => (winapi::winuser::VK_RWIN, "RightWin"),
_ => (0, "Unsupported"),
}
}
//
// When attaching a menu to the window we need to resize it so
// the current client size is preserved and still show all pixels
//
unsafe fn adjust_window_size_for_menu(handle: HWND) {
let mut rect: winapi::RECT = mem::uninitialized();
let menu_height = user32::GetSystemMetrics(winapi::winuser::SM_CYMENU);
user32::GetWindowRect(handle, &mut rect);
user32::MoveWindow(handle,
rect.left,
rect.top,
rect.right - rect.left,
(rect.bottom - rect.top) + menu_height,
1);
}
fn format_name(menu_item: &Menu, key_name: &'static str) -> String {
let mut name = menu_item.name.to_owned();
name.push_str("\t");
if (menu_item.modifier & MENU_KEY_WIN) == MENU_KEY_WIN {
name.push_str("Win-");
}
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
name.push_str("Shift-");
}
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
name.push_str("Ctrl-");
}
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
name.push_str("Alt-");
}
name.push_str(key_name);
name
}
fn is_key_virtual_range(key: raw::c_int) -> u32 {
if (key >= 0x30 && key <= 0x30) ||
(key >= 0x41 && key <= 0x5a) {
1
} else {
0
}
}
fn get_virt_key(menu_item: &Menu, key: raw::c_int) -> u32 {
let mut virt = Self::is_key_virtual_range(key);
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
virt |= 0x10;
}
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
virt |= 0x8;
}
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
virt |= 0x4;
}
virt
}
fn add_accel(accel_table: &mut Vec<ACCEL>, menu_item: &Menu) {
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
let virt = Self::get_virt_key(menu_item, vk_accel.0);
let accel = winuser::ACCEL {
fVirt: virt as BYTE,
cmd: menu_item.id as WORD,
key: vk_accel.0 as WORD };
accel_table.push(accel);
}
unsafe fn add_menu_item(&mut self, parent_menu: HMENU, menu_item: &Menu) {
let item_name = to_wstring(menu_item.name);
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
match vk_accel.0 {
0 => {
user32::AppendMenuW(parent_menu, 0x10, menu_item.id as UINT_PTR, item_name.as_ptr());
},
_ => {
let menu_name = Self::format_name(menu_item, vk_accel.1);
let w_name = to_wstring(&menu_name);
user32::AppendMenuW(parent_menu, 0x10, menu_item.id as UINT_PTR, w_name.as_ptr());
}
}
}
unsafe fn set_accel_table(&mut self) {
let mut temp_accel_table = Vec::<ACCEL>::new();
for menu in self.menus.iter() {
for item in menu.accel_items.iter() {
println!("virt {} - cmd {} - key {}", item.fVirt, item.cmd, item.key);
temp_accel_table.push(item.clone());
}
}
if self.accel_table != ptr::null_mut() {
user32::DestroyAcceleratorTable(self.accel_table);
}
self.accel_table = user32::CreateAcceleratorTableW(temp_accel_table.as_mut_ptr(),
temp_accel_table.len() as i32);
}
unsafe fn recursive_add_menu(&mut self, parent_menu: HMENU, name: &str, menu: &Vec<Menu>) -> HMENU {
let menu_name = to_wstring(name);
let popup_menu = user32::CreatePopupMenu();
user32::AppendMenuW(parent_menu, 0x10, popup_menu as UINT_PTR, menu_name.as_ptr());
for m in menu.iter() {
if let Some(ref sub_menu) = m.sub_menu {
Self::recursive_add_menu(self, popup_menu, m.name, sub_menu);
} else {
if m.id == 0xffffffff {
user32::AppendMenuW(popup_menu, 0x800, 0, ptr::null()); // separator
} else {
Self::add_menu_item(self, popup_menu, m);
}
}
}
popup_menu
}
pub fn menu_exists(&mut self, menu_name: &str) -> bool {
for menu in self.menus.iter() {
if menu.name == menu_name {
return true;
}
}
false
}
fn clone_menu(accel_dest: &mut Vec<ACCEL>, menu: &Vec<Menu>) {
for m in menu.iter() {
if let Some(ref sub_menu) = m.sub_menu {
Self::clone_menu(accel_dest, sub_menu);
}
if m.key != Key::Unknown {
Self::add_accel(accel_dest, m);
}
}
}
unsafe fn add_menu_store(&mut self, parent_menu: HMENU, menu_name: &str, menu: &Vec<Menu>) {
let mut items = Vec::<ACCEL>::new();
let menu_handle = Self::recursive_add_menu(self, parent_menu, menu_name, menu);
Self::clone_menu(&mut items, menu);
self.menus.push(MenuStore {
name: menu_name.to_owned(),
menu: menu_handle,
accel_items: items
});
}
pub fn add_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
if Self::menu_exists(self, menu_name) {
return Err(Error::MenuExists(menu_name.to_owned()));
}
unsafe {
let window = self.window.unwrap();
let mut main_menu = user32::GetMenu(window);
if main_menu == ptr::null_mut() {
main_menu = user32::CreateMenu();
user32::SetMenu(window, main_menu);
Self::adjust_window_size_for_menu(window);
}
Self::add_menu_store(self, main_menu, menu_name, menu);
Self::set_accel_table(self);
user32::DrawMenuBar(window);
}
Ok(())
}
pub fn update_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
try!(Self::remove_menu(self, menu_name));
Self::add_menu(self, menu_name, menu)
}
pub fn remove_menu(&mut self, menu_name: &str) -> Result<()> {
for i in 0..self.menus.len() {
if self.menus[i].name == menu_name {
unsafe {
user32::DestroyMenu(self.menus[i].menu);
user32::DrawMenuBar(self.window.unwrap());
self.menus.swap_remove(i);
break;
}
}
}
// TODO: Proper return here
Ok(())
}
pub fn is_menu_pressed(&mut self) -> Option<usize> {
if self.accel_key == INVALID_ACCEL {
None
} else {
let t = self.accel_key;
self.accel_key = INVALID_ACCEL;
Some(t)
}
}
}
impl Drop for Window {