parent
f51f7c0ca8
commit
cebd15bfd1
8 changed files with 235 additions and 13 deletions
|
|
@ -44,6 +44,9 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
|||
pub screen_id: Option<i32>,
|
||||
pub resize_increments: Option<(u32, u32)>,
|
||||
pub base_size: Option<(u32, u32)>,
|
||||
pub class: Option<(String, String)>,
|
||||
pub override_redirect: bool,
|
||||
pub x11_window_type: x11::util::WindowType,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ mod window;
|
|||
mod xdisplay;
|
||||
mod dnd;
|
||||
mod ime;
|
||||
mod util;
|
||||
pub mod util;
|
||||
|
||||
pub use self::monitor::{
|
||||
MonitorId,
|
||||
|
|
|
|||
|
|
@ -18,3 +18,51 @@ impl From<bool> for StateOperation {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// X window type. Maps directly to
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html).
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum WindowType {
|
||||
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
|
||||
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
|
||||
/// root window clicks.
|
||||
Desktop,
|
||||
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
|
||||
Dock,
|
||||
/// Toolbar windows. "Torn off" from the main application.
|
||||
Toolbar,
|
||||
/// Pinnable menu windows. "Torn off" from the main application.
|
||||
Menu,
|
||||
/// A small persistent utility window, such as a palette or toolbox.
|
||||
Utility,
|
||||
/// The window is a splash screen displayed as an application is starting up.
|
||||
Splash,
|
||||
/// This is a dialog window.
|
||||
Dialog,
|
||||
/// This is a normal, top-level window.
|
||||
Normal,
|
||||
}
|
||||
|
||||
impl Default for WindowType {
|
||||
fn default() -> Self {
|
||||
WindowType::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowType {
|
||||
pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> ffi::Atom {
|
||||
use self::WindowType::*;
|
||||
let atom_name: &[u8] = match self {
|
||||
Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0",
|
||||
Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0",
|
||||
Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0",
|
||||
Menu => b"_NET_WM_WINDOW_TYPE_MENU\0",
|
||||
Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
|
||||
Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
|
||||
Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
||||
Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
||||
};
|
||||
unsafe { get_atom(xconn, atom_name) }
|
||||
.expect("Failed to get atom for `WindowType`")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::{cmp, mem};
|
||||
use std::{cmp, env, mem};
|
||||
use std::borrow::Borrow;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::*;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
|
|
@ -115,6 +116,10 @@ impl Window2 {
|
|||
window_attributes |= ffi::CWBackPixel;
|
||||
}
|
||||
|
||||
if pl_attribs.override_redirect {
|
||||
window_attributes |= ffi::CWOverrideRedirect;
|
||||
}
|
||||
|
||||
// finally creating the window
|
||||
let window = unsafe {
|
||||
(xconn.xlib.XCreateWindow)(
|
||||
|
|
@ -127,12 +132,12 @@ impl Window2 {
|
|||
0,
|
||||
match pl_attribs.visual_infos {
|
||||
Some(vi) => vi.depth,
|
||||
None => ffi::CopyFromParent
|
||||
None => ffi::CopyFromParent,
|
||||
},
|
||||
ffi::InputOutput as c_uint,
|
||||
match pl_attribs.visual_infos {
|
||||
Some(vi) => vi.visual,
|
||||
None => ffi::CopyFromParent as *mut _
|
||||
None => ffi::CopyFromParent as *mut ffi::Visual,
|
||||
},
|
||||
window_attributes,
|
||||
&mut set_win_attr,
|
||||
|
|
@ -178,17 +183,42 @@ impl Window2 {
|
|||
)
|
||||
}.queue();
|
||||
|
||||
// Set ICCCM WM_CLASS property based on initial window title
|
||||
// Must be done *before* mapping the window by ICCCM 4.1.2.5
|
||||
// WM_CLASS must be set *before* mapping the window, as per ICCCM!
|
||||
{
|
||||
let name = CString::new(window_attrs.title.as_str())
|
||||
.expect("Window title contained null byte");
|
||||
let mut class_hints = {
|
||||
let class_hints = unsafe { (xconn.xlib.XAllocClassHint)() };
|
||||
util::XSmartPointer::new(xconn, class_hints)
|
||||
}.expect("XAllocClassHint returned null; out of memory");
|
||||
(*class_hints).res_name = name.as_ptr() as *mut c_char;
|
||||
(*class_hints).res_class = name.as_ptr() as *mut c_char;
|
||||
}.expect("`XAllocClassHint` returned null; out of memory");
|
||||
|
||||
let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
|
||||
let instance = CString::new(instance.as_str())
|
||||
.expect("`WM_CLASS` instance contained null byte");
|
||||
let class = CString::new(class.as_str())
|
||||
.expect("`WM_CLASS` class contained null byte");
|
||||
(instance, class)
|
||||
} else {
|
||||
let class = env::args()
|
||||
.next()
|
||||
.as_ref()
|
||||
// Default to the name of the binary (via argv[0])
|
||||
.and_then(|path| Path::new(path).file_name())
|
||||
.and_then(|bin_name| bin_name.to_str())
|
||||
.map(|bin_name| bin_name.to_owned())
|
||||
.or_else(|| Some(window_attrs.title.clone()))
|
||||
.and_then(|string| CString::new(string.as_str()).ok())
|
||||
.expect("Default `WM_CLASS` class contained null byte");
|
||||
// This environment variable is extraordinarily unlikely to actually be used...
|
||||
let instance = env::var("RESOURCE_NAME")
|
||||
.ok()
|
||||
.and_then(|instance| CString::new(instance.as_str()).ok())
|
||||
.or_else(|| Some(class.clone()))
|
||||
.expect("Default `WM_CLASS` instance contained null byte");
|
||||
(instance, class)
|
||||
};
|
||||
|
||||
(*class_hints).res_name = class.as_ptr() as *mut c_char;
|
||||
(*class_hints).res_class = instance.as_ptr() as *mut c_char;
|
||||
|
||||
unsafe {
|
||||
(xconn.xlib.XSetClassHint)(
|
||||
xconn.display,
|
||||
|
|
@ -198,6 +228,17 @@ impl Window2 {
|
|||
}//.queue();
|
||||
}
|
||||
|
||||
Window2::set_pid(xconn, x_window.window)
|
||||
.map(|flusher| flusher.queue());
|
||||
|
||||
if pl_attribs.x11_window_type != Default::default() {
|
||||
Window2::set_window_type(
|
||||
xconn,
|
||||
x_window.window,
|
||||
pl_attribs.x11_window_type,
|
||||
).queue();
|
||||
}
|
||||
|
||||
// set size hints
|
||||
{
|
||||
let mut size_hints = {
|
||||
|
|
@ -338,6 +379,96 @@ impl Window2 {
|
|||
))
|
||||
}
|
||||
|
||||
fn set_pid(xconn: &Arc<XConnection>, window: ffi::Window) -> Option<util::Flusher> {
|
||||
let pid_atom = unsafe { util::get_atom(xconn, b"_NET_WM_PID\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_WM_PID)");
|
||||
let client_machine_atom = unsafe { util::get_atom(xconn, b"WM_CLIENT_MACHINE\0") }
|
||||
.expect("Failed to call XInternAtom (WM_CLIENT_MACHINE)");
|
||||
unsafe {
|
||||
let (hostname, hostname_length) = {
|
||||
// 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is
|
||||
// the limit defined by OpenBSD.
|
||||
const MAXHOSTNAMELEN: usize = 256;
|
||||
let mut hostname: [c_char; MAXHOSTNAMELEN] = mem::uninitialized();
|
||||
let status = libc::gethostname(hostname.as_mut_ptr(), hostname.len());
|
||||
if status != 0 { return None; }
|
||||
hostname[MAXHOSTNAMELEN - 1] = '\0' as c_char; // a little extra safety
|
||||
let hostname_length = libc::strlen(hostname.as_ptr());
|
||||
(hostname, hostname_length as usize)
|
||||
};
|
||||
util::change_property(
|
||||
xconn,
|
||||
window,
|
||||
pid_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
util::Format::Long,
|
||||
util::PropMode::Replace,
|
||||
&[libc::getpid() as util::Cardinal],
|
||||
).queue();
|
||||
let flusher = util::change_property(
|
||||
xconn,
|
||||
window,
|
||||
client_machine_atom,
|
||||
ffi::XA_STRING,
|
||||
util::Format::Char,
|
||||
util::PropMode::Replace,
|
||||
&hostname[0..hostname_length],
|
||||
);
|
||||
Some(flusher)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_window_type(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
window_type: util::WindowType,
|
||||
) -> util::Flusher {
|
||||
let hint_atom = unsafe { util::get_atom(xconn, b"_NET_WM_WINDOW_TYPE\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_WM_WINDOW_TYPE)");
|
||||
let window_type_atom = window_type.as_atom(xconn);
|
||||
unsafe {
|
||||
util::change_property(
|
||||
xconn,
|
||||
window,
|
||||
hint_atom,
|
||||
ffi::XA_ATOM,
|
||||
util::Format::Long,
|
||||
util::PropMode::Replace,
|
||||
&[window_type_atom],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_urgent(&self, is_urgent: bool) {
|
||||
let xconn = &self.x.display;
|
||||
|
||||
let mut wm_hints = {
|
||||
let mut wm_hints = unsafe {
|
||||
(xconn.xlib.XGetWMHints)(xconn.display, self.x.window)
|
||||
};
|
||||
xconn.check_errors().expect("`XGetWMHints` failed");
|
||||
if wm_hints.is_null() {
|
||||
wm_hints = unsafe { (xconn.xlib.XAllocWMHints)() };
|
||||
}
|
||||
util::XSmartPointer::new(xconn, wm_hints)
|
||||
}.expect("`XAllocWMHints` returned null; out of memory");
|
||||
|
||||
if is_urgent {
|
||||
(*wm_hints).flags |= ffi::XUrgencyHint;
|
||||
} else {
|
||||
(*wm_hints).flags &= !ffi::XUrgencyHint;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(xconn.xlib.XSetWMHints)(
|
||||
xconn.display,
|
||||
self.x.window,
|
||||
wm_hints.ptr,
|
||||
);
|
||||
util::flush_requests(xconn).expect("Failed to set urgency hint");
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netwm(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue