freedesktop_portal_desktop.cpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. /**************************************************************************/
  2. /* freedesktop_portal_desktop.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "freedesktop_portal_desktop.h"
  31. #ifdef DBUS_ENABLED
  32. #include "core/crypto/crypto_core.h"
  33. #include "core/error/error_macros.h"
  34. #include "core/os/os.h"
  35. #include "core/string/ustring.h"
  36. #include "core/variant/variant.h"
  37. #ifdef SOWRAP_ENABLED
  38. #include "dbus-so_wrap.h"
  39. #else
  40. #include <dbus/dbus.h>
  41. #endif
  42. #include <unistd.h>
  43. #define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
  44. #define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
  45. #define BUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
  46. #define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
  47. #define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
  48. #define BUS_INTERFACE_SCREENSHOT "org.freedesktop.portal.Screenshot"
  49. #define BUS_INTERFACE_INHIBIT "org.freedesktop.portal.Inhibit"
  50. #define BUS_INTERFACE_REQUEST "org.freedesktop.portal.Request"
  51. #define INHIBIT_FLAG_IDLE 8
  52. bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, ReadVariantType p_type, void *r_value) {
  53. DBusMessageIter iter[3];
  54. dbus_message_iter_init(p_reply_message, &iter[0]);
  55. if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) {
  56. return false;
  57. }
  58. dbus_message_iter_recurse(&iter[0], &iter[1]);
  59. if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) {
  60. return false;
  61. }
  62. dbus_message_iter_recurse(&iter[1], &iter[2]);
  63. if (p_type == VAR_TYPE_COLOR) {
  64. if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_STRUCT) {
  65. return false;
  66. }
  67. DBusMessageIter struct_iter;
  68. dbus_message_iter_recurse(&iter[2], &struct_iter);
  69. int idx = 0;
  70. while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_DOUBLE) {
  71. double value = 0.0;
  72. dbus_message_iter_get_basic(&struct_iter, &value);
  73. if (value < 0.0 || value > 1.0) {
  74. return false;
  75. }
  76. if (idx == 0) {
  77. static_cast<Color *>(r_value)->r = value;
  78. } else if (idx == 1) {
  79. static_cast<Color *>(r_value)->g = value;
  80. } else if (idx == 2) {
  81. static_cast<Color *>(r_value)->b = value;
  82. }
  83. idx++;
  84. if (!dbus_message_iter_next(&struct_iter)) {
  85. break;
  86. }
  87. }
  88. if (idx != 3) {
  89. return false;
  90. }
  91. } else if (p_type == VAR_TYPE_UINT32) {
  92. if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_UINT32) {
  93. return false;
  94. }
  95. dbus_message_iter_get_basic(&iter[2], r_value);
  96. } else if (p_type == VAR_TYPE_BOOL) {
  97. if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_BOOLEAN) {
  98. return false;
  99. }
  100. dbus_message_iter_get_basic(&iter[2], r_value);
  101. }
  102. return true;
  103. }
  104. bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char *p_key, ReadVariantType p_type, void *r_value) {
  105. if (unsupported) {
  106. return false;
  107. }
  108. DBusError error;
  109. dbus_error_init(&error);
  110. DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
  111. if (dbus_error_is_set(&error)) {
  112. if (OS::get_singleton()->is_stdout_verbose()) {
  113. ERR_PRINT(vformat("Error opening D-Bus connection: %s", String::utf8(error.message)));
  114. }
  115. dbus_error_free(&error);
  116. unsupported = true;
  117. return false;
  118. }
  119. DBusMessage *message = dbus_message_new_method_call(
  120. BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_SETTINGS,
  121. "Read");
  122. dbus_message_append_args(
  123. message,
  124. DBUS_TYPE_STRING, &p_namespace,
  125. DBUS_TYPE_STRING, &p_key,
  126. DBUS_TYPE_INVALID);
  127. DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
  128. dbus_message_unref(message);
  129. if (dbus_error_is_set(&error)) {
  130. if (OS::get_singleton()->is_stdout_verbose()) {
  131. ERR_PRINT(vformat("Failed to read setting %s %s: %s", p_namespace, p_key, String::utf8(error.message)));
  132. }
  133. dbus_error_free(&error);
  134. dbus_connection_unref(bus);
  135. return false;
  136. }
  137. bool success = try_parse_variant(reply, p_type, r_value);
  138. dbus_message_unref(reply);
  139. dbus_connection_unref(bus);
  140. return success;
  141. }
  142. uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
  143. if (unsupported) {
  144. return 0;
  145. }
  146. uint32_t value = 0;
  147. if (read_setting("org.freedesktop.appearance", "color-scheme", VAR_TYPE_UINT32, &value)) {
  148. return value;
  149. } else {
  150. return 0;
  151. }
  152. }
  153. Color FreeDesktopPortalDesktop::get_appearance_accent_color() {
  154. if (unsupported) {
  155. return Color(0, 0, 0, 0);
  156. }
  157. Color value;
  158. if (read_setting("org.freedesktop.appearance", "accent-color", VAR_TYPE_COLOR, &value)) {
  159. return value;
  160. } else {
  161. return Color(0, 0, 0, 0);
  162. }
  163. }
  164. uint32_t FreeDesktopPortalDesktop::get_high_contrast() {
  165. if (unsupported) {
  166. return -1;
  167. }
  168. dbus_bool_t value = false;
  169. if (read_setting("org.gnome.desktop.a11y.interface", "high-contrast", VAR_TYPE_BOOL, &value)) {
  170. return value;
  171. }
  172. return -1;
  173. }
  174. static const char *cs_empty = "";
  175. void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) {
  176. CharString cs = p_string.utf8();
  177. const char *cs_ptr = cs.ptr();
  178. if (cs_ptr) {
  179. dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr);
  180. } else {
  181. dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty);
  182. }
  183. }
  184. void FreeDesktopPortalDesktop::append_dbus_dict_options(DBusMessageIter *p_iter, const TypedArray<Dictionary> &p_options, HashMap<String, String> &r_ids) {
  185. DBusMessageIter dict_iter;
  186. DBusMessageIter var_iter;
  187. DBusMessageIter arr_iter;
  188. const char *choices_key = "choices";
  189. dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
  190. dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &choices_key);
  191. dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(ssa(ss)s)", &var_iter);
  192. dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(ss(ss)s)", &arr_iter);
  193. r_ids.clear();
  194. for (int i = 0; i < p_options.size(); i++) {
  195. const Dictionary &item = p_options[i];
  196. if (!item.has("name") || !item.has("values") || !item.has("default")) {
  197. continue;
  198. }
  199. const String &name = item["name"];
  200. const Vector<String> &options = item["values"];
  201. int default_idx = item["default"];
  202. DBusMessageIter struct_iter;
  203. DBusMessageIter array_iter;
  204. DBusMessageIter array_struct_iter;
  205. dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
  206. append_dbus_string(&struct_iter, "option_" + itos(i)); // ID.
  207. append_dbus_string(&struct_iter, name); // User visible name.
  208. r_ids["option_" + itos(i)] = name;
  209. dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(ss)", &array_iter);
  210. for (int j = 0; j < options.size(); j++) {
  211. dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
  212. append_dbus_string(&array_struct_iter, itos(j));
  213. append_dbus_string(&array_struct_iter, options[j]);
  214. dbus_message_iter_close_container(&array_iter, &array_struct_iter);
  215. }
  216. dbus_message_iter_close_container(&struct_iter, &array_iter);
  217. if (options.is_empty()) {
  218. append_dbus_string(&struct_iter, (default_idx) ? "true" : "false"); // Default selection.
  219. } else {
  220. append_dbus_string(&struct_iter, itos(default_idx)); // Default selection.
  221. }
  222. dbus_message_iter_close_container(&arr_iter, &struct_iter);
  223. }
  224. dbus_message_iter_close_container(&var_iter, &arr_iter);
  225. dbus_message_iter_close_container(&dict_iter, &var_iter);
  226. dbus_message_iter_close_container(p_iter, &dict_iter);
  227. }
  228. void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts, const Vector<String> &p_filter_mimes) {
  229. DBusMessageIter dict_iter;
  230. DBusMessageIter var_iter;
  231. DBusMessageIter arr_iter;
  232. const char *filters_key = "filters";
  233. ERR_FAIL_COND(p_filter_names.size() != p_filter_exts.size());
  234. ERR_FAIL_COND(p_filter_names.size() != p_filter_mimes.size());
  235. dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
  236. dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
  237. dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
  238. dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
  239. for (int i = 0; i < p_filter_names.size(); i++) {
  240. DBusMessageIter struct_iter;
  241. DBusMessageIter array_iter;
  242. DBusMessageIter array_struct_iter;
  243. dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
  244. append_dbus_string(&struct_iter, p_filter_names[i]);
  245. dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
  246. const String &flt_orig = p_filter_exts[i];
  247. String flt;
  248. for (int j = 0; j < flt_orig.length(); j++) {
  249. if (is_unicode_letter(flt_orig[j])) {
  250. flt += vformat("[%c%c]", String::char_lowercase(flt_orig[j]), String::char_uppercase(flt_orig[j]));
  251. } else {
  252. flt += flt_orig[j];
  253. }
  254. }
  255. int filter_slice_count = flt.get_slice_count(",");
  256. for (int j = 0; j < filter_slice_count; j++) {
  257. dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
  258. String str = (flt.get_slicec(',', j).strip_edges());
  259. {
  260. const unsigned flt_type = 0;
  261. dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &flt_type);
  262. }
  263. append_dbus_string(&array_struct_iter, str);
  264. dbus_message_iter_close_container(&array_iter, &array_struct_iter);
  265. }
  266. const String &mime = p_filter_mimes[i];
  267. filter_slice_count = mime.get_slice_count(",");
  268. for (int j = 0; j < filter_slice_count; j++) {
  269. dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
  270. String str = mime.get_slicec(',', j).strip_edges();
  271. {
  272. const unsigned flt_type = 1;
  273. dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &flt_type);
  274. }
  275. append_dbus_string(&array_struct_iter, str);
  276. dbus_message_iter_close_container(&array_iter, &array_struct_iter);
  277. }
  278. dbus_message_iter_close_container(&struct_iter, &array_iter);
  279. dbus_message_iter_close_container(&arr_iter, &struct_iter);
  280. }
  281. dbus_message_iter_close_container(&var_iter, &arr_iter);
  282. dbus_message_iter_close_container(&dict_iter, &var_iter);
  283. dbus_message_iter_close_container(p_iter, &dict_iter);
  284. }
  285. void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) {
  286. DBusMessageIter dict_iter;
  287. DBusMessageIter var_iter;
  288. dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
  289. append_dbus_string(&dict_iter, p_key);
  290. if (p_as_byte_array) {
  291. DBusMessageIter arr_iter;
  292. dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter);
  293. dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter);
  294. CharString cs = p_value.utf8();
  295. const char *cs_ptr = cs.get_data();
  296. do {
  297. dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr);
  298. } while (*cs_ptr++);
  299. dbus_message_iter_close_container(&var_iter, &arr_iter);
  300. } else {
  301. dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter);
  302. append_dbus_string(&var_iter, p_value);
  303. }
  304. dbus_message_iter_close_container(&dict_iter, &var_iter);
  305. dbus_message_iter_close_container(p_iter, &dict_iter);
  306. }
  307. void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) {
  308. DBusMessageIter dict_iter;
  309. DBusMessageIter var_iter;
  310. dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
  311. append_dbus_string(&dict_iter, p_key);
  312. dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter);
  313. {
  314. int val = p_value;
  315. dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val);
  316. }
  317. dbus_message_iter_close_container(&dict_iter, &var_iter);
  318. dbus_message_iter_close_container(p_iter, &dict_iter);
  319. }
  320. bool FreeDesktopPortalDesktop::color_picker_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Color &r_color) {
  321. ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
  322. dbus_uint32_t resp_code;
  323. dbus_message_iter_get_basic(p_iter, &resp_code);
  324. if (resp_code != 0) {
  325. r_cancel = true;
  326. } else {
  327. r_cancel = false;
  328. ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
  329. ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
  330. DBusMessageIter dict_iter;
  331. dbus_message_iter_recurse(p_iter, &dict_iter);
  332. while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
  333. DBusMessageIter iter;
  334. dbus_message_iter_recurse(&dict_iter, &iter);
  335. if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
  336. const char *key;
  337. dbus_message_iter_get_basic(&iter, &key);
  338. dbus_message_iter_next(&iter);
  339. DBusMessageIter var_iter;
  340. dbus_message_iter_recurse(&iter, &var_iter);
  341. if (strcmp(key, "color") == 0) { // (ddd)
  342. if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) {
  343. DBusMessageIter struct_iter;
  344. dbus_message_iter_recurse(&var_iter, &struct_iter);
  345. int idx = 0;
  346. while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_DOUBLE) {
  347. double value = 0.0;
  348. dbus_message_iter_get_basic(&struct_iter, &value);
  349. if (idx == 0) {
  350. r_color.r = value;
  351. } else if (idx == 1) {
  352. r_color.g = value;
  353. } else if (idx == 2) {
  354. r_color.b = value;
  355. }
  356. idx++;
  357. if (!dbus_message_iter_next(&struct_iter)) {
  358. break;
  359. }
  360. }
  361. }
  362. }
  363. }
  364. if (!dbus_message_iter_next(&dict_iter)) {
  365. break;
  366. }
  367. }
  368. }
  369. return true;
  370. }
  371. bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, const HashMap<String, String> &p_ids, bool &r_cancel, Vector<String> &r_urls, int &r_index, Dictionary &r_options) {
  372. ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
  373. dbus_uint32_t resp_code;
  374. dbus_message_iter_get_basic(p_iter, &resp_code);
  375. if (resp_code != 0) {
  376. r_cancel = true;
  377. } else {
  378. r_cancel = false;
  379. ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
  380. ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
  381. DBusMessageIter dict_iter;
  382. dbus_message_iter_recurse(p_iter, &dict_iter);
  383. while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
  384. DBusMessageIter iter;
  385. dbus_message_iter_recurse(&dict_iter, &iter);
  386. if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
  387. const char *key;
  388. dbus_message_iter_get_basic(&iter, &key);
  389. dbus_message_iter_next(&iter);
  390. DBusMessageIter var_iter;
  391. dbus_message_iter_recurse(&iter, &var_iter);
  392. if (strcmp(key, "current_filter") == 0) { // (sa(us))
  393. if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) {
  394. DBusMessageIter struct_iter;
  395. dbus_message_iter_recurse(&var_iter, &struct_iter);
  396. while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) {
  397. const char *value;
  398. dbus_message_iter_get_basic(&struct_iter, &value);
  399. String name = String::utf8(value);
  400. r_index = p_names.find(name);
  401. if (!dbus_message_iter_next(&struct_iter)) {
  402. break;
  403. }
  404. }
  405. }
  406. } else if (strcmp(key, "choices") == 0) { // a(ss) {
  407. if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
  408. DBusMessageIter struct_iter;
  409. dbus_message_iter_recurse(&var_iter, &struct_iter);
  410. while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRUCT) {
  411. DBusMessageIter opt_iter;
  412. dbus_message_iter_recurse(&struct_iter, &opt_iter);
  413. const char *opt_key = nullptr;
  414. dbus_message_iter_get_basic(&opt_iter, &opt_key);
  415. String opt_skey = String::utf8(opt_key);
  416. dbus_message_iter_next(&opt_iter);
  417. const char *opt_val = nullptr;
  418. dbus_message_iter_get_basic(&opt_iter, &opt_val);
  419. String opt_sval = String::utf8(opt_val);
  420. if (p_ids.has(opt_skey)) {
  421. opt_skey = p_ids[opt_skey];
  422. if (opt_sval == "true") {
  423. r_options[opt_skey] = true;
  424. } else if (opt_sval == "false") {
  425. r_options[opt_skey] = false;
  426. } else {
  427. r_options[opt_skey] = opt_sval.to_int();
  428. }
  429. }
  430. if (!dbus_message_iter_next(&struct_iter)) {
  431. break;
  432. }
  433. }
  434. }
  435. } else if (strcmp(key, "uris") == 0) { // as
  436. if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
  437. DBusMessageIter uri_iter;
  438. dbus_message_iter_recurse(&var_iter, &uri_iter);
  439. while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) {
  440. const char *value;
  441. dbus_message_iter_get_basic(&uri_iter, &value);
  442. r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_file_decode());
  443. if (!dbus_message_iter_next(&uri_iter)) {
  444. break;
  445. }
  446. }
  447. }
  448. }
  449. }
  450. if (!dbus_message_iter_next(&dict_iter)) {
  451. break;
  452. }
  453. }
  454. }
  455. return true;
  456. }
  457. bool FreeDesktopPortalDesktop::color_picker(const String &p_xid, const Callable &p_callback) {
  458. if (unsupported) {
  459. return false;
  460. }
  461. // Open connection and add signal handler.
  462. ColorPickerData cd;
  463. cd.callback = p_callback;
  464. String token;
  465. if (make_request_token(token) != OK) {
  466. return false;
  467. }
  468. DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_SCREENSHOT, "PickColor");
  469. {
  470. DBusMessageIter iter;
  471. dbus_message_iter_init_append(message, &iter);
  472. append_dbus_string(&iter, p_xid);
  473. DBusMessageIter arr_iter;
  474. dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
  475. append_dbus_dict_string(&arr_iter, "handle_token", token);
  476. dbus_message_iter_close_container(&iter, &arr_iter);
  477. }
  478. if (!send_request(message, token, cd.path, cd.filter)) {
  479. return false;
  480. }
  481. MutexLock lock(color_picker_mutex);
  482. color_pickers.push_back(cd);
  483. return true;
  484. }
  485. bool FreeDesktopPortalDesktop::_is_interface_supported(const char *p_iface, uint32_t p_minimum_version) {
  486. bool supported = false;
  487. DBusError err;
  488. dbus_error_init(&err);
  489. DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &err);
  490. if (dbus_error_is_set(&err)) {
  491. dbus_error_free(&err);
  492. } else {
  493. DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_PROPERTIES, "Get");
  494. if (message) {
  495. const char *name_space = p_iface;
  496. const char *key = "version";
  497. dbus_message_append_args(
  498. message,
  499. DBUS_TYPE_STRING, &name_space,
  500. DBUS_TYPE_STRING, &key,
  501. DBUS_TYPE_INVALID);
  502. DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 250, &err);
  503. if (dbus_error_is_set(&err)) {
  504. dbus_error_free(&err);
  505. } else if (reply) {
  506. DBusMessageIter iter;
  507. if (dbus_message_iter_init(reply, &iter)) {
  508. DBusMessageIter iter_ver;
  509. dbus_message_iter_recurse(&iter, &iter_ver);
  510. dbus_uint32_t ver_code;
  511. dbus_message_iter_get_basic(&iter_ver, &ver_code);
  512. print_verbose(vformat("PortalDesktop: %s version %d detected, version %d required.", p_iface, ver_code, p_minimum_version));
  513. supported = ver_code >= p_minimum_version;
  514. }
  515. dbus_message_unref(reply);
  516. }
  517. dbus_message_unref(message);
  518. }
  519. dbus_connection_unref(bus);
  520. }
  521. return supported;
  522. }
  523. bool FreeDesktopPortalDesktop::is_file_chooser_supported() {
  524. static int supported = -1;
  525. if (supported == -1) {
  526. supported = _is_interface_supported(BUS_INTERFACE_FILE_CHOOSER, 3);
  527. }
  528. return supported;
  529. }
  530. bool FreeDesktopPortalDesktop::is_settings_supported() {
  531. static int supported = -1;
  532. if (supported == -1) {
  533. supported = _is_interface_supported(BUS_INTERFACE_SETTINGS, 1);
  534. }
  535. return supported;
  536. }
  537. bool FreeDesktopPortalDesktop::is_screenshot_supported() {
  538. static int supported = -1;
  539. if (supported == -1) {
  540. supported = _is_interface_supported(BUS_INTERFACE_SCREENSHOT, 1);
  541. }
  542. return supported;
  543. }
  544. bool FreeDesktopPortalDesktop::is_inhibit_supported() {
  545. static int supported = -1;
  546. if (supported == -1) {
  547. // If not sandboxed, prefer to use org.freedesktop.ScreenSaver
  548. supported = OS::get_singleton()->is_sandboxed() && _is_interface_supported(BUS_INTERFACE_INHIBIT, 1);
  549. }
  550. return supported;
  551. }
  552. Error FreeDesktopPortalDesktop::make_request_token(String &r_token) {
  553. CryptoCore::RandomGenerator rng;
  554. ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
  555. uint8_t uuid[64];
  556. Error rng_err = rng.get_random_bytes(uuid, 64);
  557. ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token.");
  558. r_token = String::hex_encode_buffer(uuid, 64);
  559. return OK;
  560. }
  561. bool FreeDesktopPortalDesktop::send_request(DBusMessage *p_message, const String &r_token, String &r_response_path, String &r_response_filter) {
  562. String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(monitor_connection));
  563. r_response_path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace_char('.', '_').remove_char(':'), r_token);
  564. r_response_filter = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", r_response_path, dbus_unique_name);
  565. DBusError err;
  566. dbus_error_init(&err);
  567. dbus_bus_add_match(monitor_connection, r_response_filter.utf8().get_data(), &err);
  568. if (dbus_error_is_set(&err)) {
  569. ERR_PRINT(vformat("Failed to add DBus match: %s.", String::utf8(err.message)));
  570. dbus_error_free(&err);
  571. return false;
  572. }
  573. DBusMessage *reply = dbus_connection_send_with_reply_and_block(monitor_connection, p_message, DBUS_TIMEOUT_INFINITE, &err);
  574. dbus_message_unref(p_message);
  575. if (!reply || dbus_error_is_set(&err)) {
  576. ERR_PRINT(vformat("Failed to send DBus message: %s.", String::utf8(err.message)));
  577. dbus_error_free(&err);
  578. dbus_bus_remove_match(monitor_connection, r_response_filter.utf8().get_data(), &err);
  579. return false;
  580. }
  581. // Check request path matches our expectation
  582. {
  583. DBusMessageIter iter;
  584. if (dbus_message_iter_init(reply, &iter)) {
  585. if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
  586. const char *new_path = nullptr;
  587. dbus_message_iter_get_basic(&iter, &new_path);
  588. if (String::utf8(new_path) != r_response_path) {
  589. ERR_PRINT(vformat("Expected request path %s but actual path was %s.", r_response_path, new_path));
  590. dbus_bus_remove_match(monitor_connection, r_response_filter.utf8().get_data(), &err);
  591. if (dbus_error_is_set(&err)) {
  592. ERR_PRINT(vformat("Failed to remove DBus match: %s.", String::utf8(err.message)));
  593. dbus_error_free(&err);
  594. }
  595. return false;
  596. }
  597. }
  598. }
  599. }
  600. dbus_message_unref(reply);
  601. return true;
  602. }
  603. Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
  604. if (unsupported) {
  605. return FAILED;
  606. }
  607. ERR_FAIL_INDEX_V(int(p_mode), DisplayServer::FILE_DIALOG_MODE_SAVE_MAX, FAILED);
  608. ERR_FAIL_NULL_V(monitor_connection, FAILED);
  609. Vector<String> filter_names;
  610. Vector<String> filter_exts;
  611. Vector<String> filter_mimes;
  612. for (int i = 0; i < p_filters.size(); i++) {
  613. Vector<String> tokens = p_filters[i].split(";");
  614. if (tokens.size() >= 1) {
  615. String flt = tokens[0].strip_edges();
  616. String mime = (tokens.size() >= 3) ? tokens[2].strip_edges() : String();
  617. if (!flt.is_empty() || !mime.is_empty()) {
  618. if (tokens.size() >= 2) {
  619. if (flt == "*.*") {
  620. filter_exts.push_back("*");
  621. } else {
  622. filter_exts.push_back(flt);
  623. }
  624. filter_mimes.push_back(mime);
  625. filter_names.push_back(tokens[1]);
  626. } else {
  627. if (flt == "*.*") {
  628. filter_exts.push_back("*");
  629. filter_names.push_back(RTR("All Files") + " (*.*)");
  630. } else {
  631. filter_exts.push_back(flt);
  632. filter_names.push_back(flt);
  633. }
  634. filter_mimes.push_back(mime);
  635. }
  636. }
  637. }
  638. }
  639. if (filter_names.is_empty()) {
  640. filter_exts.push_back("*");
  641. filter_mimes.push_back("");
  642. filter_names.push_back(RTR("All Files") + " (*.*)");
  643. }
  644. // Open connection and add signal handler.
  645. FileDialogData fd;
  646. fd.callback = p_callback;
  647. fd.prev_focus = p_window_id;
  648. fd.filter_names = filter_names;
  649. fd.opt_in_cb = p_options_in_cb;
  650. String token;
  651. Error err = make_request_token(token);
  652. if (err != OK) {
  653. return err;
  654. }
  655. // Generate FileChooser message.
  656. const char *method = nullptr;
  657. if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
  658. method = "SaveFile";
  659. } else {
  660. method = "OpenFile";
  661. }
  662. DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
  663. {
  664. DBusMessageIter iter;
  665. dbus_message_iter_init_append(message, &iter);
  666. append_dbus_string(&iter, p_xid);
  667. append_dbus_string(&iter, p_title);
  668. DBusMessageIter arr_iter;
  669. dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
  670. append_dbus_dict_string(&arr_iter, "handle_token", token);
  671. append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
  672. append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
  673. append_dbus_dict_filters(&arr_iter, filter_names, filter_exts, filter_mimes);
  674. append_dbus_dict_options(&arr_iter, p_options, fd.option_ids);
  675. append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
  676. if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
  677. append_dbus_dict_string(&arr_iter, "current_name", p_filename);
  678. }
  679. dbus_message_iter_close_container(&iter, &arr_iter);
  680. }
  681. if (!send_request(message, token, fd.path, fd.filter)) {
  682. return FAILED;
  683. }
  684. MutexLock lock(file_dialog_mutex);
  685. file_dialogs.push_back(fd);
  686. return OK;
  687. }
  688. bool FreeDesktopPortalDesktop::inhibit(const String &p_xid) {
  689. if (unsupported) {
  690. return false;
  691. }
  692. MutexLock lock(inhibit_mutex);
  693. ERR_FAIL_COND_V_MSG(!inhibit_path.is_empty(), false, "Another inhibit request is already open.");
  694. String token;
  695. if (make_request_token(token) != OK) {
  696. return false;
  697. }
  698. DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_INHIBIT, "Inhibit");
  699. {
  700. DBusMessageIter iter;
  701. dbus_message_iter_init_append(message, &iter);
  702. append_dbus_string(&iter, p_xid);
  703. dbus_uint32_t flags = INHIBIT_FLAG_IDLE;
  704. dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &flags);
  705. {
  706. DBusMessageIter arr_iter;
  707. dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
  708. append_dbus_dict_string(&arr_iter, "handle_token", token);
  709. const char *reason = "Running Godot Engine Project";
  710. append_dbus_dict_string(&arr_iter, "reason", reason);
  711. dbus_message_iter_close_container(&iter, &arr_iter);
  712. }
  713. }
  714. if (!send_request(message, token, inhibit_path, inhibit_filter)) {
  715. return false;
  716. }
  717. return true;
  718. }
  719. void FreeDesktopPortalDesktop::uninhibit() {
  720. if (unsupported) {
  721. return;
  722. }
  723. MutexLock lock(inhibit_mutex);
  724. ERR_FAIL_COND_MSG(inhibit_path.is_empty(), "No inhibit request is active.");
  725. DBusError error;
  726. dbus_error_init(&error);
  727. DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, inhibit_path.utf8().get_data(), BUS_INTERFACE_REQUEST, "Close");
  728. DBusMessage *reply = dbus_connection_send_with_reply_and_block(monitor_connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error);
  729. dbus_message_unref(message);
  730. if (dbus_error_is_set(&error)) {
  731. ERR_PRINT(vformat("Failed to uninhibit: %s.", String::utf8(error.message)));
  732. dbus_error_free(&error);
  733. } else if (reply) {
  734. dbus_message_unref(reply);
  735. }
  736. dbus_bus_remove_match(monitor_connection, inhibit_filter.utf8().get_data(), &error);
  737. if (dbus_error_is_set(&error)) {
  738. ERR_PRINT(vformat("Failed to remove match: %s.", String::utf8(error.message)));
  739. dbus_error_free(&error);
  740. }
  741. inhibit_path.clear();
  742. inhibit_filter.clear();
  743. }
  744. void FreeDesktopPortalDesktop::process_callbacks() {
  745. {
  746. MutexLock lock(file_dialog_mutex);
  747. while (!pending_file_cbs.is_empty()) {
  748. FileDialogCallback cb = pending_file_cbs.front()->get();
  749. pending_file_cbs.pop_front();
  750. if (cb.opt_in_cb) {
  751. Variant ret;
  752. Callable::CallError ce;
  753. const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };
  754. cb.callback.callp(args, 4, ret, ce);
  755. if (ce.error != Callable::CallError::CALL_OK) {
  756. ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));
  757. }
  758. } else {
  759. Variant ret;
  760. Callable::CallError ce;
  761. const Variant *args[3] = { &cb.status, &cb.files, &cb.index };
  762. cb.callback.callp(args, 3, ret, ce);
  763. if (ce.error != Callable::CallError::CALL_OK) {
  764. ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));
  765. }
  766. }
  767. }
  768. }
  769. {
  770. MutexLock lock(color_picker_mutex);
  771. while (!pending_color_cbs.is_empty()) {
  772. ColorPickerCallback cb = pending_color_cbs.front()->get();
  773. pending_color_cbs.pop_front();
  774. Variant ret;
  775. Callable::CallError ce;
  776. const Variant *args[2] = { &cb.status, &cb.color };
  777. cb.callback.callp(args, 2, ret, ce);
  778. if (ce.error != Callable::CallError::CALL_OK) {
  779. ERR_PRINT(vformat("Failed to execute color picker callback: %s.", Variant::get_callable_error_text(cb.callback, args, 2, ce)));
  780. }
  781. }
  782. }
  783. }
  784. void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) {
  785. FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
  786. while (!portal->monitor_thread_abort.is_set()) {
  787. if (portal->monitor_connection) {
  788. while (true) {
  789. DBusMessage *msg = dbus_connection_pop_message(portal->monitor_connection);
  790. if (!msg) {
  791. break;
  792. } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Settings", "SettingChanged")) {
  793. DBusMessageIter iter;
  794. if (dbus_message_iter_init(msg, &iter)) {
  795. const char *value;
  796. dbus_message_iter_get_basic(&iter, &value);
  797. String name_space = String::utf8(value);
  798. dbus_message_iter_next(&iter);
  799. dbus_message_iter_get_basic(&iter, &value);
  800. String key = String::utf8(value);
  801. if (name_space == "org.freedesktop.appearance" && (key == "color-scheme" || key == "accent-color")) {
  802. callable_mp(portal, &FreeDesktopPortalDesktop::_system_theme_changed_callback).call_deferred();
  803. }
  804. }
  805. } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
  806. String path = String::utf8(dbus_message_get_path(msg));
  807. {
  808. MutexLock lock(portal->file_dialog_mutex);
  809. for (int i = 0; i < portal->file_dialogs.size(); i++) {
  810. FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
  811. if (fd.path == path) {
  812. DBusMessageIter iter;
  813. if (dbus_message_iter_init(msg, &iter)) {
  814. bool cancel = false;
  815. Vector<String> uris;
  816. Dictionary options;
  817. int index = 0;
  818. file_chooser_parse_response(&iter, fd.filter_names, fd.option_ids, cancel, uris, index, options);
  819. if (fd.callback.is_valid()) {
  820. FileDialogCallback cb;
  821. cb.callback = fd.callback;
  822. cb.status = !cancel;
  823. cb.files = uris;
  824. cb.index = index;
  825. cb.options = options;
  826. cb.opt_in_cb = fd.opt_in_cb;
  827. portal->pending_file_cbs.push_back(cb);
  828. }
  829. if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
  830. callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);
  831. }
  832. }
  833. DBusError err;
  834. dbus_error_init(&err);
  835. dbus_bus_remove_match(portal->monitor_connection, fd.filter.utf8().get_data(), &err);
  836. dbus_error_free(&err);
  837. portal->file_dialogs.remove_at(i);
  838. break;
  839. }
  840. }
  841. }
  842. {
  843. MutexLock lock(portal->color_picker_mutex);
  844. for (int i = 0; i < portal->color_pickers.size(); i++) {
  845. FreeDesktopPortalDesktop::ColorPickerData &cd = portal->color_pickers.write[i];
  846. if (cd.path == path) {
  847. DBusMessageIter iter;
  848. if (dbus_message_iter_init(msg, &iter)) {
  849. bool cancel = false;
  850. Color c;
  851. color_picker_parse_response(&iter, cancel, c);
  852. if (cd.callback.is_valid()) {
  853. ColorPickerCallback cb;
  854. cb.callback = cd.callback;
  855. cb.color = c;
  856. cb.status = !cancel;
  857. portal->pending_color_cbs.push_back(cb);
  858. }
  859. }
  860. DBusError err;
  861. dbus_error_init(&err);
  862. dbus_bus_remove_match(portal->monitor_connection, cd.filter.utf8().get_data(), &err);
  863. dbus_error_free(&err);
  864. portal->color_pickers.remove_at(i);
  865. break;
  866. }
  867. }
  868. }
  869. {
  870. MutexLock lock(portal->inhibit_mutex);
  871. if (portal->inhibit_path == path) {
  872. DBusMessageIter iter;
  873. if (dbus_message_iter_init(msg, &iter) && dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
  874. dbus_uint32_t resp_code;
  875. dbus_message_iter_get_basic(&iter, &resp_code);
  876. if (resp_code != 0) {
  877. // The protocol does not give any further details
  878. ERR_PRINT(vformat("Inhibit portal request failed with reason %u.", resp_code));
  879. }
  880. }
  881. }
  882. }
  883. }
  884. dbus_message_unref(msg);
  885. }
  886. dbus_connection_read_write(portal->monitor_connection, 0);
  887. }
  888. OS::get_singleton()->delay_usec(50'000);
  889. }
  890. }
  891. void FreeDesktopPortalDesktop::_system_theme_changed_callback() {
  892. if (system_theme_changed.is_valid()) {
  893. Variant ret;
  894. Callable::CallError ce;
  895. system_theme_changed.callp(nullptr, 0, ret, ce);
  896. if (ce.error != Callable::CallError::CALL_OK) {
  897. ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
  898. }
  899. }
  900. }
  901. FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
  902. DBusError err;
  903. dbus_error_init(&err);
  904. monitor_connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
  905. if (dbus_error_is_set(&err)) {
  906. dbus_error_free(&err);
  907. } else {
  908. theme_path = "type='signal',sender='org.freedesktop.portal.Desktop',interface='org.freedesktop.portal.Settings',member='SettingChanged'";
  909. dbus_bus_add_match(monitor_connection, theme_path.utf8().get_data(), &err);
  910. if (dbus_error_is_set(&err)) {
  911. dbus_error_free(&err);
  912. dbus_connection_unref(monitor_connection);
  913. monitor_connection = nullptr;
  914. }
  915. dbus_connection_read_write(monitor_connection, 0);
  916. }
  917. if (!unsupported) {
  918. monitor_thread_abort.clear();
  919. monitor_thread.start(FreeDesktopPortalDesktop::_thread_monitor, this);
  920. }
  921. }
  922. FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
  923. monitor_thread_abort.set();
  924. if (monitor_thread.is_started()) {
  925. monitor_thread.wait_to_finish();
  926. }
  927. if (monitor_connection) {
  928. DBusError err;
  929. for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
  930. dbus_error_init(&err);
  931. dbus_bus_remove_match(monitor_connection, fd.filter.utf8().get_data(), &err);
  932. dbus_error_free(&err);
  933. }
  934. dbus_error_init(&err);
  935. dbus_bus_remove_match(monitor_connection, theme_path.utf8().get_data(), &err);
  936. dbus_error_free(&err);
  937. dbus_connection_unref(monitor_connection);
  938. }
  939. }
  940. #endif // DBUS_ENABLED