Browse Source

single slicer instance

check for other instances during startup
send message with command line arguments if found and terminate
listen for those messages and load objects from paths in messages from them
David Kocik 4 years ago
parent
commit
d828a1e80b

+ 3 - 0
CMakeLists.txt

@@ -160,6 +160,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Boost on Raspberry-Pi does not link to pthreads.
     set(THREADS_PREFER_PTHREAD_FLAG ON)
     find_package(Threads REQUIRED)
+
+    find_package(DBus REQUIRED)
+    include_directories(${DBUS_INCLUDE_DIRS})
 endif()
 
 if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)

+ 59 - 0
cmake/modules/FindDBus.cmake

@@ -0,0 +1,59 @@
+# - Try to find DBus
+# Once done, this will define
+#
+#  DBUS_FOUND - system has DBus
+#  DBUS_INCLUDE_DIRS - the DBus include directories
+#  DBUS_LIBRARIES - link these to use DBus
+#
+# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
+# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FIND_PACKAGE(PkgConfig)
+PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
+
+FIND_LIBRARY(DBUS_LIBRARIES
+    NAMES dbus-1
+    HINTS ${PC_DBUS_LIBDIR}
+          ${PC_DBUS_LIBRARY_DIRS}
+)
+
+FIND_PATH(DBUS_INCLUDE_DIR
+    NAMES dbus/dbus.h
+    HINTS ${PC_DBUS_INCLUDEDIR}
+          ${PC_DBUS_INCLUDE_DIRS}
+)
+
+GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
+FIND_PATH(DBUS_ARCH_INCLUDE_DIR
+    NAMES dbus/dbus-arch-deps.h
+    HINTS ${PC_DBUS_INCLUDEDIR}
+          ${PC_DBUS_INCLUDE_DIRS}
+          ${_DBUS_LIBRARY_DIR}
+          ${DBUS_INCLUDE_DIR}
+    PATH_SUFFIXES include
+)
+
+SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)

+ 12 - 1
src/PrusaSlicer.cpp

@@ -51,13 +51,14 @@
     #include "slic3r/GUI/GUI.hpp"
     #include "slic3r/GUI/GUI_App.hpp"
     #include "slic3r/GUI/3DScene.hpp"
