Why does XCreateIC in XLib return null over FFI but not in C?

I'm currently working on a PR to minifb to fix this issue, and my research says that I need to use the function Xutf8LookupString to properly read key press events as characters. This requires that an XIC instance is created, but I'm having a problem with creating that instance. I found an example C program (and I compiled and tested it and it works fine) that does everything that is needed to get these key presses correctly, but when I try to do the exact same thing in Rust, XCreateIC returns a null pointer.

The relevant C code looks like this:

    setlocale(LC_ALL, "");

    Display* dpy = XOpenDisplay(NULL);

    Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, 0);
    XMapRaised(dpy, win);
    XSync(dpy, False);

    // X input method setup, only strictly necessary for intl key text

    // loads the XMODIFIERS environment variable to see what IME to use
    XSetLocaleModifiers("");

    XIM xim = XOpenIM(dpy, 0, 0, 0);
    if(!xim){
        // fallback to internal input method
        XSetLocaleModifiers("@im=none");
        xim = XOpenIM(dpy, 0, 0, 0);
    }

    // X input context, you can have multiple for text boxes etc, but having a
    // single one is the easiest.

    XIC xic = XCreateIC(xim,
                        XNInputStyle,   XIMPreeditNothing | XIMStatusNothing,
                        XNClientWindow, win,
                        XNFocusWindow,  win,
                        NULL);

and the equivalent Rust code looks like this (some setup is removed since minifb has much more initialization code than that example):

            let handle = (d.lib.XCreateWindow)(
                d.display,
                root,
                x as i32,
                y as i32,
                width as u32,
                height as u32,
                0, /* border_width */
                d.depth,
                xlib::InputOutput as u32, /* class */
                d.visual,
                xlib::CWColormap | xlib::CWBackingStore | xlib::CWBackPixel | xlib::CWBorderPixel,
                &mut attributes,
            );

            let empty_string = CString::new("").unwrap();
            (d.lib.XSetLocaleModifiers)(empty_string.as_ptr());

            (d.lib.XSetErrorHandler)(Some(x11_error_handler));
            let xim = (d.lib.XOpenIM)(d.display, 0 as XrmDatabase, 0 as *mut c_char, 0 as *mut c_char);
            if(xim as usize) == 0{
                panic!("Failed to setup X IM via XOpenIM.");
            }

            let xn_input_style = CString::new(XNInputStyle).unwrap();
            let xn_client_window = CString::new(XNClientWindow).unwrap();
            let xic = (d.lib.XCreateIC)(xim, xn_input_style.as_ptr(), XIMPreeditNothing | XIMStatusNothing, xn_client_window.as_ptr(), handle as c_ulong, XNFocusWindow.as_ptr(), handle as c_ulong, std::ptr::null_mut::<c_void>());
            if (xic as usize) == 0{
               panic!("Failed to setup X IC via XCreateIC."); //panics here
            }

In this code, the panic at the end always runs (I'll do proper error handling once I have it working). Yet, as far as I can tell, I am doing this call in the exact same way as in the C program so I don't understand why it is doing this. Furthermore, this being a C library there's no panic error message or error return type that I can inspect. I tried to add an error callback to Xlib via

(d.lib.XSetErrorHandler)(Some(x11_error_handler));

but this error handler is not being called. Additionally, I can't step into the library usefully because it's all disassembly.

How can I debug this?

You seem to have forgotten to wrap XNFocusWindow in a CString. You could try to use CStr::as_ptr(&thing) rather than the method shorthand to avoid this kind of typos :slightly_smiling_face:


If that's not the cause of the problem, then I suggest you share a repo or something so that we can see the rest of the pipeline: I am mainly curious of the generated Rust "headers" for that FFI lib

2 Likes

Nice catch, it worked. Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.