Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer

This commit is contained in:
enricoturri1966 2020-05-15 12:26:18 +02:00
commit 94b431f3af
10 changed files with 285 additions and 199 deletions

View file

@ -536,9 +536,6 @@ int CLI::run(int argc, char **argv)
return -1; return -1;
} }
//gui->app_config = app_config;
//app_config = nullptr;
// gui->autosave = m_config.opt_string("autosave"); // gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui); GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, this, &load_configs] { gui->CallAfter([gui, this, &load_configs] {

View file

@ -323,6 +323,12 @@ void GUI_App::init_app_config()
} }
} }
void GUI_App::init_single_instance_checker(const std::string &name, const std::string &path)
{
BOOST_LOG_TRIVIAL(debug) << "init wx instance checker " << name << " "<< path;
m_single_instance_checker = std::make_unique<wxSingleInstanceChecker>(boost::nowide::widen(name), boost::nowide::widen(path));
}
bool GUI_App::OnInit() bool GUI_App::OnInit()
{ {
try { try {
@ -451,6 +457,10 @@ bool GUI_App::on_init_inner()
preset_updater->slic3r_update_notify(); preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle); preset_updater->sync(preset_bundle);
}); });
#ifdef _WIN32
//sets window property to mainframe so other instances can indentify it
OtherInstanceMessageHandler::init_windows_properties(mainframe, m_instance_hash_int);
#endif //WIN32
} }
}); });

View file

@ -13,6 +13,7 @@
#include <wx/colour.h> #include <wx/colour.h>
#include <wx/font.h> #include <wx/font.h>
#include <wx/string.h> #include <wx/string.h>
#include <wx/snglinst.h>
#include <mutex> #include <mutex>
#include <stack> #include <stack>
@ -106,6 +107,9 @@ class GUI_App : public wxApp
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue; std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
ConfigWizard* m_wizard; // Managed by wxWindow tree ConfigWizard* m_wizard; // Managed by wxWindow tree
std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler; std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker;
std::string m_instance_hash_string;
size_t m_instance_hash_int;
public: public:
bool OnInit() override; bool OnInit() override;
bool initialized() const { return m_initialized; } bool initialized() const { return m_initialized; }
@ -194,6 +198,12 @@ public:
RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); } RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); } OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); }
wxSingleInstanceChecker* single_instance_checker() {return m_single_instance_checker.get();}
void init_single_instance_checker(const std::string &name, const std::string &path);
void set_instance_hash (const size_t hash) { m_instance_hash_int = hash; m_instance_hash_string = std::to_string(hash); }
std::string get_instance_hash_string () { return m_instance_hash_string; }
size_t get_instance_hash_int () { return m_instance_hash_int; }
ImGuiWrapper* imgui() { return m_imgui.get(); } ImGuiWrapper* imgui() { return m_imgui.get(); }

View file

