Hi everyone.
This is exact port of the c app that uses onboard keyboard:
It is so exact that even replicates the strange behavior of the c app , that is, it works as intended when debugging, but doesn't work when simply run (same build).
Could anyone take a look and perhaps figure out this very strange behavior?
Below is working example implemented by me in Rust.
Thank you in advance.
cargo.toml:
[package]
name = "rust_onboard"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glib-sys = "0.15.10"
[dependencies.gtk]
version = "0.15.5"
features = ["v3_24"]
[dependencies.glib]
version = "0.15.10"
[dependencies.gio]
version = "0.15.11"
main.rs:
use glib::clone;
use glib::translate::ToGlibPtr;
use glib::{error::Error, Pid, SpawnFlags};
use glib_sys::{gpointer, GIOChannel, GIOStatus, GType};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Entry, Expander, Grid, Socket, Widget};
use std::rc::Rc;
use std::{env, path::Path};
use std::{
io::Read,
process::{Command, Stdio},
};
fn main() {
let app = Application::builder()
.application_id("org.example.FirstDesktopApp")
.build();
app.connect_activate(|app| {
// We create the main window.
let main_window = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
let entry: Entry = Entry::new();
entry.set_hexpand(true);
let socket: Socket = Socket::new();
socket.set_size_request(500, 200);
socket.set_hexpand(true);
socket.set_vexpand(true);
let expander: Expander = Expander::new(Some("Keyboard"));
expander.add(&socket);
let button: Button = Button::with_label("Get Keyboard");
button.set_hexpand(true);
button.connect_clicked(clone!(@weak socket => move |_| {
add_plug(socket);
}));
let grid: Grid = Grid::new();
main_window.add(&grid);
grid.attach(&button, 0, 0, 1, 1);
grid.attach(&entry, 0, 1, 1, 1);
grid.attach(&expander, 0, 2, 1, 1);
// Show the window.
main_window.show_all();
});
app.run();
}
fn add_plug(socket: Socket) {
println!("Add Plug");
let working_directory = env::current_dir().unwrap();
let argv = [
Path::new("onboard"),
Path::new("-e"),
Path::new("-l"),
Path::new("Phone"),
Path::new("-t"),
Path::new("ModelM"),
Path::new("-s"),
Path::new("400x300"),
];
let environ = glib::environ();
let envp: Vec<&Path> = environ.iter().map(Path::new).collect();
let flags = SpawnFlags::DO_NOT_REAP_CHILD | SpawnFlags::SEARCH_PATH;
let child_setup = None;
let io: Result<(Pid, i32, i32, i32), Error> =
glib::spawn_async_with_pipes(working_directory, &argv, &envp, flags, child_setup);
match io {
Ok(pid) => {
println!(
"Spawned with pipes: Pid: {:?}, input: {}, output: {}, error: {}",
pid.0, pid.1, pid.2, pid.3
);
unsafe {
let std_out_ch: *mut GIOChannel = glib_sys::g_io_channel_unix_new(pid.2);
let condition: u32 = (glib::IOCondition::IN | glib::IOCondition::HUP).bits();
let socket_ptr: *mut gtk::ffi::GtkSocket = socket.to_glib_none().0;
let user_data: *mut std::ffi::c_void =
socket_ptr as *mut _ as *mut std::ffi::c_void;
//glib_sys::g_io_add_watch(std_out_ch, condition, Some(watch_out_channel), user_data);
glib_sys::g_io_add_watch_full(
std_out_ch,
1,
condition,
Some(watch_out_channel),
user_data,
None,
);
}
}
Err(e) => {
eprintln!("io err:{}", e)
}
}
}
unsafe extern "C" fn watch_out_channel(channel: *mut GIOChannel, cond: u32, data: gpointer) -> i32 {
if let Ok(window_id) = read_lines_from_channel(channel) {
let filtered = window_id
.chars()
.filter(|d| {
//println!("{d}");
if d.is_ascii_digit() {
true
} else {
false
}
})
.collect::<String>();
println!("window_id: {window_id}");
let window_id = filtered.parse().unwrap();
let gtk_socket: *mut gtk::ffi::GtkSocket = data as *mut _ as *mut gtk::ffi::GtkSocket;
let socket: Socket = glib::translate::FromGlibPtrNone::from_glib_none(gtk_socket);
socket.add_id(window_id);
}
1
}
use glib_sys::GError;
use std::{
error,
ffi::{CStr, CString},
os::raw::c_char,
ptr, slice, str,
};
unsafe fn into_message(error: *mut GError) -> String {
assert!(!error.is_null());
let message: &CStr = CStr::from_ptr((*error).message);
let message: String = message.to_str().unwrap().to_owned();
glib_sys::g_error_free(error);
message
}
fn read_lines(filename: &str) -> Result<Vec<String>, Box<dyn error::Error>> {
let filename: CString = CString::new(filename)?;
let mut error: *mut GError = ptr::null_mut();
let channel: *mut GIOChannel = unsafe {
glib_sys::g_io_channel_new_file(filename.as_ptr(), b"r\0".as_ptr().cast(), &mut error)
};
if channel.is_null() {
return Err(unsafe { into_message(error) }.into());
}
let mut lines: Vec<String> = Vec::new();
loop {
let mut line_raw: *mut c_char = ptr::null_mut();
let mut length: usize = 0;
let status: GIOStatus = unsafe {
glib_sys::g_io_channel_read_line(
channel,
&mut line_raw,
&mut length,
ptr::null_mut(),
&mut error,
)
};
match status {
glib_sys::G_IO_STATUS_ERROR => {
unsafe { glib_sys::g_io_channel_unref(channel) };
return Err(unsafe { into_message(error) }.into());
}
glib_sys::G_IO_STATUS_NORMAL => {
let line_bytes: &[u8] = if length == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(line_raw.cast(), length) }
};
lines.push(str::from_utf8(line_bytes).unwrap().to_owned());
unsafe { glib_sys::g_free(line_raw.cast()) };
}
glib_sys::G_IO_STATUS_EOF => break,
glib_sys::G_IO_STATUS_AGAIN => {}
_ => unreachable!(),
}
}
unsafe { glib_sys::g_io_channel_unref(channel) };
Ok(lines)
}
fn read_lines_from_channel(channel: *mut GIOChannel) -> Result<String, Box<dyn error::Error>> {
let mut error: *mut GError = ptr::null_mut();
if channel.is_null() {
return Err(unsafe { into_message(error) }.into());
}
let mut window_id = String::new();
/*loop */
{
let mut line_raw: *mut c_char = ptr::null_mut();
let mut length: usize = 0;
let status: GIOStatus = unsafe {
glib_sys::g_io_channel_read_line(
channel,
&mut line_raw,
&mut length,
ptr::null_mut(),
&mut error,
)
};
match status {
glib_sys::G_IO_STATUS_ERROR => {
unsafe { glib_sys::g_io_channel_unref(channel) };
return Err(unsafe { into_message(error) }.into());
}
glib_sys::G_IO_STATUS_NORMAL => {
let line_bytes: &[u8] = if length == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(line_raw.cast(), length) }
};
window_id = str::from_utf8(line_bytes).unwrap().to_owned();
// lines.push(str::from_utf8(line_bytes).unwrap().to_owned());
unsafe { glib_sys::g_free(line_raw.cast()) };
}
//glib_sys::G_IO_STATUS_EOF => break,
glib_sys::G_IO_STATUS_AGAIN => {}
_ => unreachable!(),
}
}
// unsafe { glib_sys::g_io_channel_unref(channel) };
Ok(window_id)
}