fix: signal handler integration with status-go

This commit is contained in:
Richard Ramos 2020-05-16 19:46:46 -04:00 committed by Iuri Matias
parent acf0dcbf7a
commit a80a6c623b
10 changed files with 86 additions and 151 deletions

12
.gitmodules vendored
View File

@ -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

View File

@ -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

View File

@ -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
# 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() =
# 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
var app = newQApplication()
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:
@ -60,16 +68,27 @@ proc mainProc() =
appState.addChannel("test")
appState.addChannel("test2")
engine.load("../ui/main.qml")
# EXAMPLE: this will be triggered once a message is received
appState.onSignal(SignalType.Message, proc(myMessage: string): void =
echo "I received a message: ", myMessage
);
# 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()
# Handle signals as part of the state
var signalWorker: Thread[AppState]
signalWorker.createThread(proc(s:AppState) = s.processSignals, appState)
defer: signalWorker.joinThread()
libstatus.setSignalEventCallback(callback)
# Qt main event loop is entered here
# The termination of the loop will be performed when exit() or quit() is called

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
type SignalCallback* = proc(eventMessage: cstring): void
type SignalCallback* = proc(eventMessage: cstring): void {.cdecl.}
type SignalType* {.pure.} = enum
Message = "messages.new"

View File

@ -5,6 +5,7 @@ import Qt.labs.platform 1.1
import "./onboarding"
import "./app"
ApplicationWindow {
id: applicationWindow
width: 1232
@ -12,46 +13,17 @@ ApplicationWindow {
title: "Nim Status Client"
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
}
}

2
vendor/DOtherSide vendored

@ -1 +1 @@
Subproject commit 4d0d6a353c33ff2227b83562a127b3514a7e2169
Subproject commit c520b8e969fd3b9a960c27b73c05cf8eb511ae04

2
vendor/nimqml vendored

@ -1 +1 @@
Subproject commit 5c42890e5b14b7d84a2345b060d1b54bfb7aed1b
Subproject commit 79025e4687692bdda8c2364b212c4bd539c4156e