Open psychon opened 4 years ago
Having something like Xft would be extremely helpful, but that is presumably very different from icccm and ewmh support
From a quick look at /usr/include/X11/Xft/Xft.h
: Xft depends on fontconfig. Thus, one would need to also use one of the available fontconfig bindings. Besides that, Xft.h
has 500 lines of code and many functions.
I guess FFI bindings to Xft would better get their own create.
(icccm and ewmh are X11-protocol-only while font stuff needs to interact with files on the local system (and font files plus the whole font stack are complicated, I think))
Hm... After taking a closer look: libxcb-wm's icccm utilties do not really do much.
For example, the WM_TRANSIENT_FOR
utility... meh. If anyone wants to have some fun with macros, they can implement all of these. This just is a thin wrapper around a property with format 32.
And the stuff it does do is ugly and really should be implemented here. So, I'll only write utilities for WM_CLASS
, WM_SIZE_HINTS
and WM_HINTS
(since all of them involve actual parsing).
I suspect the morally correct thing to do would be to come up with some sort of xml description for icccm and ewmh, and generate the bindings automatically.
libxcb-wm heavily uses m4 to generate its API for EWMH. The icccm API is hand-written (but could easily use some macro-magic to make it less duplicated).
So... something machine readable would of course be nice, but I would also be fine with something that heavily uses macros instead.
Some more details of what I imagine here.
An example from xcb-util-icccm
:
/* WM_TRANSIENT_FOR */
xcb_void_cookie_t
xcb_icccm_set_wm_transient_for_checked(xcb_connection_t *c, xcb_window_t window,
xcb_window_t transient_for_window)
{
return xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, window,
XCB_ATOM_WM_TRANSIENT_FOR,
XCB_ATOM_WINDOW, 32, 1,
&transient_for_window);
}
xcb_void_cookie_t
xcb_icccm_set_wm_transient_for(xcb_connection_t *c, xcb_window_t window,
xcb_window_t transient_for_window)
{
return xcb_change_property(c, XCB_PROP_MODE_REPLACE, window,
XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
1, &transient_for_window);
}
xcb_get_property_cookie_t
xcb_icccm_get_wm_transient_for(xcb_connection_t *c, xcb_window_t window)
{
return xcb_get_property(c, 0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1);
}
xcb_get_property_cookie_t
xcb_icccm_get_wm_transient_for_unchecked(xcb_connection_t *c, xcb_window_t window)
{
return xcb_get_property_unchecked(c, 0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1);
}
uint8_t
xcb_icccm_get_wm_transient_for_from_reply(xcb_window_t *prop,
xcb_get_property_reply_t *reply)
{
if(!reply || reply->type != XCB_ATOM_WINDOW || reply->format != 32 || !reply->length)
return 0;
*prop = *((xcb_window_t *) xcb_get_property_value(reply));
return 1;
}
uint8_t
xcb_icccm_get_wm_transient_for_reply(xcb_connection_t *c,
xcb_get_property_cookie_t cookie,
xcb_window_t *prop,
xcb_generic_error_t **e)
{
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, e);
uint8_t ret = xcb_icccm_get_wm_transient_for_from_reply(prop, reply);
free(reply);
return ret;
}
All of this is a really thin wrapper around getting properties. The remaining parts of icccm all look like this, I think:
get_property()
have some pre-defined default.value32().next()
to get one Window
value. (But there are lists and other things...)xcb-util-ewmh
is similar, but requires some state to be kept around. This state contains lots of atoms that are needed for the functions.
Edit: Here is a possible Rust version of the above (plus one for WM_PROTOCOLS
):
pub struct TransientForCookie<'a, C>(Cookie<'a, C, GetPropertyReply>);
impl<...> TransientForCookie<...> {
pub fn reply(self) -> Result<TransientFor, ReplyError<...>> { ... }
pub fn reply_unchecked(self) -> Result<Option<TransientFor>, ConnectionError)>{ ... }
}
pub struct TransientFor(Window);
impl TransientFor {
pub fn set<'a, C>(conn: &'a C, window: Window, transient: Window) -> Result<VoidCookie, ConnectionError> {
wrapper::change_property32(
conn,
xproto::PropModeReplace,
window,
xproto::AtomEnum::WM_TRANSIENT_FOR,
xproto::AtomEnum::WINDOW,
&[transient],
)
}
pub fn get<'a, C>(conn: &'a C, window: Window) -> Result<TransientForCookie, ConnectionError> {
let cookie = xproto::get_property(
conn,
false,
window,
xproto::AtomEnum::WM_TRANSIENT_FOR,
xproto::AtomEnum::WINDOW,
0,
1,
)?;
Ok(TransientForCookie(cookie))
}
pub fn from_reply(reply: GetPropertyReply) -> Result<Self, ParseError> {
if reply.type_ != xproto::AtomEnum::WINDOW.into() || reply.format != 32 || reply.value_len == 0 {
return Err(ParseError::ParseError);
}
// Edit: Hm, what should happen for "No WM_TRANSIENT_FOR set"? I think that would currently return a `ParseError`, which is not all that useful. Should this perhaps instead return `Option<Self>` and use that for "not set"?
Ok(Self(reply.value32().next().expect("We just checked that value_len is not 0, so this cannot be None")))
}
pub fn window(&self) -> Window {
self.0
}
}
// The above is for "only a single value". The next has a list, which is a bit more complicated / different
pub struct WmProtocolsCookie<'a, C>(Cookie<'a, C, GetPropertyReply>);
impl { /* just as above, the cookie should be easily macro'ble */ }
// Hm... should this contain a GetPropertyReply instead? That would avoid a copy, but with the copy
// we can offer a better API (return a slice instead of just an iterator). I think the copy is
// worth it. Plus, people who disagree can just use the underlying xproto API directly.
pub struct WmProtocols(Vec<Atom>);
impl WmProtocols {
pub fn set(conn: &C, window: Window, protocols: &[Atom]) -> Result<VoidCookie, ConnectionError> { ... ]
pub fn get<'a, C>(conn: &'a C, window: Window) -> Result<WmProtocolsCookie, ConnectionError> { ... }
pub fn from_reply(reply: GetPropertyReply) -> Result<Self, ParseError> {
if reply.type_ != xproto::AtomEnum::ATOM.into() || reply.format != 32 {
return Err(ParseError::ParseError);
}
Ok(Self(reply.value32().unwrap().collect()))
}
pub fn protocols(&self) -> &[Atom] { ... }
}
This could clearly use the help of some macros when implemented. The cookie seems to be pretty much always identical.
See e.g. https://github.com/psychon/x11rb/issues/163#issuecomment-573145278.
Would this make more sense as a separate repo or in here? I guess "split things up into multiple repos" is better done in the future and not too early.
Edit: https://gitlab.freedesktop.org/xorg/lib/libxcb-wm