1
0

platform-nix-portal.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright (c) 2023 Lain Bailey <[email protected]>
  3. * 2021 Georges Basile Stavracas Neto <[email protected]>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <assert.h>
  18. #include <gio/gio.h>
  19. #include "bmem.h"
  20. #include "dstr.h"
  21. #define PORTAL_NAME "org.freedesktop.portal.Desktop"
  22. struct portal_inhibit_info {
  23. GDBusConnection *c;
  24. GCancellable *cancellable;
  25. unsigned int signal_id;
  26. char *sender_name;
  27. char *request_path;
  28. bool active;
  29. };
  30. static void new_request(struct portal_inhibit_info *info, char **out_token,
  31. char **out_path)
  32. {
  33. struct dstr token;
  34. struct dstr path;
  35. uint32_t id;
  36. id = rand();
  37. dstr_init(&token);
  38. dstr_printf(&token, "obs_inhibit_portal%u", id);
  39. *out_token = token.array;
  40. dstr_init(&path);
  41. dstr_printf(&path, "/org/freedesktop/portal/desktop/request/%s/%s",
  42. info->sender_name, token.array);
  43. *out_path = path.array;
  44. }
  45. static inline void unsubscribe_from_request(struct portal_inhibit_info *info)
  46. {
  47. if (info->signal_id > 0) {
  48. g_dbus_connection_signal_unsubscribe(info->c, info->signal_id);
  49. info->signal_id = 0;
  50. }
  51. }
  52. static inline void remove_inhibit_data(struct portal_inhibit_info *info)
  53. {
  54. g_clear_pointer(&info->request_path, bfree);
  55. info->active = false;
  56. }
  57. static void response_received(GDBusConnection *bus, const char *sender_name,
  58. const char *object_path,
  59. const char *interface_name,
  60. const char *signal_name, GVariant *parameters,
  61. gpointer data)
  62. {
  63. UNUSED_PARAMETER(bus);
  64. UNUSED_PARAMETER(sender_name);
  65. UNUSED_PARAMETER(object_path);
  66. UNUSED_PARAMETER(interface_name);
  67. UNUSED_PARAMETER(signal_name);
  68. struct portal_inhibit_info *info = data;
  69. g_autoptr(GVariant) ret = NULL;
  70. uint32_t response;
  71. g_variant_get(parameters, "(u@a{sv})", &response, &ret);
  72. if (response != 0) {
  73. if (response == 1)
  74. blog(LOG_WARNING, "Inhibit denied by user");
  75. remove_inhibit_data(info);
  76. }
  77. unsubscribe_from_request(info);
  78. }
  79. static void inhibited_cb(GObject *source_object, GAsyncResult *result,
  80. gpointer user_data)
  81. {
  82. UNUSED_PARAMETER(source_object);
  83. struct portal_inhibit_info *info = user_data;
  84. g_autoptr(GVariant) reply = NULL;
  85. g_autoptr(GError) error = NULL;
  86. reply = g_dbus_connection_call_finish(info->c, result, &error);
  87. if (error != NULL) {
  88. if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
  89. blog(LOG_ERROR, "Failed to inhibit: %s",
  90. error->message);
  91. unsubscribe_from_request(info);
  92. remove_inhibit_data(info);
  93. }
  94. g_clear_object(&info->cancellable);
  95. }
  96. static void do_inhibit(struct portal_inhibit_info *info, const char *reason)
  97. {
  98. GVariantBuilder options;
  99. uint32_t flags = 0xC;
  100. char *token;
  101. info->active = true;
  102. new_request(info, &token, &info->request_path);
  103. info->signal_id = g_dbus_connection_signal_subscribe(
  104. info->c, PORTAL_NAME, "org.freedesktop.portal.Request",
  105. "Response", info->request_path, NULL,
  106. G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, response_received, info,
  107. NULL);
  108. g_variant_builder_init(&options, G_VARIANT_TYPE_VARDICT);
  109. g_variant_builder_add(&options, "{sv}", "handle_token",
  110. g_variant_new_string(token));
  111. g_variant_builder_add(&options, "{sv}", "reason",
  112. g_variant_new_string(reason));
  113. bfree(token);
  114. info->cancellable = g_cancellable_new();
  115. g_dbus_connection_call(info->c, PORTAL_NAME,
  116. "/org/freedesktop/portal/desktop",
  117. "org.freedesktop.portal.Inhibit", "Inhibit",
  118. g_variant_new("(sua{sv})", "", flags, &options),
  119. NULL, G_DBUS_CALL_FLAGS_NONE, -1,
  120. info->cancellable, inhibited_cb, info);
  121. }
  122. static void uninhibited_cb(GObject *source_object, GAsyncResult *result,
  123. gpointer user_data)
  124. {
  125. UNUSED_PARAMETER(source_object);
  126. struct portal_inhibit_info *info = user_data;
  127. g_autoptr(GVariant) reply = NULL;
  128. g_autoptr(GError) error = NULL;
  129. reply = g_dbus_connection_call_finish(info->c, result, &error);
  130. if (error)
  131. blog(LOG_WARNING, "Error uninhibiting: %s", error->message);
  132. }
  133. static void do_uninhibit(struct portal_inhibit_info *info)
  134. {
  135. if (info->cancellable) {
  136. /* If uninhibit is called before the inhibit call is finished,
  137. * cancel it instead.
  138. */
  139. g_cancellable_cancel(info->cancellable);
  140. g_clear_object(&info->cancellable);
  141. } else {
  142. g_dbus_connection_call(info->c, PORTAL_NAME, info->request_path,
  143. "org.freedesktop.portal.Request",
  144. "Close", g_variant_new("()"),
  145. G_VARIANT_TYPE_UNIT,
  146. G_DBUS_CALL_FLAGS_NONE, -1, NULL,
  147. uninhibited_cb, info);
  148. }
  149. remove_inhibit_data(info);
  150. }
  151. void portal_inhibit_info_destroy(struct portal_inhibit_info *info)
  152. {
  153. if (info) {
  154. g_cancellable_cancel(info->cancellable);
  155. unsubscribe_from_request(info);
  156. remove_inhibit_data(info);
  157. g_clear_pointer(&info->sender_name, bfree);
  158. g_clear_object(&info->cancellable);
  159. g_clear_object(&info->c);
  160. bfree(info);
  161. }
  162. }
  163. struct portal_inhibit_info *portal_inhibit_info_create(void)
  164. {
  165. struct portal_inhibit_info *info = bzalloc(sizeof(*info));
  166. g_autoptr(GVariant) reply = NULL;
  167. g_autoptr(GError) error = NULL;
  168. char *aux;
  169. info->c = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
  170. if (!info->c) {
  171. blog(LOG_ERROR, "Could not create dbus connection: %s",
  172. error->message);
  173. bfree(info);
  174. return NULL;
  175. }
  176. info->sender_name =
  177. bstrdup(g_dbus_connection_get_unique_name(info->c) + 1);
  178. while ((aux = strstr(info->sender_name, ".")) != NULL)
  179. *aux = '_';
  180. reply = g_dbus_connection_call_sync(
  181. info->c, "org.freedesktop.DBus", "/org/freedesktop/DBus",
  182. "org.freedesktop.DBus", "GetNameOwner",
  183. g_variant_new("(s)", PORTAL_NAME), NULL,
  184. G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, NULL);
  185. if (reply != NULL) {
  186. blog(LOG_DEBUG, "Found portal inhibitor");
  187. return info;
  188. }
  189. portal_inhibit_info_destroy(info);
  190. return NULL;
  191. }
  192. void portal_inhibit(struct portal_inhibit_info *info, const char *reason,
  193. bool active)
  194. {
  195. if (active == info->active)
  196. return;
  197. if (active)
  198. do_inhibit(info, reason);
  199. else
  200. do_uninhibit(info);
  201. }