How can I create a MessageDialog using winrt-rs? - rust

I am attempting to show a MessageDialog upon keypress. So far, it doesn't seem to do anything. The code does compile, but does not function. The code verifies that the keypress works, but the dialog simply wont display. I have tried taking the returned IAsyncOperation and using 'get()' but that seems to freeze the application completely. What am I missing?
//#![windows_subsystem = "windows"]
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use winrt::*;
import!(
dependencies
os
modules
"windows.data.xml.dom"
"windows.foundation"
"windows.ui"
"windows.ui.popups"
);
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized (_size),
..
} => (),
Event::WindowEvent {
event: WindowEvent::KeyboardInput {input,..},
..
} if input.state == winit::event::ElementState::Pressed => {
use windows::ui::popups::MessageDialog;
let mymsg = MessageDialog::create("Test").unwrap().show_async();
println!("KeyState-{}",input.scancode);
},
_ => (),
}
});
}

This highlights one of the differences in using some WinRT APIs in a Win32 application. In a UWP application, your app has a CoreWindow associated with its main thread. Normally, dialogs query for this window and display themselves as modal to it. However, in a Win32 application the system can't make an assumption about what window you want to use. In these instances, you need to QI for the IInitializeWithWindow interface and call the initialize function with your window handle.
Since the IInitializeWithWindow interface is a pure COM interface and not a WinRT one, winrt-rs doesn't have a projection for it. Instead you'll need to define it yourself (make sure you get the GUID right!):
#[repr(C)]
pub struct abi_IInitializeWithWindow {
__base: [usize; 3],
initialize: extern "system" fn(
winrt::NonNullRawComPtr<InitializeWithWindowInterop>,
*mut c_void,
) -> winrt::ErrorCode,
}
unsafe impl winrt::ComInterface for InitializeWithWindowInterop {
type VTable = abi_IInitializeWithWindow;
fn iid() -> winrt::Guid {
winrt::Guid::from_values(1047057597, 28981, 19728, [128, 24, 159, 182, 217, 243, 63, 161])
}
}
#[repr(transparent)]
#[derive(Default, Clone)]
pub struct InitializeWithWindowInterop {
ptr: winrt::ComPtr<InitializeWithWindowInterop>,
}
impl InitializeWithWindowInterop {
pub fn initialize(
&self,
window: *mut c_void,
) -> winrt::Result<()> {
match self.ptr.abi() {
None => panic!("The `this` pointer was null when calling method"),
Some(this) => unsafe {
(this.vtable().initialize)(
this,
window,
)
.ok()?;
Ok(())
},
}
}
}
To get the window handle from your winit Window, you'll need the raw-window-handle crate. From there you can create a helper trait for anything that implements HasRawWindowHandle:
trait InitializeWithWindow {
fn initialize_with_window<O: RuntimeType + ComInterface>(&self, object: &O) -> winrt::Result<()>;
}
impl<T> InitializeWithWindow for T
where
T: HasRawWindowHandle,
{
fn initialize_with_window<O: RuntimeType + ComInterface>(
&self,
object: &O,
) -> winrt::Result<()> {
// Get the window handle
let window_handle = self.raw_window_handle();
let window_handle = match window_handle {
raw_window_handle::RawWindowHandle::Windows(window_handle) => window_handle.hwnd,
_ => panic!("Unsupported platform!"),
};
let init: InitializeWithWindowInterop = object.try_into()?;
init.initialize(window_handle)?;
Ok(())
}
}
Now in your event loop you can call it using the following:
let dialog = MessageDialog::create("Test").unwrap();
window.initialize_with_window(&dialog).unwrap();
dialog.show_async().unwrap();
println!("KeyState-{}",input.scancode);
Note that I'm not waiting on the result of the IAsyncOperation that is retunred by show_async. The reason is because only synchronous waiting it supported by the projection right now, and that would tie up your message pump and cause the window to hang. This means that the print statement will run before the dialog returns. This should improve once broader async support is up and running in the projection.
You can now use your initialize_with_window method on dialogs and pickers (e.g. FileSavePicker, GraphicsCapturePicker).

Related

Async: how to keep using the same future in a loop with select (no_std environment)?

