2016-12-10 05:10:49 +00:00
|
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
use std::convert::From;
|
|
|
|
|
use std::fmt::{self, Display};
|
|
|
|
|
use std::ops::Deref;
|
2017-01-06 23:31:12 +00:00
|
|
|
|
use std::sync::Mutex;
|
2016-12-10 05:10:49 +00:00
|
|
|
|
|
|
|
|
|
use gl;
|
|
|
|
|
use glutin;
|
|
|
|
|
|
|
|
|
|
/// Resize handling for Mac and maybe other platforms
|
|
|
|
|
///
|
|
|
|
|
/// This delegates to a statically referenced closure for convenience. The
|
|
|
|
|
/// C-style callback doesn't receive a pointer or anything, so we are forced to
|
|
|
|
|
/// use static storage.
|
|
|
|
|
///
|
|
|
|
|
/// This will fail horribly if more than one window is created. Don't do that :)
|
|
|
|
|
fn window_resize_handler(width: u32, height: u32) {
|
2017-01-06 23:31:12 +00:00
|
|
|
|
RESIZE_CALLBACK.lock().unwrap().as_ref().map(|func| (*func)(width, height));
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 23:31:12 +00:00
|
|
|
|
lazy_static! {
|
|
|
|
|
/// The resize callback invoked by `window_resize_handler`
|
2017-02-23 02:34:52 +00:00
|
|
|
|
static ref RESIZE_CALLBACK: Mutex<Option<Box<Fn(u32, u32) + 'static + Send>>> = Mutex::new(None);
|
2017-01-06 23:31:12 +00:00
|
|
|
|
}
|
2016-12-10 05:10:49 +00:00
|
|
|
|
|
|
|
|
|
/// Window errors
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum Error {
|
|
|
|
|
/// Error creating the window
|
|
|
|
|
Creation(glutin::CreationError),
|
|
|
|
|
|
|
|
|
|
/// Error manipulating the rendering context
|
|
|
|
|
Context(glutin::ContextError),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Result of fallible operations concerning a Window.
|
|
|
|
|
type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
|
|
|
|
|
|
/// A window which can be used for displaying the terminal
|
|
|
|
|
///
|
|
|
|
|
/// Wraps the underlying windowing library to provide a stable API in Alacritty
|
|
|
|
|
pub struct Window {
|
|
|
|
|
glutin_window: glutin::Window,
|
2017-02-22 20:46:08 +00:00
|
|
|
|
cursor_visible: bool,
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Threadsafe APIs for the window
|
2016-12-11 06:44:13 +00:00
|
|
|
|
pub struct Proxy {
|
|
|
|
|
inner: glutin::WindowProxy,
|
|
|
|
|
}
|
2016-12-10 05:10:49 +00:00
|
|
|
|
|
|
|
|
|
/// Information about where the window is being displayed
|
|
|
|
|
///
|
|
|
|
|
/// Useful for subsystems like the font rasterized which depend on DPI and scale
|
|
|
|
|
/// factor.
|
|
|
|
|
pub struct DeviceProperties {
|
|
|
|
|
/// Scale factor for pixels <-> points.
|
|
|
|
|
///
|
|
|
|
|
/// This will be 1. on standard displays and may have a different value on
|
|
|
|
|
/// hidpi displays.
|
|
|
|
|
pub scale_factor: f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Size of the window
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
pub struct Size<T> {
|
|
|
|
|
pub width: T,
|
|
|
|
|
pub height: T,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Strongly typed Pixels unit
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
pub struct Pixels<T>(pub T);
|
|
|
|
|
|
|
|
|
|
/// Strongly typed Points unit
|
|
|
|
|
///
|
|
|
|
|
/// Points are like pixels but adjusted for DPI.
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
pub struct Points<T>(pub T);
|
|
|
|
|
|
|
|
|
|
pub trait ToPoints {
|
|
|
|
|
fn to_points(&self, scale: f32) -> Size<Points<u32>>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToPoints for Size<Points<u32>> {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn to_points(&self, _scale: f32) -> Size<Points<u32>> {
|
|
|
|
|
*self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToPoints for Size<Pixels<u32>> {
|
|
|
|
|
fn to_points(&self, scale: f32) -> Size<Points<u32>> {
|
|
|
|
|
let width_pts = (*self.width as f32 / scale) as u32;
|
|
|
|
|
let height_pts = (*self.height as f32 / scale) as u32;
|
|
|
|
|
|
|
|
|
|
Size {
|
|
|
|
|
width: Points(width_pts),
|
|
|
|
|
height: Points(height_pts)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Display> Display for Size<T> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "{} × {}", self.width, self.height)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! deref_newtype {
|
|
|
|
|
($($src:ty),+) => {
|
|
|
|
|
$(
|
|
|
|
|
impl<T> Deref for $src {
|
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)+
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deref_newtype! { Points<T>, Pixels<T> }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: Display> Display for Pixels<T> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "{}px", self.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-10 19:27:42 +00:00
|
|
|
|
impl<T: Display> Display for Points<T> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "{}pts", self.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-10 05:10:49 +00:00
|
|
|
|
impl ::std::error::Error for Error {
|
|
|
|
|
fn cause(&self) -> Option<&::std::error::Error> {
|
|
|
|
|
match *self {
|
|
|
|
|
Error::Creation(ref err) => Some(err),
|
|
|
|
|
Error::Context(ref err) => Some(err),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
|
match *self {
|
|
|
|
|
Error::Creation(ref _err) => "Error creating glutin Window",
|
|
|
|
|
Error::Context(ref _err) => "Error operating on render context",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Error {
|
|
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
|
|
|
match *self {
|
|
|
|
|
Error::Creation(ref err) => {
|
|
|
|
|
write!(f, "Error creating glutin::Window; {}", err)
|
|
|
|
|
},
|
|
|
|
|
Error::Context(ref err) => {
|
|
|
|
|
write!(f, "Error operating on render context; {}", err)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<glutin::CreationError> for Error {
|
|
|
|
|
fn from(val: glutin::CreationError) -> Error {
|
|
|
|
|
Error::Creation(val)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<glutin::ContextError> for Error {
|
|
|
|
|
fn from(val: glutin::ContextError) -> Error {
|
|
|
|
|
Error::Context(val)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Window {
|
|
|
|
|
/// Create a new window
|
|
|
|
|
///
|
|
|
|
|
/// This creates a window and fully initializes a window.
|
2017-01-05 21:04:23 +00:00
|
|
|
|
pub fn new(
|
|
|
|
|
title: &str
|
|
|
|
|
) -> Result<Window> {
|
2016-12-10 05:10:49 +00:00
|
|
|
|
/// Create a glutin::Window
|
|
|
|
|
let mut window = glutin::WindowBuilder::new()
|
|
|
|
|
.with_vsync()
|
2017-01-05 21:04:23 +00:00
|
|
|
|
.with_title(title)
|
2016-12-10 05:10:49 +00:00
|
|
|
|
.build()?;
|
|
|
|
|
|
|
|
|
|
/// Set the glutin window resize callback for *this* window. The
|
|
|
|
|
/// function pointer must be a C-style callback. This sets such a
|
|
|
|
|
/// callback which simply delegates to a statically referenced Rust
|
|
|
|
|
/// closure.
|
|
|
|
|
window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));
|
|
|
|
|
|
|
|
|
|
/// Set OpenGL symbol loader
|
|
|
|
|
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
|
|
|
|
|
|
|
|
|
|
/// Make the window's context current so OpenGL operations can run
|
|
|
|
|
unsafe {
|
|
|
|
|
window.make_current()?;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 17:26:46 +00:00
|
|
|
|
let window = Window {
|
2016-12-10 05:10:49 +00:00
|
|
|
|
glutin_window: window,
|
2017-02-22 20:46:08 +00:00
|
|
|
|
cursor_visible: true,
|
2017-04-26 17:26:46 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.run_os_extensions();
|
|
|
|
|
|
|
|
|
|
Ok(window)
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get some properties about the device
|
|
|
|
|
///
|
|
|
|
|
/// Some window properties are provided since subsystems like font
|
|
|
|
|
/// rasterization depend on DPI and scale factor.
|
|
|
|
|
pub fn device_properties(&self) -> DeviceProperties {
|
|
|
|
|
DeviceProperties {
|
|
|
|
|
scale_factor: self.glutin_window.hidpi_factor(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the window resize callback
|
|
|
|
|
///
|
|
|
|
|
/// Pass a `move` closure which will be called with the new width and height
|
|
|
|
|
/// when the window is resized. According to the glutin docs, this can be
|
|
|
|
|
/// used to draw during resizing.
|
|
|
|
|
///
|
|
|
|
|
/// This method takes self mutably to ensure there's no race condition
|
|
|
|
|
/// setting the callback.
|
2017-01-06 23:31:12 +00:00
|
|
|
|
pub fn set_resize_callback<F: Fn(u32, u32) + 'static + Send>(&mut self, func: F) {
|
|
|
|
|
let mut guard = RESIZE_CALLBACK.lock().unwrap();
|
|
|
|
|
*guard = Some(Box::new(func));
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn inner_size_pixels(&self) -> Option<Size<Pixels<u32>>> {
|
|
|
|
|
self.glutin_window
|
|
|
|
|
.get_inner_size_pixels()
|
|
|
|
|
.map(|(w, h)| Size { width: Pixels(w), height: Pixels(h) })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn hidpi_factor(&self) -> f32 {
|
|
|
|
|
self.glutin_window.hidpi_factor()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn create_window_proxy(&self) -> Proxy {
|
2016-12-11 06:44:13 +00:00
|
|
|
|
Proxy {
|
|
|
|
|
inner: self.glutin_window.create_window_proxy(),
|
|
|
|
|
}
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn swap_buffers(&self) -> Result<()> {
|
|
|
|
|
self.glutin_window
|
|
|
|
|
.swap_buffers()
|
|
|
|
|
.map_err(From::from)
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-30 02:38:22 +00:00
|
|
|
|
/// Poll for any available events
|
2016-12-10 05:10:49 +00:00
|
|
|
|
#[inline]
|
2016-12-30 02:38:22 +00:00
|
|
|
|
pub fn poll_events(&self) -> glutin::PollEventsIterator {
|
|
|
|
|
self.glutin_window.poll_events()
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Block waiting for events
|
|
|
|
|
///
|
|
|
|
|
/// FIXME should return our own type
|
|
|
|
|
#[inline]
|
2016-12-30 02:38:22 +00:00
|
|
|
|
pub fn wait_events(&self) -> glutin::WaitEventsIterator {
|
|
|
|
|
self.glutin_window.wait_events()
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
2017-01-11 05:21:19 +00:00
|
|
|
|
|
|
|
|
|
/// Set the window title
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_title(&self, title: &str) {
|
|
|
|
|
self.glutin_window.set_title(title);
|
|
|
|
|
}
|
2017-02-22 19:52:37 +00:00
|
|
|
|
|
|
|
|
|
/// Set cursor visible
|
2017-02-22 20:46:08 +00:00
|
|
|
|
pub fn set_cursor_visible(&mut self, visible: bool) {
|
|
|
|
|
if visible != self.cursor_visible {
|
|
|
|
|
self.cursor_visible = visible;
|
2017-02-23 02:34:52 +00:00
|
|
|
|
self.glutin_window.set_cursor_state(if visible { glutin::CursorState::Normal }
|
|
|
|
|
else { glutin::CursorState::Hide }).unwrap();
|
2017-02-22 20:46:08 +00:00
|
|
|
|
}
|
2017-02-22 19:52:37 +00:00
|
|
|
|
}
|
2017-05-28 03:08:28 +00:00
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
|
pub fn get_window_id(&self) -> Option<usize> {
|
|
|
|
|
use glutin::os::unix::WindowExt;
|
|
|
|
|
|
|
|
|
|
match self.glutin_window.get_xlib_window() {
|
|
|
|
|
Some(xlib_window) => Some(xlib_window as usize),
|
|
|
|
|
None => None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
pub fn get_window_id(&self) -> Option<usize> {
|
|
|
|
|
None
|
|
|
|
|
}
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 17:26:46 +00:00
|
|
|
|
pub trait OsExtensions {
|
|
|
|
|
fn run_os_extensions(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd")))]
|
|
|
|
|
impl OsExtensions for Window { }
|
|
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd"))]
|
|
|
|
|
impl OsExtensions for Window {
|
|
|
|
|
fn run_os_extensions(&self) {
|
|
|
|
|
use ::glutin::os::unix::WindowExt;
|
|
|
|
|
use ::x11_dl::xlib::{self, XA_CARDINAL, PropModeReplace};
|
|
|
|
|
use ::std::ffi::{CStr};
|
|
|
|
|
use ::std::ptr;
|
|
|
|
|
use ::libc::getpid;
|
|
|
|
|
|
|
|
|
|
let xlib_display = self.glutin_window.get_xlib_display();
|
|
|
|
|
let xlib_window = self.glutin_window.get_xlib_window();
|
|
|
|
|
|
|
|
|
|
if let (Some(xlib_window), Some(xlib_display)) = (xlib_window, xlib_display) {
|
|
|
|
|
let xlib = xlib::Xlib::open().expect("get xlib");
|
|
|
|
|
|
|
|
|
|
// Set _NET_WM_PID to process pid
|
|
|
|
|
unsafe {
|
|
|
|
|
let _net_wm_pid = CStr::from_ptr(b"_NET_WM_PID\0".as_ptr() as *const _);
|
|
|
|
|
let atom = (xlib.XInternAtom)(xlib_display as *mut _, _net_wm_pid.as_ptr(), 0);
|
|
|
|
|
let pid = getpid();
|
|
|
|
|
|
|
|
|
|
(xlib.XChangeProperty)(xlib_display as _, xlib_window as _, atom,
|
|
|
|
|
XA_CARDINAL, 32, PropModeReplace, &pid as *const i32 as *const u8, 1);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// Although this call doesn't actually pass any data, it does cause
|
|
|
|
|
// WM_CLIENT_MACHINE to be set. WM_CLIENT_MACHINE MUST be set if _NET_WM_PID is set
|
|
|
|
|
// (which we do above).
|
|
|
|
|
unsafe {
|
|
|
|
|
(xlib.XSetWMProperties)(xlib_display as _, xlib_window as _, ptr::null_mut(),
|
|
|
|
|
ptr::null_mut(), ptr::null_mut(), 0, ptr::null_mut(), ptr::null_mut(),
|
|
|
|
|
ptr::null_mut());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-10 05:10:49 +00:00
|
|
|
|
impl Proxy {
|
|
|
|
|
/// Wakes up the event loop of the window
|
|
|
|
|
///
|
|
|
|
|
/// This is useful for triggering a draw when the renderer would otherwise
|
|
|
|
|
/// be waiting on user input.
|
|
|
|
|
pub fn wakeup_event_loop(&self) {
|
2016-12-30 02:38:22 +00:00
|
|
|
|
self.inner.wakeup_event_loop();
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait SetInnerSize<T> {
|
2017-05-06 15:45:23 +00:00
|
|
|
|
fn set_inner_size<S: ToPoints>(&mut self, size: &S);
|
2016-12-10 05:10:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SetInnerSize<Pixels<u32>> for Window {
|
2017-05-06 15:45:23 +00:00
|
|
|
|
fn set_inner_size<T: ToPoints>(&mut self, size: &T) {
|
2016-12-10 05:10:49 +00:00
|
|
|
|
let size = size.to_points(self.hidpi_factor());
|
|
|
|
|
self.glutin_window.set_inner_size(*size.width as _, *size.height as _);
|
|
|
|
|
}
|
|
|
|
|
}
|