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()