fix: signal handler integration with status-go
This commit is contained in:
parent
acf0dcbf7a
commit
a80a6c623b
|
@ -1,12 +1,6 @@
|
|||
[submodule "vendor/DOtherSide"]
|
||||
path = vendor/DOtherSide
|
||||
url = https://github.com/filcuc/DOtherSide
|
||||
[submodule "vendor/nim-stint"]
|
||||
path = vendor/nim-stint
|
||||
url = https://github.com/status-im/nim-stint
|
||||
[submodule "vendor/nimqml"]
|
||||
path = vendor/nimqml
|
||||
url = https://github.com/filcuc/nimqml/
|
||||
[submodule "vendor/nimbus-build-system"]
|
||||
path = vendor/nimbus-build-system
|
||||
url = https://github.com/status-im/nimbus-build-system
|
||||
|
@ -19,3 +13,9 @@
|
|||
[submodule "vendor/status-go"]
|
||||
path = vendor/status-go
|
||||
url = https://github.com/status-im/status-go
|
||||
[submodule "vendor/DOtherSide"]
|
||||
path = vendor/DOtherSide
|
||||
url = https://github.com/status-im/DOtherSide
|
||||
[submodule "vendor/nimqml"]
|
||||
path = vendor/nimqml
|
||||
url = https://github.com/status-im/nimqml
|
||||
|
|
|
@ -4,11 +4,13 @@ import NimQml
|
|||
QtObject:
|
||||
type ApplicationView* = ref object of QObject
|
||||
app: QApplication
|
||||
lastMessage: string
|
||||
|
||||
# Constructor
|
||||
proc newApplicationView*(app: QApplication): ApplicationView =
|
||||
new(result)
|
||||
result.app = app
|
||||
result.lastMessage = ""
|
||||
result.setup()
|
||||
|
||||
proc setup(self: ApplicationView) =
|
||||
|
@ -20,3 +22,17 @@ QtObject:
|
|||
proc onExitTriggered(self: ApplicationView) {.slot.} =
|
||||
echo "exiting..."
|
||||
self.app.quit
|
||||
|
||||
proc lastMessage*(self: ApplicationView): string {.slot.} =
|
||||
result = self.lastMessage
|
||||
|
||||
proc receivedMessage*(self: ApplicationView, lastMessage: string) {.signal.}
|
||||
|
||||
proc setLastMessage(self: ApplicationView, lastMessage: string) {.slot.} =
|
||||
self.lastMessage = lastMessage
|
||||
self.receivedMessage(lastMessage)
|
||||
|
||||
QtProperty[string] lastMessage:
|
||||
read = lastMessage
|
||||
write = setLastMessage
|
||||
notify = receivedMessage
|
||||
|
|
|
@ -4,19 +4,30 @@ import app/chat/core as chat
|
|||
import app/wallet/core as wallet
|
||||
import app/node/core as node
|
||||
import state
|
||||
|
||||
import strformat
|
||||
import strutils
|
||||
import status/core as status
|
||||
# import status/chat as status_chat
|
||||
import status/chat as status_chat
|
||||
import status/test as status_test
|
||||
import status/types
|
||||
import status/types as types
|
||||
import status/wallet as status_wallet
|
||||
import status/libstatus
|
||||
import state
|
||||
|
||||
|
||||
|
||||
proc mainProc() =
|
||||
# From QT docs:
|
||||
# For any GUI application using Qt, there is precisely one QApplication object,
|
||||
# no matter whether the application has 0, 1, 2 or more windows at any given time.
|
||||
# For non-QWidget based Qt applications, use QGuiApplication instead, as it does
|
||||
# not depend on the QtWidgets library. Use QCoreApplication for non GUI apps
|
||||
|
||||
# Global variables required due to issue described on line 75
|
||||
var app = newQApplication()
|
||||
var logic = newApplicationView(app)
|
||||
var logicQObjPointer = cast[pointer](logic.vptr)
|
||||
|
||||
proc mainProc() =
|
||||
defer: app.delete() # Defer will run this just before mainProc() function ends
|
||||
|
||||
var engine = newQQmlApplicationEngine()
|
||||
|
@ -25,16 +36,15 @@ proc mainProc() =
|
|||
var appState = state.newAppState()
|
||||
echo appState.title
|
||||
|
||||
status.init(appState)
|
||||
# TODO: @RR: commented until I'm able to fix the global variable issue described below
|
||||
#status.init(appState)
|
||||
|
||||
status_test.setupNewAccount()
|
||||
discard status_test.addPeer("enode://2c8de3cbb27a3d30cbb5b3e003bc722b126f5aef82e2052aaef032ca94e0c7ad219e533ba88c70585ebd802de206693255335b100307645ab5170e88620d2a81@47.244.221.14:443")
|
||||
echo status.callPrivateRPC("{\"jsonrpc\":\"2.0\", \"method\":\"wakuext_requestMessages\", \"params\":[{\"topics\": [\"0x7998f3c8\"]}], \"id\": 1}")
|
||||
|
||||
let applicationView = newApplicationView(app)
|
||||
defer: applicationView.delete
|
||||
|
||||
status.startMessenger()
|
||||
let logicVariant = newQVariant(logic)
|
||||
defer: logic.delete
|
||||
defer: logicVariant.delete
|
||||
engine.setRootContextProperty("logic", logicVariant)
|
||||
|
||||
var wallet = wallet.newController()
|
||||
wallet.init()
|
||||
|
@ -48,8 +58,6 @@ proc mainProc() =
|
|||
node.init()
|
||||
engine.setRootContextProperty("nodeModel", node.variant)
|
||||
|
||||
engine.load("../ui/main.qml")
|
||||
|
||||
appState.subscribe(proc () =
|
||||
# chatsModel.names = @[]
|
||||
for channel in appState.channels:
|
||||
|
@ -61,15 +69,26 @@ proc mainProc() =
|
|||
appState.addChannel("test")
|
||||
appState.addChannel("test2")
|
||||
|
||||
# EXAMPLE: this will be triggered once a message is received
|
||||
appState.onSignal(SignalType.Message, proc(myMessage: string): void =
|
||||
echo "I received a message: ", myMessage
|
||||
);
|
||||
engine.load("../ui/main.qml")
|
||||
|
||||
|
||||
# In order for status-go to be able to trigger QT events
|
||||
# the signal handler must work with the same pointers
|
||||
# and use the cdecl pragma. If I remove this pragma and use
|
||||
# a normal closure and the `logic` object, the pointer to
|
||||
# this `logic` changes each time the callback is executed
|
||||
# I also had to use a global variable, because Nim complains
|
||||
# "illegal capture 'logicQObjPointer' because ':anonymous'
|
||||
# has the calling convention: <cdecl>"
|
||||
# TODO: ask nimbus team how to work with raw pointers to avoid
|
||||
# using global variables
|
||||
var callback:SignalCallback = proc(p0: cstring) {.cdecl.} =
|
||||
setupForeignThreadGc()
|
||||
signal_handler(logicQObjPointer, p0, "setLastMessage")
|
||||
tearDownForeignThreadGc()
|
||||
|
||||
libstatus.setSignalEventCallback(callback)
|
||||
|
||||
# Handle signals as part of the state
|
||||
var signalWorker: Thread[AppState]
|
||||
signalWorker.createThread(proc(s:AppState) = s.processSignals, appState)
|
||||
defer: signalWorker.joinThread()
|
||||
|
||||
# Qt main event loop is entered here
|
||||
# The termination of the loop will be performed when exit() or quit() is called
|
||||
|
|
|
@ -11,27 +11,13 @@ type
|
|||
|
||||
Subscriber* = proc ()
|
||||
|
||||
SignalSubscriber* = proc(p0: string)
|
||||
|
||||
Signal = object
|
||||
signalType*: SignalType
|
||||
content*: string
|
||||
|
||||
var signalChannel: Channel[Signal]
|
||||
|
||||
type AppState* = ref object
|
||||
title*: string
|
||||
channels*: seq[ChatChannel]
|
||||
subscribers*: seq[Subscriber]
|
||||
signalSubscribers*: Table[SignalType, seq[SignalSubscriber]]
|
||||
|
||||
|
||||
proc newAppState*(): AppState =
|
||||
result = AppState(
|
||||
title: "hello",
|
||||
signalSubscribers: initTable[SignalType, seq[SignalSubscriber]]()
|
||||
)
|
||||
signalChannel.open()
|
||||
result = AppState(title: "hello")
|
||||
|
||||
proc subscribe*(self: AppState, subscriber: Subscriber) =
|
||||
self.subscribers.add(subscriber)
|
||||
|
@ -43,35 +29,3 @@ proc dispatch*(self: AppState) =
|
|||
proc addChannel*(self: AppState, name: string) =
|
||||
self.channels.add(ChatChannel(name: name))
|
||||
self.dispatch()
|
||||
|
||||
#####################
|
||||
# Signal Handling
|
||||
|
||||
proc processSignals*(self: AppState) =
|
||||
## Polls the signal channel and push the message to each subscriber
|
||||
{.gcsafe.}:
|
||||
while(true):
|
||||
let tried = signalChannel.tryRecv()
|
||||
if tried.dataAvailable and self.signalSubscribers.hasKey(tried.msg.signalType):
|
||||
for subscriber in self.signalSubscribers[tried.msg.signalType]:
|
||||
subscriber(tried.msg.content)
|
||||
defer:
|
||||
signalChannel.close()
|
||||
|
||||
proc addToChannel(s: Signal) {.thread.} =
|
||||
signalChannel.send(s)
|
||||
|
||||
proc nextSignal*(self: AppState, signalType: SignalType, jsonMessage: string) =
|
||||
## This is called by the signal handler for each signal received and
|
||||
## adds it to the signal channel for being consumed by the SignalSubscribers
|
||||
let signal: Signal = Signal(signalType: signalType, content: jsonMessage)
|
||||
var worker: Thread[Signal]
|
||||
createThread(worker, addToChannel, signal)
|
||||
worker.joinThread()
|
||||
|
||||
proc onSignal*(self: AppState, signalType: SignalType, subscriber: SignalSubscriber) =
|
||||
## Register a callback that will be executed once
|
||||
## a signal is received from status-go
|
||||
if not self.signalSubscribers.hasKey(signalType):
|
||||
self.signalSubscribers[signalType] = @[]
|
||||
self.signalSubscribers[signalType].add(subscriber)
|
|
@ -1,14 +1,9 @@
|
|||
import libstatus
|
||||
import signals
|
||||
import types
|
||||
import chat
|
||||
import "../state"
|
||||
|
||||
proc setSignalHandler(signalHandler: SignalCallback) =
|
||||
libstatus.setSignalEventCallback(signalHandler)
|
||||
|
||||
proc init*(state: AppState) =
|
||||
setSignalHandler(onSignal(state))
|
||||
# TODO: move signal handler from nim_status_client.nim
|
||||
# proc init*(state: AppState) =
|
||||
# setSignalHandler(onSignal(state))
|
||||
|
||||
proc startMessenger*() =
|
||||
chat.startMessenger()
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import types
|
||||
import json
|
||||
import "../state" as state
|
||||
|
||||
proc onSignal*(state: AppState): SignalCallback =
|
||||
result = proc(p0: cstring): void =
|
||||
setupForeignThreadGc()
|
||||
let jsonSignal = ($p0).parseJson
|
||||
let signalType = $jsonSignal["type"].getStr
|
||||
|
||||
# TODO: ideally the signal would receive an object
|
||||
# formatted for easier usage
|
||||
case signalType:
|
||||
of "messages.new":
|
||||
state.nextSignal(SignalType.Message, $jsonSignal)
|
||||
else:
|
||||
state.nextSignal(SignalType.Unknown, $jsonSignal)
|
||||
|
||||
|
||||
tearDownForeignThreadGc()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
type SignalCallback* = proc(eventMessage: cstring): void
|
||||
type SignalCallback* = proc(eventMessage: cstring): void {.cdecl.}
|
||||
|
||||
type SignalType* {.pure.} = enum
|
||||
Message = "messages.new"
|
||||
|
|
48
ui/main.qml
48
ui/main.qml
|
@ -5,6 +5,7 @@ import Qt.labs.platform 1.1
|
|||
import "./onboarding"
|
||||
import "./app"
|
||||
|
||||
|
||||
ApplicationWindow {
|
||||
id: applicationWindow
|
||||
width: 1232
|
||||
|
@ -13,45 +14,16 @@ ApplicationWindow {
|
|||
visible: true
|
||||
font.family: "Inter"
|
||||
|
||||
SystemTrayIcon {
|
||||
visible: true
|
||||
icon.source: "shared/img/status-logo.png"
|
||||
menu: Menu {
|
||||
MenuItem {
|
||||
text: qsTr("Quit")
|
||||
onTriggered: Qt.quit()
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
applicationWindow.show()
|
||||
applicationWindow.raise()
|
||||
applicationWindow.requestActivate()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rctAppBg
|
||||
color: "#FFFFFF"
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
anchors.fill: parent
|
||||
border.width: 0
|
||||
|
||||
Intro {
|
||||
id: onboarding
|
||||
visible: !app.visible
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
AppMain {
|
||||
id: app
|
||||
// TODO: Set this to a logic result determining when we need to show the onboarding screens
|
||||
// Set to true to hide the onboarding screens manually
|
||||
// Set to false to show the onboarding screens manually
|
||||
visible: false // logic.accountResult !== ""
|
||||
anchors.fill: parent
|
||||
}
|
||||
Text {
|
||||
id: element
|
||||
x: 772
|
||||
text: logic.lastMessage
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 17
|
||||
font.bold: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: 17
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4d0d6a353c33ff2227b83562a127b3514a7e2169
|
||||
Subproject commit c520b8e969fd3b9a960c27b73c05cf8eb511ae04
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c42890e5b14b7d84a2345b060d1b54bfb7aed1b
|
||||
Subproject commit 79025e4687692bdda8c2364b212c4bd539c4156e
|
Loading…
Reference in New Issue