mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
311404f56b
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change Adds toast notifications to the overlay. Toast notifications are transient windows (shown for about 4 seconds). These are drawn on top of the game even when the overlay is inactive (i.e., when no window is open), and does not cause any input sink behavior. Add toasts for some common user-driven actions that could use a notification, such as: * screenshot * card insert * PIN macro * API client connect/disconnect * virtual printer * screen resize toggle / scene switch Add an option to change where the toasts are displayed, or to turn it off entirely. Add SDK function for it, and add it to the samples. ## Testing Tested DDR (shown at bottom right, as is for most games) and RB (shown top right by default).
177 lines
5.0 KiB
C++
177 lines
5.0 KiB
C++
#define HEADSOCKET_IMPLEMENTATION
|
|
#include "external/headsocket.h"
|
|
|
|
#include "websocket.h"
|
|
#include "util/utils.h"
|
|
#include "util/rc4.h"
|
|
#include "util/logging.h"
|
|
#include "overlay/notifications.h"
|
|
#include "controller.h"
|
|
|
|
using namespace headsocket;
|
|
|
|
namespace api {
|
|
|
|
/*
|
|
* Client class declaration
|
|
*/
|
|
class WebSocketClient : public web_socket_client {
|
|
|
|
// required class header
|
|
HEADSOCKET_CLIENT(WebSocketClient, web_socket_client);
|
|
|
|
private:
|
|
ClientState *state = nullptr;
|
|
|
|
protected:
|
|
bool async_received_data(const data_block &db, uint8_t *ptr, size_t length) override;
|
|
|
|
void on_accept() override;
|
|
void on_disconnect() override;
|
|
};
|
|
|
|
/*
|
|
* Server class declaration
|
|
*/
|
|
class WebSocketServer : public web_socket_server<WebSocketClient> {
|
|
HEADSOCKET_SERVER(WebSocketServer, web_socket_server);
|
|
public:
|
|
WebSocketController *websocket;
|
|
};
|
|
|
|
void api::WebSocketServer::init() {}
|
|
|
|
/*
|
|
* Controller state so we don't have to import headsocket stuff in our header
|
|
*/
|
|
struct WebSocketControllerState {
|
|
std::shared_ptr<WebSocketServer> server;
|
|
};
|
|
|
|
WebSocketController::WebSocketController(Controller *controller, uint16_t port) {
|
|
this->controller = controller;
|
|
|
|
// create state
|
|
this->state = new WebSocketControllerState();
|
|
|
|
// start server
|
|
this->state->server = WebSocketServer::create(port);
|
|
this->state->server->websocket = this;
|
|
if (this->state->server->is_running()) {
|
|
log_info("api::websocket", "server listening on port: {}", port);
|
|
} else {
|
|
log_warning("api::websocket", "server failed to listen on port: {}", port);
|
|
}
|
|
}
|
|
|
|
WebSocketController::~WebSocketController() {
|
|
|
|
// stop server
|
|
this->state->server->stop();
|
|
|
|
// delete state
|
|
delete this->state;
|
|
}
|
|
|
|
void WebSocketController::free_socket() {
|
|
this->state->server->stop();
|
|
}
|
|
|
|
void WebSocketClient::on_accept() {
|
|
web_socket_client::on_accept();
|
|
|
|
// get pointer to server
|
|
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
|
if (!srv || !srv->websocket) {
|
|
log_fatal("api::websocket", "on_accept has no server");
|
|
}
|
|
|
|
// check for init
|
|
state = new ClientState();
|
|
srv->websocket->controller->init_state(state);
|
|
|
|
// log connection
|
|
log_info("api::websocket", "client connected");
|
|
overlay::notifications::add(
|
|
overlay::notifications::Severity::Success,
|
|
"API websocket client connected");
|
|
}
|
|
|
|
void WebSocketClient::on_disconnect() {
|
|
|
|
// log disconnection
|
|
log_info("api::websocket", "client disconnected");
|
|
overlay::notifications::add(
|
|
overlay::notifications::Severity::Info,
|
|
"API websocket client disconnected");
|
|
|
|
// get pointer to server
|
|
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
|
if (!srv || !srv->websocket) {
|
|
log_fatal("api::websocket", "on_disconnect has no server");
|
|
}
|
|
|
|
// clean up state
|
|
srv->websocket->controller->free_state(state);
|
|
delete state;
|
|
state = nullptr;
|
|
|
|
// call super
|
|
web_socket_client::on_disconnect();
|
|
}
|
|
|
|
/*
|
|
* This is where business actually happens, gets called on every datablock receive
|
|
*/
|
|
bool WebSocketClient::async_received_data(const data_block &db, uint8_t *ptr, size_t length) {
|
|
|
|
// get pointer to server
|
|
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
|
if (!srv || !srv->websocket) {
|
|
log_fatal("api::websocket", "received datablock without server");
|
|
}
|
|
|
|
// check state
|
|
if (!state) {
|
|
log_fatal("api::websocket", "client with no state received datablock");
|
|
}
|
|
|
|
// check datablock type
|
|
switch (db.op) {
|
|
case opcode::binary: {
|
|
|
|
// allocate buffers
|
|
std::vector<char> in(ptr, ptr + length);
|
|
std::vector<char> out;
|
|
|
|
// crypt in-data
|
|
if (state->cipher) {
|
|
state->cipher->crypt(reinterpret_cast<uint8_t *>(in.data()), in.size());
|
|
}
|
|
|
|
// process request
|
|
srv->websocket->controller->process_request(state, &in, &out);
|
|
|
|
// crypt out-data
|
|
if (state->cipher) {
|
|
state->cipher->crypt(reinterpret_cast<uint8_t *>(out.data()), out.size());
|
|
}
|
|
|
|
// send answer
|
|
push(out.data(), out.size());
|
|
|
|
// check for password change
|
|
srv->websocket->controller->process_password_change(state);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
log_warning("api::websocket", "datablock received with non-binary type");
|
|
break;
|
|
}
|
|
|
|
// always consume the datablock, nomnom
|
|
return true;
|
|
}
|
|
}
|