@ -1,13 +1,20 @@
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "InstanceCheck.hpp" #include "InstanceCheck.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp"
#include "boost/nowide/convert.hpp" #include "boost/nowide/convert.hpp"
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <iostream> #include <iostream>
#include <unordered_map>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#ifdef _WIN32
#include <strsafe.h>
#endif //WIN32
#if __linux__ #if __linux__
#include <dbus/dbus.h> /* Pull in all of D-Bus headers. */ #include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
#endif //__linux__ #endif //__linux__
@ -20,218 +27,225 @@ namespace instance_check_internal
bool should_send; bool should_send;
std::string cl_string; std::string cl_string;
}; };
static CommandLineAnalysis process_command_line(int argc, char** argv) //d:\3dmodels\Klapka\Klapka.3mf static CommandLineAnalysis process_command_line(int argc, char** argv)
{ {
CommandLineAnalysis ret { false }; CommandLineAnalysis ret { false };
if (argc < 2) if (argc < 2)
return ret; return ret;
ret.cl_string = argv[0]; ret.cl_string = escape_string_cstyle(argv[0]);
for (size_t i = 1; i < argc; i++) { for (size_t i = 1; i < argc; ++i) {
std::string token = argv[i]; const std::string token = argv[i];
if (token == "--single-instance") { if (token == "--single-instance" || token == "--single-instance=1") {
ret.should_send = true; ret.should_send = true;
} else { } else {
ret.cl_string += " "; ret.cl_string += " : ";
ret.cl_string += token; ret.cl_string += escape_string_cstyle(token);
} }
} }
BOOST_LOG_TRIVIAL(debug) << "single instance: "<< ret.should_send << ". other params: " << ret.cl_string; BOOST_LOG_TRIVIAL(debug) << "single instance: "<< ret.should_send << ". other params: " << ret.cl_string;
return ret; return ret;
} }
} //namespace instance_check_internal
#if _WIN32
#ifdef _WIN32
namespace instance_check_internal
{
static HWND l_prusa_slicer_hwnd; static HWND l_prusa_slicer_hwnd;
static BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam) static BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam)
{ {
//checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance //checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance
//search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel //search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel
//other option would be do a mutex and check for its existence //other option would be do a mutex and check for its existence
TCHAR wndText[1000]; //BOOST_LOG_TRIVIAL(error) << "ewp: version: " << l_version_wstring;
TCHAR className[1000]; TCHAR wndText[1000];
TCHAR className[1000];
GetClassName(hwnd, className, 1000); GetClassName(hwnd, className, 1000);
GetWindowText(hwnd, wndText, 1000); GetWindowText(hwnd, wndText, 1000);
std::wstring classNameString(className); std::wstring classNameString(className);
std::wstring wndTextString(wndText); std::wstring wndTextString(wndText);
if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") { if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
l_prusa_slicer_hwnd = hwnd; //check if other instances has same instance hash
ShowWindow(hwnd, SW_SHOWMAXIMIZED); //if not it is not same version(binary) as this version
SetForegroundWindow(hwnd); HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
return false; size_t other_instance_hash = PtrToUint(handle);
size_t other_instance_hash_major;
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;
size_t my_instance_hash = GUI::wxGetApp().get_instance_hash_int();
if(my_instance_hash == other_instance_hash)
{
BOOST_LOG_TRIVIAL(debug) << "win enum - found correct instance";
l_prusa_slicer_hwnd = hwnd;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
SetForegroundWindow(hwnd);
return false;
}
BOOST_LOG_TRIVIAL(debug) << "win enum - found wrong instance";
} }
return true; return true;
} }
static void send_message(const HWND hwnd) static bool send_message(const std::string& message, const std::string &version)
{ {
LPWSTR command_line_args = GetCommandLine(); if (!EnumWindows(EnumWindowsProc, 0)) {
//Create a COPYDATASTRUCT to send the information std::wstring wstr = boost::nowide::widen(message);
//cbData represents the size of the information we want to send. //LPWSTR command_line_args = wstr.c_str();//GetCommandLine();
//lpData represents the information we want to send. LPWSTR command_line_args = new wchar_t[wstr.size() + 1];
//dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA). copy(wstr.begin(), wstr.end(), command_line_args);
COPYDATASTRUCT data_to_send = { 0 }; command_line_args[wstr.size()] = 0;
data_to_send.dwData = 1; //Create a COPYDATASTRUCT to send the information
data_to_send.cbData = sizeof(TCHAR) * (wcslen(command_line_args) + 1); //cbData represents the size of the information we want to send.
data_to_send.lpData = command_line_args; //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(command_line_args) + 1);
data_to_send.lpData = command_line_args;
SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send); SendMessage(l_prusa_slicer_hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
} return true;
} //namespace instance_check_internal
bool instance_check(int argc, char** argv, bool app_config_single_instance)
{
instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
if (cla.should_send || app_config_single_instance) {
// Call EnumWidnows with own callback. cons: Based on text in the name of the window and class name which is generic.
if (!EnumWindows(instance_check_internal::EnumWindowsProc, 0)) {
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
instance_check_internal::send_message(instance_check_internal::l_prusa_slicer_hwnd);
return true;
} }
return false;
} }
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
return false;
}
#elif defined(__APPLE__) #else
namespace instance_check_internal static bool get_lock(const std::string& name, const std::string& path)
{
static int get_lock()
{ {
struct flock fl; std::string dest_dir = path + name;
int fdlock; BOOST_LOG_TRIVIAL(debug) <<"full lock path: "<< dest_dir;
struct flock fl;
int fdlock;
fl.l_type = F_WRLCK; fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET; fl.l_whence = SEEK_SET;
fl.l_start = 0; fl.l_start = 0;
fl.l_len = 1; fl.l_len = 1;
if ((fdlock = open(dest_dir.c_str(), O_WRONLY | O_CREAT, 0666)) == -1)
if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1) return true;
return 0;
if (fcntl(fdlock, F_SETLK, &fl) == -1) if (fcntl(fdlock, F_SETLK, &fl) == -1)
return 0; return true;
return 1; return false;
} }
} //namespace instance_check_internal
bool instance_check(int argc, char** argv, bool app_config_single_instance) #endif //WIN32
{ #if defined(__APPLE__)
instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) { static bool send_message(const std::string &message_text, const std::string &version)
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; {
send_message_mac(cla.cl_string); //std::string v(version);
return true; //std::replace(v.begin(), v.end(), '.', '-');
//if (!instance_check_internal::get_lock(v))
{
send_message_mac(message_text, version);
return true;
}
return false;
} }
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
return false;
}
#elif defined(__linux__) #elif defined(__linux__)
namespace instance_check_internal static bool send_message(const std::string &message_text, const std::string &version)
{
static int get_lock()
{ {
struct flock fl; /*std::string v(version);
int fdlock; std::replace(v.begin(), v.end(), '.', '-');
fl.l_type = F_WRLCK; if (!instance_check_internal::get_lock(v))*/
fl.l_whence = SEEK_SET; /*auto checker = new wxSingleInstanceChecker;
fl.l_start = 0; if ( !checker->IsAnotherRunning() ) */
fl.l_len = 1; {
DBusMessage* msg;
if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1) DBusMessageIter args;
return 0; DBusConnection* conn;
DBusError err;
if (fcntl(fdlock, F_SETLK, &fl) == -1) dbus_uint32_t serial = 0;
return 0; const char* sigval = message_text.c_str();
//std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
return 1; std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + version;
} std::string method_name = "AnotherInstace";
//std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck";
static void send_message(std::string message_text) std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + version;
{
DBusMessage* msg;
DBusMessageIter args;
DBusConnection* conn;
DBusError err;
dbus_uint32_t serial = 0;
const char* sigval = message_text.c_str();
std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
std::string method_name = "AnotherInstace";
std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck";
// initialise the error value // initialise the error value
dbus_error_init(&err); dbus_error_init(&err);
// connect to bus, and check for errors (use SESSION bus everywhere!) // connect to bus, and check for errors (use SESSION bus everywhere!)
conn = dbus_bus_get(DBUS_BUS_SESSION, &err); conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&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. Message to another instance wont be send.";
BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message; BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: " << err.message;
dbus_error_free(&err); dbus_error_free(&err);
return; return true;
} }
if (NULL == conn) { if (NULL == conn) {
BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send."; BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send.";
return; return true;
} }
//some sources do request interface ownership before constructing msg but i think its wrong. //some sources do request interface ownership before constructing msg but i think its wrong.
//create new method call message //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()); 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) { if (NULL == msg) {
BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send."; BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send.";
dbus_connection_unref(conn); dbus_connection_unref(conn);
return; return true;
} }
//the AnotherInstace method is not sending reply. //the AnotherInstace method is not sending reply.
dbus_message_set_no_reply(msg, TRUE); dbus_message_set_no_reply(msg, TRUE);
//append arguments to message //append arguments to message
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &sigval, DBUS_TYPE_INVALID)) { 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."; 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_message_unref(msg);
dbus_connection_unref(conn); dbus_connection_unref(conn);
return; return true;
}
// 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 true;
}
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;
} }
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;
}
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);
} }
#endif //__APPLE__/__linux__
} //namespace instance_check_internal } //namespace instance_check_internal
bool instance_check(int argc, char** argv, bool app_config_single_instance) bool instance_check(int argc, char** argv, bool app_config_single_instance)
{ {
std::size_t hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
std::string lock_name = std::to_string(hashed_path);
GUI::wxGetApp().set_instance_hash(hashed_path);
BOOST_LOG_TRIVIAL(debug) <<"full path: "<< lock_name;
instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv); instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) { #ifdef _WIN32
GUI::wxGetApp().init_single_instance_checker(lock_name + ".lock", data_dir() + "/cache/");
if ((cla.should_send || app_config_single_instance) && GUI::wxGetApp().single_instance_checker()->IsAnotherRunning()) {
#else // mac & linx
if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && (cla.should_send || app_config_single_instance)) {
#endif
instance_check_internal::send_message(cla.cl_string, lock_name);
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
instance_check_internal::send_message(cla.cl_string);
return true; return true;
} }
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set."; BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
return false; return false;
} }
#endif //_WIN32/__APPLE__/__linux__
namespace GUI { namespace GUI {
@ -248,23 +262,24 @@ void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
m_initialized = true; m_initialized = true;
m_callback_evt_handler = callback_evt_handler; m_callback_evt_handler = callback_evt_handler;
#if _WIN32
//create_listener_window();
#endif //_WIN32
#if defined(__APPLE__) #if defined(__APPLE__)
this->register_for_messages(); this->register_for_messages(wxGetApp().get_instance_hash_string());
#endif //__APPLE__ #endif //__APPLE__
#ifdef BACKGROUND_MESSAGE_LISTENER #ifdef BACKGROUND_MESSAGE_LISTENER
m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this))); m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this)));
#endif //BACKGROUND_MESSAGE_LISTENER #endif //BACKGROUND_MESSAGE_LISTENER
} }
void OtherInstanceMessageHandler::shutdown() void OtherInstanceMessageHandler::shutdown(MainFrame* main_frame)
{ {
BOOST_LOG_TRIVIAL(debug) << "message handler shutdown()."; BOOST_LOG_TRIVIAL(debug) << "message handler shutdown().";
assert(m_initialized); assert(m_initialized);
if (m_initialized) { if (m_initialized) {
#ifdef _WIN32
HWND hwnd = main_frame->GetHandle();
RemoveProp(hwnd, L"Instance_Hash_Minor");
RemoveProp(hwnd, L"Instance_Hash_Major");
#endif //_WIN32
#if __APPLE__ #if __APPLE__
//delete macos implementation //delete macos implementation
this->unregister_for_messages(); this->unregister_for_messages();
@ -287,12 +302,46 @@ void OtherInstanceMessageHandler::shutdown()
} }
} }
#ifdef _WIN32
void OtherInstanceMessageHandler::init_windows_properties(MainFrame* main_frame, size_t instance_hash)
{
size_t minor_hash = instance_hash & 0xFFFFFFFF;
size_t major_hash = (instance_hash & 0xFFFFFFFF00000000) >> 32;
HWND hwnd = main_frame->GetHandle();
HANDLE handle_minor = UIntToPtr(minor_hash);
HANDLE handle_major = UIntToPtr(major_hash);
SetProp(hwnd, L"Instance_Hash_Minor", handle_minor);
SetProp(hwnd, L"Instance_Hash_Major", handle_major);
//BOOST_LOG_TRIVIAL(debug) << "window properties initialized " << instance_hash << " (" << minor_hash << " & "<< major_hash;
}
#if 0
void OtherInstanceMessageHandler::print_window_info(HWND hwnd)
{
std::wstring instance_hash = boost::nowide::widen(wxGetApp().get_instance_hash_string());
TCHAR wndText[1000];
TCHAR className[1000];
GetClassName(hwnd, className, 1000);
GetWindowText(hwnd, wndText, 1000);
std::wstring classNameString(className);
std::wstring wndTextString(wndText);
HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
size_t result = PtrToUint(handle);
handle = GetProp(hwnd, L"Instance_Hash_Major");
size_t r2 = PtrToUint(handle);
r2 = (r2 << 32);
result += r2;
BOOST_LOG_TRIVIAL(info) << "window info: " << result;
}
#endif //0
#endif //WIN32
namespace MessageHandlerInternal namespace MessageHandlerInternal
{ {
// returns ::path to possible model or empty ::path if input string is not existing path // returns ::path to possible model or empty ::path if input string is not existing path
static boost::filesystem::path get_path(const std::string possible_path) static boost::filesystem::path get_path(std::string possible_path)
{ {
BOOST_LOG_TRIVIAL(debug) << "message part: " << possible_path; BOOST_LOG_TRIVIAL(debug) << "message part:" << possible_path;
if (possible_path.empty() || possible_path.size() < 3) { if (possible_path.empty() || possible_path.size() < 3) {
BOOST_LOG_TRIVIAL(debug) << "empty"; BOOST_LOG_TRIVIAL(debug) << "empty";
@ -312,9 +361,9 @@ namespace MessageHandlerInternal
} }
} //namespace MessageHandlerInternal } //namespace MessageHandlerInternal
void OtherInstanceMessageHandler::handle_message(const std::string message) { void OtherInstanceMessageHandler::handle_message(const std::string& message) {
std::vector<boost::filesystem::path> paths; std::vector<boost::filesystem::path> paths;
auto next_space = message.find(' '); auto next_space = message.find(" : ");
size_t last_space = 0; size_t last_space = 0;
int counter = 0; int counter = 0;
@ -323,17 +372,17 @@ void OtherInstanceMessageHandler::handle_message(const std::string message) {
while (next_space != std::string::npos) while (next_space != std::string::npos)
{ {
if (counter != 0) { if (counter != 0) {
const std::string possible_path = message.substr(last_space, next_space - last_space); std::string possible_path = message.substr(last_space, next_space - last_space);
boost::filesystem::path p = MessageHandlerInternal::get_path(possible_path); boost::filesystem::path p = MessageHandlerInternal::get_path(std::move(possible_path));
if(!p.string().empty()) if(!p.string().empty())
paths.emplace_back(p); paths.emplace_back(p);
} }
last_space = next_space; last_space = next_space + 3;
next_space = message.find(' ', last_space + 1); next_space = message.find(" : ", last_space);
counter++; counter++;
} }
if (counter != 0 ) { if (counter != 0 ) {
boost::filesystem::path p = MessageHandlerInternal::get_path(message.substr(last_space + 1)); boost::filesystem::path p = MessageHandlerInternal::get_path(message.substr(last_space));
if (!p.string().empty()) if (!p.string().empty())
paths.emplace_back(p); paths.emplace_back(p);
} }
@ -401,13 +450,12 @@ namespace MessageHandlerDBusInternal
{ {
const char* interface_name = dbus_message_get_interface(message); const char* interface_name = dbus_message_get_interface(message);
const char* member_name = dbus_message_get_member(message); const char* member_name = dbus_message_get_member(message);
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); respond_to_introspect(connection, message);
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} else if (0 == strcmp("com.prusa3d.prusaslicer.InstanceCheck", interface_name) && 0 == strcmp("AnotherInstace", member_name)) { } else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstace", member_name)) {
handle_method_another_instance(connection, message); handle_method_another_instance(connection, message);
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} }
@ -421,9 +469,11 @@ void OtherInstanceMessageHandler::listen()
DBusError err; DBusError err;
int name_req_val; int name_req_val;
DBusObjectPathVTable vtable; DBusObjectPathVTable vtable;
std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck"; std::string instance_hash = wxGetApp().get_instance_hash_string();
std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck"; std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + instance_hash;
std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + instance_hash;
//BOOST_LOG_TRIVIAL(debug) << "init dbus listen " << interface_name << " " << object_name;
dbus_error_init(&err); dbus_error_init(&err);
// connect to the bus and check for errors (use SESSION bus everywhere!) // connect to the bus and check for errors (use SESSION bus everywhere!)
@ -469,7 +519,7 @@ void OtherInstanceMessageHandler::listen()
return; return;
} }
BOOST_LOG_TRIVIAL(trace) << "Dbus object registered. Starting listening for messages."; BOOST_LOG_TRIVIAL(trace) << "Dbus object "<< object_name <<" registered. Starting listening for messages.";
for (;;) { for (;;) {
// Wait for 1 second // Wait for 1 second

View file

@ -11,10 +11,11 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#if __linux__
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <tbb/mutex.h> #include <tbb/mutex.h>
#include <condition_variable> #include <condition_variable>
#endif // __linux__
namespace Slic3r { namespace Slic3r {
@ -26,11 +27,13 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance);
#if __APPLE__ #if __APPLE__
// 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); void send_message_mac(const std::string& msg, const std::string& version);
#endif //__APPLE__ #endif //__APPLE__
namespace GUI { namespace GUI {
class MainFrame;
#if __linux__ #if __linux__
#define BACKGROUND_MESSAGE_LISTENER #define BACKGROUND_MESSAGE_LISTENER
#endif // __linux__ #endif // __linux__
@ -52,7 +55,7 @@ public:
// inits listening, on each platform different. On linux starts background thread // inits listening, on each platform different. On linux starts background thread
void init(wxEvtHandler* callback_evt_handler); void init(wxEvtHandler* callback_evt_handler);
// stops listening, on linux stops the background thread // stops listening, on linux stops the background thread
void shutdown(); void shutdown(MainFrame* main_frame);
//finds paths to models in message(= command line arguments, first should be prusaSlicer executable) //finds paths to models in message(= command line arguments, first should be prusaSlicer executable)
//and sends them to plater via LoadFromOtherInstanceEvent //and sends them to plater via LoadFromOtherInstanceEvent
@ -60,7 +63,10 @@ public:
// win32 - anybody who has hwnd can send message. // win32 - anybody who has hwnd can send message.
// mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating" // mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating"
// linux - instrospectable on dbus // linux - instrospectable on dbus
void handle_message(const std::string message); void handle_message(const std::string& message);
#ifdef _WIN32
static void init_windows_properties(MainFrame* main_frame, size_t instance_hash);
#endif //WIN32
private: private:
bool m_initialized { false }; bool m_initialized { false };
wxEvtHandler* m_callback_evt_handler { nullptr }; wxEvtHandler* m_callback_evt_handler { nullptr };
@ -79,7 +85,7 @@ private:
#if __APPLE__ #if __APPLE__
//implemented at InstanceCheckMac.mm //implemented at InstanceCheckMac.mm
void register_for_messages(); void register_for_messages(const std::string &version_hash);
void unregister_for_messages(); void unregister_for_messages();
// Opaque pointer to RemovableDriveManagerMM // Opaque pointer to RemovableDriveManagerMM
void* m_impl_osx; void* m_impl_osx;

View file

@ -3,6 +3,6 @@
@interface OtherInstanceMessageHandlerMac : NSObject @interface OtherInstanceMessageHandlerMac : NSObject
-(instancetype) init; -(instancetype) init;
-(void) add_observer; -(void) add_observer:(NSString *)version;
-(void) message_update:(NSNotification *)note; -(void) message_update:(NSNotification *)note;
@end @end

View file

@ -9,10 +9,12 @@
self = [super init]; self = [super init];
return self; return self;
} }
-(void)add_observer -(void)add_observer:(NSString *)version_hash
{ {
NSLog(@"adding observer"); //NSLog(@"adding observer");
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:@"OtherPrusaSlicerInstanceMessage" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; //NSString *nsver = @"OtherPrusaSlicerInstanceMessage" + version_hash;
NSString *nsver = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceMessage", version_hash];
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:nsver object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
} }
-(void)message_update:(NSNotification *)msg -(void)message_update:(NSNotification *)msg
@ -36,19 +38,22 @@
namespace Slic3r { namespace Slic3r {
void send_message_mac(const std::string msg) 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]];
//NSLog(@"sending msg %@", nsmsg); //NSString *nsver = @"OtherPrusaSlicerInstanceMessage" + [NSString stringWithCString:version.c_str() encoding:[NSString defaultCStringEncoding]];
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"OtherPrusaSlicerInstanceMessage" object:nil userInfo:[NSDictionary dictionaryWithObject:nsmsg forKey:@"data"] deliverImmediately:YES]; NSString *nsver = [NSString stringWithCString:version.c_str() encoding:[NSString defaultCStringEncoding]];
NSString *notifname = [NSString stringWithFormat: @"%@%@", @"OtherPrusaSlicerInstanceMessage", nsver];
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:notifname object:nil userInfo:[NSDictionary dictionaryWithObject:nsmsg forKey:@"data"] deliverImmediately:YES];
} }
namespace GUI { namespace GUI {
void OtherInstanceMessageHandler::register_for_messages() void OtherInstanceMessageHandler::register_for_messages(const std::string &version_hash)
{ {
m_impl_osx = [[OtherInstanceMessageHandlerMac alloc] init]; m_impl_osx = [[OtherInstanceMessageHandlerMac alloc] init];
if(m_impl_osx) { if(m_impl_osx) {
[m_impl_osx add_observer]; NSString *nsver = [NSString stringWithCString:version_hash.c_str() encoding:[NSString defaultCStringEncoding]];
[m_impl_osx add_observer:nsver];
} }
} }
@ -59,7 +64,7 @@ void OtherInstanceMessageHandler::unregister_for_messages()
[m_impl_osx release]; [m_impl_osx release];
m_impl_osx = nullptr; m_impl_osx = nullptr;
} else { } else {
NSLog(@"unreegister not required"); NSLog(@"warning: unregister instance InstanceCheck notifications not required");
} }
} }
}//namespace GUI }//namespace GUI

