mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-06 05:38:54 +08:00
dbus communication
This commit is contained in:
parent
9b1bc68961
commit
81028a5270
@ -71,6 +71,7 @@ bool DownloadApp::OnInit()
|
|||||||
|
|
||||||
|
|
||||||
if (m_dwnldr_send->get_instance_exists()) {
|
if (m_dwnldr_send->get_instance_exists()) {
|
||||||
|
std::cout << "other instance exists" << std::endl;
|
||||||
m_other_exists = true;
|
m_other_exists = true;
|
||||||
m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(0,0));
|
m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(0,0));
|
||||||
return wxApp::OnInit();
|
return wxApp::OnInit();
|
||||||
@ -78,18 +79,20 @@ bool DownloadApp::OnInit()
|
|||||||
m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(450, 340));
|
m_frame = new DownloadFrame("Downloader", wxPoint(50, 50), wxSize(450, 340));
|
||||||
m_frame->Show(true);
|
m_frame->Show(true);
|
||||||
|
|
||||||
#ifdef _WIN32
|
//#ifdef _WIN32
|
||||||
wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
// wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||||
auto frame = dynamic_cast<DownloadFrame*>(win);
|
// auto frame = dynamic_cast<DownloadFrame*>(win);
|
||||||
COPYDATASTRUCT* copy_data_structure = { 0 };
|
// COPYDATASTRUCT* copy_data_structure = { 0 };
|
||||||
copy_data_structure = (COPYDATASTRUCT*)lParam;
|
// copy_data_structure = (COPYDATASTRUCT*)lParam;
|
||||||
if (copy_data_structure->dwData == 1) {
|
// if (copy_data_structure->dwData == 1) {
|
||||||
LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData;
|
// LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData;
|
||||||
frame->handle_message(arguments);
|
// frame->handle_message(arguments);
|
||||||
}
|
// }
|
||||||
return true;
|
// return true;
|
||||||
});
|
// });
|
||||||
#endif
|
//#endif
|
||||||
|
m_message_handler = std::make_unique<OtherDownloaderMessageHandler>();
|
||||||
|
m_message_handler->init(m_frame);
|
||||||
|
|
||||||
return wxApp::OnInit();
|
return wxApp::OnInit();
|
||||||
}
|
}
|
||||||
@ -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_PROGRESS, &DownloadFrame::on_progress, this);
|
||||||
Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this);
|
Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this);
|
||||||
Bind(EVT_FILE_NAME_CHANGE, &DownloadFrame::on_name_change, 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)
|
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)
|
void DownloadFrame::handle_message(const wxString& msg)
|
||||||
{
|
{
|
||||||
//log("recieved: " + msg);
|
|
||||||
start_download(msg);
|
start_download(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DownloadFrame::on_url_msg(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
handle_message(event.GetString());
|
||||||
|
}
|
||||||
|
|
||||||
//wxIMPLEMENT_APP_NO_MAIN(MyApp);
|
//wxIMPLEMENT_APP_NO_MAIN(MyApp);
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ private:
|
|||||||
void on_cancel_button(wxCommandEvent& event);
|
void on_cancel_button(wxCommandEvent& event);
|
||||||
void on_pause_button(wxCommandEvent& event);
|
void on_pause_button(wxCommandEvent& event);
|
||||||
void on_resume_button(wxCommandEvent& event);
|
void on_resume_button(wxCommandEvent& event);
|
||||||
|
void on_url_msg(wxCommandEvent& event);
|
||||||
|
|
||||||
void update_state_labels();
|
void update_state_labels();
|
||||||
void start_next();
|
void start_next();
|
||||||
@ -82,6 +83,7 @@ class DownloadApp : public wxApp
|
|||||||
protected:
|
protected:
|
||||||
DownloadFrame* m_frame{ nullptr };
|
DownloadFrame* m_frame{ nullptr };
|
||||||
std::unique_ptr<DownloaderSend> m_dwnldr_send;
|
std::unique_ptr<DownloaderSend> m_dwnldr_send;
|
||||||
|
std::unique_ptr<OtherDownloaderMessageHandler> m_message_handler;
|
||||||
|
|
||||||
bool m_other_exists { false };
|
bool m_other_exists { false };
|
||||||
|
|
||||||
@ -90,7 +92,7 @@ public:
|
|||||||
void OnInitCmdLine(wxCmdLineParser& parser) override;
|
void OnInitCmdLine(wxCmdLineParser& parser) override;
|
||||||
bool OnCmdLineParsed(wxCmdLineParser& parser) override;
|
bool OnCmdLineParsed(wxCmdLineParser& parser) override;
|
||||||
|
|
||||||
|
DownloadFrame* get_frame() { return m_frame; }
|
||||||
};
|
};
|
||||||
|
|
||||||
wxDECLARE_APP(DownloadApp);
|
wxDECLARE_APP(DownloadApp);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "InstanceSend.hpp"
|
#include "InstanceSend.hpp"
|
||||||
|
|
||||||
|
#include "DownloaderApp.hpp"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
@ -7,7 +9,12 @@
|
|||||||
|
|
||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
#include <boost/dll/runtime_symbol_info.hpp>
|
#include <boost/dll/runtime_symbol_info.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
#include <wx/utils.h>
|
#include <wx/utils.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "libslic3r/Config.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Downloader {
|
namespace Downloader {
|
||||||
|
|
||||||
@ -208,7 +215,247 @@ bool execute_command(const wxString& command)
|
|||||||
{
|
{
|
||||||
return wxExecute(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
|
#ifdef _WIN32
|
||||||
std::string escaped = escape_strings_cstyle({ "prusa-downloader", boost::nowide::narrow(path) });
|
std::string escaped = escape_strings_cstyle({ "prusa-downloader", boost::nowide::narrow(path) });
|
||||||
return send_message_slicer(boost::nowide::widen(escaped));
|
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<std::string>{}(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
|
#endif
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SlicerSend::start_with_path(const wxString& path) const
|
bool SlicerSend::start_with_path(const wxString& path) const
|
||||||
@ -259,6 +513,11 @@ bool DownloaderSend::get_instance_exists() const
|
|||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return !EnumWindows(EnumWindowsProcDownloader, 0);
|
return !EnumWindows(EnumWindowsProcDownloader, 0);
|
||||||
|
#else
|
||||||
|
std::string slicer_path = (boost::dll::program_location()).string();
|
||||||
|
size_t hashed_path = std::hash<std::string>{}(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
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -267,9 +526,343 @@ bool DownloaderSend::send_url(const wxString& url) const
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
//std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(url) });
|
//std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(url) });
|
||||||
return send_message_downloader(url);
|
return send_message_downloader(url);
|
||||||
|
#else
|
||||||
|
std::string slicer_path = boost::dll::program_location().string();
|
||||||
|
size_t hashed_path = std::hash<std::string>{}(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
|
#endif
|
||||||
return false;
|
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"<<std::endl;
|
||||||
|
m_thread = boost::thread((boost::bind(&OtherDownloaderMessageHandler::listen, this)));
|
||||||
|
#endif //BACKGROUND_DOWNLOADER_MESSAGE_LISTENER
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtherDownloaderMessageHandler::shutdown()
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "message handler shutdown().";
|
||||||
|
#ifndef _WIN32
|
||||||
|
//instance_check_internal::delete_lockfile();
|
||||||
|
#endif //!_WIN32
|
||||||
|
assert(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__
|
||||||
|
//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<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_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<std::string> 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<boost::filesystem::path> 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<boost::filesystem::path>(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<std::string>{}(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 =
|
||||||
|
" <!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.Downloader\">"
|
||||||
|
" <method name=\"AnotherInstance\">"
|
||||||
|
" <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
|
||||||
|
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<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_DOWNLOADER_MESSAGE_LISTENER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -3,6 +3,16 @@
|
|||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
|
||||||
|
#if __linux__
|
||||||
|
#include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
|
||||||
|
#endif //__linux__
|
||||||
|
|
||||||
namespace Downloader {
|
namespace Downloader {
|
||||||
class SlicerSend
|
class SlicerSend
|
||||||
@ -20,5 +30,68 @@ public:
|
|||||||
bool get_instance_exists() const;
|
bool get_instance_exists() const;
|
||||||
bool send_url(const wxString& url) 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
|
#endif
|
Loading…
x
Reference in New Issue
Block a user