#![crate_type = "lib"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![crate_name = "glfw"]
#![allow(non_upper_case_globals)]
extern crate semver;
#[cfg(feature = "vulkan")]
extern crate vk_sys;
#[macro_use]
extern crate log;
#[macro_use]
extern crate bitflags;
#[cfg(feature = "image")]
extern crate image;
extern crate raw_window_handle;
#[cfg(all(target_os = "macos"))]
#[macro_use]
extern crate objc;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use semver::Version;
use std::error;
use std::ffi::{CStr, CString};
use std::fmt;
use std::marker::Send;
use std::mem;
#[cfg(feature = "vulkan")]
use std::os::raw::c_uint;
use std::os::raw::{c_char, c_double, c_float, c_int};
use std::os::raw::{c_uchar, c_ushort, c_void};
use std::path::PathBuf;
use std::ptr;
use std::slice;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
#[cfg(feature = "vulkan")]
use vk_sys::{
self as vk, AllocationCallbacks as VkAllocationCallbacks, Instance as VkInstance,
PhysicalDevice as VkPhysicalDevice, Result as VkResult, SurfaceKHR as VkSurfaceKHR,
};
pub use self::MouseButton::Button1 as MouseButtonLeft;
pub use self::MouseButton::Button2 as MouseButtonRight;
pub use self::MouseButton::Button3 as MouseButtonMiddle;
mod callbacks;
pub mod ffi;
pub type WindowId = usize;
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Action {
Release = ffi::RELEASE,
Press = ffi::PRESS,
Repeat = ffi::REPEAT,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Key {
Space = ffi::KEY_SPACE,
Apostrophe = ffi::KEY_APOSTROPHE,
Comma = ffi::KEY_COMMA,
Minus = ffi::KEY_MINUS,
Period = ffi::KEY_PERIOD,
Slash = ffi::KEY_SLASH,
Num0 = ffi::KEY_0,
Num1 = ffi::KEY_1,
Num2 = ffi::KEY_2,
Num3 = ffi::KEY_3,
Num4 = ffi::KEY_4,
Num5 = ffi::KEY_5,
Num6 = ffi::KEY_6,
Num7 = ffi::KEY_7,
Num8 = ffi::KEY_8,
Num9 = ffi::KEY_9,
Semicolon = ffi::KEY_SEMICOLON,
Equal = ffi::KEY_EQUAL,
A = ffi::KEY_A,
B = ffi::KEY_B,
C = ffi::KEY_C,
D = ffi::KEY_D,
E = ffi::KEY_E,
F = ffi::KEY_F,
G = ffi::KEY_G,
H = ffi::KEY_H,
I = ffi::KEY_I,
J = ffi::KEY_J,
K = ffi::KEY_K,
L = ffi::KEY_L,
M = ffi::KEY_M,
N = ffi::KEY_N,
O = ffi::KEY_O,
P = ffi::KEY_P,
Q = ffi::KEY_Q,
R = ffi::KEY_R,
S = ffi::KEY_S,
T = ffi::KEY_T,
U = ffi::KEY_U,
V = ffi::KEY_V,
W = ffi::KEY_W,
X = ffi::KEY_X,
Y = ffi::KEY_Y,
Z = ffi::KEY_Z,
LeftBracket = ffi::KEY_LEFT_BRACKET,
Backslash = ffi::KEY_BACKSLASH,
RightBracket = ffi::KEY_RIGHT_BRACKET,
GraveAccent = ffi::KEY_GRAVE_ACCENT,
World1 = ffi::KEY_WORLD_1,
World2 = ffi::KEY_WORLD_2,
Escape = ffi::KEY_ESCAPE,
Enter = ffi::KEY_ENTER,
Tab = ffi::KEY_TAB,
Backspace = ffi::KEY_BACKSPACE,
Insert = ffi::KEY_INSERT,
Delete = ffi::KEY_DELETE,
Right = ffi::KEY_RIGHT,
Left = ffi::KEY_LEFT,
Down = ffi::KEY_DOWN,
Up = ffi::KEY_UP,
PageUp = ffi::KEY_PAGE_UP,
PageDown = ffi::KEY_PAGE_DOWN,
Home = ffi::KEY_HOME,
End = ffi::KEY_END,
CapsLock = ffi::KEY_CAPS_LOCK,
ScrollLock = ffi::KEY_SCROLL_LOCK,
NumLock = ffi::KEY_NUM_LOCK,
PrintScreen = ffi::KEY_PRINT_SCREEN,
Pause = ffi::KEY_PAUSE,
F1 = ffi::KEY_F1,
F2 = ffi::KEY_F2,
F3 = ffi::KEY_F3,
F4 = ffi::KEY_F4,
F5 = ffi::KEY_F5,
F6 = ffi::KEY_F6,
F7 = ffi::KEY_F7,
F8 = ffi::KEY_F8,
F9 = ffi::KEY_F9,
F10 = ffi::KEY_F10,
F11 = ffi::KEY_F11,
F12 = ffi::KEY_F12,
F13 = ffi::KEY_F13,
F14 = ffi::KEY_F14,
F15 = ffi::KEY_F15,
F16 = ffi::KEY_F16,
F17 = ffi::KEY_F17,
F18 = ffi::KEY_F18,
F19 = ffi::KEY_F19,
F20 = ffi::KEY_F20,
F21 = ffi::KEY_F21,
F22 = ffi::KEY_F22,
F23 = ffi::KEY_F23,
F24 = ffi::KEY_F24,
F25 = ffi::KEY_F25,
Kp0 = ffi::KEY_KP_0,
Kp1 = ffi::KEY_KP_1,
Kp2 = ffi::KEY_KP_2,
Kp3 = ffi::KEY_KP_3,
Kp4 = ffi::KEY_KP_4,
Kp5 = ffi::KEY_KP_5,
Kp6 = ffi::KEY_KP_6,
Kp7 = ffi::KEY_KP_7,
Kp8 = ffi::KEY_KP_8,
Kp9 = ffi::KEY_KP_9,
KpDecimal = ffi::KEY_KP_DECIMAL,
KpDivide = ffi::KEY_KP_DIVIDE,
KpMultiply = ffi::KEY_KP_MULTIPLY,
KpSubtract = ffi::KEY_KP_SUBTRACT,
KpAdd = ffi::KEY_KP_ADD,
KpEnter = ffi::KEY_KP_ENTER,
KpEqual = ffi::KEY_KP_EQUAL,
LeftShift = ffi::KEY_LEFT_SHIFT,
LeftControl = ffi::KEY_LEFT_CONTROL,
LeftAlt = ffi::KEY_LEFT_ALT,
LeftSuper = ffi::KEY_LEFT_SUPER,
RightShift = ffi::KEY_RIGHT_SHIFT,
RightControl = ffi::KEY_RIGHT_CONTROL,
RightAlt = ffi::KEY_RIGHT_ALT,
RightSuper = ffi::KEY_RIGHT_SUPER,
Menu = ffi::KEY_MENU,
Unknown = ffi::KEY_UNKNOWN,
}
pub fn get_key_name(key: Option<Key>, scancode: Option<Scancode>) -> Option<String> {
unsafe {
string_from_nullable_c_str(ffi::glfwGetKeyName(
match key {
Some(k) => k as c_int,
None => ffi::KEY_UNKNOWN,
},
scancode.unwrap_or(ffi::KEY_UNKNOWN),
))
}
}
#[deprecated(
since = "0.16.0",
note = "'key_name' can cause a segfault, use 'get_key_name' instead"
)]
pub fn key_name(key: Option<Key>, scancode: Option<Scancode>) -> String {
unsafe {
string_from_c_str(ffi::glfwGetKeyName(
match key {
Some(k) => k as c_int,
None => ffi::KEY_UNKNOWN,
},
scancode.unwrap_or(ffi::KEY_UNKNOWN),
))
}
}
pub fn get_key_scancode(key: Option<Key>) -> Option<Scancode> {
unsafe {
match ffi::glfwGetKeyScancode(match key {
Some(key) => key as c_int,
None => ffi::KEY_UNKNOWN,
}) {
ffi::KEY_UNKNOWN => None,
scancode => Some(scancode as Scancode),
}
}
}
impl Key {
#[deprecated(
since = "0.16.0",
note = "Key method 'name' can cause a segfault, use 'get_name' instead"
)]
pub fn name(&self) -> String {
#[allow(deprecated)]
key_name(Some(*self), None)
}
pub fn get_name(&self) -> Option<String> {
get_key_name(Some(*self), None)
}
pub fn get_scancode(&self) -> Option<Scancode> {
get_key_scancode(Some(*self))
}
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum MouseButton {
Button1 = ffi::MOUSE_BUTTON_1,
Button2 = ffi::MOUSE_BUTTON_2,
Button3 = ffi::MOUSE_BUTTON_3,
Button4 = ffi::MOUSE_BUTTON_4,
Button5 = ffi::MOUSE_BUTTON_5,
Button6 = ffi::MOUSE_BUTTON_6,
Button7 = ffi::MOUSE_BUTTON_7,
Button8 = ffi::MOUSE_BUTTON_8,
}
impl MouseButton {
pub fn from_i32(n: i32) -> Option<MouseButton> {
if n >= 0 && n <= ffi::MOUSE_BUTTON_LAST {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
}
pub struct DebugAliases<T>(pub T);
impl fmt::Debug for DebugAliases<MouseButton> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let DebugAliases(button) = *self;
match button {
MouseButtonLeft => write!(f, "MouseButtonLeft"),
MouseButtonRight => write!(f, "MouseButtonRight"),
MouseButtonMiddle => write!(f, "MouseButtonMiddle"),
button => button.fmt(f),
}
}
}
#[derive(Copy, Clone)]
pub struct Callback<Fn, UserData> {
pub f: Fn,
pub data: UserData,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Error {
NoError = ffi::NO_ERROR,
NotInitialized = ffi::NOT_INITIALIZED,
NoCurrentContext = ffi::NO_CURRENT_CONTEXT,
InvalidEnum = ffi::INVALID_ENUM,
InvalidValue = ffi::INVALID_VALUE,
OutOfMemory = ffi::OUT_OF_MEMORY,
ApiUnavailable = ffi::API_UNAVAILABLE,
VersionUnavailable = ffi::VERSION_UNAVAILABLE,
PlatformError = ffi::PLATFORM_ERROR,
FormatUnavailable = ffi::FORMAT_UNAVAILABLE,
NoWindowContext = ffi::NO_WINDOW_CONTEXT,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
Error::NoError => "NoError",
Error::NotInitialized => "NotInitialized",
Error::NoCurrentContext => "NoCurrentContext",
Error::InvalidEnum => "InvalidEnum",
Error::InvalidValue => "InvalidValue",
Error::OutOfMemory => "OutOfMemory",
Error::ApiUnavailable => "ApiUnavailable",
Error::VersionUnavailable => "VersionUnavailable",
Error::PlatformError => "PlatformError",
Error::FormatUnavailable => "FormatUnavailable",
Error::NoWindowContext => "NoWindowContext",
};
f.write_str(description)
}
}
impl error::Error for Error {}
pub type ErrorCallback<UserData> = Callback<fn(Error, String, &UserData), UserData>;
pub fn fail_on_errors(_: Error, description: String, _: &()) {
panic!("GLFW Error: {}", description);
}
pub static FAIL_ON_ERRORS: Option<ErrorCallback<()>> = Some(Callback {
f: fail_on_errors as fn(Error, String, &()),
data: (),
});
pub fn log_errors(_: Error, description: String, _: &()) {
error!("GLFW Error: {}", description);
}
pub static LOG_ERRORS: Option<ErrorCallback<()>> = Some(Callback {
f: log_errors as fn(Error, String, &()),
data: (),
});
pub struct PixelImage {
pub width: u32,
pub height: u32,
pub pixels: Vec<u32>,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum CursorMode {
Normal = ffi::CURSOR_NORMAL,
Hidden = ffi::CURSOR_HIDDEN,
Disabled = ffi::CURSOR_DISABLED,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum StandardCursor {
Arrow = ffi::ARROW_CURSOR,
IBeam = ffi::IBEAM_CURSOR,
Crosshair = ffi::CROSSHAIR_CURSOR,
Hand = ffi::HAND_CURSOR,
HResize = ffi::HRESIZE_CURSOR,
VResize = ffi::VRESIZE_CURSOR,
}
#[derive(Debug)]
pub struct Cursor {
ptr: *mut ffi::GLFWcursor,
}
impl Drop for Cursor {
fn drop(&mut self) {
unsafe { ffi::glfwDestroyCursor(self.ptr) }
}
}
impl Cursor {
pub fn standard(cursor: StandardCursor) -> Cursor {
Cursor {
ptr: unsafe { ffi::glfwCreateStandardCursor(cursor as c_int) },
}
}
#[cfg(feature = "image")]
pub fn create(image: image::RgbaImage, x_hotspot: u32, y_hotspot: u32) -> Cursor {
let (width, height) = image.dimensions();
let image_data = image.into_vec();
let glfw_image = ffi::GLFWimage {
width: width as c_int,
height: height as c_int,
pixels: image_data.as_ptr() as *const c_uchar,
};
Cursor {
ptr: unsafe {
ffi::glfwCreateCursor(
&glfw_image as *const ffi::GLFWimage,
x_hotspot as c_int,
y_hotspot as c_int,
)
},
}
}
pub fn create_from_pixels(image: PixelImage, x_hotspot: u32, y_hotspot: u32) -> Cursor {
let glfw_image = ffi::GLFWimage {
width: image.width as c_int,
height: image.height as c_int,
pixels: image.pixels.as_ptr() as *const c_uchar,
};
Cursor {
ptr: unsafe {
ffi::glfwCreateCursor(
&glfw_image as *const ffi::GLFWimage,
x_hotspot as c_int,
y_hotspot as c_int,
)
},
}
}
}
#[derive(Copy, Clone)]
pub struct VidMode {
pub width: u32,
pub height: u32,
pub red_bits: u32,
pub green_bits: u32,
pub blue_bits: u32,
pub refresh_rate: u32,
}
pub struct GammaRamp {
pub red: Vec<c_ushort>,
pub green: Vec<c_ushort>,
pub blue: Vec<c_ushort>,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextReleaseBehavior {
Any = ffi::ANY_RELEASE_BEHAVIOR,
Flush = ffi::RELEASE_BEHAVIOR_FLUSH,
None = ffi::RELEASE_BEHAVIOR_NONE,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextCreationApi {
Native = ffi::NATIVE_CONTEXT_API,
Egl = ffi::EGL_CONTEXT_API,
OsMesa = ffi::OSMESA_CONTEXT_API,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum SwapInterval {
None,
Adaptive,
Sync(u32),
}
pub type GLProc = ffi::GLFWglproc;
#[cfg(feature = "vulkan")]
pub type VkProc = ffi::GLFWvkproc;
static REF_COUNT_FOR_GLFW: AtomicUsize = AtomicUsize::new(0);
#[non_exhaustive]
#[derive(Debug)]
pub struct Glfw;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum InitError {
AlreadyInitialized,
Internal,
}
impl fmt::Display for InitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
InitError::AlreadyInitialized => "Already Initialized",
InitError::Internal => "Internal Initialization Error",
};
f.write_str(description)
}
}
impl error::Error for InitError {}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum InitHint {
JoystickHatButtons(bool),
CocoaChdirResources(bool),
CocoaMenubar(bool),
}
pub fn init_hint(hint: InitHint) {
match hint {
InitHint::JoystickHatButtons(joystick_hat_buttons) => unsafe {
ffi::glfwInitHint(ffi::JOYSTICK_HAT_BUTTONS, joystick_hat_buttons as c_int)
},
InitHint::CocoaChdirResources(chdir) => unsafe {
ffi::glfwInitHint(ffi::COCOA_CHDIR_RESOURCES, chdir as c_int)
},
InitHint::CocoaMenubar(menubar) => unsafe {
ffi::glfwInitHint(ffi::COCOA_MENUBAR, menubar as c_int)
},
}
}
pub fn init<UserData: 'static>(
mut callback: Option<ErrorCallback<UserData>>,
) -> Result<Glfw, InitError> {
match callback.take() {
Some(f) => callbacks::error::set(f),
None => callbacks::error::unset(),
}
if unsafe { ffi::glfwInit() } == ffi::TRUE {
REF_COUNT_FOR_GLFW.fetch_add(1, Ordering::SeqCst);
Ok(Glfw)
} else {
Err(InitError::Internal)
}
}
impl Glfw {
pub fn set_error_callback<UserData: 'static>(
&mut self,
callback: Option<ErrorCallback<UserData>>,
) {
match callback {
Some(f) => callbacks::error::set(f),
None => callbacks::error::unset(),
}
}
pub fn set_monitor_callback<UserData: 'static>(
&mut self,
callback: Option<MonitorCallback<UserData>>,
) {
match callback {
Some(f) => callbacks::monitor::set(f),
None => callbacks::monitor::unset(),
}
}
pub fn set_joystick_callback<UserData: 'static>(
&mut self,
callback: Option<JoystickCallback<UserData>>,
) {
match callback {
Some(f) => callbacks::joystick::set(f),
None => callbacks::joystick::unset(),
}
}
pub fn with_primary_monitor<T, F>(&mut self, f: F) -> T
where
F: Fn(&mut Self, Option<&Monitor>) -> T,
{
match unsafe { ffi::glfwGetPrimaryMonitor() } {
ptr if ptr.is_null() => f(self, None),
ptr => f(self, Some(&Monitor { ptr: ptr })),
}
}
pub fn with_primary_monitor_mut<T, F>(&mut self, mut f: F) -> T
where
F: FnMut(&mut Self, Option<&Monitor>) -> T,
{
match unsafe { ffi::glfwGetPrimaryMonitor() } {
ptr if ptr.is_null() => f(self, None),
ptr => f(self, Some(&Monitor { ptr: ptr })),
}
}
pub fn with_connected_monitors<T, F>(&mut self, f: F) -> T
where
F: Fn(&mut Self, &[Monitor]) -> T,
{
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetMonitors(&mut count);
f(
self,
&slice::from_raw_parts(ptr as *const _, count as usize)
.iter()
.map(|&ptr| Monitor { ptr: ptr })
.collect::<Vec<Monitor>>(),
)
}
}
pub fn with_connected_monitors_mut<T, F>(&mut self, mut f: F) -> T
where
F: FnMut(&mut Self, &[Monitor]) -> T,
{
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetMonitors(&mut count);
f(
self,
&slice::from_raw_parts(ptr as *const _, count as usize)
.iter()
.map(|&ptr| Monitor { ptr: ptr })
.collect::<Vec<Monitor>>(),
)
}
}
#[cfg(feature = "vulkan")]
pub fn vulkan_supported(&self) -> bool {
unsafe { ffi::glfwVulkanSupported() == ffi::TRUE }
}
pub fn window_hint(&mut self, hint: WindowHint) {
#[inline(always)]
unsafe fn dont_care_hint(hint: c_int, value: Option<u32>) {
ffi::glfwWindowHint(hint, unwrap_dont_care(value))
}
#[inline(always)]
unsafe fn string_hint(hint: c_int, value: Option<String>) {
let value = if let Some(value) = &value {
value.as_str()
} else {
""
};
with_c_str(value, |value| ffi::glfwWindowHintString(hint, value))
}
match hint {
WindowHint::RedBits(bits) => unsafe { dont_care_hint(ffi::RED_BITS, bits) },
WindowHint::GreenBits(bits) => unsafe { dont_care_hint(ffi::GREEN_BITS, bits) },
WindowHint::BlueBits(bits) => unsafe { dont_care_hint(ffi::BLUE_BITS, bits) },
WindowHint::AlphaBits(bits) => unsafe { dont_care_hint(ffi::ALPHA_BITS, bits) },
WindowHint::DepthBits(bits) => unsafe { dont_care_hint(ffi::DEPTH_BITS, bits) },
WindowHint::StencilBits(bits) => unsafe { dont_care_hint(ffi::STENCIL_BITS, bits) },
WindowHint::AccumRedBits(bits) => unsafe { dont_care_hint(ffi::ACCUM_RED_BITS, bits) },
WindowHint::AccumGreenBits(bits) => unsafe {
dont_care_hint(ffi::ACCUM_GREEN_BITS, bits)
},
WindowHint::AccumBlueBits(bits) => unsafe {
dont_care_hint(ffi::ACCUM_BLUE_BITS, bits)
},
WindowHint::AccumAlphaBits(bits) => unsafe {
dont_care_hint(ffi::ACCUM_ALPHA_BITS, bits)
},
WindowHint::AuxBuffers(num_buffers) => unsafe {
dont_care_hint(ffi::AUX_BUFFERS, num_buffers)
},
WindowHint::Samples(num_samples) => unsafe {
dont_care_hint(ffi::SAMPLES, num_samples)
},
WindowHint::RefreshRate(rate) => unsafe { dont_care_hint(ffi::REFRESH_RATE, rate) },
WindowHint::Stereo(is_stereo) => unsafe {
ffi::glfwWindowHint(ffi::STEREO, is_stereo as c_int)
},
WindowHint::SRgbCapable(is_capable) => unsafe {
ffi::glfwWindowHint(ffi::SRGB_CAPABLE, is_capable as c_int)
},
WindowHint::ClientApi(api) => unsafe {
ffi::glfwWindowHint(ffi::CLIENT_API, api as c_int)
},
WindowHint::ContextVersionMajor(major) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int)
},
WindowHint::ContextVersionMinor(minor) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int)
},
WindowHint::ContextVersion(major, minor) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int);
ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int)
},
WindowHint::ContextRobustness(robustness) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_ROBUSTNESS, robustness as c_int)
},
WindowHint::OpenGlForwardCompat(is_compat) => unsafe {
ffi::glfwWindowHint(ffi::OPENGL_FORWARD_COMPAT, is_compat as c_int)
},
WindowHint::OpenGlDebugContext(is_debug) => unsafe {
ffi::glfwWindowHint(ffi::OPENGL_DEBUG_CONTEXT, is_debug as c_int)
},
WindowHint::OpenGlProfile(profile) => unsafe {
ffi::glfwWindowHint(ffi::OPENGL_PROFILE, profile as c_int)
},
WindowHint::Resizable(is_resizable) => unsafe {
ffi::glfwWindowHint(ffi::RESIZABLE, is_resizable as c_int)
},
WindowHint::Visible(is_visible) => unsafe {
ffi::glfwWindowHint(ffi::VISIBLE, is_visible as c_int)
},
WindowHint::Decorated(is_decorated) => unsafe {
ffi::glfwWindowHint(ffi::DECORATED, is_decorated as c_int)
},
WindowHint::AutoIconify(auto_iconify) => unsafe {
ffi::glfwWindowHint(ffi::AUTO_ICONIFY, auto_iconify as c_int)
},
WindowHint::Floating(is_floating) => unsafe {
ffi::glfwWindowHint(ffi::FLOATING, is_floating as c_int)
},
WindowHint::Focused(is_focused) => unsafe {
ffi::glfwWindowHint(ffi::FOCUSED, is_focused as c_int)
},
WindowHint::ContextNoError(is_no_error) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_NO_ERROR, is_no_error as c_int)
},
WindowHint::ContextCreationApi(api) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_CREATION_API, api as c_int)
},
WindowHint::ContextReleaseBehavior(behavior) => unsafe {
ffi::glfwWindowHint(ffi::CONTEXT_RELEASE_BEHAVIOR, behavior as c_int)
},
WindowHint::DoubleBuffer(is_dbuffered) => unsafe {
ffi::glfwWindowHint(ffi::DOUBLEBUFFER, is_dbuffered as c_int)
},
WindowHint::CenterCursor(center_cursor) => unsafe {
ffi::glfwWindowHint(ffi::CENTER_CURSOR, center_cursor as c_int)
},
WindowHint::TransparentFramebuffer(is_transparent) => unsafe {
ffi::glfwWindowHint(ffi::TRANSPARENT_FRAMEBUFFER, is_transparent as c_int)
},
WindowHint::FocusOnShow(focus) => unsafe {
ffi::glfwWindowHint(ffi::FOCUS_ON_SHOW, focus as c_int)
},
WindowHint::ScaleToMonitor(scale) => unsafe {
ffi::glfwWindowHint(ffi::SCALE_TO_MONITOR, scale as c_int)
},
WindowHint::CocoaRetinaFramebuffer(retina_fb) => unsafe {
ffi::glfwWindowHint(ffi::COCOA_RETINA_FRAMEBUFFER, retina_fb as c_int)
},
WindowHint::CocoaFrameName(name) => unsafe { string_hint(ffi::COCOA_FRAME_NAME, name) },
WindowHint::CocoaGraphicsSwitching(graphics_switching) => unsafe {
ffi::glfwWindowHint(ffi::COCOA_GRAPHICS_SWITCHING, graphics_switching as c_int)
},
WindowHint::X11ClassName(class_name) => unsafe {
string_hint(ffi::X11_CLASS_NAME, class_name)
},
WindowHint::X11InstanceName(instance_name) => unsafe {
string_hint(ffi::X11_INSTANCE_NAME, instance_name)
},
}
}
pub fn default_window_hints(&mut self) {
unsafe {
ffi::glfwDefaultWindowHints();
}
}
pub fn create_window(
&self,
width: u32,
height: u32,
title: &str,
mode: WindowMode,
) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
self.create_window_intern(width, height, title, mode, None)
}
fn create_window_intern(
&self,
width: u32,
height: u32,
title: &str,
mode: WindowMode,
share: Option<&Window>,
) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
let ptr = unsafe {
with_c_str(title, |title| {
ffi::glfwCreateWindow(
width as c_int,
height as c_int,
title,
mode.to_ptr(),
match share {
Some(w) => w.ptr,
None => ptr::null_mut(),
},
)
})
};
if ptr.is_null() {
None
} else {
let (drop_sender, drop_receiver) = channel();
let (sender, receiver) = channel();
unsafe {
ffi::glfwSetWindowUserPointer(ptr, mem::transmute(Box::new(sender)));
}
Some((
Window {
ptr: ptr,
glfw: self.clone(),
is_shared: share.is_some(),
drop_sender: Some(drop_sender),
drop_receiver: drop_receiver,
current_cursor: None,
},
receiver,
))
}
}
pub fn make_context_current(&mut self, context: Option<&Window>) {
match context {
Some(window) => unsafe { ffi::glfwMakeContextCurrent(window.ptr) },
None => unsafe { ffi::glfwMakeContextCurrent(ptr::null_mut()) },
}
}
#[cfg(target_os = "linux")]
pub fn get_x11_display(&self) -> *mut c_void {
unsafe { ffi::glfwGetX11Display() }
}
pub fn poll_events(&mut self) {
unsafe {
ffi::glfwPollEvents();
}
}
pub fn poll_events_unbuffered<F>(&mut self, mut f: F)
where
F: FnMut(WindowId, (f64, WindowEvent)) -> Option<(f64, WindowEvent)>,
{
let _unset_handler_guard = unsafe { crate::callbacks::unbuffered::set_handler(&mut f) };
self.poll_events();
}
pub fn wait_events(&mut self) {
unsafe {
ffi::glfwWaitEvents();
}
}
pub fn wait_events_unbuffered<F>(&mut self, mut f: F)
where
F: FnMut(WindowId, (f64, WindowEvent)) -> Option<(f64, WindowEvent)>,
{
let _unset_handler_guard = unsafe { crate::callbacks::unbuffered::set_handler(&mut f) };
self.wait_events();
}
pub fn wait_events_timeout(&mut self, timeout: f64) {
unsafe {
ffi::glfwWaitEventsTimeout(timeout);
}
}
pub fn wait_events_timeout_unbuffered<F>(&mut self, timeout: f64, mut f: F)
where
F: FnMut(WindowId, (f64, WindowEvent)) -> Option<(f64, WindowEvent)>,
{
let _unset_handler_guard = unsafe { crate::callbacks::unbuffered::set_handler(&mut f) };
self.wait_events_timeout(timeout);
}
pub fn post_empty_event(&mut self) {
unsafe {
ffi::glfwPostEmptyEvent();
}
}
pub fn get_time(&self) -> f64 {
unsafe { ffi::glfwGetTime() as f64 }
}
pub fn set_time(&mut self, time: f64) {
unsafe {
ffi::glfwSetTime(time as c_double);
}
}
pub fn get_timer_value() -> u64 {
unsafe { ffi::glfwGetTimerValue() as u64 }
}
pub fn get_timer_frequency() -> u64 {
unsafe { ffi::glfwGetTimerFrequency() as u64 }
}
pub fn set_swap_interval(&mut self, interval: SwapInterval) {
unsafe {
ffi::glfwSwapInterval(match interval {
SwapInterval::None => 0 as c_int,
SwapInterval::Adaptive => -1 as c_int,
SwapInterval::Sync(interval) => interval as c_int,
})
}
}
pub fn extension_supported(&self, extension: &str) -> bool {
unsafe {
with_c_str(extension, |extension| {
ffi::glfwExtensionSupported(extension) == ffi::TRUE
})
}
}
#[cfg(feature = "vulkan")]
pub fn get_required_instance_extensions(&self) -> Option<Vec<String>> {
let mut len: c_uint = 0;
unsafe {
let raw_extensions: *const *const c_char =
ffi::glfwGetRequiredInstanceExtensions(&mut len as *mut c_uint);
if !raw_extensions.is_null() {
return Some(
slice::from_raw_parts(raw_extensions, len as usize)
.iter()
.map(|extensions| string_from_c_str(*extensions))
.collect(),
);
}
}
None
}
pub fn get_proc_address_raw(&self, procname: &str) -> GLProc {
debug_assert!(unsafe { ffi::glfwGetCurrentContext() } != std::ptr::null_mut());
with_c_str(procname, |procname| unsafe {
ffi::glfwGetProcAddress(procname)
})
}
#[cfg(feature = "vulkan")]
pub fn get_instance_proc_address_raw(&self, instance: VkInstance, procname: &str) -> VkProc {
with_c_str(procname, |procname| unsafe {
ffi::glfwGetInstanceProcAddress(instance, procname)
})
}
#[cfg(feature = "vulkan")]
pub fn get_physical_device_presentation_support_raw(
&self,
instance: VkInstance,
device: VkPhysicalDevice,
queue_family: u32,
) -> bool {
vk::TRUE
== unsafe {
ffi::glfwGetPhysicalDevicePresentationSupport(
instance,
device,
queue_family as c_uint,
) as u32
}
}
pub fn get_joystick(&self, id: JoystickId) -> Joystick {
Joystick {
id: id,
glfw: self.clone(),
}
}
pub fn supports_raw_motion(&self) -> bool {
unsafe { ffi::glfwRawMouseMotionSupported() == ffi::TRUE }
}
pub fn update_gamepad_mappings(&self, mappings: &str) -> bool {
unsafe {
with_c_str(mappings, |mappings| {
ffi::glfwUpdateGamepadMappings(mappings) == ffi::TRUE
})
}
}
}
impl Clone for Glfw {
fn clone(&self) -> Self {
REF_COUNT_FOR_GLFW.fetch_add(1, Ordering::SeqCst);
Glfw
}
}
impl Drop for Glfw {
fn drop(&mut self) {
let old_diff = REF_COUNT_FOR_GLFW.fetch_sub(1, Ordering::SeqCst);
if old_diff == 1 {
unsafe {
ffi::glfwTerminate();
}
}
}
}
pub fn get_version() -> Version {
unsafe {
let mut major = 0;
let mut minor = 0;
let mut patch = 0;
ffi::glfwGetVersion(&mut major, &mut minor, &mut patch);
Version {
major: major as u64,
minor: minor as u64,
patch: patch as u64,
pre: Vec::new(),
build: Vec::new(),
}
}
}
pub unsafe fn string_from_c_str(c_str: *const c_char) -> String {
String::from_utf8_lossy(CStr::from_ptr(c_str).to_bytes()).into_owned()
}
pub unsafe fn string_from_nullable_c_str(c_str: *const c_char) -> Option<String> {
if c_str.is_null() {
None
} else {
Some(string_from_c_str(c_str))
}
}
pub fn with_c_str<F, T>(s: &str, f: F) -> T
where
F: FnOnce(*const c_char) -> T,
{
let c_str = CString::new(s.as_bytes());
f(c_str.unwrap().as_bytes_with_nul().as_ptr() as *const _)
}
pub fn get_version_string() -> String {
unsafe { string_from_c_str(ffi::glfwGetVersionString()) }
}
pub type MonitorCallback<UserData> = Callback<fn(Monitor, MonitorEvent, &UserData), UserData>;
#[allow(missing_copy_implementations)]
pub struct Monitor {
ptr: *mut ffi::GLFWmonitor,
}
impl std::fmt::Debug for Monitor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Monitor({:p})", self.ptr)
}
}
impl Monitor {
pub fn get_pos(&self) -> (i32, i32) {
unsafe {
let mut xpos = 0;
let mut ypos = 0;
ffi::glfwGetMonitorPos(self.ptr, &mut xpos, &mut ypos);
(xpos as i32, ypos as i32)
}
}
pub fn get_physical_size(&self) -> (i32, i32) {
unsafe {
let mut width = 0;
let mut height = 0;
ffi::glfwGetMonitorPhysicalSize(self.ptr, &mut width, &mut height);
(width as i32, height as i32)
}
}
pub fn get_name(&self) -> Option<String> {
unsafe { string_from_nullable_c_str(ffi::glfwGetMonitorName(self.ptr)) }
}
pub fn get_video_modes(&self) -> Vec<VidMode> {
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetVideoModes(self.ptr, &mut count);
slice::from_raw_parts(ptr, count as usize)
.iter()
.map(VidMode::from_glfw_vid_mode)
.collect()
}
}
pub fn get_video_mode(&self) -> Option<VidMode> {
unsafe {
let ptr = ffi::glfwGetVideoMode(self.ptr);
if ptr.is_null() {
None
} else {
Some(VidMode::from_glfw_vid_mode(&*ptr))
}
}
}
pub fn set_gamma(&mut self, gamma: f32) {
unsafe {
ffi::glfwSetGamma(self.ptr, gamma as c_float);
}
}
pub fn get_gamma_ramp(&self) -> GammaRamp {
unsafe {
let llramp = *ffi::glfwGetGammaRamp(self.ptr);
GammaRamp {
red: slice::from_raw_parts(llramp.red as *const c_ushort, llramp.size as usize)
.iter()
.map(|&x| x)
.collect(),
green: slice::from_raw_parts(llramp.green as *const c_ushort, llramp.size as usize)
.iter()
.map(|&x| x)
.collect(),
blue: slice::from_raw_parts(llramp.blue as *const c_ushort, llramp.size as usize)
.iter()
.map(|&x| x)
.collect(),
}
}
}
pub fn set_gamma_ramp(&mut self, ramp: &mut GammaRamp) {
unsafe {
ffi::glfwSetGammaRamp(
self.ptr,
&ffi::GLFWgammaramp {
red: ramp.red.as_mut_ptr(),
green: ramp.green.as_mut_ptr(),
blue: ramp.blue.as_mut_ptr(),
size: ramp.red.len() as u32,
},
);
}
}
pub fn get_content_scale(&self) -> (f32, f32) {
unsafe {
let mut xscale = 0.0_f32;
let mut yscale = 0.0_f32;
ffi::glfwGetMonitorContentScale(self.ptr, &mut xscale, &mut yscale);
(xscale, yscale)
}
}
pub fn get_workarea(&self) -> (i32, i32, i32, i32) {
unsafe {
let mut xpos = 0;
let mut ypos = 0;
let mut width = 0;
let mut height = 0;
ffi::glfwGetMonitorWorkarea(self.ptr, &mut xpos, &mut ypos, &mut width, &mut height);
(xpos, ypos, width, height)
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum MonitorEvent {
Connected = ffi::CONNECTED,
Disconnected = ffi::DISCONNECTED,
}
impl VidMode {
fn from_glfw_vid_mode(mode: &ffi::GLFWvidmode) -> VidMode {
VidMode {
width: mode.width as u32,
height: mode.height as u32,
red_bits: mode.redBits as u32,
green_bits: mode.greenBits as u32,
blue_bits: mode.blueBits as u32,
refresh_rate: mode.refreshRate as u32,
}
}
}
impl fmt::Debug for VidMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} x {}, {} = {} + {} + {}, {} Hz",
self.width,
self.height,
self.red_bits + self.green_bits + self.blue_bits,
self.red_bits,
self.green_bits,
self.blue_bits,
self.refresh_rate
)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum WindowHint {
RedBits(Option<u32>),
GreenBits(Option<u32>),
BlueBits(Option<u32>),
AlphaBits(Option<u32>),
DepthBits(Option<u32>),
StencilBits(Option<u32>),
AccumRedBits(Option<u32>),
AccumGreenBits(Option<u32>),
AccumBlueBits(Option<u32>),
AccumAlphaBits(Option<u32>),
AuxBuffers(Option<u32>),
Stereo(bool),
Samples(Option<u32>),
SRgbCapable(bool),
RefreshRate(Option<u32>),
ClientApi(ClientApiHint),
ContextVersionMajor(u32),
ContextVersionMinor(u32),
ContextVersion(u32, u32),
ContextRobustness(ContextRobustnessHint),
OpenGlForwardCompat(bool),
OpenGlDebugContext(bool),
OpenGlProfile(OpenGlProfileHint),
Resizable(bool),
Visible(bool),
Decorated(bool),
AutoIconify(bool),
Floating(bool),
Focused(bool),
ContextNoError(bool),
ContextCreationApi(ContextCreationApi),
ContextReleaseBehavior(ContextReleaseBehavior),
DoubleBuffer(bool),
CenterCursor(bool),
TransparentFramebuffer(bool),
FocusOnShow(bool),
ScaleToMonitor(bool),
CocoaRetinaFramebuffer(bool),
CocoaFrameName(Option<String>),
CocoaGraphicsSwitching(bool),
X11ClassName(Option<String>),
X11InstanceName(Option<String>),
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ClientApiHint {
NoApi = ffi::NO_API,
OpenGl = ffi::OPENGL_API,
OpenGlEs = ffi::OPENGL_ES_API,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextRobustnessHint {
NoRobustness = ffi::NO_ROBUSTNESS,
NoResetNotification = ffi::NO_RESET_NOTIFICATION,
LoseContextOnReset = ffi::LOSE_CONTEXT_ON_RESET,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum OpenGlProfileHint {
Any = ffi::OPENGL_ANY_PROFILE,
Core = ffi::OPENGL_CORE_PROFILE,
Compat = ffi::OPENGL_COMPAT_PROFILE,
}
#[derive(Copy, Clone, Debug)]
pub enum WindowMode<'a> {
FullScreen(&'a Monitor),
Windowed,
}
impl<'a> WindowMode<'a> {
fn to_ptr(&self) -> *mut ffi::GLFWmonitor {
match *self {
WindowMode::FullScreen(ref monitor) => monitor.ptr,
WindowMode::Windowed => ptr::null_mut(),
}
}
}
bitflags! {
#[doc = "Key modifiers (e.g., Shift, Control, Alt, Super)"]
pub struct Modifiers: ::std::os::raw::c_int {
const Shift = ::ffi::MOD_SHIFT;
const Control = ::ffi::MOD_CONTROL;
const Alt = ::ffi::MOD_ALT;
const Super = ::ffi::MOD_SUPER;
const CapsLock = ::ffi::MOD_CAPS_LOCK;
const NumLock = ::ffi::MOD_NUM_LOCK;
}
}
pub type Scancode = c_int;
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum WindowEvent {
Pos(i32, i32),
Size(i32, i32),
Close,
Refresh,
Focus(bool),
Iconify(bool),
FramebufferSize(i32, i32),
MouseButton(MouseButton, Action, Modifiers),
CursorPos(f64, f64),
CursorEnter(bool),
Scroll(f64, f64),
Key(Key, Scancode, Action, Modifiers),
Char(char),
CharModifiers(char, Modifiers),
FileDrop(Vec<PathBuf>),
Maximize(bool),
ContentScale(f32, f32),
}
pub fn flush_messages<'a, Message: Send>(
receiver: &'a Receiver<Message>,
) -> FlushedMessages<'a, Message> {
FlushedMessages(receiver)
}
pub struct FlushedMessages<'a, Message: 'a + Send>(&'a Receiver<Message>);
unsafe impl<'a, Message: 'a + Send> Send for FlushedMessages<'a, Message> {}
impl<'a, Message: 'static + Send> Iterator for FlushedMessages<'a, Message> {
type Item = Message;
fn next(&mut self) -> Option<Message> {
let FlushedMessages(receiver) = *self;
match receiver.try_recv() {
Ok(message) => Some(message),
_ => None,
}
}
}
#[derive(Debug)]
pub struct Window {
ptr: *mut ffi::GLFWwindow,
pub is_shared: bool,
drop_sender: Option<Sender<()>>,
drop_receiver: Receiver<()>,
current_cursor: Option<Cursor>,
pub glfw: Glfw,
}
macro_rules! set_window_callback {
($window:ident, $should_poll:expr, $ll_fn:ident, $callback:ident) => {{
if $should_poll {
unsafe {
ffi::$ll_fn($window.ptr, Some(callbacks::$callback));
}
} else {
unsafe {
ffi::$ll_fn($window.ptr, None);
}
}
}};
}
impl Window {
pub fn get_proc_address(&mut self, procname: &str) -> GLProc {
if self.ptr != unsafe { ffi::glfwGetCurrentContext() } {
self.make_current();
}
self.glfw.get_proc_address_raw(procname)
}
#[cfg(feature = "vulkan")]
pub fn get_instance_proc_address(&mut self, instance: VkInstance, procname: &str) -> VkProc {
self.glfw.get_instance_proc_address_raw(instance, procname)
}
#[cfg(feature = "vulkan")]
pub fn get_physical_device_presentation_support(
&self,
instance: VkInstance,
device: VkPhysicalDevice,
queue_family: u32,
) -> bool {
self.glfw
.get_physical_device_presentation_support_raw(instance, device, queue_family)
}
#[cfg(feature = "vulkan")]
pub fn create_window_surface(
&self,
instance: VkInstance,
allocator: *const VkAllocationCallbacks,
surface: *mut VkSurfaceKHR,
) -> VkResult {
unsafe { ffi::glfwCreateWindowSurface(instance, self.ptr, allocator, surface) }
}
pub fn create_shared(
&self,
width: u32,
height: u32,
title: &str,
mode: WindowMode,
) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
self.glfw
.create_window_intern(width, height, title, mode, Some(self))
}
pub fn close(self) {}
pub fn render_context(&mut self) -> RenderContext {
RenderContext {
ptr: self.ptr,
drop_sender: self.drop_sender.as_ref().unwrap().clone(),
}
}
pub fn should_close(&self) -> bool {
unsafe { ffi::glfwWindowShouldClose(self.ptr) == ffi::TRUE }
}
pub fn set_should_close(&mut self, value: bool) {
unsafe { ffi::glfwSetWindowShouldClose(self.ptr, value as c_int) }
}
pub fn set_title(&mut self, title: &str) {
unsafe {
with_c_str(title, |title| {
ffi::glfwSetWindowTitle(self.ptr, title);
});
}
}
pub fn get_pos(&self) -> (i32, i32) {
unsafe {
let mut xpos = 0;
let mut ypos = 0;
ffi::glfwGetWindowPos(self.ptr, &mut xpos, &mut ypos);
(xpos as i32, ypos as i32)
}
}
pub fn set_pos(&mut self, xpos: i32, ypos: i32) {
unsafe {
ffi::glfwSetWindowPos(self.ptr, xpos as c_int, ypos as c_int);
}
}
pub fn get_size(&self) -> (i32, i32) {
unsafe {
let mut width = 0;
let mut height = 0;
ffi::glfwGetWindowSize(self.ptr, &mut width, &mut height);
(width as i32, height as i32)
}
}
pub fn set_size(&mut self, width: i32, height: i32) {
unsafe {
ffi::glfwSetWindowSize(self.ptr, width as c_int, height as c_int);
}
}
pub fn get_frame_size(&self) -> (i32, i32, i32, i32) {
let (mut left, mut top, mut right, mut bottom): (i32, i32, i32, i32) = (0, 0, 0, 0);
unsafe {
ffi::glfwGetWindowFrameSize(
self.ptr,
&mut left as *mut c_int,
&mut top as *mut c_int,
&mut right as *mut c_int,
&mut bottom as *mut c_int,
);
}
(left, top, right, bottom)
}
pub fn get_framebuffer_size(&self) -> (i32, i32) {
unsafe {
let mut width = 0;
let mut height = 0;
ffi::glfwGetFramebufferSize(self.ptr, &mut width, &mut height);
(width as i32, height as i32)
}
}
pub fn set_aspect_ratio(&mut self, numer: u32, denum: u32) {
unsafe { ffi::glfwSetWindowAspectRatio(self.ptr, numer as c_int, denum as c_int) }
}
pub fn set_size_limits(
&mut self,
minwidth: Option<u32>,
minheight: Option<u32>,
maxwidth: Option<u32>,
maxheight: Option<u32>,
) {
unsafe {
ffi::glfwSetWindowSizeLimits(
self.ptr,
unwrap_dont_care(minwidth),
unwrap_dont_care(minheight),
unwrap_dont_care(maxwidth),
unwrap_dont_care(maxheight),
)
}
}
pub fn iconify(&mut self) {
unsafe {
ffi::glfwIconifyWindow(self.ptr);
}
}
pub fn restore(&mut self) {
unsafe {
ffi::glfwRestoreWindow(self.ptr);
}
}
pub fn maximize(&mut self) {
unsafe { ffi::glfwMaximizeWindow(self.ptr) }
}
pub fn show(&mut self) {
unsafe {
ffi::glfwShowWindow(self.ptr);
}
}
pub fn hide(&mut self) {
unsafe {
ffi::glfwHideWindow(self.ptr);
}
}
pub fn with_window_mode<T, F>(&self, f: F) -> T
where
F: Fn(WindowMode) -> T,
{
let ptr = unsafe { ffi::glfwGetWindowMonitor(self.ptr) };
if ptr.is_null() {
f(WindowMode::Windowed)
} else {
f(WindowMode::FullScreen(&Monitor { ptr: ptr }))
}
}
pub fn with_window_mode_mut<T, F>(&self, mut f: F) -> T
where
F: FnMut(WindowMode) -> T,
{
let ptr = unsafe { ffi::glfwGetWindowMonitor(self.ptr) };
if ptr.is_null() {
f(WindowMode::Windowed)
} else {
f(WindowMode::FullScreen(&Monitor { ptr: ptr }))
}
}
pub fn set_monitor(
&mut self,
mode: WindowMode,
xpos: i32,
ypos: i32,
width: u32,
height: u32,
refresh_rate: Option<u32>,
) {
let monitor_ptr = if let WindowMode::FullScreen(ref monitor) = mode {
monitor.ptr
} else {
ptr::null_mut()
};
unsafe {
ffi::glfwSetWindowMonitor(
self.ptr,
monitor_ptr,
xpos as c_int,
ypos as c_int,
width as c_int,
height as c_int,
unwrap_dont_care(refresh_rate),
)
}
}
pub fn focus(&mut self) {
unsafe { ffi::glfwFocusWindow(self.ptr) }
}
pub fn is_focused(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::FOCUSED) == ffi::TRUE }
}
pub fn is_iconified(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::ICONIFIED) == ffi::TRUE }
}
pub fn is_maximized(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::MAXIMIZED) == ffi::TRUE }
}
pub fn get_client_api(&self) -> c_int {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::CLIENT_API) }
}
pub fn get_context_version(&self) -> Version {
unsafe {
Version {
major: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_VERSION_MAJOR) as u64,
minor: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_VERSION_MINOR) as u64,
patch: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_REVISION) as u64,
pre: Vec::new(),
build: Vec::new(),
}
}
}
pub fn get_context_robustness(&self) -> c_int {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_ROBUSTNESS) }
}
pub fn is_opengl_forward_compat(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_FORWARD_COMPAT) == ffi::TRUE }
}
pub fn is_opengl_debug_context(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_DEBUG_CONTEXT) == ffi::TRUE }
}
pub fn get_opengl_profile(&self) -> c_int {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_PROFILE) }
}
pub fn is_resizable(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::RESIZABLE) == ffi::TRUE }
}
pub fn set_resizable(&mut self, resizable: bool) {
unsafe { ffi::glfwSetWindowAttrib(self.ptr, ffi::RESIZABLE, resizable as c_int) }
}
pub fn is_visible(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::VISIBLE) == ffi::TRUE }
}
pub fn is_decorated(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::DECORATED) == ffi::TRUE }
}
pub fn set_decorated(&mut self, decorated: bool) {
unsafe { ffi::glfwSetWindowAttrib(self.ptr, ffi::DECORATED, decorated as c_int) }
}
pub fn is_auto_iconify(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::AUTO_ICONIFY) == ffi::TRUE }
}
pub fn set_auto_iconify(&mut self, auto_iconify: bool) {
unsafe { ffi::glfwSetWindowAttrib(self.ptr, ffi::AUTO_ICONIFY, auto_iconify as c_int) }
}
pub fn is_floating(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::FLOATING) == ffi::TRUE }
}
pub fn set_floating(&mut self, floating: bool) {
unsafe { ffi::glfwSetWindowAttrib(self.ptr, ffi::FLOATING, floating as c_int) }
}
pub fn is_framebuffer_transparent(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::TRANSPARENT_FRAMEBUFFER) == ffi::TRUE }
}
pub fn is_focus_on_show(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::FOCUS_ON_SHOW) == ffi::TRUE }
}
pub fn set_focus_on_show(&mut self, focus_on_show: bool) {
unsafe { ffi::glfwSetWindowAttrib(self.ptr, ffi::FOCUS_ON_SHOW, focus_on_show as c_int) }
}
pub fn is_hovered(&self) -> bool {
unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::HOVERED) == ffi::TRUE }
}
pub fn set_pos_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowPosCallback,
window_pos_callback
);
}
pub fn set_all_polling(&mut self, should_poll: bool) {
self.set_pos_polling(should_poll);
self.set_size_polling(should_poll);
self.set_close_polling(should_poll);
self.set_refresh_polling(should_poll);
self.set_focus_polling(should_poll);
self.set_iconify_polling(should_poll);
self.set_framebuffer_size_polling(should_poll);
self.set_key_polling(should_poll);
self.set_char_polling(should_poll);
self.set_char_mods_polling(should_poll);
self.set_mouse_button_polling(should_poll);
self.set_cursor_pos_polling(should_poll);
self.set_cursor_enter_polling(should_poll);
self.set_scroll_polling(should_poll);
self.set_drag_and_drop_polling(should_poll);
self.set_maximize_polling(should_poll);
self.set_content_scale_polling(should_poll);
}
pub fn set_size_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowSizeCallback,
window_size_callback
);
}
pub fn set_close_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowCloseCallback,
window_close_callback
);
}
pub fn set_refresh_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowRefreshCallback,
window_refresh_callback
);
}
pub fn set_focus_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowFocusCallback,
window_focus_callback
);
}
pub fn set_iconify_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowIconifyCallback,
window_iconify_callback
);
}
pub fn set_framebuffer_size_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetFramebufferSizeCallback,
framebuffer_size_callback
);
}
pub fn set_drag_and_drop_polling(&mut self, should_poll: bool) {
set_window_callback!(self, should_poll, glfwSetDropCallback, drop_callback);
}
pub fn set_maximize_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowMaximizeCallback,
window_maximize_callback
);
}
pub fn set_content_scale_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetWindowContentScaleCallback,
window_content_scale_callback
);
}
pub fn get_cursor_mode(&self) -> CursorMode {
unsafe { mem::transmute(ffi::glfwGetInputMode(self.ptr, ffi::CURSOR)) }
}
pub fn set_cursor_mode(&mut self, mode: CursorMode) {
unsafe {
ffi::glfwSetInputMode(self.ptr, ffi::CURSOR, mode as c_int);
}
}
pub fn set_cursor(&mut self, cursor: Option<Cursor>) -> Option<Cursor> {
let previous = mem::replace(&mut self.current_cursor, cursor);
unsafe {
ffi::glfwSetCursor(
self.ptr,
match self.current_cursor {
Some(ref cursor) => cursor.ptr,
None => ptr::null_mut(),
},
)
}
previous
}
#[cfg(feature = "image")]
pub fn set_icon(&mut self, images: Vec<image::RgbaImage>) {
let image_data: Vec<(Vec<_>, u32, u32)> = images
.into_iter()
.map(|image| {
let (width, height) = image.dimensions();
(image.into_vec(), width, height)
})
.collect();
let glfw_images: Vec<ffi::GLFWimage> = image_data
.iter()
.map(|ref data| ffi::GLFWimage {
width: data.1 as c_int,
height: data.2 as c_int,
pixels: data.0.as_ptr() as *const c_uchar,
})
.collect();
unsafe {
ffi::glfwSetWindowIcon(
self.ptr,
glfw_images.len() as c_int,
glfw_images.as_ptr() as *const ffi::GLFWimage,
)
}
}
pub fn set_icon_from_pixels(&mut self, images: Vec<PixelImage>) {
let glfw_images: Vec<ffi::GLFWimage> = images
.iter()
.map(|image: &PixelImage| ffi::GLFWimage {
width: image.width as c_int,
height: image.height as c_int,
pixels: image.pixels.as_ptr() as *const c_uchar,
})
.collect();
unsafe {
ffi::glfwSetWindowIcon(
self.ptr,
glfw_images.len() as c_int,
glfw_images.as_ptr() as *const ffi::GLFWimage,
)
}
}
pub fn has_sticky_keys(&self) -> bool {
unsafe { ffi::glfwGetInputMode(self.ptr, ffi::STICKY_KEYS) == ffi::TRUE }
}
pub fn set_sticky_keys(&mut self, value: bool) {
unsafe {
ffi::glfwSetInputMode(self.ptr, ffi::STICKY_KEYS, value as c_int);
}
}
pub fn has_sticky_mouse_buttons(&self) -> bool {
unsafe { ffi::glfwGetInputMode(self.ptr, ffi::STICKY_MOUSE_BUTTONS) == ffi::TRUE }
}
pub fn set_sticky_mouse_buttons(&mut self, value: bool) {
unsafe {
ffi::glfwSetInputMode(self.ptr, ffi::STICKY_MOUSE_BUTTONS, value as c_int);
}
}
pub fn does_store_lock_key_mods(&self) -> bool {
unsafe { ffi::glfwGetInputMode(self.ptr, ffi::LOCK_KEY_MODS) == ffi::TRUE }
}
pub fn set_store_lock_key_mods(&mut self, value: bool) {
unsafe { ffi::glfwSetInputMode(self.ptr, ffi::LOCK_KEY_MODS, value as c_int) }
}
pub fn uses_raw_mouse_motion(&self) -> bool {
unsafe { ffi::glfwGetInputMode(self.ptr, ffi::RAW_MOUSE_MOTION) == ffi::TRUE }
}
pub fn set_raw_mouse_motion(&mut self, value: bool) {
unsafe { ffi::glfwSetInputMode(self.ptr, ffi::RAW_MOUSE_MOTION, value as c_int) }
}
pub fn get_key(&self, key: Key) -> Action {
unsafe { mem::transmute(ffi::glfwGetKey(self.ptr, key as c_int)) }
}
pub fn get_mouse_button(&self, button: MouseButton) -> Action {
unsafe { mem::transmute(ffi::glfwGetMouseButton(self.ptr, button as c_int)) }
}
pub fn get_cursor_pos(&self) -> (f64, f64) {
unsafe {
let mut xpos = 0.0;
let mut ypos = 0.0;
ffi::glfwGetCursorPos(self.ptr, &mut xpos, &mut ypos);
(xpos as f64, ypos as f64)
}
}
pub fn set_cursor_pos(&mut self, xpos: f64, ypos: f64) {
unsafe {
ffi::glfwSetCursorPos(self.ptr, xpos as c_double, ypos as c_double);
}
}
pub fn set_key_polling(&mut self, should_poll: bool) {
set_window_callback!(self, should_poll, glfwSetKeyCallback, key_callback);
}
pub fn set_char_polling(&mut self, should_poll: bool) {
set_window_callback!(self, should_poll, glfwSetCharCallback, char_callback);
}
pub fn set_char_mods_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetCharModsCallback,
char_mods_callback
);
}
pub fn set_mouse_button_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetMouseButtonCallback,
mouse_button_callback
);
}
pub fn set_cursor_pos_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetCursorPosCallback,
cursor_pos_callback
);
}
pub fn set_cursor_enter_polling(&mut self, should_poll: bool) {
set_window_callback!(
self,
should_poll,
glfwSetCursorEnterCallback,
cursor_enter_callback
);
}
pub fn set_scroll_polling(&mut self, should_poll: bool) {
set_window_callback!(self, should_poll, glfwSetScrollCallback, scroll_callback);
}
pub fn set_clipboard_string(&mut self, string: &str) {
unsafe {
with_c_str(string, |string| {
ffi::glfwSetClipboardString(self.ptr, string);
});
}
}
pub fn get_clipboard_string(&self) -> Option<String> {
unsafe { string_from_nullable_c_str(ffi::glfwGetClipboardString(self.ptr)) }
}
pub fn get_opacity(&self) -> f32 {
unsafe { ffi::glfwGetWindowOpacity(self.ptr) }
}
pub fn set_opacity(&mut self, opacity: f32) {
unsafe { ffi::glfwSetWindowOpacity(self.ptr, opacity) }
}
pub fn request_attention(&mut self) {
unsafe { ffi::glfwRequestWindowAttention(self.ptr) }
}
pub fn get_content_scale(&self) -> (f32, f32) {
unsafe {
let mut xscale = 0.0_f32;
let mut yscale = 0.0_f32;
ffi::glfwGetWindowContentScale(self.ptr, &mut xscale, &mut yscale);
(xscale, yscale)
}
}
#[cfg(target_os = "windows")]
pub fn get_win32_window(&self) -> *mut c_void {
unsafe { ffi::glfwGetWin32Window(self.ptr) }
}
#[cfg(target_os = "windows")]
pub fn get_wgl_context(&self) -> *mut c_void {
unsafe { ffi::glfwGetWGLContext(self.ptr) }
}
#[cfg(target_os = "macos")]
pub fn get_cocoa_window(&self) -> *mut c_void {
unsafe { ffi::glfwGetCocoaWindow(self.ptr) }
}
#[cfg(target_os = "macos")]
pub fn get_nsgl_context(&self) -> *mut c_void {
unsafe { ffi::glfwGetNSGLContext(self.ptr) }
}
#[cfg(target_os = "linux")]
pub fn get_x11_window(&self) -> *mut c_void {
unsafe { ffi::glfwGetX11Window(self.ptr) }
}
#[cfg(target_os = "linux")]
pub fn get_glx_context(&self) -> *mut c_void {
unsafe { ffi::glfwGetGLXContext(self.ptr) }
}
}
impl Drop for Window {
fn drop(&mut self) {
drop(self.drop_sender.take());
if self.drop_receiver.try_recv() != Err(std::sync::mpsc::TryRecvError::Disconnected) {
debug!("Attempted to drop a Window before the `RenderContext` was dropped.");
debug!("Blocking until the `RenderContext` was dropped.");
let _ = self.drop_receiver.recv();
}
if !self.ptr.is_null() {
unsafe {
let _: Box<Sender<(f64, WindowEvent)>> =
mem::transmute(ffi::glfwGetWindowUserPointer(self.ptr));
}
}
if !self.is_shared {
unsafe {
ffi::glfwDestroyWindow(self.ptr);
}
}
}
}
pub struct RenderContext {
ptr: *mut ffi::GLFWwindow,
#[allow(dead_code)]
drop_sender: Sender<()>,
}
unsafe impl Send for RenderContext {}
pub trait Context {
fn window_ptr(&self) -> *mut ffi::GLFWwindow;
fn window_id(&self) -> WindowId {
self.window_ptr() as WindowId
}
fn swap_buffers(&mut self) {
let ptr = self.window_ptr();
unsafe {
ffi::glfwSwapBuffers(ptr);
}
}
fn is_current(&self) -> bool {
self.window_ptr() == unsafe { ffi::glfwGetCurrentContext() }
}
fn make_current(&mut self) {
let ptr = self.window_ptr();
unsafe {
ffi::glfwMakeContextCurrent(ptr);
}
}
fn should_close(&self) -> bool {
let ptr = self.window_ptr();
unsafe { ffi::glfwWindowShouldClose(ptr) == ffi::TRUE }
}
fn set_should_close(&mut self, value: bool) {
let ptr = self.window_ptr();
unsafe {
ffi::glfwSetWindowShouldClose(ptr, value as c_int);
}
}
fn post_empty_event(&self) {
unsafe { ffi::glfwPostEmptyEvent() }
}
}
impl Context for Window {
fn window_ptr(&self) -> *mut ffi::GLFWwindow {
self.ptr
}
}
impl Context for RenderContext {
fn window_ptr(&self) -> *mut ffi::GLFWwindow {
self.ptr
}
}
unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle {
raw_window_handle(self)
}
}
unsafe impl HasRawWindowHandle for RenderContext {
fn raw_window_handle(&self) -> RawWindowHandle {
raw_window_handle(self)
}
}
fn raw_window_handle<C: Context>(context: &C) -> RawWindowHandle {
#[cfg(target_family = "windows")]
{
use raw_window_handle::windows::WindowsHandle;
let (hwnd, hinstance) = unsafe {
let hwnd = ffi::glfwGetWin32Window(context.window_ptr());
let hinstance = winapi::um::libloaderapi::GetModuleHandleW(std::ptr::null());
(hwnd, hinstance as _)
};
RawWindowHandle::Windows(WindowsHandle {
hwnd,
hinstance,
..WindowsHandle::empty()
})
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))]
{
use raw_window_handle::unix::XlibHandle;
let (window, display) = unsafe {
let window = ffi::glfwGetX11Window(context.window_ptr());
let display = ffi::glfwGetX11Display();
(window as std::os::raw::c_ulong, display)
};
RawWindowHandle::Xlib(XlibHandle {
window,
display,
..XlibHandle::empty()
})
}
#[cfg(target_os = "macos")]
{
use raw_window_handle::macos::MacOSHandle;
let (ns_window, ns_view) = unsafe {
let ns_window: *mut objc::runtime::Object =
ffi::glfwGetCocoaWindow(context.window_ptr()) as *mut _;
let ns_view: *mut objc::runtime::Object = objc::msg_send![ns_window, contentView];
assert_ne!(ns_view, std::ptr::null_mut());
(
ns_window as *mut std::ffi::c_void,
ns_view as *mut std::ffi::c_void,
)
};
RawWindowHandle::MacOS(MacOSHandle {
ns_window,
ns_view,
..MacOSHandle::empty()
})
}
}
pub fn make_context_current(context: Option<&dyn Context>) {
match context {
Some(ctx) => unsafe { ffi::glfwMakeContextCurrent(ctx.window_ptr()) },
None => unsafe { ffi::glfwMakeContextCurrent(ptr::null_mut()) },
}
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum JoystickId {
Joystick1 = ffi::JOYSTICK_1,
Joystick2 = ffi::JOYSTICK_2,
Joystick3 = ffi::JOYSTICK_3,
Joystick4 = ffi::JOYSTICK_4,
Joystick5 = ffi::JOYSTICK_5,
Joystick6 = ffi::JOYSTICK_6,
Joystick7 = ffi::JOYSTICK_7,
Joystick8 = ffi::JOYSTICK_8,
Joystick9 = ffi::JOYSTICK_9,
Joystick10 = ffi::JOYSTICK_10,
Joystick11 = ffi::JOYSTICK_11,
Joystick12 = ffi::JOYSTICK_12,
Joystick13 = ffi::JOYSTICK_13,
Joystick14 = ffi::JOYSTICK_14,
Joystick15 = ffi::JOYSTICK_15,
Joystick16 = ffi::JOYSTICK_16,
}
impl JoystickId {
pub fn from_i32(n: i32) -> Option<JoystickId> {
if n >= 0 && n <= ffi::JOYSTICK_LAST {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum GamepadButton {
ButtonA = ffi::GAMEPAD_BUTTON_A,
ButtonB = ffi::GAMEPAD_BUTTON_B,
ButtonX = ffi::GAMEPAD_BUTTON_X,
ButtonY = ffi::GAMEPAD_BUTTON_Y,
ButtonLeftBumper = ffi::GAMEPAD_BUTTON_LEFT_BUMPER,
ButtonRightBumper = ffi::GAMEPAD_BUTTON_RIGHT_BUMPER,
ButtonBack = ffi::GAMEPAD_BUTTON_BACK,
ButtonStart = ffi::GAMEPAD_BUTTON_START,
ButtonGuide = ffi::GAMEPAD_BUTTON_GUIDE,
ButtonLeftThumb = ffi::GAMEPAD_BUTTON_LEFT_THUMB,
ButtonRightThumb = ffi::GAMEPAD_BUTTON_RIGHT_THUMB,
ButtonDpadUp = ffi::GAMEPAD_BUTTON_DPAD_UP,
ButtonDpadRight = ffi::GAMEPAD_BUTTON_DPAD_RIGHT,
ButtonDpadDown = ffi::GAMEPAD_BUTTON_DPAD_DOWN,
ButtonDpadLeft = ffi::GAMEPAD_BUTTON_DPAD_LEFT,
}
impl GamepadButton {
pub fn from_i32(n: i32) -> Option<GamepadButton> {
if n >= 0 && n <= ffi::GAMEPAD_BUTTON_LAST {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum GamepadAxis {
AxisLeftX = ffi::GAMEPAD_AXIS_LEFT_X,
AxisLeftY = ffi::GAMEPAD_AXIS_LEFT_Y,
AxisRightX = ffi::GAMEPAD_AXIS_RIGHT_X,
AxisRightY = ffi::GAMEPAD_AXIS_RIGHT_Y,
AxisLeftTrigger = ffi::GAMEPAD_AXIS_LEFT_TRIGGER,
AxisRightTrigger = ffi::GAMEPAD_AXIS_RIGHT_TRIGGER,
}
impl GamepadAxis {
pub fn from_i32(n: i32) -> Option<GamepadAxis> {
if n >= 0 && n <= ffi::GAMEPAD_AXIS_LAST {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
}
bitflags! {
#[doc = "Joystick hats."]
pub struct JoystickHats: ::std::os::raw::c_int {
const Centered = ::ffi::HAT_CENTERED;
const Up = ::ffi::HAT_UP;
const Right = ::ffi::HAT_RIGHT;
const Down = ::ffi::HAT_DOWN;
const Left = ::ffi::HAT_LEFT;
}
}
#[derive(Clone)]
pub struct Joystick {
pub id: JoystickId,
pub glfw: Glfw,
}
pub struct GamepadState {
buttons: [Action; (ffi::GAMEPAD_BUTTON_LAST + 1) as usize],
axes: [f32; (ffi::GAMEPAD_AXIS_LAST + 1) as usize],
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum JoystickEvent {
Connected = ffi::CONNECTED,
Disconnected = ffi::DISCONNECTED,
}
pub type JoystickCallback<UserData> = Callback<fn(JoystickId, JoystickEvent, &UserData), UserData>;
impl Joystick {
pub fn is_present(&self) -> bool {
unsafe { ffi::glfwJoystickPresent(self.id as c_int) == ffi::TRUE }
}
pub fn get_axes(&self) -> Vec<f32> {
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetJoystickAxes(self.id as c_int, &mut count);
slice::from_raw_parts(ptr, count as usize)
.iter()
.map(|&a| a as f32)
.collect()
}
}
pub fn get_buttons(&self) -> Vec<c_int> {
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetJoystickButtons(self.id as c_int, &mut count);
slice::from_raw_parts(ptr, count as usize)
.iter()
.map(|&b| b as c_int)
.collect()
}
}
pub fn get_hats(&self) -> Vec<JoystickHats> {
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetJoystickHats(self.id as c_int, &mut count);
slice::from_raw_parts(ptr, count as usize)
.iter()
.map(|&b| mem::transmute(b as c_int))
.collect()
}
}
pub fn get_name(&self) -> Option<String> {
unsafe { string_from_nullable_c_str(ffi::glfwGetJoystickName(self.id as c_int)) }
}
pub fn get_guid(&self) -> Option<String> {
unsafe { string_from_nullable_c_str(ffi::glfwGetJoystickGUID(self.id as c_int)) }
}
pub fn is_gamepad(&self) -> bool {
unsafe { ffi::glfwJoystickIsGamepad(self.id as c_int) == ffi::TRUE }
}
pub fn get_gamepad_name(&self) -> Option<String> {
unsafe { string_from_nullable_c_str(ffi::glfwGetGamepadName(self.id as c_int)) }
}
pub fn get_gamepad_state(&self) -> Option<GamepadState> {
unsafe {
let mut state = ffi::GLFWgamepadstate {
buttons: [0; (ffi::GAMEPAD_BUTTON_LAST + 1) as usize],
axes: [0_f32; (ffi::GAMEPAD_AXIS_LAST + 1) as usize],
};
if ffi::glfwGetGamepadState(self.id as c_int, &mut state) == ffi::TRUE {
Some(state.into())
} else {
None
}
}
}
}
impl From<ffi::GLFWgamepadstate> for GamepadState {
fn from(state: ffi::GLFWgamepadstate) -> Self {
let mut buttons = [Action::Release; (ffi::GAMEPAD_BUTTON_LAST + 1) as usize];
let mut axes = [0_f32; (ffi::GAMEPAD_AXIS_LAST + 1) as usize];
unsafe {
state
.buttons
.iter()
.map(|&b| mem::transmute(b as c_int))
.zip(buttons.iter_mut())
.for_each(|(a, b)| *b = a);
}
state
.axes
.iter()
.map(|&f| f as f32)
.zip(axes.iter_mut())
.for_each(|(a, b)| *b = a);
Self { buttons, axes }
}
}
impl GamepadState {
pub fn get_button_state(&self, button: GamepadButton) -> Action {
self.buttons[button as usize]
}
pub fn get_axis(&self, axis: GamepadAxis) -> f32 {
self.axes[axis as usize]
}
}
#[inline(always)]
fn unwrap_dont_care(value: Option<u32>) -> c_int {
match value {
Some(v) => v as c_int,
None => ffi::DONT_CARE,
}
}