+    #include "slic3r/GUI/InstanceCheck.hpp" 
+    #include "slic3r/GUI/AppConfig.hpp" 
 #endif /* SLIC3R_GUI */
 
 using namespace Slic3r;
 
 int CLI::run(int argc, char **argv)
 {
-
 #ifdef __WXGTK__
     // On Linux, wxGTK has no support for Wayland, and the app crashes on
     // startup if gtk3 is used. This env var has to be set explicitly to
@@ -524,6 +525,16 @@ int CLI::run(int argc, char **argv)
 #ifdef SLIC3R_GUI
 // #ifdef USE_WX
         GUI::GUI_App *gui = new GUI::GUI_App();
+
+		bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
+		if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) {
+			//TODO: do we have delete gui and other stuff?
+			return -1;
+		}
+		
+		//gui->app_config = app_config;
+		//app_config = nullptr;
+		
 //		gui->autosave = m_config.opt_string("autosave");
         GUI::GUI_App::SetInstance(gui);
         gui->CallAfter([gui, this, &load_configs] {

+ 2 - 1
src/PrusaSlicer_app_msvc.cpp

@@ -7,6 +7,8 @@
 #include <shellapi.h>
 #include <wchar.h>
 
+
+
 #ifdef SLIC3R_GUI
 extern "C"
 {
@@ -216,7 +218,6 @@ int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */,
 int wmain(int argc, wchar_t **argv)
 {
 #endif
-
     std::vector<wchar_t*> argv_extended;
     argv_extended.emplace_back(argv[0]);
 

+ 5 - 0
src/libslic3r/PrintConfig.cpp

@@ -39,6 +39,11 @@ void PrintConfigDef::init_common_params()
 {
     ConfigOptionDef* def;
 
+	def = this->add("single_instance", coBool);
+	def->label = L("Single Instance");
+	def->mode = comAdvanced;
+	def->set_default_value(new ConfigOptionBool(false));
+
     def = this->add("printer_technology", coEnum);
     def->label = L("Printer technology");
     def->tooltip = L("Printer technology");

+ 8 - 0
src/slic3r/CMakeLists.txt

@@ -161,6 +161,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/DoubleSlider.hpp
     GUI/ObjectDataViewModel.cpp
     GUI/ObjectDataViewModel.hpp
+    GUI/InstanceCheck.cpp
+    GUI/InstanceCheck.hpp
     Utils/Http.cpp
     Utils/Http.hpp
     Utils/FixModelByWin10.cpp
@@ -195,6 +197,8 @@ if (APPLE)
             GUI/RemovableDriveManagerMM.mm
             GUI/RemovableDriveManagerMM.h
             GUI/Mouse3DHandlerMac.mm
+            GUI/InstanceCheckMac.mm
+            GUI/InstanceCheckMac.h
         )
     FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
 
@@ -206,6 +210,10 @@ encoding_check(libslic3r_gui)
 
 target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
 
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) 
+endif()
+
 if(APPLE)
     target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
 endif()

+ 3 - 0
src/slic3r/GUI/AppConfig.cpp

@@ -69,6 +69,9 @@ void AppConfig::set_defaults()
         set("use_retina_opengl", "1");
 #endif
 
+	if (get("single_instance").empty())
+		set("single_instance", "0");
+
     if (get("remember_output_path").empty())
         set("remember_output_path", "1");
 

+ 47 - 25
src/slic3r/GUI/GUI_App.cpp

@@ -50,6 +50,7 @@
 #include "UpdateDialogs.hpp"
 #include "Mouse3DController.hpp"
 #include "RemovableDriveManager.hpp"
+#include "InstanceCheck.hpp"
 
 #ifdef __WXMSW__
 #include <dbt.h>
@@ -209,6 +210,17 @@ static void register_win32_device_notification_event()
 		}
         return false;
     });
+
+	wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
+
+		COPYDATASTRUCT* copy_data_structure = { 0 };
+		copy_data_structure = (COPYDATASTRUCT*)lParam;
+		if (copy_data_structure->dwData == 1) {
+			LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData;
+			Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(boost::nowide::narrow(arguments));
+		}
+		return true;
+		});
 }
 #endif // WIN32
 
@@ -253,7 +265,11 @@ GUI_App::GUI_App()
     , m_imgui(new ImGuiWrapper())
     , m_wizard(nullptr)
 	, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
-{}
+	, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>())
+{
+	//app config initializes early becasuse it is used in instance checking in PrusaSlicer.cpp
+	this->init_app_config();
+}
 
 GUI_App::~GUI_App()
 {
@@ -284,6 +300,30 @@ bool GUI_App::init_opengl()
 }
 #endif // ENABLE_NON_STATIC_CANVAS_MANAGER
 