I have two async functions: get_message and get_event. I'd like to perform an action whenever a message arrives or an event comes and do that forever in an infinite loop.
The simplified setup looks like this:
use futures::{future::select, future::Either, pin_mut};
impl MsgReceiver {
async fn get_message(&mut self) -> Message { /* ... */ }
}
impl EventListener {
async fn get_event(&mut self) -> Event { /* ... */ }
}
async fn eternal_task(receiver: MsgReceiver, listener: EventListener) -> ! {
let get_msg_fut = receiver.get_message();
pin_mut!(get_msg_fut);
loop {
let get_event_fut = listener.get_event();
pin_mut!(get_event_fut);
match select(get_event_fut, get_msg_fut).await {
Either::Left((ev, r_get_msg_fut)) => {
/* react to the event */
// r_get_msg_fut is not done, how to reuse it in the next iteration?
}
Either::Right((msg, r_get_event_fut)) => {
/* react to the message */
// it's fine to drop get_event_fut here
// the following line causes a double-mut-borrow error on receiver,
// despite receiver isn't borrowed anymore (the old future is completed and dropped)
let new_future = receiver.get_message();
}
};
}
}
I have three major questions here:
When an event comes first, how to tell rust that I want to reuse the incomplete get_message future on the next loop iteration?
When a message comes first, how to construct a new future without a borrow error?
When (2) is solved, how to put the new future into the same pinned memory location and use it on the next loop iteration?
I had success using this, but could not get rid of the Box::pin
use futures::{future::select, future::Either, pin_mut};
use std::sync::Mutex;
#[derive(Debug)]
struct MsgReceiver;
#[derive(Debug)]
struct EventListener;
#[derive(Debug)]
struct Message;
#[derive(Debug)]
struct Event;
impl MsgReceiver {
async fn get_message(&mut self) -> Message {
Message
}
}
impl EventListener {
async fn get_event(&mut self) -> Event {
Event
}
}
async fn eternal_task(receiver: MsgReceiver, mut listener: EventListener) -> ! {
let receiver = Mutex::new(receiver);
let mut f = None;
loop {
let get_msg_fut = match f.take() {
None => {
let mut l = receiver.lock();
Box::pin(async move {
l.get_message().await
})
}
Some(f) => f,
};
let get_event_fut = listener.get_event();
pin_mut!(get_event_fut);
match select(get_event_fut, get_msg_fut).await {
Either::Left((ev, r_get_msg_fut)) => {
/* react to the event */
// store the future for next iteration
f = Some(r_get_msg_fut);
}
Either::Right((msg, r_get_event_fut)) => {
/* react to the message */
}
};
}
}
#[tokio::main]
async fn main() {
eternal_task(MsgReceiver, EventListener).await;
}
I think this is tricky to get right, even with unsafe which would probably be needed to accomplish this. Persisting and reusing the same variables isn't too hard, its actually #2 that's the hardest (at least with the current borrow checker).
I found a solution that totally circumvents the problem by using the async-stream crate to provide an intermediary:
async fn eternal_task(mut receiver: MsgReceiver, mut listener: EventListener) -> ! {
let combined = futures::stream::select(
stream! { loop { yield Either::Left(receiver.get_message().await); } },
stream! { loop { yield Either::Right(listener.get_event().await); } },
);
pin_mut!(combined);
while let Some(msg_or_evt) = combined.next().await {
match msg_or_evt {
Either::Left(msg) => {
// do something with msg
}
Either::Right(evt) => {
// do something with evt
}
};
}
unreachable!()
}
It uses the stream! macro to generate a type that continuously calls and yields values from .get_message() and .get_event(). It then uses futures::stream::select and Either to combine them. And then its just a matter of looping over the results. It works in #![no_std].

RUST + FLTK: accessing another widget from widget on_push() function

