import owlkettle, owlkettle/[widgetutils, adw] import owlkettle/bindings/gtk import std/[options, os] var counter: int = 0 ## Communication type ChannelHub = ref object serverChannel: Channel[string] clientChannel: Channel[string] proc sendToServer(hub: ChannelHub, msg: string): bool = echo "send client => server: ", msg hub.clientChannel.trySend(msg) proc sendToClient(hub: ChannelHub, msg: string): bool = echo "send client <= server: ", msg hub.serverChannel.trySend(msg) proc readClientMsg(hub: ChannelHub): Option[string] = let response: tuple[dataAvailable: bool, msg: string] = hub.clientChannel.tryRecv() return if response.dataAvailable: echo "read client => server: ", response.repr some(response.msg) else: none(string) proc readServerMsg(hub: ChannelHub): Option[string] = let response: tuple[dataAvailable: bool, msg: string] = hub.serverChannel.tryRecv() result = if response.dataAvailable: echo "read client <= server: ", response.repr some(response.msg) else: none(string) sleep(0) proc hasServerMsg(hub: ChannelHub): bool = hub.serverChannel.peek() > 0 ## Server proc setupServer(channels: ChannelHub): Thread[ChannelHub] = proc serverLoop(hub: ChannelHub) = while true: let msg = hub.readClientMsg() if msg.isSome(): discard hub.sendToClient("Received Message " & $counter) counter.inc sleep(0) # Reduces stress on CPU when idle, increase when higher latency is acceptable for even better idle efficiency createThread(result, serverLoop, channels) ## Client proc addServerListener(app: Viewable, hub: ChannelHub, priority: int = 200) # Forward declaration viewable App: hub: ChannelHub backendMsg: string = "" hooks: afterBuild: addServerListener(state, state.hub) type ListenerData = object hub: ChannelHub app: Viewable proc addServerListener(app: Viewable, hub: ChannelHub, priority: int = 200) = echo "Setup Server Listener" proc serverMessageListener(data: pointer): cbool {.cdecl.} = let cell = cast[ptr ListenerData](data) let listenerData = cell[] if listenerData.hub.hasServerMsg(): discard listenerData.app.redraw() return true.cbool let data = allocSharedCell(ListenerData(hub: hub, app: app)) discard g_idle_add_full(cint(priority), serverMessageListener, data, nil) method view(app: AppState): Widget = let msg: Option[string] = app.hub.readServerMsg() if msg.isSome(): app.backendMsg = msg.get() result = gui: Window: defaultSize = (500, 150) title = "Client Server Example" Box: orient = OrientY margin = 12 spacing = 6 Button {.hAlign: AlignCenter, vAlign: AlignCenter.}: Label(text = "Click me") proc clicked() = discard app.hub.sendToServer("Frontend message!") Label(text = "Message sent by Backend: ") Label(text = app.backendMsg) proc setupClient(channels: ChannelHub): Thread[ChannelHub] = proc startOwlkettle(hub: ChannelHub) = adw.brew(gui(App(hub = hub))) createThread(result, startOwlkettle, channels) proc main() = var serverToClientChannel: Channel[string] var clientToServerChannel: Channel[string] serverToClientChannel.open() clientToServerChannel.open() let hub = ChannelHub(serverChannel: serverToClientChannel, clientChannel: clientToServerChannel) let client = setupClient(hub) let server = setupServer(hub) joinThreads(server, client) main()