#![crate_type = "lib"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![crate_name = "glfw"]
#![deny(
rust_2018_compatibility,
rust_2018_idioms,
nonstandard_style,
unused,
future_incompatible,
missing_copy_implementations,
missing_debug_implementations,
missing_abi,
clippy::doc_markdown
)]
#![allow(non_upper_case_globals)]
macro_rules! make_user_callback_functions {
(
doc -> $doc:literal,
set -> $set:ident,
unset -> $unset:ident,
poll -> $poll:ident,
callback_field -> $callback_field:ident,
poll_field -> $poll_field:ident,
glfw -> $glfw:ident,
args -> ($($args:ty),*),
secret -> $secret:ident
) => {
#[doc = $doc]
pub fn $set<T>(&mut self, callback: T)
where T: FnMut(&mut Window, $($args),*) + 'static {
unsafe {
let callbacks = WindowCallbacks::get_callbacks(self.ptr);
callbacks.$callback_field = Some(Box::new(callback));
ffi::$glfw(self.ptr, Some(Self::$secret));
}
}
#[doc = $doc]
pub fn $unset(&mut self) {
unsafe {
let callbacks = WindowCallbacks::get_callbacks(self.ptr);
callbacks.$callback_field = None;
if !callbacks.$poll_field {
ffi::$glfw(self.ptr, None);
}
}
}
#[doc = $doc]
pub fn $poll(&mut self, should_poll: bool) {
unsafe {
let callbacks = WindowCallbacks::get_callbacks(self.ptr);
callbacks.$poll_field = should_poll;
if should_poll {
ffi::$glfw(self.ptr, Some(Self::$secret));
} else if callbacks.$callback_field.is_none() {
ffi::$glfw(self.ptr, None);
}
}
}
}
}
macro_rules! new_callback {
(
doc -> $doc:literal,
set -> $set:ident,
unset -> $unset:ident,
poll -> $poll:ident,
callback_field -> $callback_field:ident,
poll_field -> $poll_field:ident,
window_event -> $window_event:ident ($($args:ty),+),
glfw -> $glfw:ident ($($glfw_arg_names:ident: $glfw_args:ty),*),
convert_args -> ($($convert_args:expr),*),
secret -> $secret:ident
) => {
#[allow(unused_unsafe)]
extern "C" fn $secret(glfw_window: *mut GLFWwindow, $($glfw_arg_names: $glfw_args),*) {
unsafe {
let callbacks = WindowCallbacks::get_callbacks(glfw_window);
let window = &mut *callbacks.window_ptr;
if let Some(func) = &mut callbacks.$callback_field {
func(window, $($convert_args),*);
}
if callbacks.$poll_field {
let event = (ffi::glfwGetTime() as f64, WindowEvent::$window_event($($convert_args),*));
if let Some(event) = callbacks::unbuffered::handle(glfw_window as WindowId, event) {
callbacks.sender.send(event);
}
}
}
}
make_user_callback_functions!(
doc -> $doc,
set -> $set,
unset -> $unset,
poll -> $poll,
callback_field -> $callback_field,
poll_field -> $poll_field,
glfw -> $glfw,
args -> ($($args),*),
secret -> $secret
);
};
(
doc -> $doc:literal,
set -> $set:ident,
unset -> $unset:ident,
poll -> $poll:ident,
callback_field -> $callback_field:ident,
poll_field -> $poll_field:ident,
window_event -> $window_event:ident,
glfw -> $glfw:ident ($($glfw_arg_names:ident: $glfw_args:ty),*),
convert_args -> ($($convert_args:expr),*),
secret -> $secret:ident
) => {
#[allow(unused_unsafe)]
extern "C" fn $secret(glfw_window: *mut GLFWwindow, $($glfw_arg_names: $glfw_args),*) {
unsafe {
let callbacks = WindowCallbacks::get_callbacks(glfw_window);
let window = &mut *callbacks.window_ptr;
if let Some(func) = &mut callbacks.$callback_field {
func(window);
}
if callbacks.$poll_field {
let event = (ffi::glfwGetTime() as f64, WindowEvent::$window_event);
if let Some(event) = callbacks::unbuffered::handle(glfw_window as WindowId, event) {
callbacks.sender.send(event);
}
}
}
}
make_user_callback_functions!(
doc -> $doc,
set -> $set,
unset -> $unset,
poll -> $poll,
callback_field -> $callback_field,
poll_field -> $poll_field,
glfw -> $glfw,
args -> (),
secret -> $secret
);
}
}
#[cfg(feature = "log")]
#[macro_use]
extern crate log;
#[macro_use]
extern crate bitflags;
#[cfg(feature = "image")]
extern crate image;
#[cfg(feature = "raw-window-handle-v0-6")]
extern crate raw_window_handle_0_6 as raw_window_handle;
#[cfg(not(feature = "raw-window-handle-v0-6"))]
extern crate raw_window_handle_0_5 as raw_window_handle;
use std::collections::VecDeque;
#[cfg(not(feature = "raw-window-handle-v0-6"))]
use raw_window_handle::{HasRawWindowHandle, HasRawDisplayHandle};
#[cfg(feature = "raw-window-handle-v0-6")]
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, WindowHandle, HandleError, DisplayHandle};
use raw_window_handle::{RawWindowHandle, RawDisplayHandle};
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};
#[cfg(not(target_os = "emscripten"))]
use std::os::raw::c_void;
use std::path::PathBuf;
use std::ptr;
use std::ptr::{null, null_mut};
use std::slice;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
#[allow(unused)]
use std::ffi::*;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
#[cfg(feature = "vulkan")]
use ash::vk;
use crate::ffi::GLFWwindow;
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;
#[derive(Debug)]
#[repr(transparent)]
pub struct PWindow(Box<Window>);
impl PWindow {
fn raw_ptr(&mut self) -> *mut Window {
self.0.deref_mut()
}
}
impl Deref for PWindow {
type Target = Window;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl DerefMut for PWindow {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
unsafe impl Send for PWindow {}
unsafe impl Sync for PWindow {}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasWindowHandle for PWindow {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
self.0.window_handle()
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasDisplayHandle for PWindow {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.0.display_handle()
}
}
pub type WindowId = usize;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Version {
pub major: u64,
pub minor: u64,
pub patch: u64,
}
#[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 (0..=ffi::MOUSE_BUTTON_LAST).contains(&n) {
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),
}
}
}
#[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 fn fail_on_errors(_: Error, description: String) {
panic!("GLFW Error: {}", description);
}
#[macro_export]
macro_rules! fail_on_errors {
() => {{
|error, description| {
fail_on_errors(error, description);
}
}}
}
#[cfg(feature = "log")]
pub fn log_errors(_: Error, description: String) {
error!("GLFW Error: {}", description);
}
#[cfg(not(feature = "log"))]
pub fn log_errors(_: Error, description: String) {
eprintln!("GLFW Error: {}", description);
}
#[macro_export]
macro_rules! log_errors {
() => {{
|error, description| {
log_errors(error, description);
}
}}
}
#[derive(Debug)]
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,
}
#[derive(Debug)]
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);
#[derive(Debug)]
pub struct ThreadSafeGlfw {
glfw: Glfw
}
impl ThreadSafeGlfw {
pub fn from(glfw: &mut Glfw) -> Self {
Self {
glfw: glfw.clone()
}
}
pub fn set_swap_interval(&mut self, interval: SwapInterval) {
self.glfw.set_swap_interval(interval);
}
pub fn extension_supported(&self, extension: &str) -> bool {
self.glfw.extension_supported(extension)
}
pub fn get_time(&self) -> f64 {
self.glfw.get_time()
}
pub fn set_time(&mut self, time: f64) {
self.glfw.set_time(time);
}
#[cfg(feature = "vulkan")]
pub fn vulkan_supported(&self) -> bool {
self.glfw.vulkan_supported()
}
#[cfg(feature = "vulkan")]
pub fn get_required_instance_extensions(&self) -> Option<Vec<String>> {
self.glfw.get_required_instance_extensions()
}
#[cfg(feature = "vulkan")]
pub fn get_instance_proc_address_raw(&self, instance: vk::Instance, procname: &str) -> VkProc {
self.glfw.get_instance_proc_address_raw(instance, procname)
}
#[cfg(feature = "vulkan")]
pub fn get_physical_device_presentation_support_raw(
&self,
instance: vk::Instance,
device: vk::PhysicalDevice,
queue_family: u32,
) -> bool {
self.glfw.get_physical_device_presentation_support_raw(instance, device, queue_family)
}
pub fn get_timer_value(&self) -> u64 {
self.glfw.get_timer_value()
}
pub fn get_timer_frequency(&self) -> u64 {
self.glfw.get_timer_frequency()
}
pub fn post_empty_event(&self) {
self.glfw.post_empty_event()
}
}
unsafe impl Send for ThreadSafeGlfw {}
#[non_exhaustive]
#[derive(Debug)]
pub struct Glfw {
phantom: std::marker::PhantomData<*const ()>,
}
#[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(Copy, 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<T>(callback: T)
-> Result<Glfw, InitError>
where T: FnMut(Error, String) + 'static
{
callbacks::error::set(callback);
init_no_callbacks()
}
pub fn init_no_callbacks() -> Result<Glfw, InitError>
{
if unsafe { ffi::glfwInit() } == ffi::TRUE {
REF_COUNT_FOR_GLFW.fetch_add(1, Ordering::SeqCst);
Ok(Glfw {phantom: std::marker::PhantomData})
} else {
Err(InitError::Internal)
}
}
impl Glfw {
pub fn set_error_callback<T>(
&mut self,
callback: T,
)
where T: FnMut(Error, String) + 'static
{
callbacks::error::set(callback);
}
pub fn unset_error_callback(
&mut self,
)
{
callbacks::error::unset();
}
pub fn set_monitor_callback<T>(
&mut self,
callback: T
)
where T: FnMut(Monitor, MonitorEvent) + 'static
{
callbacks::monitor::set(callback);
}
pub fn unset_monitor_callback(
&mut self,
)
{
callbacks::monitor::unset();
}
pub fn set_joystick_callback<T>(
&mut self,
callback: T,
)
where T: FnMut(JoystickId, JoystickEvent) + 'static
{
callbacks::joystick::set(callback);
}
pub fn unset_joystick_callback(
&mut self,
)
{
callbacks::joystick::unset();
}
pub fn with_primary_monitor<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut Self, Option<&mut Monitor>) -> T,
{
match unsafe { ffi::glfwGetPrimaryMonitor() } {
ptr if ptr.is_null() => f(self, None),
ptr => f(self, Some(&mut Monitor { ptr })),
}
}
pub fn with_window_monitor<T, F>(&mut self, window: &mut Window, f: F) -> T
where
F: FnOnce(&mut Self, Option<&mut Monitor>) -> T,
{
match unsafe { ffi::glfwGetWindowMonitor(window.ptr) } {
ptr if ptr.is_null() => f(self, None),
ptr => f(self, Some(&mut Monitor { ptr })),
}
}
pub fn with_connected_monitors<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut Self, &[&mut Monitor]) -> T,
{
unsafe {
let mut count = 0;
let ptr = ffi::glfwGetMonitors(&mut count);
let mut monitors = slice::from_raw_parts(ptr as *const _, count as usize)
.iter()
.map(|&ptr| Monitor { ptr })
.collect::<Vec<Monitor>>();
let refs: Vec<&mut Monitor> = monitors
.iter_mut()
.collect();
f(self, &refs)
}
}
#[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::Maximized(is_maximized) => unsafe {
ffi::glfwWindowHint(ffi::MAXIMIZED, is_maximized 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(
&mut self,
width: u32,
height: u32,
title: &str,
mode: WindowMode<'_>,
) -> Option<(PWindow, GlfwReceiver<(f64, WindowEvent)>)> {
#[cfg(feature = "wayland")]
{
self.window_hint(WindowHint::Focused(false));
}
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<(PWindow, GlfwReceiver<(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) = glfw_channel(16, 256);
let window = Window {
ptr,
glfw: self.clone(),
is_shared: share.is_some(),
drop_sender: Some(drop_sender),
drop_receiver,
current_cursor: None,
};
let mut callbacks = Box::new(WindowCallbacks::new(sender));
let mut window = PWindow(Box::new(window));
unsafe {
callbacks.window_ptr = window.raw_ptr();
ffi::glfwSetWindowUserPointer(ptr, mem::transmute(callbacks));
}
Some((window, 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(all(target_os = "linux", not(feature = "wayland")))]
pub fn get_x11_display(&self) -> *mut c_void {
unsafe { ffi::glfwGetX11Display() }
}
#[cfg(all(target_os = "linux", feature = "wayland"))]
pub fn get_wayland_display(&self) -> *mut c_void {
unsafe { ffi::glfwGetWaylandDisplay() }
}
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(&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(&self) -> u64 {
unsafe { ffi::glfwGetTimerValue() as u64 }
}
pub fn get_timer_frequency(&self) -> u64 {
unsafe { ffi::glfwGetTimerFrequency() as u64 }
}
pub fn set_swap_interval(&mut self, interval: SwapInterval) {
unsafe {
ffi::glfwSwapInterval(match interval {
SwapInterval::None => 0_i32,
SwapInterval::Adaptive => -1_i32,
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: vk::Instance, 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: vk::Instance,
device: vk::PhysicalDevice,
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,
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 {phantom: std::marker::PhantomData}
}
}
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();
}
}
}
}
fn glfw_channel<T>(initial_capacity: usize, max_len: usize) -> (GlfwSender<T>, GlfwReceiver<T>) {
let shared = Arc::new(SharedTransmitter {
queue: Mutex::new(VecDeque::with_capacity(initial_capacity)),
max_len
});
let (mpsc_sender, mpsc_receiver) = channel();
let sender = GlfwSender { transmitter: shared.clone(), sender: mpsc_sender };
let receiver = GlfwReceiver { transmitter: shared.clone(), receiver: mpsc_receiver };
(sender, receiver)
}
#[derive(Debug)]
struct SharedTransmitter<T> {
queue: Mutex<VecDeque<T>>,
max_len: usize,
}
#[derive(Debug, Clone)]
struct GlfwSender<T> {
transmitter: Arc<SharedTransmitter<T>>,
sender: Sender<T>
}
impl<T> GlfwSender<T> {
fn send(&self, v: T) {
let mut queue = self.transmitter.queue.lock().unwrap();
if queue.len() >= self.transmitter.max_len {
let _ = self.sender.send(v);
} else {
queue.push_back(v);
}
}
}
#[derive(Debug)]
pub struct GlfwReceiver<T> {
transmitter: Arc<SharedTransmitter<T>>,
receiver: Receiver<T>
}
impl<T> GlfwReceiver<T> {
pub fn receive(&self) -> Option<T> {
let ret = self.transmitter.queue.lock().unwrap().pop_front();
if ret.is_some() {
ret
} else {
match self.receiver.try_recv() {
Ok(ret) => Some(ret),
Err(_) => None
}
}
}
}
struct WindowCallbacks {
window_ptr: *mut Window,
sender: GlfwSender<(f64, WindowEvent)>,
pos_callback: Option<Box<dyn FnMut(&mut Window, i32, i32)>>,
size_callback: Option<Box<dyn FnMut(&mut Window, i32, i32)>>,
close_callback: Option<Box<dyn FnMut(&mut Window)>>,
refresh_callback: Option<Box<dyn FnMut(&mut Window)>>,
focus_callback: Option<Box<dyn FnMut(&mut Window, bool)>>,
iconify_callback: Option<Box<dyn FnMut(&mut Window, bool)>>,
framebuffer_size_callback: Option<Box<dyn FnMut(&mut Window, i32, i32)>>,
key_callback: Option<Box<dyn FnMut(&mut Window, Key, Scancode, Action, Modifiers)>>,
char_callback: Option<Box<dyn FnMut(&mut Window, char)>>,
char_mods_callback: Option<Box<dyn FnMut(&mut Window, char, Modifiers)>>,
mouse_button_callback: Option<Box<dyn FnMut(&mut Window, MouseButton, Action, Modifiers)>>,
cursor_pos_callback: Option<Box<dyn FnMut(&mut Window, f64, f64)>>,
cursor_enter_callback: Option<Box<dyn FnMut(&mut Window, bool)>>,
scroll_callback: Option<Box<dyn FnMut(&mut Window, f64, f64)>>,
drag_and_drop_callback: Option<Box<dyn FnMut(&mut Window, Vec<PathBuf>)>>,
maximize_callback: Option<Box<dyn FnMut(&mut Window, bool)>>,
content_scale_callback: Option<Box<dyn FnMut(&mut Window, f32, f32)>>,
pos_polling: bool,
size_polling: bool,
close_polling: bool,
refresh_polling: bool,
focus_polling: bool,
iconify_polling: bool,
framebuffer_size_polling: bool,
key_polling: bool,
char_polling: bool,
char_mods_polling: bool,
mouse_button_polling: bool,
cursor_pos_polling: bool,
cursor_enter_polling: bool,
scroll_polling: bool,
drag_and_drop_polling: bool,
maximize_polling: bool,
content_scale_polling: bool
}
impl WindowCallbacks {
fn new(sender: GlfwSender<(f64, WindowEvent)>) -> Self {
Self {
window_ptr: std::ptr::null_mut(),
sender,
pos_callback: None,
size_callback: None,
close_callback: None,
refresh_callback: None,
focus_callback: None,
iconify_callback: None,
framebuffer_size_callback: None,
key_callback: None,
char_callback: None,
char_mods_callback: None,
mouse_button_callback: None,
cursor_pos_callback: None,
cursor_enter_callback: None,
scroll_callback: None,
drag_and_drop_callback: None,
maximize_callback: None,
content_scale_callback: None,
pos_polling: false,
size_polling: false,
close_polling: false,
refresh_polling: false,
focus_polling: false,
iconify_polling: false,
framebuffer_size_polling: false,
key_polling: false,
char_polling: false,
char_mods_polling: false,
mouse_button_polling: false,
cursor_pos_polling: false,
cursor_enter_polling: false,
scroll_polling: false,
drag_and_drop_polling: false,
maximize_polling: false,
content_scale_polling: false
}
}
fn get_callbacks<'a>(window: *mut GLFWwindow) -> &'a mut WindowCallbacks {
unsafe {
&mut *(ffi::glfwGetWindowUserPointer(window) as *mut WindowCallbacks)
}
}
}
pub fn get_error() -> Error {
unsafe {
mem::transmute(ffi::glfwGetError(null_mut()))
}
}
pub fn get_error_string() -> (Error, String) {
unsafe {
let mut description: *const c_char = null();
let error: Error = mem::transmute(ffi::glfwGetError(&mut description));
(error, string_from_c_str(description))
}
}
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,
}
}
}
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()) }
}
#[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()
.copied()
.collect(),
green: slice::from_raw_parts(llramp.green as *const c_ushort, llramp.size as usize)
.iter()
.copied()
.collect(),
blue: slice::from_raw_parts(llramp.blue as *const c_ushort, llramp.size as usize)
.iter()
.copied()
.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),
Maximized(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(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 = crate::ffi::MOD_SHIFT;
const Control = crate::ffi::MOD_CONTROL;
const Alt = crate::ffi::MOD_ALT;
const Super = crate::ffi::MOD_SUPER;
const CapsLock = crate::ffi::MOD_CAPS_LOCK;
const NumLock = crate::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<Message: Send>(receiver: &GlfwReceiver<Message>) -> FlushedMessages<'_, Message> {
FlushedMessages(receiver)
}
#[derive(Debug)]
pub struct FlushedMessages<'a, Message: Send>(&'a GlfwReceiver<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;
receiver.receive()
}
}
#[derive(Debug)]
pub struct Window {
ptr: *mut ffi::GLFWwindow,
pub is_shared: bool,
drop_sender: Option<Sender<()>>,
#[allow(unused)]
drop_receiver: Receiver<()>,
current_cursor: Option<Cursor>,
pub glfw: Glfw,
}
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: vk::Instance, procname: &str) -> VkProc {
self.glfw.get_instance_proc_address_raw(instance, procname)
}
#[cfg(feature = "vulkan")]
pub fn get_physical_device_presentation_support(
&self,
instance: vk::Instance,
device: vk::PhysicalDevice,
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: vk::Instance,
allocator: *const vk::AllocationCallbacks,
surface: *mut vk::SurfaceKHR,
) -> vk::Result {
unsafe { ffi::glfwCreateWindowSurface(instance, self.ptr, allocator, surface) }
}
pub fn create_shared(
&self,
width: u32,
height: u32,
title: &str,
mode: WindowMode<'_>,
) -> Option<(PWindow, GlfwReceiver<(f64, WindowEvent)>)> {
self.glfw
.create_window_intern(width, height, title, mode, Some(self))
}
pub fn close(self) {}
pub fn render_context(&mut self) -> PRenderContext {
PRenderContext(Box::new(RenderContext {
ptr: self.ptr,
glfw: self.glfw.clone(),
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: FnOnce(WindowMode<'_>) -> T,
{
let ptr = unsafe { ffi::glfwGetWindowMonitor(self.ptr) };
if ptr.is_null() {
f(WindowMode::Windowed)
} else {
f(WindowMode::FullScreen(&Monitor { 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(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,
}
}
}
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 }
}
new_callback!(
doc -> "Wrapper for `glfwSetWindowPosCallback`.",
set -> set_pos_callback,
unset -> unset_pos_callback,
poll -> set_pos_polling,
callback_field -> pos_callback,
poll_field -> pos_polling,
window_event -> Pos(i32, i32),
glfw -> glfwSetWindowPosCallback(x: c_int, y: c_int),
convert_args -> (x as i32, y as i32),
secret -> _pos_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowSizeCallback`.",
set -> set_size_callback,
unset -> unset_size_callback,
poll -> set_size_polling,
callback_field -> size_callback,
poll_field -> size_polling,
window_event -> Size(i32, i32),
glfw -> glfwSetWindowSizeCallback(width: c_int, height: c_int),
convert_args -> (width as i32, height as i32),
secret -> _size_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowCloseCallback`.",
set -> set_close_callback,
unset -> unset_close_callback,
poll -> set_close_polling,
callback_field -> close_callback,
poll_field -> close_polling,
window_event -> Close,
glfw -> glfwSetWindowCloseCallback(),
convert_args -> (),
secret -> _close_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowRefreshCallback`.",
set -> set_refresh_callback,
unset -> unset_refresh_callback,
poll -> set_refresh_polling,
callback_field -> refresh_callback,
poll_field -> refresh_polling,
window_event -> Refresh,
glfw -> glfwSetWindowRefreshCallback(),
convert_args -> (),
secret -> _refresh_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowFocusCallback`.",
set -> set_focus_callback,
unset -> unset_focus_callback,
poll -> set_focus_polling,
callback_field -> focus_callback,
poll_field -> focus_polling,
window_event -> Focus(bool),
glfw -> glfwSetWindowFocusCallback(focused: c_int),
convert_args -> (focused == ffi::TRUE),
secret -> _focus_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowIconifyCallback`.",
set -> set_iconify_callback,
unset -> unset_iconify_callback,
poll -> set_iconify_polling,
callback_field -> iconify_callback,
poll_field -> iconify_polling,
window_event -> Iconify(bool),
glfw -> glfwSetWindowIconifyCallback(iconified: c_int),
convert_args -> (iconified == ffi::TRUE),
secret -> _iconify_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetFramebufferSizeCallback`.",
set -> set_framebuffer_size_callback,
unset -> unset_framebuffer_size_callback,
poll -> set_framebuffer_size_polling,
callback_field -> framebuffer_size_callback,
poll_field -> framebuffer_size_polling,
window_event -> FramebufferSize(i32, i32),
glfw -> glfwSetFramebufferSizeCallback(width: c_int, height: c_int),
convert_args -> (width as i32, height as i32),
secret -> _framebuffer_size_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetKeyCallback`.",
set -> set_key_callback,
unset -> unset_key_callback,
poll -> set_key_polling,
callback_field -> key_callback,
poll_field -> key_polling,
window_event -> Key(Key, Scancode, Action, Modifiers),
glfw -> glfwSetKeyCallback(key: c_int, scancode: c_int, action: c_int, mods: c_int),
convert_args -> (
mem::transmute(key),
scancode, mem::transmute(action),
Modifiers::from_bits(mods).unwrap()
),
secret -> _key_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetCharCallback`.",
set -> set_char_callback,
unset -> unset_char_callback,
poll -> set_char_polling,
callback_field -> char_callback,
poll_field -> char_polling,
window_event -> Char(char),
glfw -> glfwSetCharCallback(character: c_uint),
convert_args -> (::std::char::from_u32(character).unwrap()),
secret -> _char_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetCharModsCallback`.",
set -> set_char_mods_callback,
unset -> unset_char_mods_callback,
poll -> set_char_mods_polling,
callback_field -> char_mods_callback,
poll_field -> char_mods_polling,
window_event -> CharModifiers(char, Modifiers),
glfw -> glfwSetCharModsCallback(character: c_uint, mods: c_int),
convert_args -> (
::std::char::from_u32(character).unwrap(),
Modifiers::from_bits(mods).unwrap()
),
secret -> _char_mods_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetMouseButtonCallback`.",
set -> set_mouse_button_callback,
unset -> unset_mouse_button_callback,
poll -> set_mouse_button_polling,
callback_field -> mouse_button_callback,
poll_field -> mouse_button_polling,
window_event -> MouseButton(MouseButton, Action, Modifiers),
glfw -> glfwSetMouseButtonCallback(button: c_int, action: c_int, mods: c_int),
convert_args -> (
mem::transmute(button),
mem::transmute(action),
Modifiers::from_bits(mods).unwrap()
),
secret -> _mouse_button_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetCursorPosCallback`.",
set -> set_cursor_pos_callback,
unset -> unset_cursor_pos_callback,
poll -> set_cursor_pos_polling,
callback_field -> cursor_pos_callback,
poll_field -> cursor_pos_polling,
window_event -> CursorPos(f64, f64),
glfw -> glfwSetCursorPosCallback(x: c_double, y: c_double),
convert_args -> (x as f64, y as f64),
secret -> _cursor_pos_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetCursorEnterCallback`.",
set -> set_cursor_enter_callback,
unset -> unset_cursor_enter_callback,
poll -> set_cursor_enter_polling,
callback_field -> cursor_enter_callback,
poll_field -> cursor_enter_polling,
window_event -> CursorEnter(bool),
glfw -> glfwSetCursorEnterCallback(entered: c_int),
convert_args -> (entered == ffi::TRUE),
secret -> _cursor_enter_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetScrollCallback`.",
set -> set_scroll_callback,
unset -> unset_scroll_callback,
poll -> set_scroll_polling,
callback_field -> scroll_callback,
poll_field -> scroll_polling,
window_event -> Scroll(f64, f64),
glfw -> glfwSetScrollCallback(x: c_double, y: c_double),
convert_args -> (x as f64, y as f64),
secret -> _scroll_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetDropCallback`.",
set -> set_drag_and_drop_callback,
unset -> unset_drag_and_drop_callback,
poll -> set_drag_and_drop_polling,
callback_field -> drag_and_drop_callback,
poll_field -> drag_and_drop_polling,
window_event -> FileDrop(Vec<PathBuf>),
glfw -> glfwSetDropCallback(num_paths: c_int, paths: *mut *const c_char),
convert_args -> ({
slice::from_raw_parts(paths, num_paths as usize)
.iter()
.map(|path| PathBuf::from(std::str::from_utf8({
CStr::from_ptr(*path)
.to_bytes()
})
.unwrap()
.to_string()))
.collect()
}),
secret -> _drag_and_drop_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowMaximizeCallback`.",
set -> set_maximize_callback,
unset -> unset_maximize_callback,
poll -> set_maximize_polling,
callback_field -> maximize_callback,
poll_field -> maximize_polling,
window_event -> Maximize(bool),
glfw -> glfwSetWindowMaximizeCallback(maximized: c_int),
convert_args -> (maximized == ffi::TRUE),
secret -> _maximize_callback
);
new_callback!(
doc -> "Wrapper for `glfwSetWindowContentScaleCallback`.",
set -> set_content_scale_callback,
unset -> unset_content_scale_callback,
poll -> set_content_scale_polling,
callback_field -> content_scale_callback,
poll_field -> content_scale_polling,
window_event -> ContentScale(f32, f32),
glfw -> glfwSetWindowContentScaleCallback(xscale: c_float, yscale: c_float),
convert_args -> (xscale as f32, yscale as f32),
secret -> _content_scale_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 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(|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_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(all(target_os = "linux", not(feature = "wayland")))]
pub fn get_x11_window(&self) -> *mut c_void {
unsafe { ffi::glfwGetX11Window(self.ptr) }
}
#[cfg(all(target_os = "linux", feature = "wayland"))]
pub fn get_wayland_window(&self) -> *mut c_void {
unsafe { ffi::glfwGetWaylandWindow(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());
#[cfg(feature = "log")]
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<WindowCallbacks> =
mem::transmute(ffi::glfwGetWindowUserPointer(self.ptr));
}
}
if !self.is_shared {
unsafe {
ffi::glfwDestroyWindow(self.ptr);
}
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct PRenderContext(Box<RenderContext>);
impl Deref for PRenderContext {
type Target = RenderContext;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl DerefMut for PRenderContext {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
unsafe impl Send for PRenderContext {}
unsafe impl Sync for PRenderContext {}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasWindowHandle for PRenderContext {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
self.0.window_handle()
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasDisplayHandle for PRenderContext {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.0.display_handle()
}
}
#[derive(Debug)]
pub struct RenderContext {
ptr: *mut ffi::GLFWwindow,
glfw: Glfw,
#[allow(dead_code)]
drop_sender: Sender<()>,
}
impl RenderContext {
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: vk::Instance, procname: &str) -> VkProc {
self.glfw.get_instance_proc_address_raw(instance, procname)
}
#[cfg(feature = "vulkan")]
pub fn get_physical_device_presentation_support(
&self,
instance: vk::Instance,
device: vk::PhysicalDevice,
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: vk::Instance,
allocator: *const vk::AllocationCallbacks,
surface: *mut vk::SurfaceKHR,
) -> vk::Result {
unsafe { ffi::glfwCreateWindowSurface(instance, self.ptr, allocator, surface) }
}
}
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
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasWindowHandle for Window {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
Ok(unsafe { WindowHandle::borrow_raw(raw_window_handle(self)) })
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasWindowHandle for RenderContext {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
Ok(unsafe { WindowHandle::borrow_raw(raw_window_handle(self)) })
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasDisplayHandle for Window {
fn display_handle(&'_ self) -> Result<DisplayHandle<'_>, HandleError> {
Ok(unsafe { DisplayHandle::borrow_raw(raw_display_handle()) })
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
impl HasDisplayHandle for RenderContext {
fn display_handle(&'_ self) -> Result<DisplayHandle<'_>, HandleError> {
Ok(unsafe { DisplayHandle::borrow_raw(raw_display_handle()) })
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle {
raw_window_handle(self)
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
unsafe impl HasRawWindowHandle for RenderContext {
fn raw_window_handle(&self) -> RawWindowHandle {
raw_window_handle(self)
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
unsafe impl HasRawDisplayHandle for Window {
fn raw_display_handle(&self) -> RawDisplayHandle {
raw_display_handle()
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
unsafe impl HasRawDisplayHandle for RenderContext {
fn raw_display_handle(&self) -> RawDisplayHandle {
raw_display_handle()
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
fn raw_window_handle<C: Context>(context: &C) -> RawWindowHandle {
#[cfg(target_family = "windows")]
{
use raw_window_handle::Win32WindowHandle;
use std::num::NonZeroIsize;
let (hwnd, hinstance): (*mut std::ffi::c_void, *mut std::ffi::c_void) = unsafe {
let hwnd= ffi::glfwGetWin32Window(context.window_ptr());
let hinstance: *mut c_void = winapi::um::libloaderapi::GetModuleHandleW(std::ptr::null()) as _;
(hwnd, hinstance as _)
};
let mut handle = Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
handle.hinstance = NonZeroIsize::new(hinstance as isize);
RawWindowHandle::Win32(handle)
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), not(feature = "wayland")))]
{
use raw_window_handle::XlibWindowHandle;
let window = unsafe { ffi::glfwGetX11Window(context.window_ptr()) as std::os::raw::c_ulong };
RawWindowHandle::Xlib(XlibWindowHandle::new(window))
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), feature = "wayland"))]
{
use raw_window_handle::WaylandWindowHandle;
use std::ptr::NonNull;
let surface = unsafe { ffi::glfwGetWaylandWindow(context.window_ptr()) };
let mut handle = WaylandWindowHandle::new(NonNull::new(surface).expect("wayland window surface is null"));
RawWindowHandle::Wayland(handle)
}
#[cfg(target_os = "macos")]
{
use objc2::msg_send_id;
use objc2::rc::Id;
use objc2::runtime::NSObject;
use raw_window_handle::AppKitWindowHandle;
use std::ptr::NonNull;
let ns_window: *mut NSObject =
unsafe { ffi::glfwGetCocoaWindow(context.window_ptr()) as *mut _ };
let ns_view: Option<Id<NSObject>> = unsafe { msg_send_id![ns_window, contentView] };
let ns_view = ns_view.expect("failed to access contentView on GLFW NSWindow");
let ns_view: NonNull<NSObject> = NonNull::from(&*ns_view);
let handle = AppKitWindowHandle::new(ns_view.cast());
RawWindowHandle::AppKit(handle)
}
#[cfg(target_os = "emscripten")]
{
let _ = context; let mut wh = raw_window_handle::WebWindowHandle::new(1);
RawWindowHandle::Web(wh)
}
}
#[cfg(feature = "raw-window-handle-v0-6")]
fn raw_display_handle() -> RawDisplayHandle {
#[cfg(target_family = "windows")]
{
use raw_window_handle::WindowsDisplayHandle;
RawDisplayHandle::Windows(WindowsDisplayHandle::new())
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), not(feature = "wayland")))]
{
use raw_window_handle::XlibDisplayHandle;
use std::ptr::NonNull;
let display = NonNull::new(unsafe { ffi::glfwGetX11Display() });
let handle = XlibDisplayHandle::new(display, 0);
RawDisplayHandle::Xlib(handle)
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), feature = "wayland"))]
{
use raw_window_handle::WaylandDisplayHandle;
use std::ptr::NonNull;
let display = NonNull::new(unsafe { ffi::glfwGetWaylandDisplay() });
let handle = WaylandDisplayHandle::new(display, 0);
RawDisplayHandle::Wayland(handle)
}
#[cfg(target_os = "macos")]
{
use raw_window_handle::AppKitDisplayHandle;
RawDisplayHandle::AppKit(AppKitDisplayHandle::new())
}
#[cfg(target_os = "emscripten")]
{
RawDisplayHandle::Web(raw_window_handle::WebDisplayHandle::new())
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
fn raw_window_handle<C: Context>(context: &C) -> RawWindowHandle {
#[cfg(target_family = "windows")]
{
use raw_window_handle::Win32WindowHandle;
let (hwnd, hinstance) = unsafe {
let hwnd = ffi::glfwGetWin32Window(context.window_ptr());
let hinstance = winapi::um::libloaderapi::GetModuleHandleW(std::ptr::null());
(hwnd, hinstance as _)
};
let mut handle = Win32WindowHandle::empty();
handle.hwnd = hwnd;
handle.hinstance = hinstance;
RawWindowHandle::Win32(handle)
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), not(feature = "wayland")))]
{
use raw_window_handle::XlibWindowHandle;
let mut handle = XlibWindowHandle::empty();
handle.window = unsafe { ffi::glfwGetX11Window(context.window_ptr()) as std::os::raw::c_ulong };
RawWindowHandle::Xlib(handle)
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), feature = "wayland"))]
{
use raw_window_handle::WaylandWindowHandle;
let mut handle = WaylandWindowHandle::empty();
handle.surface = unsafe { ffi::glfwGetWaylandWindow(context.window_ptr()) };
RawWindowHandle::Wayland(handle)
}
#[cfg(target_os = "macos")]
{
use raw_window_handle::AppKitWindowHandle;
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,
)
};
let mut handle = AppKitWindowHandle::empty();
handle.ns_window = ns_window;
handle.ns_view = ns_view;
RawWindowHandle::AppKit(handle)
}
#[cfg(target_os = "emscripten")]
{
let _ = context; let mut wh = raw_window_handle::WebWindowHandle::empty();
wh.id = 1;
RawWindowHandle::Web(wh)
}
}
#[cfg(not(feature = "raw-window-handle-v0-6"))]
fn raw_display_handle() -> RawDisplayHandle {
#[cfg(target_family = "windows")]
{
use raw_window_handle::WindowsDisplayHandle;
RawDisplayHandle::Windows(WindowsDisplayHandle::empty())
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), not(feature = "wayland")))]
{
use raw_window_handle::XlibDisplayHandle;
let mut handle = XlibDisplayHandle::empty();
handle.display = unsafe { ffi::glfwGetX11Display() };
RawDisplayHandle::Xlib(handle)
}
#[cfg(all(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"), feature = "wayland"))]
{
use raw_window_handle::WaylandDisplayHandle;
let mut handle = WaylandDisplayHandle::empty();
handle.display = unsafe { ffi::glfwGetWaylandDisplay() };
RawDisplayHandle::Wayland(handle)
}
#[cfg(target_os = "macos")]
{
use raw_window_handle::AppKitDisplayHandle;
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
}
#[cfg(target_os = "emscripten")]
{
RawDisplayHandle::Web(raw_window_handle::WebDisplayHandle::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 (0..=ffi::JOYSTICK_LAST).contains(&n) {
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 (0..=ffi::GAMEPAD_BUTTON_LAST).contains(&n) {
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 (0..=ffi::GAMEPAD_AXIS_LAST).contains(&n) {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
}
bitflags! {
#[doc = "Joystick hats."]
pub struct JoystickHats: ::std::os::raw::c_int {
const Centered = crate::ffi::HAT_CENTERED;
const Up = crate::ffi::HAT_UP;
const Right = crate::ffi::HAT_RIGHT;
const Down = crate::ffi::HAT_DOWN;
const Left = crate::ffi::HAT_LEFT;
}
}
#[derive(Clone, Debug)]
pub struct Joystick {
pub id: JoystickId,
pub glfw: Glfw,
}
#[derive(Copy, Clone, Debug)]
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,
}
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,
}
}