mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 08:35:54 +08:00
Multicast for login and logout.
Implemented on same base as single instance messages. Linux adds new Dbus object for this. Message format for instance messaging changed to json.
This commit is contained in:
parent
2d12c61ea0
commit
e99a53079e
@ -5,6 +5,7 @@
|
|||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "InstanceCheck.hpp"
|
#include "InstanceCheck.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
@ -16,12 +17,15 @@
|
|||||||
#include "boost/nowide/convert.hpp"
|
#include "boost/nowide/convert.hpp"
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
@ -66,9 +70,7 @@ namespace instance_check_internal
|
|||||||
static CommandLineAnalysis process_command_line(int argc, char** argv)
|
static CommandLineAnalysis process_command_line(int argc, char** argv)
|
||||||
{
|
{
|
||||||
CommandLineAnalysis ret;
|
CommandLineAnalysis ret;
|
||||||
//if (argc < 2)
|
std::vector<std::string> arguments { argv[0] };
|
||||||
// return ret;
|
|
||||||
std::vector<std::string> arguments { argv[0] };
|
|
||||||
bool send_if_url = false;
|
bool send_if_url = false;
|
||||||
bool has_url = false;
|
bool has_url = false;
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
@ -90,14 +92,22 @@ namespace instance_check_internal
|
|||||||
if (send_if_url && has_url) {
|
if (send_if_url && has_url) {
|
||||||
ret.should_send = true;
|
ret.should_send = true;
|
||||||
}
|
}
|
||||||
ret.cl_string = escape_strings_cstyle(arguments);
|
// We do now want escape_strings_cstyle that quotes strings
|
||||||
|
// It would not be possible to use inside json
|
||||||
|
for (const std::string& arg : arguments) {
|
||||||
|
ret.cl_string += escape_string_cstyle(arg);
|
||||||
|
ret.cl_string += ";";
|
||||||
|
}
|
||||||
BOOST_LOG_TRIVIAL(info) << "single instance: " <<
|
BOOST_LOG_TRIVIAL(info) << "single instance: " <<
|
||||||
(ret.should_send.has_value() ? (*ret.should_send ? "true" : "false") : "undefined") <<
|
(ret.should_send.has_value() ? (*ret.should_send ? "true" : "false") : "undefined") <<
|
||||||
". other params: " << ret.cl_string;
|
". other params: " << ret.cl_string;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string compose_message_json(const std::string& type, const std::string& data)
|
||||||
|
{
|
||||||
|
return GUI::format("{ \"type\" : \"%1%\" , \"data\" : \"%2%\"}", type, data);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
@ -168,6 +178,67 @@ namespace instance_check_internal
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL CALLBACK enum_windows_process_multicast(_In_ HWND hwnd, _In_ LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (hwnd == GUI::wxGetApp().mainframe->GetHandle()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCHAR wndText[1000];
|
||||||
|
TCHAR className[1000];
|
||||||
|
int err;
|
||||||
|
err = GetClassName(hwnd, className, 1000);
|
||||||
|
if (err == 0)
|
||||||
|
return true;
|
||||||
|
err = GetWindowText(hwnd, wndText, 1000);
|
||||||
|
if (err == 0)
|
||||||
|
return true;
|
||||||
|
std::wstring classNameString(className);
|
||||||
|
std::wstring wndTextString(wndText);
|
||||||
|
if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
|
||||||
|
//check if other instances has same instance hash
|
||||||
|
//if not it is not same version(binary) as this version
|
||||||
|
HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
|
||||||
|
uint64_t other_instance_hash = PtrToUint(handle);
|
||||||
|
uint64_t other_instance_hash_major;
|
||||||
|
uint64_t my_instance_hash = GUI::wxGetApp().get_instance_hash_int();
|
||||||
|
handle = GetProp(hwnd, L"Instance_Hash_Major");
|
||||||
|
other_instance_hash_major = PtrToUint(handle);
|
||||||
|
other_instance_hash_major = other_instance_hash_major << 32;
|
||||||
|
other_instance_hash += other_instance_hash_major;
|
||||||
|
handle = GetProp(hwnd, L"Instance_Is_Maximized");
|
||||||
|
const bool maximized = PtrToUint(handle) == 1;
|
||||||
|
|
||||||
|
if (my_instance_hash == other_instance_hash)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "win multicast enum - found instance " << hwnd;
|
||||||
|
std::wstring multicast_message = *reinterpret_cast<std::wstring*>(lParam);
|
||||||
|
std::unique_ptr<LPWSTR> message = std::make_unique<LPWSTR>(const_cast<LPWSTR>(multicast_message.c_str()));
|
||||||
|
|
||||||
|
//Create a COPYDATASTRUCT to send the information
|
||||||
|
//cbData represents the size of the information we want to send.
|
||||||
|
//lpData represents the information we want to send.
|
||||||
|
//dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA).
|
||||||
|
COPYDATASTRUCT data_to_send = { 0 };
|
||||||
|
data_to_send.dwData = 1;
|
||||||
|
data_to_send.cbData = sizeof(TCHAR) * (wcslen(*message.get()) + 1);
|
||||||
|
data_to_send.lpData = *message.get();
|
||||||
|
SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "win enum - found wrong instance";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void multicast_message_inner(const std::string& message)
|
||||||
|
{
|
||||||
|
// multicast_message must live until EnumWindows is done, it is passed as pointer parameter.
|
||||||
|
std::wstring multicast_message = boost::nowide::widen(message);
|
||||||
|
EnumWindows(enum_windows_process_multicast, reinterpret_cast<LPARAM>(&multicast_message));
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static bool get_lock(const std::string& name, const std::string& path)
|
static bool get_lock(const std::string& name, const std::string& path)
|
||||||
@ -228,6 +299,11 @@ namespace instance_check_internal
|
|||||||
#endif //WIN32
|
#endif //WIN32
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
|
||||||
|
static void multicast_message_inner(const std::string &message_text)
|
||||||
|
{
|
||||||
|
multicast_message_mac(message_text);
|
||||||
|
}
|
||||||
|
|
||||||
static bool send_message(const std::string &message_text, const std::string &version)
|
static bool send_message(const std::string &message_text, const std::string &version)
|
||||||
{
|
{
|
||||||
//std::string v(version);
|
//std::string v(version);
|
||||||
@ -242,6 +318,162 @@ namespace instance_check_internal
|
|||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
|
static void list_matching_objects(const std::string& pattern, std::vector<std::string>& result)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
|
DBusConnection* connection;
|
||||||
|
DBusError error;
|
||||||
|
|
||||||
|
// Initialize the D-Bus error.
|
||||||
|
dbus_error_init(&error);
|
||||||
|
|
||||||
|
// Connect to the session bus.
|
||||||
|
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
|
||||||
|
if (!connection) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to connect to the D-Bus session bus: " << error.message;
|
||||||
|
dbus_error_free(&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request a list of all bus names.
|
||||||
|
DBusMessage* message = dbus_message_new_method_call(
|
||||||
|
"org.freedesktop.DBus", // Destination (the D-Bus daemon)
|
||||||
|
"/org/freedesktop/DBus", // Object path
|
||||||
|
"org.freedesktop.DBus", // Interface
|
||||||
|
"ListNames" // Method
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to create D-Bus message.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error);
|
||||||
|
dbus_message_unref(message);
|
||||||
|
|
||||||
|
if (!reply) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to send message: " << error.message;
|
||||||
|
dbus_error_free(&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the reply.
|
||||||
|
DBusMessageIter args;
|
||||||
|
if (!dbus_message_iter_init(reply, &args)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Reply does not contain arguments.";
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Unexpected argument type in reply.";
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter array_iter;
|
||||||
|
dbus_message_iter_recurse(&args, &array_iter);
|
||||||
|
|
||||||
|
std::regex instance_regex(pattern);
|
||||||
|
|
||||||
|
while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRING) {
|
||||||
|
const char* name;
|
||||||
|
dbus_message_iter_get_basic(&array_iter, &name);
|
||||||
|
if (std::regex_match(name, instance_regex)) {
|
||||||
|
result.push_back(name);
|
||||||
|
}
|
||||||
|
dbus_message_iter_next(&array_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
dbus_error_free(&error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool multicast_one_message(const std::string &message_text, const std::string &interface_name)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << " to " << interface_name;
|
||||||
|
DBusMessage* msg;
|
||||||
|
DBusConnection* conn;
|
||||||
|
DBusError err;
|
||||||
|
dbus_uint32_t serial = 0;
|
||||||
|
const char* sigval = message_text.c_str();
|
||||||
|
std::string method_name = "Message";
|
||||||
|
//std::string interface_name = "com.prusa3d.prusaslicer.MulticastListener.Object" + reciever_id;
|
||||||
|
//std::string object_name = "/com/prusa3d/prusaslicer/MulticastListener/Object" + reciever_id;
|
||||||
|
std::string object_name = "/" + interface_name;
|
||||||
|
std::replace(object_name.begin(), object_name.end(), '.', '/');
|
||||||
|
|
||||||
|
// initialise the error value
|
||||||
|
dbus_error_init(&err);
|
||||||
|
// connect to bus, and check for errors (use SESSION bus everywhere!)
|
||||||
|
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||||
|
if (dbus_error_is_set(&err)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Connection Error. Message to another instance wont be send.";
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: " << err.message;
|
||||||
|
dbus_error_free(&err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (NULL == conn) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//some sources do request interface ownership before constructing msg but i think its wrong.
|
||||||
|
//create new method call message
|
||||||
|
msg = dbus_message_new_method_call(interface_name.c_str(), object_name.c_str(), interface_name.c_str(), method_name.c_str());
|
||||||
|
if (NULL == msg) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send.";
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//the Message method is not sending reply.
|
||||||
|
dbus_message_set_no_reply(msg, TRUE);
|
||||||
|
//append arguments to message
|
||||||
|
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &sigval, DBUS_TYPE_INVALID)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send.";
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// send the message and flush the connection
|
||||||
|
if (!dbus_connection_send(conn, msg, &serial)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Ran out of memory while sending DBus message.";
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dbus_connection_flush(conn);
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "DBus message sent.";
|
||||||
|
// free the message and close the connection
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void multicast_message_inner(const std::string &message_text)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
|
std::string pattern = R"(com\.prusa3d\.prusaslicer\.MulticastListener\.Object\d+)";
|
||||||
|
std::vector<std::string> instances;
|
||||||
|
std::string my_pid = std::to_string(get_current_pid());
|
||||||
|
|
||||||
|
list_matching_objects(pattern, instances);
|
||||||
|
for (const std::string& instance : instances) {
|
||||||
|
// regex object pid to not send message to myself.
|
||||||
|
std::regex objectRegex("Object(\\d+)");
|
||||||
|
std::smatch match;
|
||||||
|
if (std::regex_search(instance, match, objectRegex) && match[1] != my_pid) {
|
||||||
|
if (!multicast_one_message(message_text, instance)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed send DBUS message to " << instance;
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Successfully sent DBUS message to " << instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool send_message(const std::string &message_text, const std::string &version)
|
static bool send_message(const std::string &message_text, const std::string &version)
|
||||||
{
|
{
|
||||||
/*std::string v(version);
|
/*std::string v(version);
|
||||||
@ -372,7 +604,7 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
|
|||||||
// get_lock() creates the lockfile therefore *cla.should_send is checked after
|
// get_lock() creates the lockfile therefore *cla.should_send is checked after
|
||||||
if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) {
|
if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) {
|
||||||
#endif
|
#endif
|
||||||
instance_check_internal::send_message(cla.cl_string, lock_name);
|
instance_check_internal::send_message(instance_check_internal::compose_message_json("CLI", cla.cl_string), lock_name);
|
||||||
BOOST_LOG_TRIVIAL(error) << "Instance check: Another instance found. This instance will terminate. Lock file of current running instance is located at " << data_dir() <<
|
BOOST_LOG_TRIVIAL(error) << "Instance check: Another instance found. This instance will terminate. Lock file of current running instance is located at " << data_dir() <<
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
"\\cache\\"
|
"\\cache\\"
|
||||||
@ -394,6 +626,7 @@ wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
|
|||||||
wxDEFINE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
|
wxDEFINE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
|
||||||
wxDEFINE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
|
wxDEFINE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
|
||||||
wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
||||||
|
wxDEFINE_EVENT(EVT_STORE_READ_REQUEST, SimpleEvent);
|
||||||
|
|
||||||
void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
|
void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
|
||||||
{
|
{
|
||||||
@ -410,7 +643,8 @@ void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
|
|||||||
#endif //__APPLE__
|
#endif //__APPLE__
|
||||||
|
|
||||||
#ifdef BACKGROUND_MESSAGE_LISTENER
|
#ifdef BACKGROUND_MESSAGE_LISTENER
|
||||||
m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this)));
|
m_instance_check_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen_instance_check, this)));
|
||||||
|
m_multicast_listener_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen_multicast, this)));
|
||||||
#endif //BACKGROUND_MESSAGE_LISTENER
|
#endif //BACKGROUND_MESSAGE_LISTENER
|
||||||
}
|
}
|
||||||
void OtherInstanceMessageHandler::shutdown(MainFrame* main_frame)
|
void OtherInstanceMessageHandler::shutdown(MainFrame* main_frame)
|
||||||
@ -432,17 +666,30 @@ void OtherInstanceMessageHandler::shutdown(MainFrame* main_frame)
|
|||||||
this->unregister_for_messages();
|
this->unregister_for_messages();
|
||||||
#endif //__APPLE__
|
#endif //__APPLE__
|
||||||
#ifdef BACKGROUND_MESSAGE_LISTENER
|
#ifdef BACKGROUND_MESSAGE_LISTENER
|
||||||
if (m_thread.joinable()) {
|
if (m_instance_check_thread.joinable()) {
|
||||||
// Stop the worker thread, if running.
|
// Stop the worker thread, if running.
|
||||||
{
|
{
|
||||||
// Notify the worker thread to cancel wait on detection polling.
|
// Notify the worker thread to cancel wait on detection polling.
|
||||||
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
std::lock_guard<std::mutex> lck(m_instance_check_thread_stop_mutex);
|
||||||
m_stop = true;
|
m_instance_check_thread_stop = true;
|
||||||
}
|
}
|
||||||
m_thread_stop_condition.notify_all();
|
m_instance_check_thread_stop_condition.notify_all();
|
||||||
// Wait for the worker thread to stop.
|
// Wait for the worker thread to stop.
|
||||||
m_thread.join();
|
m_instance_check_thread.join();
|
||||||
m_stop = false;
|
m_instance_check_thread_stop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_multicast_listener_thread.joinable()) {
|
||||||
|
// Stop the worker thread, if running.
|
||||||
|
{
|
||||||
|
// Notify the worker thread to cancel wait on detection polling.
|
||||||
|
std::lock_guard<std::mutex> lck(m_multicast_listener_thread_stop_mutex);
|
||||||
|
m_multicast_listener_thread_stop = true;
|
||||||
|
}
|
||||||
|
m_multicast_listener_thread_stop_condition.notify_all();
|
||||||
|
// Wait for the worker thread to stop.
|
||||||
|
m_multicast_listener_thread.join();
|
||||||
|
m_multicast_listener_thread_stop = false;
|
||||||
}
|
}
|
||||||
#endif //BACKGROUND_MESSAGE_LISTENER
|
#endif //BACKGROUND_MESSAGE_LISTENER
|
||||||
m_callback_evt_handler = nullptr;
|
m_callback_evt_handler = nullptr;
|
||||||
@ -525,15 +772,20 @@ namespace MessageHandlerInternal
|
|||||||
}
|
}
|
||||||
} //namespace MessageHandlerInternal
|
} //namespace MessageHandlerInternal
|
||||||
|
|
||||||
void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
void OtherInstanceMessageHandler::multicast_message(const std::string& message_type, const std::string& message_data)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << " " << message_type;
|
||||||
|
instance_check_internal::multicast_message_inner(instance_check_internal::compose_message_json(message_type, message_data));
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> args;
|
|
||||||
bool parsed = unescape_strings_cstyle(message, args);
|
void OtherInstanceMessageHandler::handle_message_type_cli(const std::string& data)
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
bool parsed = unescape_strings_cstyle(data, args);
|
||||||
assert(parsed);
|
assert(parsed);
|
||||||
if (! parsed) {
|
if (! parsed) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "message from other instance is incorrectly formatted: " << message;
|
BOOST_LOG_TRIVIAL(error) << "message from other instance is incorrectly formatted: " << data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,10 +794,10 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
|||||||
// Skip the first argument, it is the path to the slicer executable.
|
// Skip the first argument, it is the path to the slicer executable.
|
||||||
auto it = args.begin();
|
auto it = args.begin();
|
||||||
for (++ it; it != args.end(); ++ it) {
|
for (++ it; it != args.end(); ++ it) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << *it;
|
||||||
boost::filesystem::path p = MessageHandlerInternal::get_path(*it);
|
boost::filesystem::path p = MessageHandlerInternal::get_path(*it);
|
||||||
if (! p.string().empty())
|
if (! p.string().empty())
|
||||||
paths.emplace_back(p);
|
paths.emplace_back(p);
|
||||||
// TODO: There is a misterious slash appearing in recieved msg on windows
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
else if (it->rfind("prusaslicer://open/?file=", 0) == 0)
|
else if (it->rfind("prusaslicer://open/?file=", 0) == 0)
|
||||||
#else
|
#else
|
||||||
@ -557,16 +809,45 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! paths.empty()) {
|
if (! paths.empty()) {
|
||||||
//wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
|
wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
|
||||||
//if (evt_handler) {
|
|
||||||
wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
if (!downloads.empty())
|
if (!downloads.empty()) {
|
||||||
{
|
|
||||||
wxPostEvent(m_callback_evt_handler, StartDownloadOtherInstanceEvent(GUI::EVT_START_DOWNLOAD_OTHER_INSTANCE, std::vector<std::string>(std::move(downloads))));
|
wxPostEvent(m_callback_evt_handler, StartDownloadOtherInstanceEvent(GUI::EVT_START_DOWNLOAD_OTHER_INSTANCE, std::vector<std::string>(std::move(downloads))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void OtherInstanceMessageHandler::handle_message_type_store_read(const std::string& data)
|
||||||
|
{
|
||||||
|
wxPostEvent(m_callback_evt_handler, SimpleEvent(GUI::EVT_STORE_READ_REQUEST));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message;
|
||||||
|
// message in format { "type" : "TYPE", "data" : "data" }
|
||||||
|
// types: CLI, STORE_READ
|
||||||
|
std::string type;
|
||||||
|
std::string data;
|
||||||
|
try {
|
||||||
|
std::stringstream ss(message);
|
||||||
|
boost::property_tree::ptree ptree;
|
||||||
|
boost::property_tree::read_json(ss, ptree);
|
||||||
|
if (const auto action = ptree.get_optional<std::string>("type"); action) {
|
||||||
|
type = *action;
|
||||||
|
}
|
||||||
|
if (const auto data_opt = ptree.get_optional<std::string>("data"); data_opt) {
|
||||||
|
data = *data_opt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Could not parse other instance message: " << e.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(!type.empty());
|
||||||
|
assert(m_message_handlers.find(type) != m_message_handlers.end()); // this assert means there is an message type that has no handling.
|
||||||
|
if (m_message_handlers.find(type) != m_message_handlers.end()) {
|
||||||
|
m_message_handlers[type](data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
void OtherInstanceMessageHandler::handle_message_other_closed()
|
void OtherInstanceMessageHandler::handle_message_other_closed()
|
||||||
@ -577,7 +858,7 @@ void OtherInstanceMessageHandler::handle_message_other_closed()
|
|||||||
|
|
||||||
#ifdef BACKGROUND_MESSAGE_LISTENER
|
#ifdef BACKGROUND_MESSAGE_LISTENER
|
||||||
|
|
||||||
namespace MessageHandlerDBusInternal
|
namespace InstanceCheckMessageHandlerDBusInternal
|
||||||
{
|
{
|
||||||
//reply to introspect makes our DBus object visible for other programs like D-Feet
|
//reply to introspect makes our DBus object visible for other programs like D-Feet
|
||||||
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request)
|
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request)
|
||||||
@ -638,26 +919,27 @@ namespace MessageHandlerDBusInternal
|
|||||||
std::string our_interface = "com.prusa3d.prusaslicer.InstanceCheck.Object" + wxGetApp().get_instance_hash_string();
|
std::string our_interface = "com.prusa3d.prusaslicer.InstanceCheck.Object" + wxGetApp().get_instance_hash_string();
|
||||||
BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
|
BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
|
||||||
if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {
|
if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {
|
||||||
respond_to_introspect(connection, message);
|
InstanceCheckMessageHandlerDBusInternal::respond_to_introspect(connection, message);
|
||||||
return DBUS_HANDLER_RESULT_HANDLED;
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstance", member_name)) {
|
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstance", member_name)) {
|
||||||
handle_method_another_instance(connection, message);
|
InstanceCheckMessageHandlerDBusInternal::handle_method_another_instance(connection, message);
|
||||||
return DBUS_HANDLER_RESULT_HANDLED;
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Introspect", member_name)) {
|
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Introspect", member_name)) {
|
||||||
respond_to_introspect(connection, message);
|
InstanceCheckMessageHandlerDBusInternal::respond_to_introspect(connection, message);
|
||||||
return DBUS_HANDLER_RESULT_HANDLED;
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
}
|
}
|
||||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
}
|
}
|
||||||
} //namespace MessageHandlerDBusInternal
|
} //namespace InstanceCheckMessageHandlerDBusInternal
|
||||||
|
|
||||||
void OtherInstanceMessageHandler::listen()
|
void OtherInstanceMessageHandler::listen_instance_check()
|
||||||
{
|
{
|
||||||
DBusConnection* conn;
|
DBusConnection* conn;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
int name_req_val;
|
int name_req_val;
|
||||||
DBusObjectPathVTable vtable;
|
DBusObjectPathVTable vtable;
|
||||||
std::string instance_hash = wxGetApp().get_instance_hash_string();
|
std::string instance_hash = wxGetApp().get_instance_hash_string();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << " " << instance_hash;
|
||||||
std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + instance_hash;
|
std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + instance_hash;
|
||||||
std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + instance_hash;
|
std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + instance_hash;
|
||||||
|
|
||||||
@ -694,7 +976,7 @@ void OtherInstanceMessageHandler::listen()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set callbacks. Unregister function should not be nessary.
|
// Set callbacks. Unregister function should not be nessary.
|
||||||
vtable.message_function = MessageHandlerDBusInternal::handle_dbus_object_message;
|
vtable.message_function = InstanceCheckMessageHandlerDBusInternal::handle_dbus_object_message;
|
||||||
vtable.unregister_function = NULL;
|
vtable.unregister_function = NULL;
|
||||||
|
|
||||||
// register new object - this is our access to DBus
|
// register new object - this is our access to DBus
|
||||||
@ -707,19 +989,169 @@ void OtherInstanceMessageHandler::listen()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(trace) << "Dbus object "<< object_name <<" registered. Starting listening for messages.";
|
BOOST_LOG_TRIVIAL(debug) << "Dbus object "<< object_name <<" registered. Starting listening for messages.";
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Wait for 1 second
|
// Wait for 1 second
|
||||||
// Cancellable.
|
// Cancellable.
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
std::unique_lock<std::mutex> lck(m_instance_check_thread_stop_mutex);
|
||||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_stop; });
|
m_instance_check_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_instance_check_thread_stop; });
|
||||||
}
|
}
|
||||||
if (m_stop)
|
if (m_instance_check_thread_stop) {
|
||||||
// Stop the worker thread.
|
// Stop the worker thread.
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
//dispatch should do all the work with incoming messages
|
||||||
|
//second parameter is blocking time that funciton waits for new messages
|
||||||
|
//that is handled here with our own event loop above
|
||||||
|
dbus_connection_read_write_dispatch(conn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace MulticastMessageHandlerDBusInternal
|
||||||
|
{
|
||||||
|
//reply to introspect makes our DBus object visible for other programs like D-Feet
|
||||||
|
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
|
DBusMessage *reply;
|
||||||
|
const char *introspection_data =
|
||||||
|
" <!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
|
||||||
|
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
|
||||||
|
" <!-- dbus-sharp 0.8.1 -->"
|
||||||
|
" <node>"
|
||||||
|
" <interface name=\"org.freedesktop.DBus.Introspectable\">"
|
||||||
|
" <method name=\"Introspect\">"
|
||||||
|
" <arg name=\"data\" direction=\"out\" type=\"s\" />"
|
||||||
|
" </method>"
|
||||||
|
" </interface>"
|
||||||
|
" <interface name=\"com.prusa3d.prusaslicer.MulticastListener\">"
|
||||||
|
" <method name=\"Message\">"
|
||||||
|
" <arg name=\"data\" direction=\"in\" type=\"s\" />"
|
||||||
|
" </method>"
|
||||||
|
" <method name=\"Introspect\">"
|
||||||
|
" <arg name=\"data\" direction=\"out\" type=\"s\" />"
|
||||||
|
" </method>"
|
||||||
|
" </interface>"
|
||||||
|
" </node>";
|
||||||
|
|
||||||
|
reply = dbus_message_new_method_return(request);
|
||||||
|
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, DBUS_TYPE_INVALID);
|
||||||
|
dbus_connection_send(connection, reply, NULL);
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
//method Message receives message from another PrusaSlicer instance
|
||||||
|
static void handle_method_message(DBusConnection *connection, DBusMessage *request)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
|
DBusError err;
|
||||||
|
char* text = nullptr;
|
||||||
|
wxEvtHandler* evt_handler;
|
||||||
|
|
||||||
|
dbus_error_init(&err);
|
||||||
|
dbus_message_get_args(request, &err, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
|
||||||
|
if (dbus_error_is_set(&err)) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Dbus method Message received with wrong arguments.";
|
||||||
|
dbus_error_free(&err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wxGetApp().other_instance_message_handler()->handle_message(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
//every dbus message received comes here
|
||||||
|
static DBusHandlerResult handle_dbus_object_message(DBusConnection *connection, DBusMessage *message, void *user_data)
|
||||||
|
{
|
||||||
|
const char* interface_name = dbus_message_get_interface(message);
|
||||||
|
const char* member_name = dbus_message_get_member(message);
|
||||||
|
std::string our_interface = "com.prusa3d.prusaslicer.MulticastListener.Object" + std::to_string(get_current_pid());
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
|
||||||
|
if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {
|
||||||
|
MulticastMessageHandlerDBusInternal::respond_to_introspect(connection, message);
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Message", member_name)) {
|
||||||
|
MulticastMessageHandlerDBusInternal::handle_method_message(connection, message);
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Introspect", member_name)) {
|
||||||
|
MulticastMessageHandlerDBusInternal::respond_to_introspect(connection, message);
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
}
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
}
|
||||||
|
} //namespace MulticastMessageHandlerDBusInternal
|
||||||
|
|
||||||
|
void OtherInstanceMessageHandler::listen_multicast()
|
||||||
|
{
|
||||||
|
DBusConnection* conn;
|
||||||
|
DBusError err;
|
||||||
|
int name_req_val;
|
||||||
|
DBusObjectPathVTable vtable;
|
||||||
|
std::string pid = std::to_string(get_current_pid());
|
||||||
|
std::string interface_name = "com.prusa3d.prusaslicer.MulticastListener.Object" + pid;
|
||||||
|
std::string object_name = "/com/prusa3d/prusaslicer/MulticastListener/Object" + pid;
|
||||||
|
|
||||||
|
dbus_error_init(&err);
|
||||||
|
|
||||||
|
// connect to the bus and check for errors (use SESSION bus everywhere!)
|
||||||
|
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||||
|
if (dbus_error_is_set(&err)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message;
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
|
||||||
|
dbus_error_free(&err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (NULL == conn) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Dbus Messages listening terminating.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// request our name on the bus and check for errors
|
||||||
|
name_req_val = dbus_bus_request_name(conn, interface_name.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
|
||||||
|
if (dbus_error_is_set(&err)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Request name Error: "<< err.message;
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
|
||||||
|
dbus_error_free(&err);
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != name_req_val) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Not primary owner of DBus name - probably another PrusaSlicer instance is running.";
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set callbacks. Unregister function should not be nessary.
|
||||||
|
vtable.message_function = MulticastMessageHandlerDBusInternal::handle_dbus_object_message;
|
||||||
|
vtable.unregister_function = NULL;
|
||||||
|
|
||||||
|
// register new object - this is our access to DBus
|
||||||
|
dbus_connection_try_register_object_path(conn, object_name.c_str(), &vtable, NULL, &err);
|
||||||
|
if ( dbus_error_is_set(&err) ) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "DBus Register object Error: "<< err.message;
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
|
||||||
|
dbus_connection_unref(conn);
|
||||||
|
dbus_error_free(&err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Dbus object "<< object_name <<" registered. Starting listening for messages.";
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
// Wait for 1 second
|
||||||
|
// Cancellable.
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lck(m_multicast_listener_thread_stop_mutex);
|
||||||
|
m_multicast_listener_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_multicast_listener_thread_stop; });
|
||||||
|
}
|
||||||
|
if (m_multicast_listener_thread_stop) {
|
||||||
|
// Stop the worker thread.
|
||||||
|
break;
|
||||||
|
}
|
||||||
//dispatch should do all the work with incoming messages
|
//dispatch should do all the work with incoming messages
|
||||||
//second parameter is blocking time that funciton waits for new messages
|
//second parameter is blocking time that funciton waits for new messages
|
||||||
//that is handled here with our own event loop above
|
//that is handled here with our own event loop above
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#endif //_WIN32
|
#endif //_WIN32
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance);
|
|||||||
// apple implementation of inner functions of instance_check
|
// apple implementation of inner functions of instance_check
|
||||||
// in InstanceCheckMac.mm
|
// in InstanceCheckMac.mm
|
||||||
void send_message_mac(const std::string& msg, const std::string& version);
|
void send_message_mac(const std::string& msg, const std::string& version);
|
||||||
|
void multicast_message_mac(const std::string &msg);
|
||||||
void send_message_mac_closing(const std::string& msg, const std::string& version);
|
void send_message_mac_closing(const std::string& msg, const std::string& version);
|
||||||
|
|
||||||
|
|
||||||
@ -54,11 +56,16 @@ wxDECLARE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEve
|
|||||||
wxDECLARE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
|
wxDECLARE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
|
||||||
using InstanceGoToFrontEvent = SimpleEvent;
|
using InstanceGoToFrontEvent = SimpleEvent;
|
||||||
wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
||||||
|
wxDECLARE_EVENT(EVT_STORE_READ_REQUEST, SimpleEvent);
|
||||||
|
|
||||||
class OtherInstanceMessageHandler
|
class OtherInstanceMessageHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OtherInstanceMessageHandler() = default;
|
OtherInstanceMessageHandler()
|
||||||
|
{
|
||||||
|
m_message_handlers["CLI"] = std::bind(&OtherInstanceMessageHandler::handle_message_type_cli, this, std::placeholders::_1);
|
||||||
|
m_message_handlers["STORE_READ"] = std::bind(&OtherInstanceMessageHandler::handle_message_type_store_read, this, std::placeholders::_1);
|
||||||
|
}
|
||||||
OtherInstanceMessageHandler(OtherInstanceMessageHandler const&) = delete;
|
OtherInstanceMessageHandler(OtherInstanceMessageHandler const&) = delete;
|
||||||
void operator=(OtherInstanceMessageHandler const&) = delete;
|
void operator=(OtherInstanceMessageHandler const&) = delete;
|
||||||
~OtherInstanceMessageHandler() { assert(!m_initialized); }
|
~OtherInstanceMessageHandler() { assert(!m_initialized); }
|
||||||
@ -68,13 +75,10 @@ public:
|
|||||||
// stops listening, on linux stops the background thread
|
// stops listening, on linux stops the background thread
|
||||||
void shutdown(MainFrame* main_frame);
|
void shutdown(MainFrame* main_frame);
|
||||||
|
|
||||||
//finds paths to models in message(= command line arguments, first should be prusaSlicer executable)
|
// message in format { "type" : "TYPE", "data" : "data" }
|
||||||
//and sends them to plater via LoadFromOtherInstanceEvent
|
void handle_message(const std::string& message);
|
||||||
//security of messages: from message all existing paths are proccesed to load model
|
|
||||||
// win32 - anybody who has hwnd can send message.
|
void multicast_message(const std::string& message_type, const std::string& message_data = std::string());
|
||||||
// mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating"
|
|
||||||
// linux - instrospectable on dbus
|
|
||||||
void handle_message(const std::string& message);
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Messege form other instance, that it deleted its lockfile - first instance to get it will create its own.
|
// Messege form other instance, that it deleted its lockfile - first instance to get it will create its own.
|
||||||
void handle_message_other_closed();
|
void handle_message_other_closed();
|
||||||
@ -84,19 +88,37 @@ public:
|
|||||||
void update_windows_properties(MainFrame* main_frame);
|
void update_windows_properties(MainFrame* main_frame);
|
||||||
#endif //WIN32
|
#endif //WIN32
|
||||||
private:
|
private:
|
||||||
|
//finds paths to models in message(= command line arguments, first should be prusaSlicer executable)
|
||||||
|
//and sends them to plater via LoadFromOtherInstanceEvent
|
||||||
|
//security of messages: from message all existing paths are proccesed to load model
|
||||||
|
// win32 - anybody who has hwnd can send message.
|
||||||
|
// mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating"
|
||||||
|
// linux - instrospectable on dbus
|
||||||
|
void handle_message_type_cli(const std::string& data);
|
||||||
|
// Passes information to UI to perform store read
|
||||||
|
void handle_message_type_store_read(const std::string& data);
|
||||||
|
std::map<std::string, std::function<void(const std::string&)>> m_message_handlers;
|
||||||
|
|
||||||
bool m_initialized { false };
|
bool m_initialized { false };
|
||||||
wxEvtHandler* m_callback_evt_handler { nullptr };
|
wxEvtHandler* m_callback_evt_handler { nullptr };
|
||||||
|
|
||||||
#ifdef BACKGROUND_MESSAGE_LISTENER
|
#ifdef BACKGROUND_MESSAGE_LISTENER
|
||||||
//worker thread to listen incoming dbus communication
|
// instance check worker thread to listen incoming dbus communication
|
||||||
boost::thread m_thread;
|
// Only one instance has registered dbus object at time
|
||||||
std::condition_variable m_thread_stop_condition;
|
boost::thread m_instance_check_thread;
|
||||||
mutable std::mutex m_thread_stop_mutex;
|
std::condition_variable m_instance_check_thread_stop_condition;
|
||||||
bool m_stop{ false };
|
mutable std::mutex m_instance_check_thread_stop_mutex;
|
||||||
bool m_start{ true };
|
bool m_instance_check_thread_stop{ false };
|
||||||
|
//bool m_instance_check_thread_start{ true };
|
||||||
// background thread method
|
void listen_instance_check();
|
||||||
void listen();
|
|
||||||
|
// "multicast" worker thread to listen incoming dbus communication
|
||||||
|
// every instance has registered its own object
|
||||||
|
boost::thread m_multicast_listener_thread;
|
||||||
|
std::condition_variable m_multicast_listener_thread_stop_condition;
|
||||||
|
mutable std::mutex m_multicast_listener_thread_stop_mutex;
|
||||||
|
bool m_multicast_listener_thread_stop{ false };
|
||||||
|
void listen_multicast();
|
||||||
#endif //BACKGROUND_MESSAGE_LISTENER
|
#endif //BACKGROUND_MESSAGE_LISTENER
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
-(instancetype) init;
|
-(instancetype) init;
|
||||||
-(void) add_observer:(NSString *)version;
|
-(void) add_observer:(NSString *)version;
|
||||||
-(void) message_update:(NSNotification *)note;
|
-(void) message_update:(NSNotification *)note;
|
||||||
|
-(void) message_multicast_update:(NSNotification *)note;
|
||||||
-(void) closing_update:(NSNotification *)note;
|
-(void) closing_update:(NSNotification *)note;
|
||||||
-(void) bring_forward;
|
-(void) bring_forward;
|
||||||
@end
|
@end
|
||||||
|
@ -11,12 +11,14 @@
|
|||||||
}
|
}
|
||||||
-(void)add_observer:(NSString *)version_hash
|
-(void)add_observer:(NSString *)version_hash
|
||||||
{
|
{
|
||||||
//NSLog(@"adding observer");
|
//NSLog(@"adding observers");
|
||||||
//NSString *nsver = @"OtherPrusaSlicerInstanceMessage" + version_hash;
|
//NSString *nsver = @"OtherPrusaSlicerInstanceMessage" + version_hash;
|
||||||
NSString *nsver = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceMessage", version_hash];
|
NSString *nsver = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceMessage", version_hash];
|
||||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:nsver object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:nsver object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||||
NSString *nsver2 = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceClosing", version_hash];
|
NSString *nsver2 = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceClosing", version_hash];
|
||||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(closing_update:) name:nsver2 object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(closing_update:) name:nsver2 object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||||
|
NSString *nsnover = [NSString stringWithFormat: @"%@", @"OtherPrusaSlicerMulticastMessage"];
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_multicast_update:) name:nsnover object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)message_update:(NSNotification *)msg
|
-(void)message_update:(NSNotification *)msg
|
||||||
@ -26,6 +28,13 @@
|
|||||||
Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String]));
|
Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(void)message_multicast_update:(NSNotification *)msg
|
||||||
|
{
|
||||||
|
//NSLog(@"message_multicast_update");
|
||||||
|
//pass message
|
||||||
|
Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String]));
|
||||||
|
}
|
||||||
|
|
||||||
-(void)closing_update:(NSNotification *)msg
|
-(void)closing_update:(NSNotification *)msg
|
||||||
{
|
{
|
||||||
//[self bring_forward];
|
//[self bring_forward];
|
||||||
@ -51,6 +60,14 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
void multicast_message_mac(const std::string &msg)
|
||||||
|
{
|
||||||
|
NSString *nsmsg = [NSString stringWithCString:msg.c_str() encoding:[NSString defaultCStringEncoding]];
|
||||||
|
//NSString *nsver = @"OtherPrusaSlicerInstanceMessage" + [NSString stringWithCString:version.c_str() encoding:[NSString defaultCStringEncoding]];
|
||||||
|
NSString *notifname = [NSString stringWithFormat: @"%@", @"OtherPrusaSlicerMulticastMessage"];
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:notifname object:nil userInfo:[NSDictionary dictionaryWithObject:nsmsg forKey:@"data"] deliverImmediately:YES];
|
||||||
|
}
|
||||||
|
|
||||||
void send_message_mac(const std::string &msg, const std::string &version)
|
void send_message_mac(const std::string &msg, const std::string &version)
|
||||||
{
|
{
|
||||||
NSString *nsmsg = [NSString stringWithCString:msg.c_str() encoding:[NSString defaultCStringEncoding]];
|
NSString *nsmsg = [NSString stringWithCString:msg.c_str() encoding:[NSString defaultCStringEncoding]];
|
||||||
|
@ -896,6 +896,11 @@ void Plater::priv::init()
|
|||||||
BOOST_LOG_TRIVIAL(trace) << "Received login from other instance event.";
|
BOOST_LOG_TRIVIAL(trace) << "Received login from other instance event.";
|
||||||
user_account->on_login_code_recieved(evt.data);
|
user_account->on_login_code_recieved(evt.data);
|
||||||
});
|
});
|
||||||
|
this->q->Bind(EVT_STORE_READ_REQUEST, [this](SimpleEvent& evt) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Received store read request from other instance event.";
|
||||||
|
user_account->on_store_read_request();
|
||||||
|
});
|
||||||
|
|
||||||
this->q->Bind(EVT_LOGIN_VIA_WIZARD, [this](Event<std::string> &evt) {
|
this->q->Bind(EVT_LOGIN_VIA_WIZARD, [this](Event<std::string> &evt) {
|
||||||
BOOST_LOG_TRIVIAL(trace) << "Received login from wizard.";
|
BOOST_LOG_TRIVIAL(trace) << "Received login from wizard.";
|
||||||
user_account->on_login_code_recieved(evt.data);
|
user_account->on_login_code_recieved(evt.data);
|
||||||
@ -970,6 +975,9 @@ void Plater::priv::init()
|
|||||||
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
|
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
|
||||||
|
|
||||||
this->main_frame->on_account_login(user_account->get_access_token());
|
this->main_frame->on_account_login(user_account->get_access_token());
|
||||||
|
|
||||||
|
// notify other instances
|
||||||
|
Slic3r::GUI::wxGetApp().other_instance_message_handler()->multicast_message("STORE_READ");
|
||||||
} else {
|
} else {
|
||||||
// refresh do different operations than on_account_login
|
// refresh do different operations than on_account_login
|
||||||
this->main_frame->on_account_did_refresh(user_account->get_access_token());
|
this->main_frame->on_account_did_refresh(user_account->get_access_token());
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include "InstanceCheck.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
|
||||||
#include <wx/stdpaths.h>
|
#include <wx/stdpaths.h>
|
||||||
|
|
||||||
namespace pt = boost::property_tree;
|
namespace pt = boost::property_tree;
|
||||||
@ -67,7 +70,9 @@ void UserAccount::do_login()
|
|||||||
}
|
}
|
||||||
void UserAccount::do_logout()
|
void UserAccount::do_logout()
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
m_communication->do_logout();
|
m_communication->do_logout();
|
||||||
|
Slic3r::GUI::wxGetApp().other_instance_message_handler()->multicast_message("STORE_READ");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UserAccount::get_access_token()
|
std::string UserAccount::get_access_token()
|
||||||
|
@ -79,6 +79,8 @@ public:
|
|||||||
void set_current_printer_data(const std::string& data) { m_current_printer_data_json_from_connect = data; }
|
void set_current_printer_data(const std::string& data) { m_current_printer_data_json_from_connect = data; }
|
||||||
|
|
||||||
void set_refresh_time(int seconds) { m_communication->set_refresh_time(seconds); }
|
void set_refresh_time(int seconds) { m_communication->set_refresh_time(seconds); }
|
||||||
|
|
||||||
|
void on_store_read_request() { m_communication->on_store_read_request(); }
|
||||||
private:
|
private:
|
||||||
void set_username(const std::string& username);
|
void set_username(const std::string& username);
|
||||||
|
|
||||||
|
@ -452,6 +452,10 @@ void UserAccountCommunication::do_clear()
|
|||||||
m_session->clear();
|
m_session->clear();
|
||||||
set_username({});
|
set_username({});
|
||||||
m_token_timer->Stop();
|
m_token_timer->Stop();
|
||||||
|
m_slave_read_timer->Stop();
|
||||||
|
m_after_race_lost_timer->Stop();
|
||||||
|
m_next_token_refresh_at = 0;
|
||||||
|
m_behave_as_master = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAccountCommunication::on_login_code_recieved(const std::string& url_message)
|
void UserAccountCommunication::on_login_code_recieved(const std::string& url_message)
|
||||||
@ -821,11 +825,51 @@ void UserAccountCommunication::on_after_race_lost_timer(wxTimerEvent& evt)
|
|||||||
|
|
||||||
void UserAccountCommunication::set_tokens(const StoreData store_data)
|
void UserAccountCommunication::set_tokens(const StoreData store_data)
|
||||||
{
|
{
|
||||||
|
if (m_token_timer->IsRunning()) {
|
||||||
|
m_token_timer->Stop();
|
||||||
|
}
|
||||||
|
if (m_slave_read_timer->IsRunning()) {
|
||||||
|
m_slave_read_timer->Stop();
|
||||||
|
}
|
||||||
|
if (m_after_race_lost_timer->IsRunning()) {
|
||||||
|
m_after_race_lost_timer->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
long long next = store_data.next_timeout.empty() ? 0 : std::stoll(store_data.next_timeout);
|
long long next = store_data.next_timeout.empty() ? 0 : std::stoll(store_data.next_timeout);
|
||||||
m_session->set_tokens(store_data.access_token, store_data.refresh_token, store_data.shared_session_key, next);
|
m_session->set_tokens(store_data.access_token, store_data.refresh_token, store_data.shared_session_key, next);
|
||||||
enqueue_id_action();
|
enqueue_id_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserAccountCommunication::on_store_read_request()
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__;
|
||||||
|
StoreData stored_data;
|
||||||
|
read_stored_data(stored_data);
|
||||||
|
|
||||||
|
if (stored_data.refresh_token.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Store is empty - logging out.";
|
||||||
|
do_logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string currrent_access_token = m_session->get_access_token();
|
||||||
|
if (currrent_access_token == stored_data.access_token)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Current token is up to date.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long expires_in_second = stored_data.next_timeout.empty() ? 0 : std::stoll(stored_data.next_timeout) - std::time(nullptr);
|
||||||
|
const auto prior_expiration_secs = std::max(m_last_token_duration_seconds / 24, 10);
|
||||||
|
if (expires_in_second > 0 /*&& expires_in_second > prior_expiration_secs*/) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Token is alive - using it.";
|
||||||
|
set_tokens(stored_data);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Token read from store is expired!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UserAccountCommunication::on_polling_timer(wxTimerEvent& evt)
|
void UserAccountCommunication::on_polling_timer(wxTimerEvent& evt)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ public:
|
|||||||
void set_tokens(const StoreData store_data);
|
void set_tokens(const StoreData store_data);
|
||||||
|
|
||||||
void on_race_lost(); // T5
|
void on_race_lost(); // T5
|
||||||
|
void on_store_read_request();
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<UserAccountSession> m_session;
|
std::unique_ptr<UserAccountSession> m_session;
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user