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

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);
}
}

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;
@ -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)
}
@ -522,6 +409,58 @@ impl Window {
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

@ -11,6 +11,8 @@ 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();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __clang__
@ -21,11 +23,14 @@ 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;
}
@ -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)
@ -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];
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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -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
@ -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,12 +81,21 @@
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;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -118,6 +126,18 @@
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;
@ -180,4 +200,137 @@
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (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};
@ -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)]
@ -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(_) => {
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,7 +228,7 @@ 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 {
@ -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,
@ -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,11 +166,11 @@ 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(_) => {
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,
};
@ -181,7 +184,7 @@ impl Window {
scale);
if handle == ptr::null_mut() {
return Err("Unable to open Window");
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
}
Ok(Window {
@ -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,
@ -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 {
@ -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)
@ -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 {
// 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 {