Window icons (#497)

This commit is contained in:
Francesca Frangipane 2018-05-07 17:36:21 -04:00 committed by GitHub
parent 1e97103094
commit 102dd07456
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 831 additions and 137 deletions

View file

@ -201,8 +201,8 @@ pub struct PlatformSpecificWindowBuilderAttributes;
pub struct PlatformSpecificHeadlessBuilderAttributes;
impl Window {
pub fn new(_: &EventsLoop, win_attribs: &WindowAttributes,
_: &PlatformSpecificWindowBuilderAttributes)
pub fn new(_: &EventsLoop, win_attribs: WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
// not implemented
@ -323,6 +323,11 @@ impl Window {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId{inner: MonitorId}

View file

@ -347,8 +347,8 @@ fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> {
}
impl Window {
pub fn new(events_loop: &EventsLoop, attribs: &::WindowAttributes,
_pl_attribs: &PlatformSpecificWindowBuilderAttributes)
pub fn new(events_loop: &EventsLoop, attribs: ::WindowAttributes,
_pl_attribs: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, ::CreationError>
{
if events_loop.window.lock().unwrap().is_some() {
@ -543,6 +543,11 @@ impl Window {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> ::MonitorId {
::MonitorId{inner: MonitorId}

View file

@ -263,7 +263,7 @@ pub struct DeviceId;
pub struct PlatformSpecificWindowBuilderAttributes;
impl Window {
pub fn new(ev: &EventsLoop, _: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
Ok(Window {
@ -370,6 +370,11 @@ impl Window {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId{inner: MonitorId}

View file

@ -6,12 +6,20 @@ use std::ffi::CStr;
use std::os::raw::*;
use std::sync::Arc;
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
use libc;
use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
use {
CreationError,
CursorState,
EventsLoopClosed,
Icon,
MouseCursor,
ControlFlow,
WindowAttributes,
};
use window::MonitorId as RootMonitorId;
use self::x11::XConnection;
use self::x11::XError;
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
pub use self::x11::XNotSupported;
@ -109,18 +117,17 @@ impl MonitorId {
impl Window {
#[inline]
pub fn new(events_loop: &EventsLoop,
window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Self, CreationError>
{
pub fn new(
events_loop: &EventsLoop,
attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, CreationError> {
match *events_loop {
EventsLoop::Wayland(ref evlp) => {
wayland::Window::new(evlp, window).map(Window::Wayland)
EventsLoop::Wayland(ref events_loop) => {
wayland::Window::new(events_loop, attribs).map(Window::Wayland)
},
EventsLoop::X(ref el) => {
x11::Window::new(el, window, pl_attribs).map(Window::X)
EventsLoop::X(ref events_loop) => {
x11::Window::new(events_loop, attribs, pl_attribs).map(Window::X)
},
}
}
@ -293,6 +300,14 @@ impl Window {
}
}
#[inline]
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
match self {
&Window::X(ref w) => w.set_window_icon(window_icon),
&Window::Wayland(_) => (),
}
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
match self {

View file

@ -23,9 +23,8 @@ pub struct Window {
}
impl Window {
pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result<Window, CreationError> {
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
// Create the window
let size = Arc::new(Mutex::new((width, height)));

View file

@ -1,28 +1,6 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Window2, XWindow};
pub use self::xdisplay::{XConnection, XNotSupported, XError};
pub mod ffi;
use platform::PlatformSpecificWindowBuilderAttributes;
use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent,
KeyboardInput, ControlFlow};
use events::ModifiersState;
use std::{mem, ptr, slice};
use std::sync::{Arc, Weak};
use std::sync::atomic::{self, AtomicBool};
use std::sync::mpsc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::os::raw::*;
use libc::{self, setlocale, LC_CTYPE};
use parking_lot::Mutex;
mod events;
mod monitor;
mod window;
@ -31,6 +9,33 @@ mod dnd;
mod ime;
mod util;
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Window2, XWindow};
pub use self::xdisplay::{XConnection, XNotSupported, XError};
use std::{mem, ptr, slice};
use std::sync::{Arc, mpsc, Weak};
use std::sync::atomic::{self, AtomicBool};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::os::raw::*;
use libc::{self, setlocale, LC_CTYPE};
use parking_lot::Mutex;
use {
ControlFlow,
CreationError,
DeviceEvent,
Event,
EventsLoopClosed,
KeyboardInput,
WindowAttributes,
WindowEvent,
};
use events::ModifiersState;
use platform::PlatformSpecificWindowBuilderAttributes;
use self::dnd::{Dnd, DndState};
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
@ -1150,10 +1155,11 @@ impl ::std::ops::Deref for Window {
impl Window {
pub fn new(
x_events_loop: &EventsLoop,
window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes
attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes
) -> Result<Self, CreationError> {
let win = Arc::new(Window2::new(&x_events_loop, window, pl_attribs)?);
let multitouch = attribs.multitouch;
let win = Arc::new(Window2::new(&x_events_loop, attribs, pl_attribs)?);
x_events_loop.shared_state
.borrow_mut()
@ -1166,7 +1172,7 @@ impl Window {
x_events_loop.windows.lock().insert(win.id(), WindowData {
config: Default::default(),
multitouch: window.multitouch,
multitouch,
cursor_pos: None,
});

View file

@ -0,0 +1,34 @@
use {Icon, Pixel, PIXEL_SIZE};
use super::*;
impl Pixel {
pub fn to_packed_argb(&self) -> Cardinal {
let mut cardinal = 0;
assert!(CARDINAL_SIZE >= PIXEL_SIZE);
let as_bytes = &mut cardinal as *mut _ as *mut u8;
unsafe {
*as_bytes.offset(0) = self.b;
*as_bytes.offset(1) = self.g;
*as_bytes.offset(2) = self.r;
*as_bytes.offset(3) = self.a;
}
cardinal
}
}
impl Icon {
pub fn to_cardinals(&self) -> Vec<Cardinal> {
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
let pixel_count = self.rgba.len() / PIXEL_SIZE;
assert_eq!(pixel_count, (self.width * self.height) as usize);
let mut data = Vec::with_capacity(pixel_count);
data.push(self.width as Cardinal);
data.push(self.height as Cardinal);
let pixels = self.rgba.as_ptr() as *const Pixel;
for pixel_index in 0..pixel_count {
let pixel = unsafe { &*pixels.offset(pixel_index as isize) };
data.push(pixel.to_packed_argb());
}
data
}
}

View file

@ -4,6 +4,7 @@
mod atom;
mod geometry;
mod hint;
mod icon;
mod input;
mod window_property;
mod wm;
@ -11,6 +12,7 @@ mod wm;
pub use self::atom::*;
pub use self::geometry::*;
pub use self::hint::*;
pub use self::icon::*;
pub use self::input::*;
pub use self::window_property::*;
pub use self::wm::*;

View file

@ -3,6 +3,9 @@ use std::fmt::Debug;
use super::*;
pub type Cardinal = c_long;
pub const CARDINAL_SIZE: usize = mem::size_of::<c_long>();
#[derive(Debug, Clone)]
pub enum GetPropertyError {
XError(XError),

View file

@ -1,24 +1,19 @@
use MouseCursor;
use CreationError;
use CreationError::OsError;
use libc;
use std::{cmp, mem};
use std::borrow::Borrow;
use std::{mem, cmp};
use std::sync::Arc;
use std::os::raw::*;
use std::ffi::CString;
use std::os::raw::*;
use std::sync::Arc;
use libc;
use parking_lot::Mutex;
use CursorState;
use WindowAttributes;
use platform::PlatformSpecificWindowBuilderAttributes;
use {CursorState, Icon, MouseCursor, WindowAttributes};
use CreationError::{self, OsError};
use platform::MonitorId as PlatformMonitorId;
use platform::PlatformSpecificWindowBuilderAttributes;
use platform::x11::MonitorId as X11MonitorId;
use window::MonitorId as RootMonitorId;
use platform::x11::monitor::get_available_monitors;
use window::MonitorId as RootMonitorId;
use super::{ffi, util, XConnection, XError, WindowId, EventsLoop};
@ -60,8 +55,8 @@ pub struct Window2 {
impl Window2 {
pub fn new(
ctx: &EventsLoop,
window_attrs: &WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
window_attrs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window2, CreationError> {
let xconn = &ctx.display;
@ -241,6 +236,11 @@ impl Window2 {
}//.queue();
}
// Set window icons
if let Some(icon) = window_attrs.window_icon {
window.set_icon_inner(icon).queue();
}
// Opt into handling window close
unsafe {
(xconn.xlib.XSetWMProtocols)(
@ -522,6 +522,53 @@ impl Window2 {
self.invalidate_cached_frame_extents();
}
fn set_icon_inner(&self, icon: Icon) -> util::Flusher {
let xconn = &self.x.display;
let icon_atom = unsafe { util::get_atom(xconn, b"_NET_WM_ICON\0") }
.expect("Failed to call XInternAtom (_NET_WM_ICON)");
let data = icon.to_cardinals();
unsafe {
util::change_property(
xconn,
self.x.window,
icon_atom,
ffi::XA_CARDINAL,
util::Format::Long,
util::PropMode::Replace,
data.as_slice(),
)
}
}
fn unset_icon_inner(&self) -> util::Flusher {
let xconn = &self.x.display;
let icon_atom = unsafe { util::get_atom(xconn, b"_NET_WM_ICON\0") }
.expect("Failed to call XInternAtom (_NET_WM_ICON)");
let empty_data: [util::Cardinal; 0] = [];
unsafe {
util::change_property(
xconn,
self.x.window,
icon_atom,
ffi::XA_CARDINAL,
util::Format::Long,
util::PropMode::Replace,
&empty_data,
)
}
}
pub fn set_window_icon(&self, icon: Option<Icon>) {
match icon {
Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(),
}.flush().expect("Failed to set icons");
}
pub fn show(&self) {
unsafe {
(self.x.display.xlib.XMapRaised)(self.x.display.display, self.x.window);

View file

@ -25,8 +25,8 @@ impl ::std::ops::Deref for Window {
impl Window {
pub fn new(events_loop: &EventsLoop,
attributes: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
attributes: ::WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
{
let weak_shared = Arc::downgrade(&events_loop.shared);
let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs)));

View file

@ -559,8 +559,8 @@ impl WindowExt for Window2 {
impl Window2 {
pub fn new(
shared: Weak<Shared>,
win_attribs: &WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
win_attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window2, CreationError> {
unsafe {
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
@ -579,7 +579,7 @@ impl Window2 {
},
};
let window = match Window2::create_window(win_attribs, pl_attribs)
let window = match Window2::create_window(&win_attribs, &pl_attribs)
{
Some(window) => window,
None => {
@ -700,8 +700,8 @@ impl Window2 {
fn create_window(
attrs: &WindowAttributes,
pl_attrs: &PlatformSpecificWindowBuilderAttributes)
-> Option<IdRef> {
pl_attrs: &PlatformSpecificWindowBuilderAttributes
) -> Option<IdRef> {
unsafe {
let autoreleasepool = NSAutoreleasePool::new(nil);
let screen = match attrs.fullscreen {
@ -1072,6 +1072,16 @@ impl Window2 {
}
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// macOS doesn't have window icons. Though, there is `setRepresentedFilename`, but that's
// semantically distinct and should only be used when the window is in some representing a
// specific file/directory. For instance, Terminal.app uses this for the CWD. Anyway, that
// should eventually be implemented as `WindowBuilderExt::with_represented_file` or
// something, and doesn't have anything to do with this.
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
unsafe {

View file

@ -279,8 +279,9 @@ impl EventsLoopProxy {
/// Executes a function in the background thread.
///
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
/// to the unstable FnBox.
/// Note that we use FnMut instead of FnOnce because boxing FnOnce won't work on stable Rust
/// until 2030 when the design of Box is finally complete.
/// https://github.com/rust-lang/rust/issues/28796
///
/// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
/// removed automatically if the callback receives a `WM_CLOSE` message for the window.
@ -302,10 +303,7 @@ impl EventsLoopProxy {
);
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
// as the events loop is still alive) or if the queue is full.
assert!(
res != 0,
"PostThreadMessage failed ; is the messages queue full?"
);
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
}
}
}

View file

@ -0,0 +1,112 @@
use std::{self, mem, ptr};
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use winapi::ctypes::{c_int, wchar_t};
use winapi::shared::minwindef::{BYTE, LPARAM, WPARAM};
use winapi::shared::windef::{HICON, HWND};
use winapi::um::winuser;
use {Pixel, PIXEL_SIZE, Icon};
use platform::platform::util;
impl Pixel {
fn to_bgra(&mut self) {
mem::swap(&mut self.r, &mut self.b);
}
}
#[derive(Debug)]
pub enum IconType {
Small = winuser::ICON_SMALL as isize,
Big = winuser::ICON_BIG as isize,
}
#[derive(Debug)]
pub struct WinIcon {
pub handle: HICON,
}
impl WinIcon {
#[allow(dead_code)]
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, util::WinError> {
let wide_path: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
let handle = unsafe {
winuser::LoadImageW(
ptr::null_mut(),
wide_path.as_ptr() as *const wchar_t,
winuser::IMAGE_ICON,
0, // 0 indicates that we want to use the actual width
0, // and height
winuser::LR_LOADFROMFILE,
) as HICON
};
if !handle.is_null() {
Ok(WinIcon { handle })
} else {
Err(util::WinError::from_last_error())
}
}
pub fn from_icon(icon: Icon) -> Result<Self, util::WinError> {
Self::from_rgba(icon.rgba, icon.width, icon.height)
}
pub fn from_rgba(mut rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, util::WinError> {
assert_eq!(rgba.len() % PIXEL_SIZE, 0);
let pixel_count = rgba.len() / PIXEL_SIZE;
assert_eq!(pixel_count, (width * height) as usize);
let mut and_mask = Vec::with_capacity(pixel_count);
let pixels = rgba.as_mut_ptr() as *mut Pixel; // how not to write idiomatic Rust
for pixel_index in 0..pixel_count {
let pixel = unsafe { &mut *pixels.offset(pixel_index as isize) };
and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel
pixel.to_bgra();
}
assert_eq!(and_mask.len(), pixel_count);
let handle = unsafe {
winuser::CreateIcon(
ptr::null_mut(),
width as c_int,
height as c_int,
1,
(PIXEL_SIZE * 8) as BYTE,
and_mask.as_ptr() as *const BYTE,
rgba.as_ptr() as *const BYTE,
) as HICON
};
if !handle.is_null() {
Ok(WinIcon { handle })
} else {
Err(util::WinError::from_last_error())
}
}
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
unsafe {
winuser::SendMessageW(
hwnd,
winuser::WM_SETICON,
icon_type as WPARAM,
self.handle as LPARAM,
);
}
}
}
impl Drop for WinIcon {
fn drop(&mut self) {
unsafe { winuser::DestroyIcon(self.handle) };
}
}
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
unsafe {
winuser::SendMessageW(
hwnd,
winuser::WM_SETICON,
icon_type as WPARAM,
0 as LPARAM,
);
}
}

View file

@ -10,6 +10,7 @@ pub use self::window::Window;
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub parent: Option<HWND>,
pub taskbar_icon: Option<::Icon>,
}
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
@ -45,6 +46,7 @@ unsafe impl Sync for WindowId {}
mod event;
mod events_loop;
mod icon;
mod monitor;
mod raw_input;
mod util;

View file

@ -1,6 +1,23 @@
use std::{self, mem, ptr};
use std::ops::BitAnd;
use winapi::ctypes::wchar_t;
use winapi::shared::minwindef::DWORD;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::{
FormatMessageW,
FORMAT_MESSAGE_ALLOCATE_BUFFER,
FORMAT_MESSAGE_FROM_SYSTEM,
FORMAT_MESSAGE_IGNORE_INSERTS,
lstrlenW,
LocalFree,
};
use winapi::um::winnt::{
LPCWSTR,
MAKELANGID,
LANG_NEUTRAL,
SUBLANG_DEFAULT,
};
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
@ -14,3 +31,42 @@ pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
.trim_right_matches(0 as char)
.to_string()
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WinError(Option<String>);
impl WinError {
pub fn from_last_error() -> Self {
WinError(unsafe { get_last_error() })
}
}
pub unsafe fn get_last_error() -> Option<String> {
let err = GetLastError();
if err != 0 {
let buf_addr: LPCWSTR = {
let mut buf_addr: LPCWSTR = mem::uninitialized();
FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as DWORD,
// This is a pointer to a pointer
&mut buf_addr as *mut LPCWSTR as *mut _,
0,
ptr::null_mut(),
);
buf_addr
};
if !buf_addr.is_null() {
let buf_len = lstrlenW(buf_addr) as usize;
let buf_slice = std::slice::from_raw_parts(buf_addr, buf_len);
let string = wchar_to_string(buf_slice);
LocalFree(buf_addr as *mut _);
return Some(string);
}
}
None
}

View file

@ -1,35 +1,31 @@
#![cfg(target_os = "windows")]
use std::cell::Cell;
use std::ffi::OsStr;
use std::io;
use std::mem;
use std::{io, mem, ptr};
use std::os::raw;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::cell::Cell;
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
use platform::platform::EventsLoop;
use platform::platform::PlatformSpecificWindowBuilderAttributes;
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
use platform::platform::WindowId;
use winapi::shared::minwindef::{BOOL, DWORD, UINT};
use winapi::shared::windef::{HDC, HWND, POINT, RECT};
use winapi::um::{combaseapi, dwmapi, libloaderapi, processthreadsapi, winuser};
use winapi::um::objbase::{COINIT_MULTITHREADED};
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
use winapi::um::winnt::{HRESULT, LONG, LPCWSTR};
use CreationError;
use CursorState;
use Icon;
use MonitorId as RootMonitorId;
use MouseCursor;
use WindowAttributes;
use MonitorId as RootMonitorId;
use winapi::shared::minwindef::{UINT, DWORD, BOOL};
use winapi::shared::windef::{HWND, HDC, RECT, POINT};
use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi};
use winapi::um::winnt::{LPCWSTR, LONG, HRESULT};
use winapi::um::combaseapi;
use winapi::um::objbase::{COINIT_MULTITHREADED};
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
use platform::platform::{EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId};
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
use platform::platform::icon::{self, IconType, WinIcon};
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
/// The Win32 implementation of the main `Window` object.
pub struct Window {
@ -39,6 +35,9 @@ pub struct Window {
/// The current window state.
window_state: Arc<Mutex<events_loop::WindowState>>,
window_icon: Cell<Option<WinIcon>>,
taskbar_icon: Cell<Option<WinIcon>>,
// The events loop proxy.
events_loop_proxy: events_loop::EventsLoopProxy,
}
@ -72,19 +71,19 @@ unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> B
}
impl Window {
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
{
let mut w_attr = Some(w_attr.clone());
let mut pl_attr = Some(pl_attr.clone());
pub fn new(
events_loop: &EventsLoop,
w_attr: WindowAttributes,
pl_attr: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, CreationError> {
let (tx, rx) = channel();
let proxy = events_loop.create_proxy();
events_loop.execute_in_thread(move |inserter| {
// We dispatch an `init` function because of code style.
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter, proxy.clone()) };
// First person to remove the need for cloning here gets a cookie!
let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) };
let _ = tx.send(win);
});
@ -92,10 +91,11 @@ impl Window {
}
pub fn set_title(&self, text: &str) {
let text = OsStr::new(text)
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
unsafe {
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
.collect::<Vec<_>>();
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
}
}
@ -610,6 +610,32 @@ impl Window {
inner: EventsLoop::get_current_monitor(self.window.0),
}
}
#[inline]
pub fn set_window_icon(&self, mut window_icon: Option<Icon>) {
let window_icon = window_icon
.take()
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_SMALL`"));
if let Some(ref window_icon) = window_icon {
window_icon.set_for_window(self.window.0, IconType::Small);
} else {
icon::unset_for_window(self.window.0, IconType::Small);
}
self.window_icon.replace(window_icon);
}
#[inline]
pub fn set_taskbar_icon(&self, mut taskbar_icon: Option<Icon>) {
let taskbar_icon = taskbar_icon
.take()
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_BIG`"));
if let Some(ref taskbar_icon) = taskbar_icon {
taskbar_icon.set_for_window(self.window.0, IconType::Big);
} else {
icon::unset_for_window(self.window.0, IconType::Big);
}
self.taskbar_icon.replace(taskbar_icon);
}
}
impl Drop for Window {
@ -642,13 +668,44 @@ pub unsafe fn adjust_size(
(rect.right - rect.left, rect.bottom - rect.top)
}
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
inserter: events_loop::Inserter, events_loop_proxy: events_loop::EventsLoopProxy) -> Result<Window, CreationError> {
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
unsafe fn init(
mut window: WindowAttributes,
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
inserter: events_loop::Inserter,
events_loop_proxy: events_loop::EventsLoopProxy,
) -> Result<Window, CreationError> {
let title = OsStr::new(&window.title)
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
let window_icon = {
let icon = window.window_icon
.take()
.map(WinIcon::from_icon);
if icon.is_some() {
Some(icon.unwrap().map_err(|err| {
CreationError::OsError(format!("Failed to create `ICON_SMALL`: {:?}", err))
})?)
} else {
None
}
};
let taskbar_icon = {
let icon = pl_attribs.taskbar_icon
.take()
.map(WinIcon::from_icon);
if icon.is_some() {
Some(icon.unwrap().map_err(|err| {
CreationError::OsError(format!("Failed to create `ICON_BIG`: {:?}", err))
})?)
} else {
None
}
};
// registering the window class
let class_name = register_window_class();
let class_name = register_window_class(&window_icon, &taskbar_icon);
// building a RECT object with coordinates
let mut rect = RECT {
@ -738,17 +795,21 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
}
}
let (transparent, maximized, fullscreen) = (
window.transparent.clone(), window.maximized.clone(), window.fullscreen.clone()
);
// Creating a mutex to track the current window state
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
cursor: winuser::IDC_ARROW, // use arrow by default
cursor_state: CursorState::Normal,
attributes: window.clone(),
attributes: window,
mouse_in_window: false,
saved_window_info: None,
}));
// making the window transparent
if window.transparent {
if transparent {
let bb = dwmapi::DWM_BLURBEHIND {
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
fEnable: 1,
@ -762,12 +823,14 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
let win = Window {
window: real_window,
window_state: window_state,
events_loop_proxy
window_icon: Cell::new(window_icon),
taskbar_icon: Cell::new(taskbar_icon),
events_loop_proxy,
};
win.set_maximized(window.maximized);
if let Some(_) = window.fullscreen {
win.set_fullscreen(window.fullscreen);
win.set_maximized(maximized);
if let Some(_) = fullscreen {
win.set_fullscreen(fullscreen);
force_window_active(win.window.0);
}
@ -776,9 +839,23 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
Ok(win)
}
unsafe fn register_window_class() -> Vec<u16> {
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
.collect::<Vec<_>>();
unsafe fn register_window_class(
window_icon: &Option<WinIcon>,
taskbar_icon: &Option<WinIcon>,
) -> Vec<u16> {
let class_name: Vec<_> = OsStr::new("Window Class")
.encode_wide()
.chain(Some(0).into_iter())
.collect();
let h_icon = taskbar_icon
.as_ref()
.map(|icon| icon.handle)
.unwrap_or(ptr::null_mut());
let h_icon_small = window_icon
.as_ref()
.map(|icon| icon.handle)
.unwrap_or(ptr::null_mut());
let class = winuser::WNDCLASSEXW {
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
@ -787,12 +864,12 @@ unsafe fn register_window_class() -> Vec<u16> {
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
hIcon: ptr::null_mut(),
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
hIcon: h_icon,
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: ptr::null_mut(),
hIconSm: h_icon_small,
};
// We ignore errors because registering the same window class twice would trigger