diff --git a/Makefile b/Makefile index 968a543..a065e3b 100644 --- a/Makefile +++ b/Makefile @@ -28,4 +28,4 @@ build: target/release/rwm .PHONY: build xephyr: - Xephyr :1 & DISPLAY=:1.0 cargo run && kill %1 + Xephyr :1 -screen 960x540 & DISPLAY=:1.0 cargo run && kill %1 diff --git a/src/config.rs b/src/config.rs index 3e509b8..b1411d2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, error::Error, - ffi::{c_float, c_int, c_uint, CStr, CString}, + ffi::{c_float, c_int, c_uint, CString}, path::Path, ptr::null, sync::LazyLock, @@ -41,8 +41,8 @@ impl Default for Config { resize_hints: false, lock_fullscreen: true, fonts: vec![c"monospace:size=10".into()], - tags: [c"1", c"2", c"3", c"4", c"5", c"6", c"7", c"8", c"9"] - .map(CString::from) + tags: ["1", "2", "3", "4", "5", "6", "7", "8", "9"] + .map(String::from) .to_vec(), colors: default_colors(), keys: default_keys().to_vec(), @@ -56,7 +56,7 @@ impl Default for Config { showsystray: SHOWSYSTRAY, buttons: BUTTONS.to_vec(), layouts: LAYOUTS.to_vec(), - scratchpadname: SCRATCHPADNAME.into(), + scratchpadname: SCRATCHPADNAME.to_string(), } } } @@ -88,7 +88,7 @@ pub struct Config { pub fonts: Vec, - pub tags: Vec, + pub tags: Vec, pub colors: [[CString; 3]; 2], @@ -118,7 +118,7 @@ pub struct Config { pub layouts: Vec, - pub scratchpadname: CString, + pub scratchpadname: String, } unsafe impl Send for Config {} @@ -181,7 +181,7 @@ fn get_rules(v: &mut HashMap) -> Result, FigError> { // NOTE each into_raw call is a leak, but these should live as long as the // program runs anyway - let maybe_string = |val: Value| -> Result<*const c_char, FigError> { + let maybe_cstring = |val: Value| -> Result<*const c_char, FigError> { if let Ok(Ok(s)) = String::try_from(val.clone()).map(CString::new) { Ok(s.into_raw()) } else if val.is_nil() { @@ -192,6 +192,17 @@ fn get_rules(v: &mut HashMap) -> Result, FigError> { } }; + let maybe_string = |val: Value| -> Result { + if let Ok(s) = String::try_from(val.clone()) { + Ok(s) + } else if val.is_nil() { + Ok(String::new()) + } else { + log::error!("expected Str or Nil"); + Err(FigError::Conversion) + } + }; + let mut ret = Vec::new(); for rule in rules { let rule: Vec = rule.try_into()?; @@ -200,8 +211,8 @@ fn get_rules(v: &mut HashMap) -> Result, FigError> { return err; } ret.push(Rule { - class: maybe_string(rule[0].clone())?, - instance: maybe_string(rule[1].clone())?, + class: maybe_cstring(rule[0].clone())?, + instance: maybe_cstring(rule[1].clone())?, title: maybe_string(rule[2].clone())?, tags: i64::try_from(rule[3].clone())? as u32, isfloating: rule[4].clone().try_into()?, @@ -280,9 +291,6 @@ fn get_layouts( } let symbol: String = layout[0].clone().try_into()?; - let symbol = CString::new(symbol).map_err(|_| FigError::Conversion)?; - // LEAK but okay since this should hang around for the whole program - let symbol = symbol.into_raw(); type F = fn(&mut State, *mut Monitor); let arrange = match &layout[1] { @@ -310,13 +318,17 @@ impl TryFrom for Config { val.try_into_int().map_err(|_| "unable to parse int") }; let bool = |val: fig::Value| val.try_into(); - let str_list = |val: fig::Value| -> Result, _> { + let cstr_list = |val: fig::Value| -> Result, _> { let strs: Vec = val.try_into()?; strs.into_iter() .map(CString::new) .collect::, _>>() .map_err(|_| Box::new(FigError::Conversion)) }; + let str_list = |val: fig::Value| -> Result, FigError> { + let strs: Vec = val.try_into()?; + Ok(strs.into_iter().map(String::from).collect()) + }; Ok(Self { borderpx: int(get(&mut v, "borderpx")?)? as c_uint, snap: int(get(&mut v, "snap")?)? as c_uint, @@ -326,7 +338,7 @@ impl TryFrom for Config { nmaster: int(get(&mut v, "nmaster")?)? as c_int, resize_hints: bool(get(&mut v, "resize_hints")?)?, lock_fullscreen: bool(get(&mut v, "lock_fullscreen")?)?, - fonts: str_list(get(&mut v, "fonts")?)?, + fonts: cstr_list(get(&mut v, "fonts")?)?, tags: str_list(get(&mut v, "tags")?)?, colors: get_colors(&mut v)?, keys: get_keys(&mut v)?, @@ -343,10 +355,7 @@ impl TryFrom for Config { showsystray: get(&mut v, "showsystray")?.try_into()?, buttons: get_buttons(&mut v)?, layouts: get_layouts(&mut v)?, - scratchpadname: CString::new(String::try_from(get( - &mut v, - "scratchpadname", - )?)?)?, + scratchpadname: String::try_from(get(&mut v, "scratchpadname")?)?, }) } } @@ -419,7 +428,7 @@ const RULES: [Rule; 3] = [ Rule { class: c"Gimp".as_ptr(), instance: null(), - title: null(), + title: String::new(), tags: 0, isfloating: true, isterminal: false, @@ -429,7 +438,7 @@ const RULES: [Rule; 3] = [ Rule { class: c"Firefox".as_ptr(), instance: null(), - title: null(), + title: String::new(), tags: 1 << 8, isfloating: false, isterminal: false, @@ -439,7 +448,7 @@ const RULES: [Rule; 3] = [ Rule { class: c"st-256color".as_ptr(), instance: null(), - title: null(), + title: String::new(), tags: 0, isfloating: false, isterminal: true, @@ -450,11 +459,13 @@ const RULES: [Rule; 3] = [ // layouts -const LAYOUTS: [Layout; 3] = [ - Layout { symbol: c"[]=".as_ptr(), arrange: Some(tile) }, - Layout { symbol: c"><>".as_ptr(), arrange: None }, - Layout { symbol: c"[M]".as_ptr(), arrange: Some(monocle) }, -]; +static LAYOUTS: LazyLock<[Layout; 3]> = LazyLock::new(|| { + [ + Layout { symbol: "[]=".to_string(), arrange: Some(tile) }, + Layout { symbol: "><>".to_string(), arrange: None }, + Layout { symbol: "[M]".to_string(), arrange: Some(monocle) }, + ] +}); // key definitions const MODKEY: c_uint = Mod4Mask; @@ -481,12 +492,12 @@ static DMENUCMD: LazyLock> = LazyLock::new(|| { }); static TERMCMD: LazyLock> = LazyLock::new(|| vec!["st".into()]); -const SCRATCHPADNAME: &CStr = c"scratchpad"; +const SCRATCHPADNAME: &str = "scratchpad"; static SCRATCHPADCMD: LazyLock> = LazyLock::new(|| { vec![ "st".into(), "-t".into(), - SCRATCHPADNAME.to_str().unwrap().to_owned(), + SCRATCHPADNAME.to_string(), "-g".into(), "120x34".into(), ] diff --git a/src/drw.rs b/src/drw.rs index 73953e9..ab563d3 100644 --- a/src/drw.rs +++ b/src/drw.rs @@ -16,20 +16,11 @@ use x11::xlib::{ }; use crate::enums::Col; -use crate::util::between; use crate::util::die; use crate::Clr; use crate::Cursor as Cur; use crate::Window; -// defined in drw.c -const UTF_SIZ: usize = 4; -const UTF_INVALID: usize = 0xFFFD; -const UTFBYTE: [c_uchar; UTF_SIZ + 1] = [0x80, 0, 0xC0, 0xE0, 0xF0]; -const UTFMASK: [c_uchar; UTF_SIZ + 1] = [0xC0, 0x80, 0xE0, 0xF0, 0xF8]; -const UTFMIN: [c_long; UTF_SIZ + 1] = [0, 0, 0x80, 0x800, 0x10000]; -const UTFMAX: [c_long; UTF_SIZ + 1] = [0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF]; - // defined in /usr/include/fontconfig/fontconfig.h const FC_TRUE: i32 = 1; @@ -55,58 +46,6 @@ pub struct Drw { pub scheme: Vec, pub fonts: Vec, } - -fn utf8decodebyte(c: c_char, i: &mut usize) -> c_long { - *i = 0; - while *i < UTF_SIZ + 1 { - if c as c_uchar & UTFMASK[*i] == UTFBYTE[*i] { - return (c as c_uchar & !UTFMASK[*i]) as c_long; - } - *i += 1; - } - 0 -} - -fn utf8validate(u: &mut c_long, i: usize) -> usize { - if !between(*u, UTFMIN[i], UTFMAX[i]) || between(*u, 0xD800, 0xDFFF) { - *u = UTF_INVALID as c_long; - } - let mut i = 1; - while *u > UTFMAX[i] { - i += 1; - } - i -} - -fn utf8decode(c: *const i8, u: &mut c_long) -> usize { - unsafe { - *u = UTF_INVALID as c_long; - let mut len = 0; - let mut udecoded = utf8decodebyte(*c, &mut len); - if !between(len, 1, UTF_SIZ) { - return 1; - } - let mut i = 1; - let mut j = 1; - let mut type_ = 0; - while i < UTF_SIZ && j < len { - udecoded = (udecoded << 6) | utf8decodebyte(*c.add(i), &mut type_); - if type_ != 0 { - return j; - } - i += 1; - j += 1; - } - if j < len { - return 0; - } - *u = udecoded; - utf8validate(u, len); - - len - } -} - /// # Safety pub unsafe fn create( dpy: *mut Display, @@ -321,9 +260,9 @@ pub fn scm_create( } /// # Safety -pub unsafe fn fontset_getwidth(drw: &mut Drw, text: *const c_char) -> c_uint { +pub unsafe fn fontset_getwidth(drw: &mut Drw, text: &str) -> c_uint { log::trace!("fontset_getwidth"); - if drw.fonts.is_empty() || text.is_null() { + if drw.fonts.is_empty() || text.is_empty() { return 0; } self::text(drw, 0, 0, 0, 0, 0, text, 0) as c_uint @@ -337,7 +276,7 @@ pub unsafe fn text( mut w: c_uint, h: c_uint, lpad: c_uint, - mut text: *const c_char, + mut text: &str, invert: c_int, ) -> c_int { // this function is very confusing and likely can be dramatically simplified @@ -346,7 +285,7 @@ pub unsafe fn text( unsafe { log::trace!( "text: {drw:?}, {x}, {y}, {w}, {h}, {lpad}, {:?}, {invert}", - std::ffi::CStr::from_ptr(text) + text ); let mut ty: c_int; let mut ellipsis_x: c_int = 0; @@ -390,7 +329,7 @@ pub unsafe fn text( static mut ELLIPSIS_WIDTH: c_uint = 0; if (render != 0 && (drw.scheme.is_empty() || w == 0)) - || text.is_null() + || text.is_empty() || drw.fonts.is_empty() { return 0; @@ -421,7 +360,7 @@ pub unsafe fn text( } if ELLIPSIS_WIDTH == 0 && render != 0 { - ELLIPSIS_WIDTH = fontset_getwidth(drw, c"...".as_ptr()); + ELLIPSIS_WIDTH = fontset_getwidth(drw, "..."); } usedfont = 0; log::trace!("text: entering loop"); @@ -429,7 +368,7 @@ pub unsafe fn text( ew = 0; ellipsis_len = 0; utf8strlen = 0; - utf8str = text; + utf8str = text.as_ptr().cast(); nextfont = None; // I believe this loop is just walking along the characters in text @@ -442,8 +381,9 @@ pub unsafe fn text( // `while(*text)` to `while !text.is_null()`, but we actually need // to check if we're at the null byte at the end of the string, NOT // if text is a null pointer - while *text != b'\0' as i8 { - utf8charlen = utf8decode(text, &mut utf8codepoint) as c_int; + for c in text.chars() { + utf8codepoint = c as i64; + utf8charlen = c.len_utf8() as i32; for (font_idx, curfont) in drw.fonts.iter().enumerate() { charexists = charexists || xft::XftCharExists( @@ -454,7 +394,7 @@ pub unsafe fn text( if charexists { font_getexts( curfont, - text, + text.as_ptr().cast(), utf8charlen as u32, &mut tmpw, ); @@ -476,7 +416,7 @@ pub unsafe fn text( } } else if font_idx == usedfont { utf8strlen += utf8charlen; - text = text.add(utf8charlen as usize); + text = &text[utf8charlen as usize..]; ew += tmpw; } else { nextfont = Some(font_idx); @@ -520,19 +460,10 @@ pub unsafe fn text( } if render != 0 && overflow != 0 { log::trace!("recursing for render != && overflow != 0"); - self::text( - drw, - ellipsis_x, - y, - ellipsis_w, - h, - 0, - c"...".as_ptr(), - invert, - ); + self::text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); } - if *text == b'\0' as i8 || overflow != 0 { + if text.is_empty() || overflow != 0 { break; } else if nextfont.is_some_and(|n| n < drw.fonts.len()) { charexists = false; @@ -680,23 +611,3 @@ pub fn resize(drw: &mut Drw, w: c_uint, h: c_uint) { ); } } - -#[cfg(test)] -mod tests { - #[test] - fn test_utf8decode() { - let tests = [ - (c"GNU Emacs at omsf", 71, 1), - (c"NU Emacs at omsf", 78, 1), - (c"U Emacs at omsf", 85, 1), - (c"๐Ÿ•”", 0x1f554, 4), - ]; - - for (inp, want_u, ret) in tests { - let mut u = 0; - let got = super::utf8decode(inp.as_ptr(), &mut u); - assert_eq!(got, ret); - assert_eq!(u, want_u); - } - } -} diff --git a/src/handlers.rs b/src/handlers.rs index 4baef74..1acdf6b 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -58,7 +58,7 @@ pub(crate) fn buttonpress(state: &mut State, e: *mut XEvent) { let mut x = 0; // emulating do-while loop { - x += textw(&mut state.drw, CONFIG.tags[i].as_ptr()); + x += textw(&mut state.drw, &CONFIG.tags[i]); // condition if ev.x < x { break; @@ -72,15 +72,12 @@ pub(crate) fn buttonpress(state: &mut State, e: *mut XEvent) { click = Clk::TagBar; arg = Arg::Ui(1 << i); } else if ev.x - < x + textw( - &mut state.drw, - &raw const (*state.selmon).ltsymbol as *const _, - ) + < x + textw(&mut state.drw, &(*state.selmon).ltsymbol) { click = Clk::LtSymbol; } else if ev.x > (*state.selmon).ww - - textw(&mut state.drw, &raw const state.stext as *const _) + - textw(&mut state.drw, &state.stext) - getsystraywidth() as i32 { click = Clk::StatusText; diff --git a/src/key_handlers.rs b/src/key_handlers.rs index bc80d62..8d7aa8a 100644 --- a/src/key_handlers.rs +++ b/src/key_handlers.rs @@ -258,11 +258,10 @@ pub(crate) fn setlayout(state: &mut State, arg: *const Arg) { [(*state.selmon).pertag.curtag as usize] [(*state.selmon).sellt as usize]; } - libc::strncpy( - (*state.selmon).ltsymbol.as_mut_ptr(), - (*(*state.selmon).lt[(*state.selmon).sellt as usize]).symbol, - size_of_val(&(*state.selmon).ltsymbol), - ); + (*state.selmon).ltsymbol = (*(*state.selmon).lt + [(*state.selmon).sellt as usize]) + .symbol + .clone(); if !(*state.selmon).sel.is_null() { arrange(state, state.selmon); } else { diff --git a/src/layouts.rs b/src/layouts.rs index 813be79..d5b7c27 100644 --- a/src/layouts.rs +++ b/src/layouts.rs @@ -16,12 +16,7 @@ pub(crate) fn monocle(state: &mut State, m: *mut Monitor) { }); if n > 0 { // override layout symbol - libc::snprintf( - (*m).ltsymbol.as_mut_ptr(), - size_of_val(&(*m).ltsymbol), - c"[%d]".as_ptr(), - n, - ); + (*m).ltsymbol = format!("[{n}]"); } cfor!((c = nexttiled((*m).clients); !c.is_null(); c = nexttiled((*c).next)) { resize(state, c, (*m).wx, (*m).wy, (*m).ww - 2 * (*c).bw, (*m).wh - 2 * (*c).bw, 0); diff --git a/src/lib.rs b/src/lib.rs index 6a3153b..5cf045f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,11 +76,11 @@ pub struct Cursor { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct Rule { pub class: *const c_char, pub instance: *const c_char, - pub title: *const c_char, + pub title: String, pub tags: c_uint, pub isfloating: bool, pub isterminal: bool, @@ -94,9 +94,9 @@ pub struct Systray { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct Layout { - pub symbol: *const c_char, + pub symbol: String, pub arrange: Option, } @@ -121,7 +121,7 @@ pub struct Pertag { #[repr(C)] #[derive(Debug, Clone)] pub struct Monitor { - pub ltsymbol: [c_char; 16usize], + pub ltsymbol: String, pub mfact: f32, pub nmaster: c_int, pub num: c_int, @@ -149,9 +149,9 @@ pub struct Monitor { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct Client { - pub name: [c_char; 256usize], + pub name: String, pub mina: f32, pub maxa: f32, pub x: c_int, diff --git a/src/main.rs b/src/main.rs index f999344..0406a4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ //! tiling window manager based on dwm use std::cmp::max; -use std::ffi::{c_char, c_int, c_uint, c_ulong, CStr}; +use std::ffi::{c_int, c_uint, c_ulong, CStr}; use std::io::Read; -use std::mem::size_of_val; use std::mem::{size_of, MaybeUninit}; use std::ptr::null_mut; use std::sync::LazyLock; @@ -172,11 +171,7 @@ fn createmon() -> *mut Monitor { (*m).topbar = CONFIG.topbar; (*m).lt[0] = &CONFIG.layouts[0]; (*m).lt[1] = &CONFIG.layouts[1 % CONFIG.layouts.len()]; - libc::strncpy( - &mut (*m).ltsymbol as *mut _, - CONFIG.layouts[0].symbol, - size_of_val(&(*m).ltsymbol), - ); + (*m).ltsymbol = CONFIG.layouts[0].symbol.clone(); (*m).pertag = Pertag { curtag: 1, @@ -269,7 +264,7 @@ fn setup(dpy: *mut Display) -> State { drw, selmon: null_mut(), mons: null_mut(), - stext: ['\0' as c_char; 256], + stext: String::new(), scheme: Default::default(), }; @@ -630,11 +625,7 @@ fn arrange(state: &mut State, mut m: *mut Monitor) { fn arrangemon(state: &mut State, m: *mut Monitor) { log::trace!("arrangemon"); unsafe { - libc::strncpy( - (*m).ltsymbol.as_mut_ptr(), - (*(*m).lt[(*m).sellt as usize]).symbol, - size_of_val(&(*m).ltsymbol), - ); + (*m).ltsymbol = (*(*m).lt[(*m).sellt as usize]).symbol.clone(); let arrange = (*(*m).lt[(*m).sellt as usize]).arrange; if let Some(arrange) = arrange { (arrange)(state, m); @@ -1131,15 +1122,8 @@ fn unfocus(state: &mut State, c: *mut Client, setfocus: bool) { fn updatestatus(state: &mut State) { log::trace!("updatestatus"); unsafe { - if gettextprop( - state.dpy, - ROOT, - XA_WM_NAME, - state.stext.as_mut_ptr(), - size_of_val(&state.stext) as u32, - ) == 0 - { - libc::strcpy(&raw mut state.stext as *mut _, c"rwm-1.0".as_ptr()); + if gettextprop(state.dpy, ROOT, XA_WM_NAME, &mut state.stext) == 0 { + state.stext = "rwm-1.0".to_string(); } drawbar(state, state.selmon); updatesystray(state); @@ -1251,8 +1235,7 @@ fn updatesystray(state: &mut State) { let mut i: *mut Client; let m: *mut Monitor = systraytomon(state, null_mut()); let mut x: c_int = (*m).mx + (*m).mw; - let sw = textw(&mut state.drw, &raw const state.stext as *const _) - - LRPAD + let sw = textw(&mut state.drw, &state.stext) - LRPAD + CONFIG.systrayspacing as i32; let mut w = 1; @@ -1431,7 +1414,7 @@ fn systraytomon(state: &State, m: *mut Monitor) -> *mut Monitor { } } -fn textw(drw: &mut Drw, x: *const c_char) -> c_int { +fn textw(drw: &mut Drw, x: &str) -> c_int { log::trace!("textw"); unsafe { drw::fontset_getwidth(drw, x) as c_int + LRPAD } } @@ -1460,9 +1443,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { if m == state.selmon { // status is only drawn on selected monitor drw::setscheme(&mut state.drw, state.scheme[Scheme::Norm].clone()); - tw = textw(&mut state.drw, &raw const state.stext as *const _) - - LRPAD / 2 - + 2; // 2px right padding + tw = textw(&mut state.drw, &state.stext) - LRPAD / 2 + 2; // 2px right padding log::trace!("drawbar: text"); drw::text( &mut state.drw, @@ -1471,7 +1452,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { tw as u32, state.bh as u32, (LRPAD / 2 - 2) as u32, - &raw const state.stext as *const _, + &state.stext, 0, ); } @@ -1490,7 +1471,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { let mut x = 0; for (i, tag) in CONFIG.tags.iter().enumerate() { let text = tag.to_owned(); - let w = textw(&mut state.drw, text.as_ptr()); + let w = textw(&mut state.drw, &text); drw::setscheme( &mut state.drw, state.scheme[if ((*m).tagset[(*m).seltags as usize] & 1 << i) @@ -1510,7 +1491,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { w as u32, state.bh as u32, LRPAD as u32 / 2, - text.as_ptr(), + &text, (urg as i32) & 1 << i, ); @@ -1531,7 +1512,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { x += w as i32; } - let w = textw(&mut state.drw, (*m).ltsymbol.as_ptr()); + let w = textw(&mut state.drw, &(*m).ltsymbol); drw::setscheme(&mut state.drw, state.scheme[Scheme::Norm].clone()); log::trace!("drawbar: text 3"); x = drw::text( @@ -1541,7 +1522,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { w as u32, state.bh as u32, LRPAD as u32 / 2, - (*m).ltsymbol.as_ptr(), + &(*m).ltsymbol, 0, ) as i32; log::trace!("finished drawbar text 3"); @@ -1566,7 +1547,7 @@ fn drawbar(state: &mut State, m: *mut Monitor) { w as u32, state.bh as u32, LRPAD as u32 / 2, - (*(*m).sel).name.as_ptr(), + &(*(*m).sel).name, 0, ); if (*(*m).sel).isfloating { @@ -1611,15 +1592,10 @@ fn gettextprop( dpy: *mut Display, w: Window, atom: Atom, - text: *mut i8, - size: u32, + text: &mut String, ) -> c_int { log::trace!("gettextprop"); unsafe { - if text.is_null() || size == 0 { - return 0; - } - *text = '\0' as i8; let mut name = xlib::XTextProperty { value: std::ptr::null_mut(), encoding: 0, @@ -1634,7 +1610,8 @@ fn gettextprop( let mut n = 0; let mut list: *mut *mut i8 = std::ptr::null_mut(); if name.encoding == XA_STRING { - libc::strncpy(text, name.value as *mut _, size as usize - 1); + let name_val = CStr::from_ptr(name.value.cast()); + *text = name_val.to_string_lossy().to_string(); } else if xlib::XmbTextPropertyToTextList( dpy, &name, @@ -1644,11 +1621,28 @@ fn gettextprop( && n > 0 && !(*list).is_null() { - libc::strncpy(text, *list, size as usize - 1); + // TODO handle this properly. *list is a "string" in some encoding I + // don't understand. the main test case I noticed an issue with was + // a browser tab with a ยท, which was initially taking the value -73 + // as an i8, which is the correct character 183 as a u8. This + // solution works for characters like that that fit in a u8 but + // doesn't work for larger characters like ัž (cyrillic short u). + // actually `list` doesn't even contain the right characters for the + // short u. it just starts at the space after it, as demonstrated by + // using libc::printf to try to print it. + // + // Looks like my encoding is different. Getting 238 in Rust vs 287 + // in C. Using XGetAtomName shows 238 is UTF8_STRING, while 287 is + // _NET_WM_WINDOW_TYPE_POPUP_MENU (??). In dwm in the VM, 287 is + // also UTF8_STRING + *text = String::new(); + let mut c = *list; + while *c != 0 { + text.push(char::from(*c as u8)); + c = c.offset(1); + } xlib::XFreeStringList(list); } - let p = text.offset(size as isize - 1); - *p = '\0' as i8; xlib::XFree(name.value as *mut _); } 1 @@ -2097,7 +2091,7 @@ fn cleanup(mut state: State) { let a = Arg::Ui(!0); view(&mut state, &a); (*state.selmon).lt[(*state.selmon).sellt as usize] = - &Layout { symbol: c"".as_ptr(), arrange: None }; + &Layout { symbol: String::new(), arrange: None }; let mut m = state.mons; while !m.is_null() { @@ -2509,6 +2503,7 @@ fn manage(state: &mut State, w: Window, wa: *mut xlib::XWindowAttributes) { (*c).h = wa.height; (*c).oldh = wa.height; (*c).oldbw = wa.border_width; + (*c).name = String::new(); let mut term: *mut Client = null_mut(); @@ -2551,8 +2546,7 @@ fn manage(state: &mut State, w: Window, wa: *mut xlib::XWindowAttributes) { // like something meant to be handled by RULES (*state.selmon).tagset[(*state.selmon).seltags as usize] &= !*SCRATCHTAG; - if libc::strcmp((*c).name.as_ptr(), CONFIG.scratchpadname.as_ptr()) == 0 - { + if (*c).name == CONFIG.scratchpadname { (*c).tags = *SCRATCHTAG; (*(*c).mon).tagset[(*(*c).mon).seltags as usize] |= (*c).tags; (*c).isfloating = true; @@ -2813,8 +2807,7 @@ fn applyrules(state: &mut State, c: *mut Client) { }; for r in &CONFIG.rules { - if (r.title.is_null() - || !libc::strstr((*c).name.as_ptr(), r.title).is_null()) + if (r.title.is_empty() || (*c).name.contains(&r.title)) && (r.class.is_null() || !libc::strstr(class.as_ptr(), r.class).is_null()) && (r.instance.is_null() @@ -2909,24 +2902,14 @@ fn updatetitle(state: &mut State, c: *mut Client) { state.dpy, (*c).win, state.netatom[Net::WMName as usize], - &mut (*c).name as *mut _, - size_of_val(&(*c).name) as u32, + &mut (*c).name, ) == 0 { - gettextprop( - state.dpy, - (*c).win, - XA_WM_NAME, - &mut (*c).name as *mut _, - size_of_val(&(*c).name) as u32, - ); + gettextprop(state.dpy, (*c).win, XA_WM_NAME, &mut (*c).name); } - if (*c).name[0] == '\0' as i8 { + if (*c).name.is_empty() { /* hack to mark broken clients */ - libc::strcpy( - &mut (*c).name as *mut _, - BROKEN.as_ptr() as *const c_char, - ); + (*c).name = BROKEN.to_string_lossy().to_string(); } } } diff --git a/src/state.rs b/src/state.rs index 62b322f..69c5964 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,4 @@ -use std::{ - ffi::{c_char, c_int}, - ops::Index, -}; +use std::{ffi::c_int, ops::Index}; use x11::xlib::{self, Atom, Display}; @@ -50,7 +47,7 @@ pub struct State { pub cursors: Cursors, pub selmon: *mut Monitor, pub mons: *mut Monitor, - pub stext: [c_char; 256], + pub stext: String, pub scheme: ClrScheme, } diff --git a/src/tests.rs b/src/tests.rs index 356331f..cabee3b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -43,7 +43,7 @@ fn main() { CONFIG .tags .iter() - .map(|tag| textw(&mut state.drw, tag.as_ptr())) + .map(|tag| textw(&mut state.drw, tag)) .sum::() + 5, state.dpy, diff --git a/src/util.rs b/src/util.rs index d491f84..d94d65d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -14,8 +14,3 @@ pub fn ecalloc(nmemb: size_t, size: size_t) -> *mut c_void { } ret } - -#[inline] -pub(crate) fn between(x: T, a: T, b: T) -> bool { - a <= x && x <= b -}