Atlas - SDL_dbustray.c
Home / ext / SDL / src / tray / unix Lines: 102 | Size: 51150 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22/* Special thanks to the kind Hayden Gray (thag_iceman/A1029384756) from the SDL community for his help! */ 23 24#include "SDL_internal.h" 25 26#include "../../core/linux/SDL_dbus.h" 27 28#ifdef SDL_USE_LIBDBUS 29 30#include "../../video/SDL_surface_c.h" 31#include "../../core/unix/SDL_appid.h" 32#include "../SDL_tray_utils.h" 33#include "SDL_unixtray.h" 34#include <unistd.h> 35 36typedef struct SDL_TrayDriverDBus 37{ 38 SDL_DBusContext *dbus; 39} SDL_TrayDriverDBus; 40 41typedef struct SDL_TrayDBus 42{ 43 DBusConnection *connection; 44 char *service_name; 45 46 char *tooltip; 47 SDL_Surface *surface; 48 49 SDL_TrayClickCallback l_cb; 50 SDL_TrayClickCallback r_cb; 51 SDL_TrayClickCallback m_cb; 52 void *udata; 53 54 bool block; 55} SDL_TrayDBus; 56 57typedef struct SDL_TrayMenuDBus 58{ 59 SDL_ListNode *menu; 60 const char *menu_path; 61 62 SDL_TrayEntry **array_representation; 63} SDL_TrayMenuDBus; 64 65typedef struct SDL_TrayEntryDBus 66{ 67 SDL_MenuItem *item; 68 SDL_TrayMenu *sub_menu; 69} SDL_TrayEntryDBus; 70 71#define SNI_INTERFACE "org.kde.StatusNotifierItem" 72#define SNI_WATCHER_SERVICE "org.kde.StatusNotifierWatcher" 73#define SNI_WATCHER_PATH "/StatusNotifierWatcher" 74#define SNI_WATCHER_INTERFACE "org.kde.StatusNotifierWatcher" 75#define SNI_OBJECT_PATH "/StatusNotifierItem" 76static const char *sni_introspect = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\r\n<node>\r\n <interface name=\"org.kde.StatusNotifierItem\">\r\n\r\n <property name=\"Category\" type=\"s\" access=\"read\"/>\r\n <property name=\"Id\" type=\"s\" access=\"read\"/>\r\n <property name=\"Title\" type=\"s\" access=\"read\"/>\r\n <property name=\"Status\" type=\"s\" access=\"read\"/>\r\n <property name=\"WindowId\" type=\"i\" access=\"read\"/>\r\n\r\n <!-- An additional path to add to the theme search path to find the icons specified above. -->\r\n <property name=\"IconThemePath\" type=\"s\" access=\"read\"/>\r\n <property name=\"Menu\" type=\"o\" access=\"read\"/>\r\n <property name=\"ItemIsMenu\" type=\"b\" access=\"read\"/>\r\n\r\n\r\n <!-- main icon -->\r\n <!-- names are preferred over pixmaps -->\r\n <property name=\"IconName\" type=\"s\" access=\"read\"/>\r\n\r\n <!--struct containing width, height and image data-->\r\n <property name=\"IconPixmap\" type=\"a(iiay)\" access=\"read\">\r\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"KDbusImageVector\"/>\r\n </property>\r\n\r\n <property name=\"OverlayIconName\" type=\"s\" access=\"read\"/>\r\n\r\n <property name=\"OverlayIconPixmap\" type=\"a(iiay)\" access=\"read\">\r\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"KDbusImageVector\"/>\r\n </property>\r\n\r\n\r\n <!-- Requesting attention icon -->\r\n <property name=\"AttentionIconName\" type=\"s\" access=\"read\"/>\r\n\r\n <!--same definition as image-->\r\n <property name=\"AttentionIconPixmap\" type=\"a(iiay)\" access=\"read\">\r\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"KDbusImageVector\"/>\r\n </property>\r\n\r\n <property name=\"AttentionMovieName\" type=\"s\" access=\"read\"/>\r\n\r\n\r\n\r\n <!-- tooltip data -->\r\n\r\n <!--(iiay) is an image-->\r\n <property name=\"ToolTip\" type=\"(sa(iiay)ss)\" access=\"read\">\r\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"KDbusToolTipStruct\"/>\r\n </property>\r\n\r\n <method name=\"ProvideXdgActivationToken\">\r\n <arg name=\"token\" type=\"s\" direction=\"in\"/>\r\n </method>\r\n\r\n <!-- interaction: the systemtray wants the application to do something -->\r\n <method name=\"ContextMenu\">\r\n <!-- we\'re passing the coordinates of the icon, so the app knows where to put the popup window -->\r\n <arg name=\"x\" type=\"i\" direction=\"in\"/>\r\n <arg name=\"y\" type=\"i\" direction=\"in\"/>\r\n </method>\r\n\r\n <method name=\"Activate\">\r\n <arg name=\"x\" type=\"i\" direction=\"in\"/>\r\n <arg name=\"y\" type=\"i\" direction=\"in\"/>\r\n </method>\r\n\r\n <method name=\"SecondaryActivate\">\r\n <arg name=\"x\" type=\"i\" direction=\"in\"/>\r\n <arg name=\"y\" type=\"i\" direction=\"in\"/>\r\n </method>\r\n\r\n <method name=\"Scroll\">\r\n <arg name=\"delta\" type=\"i\" direction=\"in\"/>\r\n <arg name=\"orientation\" type=\"s\" direction=\"in\"/>\r\n </method>\r\n\r\n <!-- Signals: the client wants to change something in the status-->\r\n <signal name=\"NewTitle\">\r\n </signal>\r\n\r\n <signal name=\"NewIcon\">\r\n </signal>\r\n\r\n <signal name=\"NewAttentionIcon\">\r\n </signal>\r\n\r\n <signal name=\"NewOverlayIcon\">\r\n </signal>\r\n\r\n <signal name=\"NewMenu\">\r\n </signal>\r\n\r\n <signal name=\"NewToolTip\">\r\n </signal>\r\n\r\n <signal name=\"NewStatus\">\r\n <arg name=\"status\" type=\"s\"/>\r\n </signal>\r\n\r\n </interface>\r\n</node>"; 77 78static DBusHandlerResult TrayHandleGetAllProps(SDL_Tray *tray, SDL_TrayDBus *tray_dbus, SDL_TrayDriverDBus *driver, DBusMessage *msg) 79{ 80 SDL_TrayMenuDBus *menu_dbus; 81 DBusMessageIter iter, dict_iter, entry_iter, variant_iter; 82 DBusMessageIter struct_iter, array_iter; 83 DBusMessage *reply; 84 const char *interface; 85 const char *key; 86 const char *value; 87 const char *empty; 88 dbus_uint32_t uint32_val; 89 dbus_bool_t bool_value; 90 91 menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 92 93 empty = ""; 94 driver->dbus->message_iter_init(msg, &iter); 95 driver->dbus->message_iter_get_basic(&iter, &interface); 96 reply = driver->dbus->message_new_method_return(msg); 97 driver->dbus->message_iter_init_append(reply, &iter); 98 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter); 99 100 key = "Category"; 101 value = "ApplicationStatus"; 102 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 103 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 104 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 105 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 106 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 107 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 108 109 key = "Id"; 110 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 111 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 112 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 113 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &tray_dbus->service_name); 114 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 115 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 116 117 key = "Title"; 118 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 119 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 120 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 121 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &empty); 122 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 123 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 124 125 key = "Status"; 126 value = "Active"; 127 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 128 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 129 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 130 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 131 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 132 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 133 134 key = "IconName"; 135 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 136 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 137 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 138 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &empty); 139 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 140 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 141 142 key = "WindowId"; 143 uint32_val = 0; 144 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 145 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 146 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "i", &variant_iter); 147 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &uint32_val); 148 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 149 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 150 151 key = "ItemIsMenu"; 152 menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 153 if (menu_dbus && menu_dbus->menu_path) { 154 bool_value = TRUE; 155 } else { 156 bool_value = FALSE; 157 } 158 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 159 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 160 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 161 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_value); 162 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 163 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 164 165 if (menu_dbus && menu_dbus->menu_path) { 166 key = "Menu"; 167 value = menu_dbus->menu_path; 168 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 169 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 170 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "o", &variant_iter); 171 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_OBJECT_PATH, &value); 172 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 173 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 174 } else { 175 key = "Menu"; 176 value = "/NO_DBUSMENU"; 177 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 178 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 179 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "o", &variant_iter); 180 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_OBJECT_PATH, &value); 181 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 182 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 183 } 184 185 if (tray_dbus->surface) { 186 DBusMessageIter pixmap_array_iter, pixmap_struct_iter, pixmap_byte_array_iter; 187 188 key = "IconPixmap"; 189 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 190 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 191 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "a(iiay)", &variant_iter); 192 driver->dbus->message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "(iiay)", &pixmap_array_iter); 193 driver->dbus->message_iter_open_container(&pixmap_array_iter, DBUS_TYPE_STRUCT, NULL, &pixmap_struct_iter); 194 driver->dbus->message_iter_append_basic(&pixmap_struct_iter, DBUS_TYPE_INT32, &tray_dbus->surface->w); 195 driver->dbus->message_iter_append_basic(&pixmap_struct_iter, DBUS_TYPE_INT32, &tray_dbus->surface->h); 196 driver->dbus->message_iter_open_container(&pixmap_struct_iter, DBUS_TYPE_ARRAY, "y", &pixmap_byte_array_iter); 197 driver->dbus->message_iter_append_fixed_array(&pixmap_byte_array_iter, DBUS_TYPE_BYTE, &tray_dbus->surface->pixels, tray_dbus->surface->pitch * tray_dbus->surface->h); 198 driver->dbus->message_iter_close_container(&pixmap_struct_iter, &pixmap_byte_array_iter); 199 driver->dbus->message_iter_close_container(&pixmap_array_iter, &pixmap_struct_iter); 200 driver->dbus->message_iter_close_container(&variant_iter, &pixmap_array_iter); 201 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 202 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 203 } 204 205 if (tray_dbus->tooltip) { 206 key = "ToolTip"; 207 driver->dbus->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 208 driver->dbus->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 209 driver->dbus->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "(sa(iiay)ss)", &variant_iter); 210 driver->dbus->message_iter_open_container(&variant_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 211 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &empty); 212 driver->dbus->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(iiay)", &array_iter); 213 driver->dbus->message_iter_close_container(&struct_iter, &array_iter); 214 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &empty); 215 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &tray_dbus->tooltip); 216 driver->dbus->message_iter_close_container(&variant_iter, &struct_iter); 217 driver->dbus->message_iter_close_container(&entry_iter, &variant_iter); 218 driver->dbus->message_iter_close_container(&dict_iter, &entry_iter); 219 driver->dbus->message_iter_close_container(&iter, &dict_iter); 220 } 221 222 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 223 driver->dbus->message_unref(reply); 224 return DBUS_HANDLER_RESULT_HANDLED; 225} 226 227static DBusHandlerResult TrayHandleGetProp(SDL_Tray *tray, SDL_TrayDBus *tray_dbus, SDL_TrayDriverDBus *driver, DBusMessage *msg) 228{ 229 SDL_TrayMenuDBus *menu_dbus; 230 DBusMessageIter iter, variant_iter; 231 DBusMessage *reply; 232 const char *interface, *property; 233 const char *value; 234 const char *empty; 235 dbus_bool_t bool_value; 236 237 menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 238 239 empty = ""; 240 driver->dbus->message_iter_init(msg, &iter); 241 driver->dbus->message_iter_get_basic(&iter, &interface); 242 driver->dbus->message_iter_next(&iter); 243 driver->dbus->message_iter_get_basic(&iter, &property); 244 245 reply = driver->dbus->message_new_method_return(msg); 246 driver->dbus->message_iter_init_append(reply, &iter); 247 248 if (!SDL_strcmp(property, "Category")) { 249 value = "ApplicationStatus"; 250 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 251 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 252 driver->dbus->message_iter_close_container(&iter, &variant_iter); 253 } else if (!SDL_strcmp(property, "Id")) { 254 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 255 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &tray_dbus->service_name); 256 driver->dbus->message_iter_close_container(&iter, &variant_iter); 257 } else if (!SDL_strcmp(property, "Title")) { 258 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 259 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &empty); 260 driver->dbus->message_iter_close_container(&iter, &variant_iter); 261 } else if (!SDL_strcmp(property, "Status")) { 262 value = "Active"; 263 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 264 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 265 driver->dbus->message_iter_close_container(&iter, &variant_iter); 266 } else if (!SDL_strcmp(property, "IconName")) { 267 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 268 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &empty); 269 driver->dbus->message_iter_close_container(&iter, &variant_iter); 270 } else if (!SDL_strcmp(property, "ItemIsMenu")) { 271 if (menu_dbus && menu_dbus->menu_path) { 272 bool_value = TRUE; 273 } else { 274 bool_value = FALSE; 275 } 276 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 277 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_value); 278 driver->dbus->message_iter_close_container(&iter, &variant_iter); 279 } else if (!SDL_strcmp(property, "Menu")) { 280 if (menu_dbus && menu_dbus->menu_path) { 281 value = menu_dbus->menu_path; 282 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "o", &variant_iter); 283 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_OBJECT_PATH, &value); 284 driver->dbus->message_iter_close_container(&iter, &variant_iter); 285 } else { 286 value = "/NO_DBUSMENU"; 287 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "o", &variant_iter); 288 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_OBJECT_PATH, &value); 289 driver->dbus->message_iter_close_container(&iter, &variant_iter); 290 } 291 } else if (!SDL_strcmp(property, "IconPixmap") && tray_dbus->surface) { 292 DBusMessageIter array_iter, struct_iter; 293 DBusMessageIter byte_array_iter; 294 295 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "a(iiay)", &variant_iter); 296 driver->dbus->message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "(iiay)", &array_iter); 297 driver->dbus->message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 298 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &tray_dbus->surface->w); 299 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &tray_dbus->surface->h); 300 driver->dbus->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "y", &byte_array_iter); 301 driver->dbus->message_iter_append_fixed_array(&byte_array_iter, DBUS_TYPE_BYTE, &tray_dbus->surface->pixels, tray_dbus->surface->pitch * tray_dbus->surface->h); 302 driver->dbus->message_iter_close_container(&struct_iter, &byte_array_iter); 303 driver->dbus->message_iter_close_container(&array_iter, &struct_iter); 304 driver->dbus->message_iter_close_container(&variant_iter, &array_iter); 305 driver->dbus->message_iter_close_container(&iter, &variant_iter); 306 } else if (!SDL_strcmp(property, "ToolTip") && tray_dbus->tooltip) { 307 DBusMessageIter struct_iter, array_iter; 308 309 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "(sa(iiay)ss)", &variant_iter); 310 driver->dbus->message_iter_open_container(&variant_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 311 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &empty); 312 driver->dbus->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(iiay)", &array_iter); 313 driver->dbus->message_iter_close_container(&struct_iter, &array_iter); 314 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &empty); 315 driver->dbus->message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &tray_dbus->tooltip); 316 driver->dbus->message_iter_close_container(&variant_iter, &struct_iter); 317 driver->dbus->message_iter_close_container(&iter, &variant_iter); 318 } else if (!SDL_strcmp(property, "WindowId")) { 319 dbus_uint32_t uint32_val; 320 321 uint32_val = 0; 322 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "i", &variant_iter); 323 driver->dbus->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &uint32_val); 324 driver->dbus->message_iter_close_container(&iter, &variant_iter); 325 } else { 326 driver->dbus->message_unref(reply); 327 reply = driver->dbus->message_new_error(msg, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); 328 } 329 330 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 331 driver->dbus->message_unref(reply); 332 return DBUS_HANDLER_RESULT_HANDLED; 333} 334 335static DBusHandlerResult TrayMessageHandler(DBusConnection *connection, DBusMessage *msg, void *user_data) 336{ 337 SDL_Tray *tray; 338 SDL_TrayDBus *tray_dbus; 339 SDL_TrayDriverDBus *driver; 340 DBusMessage *reply; 341 342 tray = user_data; 343 tray_dbus = (SDL_TrayDBus *)tray->internal; 344 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 345 346 if (driver->dbus->message_is_method_call(msg, "org.freedesktop.DBus.Properties", "Get")) { 347 return TrayHandleGetProp(tray, tray_dbus, driver, msg); 348 } else if (driver->dbus->message_is_method_call(msg, "org.freedesktop.DBus.Properties", "GetAll")) { 349 return TrayHandleGetAllProps(tray, tray_dbus, driver, msg); 350 } else if (driver->dbus->message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { 351 reply = driver->dbus->message_new_method_return(msg); 352 driver->dbus->message_append_args(reply, DBUS_TYPE_STRING, &sni_introspect, DBUS_TYPE_INVALID); 353 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 354 driver->dbus->message_unref(reply); 355 return DBUS_HANDLER_RESULT_HANDLED; 356 } else if (driver->dbus->message_is_method_call(msg, SNI_INTERFACE, "ContextMenu")) { 357 if (tray_dbus->r_cb) { 358 tray_dbus->r_cb(tray_dbus->udata, tray); 359 } 360 361 reply = driver->dbus->message_new_method_return(msg); 362 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 363 driver->dbus->message_unref(reply); 364 return DBUS_HANDLER_RESULT_HANDLED; 365 } else if (driver->dbus->message_is_method_call(msg, SNI_INTERFACE, "Activate")) { 366 if (tray_dbus->l_cb) { 367 tray_dbus->l_cb(tray_dbus->udata, tray); 368 } 369 370 reply = driver->dbus->message_new_method_return(msg); 371 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 372 driver->dbus->message_unref(reply); 373 return DBUS_HANDLER_RESULT_HANDLED; 374 } else if (driver->dbus->message_is_method_call(msg, SNI_INTERFACE, "SecondaryActivate")) { 375 if (tray_dbus->m_cb) { 376 tray_dbus->m_cb(tray_dbus->udata, tray); 377 } 378 379 reply = driver->dbus->message_new_method_return(msg); 380 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 381 driver->dbus->message_unref(reply); 382 return DBUS_HANDLER_RESULT_HANDLED; 383 } else if (driver->dbus->message_is_method_call(msg, SNI_INTERFACE, "Scroll")) { 384 DBusError err; 385 char *orientation; 386 Sint32 delta; 387 388 driver->dbus->error_init(&err); 389 driver->dbus->message_get_args(msg, &err, DBUS_TYPE_INT32, &delta, DBUS_TYPE_STRING, &orientation, DBUS_TYPE_INVALID); 390 if (!driver->dbus->error_is_set(&err)) { 391 /* Scroll callback support will come later :) */ 392 } else { 393 driver->dbus->error_free(&err); 394 } 395 396 reply = driver->dbus->message_new_method_return(msg); 397 driver->dbus->connection_send(tray_dbus->connection, reply, NULL); 398 driver->dbus->message_unref(reply); 399 return DBUS_HANDLER_RESULT_HANDLED; 400 } 401 402 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 403} 404 405SDL_Tray *CreateTray(SDL_TrayDriver *driver, SDL_PropertiesID props) 406{ 407 SDL_TrayDriverDBus *dbus_driver; 408 SDL_TrayDBus *tray_dbus; 409 SDL_Tray *tray; 410 SDL_Surface *icon; 411 const char *tooltip; 412 const char *object_path; 413 char *register_name; 414 DBusObjectPathVTable vtable; 415 DBusError err; 416 int status; 417 dbus_bool_t bool_status; 418#define CLEANUP() \ 419 SDL_free(tray_dbus->tooltip); \ 420 SDL_DestroySurface(tray_dbus->surface); \ 421 SDL_free(tray); \ 422 SDL_free(tray_dbus) 423#define CLEANUP2() \ 424 dbus_driver->dbus->connection_close(tray_dbus->connection); \ 425 CLEANUP() 426 427 /* Get properties */ 428 tooltip = SDL_GetStringProperty(props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, NULL); 429 icon = (SDL_Surface *)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_ICON_POINTER, NULL); 430 431 /* Allocate the tray structure */ 432 tray_dbus = SDL_malloc(sizeof(SDL_TrayDBus)); 433 if (!tray_dbus) { 434 return NULL; 435 } 436 tray = SDL_malloc(sizeof(SDL_Tray)); 437 if (!tray) { 438 CLEANUP(); 439 return NULL; 440 } 441 442 /* Populate */ 443 tray->menu = NULL; 444 tray->driver = driver; 445 tray->internal = tray_dbus; 446 if (tooltip) { 447 tray_dbus->tooltip = SDL_strdup(tooltip); 448 } else { 449 tray_dbus->tooltip = NULL; 450 } 451 tray_dbus->surface = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_ARGB32); 452 tray_dbus->block = false; 453 454 /* Connect */ 455 dbus_driver = (SDL_TrayDriverDBus *)driver->internal; 456 dbus_driver->dbus->error_init(&err); 457 tray_dbus->connection = dbus_driver->dbus->bus_get_private(DBUS_BUS_SESSION, &err); 458 if (dbus_driver->dbus->error_is_set(&err)) { 459 SDL_SetError("Unable to create tray: %s", err.message); 460 dbus_driver->dbus->error_free(&err); 461 CLEANUP(); 462 return NULL; 463 } 464 if (!tray_dbus->connection) { 465 SDL_SetError("Unable to create tray: unable to get connection!"); 466 CLEANUP(); 467 return NULL; 468 } 469 470 /* Request name */ 471 driver->count++; 472 if (SDL_GetSandbox() == SDL_SANDBOX_FLATPAK) { 473 SDL_asprintf(&tray_dbus->service_name, "%s.tray%d", SDL_GetAppID(), driver->count); 474 } else { 475 SDL_asprintf(&tray_dbus->service_name, "org.kde.StatusNotifierItem-%d-%d", getpid(), driver->count); 476 } 477 status = dbus_driver->dbus->bus_request_name(tray_dbus->connection, tray_dbus->service_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &err); 478 if (dbus_driver->dbus->error_is_set(&err)) { 479 SDL_SetError("Unable to create tray: %s", err.message); 480 dbus_driver->dbus->error_free(&err); 481 CLEANUP2(); 482 return NULL; 483 } 484 if (status != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { 485 SDL_SetError("Unable to create tray: unable to request a unique name!"); 486 CLEANUP2(); 487 return NULL; 488 } 489 490 /* Create object */ 491 object_path = SNI_OBJECT_PATH; 492 vtable.message_function = TrayMessageHandler; 493 bool_status = dbus_driver->dbus->connection_try_register_object_path(tray_dbus->connection, object_path, &vtable, tray, &err); 494 if (dbus_driver->dbus->error_is_set(&err)) { 495 SDL_SetError("Unable to create tray: %s", err.message); 496 dbus_driver->dbus->error_free(&err); 497 CLEANUP2(); 498 return NULL; 499 } 500 if (!bool_status) { 501 SDL_SetError("Unable to create tray: unable to register object path!"); 502 CLEANUP2(); 503 return NULL; 504 } 505 506 /* Register */ 507 register_name = tray_dbus->service_name; 508 if (!SDL_DBus_CallVoidMethodOnConnection(tray_dbus->connection, SNI_WATCHER_SERVICE, SNI_WATCHER_PATH, SNI_WATCHER_INTERFACE, "RegisterStatusNotifierItem", DBUS_TYPE_STRING, ®ister_name, DBUS_TYPE_INVALID)) { 509 SDL_SetError("Unable to create tray: unable to register status notifier item!"); 510 CLEANUP2(); 511 return NULL; 512 } 513 514 /* Icon mouse event callbacks */ 515 tray_dbus->l_cb = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_LEFTCLICK_CALLBACK_POINTER, NULL); 516 tray_dbus->r_cb = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_RIGHTCLICK_CALLBACK_POINTER, NULL); 517 tray_dbus->m_cb = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_MIDDLECLICK_CALLBACK_POINTER, NULL); 518 tray_dbus->udata = SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_USERDATA_POINTER, NULL); 519 520 return tray; 521} 522 523void DestroyDriver(SDL_TrayDriver *driver) 524{ 525 SDL_DBus_Quit(); 526 SDL_free(driver); 527} 528 529void DestroyMenu(SDL_TrayMenu *menu) 530{ 531 SDL_TrayMenuDBus *menu_dbus; 532 533 if (!menu) { 534 return; 535 } 536 537 menu_dbus = (SDL_TrayMenuDBus *)menu->internal; 538 539 if (menu_dbus->menu) { 540 SDL_ListNode *cursor; 541 542 cursor = menu_dbus->menu; 543 while (cursor) { 544 SDL_MenuItem *item; 545 SDL_TrayEntry *entry; 546 SDL_TrayEntryDBus *entry_dbus; 547 548 item = cursor->entry; 549 entry = item->udata; 550 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 551 552 if (entry_dbus->sub_menu) { 553 DestroyMenu(entry_dbus->sub_menu); 554 entry_dbus->sub_menu = NULL; 555 } 556 SDL_free(item); 557 SDL_free(entry); 558 559 cursor = cursor->next; 560 } 561 SDL_ListClear(&menu_dbus->menu); 562 } 563 564 if (menu_dbus->array_representation) { 565 SDL_free(menu_dbus->array_representation); 566 } 567 568 SDL_free(menu_dbus); 569 SDL_free(menu); 570} 571 572void DestroyTray(SDL_Tray *tray) 573{ 574 SDL_TrayDBus *tray_dbus; 575 SDL_TrayDriverDBus *driver; 576 577 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 578 tray_dbus = (SDL_TrayDBus *)tray->internal; 579 580 /* Destroy connection */ 581 driver->dbus->connection_flush(tray_dbus->connection); 582 tray_dbus->block = true; 583 driver->dbus->connection_close(tray_dbus->connection); 584 tray_dbus->connection = NULL; 585 586 /* Destroy icon and tooltip */ 587 SDL_free(tray_dbus->tooltip); 588 SDL_DestroySurface(tray_dbus->surface); 589 590 /* Destroy the menus and entries */ 591 if (tray->menu) { 592 DestroyMenu(tray->menu); 593 tray->menu = NULL; 594 } 595 596 /* Free the tray */ 597 SDL_free(tray); 598} 599 600void UpdateTray(SDL_Tray *tray) 601{ 602 SDL_TrayDBus *tray_dbus; 603 SDL_TrayDriverDBus *driver; 604 605 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 606 tray_dbus = (SDL_TrayDBus *)tray->internal; 607 608 if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 609 return; 610 } 611 612 if (tray_dbus->block) { 613 return; 614 } 615 616 driver->dbus->connection_read_write(tray_dbus->connection, 0); 617 while (driver->dbus->connection_dispatch(tray_dbus->connection) == DBUS_DISPATCH_DATA_REMAINS) { 618 if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 619 break; 620 } 621 622 if (tray_dbus->block) { 623 break; 624 } 625 626 SDL_DelayNS(SDL_US_TO_NS(10)); 627 } 628} 629 630void SetTrayIcon(SDL_Tray *tray, SDL_Surface *surface) 631{ 632 SDL_TrayDBus *tray_dbus; 633 SDL_TrayDriverDBus *driver; 634 DBusMessage *signal; 635 636 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 637 tray_dbus = (SDL_TrayDBus *)tray->internal; 638 639 if (tray_dbus->surface) { 640 SDL_DestroySurface(tray_dbus->surface); 641 } 642 tray_dbus->surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB32); 643 644 signal = driver->dbus->message_new_signal(SNI_OBJECT_PATH, SNI_INTERFACE, "NewIcon"); 645 driver->dbus->connection_send(tray_dbus->connection, signal, NULL); 646 driver->dbus->connection_flush(tray_dbus->connection); 647 driver->dbus->message_unref(signal); 648} 649 650void SetTrayTooltip(SDL_Tray *tray, const char *text) 651{ 652 SDL_TrayDBus *tray_dbus; 653 SDL_TrayDriverDBus *driver; 654 DBusMessage *signal; 655 656 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 657 tray_dbus = (SDL_TrayDBus *)tray->internal; 658 659 if (tray_dbus->tooltip) { 660 SDL_free(tray_dbus->tooltip); 661 } 662 663 if (text) { 664 tray_dbus->tooltip = SDL_strdup(text); 665 } else { 666 tray_dbus->tooltip = NULL; 667 } 668 669 signal = driver->dbus->message_new_signal(SNI_OBJECT_PATH, SNI_INTERFACE, "NewToolTip"); 670 driver->dbus->connection_send(tray_dbus->connection, signal, NULL); 671 driver->dbus->connection_flush(tray_dbus->connection); 672 driver->dbus->message_unref(signal); 673} 674 675SDL_TrayMenu *CreateTrayMenu(SDL_Tray *tray) 676{ 677 SDL_TrayMenuDBus *menu_dbus; 678 679 menu_dbus = SDL_malloc(sizeof(SDL_TrayMenuDBus)); 680 if (!menu_dbus) { 681 SDL_SetError("Unable to create tray menu: allocation failure!"); 682 return NULL; 683 } 684 tray->menu = SDL_malloc(sizeof(SDL_TrayMenu)); 685 if (!tray->menu) { 686 SDL_free(menu_dbus); 687 SDL_SetError("Unable to create tray menu: allocation failure!"); 688 return NULL; 689 } 690 691 menu_dbus->menu = NULL; 692 menu_dbus->menu_path = NULL; 693 tray->menu->parent_tray = tray; 694 tray->menu->parent_entry = NULL; 695 tray->menu->internal = menu_dbus; 696 menu_dbus->array_representation = NULL; 697 698 return tray->menu; 699} 700 701SDL_TrayMenu *CreateTraySubmenu(SDL_TrayEntry *entry) 702{ 703 SDL_TrayMenuDBus *menu_dbus; 704 SDL_TrayMenu *menu; 705 SDL_TrayEntryDBus *entry_dbus; 706 707 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 708 menu_dbus = SDL_malloc(sizeof(SDL_TrayMenuDBus)); 709 if (!menu_dbus) { 710 SDL_SetError("Unable to create tray submenu: allocation failure!"); 711 return NULL; 712 } 713 menu = SDL_malloc(sizeof(SDL_TrayMenu)); 714 if (!menu) { 715 SDL_free(menu_dbus); 716 SDL_SetError("Unable to create tray submenu: allocation failure!"); 717 return NULL; 718 } 719 720 menu_dbus->menu = NULL; 721 menu_dbus->menu_path = NULL; 722 menu->parent_tray = entry->parent->parent_tray; 723 menu->parent_entry = entry; 724 menu->internal = menu_dbus; 725 entry_dbus->sub_menu = menu; 726 menu_dbus->array_representation = NULL; 727 728 return menu; 729} 730 731SDL_TrayMenu *GetTraySubmenu(SDL_TrayEntry *entry) 732{ 733 SDL_TrayEntryDBus *entry_dbus; 734 735 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 736 737 return entry_dbus->sub_menu; 738} 739 740bool TrayRightClickHandler(SDL_ListNode *menu, void *udata) 741{ 742 SDL_Tray *tray = (SDL_Tray *)udata; 743 SDL_TrayDBus *tray_dbus = (SDL_TrayDBus *)tray->internal; 744 745 return tray_dbus->r_cb(tray_dbus->udata, tray); 746} 747 748void TraySendNewMenu(SDL_Tray *tray, const char *new_path) 749{ 750 SDL_TrayDriverDBus *driver; 751 SDL_TrayDBus *tray_dbus; 752 SDL_TrayMenuDBus *menu_dbus; 753 DBusMessage *signal; 754 755 tray_dbus = (SDL_TrayDBus *)tray->internal; 756 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 757 menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 758 759 driver->dbus->connection_flush(tray_dbus->connection); 760 761 signal = driver->dbus->message_new_signal(SNI_OBJECT_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged"); 762 if (signal) { 763 DBusMessageIter iter, dict, ientry, value; 764 const char *iface; 765 const char *prop; 766 const char *path; 767 dbus_bool_t bool_val; 768 769 iface = SNI_INTERFACE; 770 prop = "Menu"; 771 if (new_path) { 772 path = menu_dbus->menu_path = new_path; 773 } else { 774 path = menu_dbus->menu_path; 775 } 776 bool_val = TRUE; 777 driver->dbus->message_iter_init_append(signal, &iter); 778 driver->dbus->message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface); 779 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); 780 driver->dbus->message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &ientry); 781 driver->dbus->message_iter_append_basic(&ientry, DBUS_TYPE_STRING, &prop); 782 driver->dbus->message_iter_open_container(&ientry, DBUS_TYPE_VARIANT, "o", &value); 783 driver->dbus->message_iter_append_basic(&value, DBUS_TYPE_OBJECT_PATH, &path); 784 driver->dbus->message_iter_close_container(&ientry, &value); 785 driver->dbus->message_iter_close_container(&dict, &ientry); 786 prop = "ItemIsMenu"; 787 driver->dbus->message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &ientry); 788 driver->dbus->message_iter_append_basic(&ientry, DBUS_TYPE_STRING, &prop); 789 driver->dbus->message_iter_open_container(&ientry, DBUS_TYPE_VARIANT, "b", &value); 790 driver->dbus->message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &bool_val); 791 driver->dbus->message_iter_close_container(&ientry, &value); 792 driver->dbus->message_iter_close_container(&dict, &ientry); 793 driver->dbus->message_iter_close_container(&iter, &dict); 794 driver->dbus->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &dict); 795 driver->dbus->message_iter_close_container(&iter, &dict); 796 driver->dbus->connection_send(tray_dbus->connection, signal, NULL); 797 driver->dbus->connection_flush(tray_dbus->connection); 798 driver->dbus->message_unref(signal); 799 } 800 801 signal = driver->dbus->message_new_signal(SNI_OBJECT_PATH, SNI_INTERFACE, "NewMenu"); 802 if (signal) { 803 driver->dbus->connection_send(tray_dbus->connection, signal, NULL); 804 driver->dbus->connection_flush(tray_dbus->connection); 805 driver->dbus->message_unref(signal); 806 } 807 808 driver->dbus->connection_flush(tray_dbus->connection); 809} 810 811void TrayNewMenuOnMenuUpdateCallback(SDL_ListNode *menu, const char *path, void *cbdata) 812{ 813 TraySendNewMenu((SDL_Tray *)cbdata, path); 814} 815 816SDL_TrayEntry *InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) 817{ 818 SDL_Tray *tray; 819 SDL_TrayDriverDBus *driver; 820 SDL_TrayMenuDBus *menu_dbus; 821 SDL_TrayDBus *tray_dbus; 822 SDL_TrayEntry *entry; 823 SDL_TrayEntryDBus *entry_dbus; 824 bool update; 825 826 tray = menu->parent_tray; 827 menu_dbus = (SDL_TrayMenuDBus *)menu->internal; 828 tray_dbus = (SDL_TrayDBus *)tray->internal; 829 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 830 831 entry_dbus = SDL_malloc(sizeof(SDL_TrayEntryDBus)); 832 if (!entry_dbus) { 833 SDL_SetError("Unable to create tray entry: allocation failure!"); 834 return NULL; 835 } 836 entry = SDL_malloc(sizeof(SDL_TrayEntry)); 837 if (!entry) { 838 SDL_free(entry_dbus); 839 SDL_SetError("Unable to create tray entry: allocation failure!"); 840 return NULL; 841 } 842 843 entry->parent = menu; 844 entry->internal = entry_dbus; 845 entry_dbus->item = SDL_DBus_CreateMenuItem(); 846 entry_dbus->item->utf8 = label; 847 if (!label) { 848 entry_dbus->item->type = SDL_MENU_ITEM_TYPE_SEPERATOR; 849 } else if (flags & SDL_TRAYENTRY_CHECKBOX) { 850 entry_dbus->item->type = SDL_MENU_ITEM_TYPE_CHECKBOX; 851 } else { 852 entry_dbus->item->type = SDL_MENU_ITEM_TYPE_NORMAL; 853 } 854 entry_dbus->item->flags = SDL_MENU_ITEM_FLAGS_NONE; 855 entry_dbus->item->cb_data = NULL; 856 entry_dbus->item->cb = NULL; 857 entry_dbus->item->sub_menu = NULL; 858 entry_dbus->item->udata = entry; 859 entry_dbus->sub_menu = NULL; 860 861 if (menu_dbus->menu) { 862 update = true; 863 } else { 864 update = false; 865 } 866 867 if (menu->parent_entry) { 868 update = true; 869 } 870 871 SDL_ListInsertAtPosition(&menu_dbus->menu, pos, entry_dbus->item); 872 873 if (menu->parent_entry) { 874 SDL_TrayEntryDBus *parent_entry_dbus; 875 876 parent_entry_dbus = (SDL_TrayEntryDBus *)menu->parent_entry->internal; 877 parent_entry_dbus->item->sub_menu = menu_dbus->menu; 878 } 879 880 if (update) { 881 SDL_TrayMenuDBus *main_menu_dbus; 882 883 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 884 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 885 } else { 886 menu_dbus->menu_path = SDL_DBus_ExportMenu(driver->dbus, tray_dbus->connection, menu_dbus->menu); 887 888 if (menu_dbus->menu_path) { 889 TraySendNewMenu(tray, NULL); 890 } 891 } 892 893 if (menu->parent_tray && !menu->parent_entry && tray_dbus->r_cb) { 894 SDL_DBus_RegisterMenuOpenCallback(menu_dbus->menu, TrayRightClickHandler, tray); 895 } 896 897 return entry; 898} 899 900SDL_TrayEntry **GetTrayEntries(SDL_TrayMenu *menu, int *count) 901{ 902 SDL_TrayEntry **array_representation; 903 SDL_TrayMenuDBus *menu_dbus; 904 SDL_ListNode *cursor; 905 int sz; 906 int i; 907 908 menu_dbus = (SDL_TrayMenuDBus *)menu->internal; 909 910 if (menu_dbus->array_representation) { 911 SDL_free(menu_dbus->array_representation); 912 } 913 914 sz = SDL_ListCountEntries(&menu_dbus->menu); 915 array_representation = SDL_calloc(sz + 1, sizeof(SDL_TrayEntry *)); 916 if (!array_representation) { 917 SDL_SetError("Memory allocation failure!"); 918 return NULL; 919 } 920 921 i = 0; 922 cursor = menu_dbus->menu; 923 while (cursor) { 924 SDL_MenuItem *item; 925 926 item = cursor->entry; 927 array_representation[i] = item->udata; 928 cursor = cursor->next; 929 i++; 930 } 931 array_representation[sz] = NULL; 932 933 *count = sz; 934 return array_representation; 935} 936 937void RemoveTrayEntry(SDL_TrayEntry *entry) 938{ 939 SDL_TrayEntryDBus *entry_dbus; 940 SDL_TrayMenuDBus *menu_dbus; 941 SDL_Tray *tray; 942 SDL_TrayDriverDBus *driver; 943 SDL_TrayDBus *tray_dbus; 944 SDL_TrayMenuDBus *main_menu_dbus; 945 const char *old_path; 946 947 tray = entry->parent->parent_tray; 948 tray_dbus = (SDL_TrayDBus *)tray->internal; 949 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 950 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 951 menu_dbus = (SDL_TrayMenuDBus *)entry->parent->internal; 952 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 953 954 tray_dbus->block = true; 955 if (menu_dbus->menu->entry == entry_dbus->item && menu_dbus->menu->next) { 956 SDL_DBus_TransferMenuItemProperties(entry_dbus->item, (SDL_MenuItem *)menu_dbus->menu->next->entry); 957 } 958 959 old_path = NULL; 960 if (!main_menu_dbus->menu->next) { 961 old_path = main_menu_dbus->menu_path; 962 } 963 964 driver->dbus->connection_flush(tray_dbus->connection); 965 DestroyMenu(entry_dbus->sub_menu); 966 SDL_ListRemove(&menu_dbus->menu, entry_dbus->item); 967 SDL_free(entry_dbus->item); 968 SDL_free(entry_dbus); 969 SDL_free(entry); 970 971 if (old_path) { 972 SDL_DBus_RetractMenu(driver->dbus, tray_dbus->connection, &main_menu_dbus->menu_path); 973 } 974 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 975 driver->dbus->connection_flush(tray_dbus->connection); 976 tray_dbus->block = false; 977} 978 979void EntryCallback(SDL_MenuItem *item, void *udata) 980{ 981 SDL_TrayCallback entry_cb; 982 983 entry_cb = item->udata2; 984 entry_cb(udata, item->udata); 985} 986 987void SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) 988{ 989 SDL_TrayEntryDBus *entry_dbus; 990 SDL_Tray *tray; 991 SDL_TrayDriverDBus *driver; 992 SDL_TrayDBus *tray_dbus; 993 SDL_TrayMenuDBus *main_menu_dbus; 994 995 tray = entry->parent->parent_tray; 996 tray_dbus = (SDL_TrayDBus *)tray->internal; 997 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 998 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 999 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 1000 1001 entry_dbus->item->cb = EntryCallback; 1002 entry_dbus->item->cb_data = userdata; 1003 entry_dbus->item->udata2 = callback; 1004 1005 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 1006} 1007 1008void SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) 1009{ 1010 SDL_TrayEntryDBus *entry_dbus; 1011 SDL_Tray *tray; 1012 SDL_TrayDriverDBus *driver; 1013 SDL_TrayDBus *tray_dbus; 1014 SDL_TrayMenuDBus *main_menu_dbus; 1015 1016 tray = entry->parent->parent_tray; 1017 tray_dbus = (SDL_TrayDBus *)tray->internal; 1018 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 1019 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 1020 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 1021 1022 entry_dbus->item->utf8 = label; 1023 1024 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 1025} 1026 1027const char *GetTrayEntryLabel(SDL_TrayEntry *entry) 1028{ 1029 return ((SDL_TrayEntryDBus *)entry->internal)->item->utf8; 1030} 1031 1032void SetTrayEntryChecked(SDL_TrayEntry *entry, bool val) 1033{ 1034 SDL_TrayEntryDBus *entry_dbus; 1035 SDL_Tray *tray; 1036 SDL_TrayDriverDBus *driver; 1037 SDL_TrayDBus *tray_dbus; 1038 SDL_TrayMenuDBus *main_menu_dbus; 1039 1040 tray = entry->parent->parent_tray; 1041 tray_dbus = (SDL_TrayDBus *)tray->internal; 1042 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 1043 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 1044 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 1045 1046 if (val) { 1047 entry_dbus->item->flags |= SDL_MENU_ITEM_FLAGS_CHECKED; 1048 } else { 1049 entry_dbus->item->flags &= ~(SDL_MENU_ITEM_FLAGS_CHECKED); 1050 } 1051 1052 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 1053} 1054 1055bool GetTrayEntryChecked(SDL_TrayEntry *entry) 1056{ 1057 return ((SDL_TrayEntryDBus *)entry->internal)->item->flags & SDL_MENU_ITEM_FLAGS_CHECKED; 1058} 1059 1060void SetTrayEntryEnabled(SDL_TrayEntry *entry, bool val) 1061{ 1062 SDL_TrayEntryDBus *entry_dbus; 1063 SDL_Tray *tray; 1064 SDL_TrayDriverDBus *driver; 1065 SDL_TrayDBus *tray_dbus; 1066 SDL_TrayMenuDBus *main_menu_dbus; 1067 1068 tray = entry->parent->parent_tray; 1069 tray_dbus = (SDL_TrayDBus *)tray->internal; 1070 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 1071 entry_dbus = (SDL_TrayEntryDBus *)entry->internal; 1072 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 1073 1074 if (!val) { 1075 entry_dbus->item->flags |= SDL_MENU_ITEM_FLAGS_DISABLED; 1076 } else { 1077 entry_dbus->item->flags &= ~(SDL_MENU_ITEM_FLAGS_DISABLED); 1078 } 1079 1080 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 1081} 1082 1083bool GetTrayEntryEnabled(SDL_TrayEntry *entry) 1084{ 1085 return !(((SDL_TrayEntryDBus *)entry->internal)->item->flags & SDL_MENU_ITEM_FLAGS_DISABLED); 1086} 1087 1088void ClickTrayEntry(SDL_TrayEntry *entry) 1089{ 1090 SDL_TrayEntryDBus *dbus_entry; 1091 SDL_TrayCallback entry_cb; 1092 1093 dbus_entry = (SDL_TrayEntryDBus *)entry->internal; 1094 if (dbus_entry->item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) { 1095 SDL_Tray *tray; 1096 SDL_TrayDriverDBus *driver; 1097 SDL_TrayDBus *tray_dbus; 1098 SDL_TrayMenuDBus *main_menu_dbus; 1099 1100 tray = entry->parent->parent_tray; 1101 tray_dbus = (SDL_TrayDBus *)tray->internal; 1102 driver = (SDL_TrayDriverDBus *)tray->driver->internal; 1103 main_menu_dbus = (SDL_TrayMenuDBus *)tray->menu->internal; 1104 1105 dbus_entry->item->flags ^= SDL_MENU_ITEM_FLAGS_CHECKED; 1106 SDL_DBus_UpdateMenu(driver->dbus, tray_dbus->connection, main_menu_dbus->menu, main_menu_dbus->menu_path, TrayNewMenuOnMenuUpdateCallback, tray, SDL_DBUS_UPDATE_MENU_FLAGS_NONE); 1107 } 1108 entry_cb = dbus_entry->item->udata2; 1109 entry_cb(dbus_entry->item->cb_data, dbus_entry->item->udata); 1110} 1111 1112SDL_TrayDriver *SDL_Tray_CreateDBusDriver(void) 1113{ 1114 SDL_TrayDriverDBus *dbus_driver; 1115 SDL_TrayDriver *driver; 1116 SDL_DBusContext *ctx; 1117 DBusMessage *saved; 1118 char **paths; 1119 int count; 1120 int i; 1121 bool sni_supported; 1122 1123 /* Init DBus and get context */ 1124 SDL_DBus_Init(); 1125 ctx = SDL_DBus_GetContext(); 1126 if (!ctx) { 1127 return NULL; 1128 } 1129 1130 /* SNI support detection */ 1131 sni_supported = false; 1132 paths = NULL; 1133 count = 0; 1134 1135 if (SDL_DBus_CallMethod(&saved, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames", DBUS_TYPE_INVALID, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, &count, DBUS_TYPE_INVALID)) { 1136 SDL_DBus_FreeReply(&saved); 1137 1138 if (paths) { 1139 bool watcher_found; 1140 1141 watcher_found = false; 1142 for (i = 0; i < count; i++) { 1143 if (!SDL_strcmp(paths[i], SNI_WATCHER_SERVICE)) { 1144 watcher_found = true; 1145 } 1146 } 1147 ctx->free_string_array(paths); 1148 1149 if (watcher_found) { 1150 dbus_bool_t host_registered; 1151 1152 host_registered = FALSE; 1153 SDL_DBus_QueryProperty(&saved, SNI_WATCHER_SERVICE, SNI_WATCHER_PATH, SNI_WATCHER_INTERFACE, "IsStatusNotifierHostRegistered", DBUS_TYPE_BOOLEAN, &host_registered); 1154 SDL_DBus_FreeReply(&saved); 1155 if (host_registered) { 1156 sni_supported = true; 1157 } 1158 } 1159 } 1160 } 1161 1162 if (!sni_supported) { 1163 SDL_SetError("Unable to create tray: no SNI support!"); 1164 SDL_DBus_Quit(); 1165 return NULL; 1166 } 1167 1168 /* Allocate the driver struct */ 1169 dbus_driver = SDL_malloc(sizeof(SDL_TrayDriverDBus)); 1170 if (!dbus_driver) { 1171 return NULL; 1172 } 1173 driver = SDL_malloc(sizeof(SDL_TrayDriver)); 1174 if (!driver) { 1175 SDL_free(dbus_driver); 1176 return NULL; 1177 } 1178 1179 /* Populate */ 1180 dbus_driver->dbus = ctx; 1181 driver->internal = dbus_driver; 1182 driver->name = "dbus"; 1183 driver->count = 0; 1184 driver->CreateTray = CreateTray; 1185 driver->DestroyTray = DestroyTray; 1186 driver->UpdateTray = UpdateTray; 1187 driver->SetTrayIcon = SetTrayIcon; 1188 driver->SetTrayTooltip = SetTrayTooltip; 1189 driver->CreateTrayMenu = CreateTrayMenu; 1190 driver->InsertTrayEntryAt = InsertTrayEntryAt; 1191 driver->CreateTraySubmenu = CreateTraySubmenu; 1192 driver->GetTraySubmenu = GetTraySubmenu; 1193 driver->GetTrayEntries = GetTrayEntries; 1194 driver->RemoveTrayEntry = RemoveTrayEntry; 1195 driver->SetTrayEntryCallback = SetTrayEntryCallback; 1196 driver->SetTrayEntryLabel = SetTrayEntryLabel; 1197 driver->GetTrayEntryLabel = GetTrayEntryLabel; 1198 driver->SetTrayEntryChecked = SetTrayEntryChecked; 1199 driver->GetTrayEntryChecked = GetTrayEntryChecked; 1200 driver->SetTrayEntryEnabled = SetTrayEntryEnabled; 1201 driver->GetTrayEntryEnabled = GetTrayEntryEnabled; 1202 driver->ClickTrayEntry = ClickTrayEntry; 1203 driver->DestroyDriver = DestroyDriver; 1204 1205 return driver; 1206} 1207 1208#endif // SDL_USE_LIBDBUS 1209[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.