+void GUI_App::init_app_config()
+{
+	// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
+	SetAppName(SLIC3R_APP_KEY);
+	//SetAppName(SLIC3R_APP_KEY "-beta");
+	SetAppDisplayName(SLIC3R_APP_NAME);
+
+	// Set the Slic3r data directory at the Slic3r XS module.
+	// Unix: ~/ .Slic3r
+	// Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
+	// Mac : "~/Library/Application Support/Slic3r"
+
+	if (data_dir().empty())
+		set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
+
+	if (!app_config)
+		app_config = new AppConfig();
+
+	// load settings
+	app_conf_exists = app_config->exists();
+	if (app_conf_exists) {
+		app_config->load();
+	}
+}
 bool GUI_App::OnInit()
 {
     try {
@@ -301,34 +341,14 @@ bool GUI_App::on_init_inner()
     wxCHECK_MSG(wxDirExists(resources_dir), false,
         wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
 
-    // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
-    SetAppName(SLIC3R_APP_KEY);
-//    SetAppName(SLIC3R_APP_KEY "-beta");
-    SetAppDisplayName(SLIC3R_APP_NAME);
-
-// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
+     // Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
 //    wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0);
-// Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
-// performance when working on high resolution multi-display setups.
+    // Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
+    // performance when working on high resolution multi-display setups.
 //    wxSystemOptions::SetOption("msw.notebook.themed-background", 0);
 
 //     Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
-
-    // Set the Slic3r data directory at the Slic3r XS module.
-    // Unix: ~/ .Slic3r
-    // Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
-    // Mac : "~/Library/Application Support/Slic3r"
-    if (data_dir().empty())
-        set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
-
-    app_config = new AppConfig();
-
-    // load settings
-    app_conf_exists = app_config->exists();
-    if (app_conf_exists) {
-        app_config->load();
-    }
-    
+   
     std::string msg = Http::tls_global_init();
     wxRichMessageDialog
         dlg(nullptr,
@@ -407,6 +427,8 @@ bool GUI_App::on_init_inner()
         if (! plater_)
             return;
 
+		//m_other_instance_message_handler->report();
+
         if (app_config->dirty() && app_config->get("autosave") == "1")
             app_config->save();
 

+ 4 - 1
src/slic3r/GUI/GUI_App.hpp

@@ -35,6 +35,7 @@ class PrintHostJobQueue;
 
 namespace GUI{
 class RemovableDriveManager;
+class OtherInstanceMessageHandler;
 enum FileType
 {
     FT_STL,
@@ -108,7 +109,7 @@ class GUI_App : public wxApp
     std::unique_ptr<ImGuiWrapper> m_imgui;
     std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
     ConfigWizard* m_wizard;    // Managed by wxWindow tree
-
+	std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
 public:
     bool            OnInit() override;
     bool            initialized() const { return m_initialized; }
@@ -196,6 +197,7 @@ public:
     std::vector<Tab *>      tabs_list;
 
 	RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
+	OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); }
 
     ImGuiWrapper* imgui() { return m_imgui.get(); }
 
@@ -211,6 +213,7 @@ public:
 
 private:
     bool            on_init_inner();
+	void            init_app_config();
     void            window_pos_save(wxTopLevelWindow* window, const std::string &name);
     void            window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
     void            window_pos_sanitize(wxTopLevelWindow* window);

+ 495 - 0
src/slic3r/GUI/InstanceCheck.cpp

@@ -0,0 +1,495 @@
+#include "GUI_App.hpp"
+#include "InstanceCheck.hpp"
+
+#include "boost/nowide/convert.hpp"
+#include <boost/log/trivial.hpp>
+#include <iostream>
+
+#include <fcntl.h>
+#include <errno.h>
+
+#if __linux__
+#include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
+#endif //__linux__
+
+namespace Slic3r {
+namespace instance_check_internal
+{
+	struct CommandLineAnalysis
+	{
+		bool           should_send;
+		std::string    cl_string;
+	};
+	static CommandLineAnalysis process_command_line(int argc, char** argv) //d:\3dmodels\Klapka\Klapka.3mf
+	{
+		CommandLineAnalysis ret { false };
+		if (argc < 2)
+			return ret;
+		ret.cl_string = argv[0];
+		for (size_t i = 1; i < argc; i++) {
+			std::string token = argv[i];
+			if (token == "--single-instance") {
+				ret.should_send = true;
+			} else {
+				ret.cl_string += " ";
+				ret.cl_string += token;
+			}
+		}
+		BOOST_LOG_TRIVIAL(debug) << "single instance: "<< ret.should_send << ". other params: " << ret.cl_string;
+		return ret;
+	}
+} //namespace instance_check_internal
+
+#if _WIN32
+
+namespace instance_check_internal
+{
+	static HWND l_prusa_slicer_hwnd;
+	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
+		//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
+		TCHAR wndText[1000];
+		TCHAR className[1000];
+		GetClassName(hwnd, className, 1000);
+		GetWindowText(hwnd, wndText, 1000);
+		std::wstring classNameString(className);
+		std::wstring wndTextString(wndText);
+		if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
+			l_prusa_slicer_hwnd = hwnd;
+			ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+			SetForegroundWindow(hwnd);
+			return false;
+		}
+		return true;
+	}
+	static void send_message(const HWND hwnd)
+	{
+		LPWSTR command_line_args = GetCommandLine();
+		//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(command_line_args) + 1);
+		data_to_send.lpData = command_line_args;
+
+		SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
+	}
+} //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;
+		}
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+
+#elif defined(__APPLE__)
+
+namespace instance_check_internal
+{
+	static int get_lock() 
+	{
+		struct flock fl;
+		int fdlock;
+		fl.l_type = F_WRLCK;
+		fl.l_whence = SEEK_SET;
+		fl.l_start = 0;
+		fl.l_len = 1;
+
+		if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1)
+			return 0;
+
+		if (fcntl(fdlock, F_SETLK, &fl) == -1)
+			return 0;
+
+		return 1;
+	}
+} //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 (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) {
+		BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+		send_message_mac(cla.cl_string);
+		return true;
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+
+#elif defined(__linux__)
+
+namespace instance_check_internal
+{
+	static int get_lock() 
+	{
+		struct flock fl;
+		int fdlock;
+		fl.l_type = F_WRLCK;
+		fl.l_whence = SEEK_SET;
+		fl.l_start = 0;
+		fl.l_len = 1;
+
+		if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1)
+			return 0;
+
+		if (fcntl(fdlock, F_SETLK, &fl) == -1)
+			return 0;
+
+		return 1;
+	}
+
+	static void send_message(std::string message_text)
+	{
+		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
+	    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;
+	    }
+	    if (NULL == conn) { 
+	    	BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send.";
+	        return;
+	    }	    
+
+		//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;
+	    }
+	    //the AnotherInstace 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;
+		}
+
+	    // 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);
+	}
+} //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 (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) {
+		BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+		instance_check_internal::send_message(cla.cl_string);
+		return true;
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+#endif //_WIN32/__APPLE__/__linux__
+
+
+
+namespace GUI {
+
+wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
+wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
+
+void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
+{
+	assert(!m_initialized);
+	assert(m_callback_evt_handler == nullptr);
+	if (m_initialized) 
+		return;
+
+	m_initialized = true;
+	m_callback_evt_handler = callback_evt_handler;
+
+#if _WIN32 
+	//create_listener_window();
+#endif  //_WIN32
+
+#if defined(__APPLE__)
+	this->register_for_messages();
+#endif //__APPLE__
+
+#ifdef BACKGROUND_MESSAGE_LISTENER
+	m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this)));
+#endif //BACKGROUND_MESSAGE_LISTENER
+}
+void OtherInstanceMessageHandler::shutdown()
+{
+	BOOST_LOG_TRIVIAL(debug) << "message handler shutdown().";
+	assert(m_initialized);
+	if (m_initialized) {
+#if __APPLE__
+		//delete macos implementation
+		this->unregister_for_messages();
+#endif //__APPLE__
+#ifdef BACKGROUND_MESSAGE_LISTENER
+		if (m_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_thread_stop_mutex);
+				m_stop = true;
+			}
+			m_thread_stop_condition.notify_all();
+			// Wait for the worker thread to stop.
+			m_thread.join();
+			m_stop = false;
+		}
+#endif //BACKGROUND_MESSAGE_LISTENER
+	m_initialized = false;
+	}
+}
+
+namespace MessageHandlerInternal
+{
+   // 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)
+	{
+		BOOST_LOG_TRIVIAL(debug) << "message part: " << possible_path;
+
+		if (possible_path.empty() || possible_path.size() < 3) {
+			BOOST_LOG_TRIVIAL(debug) << "empty";
+			return boost::filesystem::path();
+		}
+		if (boost::filesystem::exists(possible_path)) {
+			BOOST_LOG_TRIVIAL(debug) << "is path";
+			return boost::filesystem::path(possible_path);
+		} else if (possible_path[0] == '\"') {
+			if(boost::filesystem::exists(possible_path.substr(1, possible_path.size() - 2))) {
+				BOOST_LOG_TRIVIAL(debug) << "is path in quotes";
+				return boost::filesystem::path(possible_path.substr(1, possible_path.size() - 2));
+			}
+		}
+		BOOST_LOG_TRIVIAL(debug) << "is NOT path";
+		return boost::filesystem::path();
+	}
+} //namespace MessageHandlerInternal
+
+void OtherInstanceMessageHandler::handle_message(const std::string message) {
+	std::vector<boost::filesystem::path> paths;
+	auto                                 next_space = message.find(' ');
+	size_t                               last_space = 0;
+	int                                  counter    = 0;
+
+	BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message;
+
+	while (next_space != std::string::npos)
+	{	
+		if (counter != 0) {
+			const std::string possible_path = message.substr(last_space, next_space - last_space);
+			boost::filesystem::path p = MessageHandlerInternal::get_path(possible_path);
+			if(!p.string().empty())
+				paths.emplace_back(p);
+		}
+		last_space = next_space;
+		next_space = message.find(' ', last_space + 1);
+		counter++;
+	}
+	if (counter != 0 ) {
+		boost::filesystem::path p = MessageHandlerInternal::get_path(message.substr(last_space + 1));
+		if (!p.string().empty())
+			paths.emplace_back(p);
+	}
+	if (!paths.empty()) {
+		//wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
+		//if (evt_handler) {
+			wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
+		//}
+	}
+}
+
+#ifdef BACKGROUND_MESSAGE_LISTENER
+
+namespace MessageHandlerDBusInternal
+{
+	//reply to introspect makes our DBus object visible for other programs like D-Feet
+	static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) 
+	{
+    	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.InstanceCheck\">"
+	        "     <method name=\"AnotherInstace\">"
+	        "       <arg name=\"data\" direction=\"in\" 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 AnotherInstance receives message from another PrusaSlicer instance 
+	static void handle_method_another_instance(DBusConnection *connection, DBusMessage *request)
+	{
+	    DBusError     err;
+	    char*         text= "";
+		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(trace) << "Dbus method AnotherInstance received with wrong arguments.";
+	    	dbus_error_free(&err);
+	        return;
+	    }
+	    wxGetApp().other_instance_message_handler()->handle_message(text);
+
+		evt_handler = wxGetApp().plater();
+		if (evt_handler) {
+			wxPostEvent(evt_handler, InstanceGoToFrontEvent(EVT_INSTANCE_GO_TO_FRONT));
+		}
+	}
+	//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);
+
+	    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)) {		
+	        respond_to_introspect(connection, message);
+	        return DBUS_HANDLER_RESULT_HANDLED;
+	    } else if (0 == strcmp("com.prusa3d.prusaslicer.InstanceCheck", interface_name) && 0 == strcmp("AnotherInstace", member_name)) {
+	        handle_method_another_instance(connection, message);
+	        return DBUS_HANDLER_RESULT_HANDLED;
+	    } 
+	    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+} //namespace MessageHandlerDBusInternal
+
+void OtherInstanceMessageHandler::listen()
+{
+    DBusConnection* 	 conn;
+    DBusError 			 err;
+    int 				 name_req_val;
+    DBusObjectPathVTable vtable;
+	std::string			 interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
+    std::string			 object_name 	= "/com/prusa3d/prusaslicer/InstanceCheck";
+
+    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 = MessageHandlerDBusInternal::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(trace) << "Dbus object registered. Starting listening for messages.";
+
+	for (;;) {
+		// Wait for 1 second 
+		// Cancellable.
+		{
+			std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
+			m_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_stop; });
+		}
+		if (m_stop)
+			// Stop the worker thread.
+
+			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);
+}
+#endif //BACKGROUND_MESSAGE_LISTENER
+} // namespace GUI
+} // namespace Slic3r

Some files were not shown because too many files changed in this diff