I'm currently working on writing up a tui for a server application in a personal project, and I picked the cursive library to do it. I have run into a problem with a component of my application that is supposed to serve as the server's console window. In setting up the ability to output text to the window, I've run into a problem where I can't update the TextView.
According to the last comment in this issue, the way to do this is to use a TextContent and update that, which I set up below. While the initial content will show up in the UI, no updates to the content do. I have tried setting content on the TextView directly by giving it a name, but no luck. I've been able to get_content on the TextView itself and see that the content has been updated, it just isn't reflected in the UI.
Is there any way to do this, or do I need to make a custom view for the output?
Here is the code in question:
use std::ops::Add;
use std::borrow::{BorrowMut, Borrow};
use cursive::{View, Printer, Vec2, Rect, With};
use cursive::event::{Event, EventResult, AnyCb, Key};
use cursive::view::{Selector, ViewNotFound, ViewWrapper};
use cursive::direction::Direction;
use cursive::views::{EditView, LinearLayout, OnEventView, DummyView, TextView, TextContent};
use cursive::traits::{Nameable, Scrollable, Resizable};
use cursive::view::scroll::Scroller;
pub const CONSOLE_INPUT_NAME: &str = "console_input";
pub const CONSOLE_OUTPUT_LENGTH: u16 = 1000;
pub(crate) struct ConsoleView {
inner_view: LinearLayout,
console_output: TextContent,
console_output_history: LinkedList<String>
}
impl Default for ConsoleView {
fn default() -> Self {
let console_output = TextContent::new("This content should get replaced.");
let inner_view = LinearLayout::vertical()
.child(TextView::new_with_content(console_output.clone())
.full_screen()
.scrollable()
.wrap_with(OnEventView::new)
.on_pre_event_inner(Key::PageUp, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_up() {
scroller.scroll_up(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
})
.on_pre_event_inner(Key::PageDown, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_down() {
scroller.scroll_down(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
}))
.child(DummyView)
.child(EditView::new().with_name(CONSOLE_INPUT_NAME));
return Self {
inner_view,
console_output,
console_output_history: LinkedList::new()
}
}
}
impl ViewWrapper for ConsoleView {
type V = LinearLayout;
fn with_view<F, R>(&self, f: F) -> Option<R> where F: FnOnce(&Self::V) -> R {
return Some(f(self.inner_view.borrow()));
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R> where F: FnOnce(&mut Self::V) -> R {
return Some(f(self.inner_view.borrow_mut()));
}
fn into_inner(self) -> Result<Self::V, Self> where Self: Sized, Self::V: Sized {
return Ok(self.inner_view);
}
fn wrap_draw(&self, printer: &Printer) {
self.inner_view.draw(printer);
}
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
return req;
}
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
return self.inner_view.on_event(ch);
}
fn wrap_layout(&mut self, size: Vec2) {
self.inner_view.layout(size);
}
fn wrap_take_focus(&mut self, source: Direction) -> bool {
return self.inner_view.take_focus(source);
}
fn wrap_call_on_any<'a>(&mut self, selector: &Selector<'_>, callback: AnyCb<'a>) {
self.inner_view.call_on_any(selector, callback);
}
fn wrap_focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ViewNotFound> {
return self.inner_view.focus_view(selector);
}
fn wrap_needs_relayout(&self) -> bool {
return self.inner_view.needs_relayout();
}
fn wrap_important_area(&self, size: Vec2) -> Rect {
return self.inner_view.important_area(size);
}
}
impl ConsoleView {
pub fn print(&mut self, line: &str) {
log::info!("Printing to console: {}", line);
self.console_output_history.push_back(line.to_string());
if self.console_output_history.len() > CONSOLE_OUTPUT_LENGTH as usize {
self.console_output_history.pop_front();
}
let out = self.console_output_history.iter().fold("".to_string(), |acc, s| {
return acc.add(s.as_str()).add("\n");
});
self.console_output.set_content(out);
}
}
Here is a main() to go with to demonstrate the problem. Basically, once the UI is set up and the event loop is started, I can't update the information displayed in the UI. Given that the UI doesn't display until I start the event loop, this is a problem.
let mut siv = cursive::default();
siv.add_layer(ConsoleView::default().with_name("view_name"));
siv.add_global_callback(Key::Esc, |s| s.quit());
siv.run();
siv.call_on_name("view_name", |view: &mut ConsoleView| {
view.print("I should see this text in the app.");
});
}
Related
I have the goal of wrapping an Iterator<Item = rusb::Device<_> to Iterator<Item = LitraDevice>. The latter contains specific implementation.
To make this work I tried the following code:
use std::iter::Filter;
use rusb;
const VENDOR: u16 = 0x046d;
const PRODUCT: u16 = 0xc900;
struct LitraDevice {
dev: rusb::Device<rusb::GlobalContext>,
}
pub struct LitraDevices {
unfiltered: rusb::DeviceList<rusb::GlobalContext>,
}
struct LitraDeviceIterator<'a> {
it: Filter<rusb::Devices<'a, rusb::GlobalContext>, for<'r> fn(&'r rusb::Device<rusb::GlobalContext>) -> bool>,
}
impl LitraDevices {
pub fn new() -> Self {
let unfiltered = rusb::devices().unwrap();
LitraDevices { unfiltered }
}
fn can_not_handle<'r>(dev: &'r rusb::Device<rusb::GlobalContext>) -> bool {
let desc = dev.device_descriptor().unwrap();
match (desc.vendor_id(), desc.product_id()) {
(VENDOR, PRODUCT) => (),
_ => return true,
}
match desc.class_code() {
LIBUSB_CLASS_HID => return true, // Skip HID devices, they are handled directly by OS libraries
_ => return false,
}
}
pub fn iter<'a>(self) -> LitraDeviceIterator<'a> {
let it = self.unfiltered.iter().filter(Self::can_not_handle);
LitraDeviceIterator{
it,
}
}
}
impl <'a> Iterator for LitraDeviceIterator<'a> {
type Item = LitraDevice;
fn next(&mut self) -> Option<Self::Item> {
let n = self.it.next();
match n {
Some(Device) => return Some(LitraDevice{dev: n.unwrap()}),
None => return None,
}
}
}
Now I really cannot figure out how to code LitraDeviceIterator so that it wraps the filtered iterator.
All code iterations I have tried so far turn into a generic nightmare very quickly.
I rewrote your iter() to yield LitraDevice, you can surely take it wherever you wanted to go from there.
The first underlying issue is that filter() yields references, but in cases like these, you actually mean to move yielded items while filtering. That's what filter_map() is capable of. That way, you can scrap the references, greatly simplifying your code.
(This code does not work yet, read on)
pub fn iter(self) -> impl Iterator<Item = LitraDevice> {
self.unfiltered.iter().filter_map(|dev| {
(!Self::can_not_handle(&dev))
.then_some(dev)
.map(|dev| LitraDevice { dev })
})
}
Now, there's a second little issue at play her: rusb::DeviceList<T : UsbContext>>::iter(&self) returns rusb::Devices<'_, T>, '_ being the anonymous lifetime inferred from &self. Meaning, while you can drive rusb::Devices<'_, T> to yield Device<T>s, you can not actually keep it around longer than self.unfiltered. More specifically, as you consume self in iter(), you can not return an iterator referencing that rusb::Devices<'_, T> from iter(). One solution is to immediately collect, then again moving into an iterator.
pub fn iter(self) -> impl Iterator<Item = LitraDevice> {
let devices = self.unfiltered.iter().collect::<Vec<_>>();
devices.into_iter().filter_map(|dev| {
(!Self::can_not_handle(&dev))
.then_some(dev)
.map(|dev| LitraDevice { dev })
})
}
I've been playing around with Rust by following along with Roguelike Tutorial, and have started to branch out a bit in hopes of creating some kind of nature simulation.
In my simple POC, I'm trying to have multiple "Creatures" wandering the map looking for entities with the ProvidesHealth component (so like plants or bushes or something that get eaten).
In the Roguelike tutorial, monsters can easily locate the player at all times because the player is shared throughout the world as a resource, but in my case, I can't figure out the best way to simulate this behavior in my Specs system.
The Creature entities have a Viewshed component to act as their vision. I originally thought I'd be able to iterate thru the Viewshed's visible_tiles and check if an entity with the ProvidesHealth entity was there, but I wasn't able to get that work.
Any thoughts on this would be greatly appreciated! I'm not sure if my approach is totally off, or I'm missing something simple.
Thanks!
// [dependencies]
// bracket-lib = { git = "https://github.com/thebracket/bracket-lib.git", rev = "927d229" }
// specs = "0.16.1"
// specs-derive = "0.4.1"
use bracket_lib::prelude::*;
use specs::prelude::*;
use specs_derive::*;
use std::{thread, time};
#[derive(Component)]
struct Position {
x: i32,
y: i32,
}
#[derive(Component)]
struct Renderable {
glyph: FontCharType,
fg: RGB,
bg: RGB,
}
#[derive(Component)]
struct Creature {}
#[derive(Component)]
struct ProvidesHealth {
pub hp_gain: i32
}
#[derive(Component)]
pub struct Viewshed {
pub visible_tiles: Vec<Point>,
pub range: i32,
pub dirty: bool
}
struct State {
ecs: World
}
impl State {
fn run_systems(&mut self) {
let mut vis = VisSystem {};
vis.run_now(&self.ecs);
let mut ai = CreatureAI {};
ai.run_now(&self.ecs);
self.ecs.maintain();
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut BTerm) {
ctx.cls();
self.run_systems();
// map defined in separate file, but isn't really
// important for this question
// draw_map(&self.ecs, ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
for (pos, ren) in (&positions, &renderables).join() {
ctx.set(pos.x, pos.y, ren.fg, ren.bg, ren.glyph);
}
let sleep = time::Duration::from_millis(200);
thread::sleep(sleep);
}
}
fn main() -> BError {
let mut context = BTermBuilder::simple80x50()
.build()?;
let mut gs = State {
ecs: World::new(),
};
gs.ecs.register::<Position>();
gs.ecs.register::<Renderable>();
gs.ecs.register::<Creature>();
gs.ecs.register::<ProvidesHealth>();
gs.ecs.register::<Viewshed>();
// add one Creature
gs.ecs
.create_entity()
.with(Position {x: 10, y: 20})
.with(Renderable {
glyph: to_cp437('#'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK)
})
.with(Creature {})
.with(Viewshed { visible_tiles : Vec::new(), range: 6, dirty: true })
.with(HealthStats { max_hp: 100, hp: 100 })
.build();
// add one "food" item
gs.ecs
.create_entity()
.with(Position {x: 35, y: 35})
.with(Renderable {
glyph: to_cp437('*'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK),
})
.with(ProvidesHealth { hp_gain: 10 })
.build();
// map defined in separate file, but isn't really
// important for this question
let mut map = Map::new_map();
gs.ecs.insert(map);
main_loop(context, gs)
}
struct VisSystem {}
impl<'a> System<'a> for VisSystem {
type SystemData = (
WriteExpect<'a, Map>,
Entities<'a>,
WriteStorage<'a, Viewshed>,
ReadStorage<'a, Position>,
);
fn run(&mut self, data: Self::SystemData) {
let (map, entities, mut viewshed, pos) = data;
for (_ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if viewshed.dirty {
viewshed.visible_tiles = field_of_view(
Point::new(pos.x, pos.y),
viewshed.range,
&*map,
)
}
}
}
}
struct CreatureAI {}
impl<'a> System<'a> for CreatureAI {
#[allow(clippy::type_complexity)]
type SystemData = (
// ...
);
fn run(&mut self, data: Self::SystemData) {
// ... not sure what to do here\
//
// by doing a join on (viewshed, position),
// i'd be able to iterate thru viewshed.visible_tiles,
// but i can't figure out how I could check if a given
// entity located at the Point has the "ProvidesHealth"
// component or not
}
}
I'm using the tracing library in my project and there is one thing I'm not able to figure out: How can I access a value (that I set in my span when I create it) in my Layer?
My layer looks like this:
impl<S> Layer<S> for CustomLayer where S: Subscriber {
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
Interest::sometimes() //hardcoding so enabled() will be called everytime a span is created
}
fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
if metadata.is_span() {
// How do I access value of key here?
if value == X {
true
} else if value == Y {
false
}
}
true // default
}
}
You can access the data in a Span if you have access to either its ValueSet (as found in new_span() or on_new_span() via Attributes) or a Record entry for it (as found in record() or on_record()). With that you can use the visitor pattern to find the information you desire. Here's a simple implementation that checks if a field exists and its value is a matching string:
use std::fmt::Debug;
use tracing::field::{ValueSet, Visit, Field};
use tracing::span::Record;
struct MatchStrVisitor<'a> {
field: &'a str,
value: &'a str,
matched: bool,
}
impl Visit for MatchStrVisitor<'_> {
fn record_debug(&mut self, _field: &Field, _value: &dyn Debug) {}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == self.field && value == self.value {
self.matched = true;
}
}
}
fn value_in_valueset(valueset: &ValueSet<'_>, field: &str, value: &str) -> bool {
let mut visitor = MatchStrVisitor { field, value, matched: false };
valueset.record(&mut visitor);
visitor.matched
}
fn value_in_record(record: &Record<'_>, field: &str, value: &str) -> bool {
let mut visitor = MatchStrVisitor { field, value, matched: false };
record.record(&mut visitor);
visitor.matched
}
This is pretty rudimentary but hopefully demonstrates what is possible. One thing to note is that the "value" that is stored is either a primitive value (i64, u64, bool, str, etc.) or in a type-erased form via &dyn Debug. Those are the only types of values you can receive from the visitor.
Addressing OP's case in particular, as explained in this issue you cannot access this information in the enabled() method since that occurs before any values are recorded. You will need to make your determination in the new_span() method, and use span extensions via the registry to track whether you consider the span is "enabled" in your other methods.
Here's another rudimentary example:
use tracing::span::Attributes;
use tracing::{Subscriber, Metadata, Id, Event};
use tracing::subscriber::Interest;
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
struct CustomLayer;
struct CustomLayerEnabled;
impl<S> Layer<S> for CustomLayer where S: Subscriber + for <'a> LookupSpan<'a> {
fn register_callsite(&self, _metadata: &'static Metadata<'static>) -> Interest {
Interest::sometimes()
}
fn enabled(&self, metadata: &Metadata<'_>, _ctx: Context<'_, S>) -> bool {
metadata.is_span()
}
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
if value_in_valueset(attrs.values(), "myfield", "myvalue") {
ctx.span(id).unwrap().extensions_mut().insert(CustomLayerEnabled);
}
}
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
let span_id = event.parent().unwrap();
if let None = ctx.span(span_id).unwrap().extensions().get::<CustomLayerEnabled>() {
return;
}
// ... rest of your logic
}
}
Note: I've completely rewritten this answer taking info from the comments and my newfound experience.
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(())
}
I'm trying to create some sets of Strings and then merge some of these sets so that they have the same tag (of type usize). Once I initialize the map, I start adding strings:
self.clusters.make_set("a");
self.clusters.make_set("b");
When I call self.clusters.find("a") and self.clusters.find("b"), different values are returned, which is fine because I haven't merged the sets yet. Then I call the following method to merge two sets
let _ = self.clusters.union("a", "b");
If I call self.clusters.find("a") and self.clusters.find("b") now, I get the same value. However, when I call the finalize() method and try to iterate through the map, the original tags are returned, as if I never merged the sets.
self.clusters.finalize();
for (address, tag) in &self.clusters.map {
self.clusterizer_writer.write_all(format!("{};{}\n", address,
self.clusters.parent[*tag]).as_bytes()).unwrap();
}
// to output all keys with the same tag as a list.
let a: Vec<(usize, Vec<String>)> = {
let mut x = HashMap::new();
for (k, v) in self.clusters.map.clone() {
x.entry(v).or_insert_with(Vec::new).push(k)
}
x.into_iter().collect()
};
I can't figure out why this is the case, but I'm relatively new to Rust; maybe its an issue with pointers?
Instead of "a" and "b", I'm actually using something like utils::arr_to_hex(&input.outpoint.txid) of type String.
This is the Rust implementation of the Union-Find algorithm that I am using:
/// Tarjan's Union-Find data structure.
#[derive(RustcDecodable, RustcEncodable)]
pub struct DisjointSet<T: Clone + Hash + Eq> {
set_size: usize,
parent: Vec<usize>,
rank: Vec<usize>,
map: HashMap<T, usize>, // Each T entry is mapped onto a usize tag.
}
impl<T> DisjointSet<T>
where
T: Clone + Hash + Eq,
{
pub fn new() -> Self {
const CAPACITY: usize = 1000000;
DisjointSet {
set_size: 0,
parent: Vec::with_capacity(CAPACITY),
rank: Vec::with_capacity(CAPACITY),
map: HashMap::with_capacity(CAPACITY),
}
}
pub fn make_set(&mut self, x: T) {
if self.map.contains_key(&x) {
return;
}
let len = &mut self.set_size;
self.map.insert(x, *len);
self.parent.push(*len);
self.rank.push(0);
*len += 1;
}
/// Returns Some(num), num is the tag of subset in which x is.
/// If x is not in the data structure, it returns None.
pub fn find(&mut self, x: T) -> Option<usize> {
let pos: usize;
match self.map.get(&x) {
Some(p) => {
pos = *p;
}
None => return None,
}
let ret = DisjointSet::<T>::find_internal(&mut self.parent, pos);
Some(ret)
}
/// Implements path compression.
fn find_internal(p: &mut Vec<usize>, n: usize) -> usize {
if p[n] != n {
let parent = p[n];
p[n] = DisjointSet::<T>::find_internal(p, parent);
p[n]
} else {
n
}
}
/// Union the subsets to which x and y belong.
/// If it returns Ok<u32>, it is the tag for unified subset.
/// If it returns Err(), at least one of x and y is not in the disjoint-set.
pub fn union(&mut self, x: T, y: T) -> Result<usize, ()> {
let x_root;
let y_root;
let x_rank;
let y_rank;
match self.find(x) {
Some(x_r) => {
x_root = x_r;
x_rank = self.rank[x_root];
}
None => {
return Err(());
}
}
match self.find(y) {
Some(y_r) => {
y_root = y_r;
y_rank = self.rank[y_root];
}
None => {
return Err(());
}
}
// Implements union-by-rank optimization.
if x_root == y_root {
return Ok(x_root);
}
if x_rank > y_rank {
self.parent[y_root] = x_root;
return Ok(x_root);
} else {
self.parent[x_root] = y_root;
if x_rank == y_rank {
self.rank[y_root] += 1;
}
return Ok(y_root);
}
}
/// Forces all laziness, updating every tag.
pub fn finalize(&mut self) {
for i in 0..self.set_size {
DisjointSet::<T>::find_internal(&mut self.parent, i);
}
}
}
I think you're just not extracting the information out of your DisjointSet struct correctly.
I got sniped by this and implemented union find. First, with a basic usize implemention:
pub struct UnionFinderImpl {
parent: Vec<usize>,
}
Then with a wrapper for more generic types:
pub struct UnionFinder<T: Hash> {
rev: Vec<Rc<T>>,
fwd: HashMap<Rc<T>, usize>,
uf: UnionFinderImpl,
}
Both structs implement a groups() method that returns a Vec<Vec<>> of groups. Clone isn't required because I used Rc.
Playground