1 #include "GUI_App.hpp"
2 #include "InstanceCheck.hpp"
3 #include "Plater.hpp"
4
5 #ifdef _WIN32
6 #include "MainFrame.hpp"
7 #endif
8
9 #include "libslic3r/Utils.hpp"
10 #include "libslic3r/Config.hpp"
11
12 #include "boost/nowide/convert.hpp"
13 #include <boost/log/trivial.hpp>
14 #include <boost/filesystem/operations.hpp>
15 #include <iostream>
16 #include <unordered_map>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <optional>
20 #include <cstdint>
21
22 #ifdef _WIN32
23 #include <strsafe.h>
24 #endif //WIN32
25
26 #if defined(__linux__) || defined (__FreeBSD__)
27 #include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
28 #endif //__linux__
29
30 namespace Slic3r {
31 namespace instance_check_internal
32 {
33 struct CommandLineAnalysis
34 {
35 std::optional<bool> should_send;
36 std::string cl_string;
37 };
process_command_line(int argc,char ** argv)38 static CommandLineAnalysis process_command_line(int argc, char** argv)
39 {
40 CommandLineAnalysis ret;
41 //if (argc < 2)
42 // return ret;
43 std::vector<std::string> arguments { argv[0] };
44 for (int i = 1; i < argc; ++i) {
45 const std::string token = argv[i];
46 // Processing of boolean command line arguments shall match DynamicConfig::read_cli().
47 if (token == "--single-instance")
48 ret.should_send = true;
49 else if (token == "--no-single-instance")
50 ret.should_send = false;
51 else
52 arguments.emplace_back(token);
53 }
54 ret.cl_string = escape_strings_cstyle(arguments);
55 BOOST_LOG_TRIVIAL(debug) << "single instance: " <<
56 (ret.should_send.has_value() ? (*ret.should_send ? "true" : "false") : "undefined") <<
57 ". other params: " << ret.cl_string;
58 return ret;
59 }
60
61
62
63 #ifdef _WIN32
64
65 static HWND l_prusa_slicer_hwnd;
EnumWindowsProc(_In_ HWND hwnd,_In_ LPARAM lParam)66 static BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam)
67 {
68 //checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance
69 //search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel
70 //other option would be do a mutex and check for its existence
71 //BOOST_LOG_TRIVIAL(error) << "ewp: version: " << l_version_wstring;
72 TCHAR wndText[1000];
73 TCHAR className[1000];
74 int err;
75 err = GetClassName(hwnd, className, 1000);
76 if (err == 0)
77 return true;
78 err = GetWindowText(hwnd, wndText, 1000);
79 if (err == 0)
80 return true;
81 std::wstring classNameString(className);
82 std::wstring wndTextString(wndText);
83 if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
84 //check if other instances has same instance hash
85 //if not it is not same version(binary) as this version
86 HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
87 uint64_t other_instance_hash = PtrToUint(handle);
88 uint64_t other_instance_hash_major;
89 uint64_t my_instance_hash = GUI::wxGetApp().get_instance_hash_int();
90 handle = GetProp(hwnd, L"Instance_Hash_Major");
91 other_instance_hash_major = PtrToUint(handle);
92 other_instance_hash_major = other_instance_hash_major << 32;
93 other_instance_hash += other_instance_hash_major;
94 if(my_instance_hash == other_instance_hash)
95 {
96 BOOST_LOG_TRIVIAL(debug) << "win enum - found correct instance";
97 l_prusa_slicer_hwnd = hwnd;
98 ShowWindow(hwnd, SW_SHOWMAXIMIZED);
99 SetForegroundWindow(hwnd);
100 return false;
101 }
102 BOOST_LOG_TRIVIAL(debug) << "win enum - found wrong instance";
103 }
104 return true;
105 }
send_message(const std::string & message,const std::string & version)106 static bool send_message(const std::string& message, const std::string &version)
107 {
108 if (!EnumWindows(EnumWindowsProc, 0)) {
109 std::wstring wstr = boost::nowide::widen(message);
110 std::unique_ptr<LPWSTR> command_line_args = std::make_unique<LPWSTR>(const_cast<LPWSTR>(wstr.c_str()));
111 /*LPWSTR command_line_args = new wchar_t[wstr.size() + 1];
112 copy(wstr.begin(), wstr.end(), command_line_args);
113 command_line_args[wstr.size()] = 0;*/
114
115 //Create a COPYDATASTRUCT to send the information
116 //cbData represents the size of the information we want to send.
117 //lpData represents the information we want to send.
118 //dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA).
119 COPYDATASTRUCT data_to_send = { 0 };
120 data_to_send.dwData = 1;
121 data_to_send.cbData = sizeof(TCHAR) * (wcslen(*command_line_args.get()) + 1);
122 data_to_send.lpData = *command_line_args.get();
123 SendMessage(l_prusa_slicer_hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
124 return true;
125 }
126 return false;
127 }
128
129 #else
130
get_lock(const std::string & name,const std::string & path)131 static bool get_lock(const std::string& name, const std::string& path)
132 {
133 std::string dest_dir = path + name;
134 BOOST_LOG_TRIVIAL(debug) <<"full lock path: "<< dest_dir;
135 struct flock fl;
136 int fdlock;
137 fl.l_type = F_WRLCK;
138 fl.l_whence = SEEK_SET;
139 fl.l_start = 0;
140 fl.l_len = 1;
141
142 if (! boost::filesystem::is_directory(path)) {
143 BOOST_LOG_TRIVIAL(debug) << "get_lock(): datadir does not exist yet, creating...";
144 if (! boost::filesystem::create_directories(path))
145 BOOST_LOG_TRIVIAL(debug) << "get_lock(): unable to create datadir !!!";
146 }
147
148 if ((fdlock = open(dest_dir.c_str(), O_WRONLY | O_CREAT, 0666)) == -1)
149 return true;
150
151 if (fcntl(fdlock, F_SETLK, &fl) == -1)
152 return true;
153
154 return false;
155 }
156
157 #endif //WIN32
158 #if defined(__APPLE__)
159
send_message(const std::string & message_text,const std::string & version)160 static bool send_message(const std::string &message_text, const std::string &version)
161 {
162 //std::string v(version);
163 //std::replace(v.begin(), v.end(), '.', '-');
164 //if (!instance_check_internal::get_lock(v))
165 {
166 send_message_mac(message_text, version);
167 return true;
168 }
169 return false;
170 }
171
172 #elif defined(__linux__) || defined (__FreeBSD__)
173
send_message(const std::string & message_text,const std::string & version)174 static bool send_message(const std::string &message_text, const std::string &version)
175 {
176 /*std::string v(version);
177 std::replace(v.begin(), v.end(), '.', '-');
178 if (!instance_check_internal::get_lock(v))*/
179 /*auto checker = new wxSingleInstanceChecker;
180 if ( !checker->IsAnotherRunning() ) */
181 {
182 DBusMessage* msg;
183 // DBusMessageIter args;
184 DBusConnection* conn;
185 DBusError err;
186 dbus_uint32_t serial = 0;
187 const char* sigval = message_text.c_str();
188 //std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
189 std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + version;
190 std::string method_name = "AnotherInstance";
191 //std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck";
192 std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + version;
193
194
195 // initialise the error value
196 dbus_error_init(&err);
197
198 // connect to bus, and check for errors (use SESSION bus everywhere!)
199 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
200 if (dbus_error_is_set(&err)) {
201 BOOST_LOG_TRIVIAL(error) << "DBus Connection Error. Message to another instance wont be send.";
202 BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: " << err.message;
203 dbus_error_free(&err);
204 return true;
205 }
206 if (NULL == conn) {
207 BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send.";
208 return true;
209 }
210
211 //some sources do request interface ownership before constructing msg but i think its wrong.
212
213 //create new method call message
214 msg = dbus_message_new_method_call(interface_name.c_str(), object_name.c_str(), interface_name.c_str(), method_name.c_str());
215 if (NULL == msg) {
216 BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send.";
217 dbus_connection_unref(conn);
218 return true;
219 }
220 //the AnotherInstance method is not sending reply.
221 dbus_message_set_no_reply(msg, TRUE);
222
223 //append arguments to message
224 if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &sigval, DBUS_TYPE_INVALID)) {
225 BOOST_LOG_TRIVIAL(error) << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send.";
226 dbus_message_unref(msg);
227 dbus_connection_unref(conn);
228 return true;
229 }
230
231 // send the message and flush the connection
232 if (!dbus_connection_send(conn, msg, &serial)) {
233 BOOST_LOG_TRIVIAL(error) << "Ran out of memory while sending DBus message.";
234 dbus_message_unref(msg);
235 dbus_connection_unref(conn);
236 return true;
237 }
238 dbus_connection_flush(conn);
239
240 BOOST_LOG_TRIVIAL(trace) << "DBus message sent.";
241
242 // free the message and close the connection
243 dbus_message_unref(msg);
244 dbus_connection_unref(conn);
245 return true;
246 }
247 return false;
248 }
249
250 #endif //__APPLE__/__linux__
251 } //namespace instance_check_internal
252
instance_check(int argc,char ** argv,bool app_config_single_instance)253 bool instance_check(int argc, char** argv, bool app_config_single_instance)
254 {
255 std::size_t hashed_path;
256 #ifdef _WIN32
257 hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
258 #else
259 boost::system::error_code ec;
260 #ifdef __linux__
261 // If executed by an AppImage, start the AppImage, not the main process.
262 // see https://docs.appimage.org/packaging-guide/environment-variables.html#id2
263 const char *appimage_env = std::getenv("APPIMAGE");
264 bool appimage_env_valid = false;
265 if (appimage_env) {
266 try {
267 auto appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env));
268 if (boost::filesystem::exists(appimage_path)) {
269 hashed_path = std::hash<std::string>{}(appimage_path.string());
270 appimage_env_valid = true;
271 }
272 } catch (std::exception &) {
273 }
274 if (! appimage_env_valid)
275 BOOST_LOG_TRIVIAL(error) << "APPIMAGE environment variable was set, but it does not point to a valid file: " << appimage_env;
276 }
277 if (! appimage_env_valid)
278 #endif // __linux__
279 hashed_path = std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]), ec).string());
280 if (ec.value() > 0) { // canonical was not able to find the executable (can happen with appimage on some systems. Does it fail on Fuse file systems?)
281 ec.clear();
282 // Compose path with boost canonical of folder and filename
283 hashed_path = std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]).parent_path(), ec).string() + "/" + boost::filesystem::system_complete(argv[0]).filename().string());
284 if (ec.value() > 0) {
285 // Still not valid, process without canonical
286 hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
287 }
288 }
289 #endif // _WIN32
290
291 std::string lock_name = std::to_string(hashed_path);
292 GUI::wxGetApp().set_instance_hash(hashed_path);
293 BOOST_LOG_TRIVIAL(debug) <<"full path: "<< lock_name;
294 instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
295 if (! cla.should_send.has_value())
296 cla.should_send = app_config_single_instance;
297 #ifdef _WIN32
298 GUI::wxGetApp().init_single_instance_checker(lock_name + ".lock", data_dir() + "/cache/");
299 if (cla.should_send.value() && GUI::wxGetApp().single_instance_checker()->IsAnotherRunning()) {
300 #else // mac & linx
301 // get_lock() creates the lockfile therefore *cla.should_send is checked after
302 if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) {
303 #endif
304 instance_check_internal::send_message(cla.cl_string, lock_name);
305 BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
306 return true;
307 }
308 BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
309 return false;
310 }
311
312 #ifdef __APPLE__
313 bool unlock_lockfile(const std::string& name, const std::string& path)
314 {
315 std::string dest_dir = path + name;
316 //BOOST_LOG_TRIVIAL(debug) << "full lock path: " << dest_dir;
317 struct flock fl;
318 int fdlock;
319 fl.l_type = F_UNLCK;
320 fl.l_whence = SEEK_SET;
321 fl.l_start = 0;
322 fl.l_len = 1;
323 if ((fdlock = open(dest_dir.c_str(), O_WRONLY | O_CREAT, 0666)) == -1)
324 return false;
325
326 if (fcntl(fdlock, F_SETLK, &fl) == -1)
327 return false;
328
329 return true;
330 }
331 #endif //__APPLE__
332 namespace GUI {
333
334 wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
335 wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
336
337 void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
338 {
339 assert(!m_initialized);
340 assert(m_callback_evt_handler == nullptr);
341 if (m_initialized)
342 return;
343
344 m_initialized = true;
345 m_callback_evt_handler = callback_evt_handler;
346
347 #if defined(__APPLE__)
348 this->register_for_messages(wxGetApp().get_instance_hash_string());
349 #endif //__APPLE__
350
351 #ifdef BACKGROUND_MESSAGE_LISTENER
352 m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this)));
353 #endif //BACKGROUND_MESSAGE_LISTENER
354 }
355 void OtherInstanceMessageHandler::shutdown(MainFrame* main_frame)
356 {
357 BOOST_LOG_TRIVIAL(debug) << "message handler shutdown().";
358 assert(m_initialized);
359 if (m_initialized) {
360 #ifdef _WIN32
361 HWND hwnd = main_frame->GetHandle();
362 RemoveProp(hwnd, L"Instance_Hash_Minor");
363 RemoveProp(hwnd, L"Instance_Hash_Major");
364 #endif //_WIN32
365 #if __APPLE__
366 //delete macos implementation
367 this->unregister_for_messages();
368 #endif //__APPLE__
369 #ifdef BACKGROUND_MESSAGE_LISTENER
370 if (m_thread.joinable()) {
371 // Stop the worker thread, if running.
372 {
373 // Notify the worker thread to cancel wait on detection polling.
374 std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
375 m_stop = true;
376 }
377 m_thread_stop_condition.notify_all();
378 // Wait for the worker thread to stop.
379 m_thread.join();
380 m_stop = false;
381 }
382 #endif //BACKGROUND_MESSAGE_LISTENER
383 m_callback_evt_handler = nullptr;
384 m_initialized = false;
385 }
386 }
387
388 #ifdef _WIN32
389 void OtherInstanceMessageHandler::init_windows_properties(MainFrame* main_frame, size_t instance_hash)
390 {
391 size_t minor_hash = instance_hash & 0xFFFFFFFF;
392 size_t major_hash = (instance_hash & 0xFFFFFFFF00000000) >> 32;
393 HWND hwnd = main_frame->GetHandle();
394 HANDLE handle_minor = UIntToPtr(minor_hash);
395 HANDLE handle_major = UIntToPtr(major_hash);
396 SetProp(hwnd, L"Instance_Hash_Minor", handle_minor);
397 SetProp(hwnd, L"Instance_Hash_Major", handle_major);
398 //BOOST_LOG_TRIVIAL(debug) << "window properties initialized " << instance_hash << " (" << minor_hash << " & "<< major_hash;
399 }
400
401 #if 0
402
403 void OtherInstanceMessageHandler::print_window_info(HWND hwnd)
404 {
405 std::wstring instance_hash = boost::nowide::widen(wxGetApp().get_instance_hash_string());
406 TCHAR wndText[1000];
407 TCHAR className[1000];
408 GetClassName(hwnd, className, 1000);
409 GetWindowText(hwnd, wndText, 1000);
410 std::wstring classNameString(className);
411 std::wstring wndTextString(wndText);
412 HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
413 size_t result = PtrToUint(handle);
414 handle = GetProp(hwnd, L"Instance_Hash_Major");
415 size_t r2 = PtrToUint(handle);
416 r2 = (r2 << 32);
417 result += r2;
418 BOOST_LOG_TRIVIAL(info) << "window info: " << result;
419 }
420 #endif //0
421 #endif //WIN32
422 namespace MessageHandlerInternal
423 {
424 // returns ::path to possible model or empty ::path if input string is not existing path
425 static boost::filesystem::path get_path(const std::string& possible_path)
426 {
427 BOOST_LOG_TRIVIAL(debug) << "message part:" << possible_path;
428
429 if (possible_path.empty() || possible_path.size() < 3) {
430 BOOST_LOG_TRIVIAL(debug) << "empty";
431 return boost::filesystem::path();
432 }
433 if (boost::filesystem::exists(possible_path)) {
434 BOOST_LOG_TRIVIAL(debug) << "is path";
435 return boost::filesystem::path(possible_path);
436 } else if (possible_path[0] == '\"') {
437 if(boost::filesystem::exists(possible_path.substr(1, possible_path.size() - 2))) {
438 BOOST_LOG_TRIVIAL(debug) << "is path in quotes";
439 return boost::filesystem::path(possible_path.substr(1, possible_path.size() - 2));
440 }
441 }
442 BOOST_LOG_TRIVIAL(debug) << "is NOT path";
443 return boost::filesystem::path();
444 }
445 } //namespace MessageHandlerInternal
446
447 void OtherInstanceMessageHandler::handle_message(const std::string& message)
448 {
449 BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message;
450
451 std::vector<std::string> args;
452 bool parsed = unescape_strings_cstyle(message, args);
453 assert(parsed);
454 if (! parsed) {
455 BOOST_LOG_TRIVIAL(error) << "message from other instance is incorrectly formatted: " << message;
456 return;
457 }
458
459 std::vector<boost::filesystem::path> paths;
460 // Skip the first argument, it is the path to the slicer executable.
461 auto it = args.begin();
462 for (++ it; it != args.end(); ++ it) {
463 boost::filesystem::path p = MessageHandlerInternal::get_path(*it);
464 if (! p.string().empty())
465 paths.emplace_back(p);
466 }
467 if (! paths.empty()) {
468 //wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
469 //if (evt_handler) {
470 wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
471 //}
472 }
473 }
474
475 #ifdef BACKGROUND_MESSAGE_LISTENER
476
477 namespace MessageHandlerDBusInternal
478 {
479 //reply to introspect makes our DBus object visible for other programs like D-Feet
480 static void respond_to_introspect(DBusConnection *connection, DBusMessage *request)
481 {
482 DBusMessage *reply;
483 const char *introspection_data =
484 " <!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
485 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
486 " <!-- dbus-sharp 0.8.1 -->"
487 " <node>"
488 " <interface name=\"org.freedesktop.DBus.Introspectable\">"
489 " <method name=\"Introspect\">"
490 " <arg name=\"data\" direction=\"out\" type=\"s\" />"
491 " </method>"
492 " </interface>"
493 " <interface name=\"com.prusa3d.prusaslicer.InstanceCheck\">"
494 " <method name=\"AnotherInstance\">"
495 " <arg name=\"data\" direction=\"in\" type=\"s\" />"
496 " </method>"
497 " </interface>"
498 " </node>";
499
500 reply = dbus_message_new_method_return(request);
501 dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, DBUS_TYPE_INVALID);
502 dbus_connection_send(connection, reply, NULL);
503 dbus_message_unref(reply);
504 }
505 //method AnotherInstance receives message from another PrusaSlicer instance
506 static void handle_method_another_instance(DBusConnection *connection, DBusMessage *request)
507 {
508 DBusError err;
509 char* text = nullptr;
510 wxEvtHandler* evt_handler;
511
512 dbus_error_init(&err);
513 dbus_message_get_args(request, &err, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
514 if (dbus_error_is_set(&err)) {
515 BOOST_LOG_TRIVIAL(trace) << "Dbus method AnotherInstance received with wrong arguments.";
516 dbus_error_free(&err);
517 return;
518 }
519 wxGetApp().other_instance_message_handler()->handle_message(text);
520
521 evt_handler = wxGetApp().plater();
522 if (evt_handler) {
523 wxPostEvent(evt_handler, InstanceGoToFrontEvent(EVT_INSTANCE_GO_TO_FRONT));
524 }
525 }
526 //every dbus message received comes here
527 static DBusHandlerResult handle_dbus_object_message(DBusConnection *connection, DBusMessage *message, void *user_data)
528 {
529 const char* interface_name = dbus_message_get_interface(message);
530 const char* member_name = dbus_message_get_member(message);
531 std::string our_interface = "com.prusa3d.prusaslicer.InstanceCheck.Object" + wxGetApp().get_instance_hash_string();
532 BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
533 if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {
534 respond_to_introspect(connection, message);
535 return DBUS_HANDLER_RESULT_HANDLED;
536 } else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstance", member_name)) {
537 handle_method_another_instance(connection, message);
538 return DBUS_HANDLER_RESULT_HANDLED;
539 }
540 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
541 }
542 } //namespace MessageHandlerDBusInternal
543
544 void OtherInstanceMessageHandler::listen()
545 {
546 DBusConnection* conn;
547 DBusError err;
548 int name_req_val;
549 DBusObjectPathVTable vtable;
550 std::string instance_hash = wxGetApp().get_instance_hash_string();
551 std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck.Object" + instance_hash;
552 std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck/Object" + instance_hash;
553
554 //BOOST_LOG_TRIVIAL(debug) << "init dbus listen " << interface_name << " " << object_name;
555 dbus_error_init(&err);
556
557 // connect to the bus and check for errors (use SESSION bus everywhere!)
558 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
559 if (dbus_error_is_set(&err)) {
560 BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message;
561 BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
562 dbus_error_free(&err);
563 return;
564 }
565 if (NULL == conn) {
566 BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Dbus Messages listening terminating.";
567 return;
568 }
569
570 // request our name on the bus and check for errors
571 name_req_val = dbus_bus_request_name(conn, interface_name.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
572 if (dbus_error_is_set(&err)) {
573 BOOST_LOG_TRIVIAL(error) << "DBus Request name Error: "<< err.message;
574 BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
575 dbus_error_free(&err);
576 dbus_connection_unref(conn);
577 return;
578 }
579 if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != name_req_val) {
580 BOOST_LOG_TRIVIAL(error) << "Not primary owner of DBus name - probably another PrusaSlicer instance is running.";
581 BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
582 dbus_connection_unref(conn);
583 return;
584 }
585
586 // Set callbacks. Unregister function should not be nessary.
587 vtable.message_function = MessageHandlerDBusInternal::handle_dbus_object_message;
588 vtable.unregister_function = NULL;
589
590 // register new object - this is our access to DBus
591 dbus_connection_try_register_object_path(conn, object_name.c_str(), &vtable, NULL, &err);
592 if ( dbus_error_is_set(&err) ) {
593 BOOST_LOG_TRIVIAL(error) << "DBus Register object Error: "<< err.message;
594 BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
595 dbus_connection_unref(conn);
596 dbus_error_free(&err);
597 return;
598 }
599
600 BOOST_LOG_TRIVIAL(trace) << "Dbus object "<< object_name <<" registered. Starting listening for messages.";
601
602 for (;;) {
603 // Wait for 1 second
604 // Cancellable.
605 {
606 std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
607 m_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_stop; });
608 }
609 if (m_stop)
610 // Stop the worker thread.
611
612 break;
613 //dispatch should do all the work with incoming messages
614 //second parameter is blocking time that funciton waits for new messages
615 //that is handled here with our own event loop above
616 dbus_connection_read_write_dispatch(conn, 0);
617 }
618
619 dbus_connection_unref(conn);
620 }
621 #endif //BACKGROUND_MESSAGE_LISTENER
622 } // namespace GUI
623 } // namespace Slic3r
624