5
votes

I have problem with resolving c-union structure XEvent.

I'm experimenting with Xlib and X Record Extension in Rust. I'm generate ffi-bindings with rust-bindgen. All code hosted on github alxkolm/rust-xlib-record.

Trouble happen on line src/main.rs:106 when I try extract data from XEvent structure.


let key_event: *mut xlib::XKeyEvent = event.xkey();
println!("KeyPress {}", (*key_event).keycode); // this always print 128 on any key

My program listen key events and print out keycode. But it is always 128 on any key I press. I think this wrong conversion from C union type to Rust type.

Definition of XEvent starts here src/xlib.rs:1143. It's the c-union. Original C definition here.

Code from GitHub can be run by cargo run command. It's compile without errors.

What I do wrong?

1
Could you specify exactly the error you encounter? If you have a compilation-time error, then the compiler error message would be useful; if you have a load time error, then the... Or is the problem that you always get 128 and were expecting something else?Matthieu M.
On any key I press keycode always is 128. It's fully compilable without errors. Fix text of question.alxkolm
Have you checked that the type of the event is what you would expect? The generated Rust code obeys you blindly: you ask for xkey it interprets the memory correspondingly, however if it were not a key-pressed event but something else, then it won't make sense.Matthieu M.
Yes, you right. I wrong in memory interpret early in code. And event variable contains wrong data. Thanks.alxkolm

1 Answers

4
votes

Beware that rustbindgen generates binding to C union with as much safety as in C; as a result, when calling:

event.xkey(); // gets the C union 'xkey' field

There is no runtime check that xkey is the field currently containing a value.

This is because since C does not have tagged union (ie, the union knowing which field is currently in use), developers came up with various ways of encoding this information (*), the two that I know of being:

  • an external supplier; typically another field of the structure right before the union
  • the first field of each of the structures in the union

Here, you are in the latter case int type; is the first field of the union and each nested structure starts with int _type; to denote this. As a result, you need a two-steps approach:

  1. consult type()
  2. depending on the value, call the correct reinterpretation

The mapping from the value of type to the actual field being used should be part of the documentation of the C library, hopefully.

I invite you to come up with a wrapper around this low-level union that will make it safer to retrieve the result. At the very least, you could check it is the right type in the accessor; the full approach being to come up with a Rust enum that would wrap proxies to all the fields and allow pattern-matching.

(*) and actually sometimes disregard it altogether, for example in C99 to reinterpret a float as an int a union { float f; int i; } can be used.