I used FLTK to create a window and two buttons inside, the btn_A has a callback and should change the btn_B label, but I dont see any non-monstrous approach do to this, ples halp? =''[
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application=app::App::default();
let mut win = window::Window::default().with_size(500,300);
//BTN_A
let mut btn_A:Listener<_> = button::Button::new(100,100,100,50,"btn_A").into();
//BTN_B
let mut btn_B:Listener<_> = button::Button::new(300,100,100,50,"btn_B").into();
//BTN_A_CALLBACK
btn_A.handle(|elem,evt| match evt {
enums::Event::Push => { btn_A(elem); true }
_ => { false }
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A(elem:&mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
}
In principle all that is needed is to pass a mutable reference to btn_B to your handler function:
pub fn btn_A(elem:&mut button::Button, btn_B: &mut button::Button) {
...
}
However there is one slight problem with your code: You named the function the same as the variable that holds your button.
Apart from that in the most recent version of the fltk crate (v.1.2.23, that I used because you did not specify which version you used in your question) there does not seem to be a Listener<_> type.
Here is an example based on the snippet you posted for changing the label of btn_B:
use fltk::{prelude::{WidgetExt, GroupExt, WidgetBase}, window, app, button, enums};
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application = app::App::default();
let mut win = window::Window::default().with_size(500, 300);
//BTN_A
let mut btn_A = button::Button::new(100, 100, 100, 50, "btn_A");
//BTN_B
let mut btn_B = button::Button::new(300, 100, 100, 50, "btn_B");
//BTN_A_CALLBACK
btn_A.handle(move |elem, evt| match evt {
enums::Event::Push => {
btn_A_click(elem, &mut btn_B);
true
}
_ => false,
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A_click(elem: &mut button::Button, btn_B: &mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
btn_B.set_label("New title.")
}
Also note, that the handle closure now takes ownership of btn_B because of the move keyword.

Is there a way to pass an extra value to the panic handler?

I'm writing a UEFI OS loader, and I use the system table provided by efi_main in the panic handler to print a string on the console. Currently, I'm using a global static variable and a helper function to access it like this:
static SYSTEM_TABLE_WRAPPER: Lazy<Spinlock<Option<SystemTable>>> =
Lazy::new(|| Spinlock::new(None));
#[panic_handler]
fn panic(i: &PanicInfo<'_>) -> ! {
// SAFETY: The existing lock is forgotten. There is no way to access the lock from the panic
// handler.
unsafe { unlock_system_table() }
error!("{}", i);
loop {
x86_64::instructions::hlt();
}
}
pub fn _print(args: fmt::Arguments<'_>) {
let mut st = crate::system_table();
let mut stdout = st.con_out();
let _ = stdout.write_fmt(args);
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("\n");
};
($($arg:tt)*)=>{
$crate::print!("{}\n",core::format_args!($($arg)*));
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {
$crate::io::_print(core::format_args!($($arg)*));
};
}
pub(crate) fn system_table<'a>() -> MappedSpinlockGuard<'a, uefi_wrapper::SystemTable> {
let st = SYSTEM_TABLE_WRAPPER.try_lock();
let st = st.expect("Failed to lock the global System Table.");
SpinlockGuard::map(st, |st| {
let st = st.as_mut();
let st = st.expect("The global System Table is not initialized.");
&mut st.0
})
}
Although this works correctly, I'd like to avoid using any global variables if possible. Is there a way to do that?
I dont think so. If its possible, any parameter would be a global as well. Making it more complex.
Global variables are ok for this. Create your own global panic object and give it to a new panic handler from the real one.
Because there seems to be no way to do that, I changed the way.
Firstly, I defined the uefi_print and uefi_println macros.
#[macro_export]
macro_rules! uefi_print{
($st:expr,$($arg:tt)*)=>{
$crate::io::_print($st,format_args!($($arg)*));
}
}
#[macro_export]
macro_rules! uefi_println {
($st:expr) => {
$crate::uefi_print!($st,"\n");
};
($st:expr,$($arg:tt)*)=>{
$crate::uefi_print!($st,"{}\n",format_args!($($arg)*));
}
}
#[doc(hidden)]
pub fn _print(st: &mut crate::SystemTable, args: fmt::Arguments<'_>) {
let mut con_out = st.con_out();
let _ = con_out.write_fmt(args);
}
These macros are similar to print and println macros, but the first parameter is the mutable reference to SystemTable. The example macro invocation:
#[no_mangle]
pub extern "win64" fn efi_main(h: uefi_wrapper::Handle, mut st: bootx64::SystemTable) -> ! {
let resolution = gop::set_preferred_resolution(&mut st);
uefi_println!(&mut st, "GOP info: {:?}", resolution,);
// ...
}
Secondly, I defined the uefi_panic macro:
#[macro_export]
macro_rules! uefi_panic {
($st:expr) => {
$crate::uefi_panic!($st, "explicit panic");
};
($st:expr,$($t:tt)+)=>{
$crate::uefi_println!($st,"panicked at '{}', {}:{}:{}",core::format_args!($($t)+),file!(),line!(),column!());
panic!();
}
}
#[panic_handler]
fn panic(_: &PanicInfo<'_>) -> ! {
loop {
x86_64::instructions::hlt();
}
}
The first parameter of the macro is also the mutable reference to SystemTable.
Thirdly, I defined a wrapper type for SystemTable that defines the additional method expect_ok.
#[repr(transparent)]
#[derive(Debug)]
pub struct SystemTable(uefi_wrapper::SystemTable);
impl SystemTable {
// ...
/// # Panics
///
/// This method panics if `result` is [`Err`].
pub fn expect_ok<T, E: fmt::Debug>(&mut self, result: Result<T, E>, msg: &str) -> T {
match result {
Ok(val) => val,
Err(e) => {
uefi_panic!(self, "{}: {:?}", msg, e);
}
}
}
}
Now I prefer this way to the previous method. The exit boot services function can move the ownership of SystemTable and ImageHandle. This way prevents these types from being used after calling it. After all, I should not create a global static SystemTable since it won't be valid after exiting the boot services.

How to implement callback on typing in textbox with rust druid (not lens, but a method call)?

I want to call the following method with arguments, either by passing them or from a closure:
fn set_border(&mut self, arg: &str, is_left_border: bool) -> () {
let val = arg.parse::<f64>();
match val {
Ok(float) => { if is_left_border {self.left_border = Some(float)} else {self.right_border = Some(float)}},
Err(_) => {}
}
}
when text is entered to the textbox. I didn't find a way to use lens to access methods, but I'm quite new to rust and decided to ask for advice.
As far as I'm concerned if I can "track" changes of the field and do it that way it will also do.
Thanks in advance.
You can use a Controller to be called when the TextBox receives a call to its update method and then check whether the data has changed:
use druid::{
AppLauncher,
WidgetExt,
Widget,
Env,
UpdateCtx,
WindowDesc,
widget::TextBox,
widget::Controller
};
struct UpdateCallback();
impl Controller<String, TextBox<String>> for UpdateCallback {
fn update(&mut self,
child: &mut TextBox<String>,
ctx: &mut UpdateCtx<'_, '_>,
old_data: &String,
data: &String,
env: &Env
) {
if old_data != data {
// the data has changed, you can call your function here
println!("{}", data);
}
// also inform the child that the data has changed
child.update(ctx, old_data, data, env)
}
}
fn build_root_widget() -> impl Widget<String> {
TextBox::new().controller(UpdateCallback())
}
fn main() {
AppLauncher::with_window(WindowDesc::new(build_root_widget)).launch("Test".to_string()).unwrap();
}
The relevant part here is the Controller impl for UpdateCallback as well as the call to controller() inside the build_root_widget() function.

Pass an mutable instance method to a function

I am teaching myself Rust by creating a toy SDL2 lib for myself.
I created something similar in Go and am trying to port my code across. So far, this is the problem I cannot overcome. I want my library to have callback to a function on the program state so I can have keyboard events sent from my library code to my client code.
The aim is for the keydown events from the SDL keyboard event pump should trigger the on_keydown function on the state object. If I remove the State struct and just use static functions then it works. Of course this prevents me from changing the state of the program based on keyboard actions.
I am trying to use external crates as little as possible.
The relevant parts of the library.
pub enum GameCommand {
Quit,
Continue,
}
pub struct Window {
keydown_event: fn(Event) -> GameCommand,
}
impl Window {
pub fn set_keydown_event(&mut self, f: fn(e: Event) -> GameCommand) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
let mut event_pump = self.game.context.event_pump()?;
'running: loop {
// Handle events
for event in event_pump.poll_iter() {
let mut gc = GameCommand::Continue;
match event {
Event::Quit { .. } => break 'running,
Event::KeyDown { repeat: false, .. } => {
gc = (self.keydown_event)(event);
}
_ => {}
}
if let GameCommand::Quit = gc {
break 'running
}
}
}
Ok(())
}
}
Now the relevant part of the client bin.
struct State {
bgcolor: Color,
}
impl State {
fn on_keydown(&mut self, event: Event) -> GameCommand {
match event {
Event::KeyDown { keycode: Some(Keycode::R), .. } => {
self.bgcolor.r += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::G), .. } => {
self.bgcolor.g += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::B), .. } => {
self.bgcolor.b += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::Escape), ..} => {
GameCommand::Quit
},
_ => GameCommand::Continue,
}
}
}
Now the main function.
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
window.set_keydown_event(state.on_keydown);
Ok(())
}
There is a far bit of code skipped to keep it shortish. The error I get with this code is.
{
"code": "E0615",
"message": "attempted to take value of method `on_keydown` on type `State`\n\nmethod, not a field\n\nhelp: use parentheses to call the method: `(_)`",
}
If I window.set_keydown_event(state.on_keydown); I get this error.
{
"code": "E0308",
"message": "mismatched types\n\nexpected fn pointer, found enum `sdlgame::GameCommand`\n\nnote: expected fn pointer `fn(sdl2::event::Event) -> sdlgame::GameCommand`\n found enum `sdlgame::GameCommand`",
}
I assume the problem is the difference in function signatures. In the set_keydown_event function it expects.
fn(Event) -> GameCommand
Which is why a plain function not associated with a struct works. For the instance method to mutate state it requires the signature.
fn on_keydown(&mut self, event: Event) -> GameCommand
Initially, I am trying to achieve this is a single threaded manner as I am trying to keep things simple for me to reason out. Multi-threading will come later.
Is this possible in Rust and what is the correct way of achieving this result?
Thanks in advance.
Basically, you need to use function traits as well as an explicit closure so the call is bound to the variable. So, you'd change your Window to use a function trait:
// F is now the function type
pub struct Window<F: FnMut(Event) -> GameCommand> {
keydown_event: F,
}
Then you'd change your impl to support that function trait:
// put generic in impl
impl<F: FnMut(Event) -> GameCommand> Window<F> {
// take F as the parameter type now
pub fn set_keydown_event(&mut self, f: F) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
// this function should stay the same
}
}
Then, you'd pass an explicit closure to it:
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
// binds the on_keydown function to the state variable
window.set_keydown_event(|x| state.on_keydown(x));
Ok(())
}

Resources