I’ve created a websocket server using Rust Axum crate. Now I need an UI, which can show live activity and updates from this server. I’m trying to use gtk4-rs for GUI part. But I’m not really sure how to properly architect this app so that the app state can be shared between and consumed by both the websocket server and GTK. The shared state could be updated by GTK widgets (for storing user inputs) and also by WS server (messages received from connected clients) simultaneously.
I thought of creating a shared struct object which uses Arc<Mutex<T>>
but then how does UI get updated (notified for updating itself) when a particular state update is pushed from WS server side. I guess, we can’t store this shared struct in GObject
and take advantage of its properties and signals, because it is not thread-safe and thus can’t be shared with WS server.
Another approach is to use channels to communicate updates between GTK and WS server but then we need to manage two copies of state, one for each side. This approach seems more practical to me but what about duplicating the state and state getting out of sync. Also, communicating multiple types of updates (messages) with their data won’t be easy.
This question was initially asked on gtk-rs matrix chat, but as this is more generic and about synchronization between multiple decentralized clients in Rust. They suggested me to ask in Rust forum.
Here are some excerpts from the chat for more info:
Me: "Ok. I'll duplicate app state in Axum and GTK (store in a single
GObject
and update the UI with properties, signals, expressions etc.) and use tokio's broadcast channel to communicate state changes bidirectionally. Is this correct? Thanks."
Member 1: "why would you duplicate state?"
Me: " Because if I store it in shared
Arc<Mutex>
, then how do I automatically update UI when it is updated by the server? Do I need to poll the state in UI? If I store the state inGObject
then it can't be mutated or accessed by server as it is not thread-safe and hence can't be shared with server."
Member 1: "you could send a message to the UI thread whenever the state changes and then it updates itself"
Me: "Yes, that's what causing me difficulty. Suppose, the GUI shows all the WS clients connected to the server and also messages received from them. It also allows user to send message to any connected client by selecting it. So, for this to work GUI needs to store references/ids of clients, so they can be communicated back. Also, GUI needs to store all the messages of all the clients.
The WS server can also reply automatically to client based on their last message content.
Now, when a message is received from a client, then this message is communicated to UI thread via channel and pushed to corresponding client's history of messages in GUI state.
Another case, when user tries to send a message to a particular WS client, then this messages is communicated to WS server via same channel and ultimately sent to the client by WS server. Now for this to work, WS server needs to manage list of connected clients, so that when GUI sends a message with a client id, then server can forward it to corresponding client.
That's why I'm saying we're maintaining state both in GUI and server. Here list of connected clients needs to be duplicated and kept in sync between both GUI and server.
Thanks for your replies."
Member 2: "This problem you are trying to solve is really not related to gtk at all. You are trying to solve a synchronisation problem between multiple decentralized clients. This is a very difficult problem that requires highly specialized solutions.
You can of course propagate state within an app by using property bindings and signals. But how you get the state to your client and back is not really something we have any stock solutions for. You might be more successful in asking that question in a more generic rust channel or forum :)"
Me: "I understand that this problem does not specifically target GTK. I'm sure you guys are expert Rust developers too as you are providing Rust bindings for a complex GUI project.
Anyways, I just need someone to guide me in right direction or provide me pointers to resources which can help me tackle this problem. Is there any example or open-source project which uses similar pattern?
I guess, this kind of problem is not very special and should have been encountered by others in past and have solutions written for it.
I tried to ask this question on stackoverflow too, but there were no helpful answer too. Some people told me to separate the server and GUI and use IPC with some networking protocol but I don't understand how this can benefit or simplify the architecture of app. We'll need to use message passing anyway and it'll complicate things more in my opinion.
I'll be very grateful if someone help me find solution for this or maybe write an example in gtk4-rs repo so that whole community can benefit. It'll help more users to adopt gtk4-rs easily and provide comprehensive understanding of it.
Asking for more from open-source developers is rude sometimes but I really appreciate all of your hard work."
Sorry for the very long post. Your answer is highly appreciated.