From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
Apparently, StatusNotifierWatcher doesn't allow manual unregister method, so it's impossible to properly utilize multiple SNI objects within single DBUS connection. xembed-sni-proxy utilizes the same behaviour. --- dlls/winesni.drv/dbus.c | 277 ++++++++++++++++++++++------------------ 1 file changed, 153 insertions(+), 124 deletions(-)
diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c index 4432bcde848..d3df503e8e8 100644 --- a/dlls/winesni.drv/dbus.c +++ b/dlls/winesni.drv/dbus.c @@ -42,6 +42,7 @@
#include <dbus/dbus.h>
+#include "wine/list.h" #include "wine/gdi_driver.h" #include "wine/debug.h"
@@ -68,6 +69,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(winesni); DO_FUNC(dbus_connection_unregister_object_path); \ DO_FUNC(dbus_connection_list_registered); \ DO_FUNC(dbus_connection_close); \ + DO_FUNC(dbus_connection_ref); \ DO_FUNC(dbus_connection_unref); \ DO_FUNC(dbus_connection_get_object_path_data); \ DO_FUNC(dbus_connection_get_unix_fd); \ @@ -113,6 +115,7 @@ DBUS_FUNCS; /* an individual systray icon */ struct tray_icon { + struct list entry; HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */ HICON hIcon; void* icon_bitmap; @@ -123,9 +126,12 @@ struct tray_icon UINT callback_message; char tiptext[128 * 3]; /* tooltip text */ UINT version; /* notify icon api version */ + DBusConnection* connection; pthread_mutex_t mutex; /* mutex */ };
+static struct list sni_list = LIST_INIT( sni_list ); +static pthread_mutex_t list_mutex = PTHREAD_MUTEX_INITIALIZER;
#define BALLOON_SHOW_MIN_TIMEOUT 10000 #define BALLOON_SHOW_MAX_TIMEOUT 30000 @@ -139,7 +145,7 @@ static const char* kde_item_interface_name = "org.kde.StatusNotifierItem"; static const char* freedesktop_item_interface_name = "org.freedesktop.StatusNotifierItem"; static const char* item_interface_name = "org.kde.StatusNotifierItem";
-static DBusConnection *connection; +static DBusConnection *global_connection; static char* status_notifier_dst_path = NULL;
static const char *status_field = "Status"; @@ -169,45 +175,18 @@ static const char* dbus_name_owning_match = "type='signal'," "interface='org.freedesktop.DBus'," "sender='org.freedesktop.DBus'," "member='NameOwnerChanged'"; -static const char* const tray_path_prefix = "/org/wine/tray"; -static const char* const tray_path_format = "/org/wine/tray/%zu/%d"; -static const char* const tray_path_fragment_format = "/org/wine/tray/%zu"; +static const char* const object_path = "/StatusNotifierItem";
-static BOOL register_notification_item(const char* object_name); +static BOOL register_notification_item(DBusConnection* ctx);
-static void register_items(DBusConnection *ctx) +static void restore_items(DBusConnection *ctx) { - char** child_entries = NULL; - char** child_entry_ptr = NULL; + struct tray_icon *icon;
- if (!p_dbus_connection_list_registered(connection, tray_path_prefix, &child_entries) || child_entries == NULL) { - goto fail; - } - - child_entry_ptr = child_entries; - while (*child_entry_ptr != NULL) { - char object_window_name[64]; - char object_name[128]; - char** object_child_entries = NULL; - char** object_child_entry_ptr = NULL; - - snprintf(object_window_name, sizeof(object_window_name), "%s/%s", tray_path_prefix,*child_entry_ptr); - - if (!p_dbus_connection_list_registered(connection, object_window_name, &object_child_entries) || object_child_entries == NULL) { - goto fail; - } - object_child_entry_ptr = object_child_entries; - while (*object_child_entry_ptr != NULL) { - snprintf(object_name, sizeof(object_name), "%s/%s", object_window_name, *object_child_entry_ptr); - register_notification_item(object_name); - object_child_entry_ptr++; - } - - p_dbus_free_string_array(object_child_entries); - child_entry_ptr++; - } -fail: - p_dbus_free_string_array(child_entries); + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY( icon, &sni_list, struct tray_icon, entry ) + register_notification_item(icon->connection); + pthread_mutex_unlock(&list_mutex); }
static DBusHandlerResult name_owner_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data ) @@ -247,7 +226,7 @@ static DBusHandlerResult name_owner_filter( DBusConnection *ctx, DBusMessage *ms status_notifier_dst_path = strdup(new_path); free(old_path); if (status_notifier_dst_path[0] != '\0') { - register_items(ctx); + restore_items(ctx); } } } @@ -256,7 +235,7 @@ cleanup: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
-static BOOL get_owner_for_interface(const char* interface_name, char** owner_path) +static BOOL get_owner_for_interface(DBusConnection* connection, const char* interface_name, char** owner_path) { DBusMessage* msg = NULL; DBusMessageIter args; @@ -312,9 +291,9 @@ err: return FALSE; }
-BOOL get_notifier_watcher_owner_for_interface(const char* interface_name, const char* sni_interface_name) +BOOL get_notifier_watcher_owner_for_interface(DBusConnection* connection, const char* interface_name, const char* sni_interface_name) { - if (!get_owner_for_interface(interface_name, &status_notifier_dst_path)) { + if (!get_owner_for_interface(connection, interface_name, &status_notifier_dst_path)) { return FALSE; } TRACE("found notifier destination name %s\n", status_notifier_dst_path); @@ -330,20 +309,20 @@ BOOL get_notifier_watcher_owner(void) if (!p_dbus_threads_init_default()) { goto err; } - if (!(connection = p_dbus_bus_get_private( DBUS_BUS_SESSION, &error ))) { + if (!(global_connection = p_dbus_bus_get_private( DBUS_BUS_SESSION, &error ))) { WARN("failed to get system dbus connection: %s\n", error.message ); p_dbus_error_free( &error ); goto err; }
- if (!get_notifier_watcher_owner_for_interface(kde_watcher_interface_name, kde_item_interface_name) && - !get_notifier_watcher_owner_for_interface(freedesktop_watcher_interface_name, freedesktop_item_interface_name)) { + if (!get_notifier_watcher_owner_for_interface(global_connection, kde_watcher_interface_name, kde_item_interface_name) && + !get_notifier_watcher_owner_for_interface(global_connection, freedesktop_watcher_interface_name, freedesktop_item_interface_name)) { WARN("failed to query watcher interface owner\n"); goto err; }
- p_dbus_connection_add_filter( connection, name_owner_filter, NULL, NULL ); - p_dbus_bus_add_match( connection, dbus_name_owning_match, &error ); + p_dbus_connection_add_filter( global_connection, name_owner_filter, NULL, NULL ); + p_dbus_bus_add_match( global_connection, dbus_name_owning_match, &error ); if (p_dbus_error_is_set(&error)) { WARN("failed to register matcher %s: %s\n", error.name, error.message); p_dbus_error_free( &error); @@ -386,7 +365,7 @@ fail: return FALSE; }
-static BOOL send_notification(const WCHAR* title, const WCHAR* text, HICON icon, UINT info_flags, UINT timeout) { +static BOOL send_notification(DBusConnection* connection, const WCHAR* title, const WCHAR* text, HICON icon, UINT info_flags, UINT timeout) { char info_text[256 * 3]; char info_title[128 * 3]; const char *info_text_ptr = info_text, *info_title_ptr = info_title; @@ -405,7 +384,7 @@ static BOOL send_notification(const WCHAR* title, const WCHAR* text, HICON icon, HICON new_icon = NULL; int expire_timeout; //TODO: startup the service - if (!get_owner_for_interface("org.freedesktop.Notifications", ¬ifications_dst_path)) { + if (!get_owner_for_interface(connection, "org.freedesktop.Notifications", ¬ifications_dst_path)) { goto err; } info_title[0] = 0; @@ -555,9 +534,9 @@ err: }
void dbus_finalize() { - if (connection != NULL) { - p_dbus_connection_close(connection); - p_dbus_connection_unref(connection); + if (global_connection != NULL) { + p_dbus_connection_close(global_connection); + p_dbus_connection_unref(global_connection); } if (dbus_module != NULL) { dlclose(dbus_module); @@ -975,58 +954,97 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage return DBUS_HANDLER_RESULT_HANDLED; }
-void unregister_notification_handler(DBusConnection* conn, void* user_data) -{ - struct tray_icon* icon = (struct tray_icon*)user_data; - - if (icon->hIcon) NtUserDestroyCursor(icon->hIcon, 0); - if (icon->icon_bitmap) free(icon->icon_bitmap); - if (icon) pthread_mutex_destroy(&icon->mutex); - - free(icon); -} - const DBusObjectPathVTable notification_vtable = { .message_function = notification_message_handler, - .unregister_function = unregister_notification_handler };
void run_dbus_loop(void) { int dbus_conn_fd; - struct pollfd fd_info; - p_dbus_connection_get_unix_fd(connection, &dbus_conn_fd); - - fd_info = (struct pollfd) { + DBusConnection* conns[128]; + struct pollfd fd_info[128]; + int fd_count; + struct pollfd* fd_ptr = fd_info; + p_dbus_connection_get_unix_fd(global_connection, &dbus_conn_fd); + + conns[0] = global_connection; + fd_info[0] = (struct pollfd) { .fd = dbus_conn_fd, .events = POLLIN, }; while (true) { + int i, poll_ret; + struct tray_icon* icon; + fd_count = 1; + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY( icon, &sni_list, struct tray_icon, entry ) { + int sni_dbus_conn_fd; + if (fd_count >= 128) { + break; + } + + p_dbus_connection_get_unix_fd(icon->connection, &sni_dbus_conn_fd); + conns[fd_count] = p_dbus_connection_ref(icon->connection); + fd_info[fd_count++] = (struct pollfd) { + .fd = sni_dbus_conn_fd, + .events = POLLIN, + .revents = 0, + }; + } + pthread_mutex_unlock(&list_mutex); /* TODO: utilize DBusWatch */ - poll(&fd_info, 1, -1); - /* non blocking read of the next available message */ - if (!p_dbus_connection_read_write(connection, 0)) { - break; + poll_ret = poll(fd_ptr, fd_count, 100); + if (poll_ret == 0) { + continue; } - while ( p_dbus_connection_get_dispatch_status ( connection ) == DBUS_DISPATCH_DATA_REMAINS ) - { - p_dbus_connection_dispatch ( connection ) ; + if (poll_ret == -1) { + ERR("fd poll error\n"); + continue; + } + for ( i = 0; i < fd_count; i++ ) { + if ((fd_info[i].revents & POLLIN) && !(fd_info[i].revents & (POLLERR | POLLHUP | POLLNVAL))) { + /* non blocking read of the next available message */ + if (!p_dbus_connection_read_write(conns[i], 0)) { + break; + } + while ( p_dbus_connection_get_dispatch_status ( conns[i] ) == DBUS_DISPATCH_DATA_REMAINS ) + { + p_dbus_connection_dispatch ( conns[i] ) ; + } + } + if (i > 0) p_dbus_connection_unref(conns[i]); } } }
-static BOOL register_notification_item(const char* object_name) +static struct tray_icon *get_icon(HWND owner, UINT id) +{ + struct tray_icon *this; + + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY( this, &sni_list, struct tray_icon, entry ) { + if ((this->id == id) && (this->owner == owner)) { + pthread_mutex_unlock(&list_mutex); + return this; + } + } + pthread_mutex_unlock(&list_mutex); + return NULL; +} + +static BOOL register_notification_item(DBusConnection* connection) { DBusMessageIter args; DBusPendingCall* pending; DBusMessage* msg = NULL; DBusError error; + const char* object_name = object_path; char service_object_name[256]; if (strcmp(watcher_interface_name, freedesktop_watcher_interface_name) == 0) { /* prepend source unique name */ - snprintf(service_object_name, sizeof(service_object_name), "%s%s", p_dbus_bus_get_unique_name(connection), object_name); + snprintf(service_object_name, sizeof(service_object_name), "%s%s", p_dbus_bus_get_unique_name(connection), object_path); object_name = service_object_name; } p_dbus_error_init( &error ); @@ -1074,7 +1092,7 @@ err: return FALSE; }
-static BOOL send_signal_to_item(const char* object_path, const char* signal_name) +static BOOL send_signal_to_item(DBusConnection* connection, const char* signal_name) { DBusMessage* msg; DBusError error; @@ -1124,12 +1142,17 @@ fail:
BOOL add_icon(const NOTIFYICONDATAW* icon_data) { - char object_name[64]; + DBusConnection* connection = NULL; struct tray_icon* icon = NULL; DBusError error; bool registered = false; p_dbus_error_init( &error ); - snprintf(object_name, sizeof(object_name), tray_path_format, (size_t)icon_data->hWnd, icon_data->uID); + + if (!(connection = p_dbus_bus_get_private( DBUS_BUS_SESSION, &error ))) { + WARN("failed to get system dbus connection: %s\n", error.message ); + p_dbus_error_free( &error ); + goto fail; + } icon = malloc(sizeof(struct tray_icon)); if (!icon) { goto fail; @@ -1138,12 +1161,15 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) memset(icon, 0, sizeof(*icon)); icon->id = icon_data->uID; icon->owner = icon_data->hWnd; + icon->connection = connection; if (pthread_mutex_init(&icon->mutex, NULL)) { + WARN("failed to initialize mutex\n" ); goto fail; } if (icon_data->uFlags & NIF_ICON) { if (!get_icon_data(icon_data, icon)) { + WARN("failed to get icon info\n" ); goto fail; } } @@ -1160,23 +1186,23 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) icon->state = (icon->state & ~icon_data->dwStateMask) | (icon_data->dwState & icon_data->dwStateMask); } if (icon_data->uFlags & NIF_INFO && icon_data->cbSize >= NOTIFYICONDATAA_V2_SIZE) { - send_notification(icon_data->szInfoTitle, icon_data->szInfo, icon_data->hBalloonIcon, icon_data->dwInfoFlags, icon_data->uTimeout); + send_notification(icon->connection, icon_data->szInfoTitle, icon_data->szInfo, icon_data->hBalloonIcon, icon_data->dwInfoFlags, icon_data->uTimeout); } icon->version = icon_data->uVersion; - if (!p_dbus_connection_try_register_object_path(connection, object_name, ¬ification_vtable, icon, &error)) { + if (!p_dbus_connection_try_register_object_path(connection, object_path, ¬ification_vtable, icon, &error)) { WARN("failed register object %s: %s\n", error.name, error.message); p_dbus_error_free( &error ); goto fail; } registered = true; - /* don't register, if there is no SNWatcher available, it might be reinitializing */ if (status_notifier_dst_path != NULL && status_notifier_dst_path[0] != '\0' && - !register_notification_item(object_name)) { - WARN("failed to register item %s\n", object_name); - p_dbus_connection_unregister_object_path(connection, object_name); + !register_notification_item(connection)) { + WARN("failed to register item\n"); + p_dbus_connection_unregister_object_path(connection, object_path); goto fail; } + list_add_tail(&sni_list, &icon->entry); return TRUE; fail: if (icon && icon->hIcon) NtUserDestroyCursor(icon->hIcon, 0); @@ -1184,39 +1210,53 @@ fail: if (icon) pthread_mutex_destroy(&icon->mutex); free(icon); if (registered) { - p_dbus_connection_unregister_object_path(connection, object_name); + p_dbus_connection_unregister_object_path(connection, object_path); + } + if (connection) { + p_dbus_connection_close(connection); } return FALSE; }
-BOOL delete_icon(const NOTIFYICONDATAW* icon_data) +BOOL cleanup_icon(struct tray_icon* icon) { - DBusError error; - char object_name[64]; - p_dbus_error_init( &error ); - snprintf(object_name, sizeof(object_name), tray_path_format, (size_t)icon_data->hWnd, icon_data->uID); + pthread_mutex_lock(&icon->mutex); + p_dbus_connection_close(icon->connection); + p_dbus_connection_unref(icon->connection); + pthread_mutex_unlock(&icon->mutex); + if (icon->hIcon) NtUserDestroyCursor(icon->hIcon, 0); + if (icon->icon_bitmap) free(icon->icon_bitmap); + if (icon) pthread_mutex_destroy(&icon->mutex);
- if (!p_dbus_connection_unregister_object_path(connection, object_name)) { - WARN("failed register object %s: %s\n", error.name, error.message); - p_dbus_error_free( &error ); - return FALSE; - } + free(icon); return TRUE; }
+BOOL delete_icon(const NOTIFYICONDATAW* icon_data) +{ + struct tray_icon *icon = NULL, *this, *next; + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY_SAFE( this, next, &sni_list, struct tray_icon, entry ) { + if ((this->id == icon_data->uID) && (this->owner == icon_data->hWnd)) { + list_remove(&this->entry); + icon = this; + break; + } + } + pthread_mutex_unlock(&list_mutex); + if (!icon) return FALSE; + return cleanup_icon(icon); +} + BOOL modify_icon(const NOTIFYICONDATAW* icon_data) { - DBusError error; - char object_path[64]; struct tray_icon* icon; const char* pending_signals[4]; UINT signal_count = 0; UINT new_state; UINT i; - p_dbus_error_init( &error ); - snprintf(object_path, sizeof(object_path), tray_path_format, (size_t)icon_data->hWnd, icon_data->uID); - - if (!p_dbus_connection_get_object_path_data(connection, object_path, (void**)&icon)) { + icon = get_icon(icon_data->hWnd, icon_data->uID); + if (!icon) { return FALSE; }
@@ -1251,13 +1291,13 @@ BOOL modify_icon(const NOTIFYICONDATAW* icon_data)
/* send the signals */ for (i = 0; i < signal_count; i++) { - if (!send_signal_to_item(object_path, pending_signals[i])) { + if (!send_signal_to_item(icon->connection, pending_signals[i])) { goto err_post_unlock; } }
if (icon_data->uFlags & NIF_INFO && icon_data->cbSize >= NOTIFYICONDATAA_V2_SIZE) { - send_notification(icon_data->szInfoTitle, icon_data->szInfo, icon_data->hBalloonIcon, icon_data->dwInfoFlags, icon_data->uTimeout); + send_notification(icon->connection, icon_data->szInfoTitle, icon_data->szInfo, icon_data->hBalloonIcon, icon_data->dwInfoFlags, icon_data->uTimeout); } return TRUE; err: @@ -1268,13 +1308,9 @@ err_post_unlock:
BOOL set_icon_version(const NOTIFYICONDATAW* icon_data) { - DBusError error; - char object_path[64]; struct tray_icon* icon; - p_dbus_error_init( &error ); - snprintf(object_path, sizeof(object_path), tray_path_format, (size_t)icon_data->hWnd, icon_data->uID); - - if (!p_dbus_connection_get_object_path_data(connection, object_path, (void**)&icon)) { + icon = get_icon(icon_data->hWnd, icon_data->uID); + if (!icon) { return FALSE; }
@@ -1287,23 +1323,16 @@ BOOL set_icon_version(const NOTIFYICONDATAW* icon_data)
BOOL cleanup_icons(HWND owner) { - char object_prefix[64]; - char** child_entries = NULL; - char** child_entry_ptr = NULL; - snprintf(object_prefix, sizeof(object_prefix), tray_path_fragment_format, (size_t)owner); + struct tray_icon *this, *next;
- if (!p_dbus_connection_list_registered(connection, object_prefix, &child_entries) || child_entries == NULL) { - return FALSE; - } - - child_entry_ptr = child_entries; - while (*child_entry_ptr != NULL) { - char object_name[128]; - snprintf(object_name, sizeof(object_name), "%s/%s", object_prefix, *child_entry_ptr); - p_dbus_connection_unregister_object_path(connection, object_name); - child_entry_ptr++; + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY_SAFE( this, next, &sni_list, struct tray_icon, entry ) { + if (this->owner == owner) { + list_remove(&this->entry); + cleanup_icon(this); + } } + pthread_mutex_unlock(&list_mutex);
- p_dbus_free_string_array(child_entries); return TRUE; }