#![deny(missing_debug_implementations)]
#[cfg(not(any(
target_os = "macos",
target_os = "redox",
windows,
target_arch = "wasm32"
)))]
#[cfg(feature = "wayland")]
#[macro_use]
extern crate dlib;
mod error;
mod icon;
mod key;
mod key_handler;
mod os;
mod rate;
use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle, WindowHandle};
use std::{ffi::c_void, fmt, time::Duration};
#[cfg(target_arch = "wasm32")]
use std::panic;
#[cfg(target_os = "macos")]
use os::macos as imp;
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
))]
use os::posix as imp;
#[cfg(target_os = "redox")]
use os::redox as imp;
#[cfg(target_arch = "wasm32")]
use os::wasm as imp;
#[cfg(target_os = "windows")]
use os::windows as imp;
pub use error::Error;
pub use icon::Icon;
pub use key::Key;
pub use raw_window_handle::HasWindowHandle;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Copy, Debug)]
pub enum Scale {
FitScreen,
X1,
X2,
X4,
X8,
X16,
X32,
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum KeyRepeat {
Yes,
No,
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum MouseButton {
Left,
Middle,
Right,
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum MouseMode {
Pass,
Clamp,
Discard,
}
impl MouseMode {
pub(crate) fn get_pos(
self,
mut x: f32,
mut y: f32,
scale: f32,
mut width: f32,
mut height: f32,
) -> Option<(f32, f32)> {
x /= scale;
y /= scale;
width /= scale;
height /= scale;
match self {
Self::Pass => Some((x, y)),
Self::Clamp => Some((x.clamp(0.0, width - 1.0), y.clamp(0.0, height - 1.0))),
Self::Discard => {
if x < 0.0 || y < 0.0 || x >= width || y >= height {
None
} else {
Some((x, y))
}
}
}
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum CursorStyle {
Arrow,
Ibeam,
Crosshair,
ClosedHand,
OpenHand,
ResizeLeftRight,
ResizeUpDown,
ResizeAll,
}
pub trait InputCallback {
fn add_char(&mut self, uni_char: u32);
fn set_key_state(&mut self, _key: Key, _state: bool) {}
}
pub struct Window(imp::Window);
impl fmt::Debug for Window {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Window").field(&format_args!("..")).finish()
}
}
impl HasWindowHandle for Window {
fn window_handle(&self) -> std::result::Result<WindowHandle, HandleError> {
self.0.window_handle()
}
}
impl HasDisplayHandle for Window {
fn display_handle(&self) -> std::result::Result<DisplayHandle, HandleError> {
self.0.display_handle()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ScaleMode {
Stretch,
AspectRatioStretch,
Center,
UpperLeft,
}
#[derive(Clone, Copy, Debug)]
pub struct WindowOptions {
pub borderless: bool,
pub title: bool,
pub resize: bool,
pub scale: Scale,
pub scale_mode: ScaleMode,
pub topmost: bool,
pub transparency: bool,
pub none: bool,
}
#[allow(dead_code)]
impl WindowOptions {
const WINDOW_BORDERLESS: u32 = 1 << 1;
const WINDOW_RESIZE: u32 = 1 << 2;
const WINDOW_TITLE: u32 = 1 << 3;
#[inline]
pub(crate) fn get_flags(self) -> u32 {
let mut flags = 0u32;
if self.borderless {
flags |= Self::WINDOW_BORDERLESS;
}
if self.title {
flags |= Self::WINDOW_TITLE;
}
if self.resize {
flags |= Self::WINDOW_RESIZE;
}
flags
}
}
impl Window {
#[cfg(not(target_arch = "wasm32"))]
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
if opts.transparency && !opts.borderless {
return Err(Error::WindowCreate(
"Window transparency requires the borderless property".to_owned(),
));
}
imp::Window::new(name, width, height, opts).map(Window)
}
#[cfg(target_arch = "wasm32")]
pub fn new(
container: &str,
width: usize,
height: usize,
opts: WindowOptions,
) -> Result<Window> {
panic::set_hook(Box::new(console_error_panic_hook::hook));
if opts.transparency && !opts.borderless {
return Err(Error::WindowCreate(
"Window transparency requires the borderless property".to_owned(),
));
}
imp::Window::new(container, width, height, opts).map(Window)
}
#[inline]
pub fn set_title(&mut self, title: &str) {
self.0.set_title(title)
}
#[inline]
pub fn set_icon(&mut self, icon: Icon) {
self.0.set_icon(icon)
}
#[inline]
pub fn get_window_handle(&self) -> *mut c_void {
self.0.get_window_handle()
}
#[inline]
pub fn update_with_buffer(
&mut self,
buffer: &[u32],
width: usize,
height: usize,
) -> Result<()> {
let update_result = self
.0
.update_with_buffer_stride(buffer, width, height, width);
self.0.update_rate();
update_result
}
#[inline]
pub fn update(&mut self) {
self.0.update();
self.0.update_rate();
}
#[inline]
pub fn is_open(&self) -> bool {
self.0.is_open()
}
#[inline]
pub fn set_position(&mut self, x: isize, y: isize) {
self.0.set_position(x, y)
}
#[inline]
pub fn get_position(&self) -> (isize, isize) {
self.0.get_position()
}
#[inline]
pub fn topmost(&self, topmost: bool) {
self.0.topmost(topmost)
}
#[inline]
pub fn set_background_color(&mut self, red: u8, green: u8, blue: u8) {
self.0
.set_background_color((red as u32) << 16 | (green as u32) << 8 | blue as u32);
}
#[inline]
pub fn set_cursor_visibility(&mut self, visibility: bool) {
self.0.set_cursor_visibility(visibility);
}
#[inline]
#[deprecated(
since = "0.26.0",
note = "use `set_target_fps` instead, this function will be removed in the future"
)]
pub fn limit_update_rate(&mut self, time: Option<Duration>) {
self.0.set_rate(time)
}
#[inline]
pub fn set_target_fps(&mut self, fps: usize) {
match fps {
0 => self.0.set_rate(None),
non_zero => {
let fps = Duration::from_secs_f32(1. / non_zero as f32);
self.0.set_rate(Some(fps));
}
}
}
#[inline]
pub fn get_size(&self) -> (usize, usize) {
self.0.get_size()
}
#[inline]
pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> {
self.0.get_mouse_pos(mode)
}
#[inline]
pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> {
self.0.get_unscaled_mouse_pos(mode)
}
#[inline]
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
self.0.get_mouse_down(button)
}
#[inline]
pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> {
self.0.get_scroll_wheel()
}
#[inline]
pub fn set_cursor_style(&mut self, cursor: CursorStyle) {
self.0.set_cursor_style(cursor)
}
#[inline]
pub fn get_keys(&self) -> Vec<Key> {
self.0.get_keys()
}
#[inline]
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Vec<Key> {
self.0.get_keys_pressed(repeat)
}
#[inline]
pub fn get_keys_released(&self) -> Vec<Key> {
self.0.get_keys_released()
}
#[inline]
pub fn is_key_down(&self, key: Key) -> bool {
self.0.is_key_down(key)
}
#[inline]
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
self.0.is_key_pressed(key, repeat)
}
#[inline]
pub fn is_key_released(&self, key: Key) -> bool {
self.0.is_key_released(key)
}
#[inline]
pub fn set_key_repeat_delay(&mut self, delay: f32) {
self.0.set_key_repeat_delay(delay)
}
#[inline]
pub fn set_key_repeat_rate(&mut self, rate: f32) {
self.0.set_key_repeat_rate(rate)
}
#[inline]
pub fn is_active(&mut self) -> bool {
self.0.is_active()
}
#[inline]
pub fn set_input_callback(&mut self, callback: Box<dyn InputCallback>) {
self.0.set_input_callback(callback)
}
#[inline]
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
self.0.add_menu(&menu.0)
}
#[inline]
pub fn remove_menu(&mut self, handle: MenuHandle) {
self.0.remove_menu(handle)
}
#[cfg(any(target_os = "macos", target_os = "windows", target_arch = "wasm32"))]
#[inline]
pub fn get_posix_menus(&self) -> Option<&Vec<UnixMenu>> {
None
}
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
))]
#[inline]
pub fn get_posix_menus(&self) -> Option<&Vec<UnixMenu>> {
self.0.get_posix_menus()
}
#[deprecated(
since = "0.17.0",
note = "`get_unix_menus` will be removed in 1.0.0, use `get_posix_menus` instead"
)]
#[inline]
pub fn get_unix_menus(&self) -> Option<&Vec<UnixMenu>> {
self.get_posix_menus()
}
#[inline]
pub fn is_menu_pressed(&mut self) -> Option<usize> {
self.0.is_menu_pressed()
}
}
pub const MENU_KEY_COMMAND: usize = 1;
pub const MENU_KEY_WIN: usize = 2;
pub const MENU_KEY_SHIFT: usize = 4;
pub const MENU_KEY_CTRL: usize = 8;
pub const MENU_KEY_ALT: usize = 16;
const MENU_ID_SEPARATOR: usize = 0xffffffff;
#[derive(Debug, Clone)]
pub struct UnixMenu {
pub name: String,
pub items: Vec<UnixMenuItem>,
#[doc(hidden)]
pub handle: MenuHandle,
#[doc(hidden)]
pub item_counter: MenuItemHandle,
}
#[derive(Debug, Clone)]
pub struct UnixMenuItem {
pub sub_menu: Option<Box<UnixMenu>>,
pub handle: MenuItemHandle,
pub id: usize,
pub label: String,
pub enabled: bool,
pub key: Key,
pub modifier: usize,
}
#[derive(Debug, Copy, Clone)]
#[doc(hidden)]
pub struct MenuItemHandle(pub u64);
#[derive(Debug, Copy, Clone, PartialEq)]
#[doc(hidden)]
pub struct MenuHandle(pub u64);
pub struct Menu(imp::Menu);
impl fmt::Debug for Menu {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Menu").field(&format_args!("..")).finish()
}
}
impl Menu {
pub fn new(name: &str) -> Result<Menu> {
imp::Menu::new(name).map(Menu)
}
#[inline]
pub fn destroy_menu(&mut self) {
}
#[inline]
pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) {
self.0.add_sub_menu(name, &menu.0)
}
#[inline]
pub fn add_separator(&mut self) {
self.add_menu_item(&MenuItem {
id: MENU_ID_SEPARATOR,
..MenuItem::default()
});
}
#[inline]
pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle {
self.0.add_menu_item(item)
}
#[inline]
pub fn add_item(&mut self, name: &str, id: usize) -> MenuItem {
MenuItem {
id,
label: name.to_owned(),
menu: Some(self),
..MenuItem::default()
}
}
#[inline]
pub fn remove_item(&mut self, item: &MenuItemHandle) {
self.0.remove_item(item)
}
}
#[derive(Debug)]
pub struct MenuItem<'a> {
pub id: usize,
pub label: String,
pub enabled: bool,
pub key: Key,
pub modifier: usize,
#[doc(hidden)]
pub menu: Option<&'a mut Menu>,
}
impl Default for MenuItem<'_> {
fn default() -> Self {
MenuItem {
id: MENU_ID_SEPARATOR,
label: String::default(),
enabled: true,
key: Key::Unknown,
modifier: 0,
menu: None,
}
}
}
impl Clone for MenuItem<'_> {
fn clone(&self) -> Self {
MenuItem {
id: self.id,
label: self.label.clone(),
enabled: self.enabled,
key: self.key,
modifier: self.modifier,
menu: None,
}
}
}
impl MenuItem<'_> {
pub fn new(name: &str, id: usize) -> MenuItem {
MenuItem {
id,
label: name.to_owned(),
..MenuItem::default()
}
}
#[inline]
pub fn shortcut(self, key: Key, modifier: usize) -> Self {
MenuItem {
key,
modifier,
..self
}
}
#[inline]
pub fn separator(self) -> Self {
MenuItem {
id: MENU_ID_SEPARATOR,
..self
}
}
#[inline]
pub fn enabled(self, enabled: bool) -> Self {
MenuItem { enabled, ..self }
}
#[inline]
pub fn build(&mut self) -> MenuItemHandle {
let t = self.clone();
if let Some(ref mut menu) = self.menu {
menu.0.add_menu_item(&t)
} else {
MenuItemHandle(0)
}
}
}
impl Default for WindowOptions {
fn default() -> WindowOptions {
WindowOptions {
borderless: false,
transparency: false,
title: true,
resize: false,
scale: Scale::X1,
scale_mode: ScaleMode::Stretch,
topmost: false,
none: false,
}
}
}
pub(crate) fn check_buffer_size(
buffer: &[u32],
mut buf_width: usize,
buf_height: usize,
buf_stride: usize,
) -> Result<()> {
buf_width = buf_width.max(buf_stride);
let buf_size = std::mem::size_of_val(buffer);
let required_buf_size = buf_width * buf_height * std::mem::size_of::<u32>();
if buf_size < required_buf_size {
let err = format!(
"Update failed because input buffer is too small. Required size for {} ({} stride) x {} buffer is {}
bytes but the size of the input buffer has the size {} bytes",
buf_width, buf_stride, buf_height, required_buf_size, buf_size);
Err(Error::UpdateFailed(err))
} else {
Ok(())
}
}