diff --git a/src/downloader/DownloaderApp.cpp b/src/downloader/DownloaderApp.cpp index 4cf3b5369f..9fd5dbf627 100644 --- a/src/downloader/DownloaderApp.cpp +++ b/src/downloader/DownloaderApp.cpp @@ -71,6 +71,7 @@ bool DownloadApp::OnInit() if (m_dwnldr_send->get_instance_exists()) { + std::cout << "other instance exists" << std::endl; m_other_exists = true; m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(0,0)); return wxApp::OnInit(); @@ -78,18 +79,20 @@ bool DownloadApp::OnInit() m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(450, 340)); m_frame->Show(true); -#ifdef _WIN32 - wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) { - auto frame = dynamic_cast(win); - COPYDATASTRUCT* copy_data_structure = { 0 }; - copy_data_structure = (COPYDATASTRUCT*)lParam; - if (copy_data_structure->dwData == 1) { - LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData; - frame->handle_message(arguments); - } - return true; - }); -#endif +//#ifdef _WIN32 +// wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) { +// auto frame = dynamic_cast(win); +// COPYDATASTRUCT* copy_data_structure = { 0 }; +// copy_data_structure = (COPYDATASTRUCT*)lParam; +// if (copy_data_structure->dwData == 1) { +// LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData; +// frame->handle_message(arguments); +// } +// return true; +// }); +//#endif + m_message_handler = std::make_unique(); + m_message_handler->init(m_frame); return wxApp::OnInit(); } @@ -124,7 +127,7 @@ bool DownloadApp::OnCmdLineParsed(wxCmdLineParser& parser) m_frame->log("sent " + url); m_frame->Close(true); return false; - } else { + } else { if (!url.empty() && m_frame != nullptr) { m_frame->start_download(std::move(url)); //m_frame->start_download("prusaslicer://open?file=https%3A%2F%2Fmedia.printables.com%2Fmedia%2Fprints%2F152208%2Fstls%2F1431590_8b8287b3-03b1-4cbe-82d0-268a0affa171%2Ff1_logo.stl"); @@ -203,6 +206,8 @@ DownloadFrame::DownloadFrame(const wxString& title, const wxPoint& pos, const wx Bind(EVT_FILE_PROGRESS, &DownloadFrame::on_progress, this); Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this); Bind(EVT_FILE_NAME_CHANGE, &DownloadFrame::on_name_change, this); + Bind(EVT_URL_MSG, &DownloadFrame::on_url_msg, this); + } void DownloadFrame::start_download(wxString url) @@ -498,10 +503,13 @@ wxString DownloadFrame::get_folder_path_of(int id) const void DownloadFrame::handle_message(const wxString& msg) { - //log("recieved: " + msg); start_download(msg); } +void DownloadFrame::on_url_msg(wxCommandEvent& event) +{ + handle_message(event.GetString()); +} //wxIMPLEMENT_APP_NO_MAIN(MyApp); diff --git a/src/downloader/DownloaderApp.hpp b/src/downloader/DownloaderApp.hpp index 2267135e98..e95552773c 100644 --- a/src/downloader/DownloaderApp.hpp +++ b/src/downloader/DownloaderApp.hpp @@ -32,7 +32,8 @@ private: void on_cancel_button(wxCommandEvent& event); void on_pause_button(wxCommandEvent& event); void on_resume_button(wxCommandEvent& event); - + void on_url_msg(wxCommandEvent& event); + void update_state_labels(); void start_next(); void set_download_state(int id, DownloadState state); @@ -82,6 +83,7 @@ class DownloadApp : public wxApp protected: DownloadFrame* m_frame{ nullptr }; std::unique_ptr m_dwnldr_send; + std::unique_ptr m_message_handler; bool m_other_exists { false }; @@ -90,7 +92,7 @@ public: void OnInitCmdLine(wxCmdLineParser& parser) override; bool OnCmdLineParsed(wxCmdLineParser& parser) override; - + DownloadFrame* get_frame() { return m_frame; } }; wxDECLARE_APP(DownloadApp); diff --git a/src/downloader/InstanceSend.cpp b/src/downloader/InstanceSend.cpp index 7ecab44457..3b5e3513f0 100644 --- a/src/downloader/InstanceSend.cpp +++ b/src/downloader/InstanceSend.cpp @@ -1,4 +1,6 @@ #include "InstanceSend.hpp" + +#include "DownloaderApp.hpp" #ifdef _WIN32 #include @@ -7,7 +9,12 @@ #include #include +#include #include +#include + +#include "libslic3r/Config.hpp" + namespace Downloader { @@ -208,7 +215,247 @@ bool execute_command(const wxString& command) { return wxExecute(command); } -#endif + +#elif defined(__linux__) + +bool send_message_slicer(const std::string &message_text, const std::string &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 interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + version; + std::string method_name = "AnotherInstance"; + //std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck"; + std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + version; + + std::cout << "interface_name: " << interface_name << std::endl; + std::cout << "method_name: " << method_name<< std::endl; + std::cout << "object_name: " << object_name << std::endl; + // 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; + std::cout << "DBus Connection Error. Message to another instance wont be send." << std::endl; + std::cout << "DBus Connection Error: " << err.message << std::endl; + dbus_error_free(&err); + return true; + } + if (NULL == conn) { + //BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send."; + std::cout << "DBus Connection is NULL. Message to another instance wont be send." << std::endl; + return true; + } + std::cout<< "dbus_bus_get SUCCESS" << std::endl; + //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."; + std::cout << "DBus Message is NULL. Message to another instance wont be send."<< std::endl; + dbus_connection_unref(conn); + return true; + } + std::cout<< "dbus_message_new_method_call SUCCESS" << std::endl; + //the AnotherInstance 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."; + std::cout << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send." << std::endl; + dbus_message_unref(msg); + dbus_connection_unref(conn); + 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."; + std::cout << "Ran out of memory while sending DBus message." << std::endl; + dbus_message_unref(msg); + dbus_connection_unref(conn); + return true; + } + dbus_connection_flush(conn); + + BOOST_LOG_TRIVIAL(trace) << "DBus message sent."; + std::cout << "DBus message sent." << std::endl; + + // free the message and close the connection + dbus_message_unref(msg); + dbus_connection_unref(conn); + return true; +} + +bool send_message_downloader(const std::string &message_text, const std::string &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 interface_name = "com.prusa3d.prusaslicer.Downloader.Object" + version; + std::string method_name = "AnotherInstance"; + //std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck"; + std::string object_name = "/com/prusa3d/prusaslicer/Downloader/Object" + version; + + std::cout << "interface_name: " << interface_name << std::endl; + std::cout << "method_name: " << method_name<< std::endl; + std::cout << "object_name: " << object_name << std::endl; + // 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; + std::cout << "DBus Connection Error. Message to another instance wont be send." << std::endl; + std::cout << "DBus Connection Error: " << err.message << std::endl; + dbus_error_free(&err); + return true; + } + if (NULL == conn) { + //BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send."; + std::cout << "DBus Connection is NULL. Message to another instance wont be send." << std::endl; + return true; + } + std::cout<< "dbus_bus_get SUCCESS" << std::endl; + //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."; + std::cout << "DBus Message is NULL. Message to another instance wont be send."<< std::endl; + dbus_connection_unref(conn); + return true; + } + std::cout<< "dbus_message_new_method_call SUCCESS" << std::endl; + //the AnotherInstance 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."; + std::cout << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send." << std::endl; + dbus_message_unref(msg); + dbus_connection_unref(conn); + 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."; + std::cout << "Ran out of memory while sending DBus message." << std::endl; + dbus_message_unref(msg); + dbus_connection_unref(conn); + return true; + } + dbus_connection_flush(conn); + + BOOST_LOG_TRIVIAL(trace) << "DBus message sent."; + std::cout << "DBus message sent." << std::endl; + + // free the message and close the connection + dbus_message_unref(msg); + dbus_connection_unref(conn); + return true; +} + +bool get_other_downloader_exists(const std::string &version) +{ + + DBusMessage* msg; + DBusMessage* reply; + const char * result = nullptr; + // 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.Downloader.Object" + version; + std::string method_name = "Introspect"; + std::string object_name = "/com/prusa3d/prusaslicer/Downloader/Object" + version; + + // 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; + std::cout << "DBus Connection Error. Message to another instance wont be send." << std::endl; + std::cout << "DBus Connection Error: " << err.message << std::endl; + dbus_error_free(&err); + return true; + } + if (NULL == conn) { + //BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send."; + std::cout << "DBus Connection is NULL. Message to another instance wont be send." << std::endl; + return true; + } + //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."; + std::cout << "DBus Message is NULL. Message to another instance wont be send."<< std::endl; + dbus_connection_unref(conn); + return true; + } + //the AnotherInstance method is not sending reply. + dbus_message_set_no_reply(msg, TRUE); + + if ( nullptr == (reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err)) ) { + dbus_message_unref(msg); + dbus_connection_unref(conn); + perror(err.name); + perror(err.message); + return true; + } + + if ( !dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID) ) { + dbus_message_unref(msg); + dbus_message_unref(reply); + dbus_connection_unref(conn); + perror(err.name); + perror(err.message); + return true; + } + // Work with the results of the remote procedure call + + std::cout << "Connected to D-Bus as \"" << ::dbus_bus_get_unique_name(conn) << "\"." << std::endl; + std::cout << "Introspection Result:" << std::endl; + std::cout << std::endl << result << std::endl << std::endl; + + //dbus_connection_flush(conn); + + //BOOST_LOG_TRIVIAL(trace) << "DBus message sent."; + std::cout << "DBus message sent." << std::endl; + + // free the message and close the connection + dbus_message_unref(msg); + dbus_connection_unref(conn); + return false; + +} + +#endif //__APPLE__/__linux__ + } @@ -227,8 +474,15 @@ bool SlicerSend::send_path(const wxString& path) const #ifdef _WIN32 std::string escaped = escape_strings_cstyle({ "prusa-downloader", boost::nowide::narrow(path) }); return send_message_slicer(boost::nowide::widen(escaped)); +#else + // todo: this path will be different + std::string slicer_path = (boost::dll::program_location().parent_path().parent_path() / "prusa-slicer").string(); + size_t hashed_path = std::hash{}(boost::filesystem::canonical(boost::filesystem::system_complete(slicer_path)).string()); + std::string lock_name = std::to_string(hashed_path); + std::cout << "hash: "<< lock_name; + std::string escaped = escape_strings_cstyle({ "prusa-downloader", boost::nowide::narrow(path) }); + return send_message_slicer(escaped, lock_name); #endif - return false; } bool SlicerSend::start_with_path(const wxString& path) const @@ -259,6 +513,11 @@ bool DownloaderSend::get_instance_exists() const { #ifdef _WIN32 return !EnumWindows(EnumWindowsProcDownloader, 0); +#else + std::string slicer_path = (boost::dll::program_location()).string(); + size_t hashed_path = std::hash{}(boost::filesystem::canonical(boost::filesystem::system_complete(slicer_path)).string()); + std::string lock_name = std::to_string(hashed_path); + return !get_other_downloader_exists(lock_name); #endif return false; } @@ -267,9 +526,343 @@ bool DownloaderSend::send_url(const wxString& url) const #ifdef _WIN32 //std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(url) }); return send_message_downloader(url); +#else + std::string slicer_path = boost::dll::program_location().string(); + size_t hashed_path = std::hash{}(boost::filesystem::canonical(boost::filesystem::system_complete(slicer_path)).string()); + std::string lock_name = std::to_string(hashed_path); + return send_message_downloader(boost::nowide::narrow(url), lock_name); #endif return false; } +// --------------OtherDownloaderMessageHandler--------------------- + +wxDEFINE_EVENT(EVT_URL_MSG, wxCommandEvent); +wxEvtHandler* evt_handler_global; + +void OtherDownloaderMessageHandler::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 defined(__APPLE__) + //this->register_for_messages(wxGetApp().get_instance_hash_string()); +#endif //__APPLE__ + +#ifdef BACKGROUND_DOWNLOADER_MESSAGE_LISTENER + std::cout<< "init OtherDownloaderMessageHandler"<GetHandle(); + //RemoveProp(hwnd, L"Instance_Hash_Minor"); + //RemoveProp(hwnd, L"Instance_Hash_Major"); +#endif //_WIN32 +#if __APPLE__ + //delete macos implementation + //this->unregister_for_messages(); +#endif //__APPLE__ +#ifdef BACKGROUND_DOWNLOADER_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 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_DOWNLOADER_MESSAGE_LISTENER + m_callback_evt_handler = nullptr; + m_initialized = false; + } +} + +#ifdef _WIN32 +void OtherDownloaderMessageHandler::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 { + // returns ::path to possible model or empty ::path if input string is not existing path + 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 + +void OtherDownloaderMessageHandler::handle_message(const std::string& message) +{ + BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message; + + std::vector args; + bool parsed = Slic3r::unescape_strings_cstyle(message, args); + assert(parsed); + if (! parsed) { + BOOST_LOG_TRIVIAL(error) << "message from other instance is incorrectly formatted: " << message; + return; + } + + std::vector paths; + // Skip the first argument, it is the path to the slicer executable. + auto it = args.begin(); + for (++ it; it != args.end(); ++ it) { + //boost::filesystem::path p = MessageHandlerInternal::get_path(*it); + //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(Slic3r::GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector(std::move(paths)))); + //} + } +} + +#ifdef __APPLE__ +//void OtherDownloaderMessageHandler::handle_message_other_closed() +//{ +// instance_check_internal::get_lock(wxGetApp().get_instance_hash_string() + ".lock", data_dir() + "/cache/"); +//} +#endif //__APPLE__ + +#ifdef BACKGROUND_DOWNLOADER_MESSAGE_LISTENER + +namespace +{ + std::string get_instance_hash() + { + std::string slicer_path = (boost::dll::program_location()).string(); + size_t hashed_path = std::hash{}(boost::filesystem::canonical(boost::filesystem::system_complete(slicer_path)).string()); + std::string lock_name = std::to_string(hashed_path); + //std::cout << "hash: "<< lock_name; + return lock_name; + } + //reply to introspect makes our DBus object visible for other programs like D-Feet + void respond_to_introspect(DBusConnection *connection, DBusMessage *request) + { + DBusMessage *reply; + const char *introspection_data = + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " "; + + 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 + void handle_method_another_instance(DBusConnection *connection, DBusMessage *request) + { + 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(trace) << "Dbus method AnotherInstance received with wrong arguments."; + dbus_error_free(&err); + return; + } + + if (evt_handler_global) { + wxCommandEvent* evt = new wxCommandEvent(EVT_URL_MSG); + evt->SetString(boost::nowide::widen(text)); + evt_handler_global->QueueEvent(evt); + } + + } + + //every dbus message received comes here +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.Downloader.Object" + get_instance_hash(); + //BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name; + std::cout << "DBus message received: interface: " << interface_name << ", member: " << member_name << std::endl; + 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(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstance", member_name)) { + handle_method_another_instance(connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Introspect", member_name)) { + respond_to_introspect(connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +} //namespace + +void OtherDownloaderMessageHandler::listen() +{ + DBusConnection* conn; + DBusError err; + int name_req_val; + DBusObjectPathVTable vtable; + std::string instance_hash = get_instance_hash(); + std::string interface_name = "com.prusa3d.prusaslicer.Downloader.Object" + instance_hash; + std::string object_name = "/com/prusa3d/prusaslicer/Downloader/Object" + instance_hash; + + //BOOST_LOG_TRIVIAL(debug) << "init dbus listen " << interface_name << " " << object_name; + std::cout<< "init dbus listen " << interface_name << " " << object_name << std::endl; + + 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; + } + + evt_handler_global = m_callback_evt_handler; + + // Set callbacks. Unregister function should not be nessary. + vtable.message_function = 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 "<< object_name <<" registered. Starting listening for messages."; + std::cout << "Dbus object "<< object_name <<" registered. Starting listening for messages." << std::endl; + + for (;;) { + // Wait for 1 second + // Cancellable. + { + std::unique_lock 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_DOWNLOADER_MESSAGE_LISTENER + + + + + + } \ No newline at end of file diff --git a/src/downloader/InstanceSend.hpp b/src/downloader/InstanceSend.hpp index 7c113a482f..5d98eea56b 100644 --- a/src/downloader/InstanceSend.hpp +++ b/src/downloader/InstanceSend.hpp @@ -3,6 +3,16 @@ #include #include +#include + +#include +#include +#include + + +#if __linux__ +#include /* Pull in all of D-Bus headers. */ +#endif //__linux__ namespace Downloader { class SlicerSend @@ -20,5 +30,68 @@ public: bool get_instance_exists() const; bool send_url(const wxString& url) const; }; + +#if __linux__ + #define BACKGROUND_DOWNLOADER_MESSAGE_LISTENER +#endif // __linux__ + +class OtherDownloaderMessageHandler +{ +public: + OtherDownloaderMessageHandler() = default; + OtherDownloaderMessageHandler(OtherDownloaderMessageHandler const&) = delete; + void operator=(OtherDownloaderMessageHandler const&) = delete; + ~OtherDownloaderMessageHandler() { assert(!m_initialized); } + + // inits listening, on each platform different. On linux starts background thread + void init(wxEvtHandler* callback_evt_handler); + // stops listening, on linux stops the background thread + //void shutdown(MainFrame* main_frame); + void shutdown(); + + //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(const std::string& message); +#ifdef __APPLE__ + // Messege form other instance, that it deleted its lockfile - first instance to get it will create its own. + //void handle_message_other_closed(); +#endif //__APPLE__ +#ifdef _WIN32 + static void init_windows_properties(MainFrame* main_frame, size_t instance_hash); +#endif //WIN32 +private: + bool m_initialized { false }; + wxEvtHandler* m_callback_evt_handler { nullptr }; + +#ifdef BACKGROUND_DOWNLOADER_MESSAGE_LISTENER + //worker thread to listen incoming dbus communication + boost::thread m_thread; + std::condition_variable m_thread_stop_condition; + mutable std::mutex m_thread_stop_mutex; + bool m_stop{ false }; + bool m_start{ true }; + + // background thread method + void listen(); +#endif //BACKGROUND_DOWNLOADER_MESSAGE_LISTENER + +#if __APPLE__ + //implemented at InstanceCheckMac.mm + //void register_for_messages(const std::string &version_hash); + //void unregister_for_messages(); + // Opaque pointer to RemovableDriveManagerMM + //void* m_impl_osx; +public: + //void bring_instance_forward(); +#endif //__APPLE__ + +}; + +wxDECLARE_EVENT(EVT_URL_MSG, wxCommandEvent); + } #endif \ No newline at end of file