Как мне получить класс окна X с идентификатором окна с помощью rust-xcb?

Я пытаюсь использовать rust-xcb для получения класса окна задан идентификатор окна.

fn get_class(conn: &xcb::Connection, id: &i32) {
    let window: xcb::xproto::Window = *id as u32;
    let class_prop: xcb::xproto::Atom = 67; // XCB_ATOM_WM_CLASS from xproto.h
    let cookie = xcb::xproto::get_property(&conn, false, window, class_prop, 0, 0, 2);
    match cookie.get_reply() {
        Ok(reply) => {
            let x: &[std::os::raw::c_void] = reply.value();
            println!("reply is {:?}", x[0]);
        }   
        Err(err) => println!("err {:?}", err),
    }
}

Документация довольно скудная и не очень полезная, хотя я нашел немного о GetPropertyReply и xcb_get_property_reply_t переносится.

Я просмотрел этот ответ в JavaScript, но я не знаю, что эквивалентно ctypes в Ржавчина есть. Я попытался просто преобразовать &[c_void] в &str или String:

 ...
 Ok(reply) => {
     let len = reply.value_len() as usize;
     let buf = reply.value() as &str;
     println!("{}", buf.slice_unchecked(0, len)); // this seems redundant
 }   
 ...

но он возвращается

error: non-scalar cast: `&[_]` as `&str`

Я попытался преобразовать &[c_void] в &[u8], а затем собрать Vec в String, что вроде работает:

  ...
  Ok(reply) => {
      let value : &[u8] = reply.value();
      let buf : String = value.into_iter().map(|i| *i as char).collect();
      println!("\t{:?}", buf);
  }
  ...

но теперь я получаю странные результаты. например, когда я использую xprop в Chrome, я вижу "google-chrome", но для меня отображается только "google-c", а "roxterm" отображается как "roxterm\u{0}". Я предполагаю, что "\u{0}" связано с Unicode, но я не уверен, и я также не знаю, почему вещи объединяются. Может, мне еще раз проверить ответ?


person erp    schedule 29.06.2017    source источник
comment
Если это строка байтов, отличных от ascii, то это неправильный способ преобразования ее в строку, поскольку предполагается, что каждый байт является символом. Вместо этого вы должны использовать String::from_utf8.   -  person Peter Hall    schedule 29.06.2017
comment
о, круто, это решает проблему с юникодом. другая проблема - неполные строки, в чем я уверен, потому что я должен каким-то образом повторно запросить файл cookie. проверка value.bytes_after() показывает, что действительно есть байты, которые я должен принять, но неясно, как я должен это сделать. повторный вызов cookie.get_reply() просто зависает.   -  person erp    schedule 30.06.2017
comment
Я разобрался, у xcb::xproto::get_property() есть long_offset и long_length которые можно настроить как надо.   -  person erp    schedule 30.06.2017


Ответы (1)


Вот моя обновленная функция:

fn get_class(conn: &Connection, id: &i32) -> String {
    let window: xproto::Window = *id as u32;
    let long_length: u32 = 8;
    let mut long_offset: u32 = 0;
    let mut buf = Vec::new();
    loop {
        let cookie = xproto::get_property(
            &conn,
            false,
            window,
            xproto::ATOM_WM_CLASS,
            xproto::ATOM_STRING,
            long_offset,
            long_length,
        );
        match cookie.get_reply() {
            Ok(reply) => {
                let value: &[u8] = reply.value();
                buf.extend_from_slice(value);
                match reply.bytes_after() {
                    0 => break,
                    _ => {
                        let len = reply.value_len();
                        long_offset += len / 4;
                    }   
                }
            }   
            Err(err) => {
                println!("{:?}", err);
                break;
            }   
        }
    }
    let result = String::from_utf8(buf).unwrap();
    let results: Vec<&str> = result.split('\0').collect();
    results[0].to_string()
}

В этом вопросе было три основных части:

  1. Я поместил xproto::get_property() в цикл, чтобы проверить reply.bytes_after() и соответствующим образом настроить long_offset. Я думаю, что с соответствующим long_length обычно будет только одно чтение, но просто в целях безопасности.
  2. Как сказал @peter-hall, преобразование &[u8] -> String должно выполняться с использованием String::from_utf8, для которого требуется Vec; поэтому я let mut buf = Vec::new() и buf.extend_from_slice по циклу перед созданием строки результата с String::from_utf8(buf).unwrap()
  3. Согласно эта случайная страница WM_CLASS на самом деле представляет собой два последовательных нуля завершенные строки, поэтому я делю результат на \0 и беру первое значение.

Возможно, я просто искал не в том месте, но у xcb абсолютно ужасная документация.

person erp    schedule 06.07.2017