View file

@ -242,7 +242,7 @@ void MainFrame::shutdown()
// Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater. // Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
wxGetApp().removable_drive_manager()->shutdown(); wxGetApp().removable_drive_manager()->shutdown();
//stop listening for messages from other instances //stop listening for messages from other instances
wxGetApp().other_instance_message_handler()->shutdown(); wxGetApp().other_instance_message_handler()->shutdown(this);
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback, // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
// but in rare cases it may not have been called yet. // but in rare cases it may not have been called yet.
wxGetApp().app_config->save(); wxGetApp().app_config->save();

View file

@ -2037,16 +2037,20 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) { this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) {
BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward"; BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward";
//this code maximize app window on Fedora //this code maximize app window on Fedora
wxGetApp().mainframe->Iconize(false); {
if (wxGetApp().mainframe->IsMaximized()) wxGetApp().mainframe->Iconize(false);
wxGetApp().mainframe->Maximize(true); if (wxGetApp().mainframe->IsMaximized())
else wxGetApp().mainframe->Maximize(true);
wxGetApp().mainframe->Maximize(false); else
//this code (without code above) maximize window on Ubuntu wxGetApp().mainframe->Maximize(false);
wxGetApp().mainframe->Restore(); }
wxGetApp().GetTopWindow()->SetFocus(); // focus on my window //this code maximize window on Ubuntu
wxGetApp().GetTopWindow()->Raise(); // bring window to front {
wxGetApp().GetTopWindow()->Show(true); // show the window wxGetApp().mainframe->Restore();
wxGetApp().GetTopWindow()->SetFocus(); // focus on my window
wxGetApp().GetTopWindow()->Raise(); // bring window to front
wxGetApp().GetTopWindow()->Show(true); // show the window
}
}); });
wxGetApp().other_instance_message_handler()->init(this->q); wxGetApp().other_instance_message_handler()->init(this->q);

View file

@ -102,7 +102,11 @@ void PreferencesDialog::build()
def.label = L("Single Instance"); def.label = L("Single Instance");
def.type = coBool; def.type = coBool;
def.tooltip = L("If this is enabled, when staring PrusaSlicer and another instance is running, that instance will be reactivated instead."); #if __APPLE__
def.tooltip = L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances of same app from the command line. In such case this settings will allow only one instance.");
#else
def.tooltip = L("If this is enabled, when staring PrusaSlicer and another instance of same PrusaSlicer is running, that instance will be reactivated instead.");
#endif
def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false }); def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false });
option = Option(def, "single_instance"); option = Option(def, "single_instance");
m_optgroup_general->append_single_option_line(option); m_optgroup_general->append_single_option_line(option);