Adds the tray icons implementation based on org.kde.StatusNotifierItem interface usage. Does allow restarting StatusNotifierWatcher object, but will fallback to XEMBED or internal tray, if wine gets initialized when there is no StatusNotifierWatcher object registered.
-- v24: winesni.drv: add dbus watch instead of using plain unix fds and flush winesni.drv: replaced the dbus connection logic with a single connection per each SNI object winesni.drv: wrap functions with pthread mutex locking winesni.drv: implement basic balloon notification support explorer: add winesni.drv tray implementation support winesni.drv: add KDE StatusNotifierItem implementation for tray
From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
--- configure | 10 +- configure.ac | 9 +- dlls/winesni.drv/Makefile.in | 13 + dlls/winesni.drv/dbus.c | 1093 +++++++++++++++++++++++++++++ dlls/winesni.drv/dllmain.c | 48 ++ dlls/winesni.drv/image.c | 166 +++++ dlls/winesni.drv/snidrv_dll.h | 33 + dlls/winesni.drv/systray.c | 41 ++ dlls/winesni.drv/unixlib.c | 81 +++ dlls/winesni.drv/unixlib.h | 54 ++ dlls/winesni.drv/version.rc | 22 + dlls/winesni.drv/winesni.drv.spec | 2 + 12 files changed, 1566 insertions(+), 6 deletions(-) create mode 100644 dlls/winesni.drv/Makefile.in create mode 100644 dlls/winesni.drv/dbus.c create mode 100644 dlls/winesni.drv/dllmain.c create mode 100644 dlls/winesni.drv/image.c create mode 100644 dlls/winesni.drv/snidrv_dll.h create mode 100644 dlls/winesni.drv/systray.c create mode 100644 dlls/winesni.drv/unixlib.c create mode 100644 dlls/winesni.drv/unixlib.h create mode 100644 dlls/winesni.drv/version.rc create mode 100644 dlls/winesni.drv/winesni.drv.spec
diff --git a/configure b/configure index 93cdaa0a365..f5c2825cf9b 100755 --- a/configure +++ b/configure @@ -1485,6 +1485,7 @@ enable_wineusb_sys enable_winevulkan enable_winewayland_drv enable_winex11_drv +enable_winesni_drv enable_winexinput_sys enable_wing32 enable_winhttp @@ -2445,7 +2446,7 @@ Optional Packages: --without-capi do not use CAPI (ISDN support) --without-coreaudio do not use the CoreAudio sound support --without-cups do not use CUPS - --without-dbus do not use DBus (dynamic device support) + --without-dbus do not use DBus (dynamic device support, SNI tray) --with-float-abi=abi specify the ABI (soft|softfp|hard) for ARM platforms --without-fontconfig do not use fontconfig --without-freetype do not use the FreeType library @@ -16115,9 +16116,11 @@ fi
CPPFLAGS=$ac_save_CPPFLAGS
+else + enable_winesni_drv=no fi case $host_os in - darwin*|macosx*) ;; + darwin*|macosx*) enable_winesni_drv=no ;; *) if test "x$ac_cv_lib_soname_dbus_1" = "x" then : case "x$with_dbus" in @@ -16126,7 +16129,7 @@ then : *) as_fn_error $? "libdbus ${notice_platform}development files not found, no dynamic device support. This is an error since --with-dbus was requested." "$LINENO" 5 ;; esac - +enable_winesni_drv=${enable_winesni_drv:-no} fi ;; esac
@@ -21964,6 +21967,7 @@ wine_fn_config_makefile dlls/wineusb.sys enable_wineusb_sys wine_fn_config_makefile dlls/winevulkan enable_winevulkan wine_fn_config_makefile dlls/winewayland.drv enable_winewayland_drv wine_fn_config_makefile dlls/winex11.drv enable_winex11_drv +wine_fn_config_makefile dlls/winesni.drv enable_winesni_drv wine_fn_config_makefile dlls/winexinput.sys enable_winexinput_sys wine_fn_config_makefile dlls/wing.dll16 enable_win16 wine_fn_config_makefile dlls/wing32 enable_wing32 diff --git a/configure.ac b/configure.ac index dd8520f6f48..1b8cb27857b 100644 --- a/configure.ac +++ b/configure.ac @@ -28,7 +28,7 @@ AC_ARG_WITH(alsa, AS_HELP_STRING([--without-alsa],[do not use the Alsa soun AC_ARG_WITH(capi, AS_HELP_STRING([--without-capi],[do not use CAPI (ISDN support)])) AC_ARG_WITH(coreaudio, AS_HELP_STRING([--without-coreaudio],[do not use the CoreAudio sound support])) AC_ARG_WITH(cups, AS_HELP_STRING([--without-cups],[do not use CUPS])) -AC_ARG_WITH(dbus, AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic device support)])) +AC_ARG_WITH(dbus, AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic device support, SNI tray)])) AC_ARG_WITH(float-abi, AS_HELP_STRING([--with-float-abi=abi],[specify the ABI (soft|softfp|hard) for ARM platforms])) AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig])) AC_ARG_WITH(freetype, AS_HELP_STRING([--without-freetype],[do not use the FreeType library])) @@ -1405,11 +1405,13 @@ then [AC_CHECK_HEADER([dbus/dbus.h], [WINE_CHECK_SONAME(dbus-1, dbus_connection_close,,[DBUS_CFLAGS=""],[$DBUS_LIBS])], [DBUS_CFLAGS=""])]) +else + enable_winesni_drv=no fi case $host_os in - darwin*|macosx*) ;; + darwin*|macosx*) enable_winesni_drv=no ;; *) WINE_NOTICE_WITH(dbus,[test "x$ac_cv_lib_soname_dbus_1" = "x"], - [libdbus ${notice_platform}development files not found, no dynamic device support.]) ;; + [libdbus ${notice_platform}development files not found, no dynamic device support.], [enable_winesni_drv]) ;; esac
dnl **** Check for libgnutls **** @@ -3173,6 +3175,7 @@ WINE_CONFIG_MAKEFILE(dlls/wineusb.sys) WINE_CONFIG_MAKEFILE(dlls/winevulkan) WINE_CONFIG_MAKEFILE(dlls/winewayland.drv) WINE_CONFIG_MAKEFILE(dlls/winex11.drv) +WINE_CONFIG_MAKEFILE(dlls/winesni.drv) WINE_CONFIG_MAKEFILE(dlls/winexinput.sys) WINE_CONFIG_MAKEFILE(dlls/wing.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/wing32) diff --git a/dlls/winesni.drv/Makefile.in b/dlls/winesni.drv/Makefile.in new file mode 100644 index 00000000000..ed73bf9092b --- /dev/null +++ b/dlls/winesni.drv/Makefile.in @@ -0,0 +1,13 @@ +MODULE = winesni.drv +UNIXLIB = winesni.so +IMPORTS = uuid win32u +UNIX_CFLAGS = $(DBUS_CFLAGS) $(HAL_CFLAGS) +UNIX_LIBS = -lwin32u + +SOURCES = \ + dllmain.c \ + unixlib.c \ + dbus.c \ + systray.c \ + image.c \ + version.rc \ diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c new file mode 100644 index 00000000000..075ae5a43c5 --- /dev/null +++ b/dlls/winesni.drv/dbus.c @@ -0,0 +1,1093 @@ +/* + * DBus tray support + * + * Copyright 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include "unixlib.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +#include <pthread.h> +#include "shellapi.h" +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <dlfcn.h> + +#include <poll.h> + +#include <dbus/dbus.h> + +#include "wine/gdi_driver.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winesni); + +#define DBUS_FUNCS \ + DO_FUNC(dbus_bus_add_match); \ + DO_FUNC(dbus_bus_get); \ + DO_FUNC(dbus_bus_get_private); \ + DO_FUNC(dbus_bus_add_match); \ + DO_FUNC(dbus_bus_remove_match); \ + DO_FUNC(dbus_bus_get_unique_name); \ + DO_FUNC(dbus_connection_add_filter); \ + DO_FUNC(dbus_connection_read_write); \ + DO_FUNC(dbus_connection_dispatch); \ + DO_FUNC(dbus_connection_get_dispatch_status); \ + DO_FUNC(dbus_connection_read_write_dispatch); \ + DO_FUNC(dbus_connection_remove_filter); \ + DO_FUNC(dbus_connection_send); \ + DO_FUNC(dbus_connection_send_with_reply); \ + DO_FUNC(dbus_connection_send_with_reply_and_block); \ + DO_FUNC(dbus_connection_flush); \ + DO_FUNC(dbus_connection_try_register_object_path); \ + DO_FUNC(dbus_connection_unregister_object_path); \ + DO_FUNC(dbus_connection_list_registered); \ + DO_FUNC(dbus_connection_close); \ + DO_FUNC(dbus_connection_unref); \ + DO_FUNC(dbus_connection_get_object_path_data); \ + DO_FUNC(dbus_connection_get_unix_fd); \ + DO_FUNC(dbus_error_free); \ + DO_FUNC(dbus_error_init); \ + DO_FUNC(dbus_error_is_set); \ + DO_FUNC(dbus_set_error_from_message); \ + DO_FUNC(dbus_free_string_array); \ + DO_FUNC(dbus_message_get_args); \ + DO_FUNC(dbus_message_get_interface); \ + DO_FUNC(dbus_message_get_member); \ + DO_FUNC(dbus_message_get_path); \ + DO_FUNC(dbus_message_get_type); \ + DO_FUNC(dbus_message_is_signal); \ + DO_FUNC(dbus_message_iter_append_basic); \ + DO_FUNC(dbus_message_iter_get_arg_type); \ + DO_FUNC(dbus_message_iter_get_basic); \ + DO_FUNC(dbus_message_iter_append_fixed_array); \ + DO_FUNC(dbus_message_iter_get_fixed_array); \ + DO_FUNC(dbus_message_iter_init); \ + DO_FUNC(dbus_message_iter_init_append); \ + DO_FUNC(dbus_message_iter_next); \ + DO_FUNC(dbus_message_iter_recurse); \ + DO_FUNC(dbus_message_iter_open_container); \ + DO_FUNC(dbus_message_iter_close_container); \ + DO_FUNC(dbus_message_iter_abandon_container_if_open); \ + DO_FUNC(dbus_message_new_method_return); \ + DO_FUNC(dbus_message_new_method_call); \ + DO_FUNC(dbus_message_new_signal); \ + DO_FUNC(dbus_message_is_method_call); \ + DO_FUNC(dbus_message_new_error); \ + DO_FUNC(dbus_pending_call_block); \ + DO_FUNC(dbus_pending_call_unref); \ + DO_FUNC(dbus_pending_call_steal_reply); \ + DO_FUNC(dbus_threads_init_default); \ + DO_FUNC(dbus_message_unref) + +#define DO_FUNC(f) static typeof(f) * p_##f +DBUS_FUNCS; +#undef DO_FUNC + + +/* an individual systray icon */ +struct tray_icon +{ + HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */ + HICON hIcon; + void* icon_bitmap; + UINT icon_width; + UINT icon_height; + UINT state; /* state flags */ + UINT id; /* the unique id given by the app */ + UINT callback_message; + char tiptext[128 * 3]; /* tooltip text */ + UINT version; /* notify icon api version */ +}; + +static void* dbus_module = NULL; +static const char* kde_watcher_interface_name = "org.kde.StatusNotifierWatcher"; +static const char* freedesktop_watcher_interface_name = "org.freedesktop.StatusNotifierWatcher"; +static const char* watcher_interface_name = "org.kde.StatusNotifierWatcher"; + +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 char* status_notifier_dst_path = NULL; + +static const char *status_field = "Status"; +static const char *icon_field = "IconPixmap"; +static const char *icon_name_field = "IconName"; +static const char *title_field = "Title"; +static const char *category_field = "Category"; +static const char *id_field = "Id"; +/*TODO: sync it*/ +/*static pthread_mutex_t status_notifier_item_mutex = PTHREAD_MUTEX_INITIALIZER;*/ + +BOOL load_dbus_functions(void) +{ + if (!(dbus_module = dlopen( SONAME_LIBDBUS_1, RTLD_NOW ))) + goto failed; + +#define DO_FUNC(f) if (!(p_##f = dlsym( dbus_module, #f ))) goto failed + DBUS_FUNCS; +#undef DO_FUNC + return TRUE; + +failed: + WARN( "failed to load DBUS support: %s\n", dlerror() ); + return FALSE; +} + + +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 BOOL register_notification_item(const char* object_name); + +static void register_items(DBusConnection *ctx) +{ + char** child_entries = NULL; + char** child_entry_ptr = NULL; + + 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); +} + +static DBusHandlerResult name_owner_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data ) +{ + char *interface_name, *old_path, *new_path; + DBusError error; + + p_dbus_error_init( &error ); + + if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus", "NameOwnerChanged" ) && + p_dbus_message_get_args( msg, &error, DBUS_TYPE_STRING, &interface_name, DBUS_TYPE_STRING, &old_path, + DBUS_TYPE_STRING, &new_path, DBUS_TYPE_INVALID )) + { + /* check if watcher is disabled first*/ + if (status_notifier_dst_path != NULL && + status_notifier_dst_path[0] != '\0' && + strcmp(interface_name, watcher_interface_name) == 0) + { + old_path = status_notifier_dst_path; + status_notifier_dst_path = strdup(new_path); + free(old_path); + } + else if (status_notifier_dst_path == NULL || + status_notifier_dst_path[0] == '\0') + { + bool is_kde_interface = strcmp(interface_name, kde_watcher_interface_name) == 0; + bool is_freedesktop_interface = strcmp(interface_name, freedesktop_watcher_interface_name) == 0; + if (!is_kde_interface && !is_freedesktop_interface) goto cleanup; + + if (strcmp(interface_name, kde_watcher_interface_name) == 0) + { + watcher_interface_name = kde_watcher_interface_name; + item_interface_name = kde_item_interface_name; + } + else if (strcmp(interface_name, freedesktop_watcher_interface_name) == 0) + { + watcher_interface_name = freedesktop_watcher_interface_name; + item_interface_name = freedesktop_item_interface_name; + } + /* TODO: lock the mutex */ + old_path = status_notifier_dst_path; + status_notifier_dst_path = strdup(new_path); + free(old_path); + if (status_notifier_dst_path[0] != '\0') register_items(ctx); + } + } +cleanup: + p_dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +BOOL get_notifier_watcher_owner_for_interface(const char* interface_name, const char* sni_interface_name) +{ + DBusMessage* msg = NULL; + DBusMessageIter args; + DBusPendingCall* pending; + DBusError error; + char* status_notifier_dest = NULL; + p_dbus_error_init( &error ); + msg = p_dbus_message_new_method_call("org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetNameOwner"); + if (!msg) goto err; + + p_dbus_message_iter_init_append(msg, &args); + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &interface_name )) goto err; + if (!p_dbus_connection_send_with_reply (connection, msg, &pending, -1)) goto err; + if (!pending) goto err; + + p_dbus_connection_flush(connection); + + p_dbus_message_unref(msg); + + p_dbus_pending_call_block(pending); + + msg = p_dbus_pending_call_steal_reply(pending); + p_dbus_pending_call_unref(pending); + if (!msg) goto err; + + if (p_dbus_set_error_from_message (&error, msg)) + { + WARN("failed to query an owner - %s: %s\n", error.name, error.message); + p_dbus_error_free( &error); + goto err; + } + else if (!p_dbus_message_get_args( msg, &error, DBUS_TYPE_STRING, &status_notifier_dest, + DBUS_TYPE_INVALID )) + { + WARN("failed to get a response - %s: %s\n", error.name, error.message); + p_dbus_error_free( &error ); + goto err; + } + else + { + TRACE("found notifier destination name %s\n", status_notifier_dest); + watcher_interface_name = interface_name; + item_interface_name = sni_interface_name; + status_notifier_dst_path = strdup(status_notifier_dest); + } + p_dbus_message_unref(msg); + return TRUE; +err: + p_dbus_message_unref(msg); + return FALSE; +} + +BOOL get_notifier_watcher_owner(void) +{ + DBusError error; + p_dbus_error_init( &error ); + if (!p_dbus_threads_init_default()) goto err; + + 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 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)) + { + 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 ); + if (p_dbus_error_is_set(&error)) + { + WARN("failed to register matcher %s: %s\n", error.name, error.message); + p_dbus_error_free( &error); + goto err; + } + return TRUE; + +err: + return FALSE; +} + +void dbus_finalize() +{ + if (connection != NULL) + { + p_dbus_connection_close(connection); + p_dbus_connection_unref(connection); + } + if (dbus_module != NULL) + { + dlclose(dbus_module); + } +} + +static BOOL handle_id(DBusConnection* conn, DBusMessageIter *iter, const struct tray_icon* icon) +{ + char id[64]; + const char* id_ptr = id; + snprintf(id, 64, "wine_tray_%d", icon->id); + p_dbus_message_iter_append_basic(iter, 's', &id_ptr); + return true; +} + +static bool handle_icon_name(DBusConnection* conn, DBusMessageIter *iter) +{ + const char* icon_name = "wine_tray_icon"; + return p_dbus_message_iter_append_basic(iter, 's', &icon_name); +} + +static BOOL handle_icon(DBusConnection* conn, DBusMessageIter *iter, const struct tray_icon* icon) +{ + DBusMessageIter aIter, sIter,bIter; + if (!p_dbus_message_iter_open_container(iter, 'a', "(iiay)", &aIter)) + { + WARN("Failed to open array!\n"); + goto fail; + } + + if (!p_dbus_message_iter_open_container(&aIter, 'r', NULL, &sIter)) + { + WARN("Failed to open struct inside array!\n"); + p_dbus_message_iter_abandon_container_if_open(iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&sIter, 'i', &icon->icon_width); + p_dbus_message_iter_append_basic(&sIter, 'i', &icon->icon_height); + + if (p_dbus_message_iter_open_container(&sIter, 'a', DBUS_TYPE_BYTE_AS_STRING, &bIter)) + { + p_dbus_message_iter_append_fixed_array(&bIter, DBUS_TYPE_BYTE, &icon->icon_bitmap, icon->icon_width * icon->icon_height * 4); + p_dbus_message_iter_close_container(&sIter, &bIter); + } + else + goto fail; + + p_dbus_message_iter_close_container(&aIter, &sIter); + p_dbus_message_iter_close_container(iter, &aIter); + return TRUE; +fail: + return FALSE; +} + +static void handle_title(DBusConnection* conn, DBusMessageIter *iter, const struct tray_icon* icon) +{ + const char* tiptext = (const char*)icon->tiptext; + p_dbus_message_iter_append_basic(iter, 's', &tiptext); +} + +static void handle_category(DBusConnection* conn, DBusMessageIter *iter) +{ + const char *cat = "ApplicationStatus"; + p_dbus_message_iter_append_basic(iter, 's', &cat); +} + +static void handle_status(DBusConnection* conn, DBusMessageIter *iter, const struct tray_icon* icon) +{ + const char *status = (icon->state & NIS_HIDDEN) ? "Passive" : "Active"; + p_dbus_message_iter_append_basic(iter, 's', &status); +} + +static BOOL notify_owner( const struct tray_icon *icon, UINT msg, unsigned short x, unsigned short y) +{ + WPARAM wp = icon->id; + LPARAM lp = msg; + + if (icon->version >= NOTIFYICON_VERSION_4) + { + POINT pt = { x, y }; + + wp = MAKEWPARAM( pt.x, pt.y ); + lp = MAKELPARAM( msg, icon->id ); + } + + TRACE( "relaying 0x%x\n", msg ); + if (!NtUserMessageCall( icon->owner, icon->callback_message, wp, lp, 0, NtUserSendNotifyMessage, FALSE )) + { + WARN( "application window was destroyed, removing icon %u\n", icon->id ); + /* TODO: delete the icon */ + return FALSE; + } + return TRUE; +} + +static DBusHandlerResult notification_send_error(DBusConnection *conn, DBusMessage *message, const char* error, const char* message_text) +{ + DBusMessage* reply; + unsigned serial = 0; + reply = p_dbus_message_new_error (message, error, message_text); + if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (!p_dbus_connection_send(conn, reply, &serial)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + p_dbus_connection_flush(conn); + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult get_all_tray_properties(DBusConnection* conn, DBusMessage *message, const struct tray_icon* icon) +{ + DBusMessage* reply; + DBusMessageIter iter, aIter, vIter; + DBusMessageIter eIter; + unsigned serial = 0; + reply = p_dbus_message_new_method_return(message); + if (!reply) goto fail; + + p_dbus_message_iter_init_append(reply, &iter); + + if (!p_dbus_message_iter_open_container(&iter, 'a', "{sv}", &aIter)) goto fail; + + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &id_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "s", &vIter)) + { + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_id(conn, &vIter, icon); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &category_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "s", &vIter)) + { + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_category(conn, &vIter); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + /* Title */ + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &title_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "s", &vIter)) + { + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_title(conn, &vIter, icon); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + /* status */ + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &status_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "s", &vIter)) + { + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_status(conn, &vIter, icon); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + + if (icon->icon_bitmap != NULL) + { + /* Icon */ + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &icon_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "a(iiay)", &vIter)) + { + WARN("failed to create iconpixmap array\n"); + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_icon(conn, &vIter, icon); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + } + + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &icon_name_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "s", &vIter)) + { + WARN("failed to create icon name value\n"); + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&iter, &aIter); + goto fail; + } + + handle_icon_name(conn, &vIter); + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + p_dbus_message_iter_close_container(&iter, &aIter); + + if (!p_dbus_connection_send(conn, reply, &serial)) goto fail; + p_dbus_connection_flush(conn); + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; +fail: + p_dbus_message_unref(reply); + + return notification_send_error(conn, message, "org.freedesktop.DBus.Error.Failed", "got an error while processing properties"); +} + +DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage *message, void *data) +{ + const struct tray_icon* icon = (struct tray_icon*)data; + if (p_dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get")) + { + const char* interface_name = ""; + const char* property_name = ""; + DBusMessage* reply = NULL; + DBusMessageIter iter, vIter; + unsigned serial = 0; + DBusError error; + p_dbus_error_init(&error); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_STRING, &interface_name, + DBUS_TYPE_STRING, &property_name, DBUS_TYPE_INVALID )) + { + DBusHandlerResult ret = notification_send_error (conn, message, error.name, error.message); + p_dbus_error_free( &error); + return ret; + } + + if (strcmp(interface_name, freedesktop_item_interface_name) != 0 && + strcmp(interface_name, kde_item_interface_name) != 0) + { + char error_message[128]; + snprintf(error_message, sizeof(error_message), "unsupported interface %s", interface_name); + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.UnknownProperty", error_message); + } + if (strcmp(property_name, title_field) == 0) + { + reply = p_dbus_message_new_method_return(message); + p_dbus_message_iter_init_append(reply, &iter); + if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + handle_title(conn, &vIter, icon); + p_dbus_message_iter_close_container(&iter, &vIter); + } + else if (strcmp(property_name, id_field) == 0) + { + reply = p_dbus_message_new_method_return(message); + p_dbus_message_iter_init_append(reply, &iter); + if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + handle_id(conn, &vIter, icon); + p_dbus_message_iter_close_container(&iter, &vIter); + } + else if (strcmp(property_name, icon_field) == 0 && icon->hIcon) + { + reply = p_dbus_message_new_method_return(message); + p_dbus_message_iter_init_append(reply, &iter); + if (!p_dbus_message_iter_open_container(&iter, 'v', "a(iiay)", &vIter)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + handle_icon(conn, &vIter, icon); + p_dbus_message_iter_close_container(&iter, &vIter); + } + else if (strcmp(property_name, status_field) == 0) + { + reply = p_dbus_message_new_method_return(message); + p_dbus_message_iter_init_append(reply, &iter); + if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + handle_status(conn, &vIter, icon); + p_dbus_message_iter_close_container(&iter, &vIter); + } + else + { + char error_message[128]; + snprintf(error_message, sizeof(error_message), "interface doesn't have the property %s", property_name); + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.UnknownProperty", error_message); + } + if (!p_dbus_connection_send(conn, reply, &serial)) + { + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + p_dbus_connection_flush(conn); + p_dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (p_dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll")) + { + const char* interface_name = ""; + DBusMessageIter args; + if (!p_dbus_message_iter_init(message, &args)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + else if (DBUS_TYPE_STRING != p_dbus_message_iter_get_arg_type(&args)) + { + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + } + else + p_dbus_message_iter_get_basic(&args, &interface_name); + + if (strcmp(kde_item_interface_name, interface_name) == 0 || + strcmp(freedesktop_item_interface_name, interface_name) == 0) + return get_all_tray_properties(conn, message, icon); + else + return notification_send_error(conn, message, "org.freedesktop.DBus.Error.UnknownInterface", "Call to Get has wrong args"); + + } + else if (p_dbus_message_is_method_call(message, kde_item_interface_name, "ContextMenu") || + p_dbus_message_is_method_call(message, freedesktop_item_interface_name, "ContextMenu")) + { + int x,y; + DBusMessageIter args; + if (!p_dbus_message_iter_init(message, &args)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &x); + + if (!p_dbus_message_iter_next(&args) || DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &y); + + notify_owner( icon, WM_RBUTTONDOWN, (unsigned short) x, (unsigned short) y); + if (icon->version > 0) + notify_owner( icon, WM_CONTEXTMENU, (unsigned short) x, (unsigned short) y); + } + else if (p_dbus_message_is_method_call(message, kde_item_interface_name, "Activate") || + p_dbus_message_is_method_call(message, freedesktop_item_interface_name, "Activate")) + { + int x,y; + DBusMessageIter args; + if (!p_dbus_message_iter_init(message, &args)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &x); + + if (!p_dbus_message_iter_next(&args) || DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &y); + + notify_owner( icon, WM_LBUTTONDOWN, (unsigned short) x, (unsigned short) y); + if (icon->version > 0) notify_owner( icon, NIN_SELECT, (unsigned short) x, (unsigned short) y); + } + else if (p_dbus_message_is_method_call(message, kde_item_interface_name, "SecondaryActivate") || + p_dbus_message_is_method_call(message, freedesktop_item_interface_name, "SecondaryActivate")) + { + int x,y; + DBusMessageIter args; + if (!p_dbus_message_iter_init(message, &args)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &x); + + if (!p_dbus_message_iter_next(&args) || DBUS_TYPE_INT32 != p_dbus_message_iter_get_arg_type(&args)) + return notification_send_error (conn, message, "org.freedesktop.DBus.Error.InvalidArgs", "Call to Get has wrong args"); + else + p_dbus_message_iter_get_basic(&args, &y); + notify_owner( icon, WM_MBUTTONDOWN, (unsigned short) x, (unsigned short) y); + } + else if (p_dbus_message_is_method_call(message, kde_item_interface_name, "Scroll") || + p_dbus_message_is_method_call(message, freedesktop_item_interface_name, "Scroll")) + { + /* do nothing */ + } + else if (p_dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) + return notification_send_error (conn, message, "DBus.Error.UnknownMethod", "Unknown method"); + + 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); + + 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) { + .fd = dbus_conn_fd, + .events = POLLIN, + }; + while (true) + { + /* 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; + + while ( p_dbus_connection_get_dispatch_status ( connection ) == DBUS_DISPATCH_DATA_REMAINS ) + { + p_dbus_connection_dispatch ( connection ) ; + } + } +} + +static BOOL register_notification_item(const char* object_name) +{ + DBusMessageIter args; + DBusPendingCall* pending; + DBusMessage* msg = NULL; + DBusError error; + 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); + object_name = service_object_name; + } + p_dbus_error_init( &error ); + msg = p_dbus_message_new_method_call(status_notifier_dst_path, + "/StatusNotifierWatcher", + watcher_interface_name , + "RegisterStatusNotifierItem"); + if (!msg) goto err; + + p_dbus_message_iter_init_append(msg, &args); + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &object_name )) + goto err; + + if (!p_dbus_connection_send_with_reply (connection, msg, &pending, -1)) + goto err; + if (!pending) goto err; + + p_dbus_connection_flush(connection); + p_dbus_message_unref(msg); + + p_dbus_pending_call_block(pending); + + msg = p_dbus_pending_call_steal_reply(pending); + if (NULL == msg) goto err; + + p_dbus_pending_call_unref(pending); + + if (p_dbus_set_error_from_message (&error, msg)) + { + WARN("got error %s: %s\n", error.name, error.message); + p_dbus_error_free( &error); + goto err; + } + + p_dbus_message_unref(msg); + return TRUE; +err: + if (msg != NULL) p_dbus_message_unref(msg); + return FALSE; +} + +static BOOL send_signal_to_item(const char* object_path, const char* signal_name) +{ + DBusMessage* msg; + DBusError error; + unsigned serial = 0; + p_dbus_error_init( &error ); + msg = p_dbus_message_new_signal(object_path, + item_interface_name, + signal_name); + if (NULL == msg) return FALSE; + + if (!p_dbus_connection_send (connection, msg, &serial)) + { + p_dbus_message_unref(msg); + return FALSE; + } + + p_dbus_connection_flush(connection); + + p_dbus_message_unref(msg); + + return TRUE; +} + + +BOOL get_icon_data(const NOTIFYICONDATAW* icon_data, struct tray_icon* dst) +{ + void* bits = NULL; + unsigned width, height; + HICON new_icon = NULL; + new_icon = CopyImage(icon_data->hIcon, IMAGE_ICON, 0, 0, 0); + if (!create_bitmap_from_icon(new_icon, &width, &height, &bits)) + goto fail; + + if (dst->hIcon) NtUserDestroyCursor(dst->hIcon, 0); + if (dst->icon_bitmap) free(dst->icon_bitmap); + dst->hIcon = new_icon; + dst->icon_bitmap = bits; + dst->icon_width = width; + dst->icon_height = height; + return TRUE; +fail: + NtUserDestroyCursor(new_icon, 0); + free(bits); + return FALSE; +} + +BOOL add_icon(const NOTIFYICONDATAW* icon_data) +{ + char object_name[64]; + 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); + icon = malloc(sizeof(struct tray_icon)); + if (!icon) goto fail; + + memset(icon, 0, sizeof(*icon)); + icon->id = icon_data->uID; + icon->owner = icon_data->hWnd; + if (icon_data->uFlags & NIF_ICON) + { + if (!get_icon_data(icon_data, icon)) goto fail; + } + if (icon_data->uFlags & NIF_MESSAGE) + { + icon->callback_message = icon_data->uCallbackMessage; + } + if (icon_data->uFlags & NIF_TIP) + ntdll_wcstoumbs(icon_data->szTip, wcslen(icon_data->szTip) + 1, icon->tiptext, ARRAY_SIZE(icon->tiptext), FALSE); + if (icon_data->uFlags & NIF_STATE) + icon->state = (icon->state & ~icon_data->dwStateMask) | (icon_data->dwState & icon_data->dwStateMask); + + icon->version = icon_data->uVersion; + if (!p_dbus_connection_try_register_object_path(connection, object_name, ¬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); + goto fail; + } + return TRUE; +fail: + if (icon && icon->hIcon) NtUserDestroyCursor(icon->hIcon, 0); + if (icon && icon->icon_bitmap) free(icon->icon_bitmap); + free(icon); + if (registered) + p_dbus_connection_unregister_object_path(connection, object_name); + return FALSE; +} + +BOOL delete_icon(const NOTIFYICONDATAW* icon_data) +{ + 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); + + 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; + } + return TRUE; +} + +BOOL modify_icon(const NOTIFYICONDATAW* icon_data) +{ + DBusError error; + char object_path[64]; + struct tray_icon* icon; + UINT new_state; + 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)) + return FALSE; + + if (icon_data->uFlags & NIF_ICON) + { + if (!get_icon_data(icon_data, icon)) + goto err; + if (!send_signal_to_item(object_path, "NewIcon")) + goto err; + } + + if (icon_data->uFlags & NIF_MESSAGE) + { + icon->callback_message = icon_data->uCallbackMessage; + } + + if (icon_data->uFlags & NIF_STATE) + { + new_state = (icon->state & ~icon_data->dwStateMask) | (icon_data->dwState & icon_data->dwStateMask); + if (new_state != icon->state) + { + icon->state = new_state; + if (!send_signal_to_item(object_path, "NewStatus")) goto err; + } + } + + if (icon_data->uFlags & NIF_TIP) + { + ntdll_wcstoumbs(icon_data->szTip, wcslen(icon_data->szTip) + 1, icon->tiptext, ARRAY_SIZE(icon->tiptext), FALSE); + if (!send_signal_to_item(object_path, "NewTitle")) goto err; + } + return TRUE; +err: + return FALSE; +} + +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)) + return FALSE; + + icon->version = icon_data->uVersion; + return TRUE; +} + +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); + + 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++; + } + + p_dbus_free_string_array(child_entries); + return TRUE; +} diff --git a/dlls/winesni.drv/dllmain.c b/dlls/winesni.drv/dllmain.c new file mode 100644 index 00000000000..abd0f9df7a3 --- /dev/null +++ b/dlls/winesni.drv/dllmain.c @@ -0,0 +1,48 @@ +/* + * winesni.drv entry points + * + * Copyright 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "snidrv_dll.h" +#include "wine/debug.h" + + +static DWORD WINAPI dbus_sni_read_thread(void* arg) { + SNIDRV_UNIX_CALL(run_loop, NULL); + /* This thread terminates only if an unrecoverable error occured during + * event reading. */ + TerminateProcess(GetCurrentProcess(), 1); + return 0; +} + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + if (reason != DLL_PROCESS_ATTACH) return TRUE; + + DisableThreadLibraryCalls(instance); + if (__wine_init_unix_call()) return FALSE; + + if (SNIDRV_UNIX_CALL(init, NULL)) { + return FALSE; + } + + /* Read dbus messages from a dedicated thread. */ + CloseHandle(CreateThread(NULL, 0, dbus_sni_read_thread, NULL, 0, NULL)); + + return TRUE; +} diff --git a/dlls/winesni.drv/image.c b/dlls/winesni.drv/image.c new file mode 100644 index 00000000000..2f260daff64 --- /dev/null +++ b/dlls/winesni.drv/image.c @@ -0,0 +1,166 @@ +/* + * DBus tray support + * + * Copyright 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include "unixlib.h" +#include "stdlib.h" +#include "ntuser.h" +#include "ntgdi.h" + +/*********************************************************************** + * get_mono_icon_argb + * + * Return a monochrome icon/cursor bitmap bits in ARGB format. + */ +static unsigned int *get_mono_icon_argb( HDC hdc, HBITMAP bmp, unsigned int *width, unsigned int *height ) +{ + BITMAP bm; + char *mask; + unsigned int i, j, stride, mask_size, bits_size, *bits = NULL, *ptr; + + if (!NtGdiExtGetObjectW( bmp, sizeof(bm), &bm )) return NULL; + stride = ((bm.bmWidth + 15) >> 3) & ~1; + mask_size = stride * bm.bmHeight; + if (!(mask = malloc( mask_size ))) return NULL; + if (!NtGdiGetBitmapBits( bmp, mask_size, mask )) goto done; + + bm.bmHeight /= 2; + bits_size = bm.bmWidth * bm.bmHeight * sizeof(*bits); + if (!(bits = malloc( bits_size ))) goto done; + + ptr = bits; + for (i = 0; i < bm.bmHeight; i++) + for (j = 0; j < bm.bmWidth; j++, ptr++) + { + int and = ((mask[i * stride + j / 8] << (j % 8)) & 0x80); + int xor = ((mask[(i + bm.bmHeight) * stride + j / 8] << (j % 8)) & 0x80); + if (!xor && and) + *ptr = 0; + else if (xor && !and) + *ptr = 0xffffffff; + else + /* we can't draw "invert" pixels, so render them as black instead */ +#ifdef WORDS_BIGENDIAN + *ptr = 0xff000000; +#else + *ptr = 0x000000ff; +#endif + } + + *width = bm.bmWidth; + *height = bm.bmHeight; + +done: + free( mask ); + return bits; +} + +/*********************************************************************** + * get_bitmap_argb + * + * Return the bitmap bits in ARGB format. Helper for setting icons and cursors. + */ +static unsigned int *get_bitmap_argb( HDC hdc, HBITMAP color, HBITMAP mask, unsigned int *width, + unsigned int *height ) +{ + char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; + BITMAPINFO *info = (BITMAPINFO *)buffer; + BITMAP bm; + unsigned int *ptr, *bits = NULL; + unsigned char *mask_bits = NULL; + int i, j; + BOOL has_alpha = FALSE; + + if (!color) return get_mono_icon_argb( hdc, mask, width, height ); + + if (!NtGdiExtGetObjectW( color, sizeof(bm), &bm )) return NULL; + info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info->bmiHeader.biWidth = bm.bmWidth; + info->bmiHeader.biHeight = -bm.bmHeight; + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = 32; + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrUsed = 0; + info->bmiHeader.biClrImportant = 0; + if (!(bits = malloc( bm.bmWidth * bm.bmHeight * sizeof(unsigned int) ))) + goto failed; + if (!NtGdiGetDIBitsInternal( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS, 0, 0 )) + goto failed; + + *width = bm.bmWidth; + *height = bm.bmHeight; + + for (i = 0; i < bm.bmWidth * bm.bmHeight; i++) + if ((has_alpha = (bits[i] & 0xff000000) != 0)) break; + + if (!has_alpha) + { + unsigned int width_bytes = (bm.bmWidth + 31) / 32 * 4; + /* generate alpha channel from the mask */ + info->bmiHeader.biBitCount = 1; + info->bmiHeader.biSizeImage = width_bytes * bm.bmHeight; + if (!(mask_bits = malloc( info->bmiHeader.biSizeImage ))) goto failed; + if (!NtGdiGetDIBitsInternal( hdc, mask, 0, bm.bmHeight, mask_bits, info, DIB_RGB_COLORS, 0, 0 )) + goto failed; + ptr = bits; + for (i = 0; i < bm.bmHeight; i++) + for (j = 0; j < bm.bmWidth; j++, ptr++) + if (!((mask_bits[i * width_bytes + j / 8] << (j % 8)) & 0x80)) *ptr |= 0xff000000; + free( mask_bits ); + } +#ifndef WORDS_BIGENDIAN + for (unsigned i = 0; i < bm.bmWidth * bm.bmHeight; i++) + { + bits[i] = ((bits[i] & 0xFF) << 24) | ((bits[i] & 0xFF00) << 8) | ((bits[i] & 0xFF0000) >> 8) | ((bits[i] & 0xFF000000) >> 24); + } +#endif + + return bits; + +failed: + free( bits ); + free( mask_bits ); + *width = *height = 0; + return NULL; +} + + +BOOL create_bitmap_from_icon(HANDLE icon, unsigned *p_width, unsigned *p_height, void** p_bits) +{ + ICONINFO info; + HDC hdc; + + if (!NtUserGetIconInfo(icon, &info, NULL, NULL, NULL, 0)) + return FALSE; + + hdc = NtGdiCreateCompatibleDC( 0 ); + *p_bits = get_bitmap_argb( hdc, info.hbmColor, info.hbmMask, p_width, p_height ); + NtGdiDeleteObjectApp( info.hbmMask ); + NtGdiDeleteObjectApp( hdc ); + return *p_bits != NULL; +} diff --git a/dlls/winesni.drv/snidrv_dll.h b/dlls/winesni.drv/snidrv_dll.h new file mode 100644 index 00000000000..c725efd4ea2 --- /dev/null +++ b/dlls/winesni.drv/snidrv_dll.h @@ -0,0 +1,33 @@ +/* + * SNI driver DLL definitions + * + * Copyright 2022 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_SNIDRV_DLL_H +#define __WINE_SNIDRV_DLL_H + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" + +#include "unixlib.h" + +#define SNIDRV_UNIX_CALL(func, params) WINE_UNIX_CALL(snidrv_unix_func_ ## func, params) + + +#endif /* __WINE_SNIDRV_DLL_H */ diff --git a/dlls/winesni.drv/systray.c b/dlls/winesni.drv/systray.c new file mode 100644 index 00000000000..dd52e660002 --- /dev/null +++ b/dlls/winesni.drv/systray.c @@ -0,0 +1,41 @@ +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "snidrv_dll.h" +#include "shellapi.h" + +#include "wine/list.h" +#include "wine/debug.h" + + +WINE_DEFAULT_DEBUG_CHANNEL(systray); +/*********************************************************************** + * wine_notify_icon (X11DRV.@) + * + * Driver-side implementation of Shell_NotifyIcon. + */ +int CDECL wine_notify_icon( DWORD msg, NOTIFYICONDATAW *data ) +{ + BOOL ret = FALSE; + switch (msg) + { + case NIM_ADD: + ret = SNIDRV_UNIX_CALL(add_icon, data) == STATUS_SUCCESS; + break; + case NIM_DELETE: + ret = SNIDRV_UNIX_CALL(delete_icon, data) == STATUS_SUCCESS; + break; + case NIM_MODIFY: + ret = SNIDRV_UNIX_CALL(modify_icon, data) == STATUS_SUCCESS; + break; + case NIM_SETVERSION: + ret = SNIDRV_UNIX_CALL(set_icon_version, data) == STATUS_SUCCESS; + break; + case 0xdead: /* Wine extension: owner window has died */ + ret = SNIDRV_UNIX_CALL(cleanup_icons, data->hWnd ) == STATUS_SUCCESS; + break; + default: + FIXME( "unhandled tray message: %lu\n", msg ); + break; + } + return ret; +} diff --git a/dlls/winesni.drv/unixlib.c b/dlls/winesni.drv/unixlib.c new file mode 100644 index 00000000000..9352d54aad8 --- /dev/null +++ b/dlls/winesni.drv/unixlib.c @@ -0,0 +1,81 @@ +/* + * DBus tray initializer + * + * Copyright 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include "unixlib.h" +#include "wine/unixlib.h" + +NTSTATUS snidrv_unix_init(void *arg) +{ + if (!load_dbus_functions()) goto err; + if (!get_notifier_watcher_owner()) goto err; + return STATUS_SUCCESS; +err: + dbus_finalize(); + return STATUS_UNSUCCESSFUL; +} + + +NTSTATUS snidrv_unix_run_loop(void* arg) +{ + run_dbus_loop(); + return STATUS_SUCCESS; +} + +NTSTATUS snidrv_unix_add_icon(void* icon_data) +{ + return add_icon((const NOTIFYICONDATAW*)icon_data) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +NTSTATUS snidrv_unix_delete_icon(void* icon_data) +{ + return delete_icon((const NOTIFYICONDATAW*)icon_data) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +NTSTATUS snidrv_unix_modify_icon(void* icon_data) +{ + return modify_icon((const NOTIFYICONDATAW*)icon_data) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +NTSTATUS snidrv_unix_set_icon_version(void* icon_data) +{ + return set_icon_version((const NOTIFYICONDATAW*)icon_data) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +NTSTATUS snidrv_unix_cleanup_icons(void* owner) +{ + return cleanup_icons((HWND)owner) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + snidrv_unix_init, + snidrv_unix_run_loop, + snidrv_unix_add_icon, + snidrv_unix_delete_icon, + snidrv_unix_modify_icon, + snidrv_unix_set_icon_version, + snidrv_unix_cleanup_icons, +}; diff --git a/dlls/winesni.drv/unixlib.h b/dlls/winesni.drv/unixlib.h new file mode 100644 index 00000000000..4476a4cbff6 --- /dev/null +++ b/dlls/winesni.drv/unixlib.h @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_SNIDRV_UNIXLIB_H +#define __WINE_SNIDRV_UNIXLIB_H + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "shellapi.h" +#include "winternl.h" + +#include "wine/unixlib.h" + +enum snirv_unix_func +{ + snidrv_unix_func_init, + snidrv_unix_func_run_loop, + snidrv_unix_func_add_icon, + snidrv_unix_func_delete_icon, + snidrv_unix_func_modify_icon, + snidrv_unix_func_set_icon_version, + snidrv_unix_func_cleanup_icons, + snidrv_unix_func_count, +}; + +extern BOOL add_icon(const NOTIFYICONDATAW* icon_data) DECLSPEC_HIDDEN; +extern BOOL delete_icon(const NOTIFYICONDATAW* icon_data) DECLSPEC_HIDDEN; +extern BOOL modify_icon(const NOTIFYICONDATAW* icon_data) DECLSPEC_HIDDEN; +extern BOOL set_icon_version(const NOTIFYICONDATAW* icon_data) DECLSPEC_HIDDEN; +extern BOOL cleanup_icons(HWND owner) DECLSPEC_HIDDEN; + +void dbus_finalize(void); +void run_dbus_loop(void); +BOOL load_dbus_functions(void); +BOOL get_notifier_watcher_owner(void); +BOOL create_bitmap_from_icon(HANDLE icon, unsigned *p_width, unsigned *p_height, void** p_bits); +#endif /* __WINE_SNIDRV_UNIXLIB_H */ diff --git a/dlls/winesni.drv/version.rc b/dlls/winesni.drv/version.rc new file mode 100644 index 00000000000..452333da1e5 --- /dev/null +++ b/dlls/winesni.drv/version.rc @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Sergei Chernyadyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WINE_FILEDESCRIPTION_STR "Wine DBUS SNI driver" +#define WINE_FILENAME_STR "winesni.drv" + +#include "wine/wine_common_ver.rc" diff --git a/dlls/winesni.drv/winesni.drv.spec b/dlls/winesni.drv/winesni.drv.spec new file mode 100644 index 00000000000..5f086f5c4e5 --- /dev/null +++ b/dlls/winesni.drv/winesni.drv.spec @@ -0,0 +1,2 @@ +# System tray +@ cdecl wine_notify_icon(long ptr)
From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
--- programs/explorer/desktop.c | 9 ++++++++- programs/explorer/explorer_private.h | 2 +- programs/explorer/systray.c | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 1610814048e..fdadf5aecf4 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1025,6 +1025,7 @@ void manage_desktop( WCHAR *arg ) MSG msg; HWND hwnd; HMODULE graphics_driver; + HMODULE remote_notification_driver; unsigned int width, height; WCHAR *cmdline = NULL, *driver = NULL; WCHAR *p = arg; @@ -1034,6 +1035,7 @@ void manage_desktop( WCHAR *arg ) HMODULE shell32; HANDLE thread; DWORD id; + void* notify_icon_ptr = NULL;
/* get the rest of the command line (if any) */ while (*p && !is_whitespace(*p)) p++; @@ -1110,7 +1112,12 @@ void manage_desktop( WCHAR *arg )
if (using_root) enable_shell = FALSE;
- initialize_systray( graphics_driver, using_root, enable_shell ); + remote_notification_driver = LoadLibraryW( L"winesni.drv" ); + if (remote_notification_driver) + notify_icon_ptr = (void *)GetProcAddress( remote_notification_driver, "wine_notify_icon" ); + else + notify_icon_ptr = (void *)GetProcAddress( graphics_driver, "wine_notify_icon" ); + initialize_systray( notify_icon_ptr, using_root, enable_shell ); if (!using_root) initialize_launchers( hwnd );
if ((shell32 = LoadLibraryW( L"shell32.dll" )) && diff --git a/programs/explorer/explorer_private.h b/programs/explorer/explorer_private.h index 995e7eb803e..fe0a533a1ee 100644 --- a/programs/explorer/explorer_private.h +++ b/programs/explorer/explorer_private.h @@ -22,7 +22,7 @@ #define __WINE_EXPLORER_PRIVATE_H
extern void manage_desktop( WCHAR *arg ) DECLSPEC_HIDDEN; -extern void initialize_systray( HMODULE graphics_driver, BOOL using_root, BOOL enable_shell ) DECLSPEC_HIDDEN; +extern void initialize_systray( void* fn_notify_icon, BOOL using_root, BOOL enable_shell ) DECLSPEC_HIDDEN; extern void initialize_appbar(void) DECLSPEC_HIDDEN; extern void handle_parent_notify( HWND hwnd, WPARAM wp ) DECLSPEC_HIDDEN; extern void do_startmenu( HWND owner ) DECLSPEC_HIDDEN; diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 5070e123829..b8a7b9f9a0d 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -891,13 +891,13 @@ void handle_parent_notify( HWND hwnd, WPARAM wp ) }
/* this function creates the listener window */ -void initialize_systray( HMODULE graphics_driver, BOOL using_root, BOOL arg_enable_shell ) +void initialize_systray( void* fn_notify_icon, BOOL using_root, BOOL arg_enable_shell ) { WNDCLASSEXW class; static const WCHAR classname[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; RECT work_rect, primary_rect, taskbar_rect;
- if (using_root && graphics_driver) wine_notify_icon = (void *)GetProcAddress( graphics_driver, "wine_notify_icon" ); + if (using_root && fn_notify_icon) wine_notify_icon = (void *)fn_notify_icon;
icon_cx = GetSystemMetrics( SM_CXSMICON ) + 2*ICON_BORDER; icon_cy = GetSystemMetrics( SM_CYSMICON ) + 2*ICON_BORDER;
From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
--- dlls/winesni.drv/dbus.c | 234 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 226 insertions(+), 8 deletions(-)
diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c index 075ae5a43c5..732b4948377 100644 --- a/dlls/winesni.drv/dbus.c +++ b/dlls/winesni.drv/dbus.c @@ -125,6 +125,10 @@ struct tray_icon UINT version; /* notify icon api version */ };
+ +#define BALLOON_SHOW_MIN_TIMEOUT 10000 +#define BALLOON_SHOW_MAX_TIMEOUT 30000 + static void* dbus_module = NULL; static const char* kde_watcher_interface_name = "org.kde.StatusNotifierWatcher"; static const char* freedesktop_watcher_interface_name = "org.freedesktop.StatusNotifierWatcher"; @@ -256,7 +260,7 @@ cleanup: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
-BOOL get_notifier_watcher_owner_for_interface(const char* interface_name, const char* sni_interface_name) +static BOOL get_owner_for_interface(const char* interface_name, char** owner_path) { DBusMessage* msg = NULL; DBusMessageIter args; @@ -298,13 +302,7 @@ BOOL get_notifier_watcher_owner_for_interface(const char* interface_name, const p_dbus_error_free( &error ); goto err; } - else - { - TRACE("found notifier destination name %s\n", status_notifier_dest); - watcher_interface_name = interface_name; - item_interface_name = sni_interface_name; - status_notifier_dst_path = strdup(status_notifier_dest); - } + *owner_path = strdup(status_notifier_dest); p_dbus_message_unref(msg); return TRUE; err: @@ -312,6 +310,16 @@ err: return FALSE; }
+BOOL get_notifier_watcher_owner_for_interface(const char* interface_name, const char* sni_interface_name) +{ + if (!get_owner_for_interface(interface_name, &status_notifier_dst_path)) + return FALSE; + TRACE("found notifier destination name %s\n", status_notifier_dst_path); + watcher_interface_name = interface_name; + item_interface_name = sni_interface_name; + return TRUE; +} + BOOL get_notifier_watcher_owner(void) { DBusError error; @@ -346,6 +354,210 @@ err: return FALSE; }
+static BOOL handle_notification_icon(DBusMessageIter *iter, const unsigned char* icon_bits, unsigned width, unsigned height) +{ + DBusMessageIter sIter,bIter; + unsigned row_stride = width * 4; + const unsigned channel_count = 4; + const unsigned bits_per_sample = 8; + const bool has_alpha = true; + if (!p_dbus_message_iter_open_container(iter, 'r', NULL, &sIter)) + { + WARN("Failed to open struct inside array!\n"); + goto fail; + } + + p_dbus_message_iter_append_basic(&sIter, 'i', &width); + p_dbus_message_iter_append_basic(&sIter, 'i', &height); + p_dbus_message_iter_append_basic(&sIter, 'i', &row_stride); + p_dbus_message_iter_append_basic(&sIter, 'b', &has_alpha); + p_dbus_message_iter_append_basic(&sIter, 'i', &bits_per_sample); + p_dbus_message_iter_append_basic(&sIter, 'i', &channel_count); + + if (p_dbus_message_iter_open_container(&sIter, 'a', DBUS_TYPE_BYTE_AS_STRING, &bIter)) + { + p_dbus_message_iter_append_fixed_array(&bIter, DBUS_TYPE_BYTE, &icon_bits, width * height * 4); + p_dbus_message_iter_close_container(&sIter, &bIter); + } + else + { + p_dbus_message_iter_abandon_container_if_open(iter, &sIter); + goto fail; + } + p_dbus_message_iter_close_container(iter, &sIter); + return TRUE; +fail: + return FALSE; +} + +static BOOL send_notification(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; + const char* empty_string = ""; + const char* icon_name = ""; + unsigned id = 0; + BOOL ret = FALSE; + char* notifications_dst_path = NULL; + DBusMessage* msg = NULL; + DBusMessageIter args, aIter, eIter, vIter; + DBusPendingCall* pending; + DBusError error; + /* icon */ + void* icon_bits = NULL; + unsigned width, height; + HICON new_icon = NULL; + int expire_timeout; + //TODO: startup the service + if (!get_owner_for_interface("org.freedesktop.Notifications", ¬ifications_dst_path)) + goto err; + info_title[0] = 0; + info_text[0] = 0; + if (title) ntdll_wcstoumbs(title, wcslen(title) + 1, info_title, ARRAY_SIZE(info_title), FALSE); + if (text) ntdll_wcstoumbs(text, wcslen(text) + 1, info_text, ARRAY_SIZE(info_text), FALSE); + /*icon*/ + if ((info_flags & NIIF_ICONMASK) == NIIF_USER && icon) + { + unsigned int *u_icon_bits; + new_icon = CopyImage(icon, IMAGE_ICON, 0, 0, 0); + if (!create_bitmap_from_icon(new_icon, &width, &height, &icon_bits)) + { + WARN("failed to copy icon %p\n", new_icon); + goto err; + } + u_icon_bits = icon_bits; + /* convert to RGBA, turns out that unlike tray icons it needs RGBA */ + for (unsigned i = 0; i < width * height; i++) + { +#ifdef WORDS_BIGENDIAN + u_icon_bits[i] = (u_icon_bits[i] << 8) | (u_icon_bits[i] >> 24); +#else + u_icon_bits[i] = (u_icon_bits[i] << 24) | (u_icon_bits[i] >> 8); +#endif + } + + } + else + { + /* show placeholder icons */ + switch (info_flags & NIIF_ICONMASK) + { + case NIIF_INFO: + icon_name = "dialog-information"; + break; + case NIIF_WARNING: + icon_name = "dialog-warning"; + break; + case NIIF_ERROR: + icon_name = "dialog-error"; + break; + default: + break; + } + } + p_dbus_error_init( &error ); + msg = p_dbus_message_new_method_call(notifications_dst_path, + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "Notify"); + if (!msg) goto err; + + p_dbus_message_iter_init_append(msg, &args); + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &empty_string )) + goto err; + /* override id */ + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &id )) + goto err; + /* icon name */ + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &icon_name )) + goto err; + /* title */ + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info_title_ptr )) + goto err; + /* body */ + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info_text_ptr )) + goto err; + /* actions */ + /* TODO: add default action */ + if (p_dbus_message_iter_open_container(&args, 'a', DBUS_TYPE_STRING_AS_STRING, &aIter)) + p_dbus_message_iter_close_container(&args, &aIter); + else + goto err; + + /* hints */ + if (p_dbus_message_iter_open_container(&args, 'a', "{sv}", &aIter)) + { + if ((info_flags & NIIF_ICONMASK) == NIIF_USER && icon) + { + const char* icon_data_field = "image-data"; + if (!p_dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) + { + p_dbus_message_iter_abandon_container_if_open(&args, &aIter); + goto err; + } + + p_dbus_message_iter_append_basic(&eIter, 's', &icon_data_field); + + if (!p_dbus_message_iter_open_container(&eIter, 'v', "(iiibiiay)", &vIter)) + { + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&args, &aIter); + goto err; + } + + if (!handle_notification_icon(&vIter, icon_bits, width, height)) + { + p_dbus_message_iter_abandon_container_if_open(&eIter, &vIter); + p_dbus_message_iter_abandon_container_if_open(&aIter, &eIter); + p_dbus_message_iter_abandon_container_if_open(&args, &aIter); + goto err; + } + + p_dbus_message_iter_close_container(&eIter, &vIter); + p_dbus_message_iter_close_container(&aIter, &eIter); + } + p_dbus_message_iter_close_container(&args, &aIter); + } + else + goto err; + if (timeout == 0) + /* just set it to system default */ + expire_timeout = -1; + else + expire_timeout = max(min(timeout, BALLOON_SHOW_MAX_TIMEOUT), BALLOON_SHOW_MIN_TIMEOUT); + + /* timeout */ + if (!p_dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &expire_timeout )) + goto err; + if (!p_dbus_connection_send_with_reply (connection, msg, &pending, -1)) + goto err; + if (!pending) goto err; + + p_dbus_connection_flush(connection); + + p_dbus_message_unref(msg); + + p_dbus_pending_call_block(pending); + + msg = p_dbus_pending_call_steal_reply(pending); + p_dbus_pending_call_unref(pending); + if (!msg) goto err; + + if (p_dbus_set_error_from_message (&error, msg)) + { + WARN("failed to query an owner - %s: %s\n", error.name, error.message); + p_dbus_error_free( &error); + goto err; + } + ret = TRUE; +err: + if (new_icon) NtUserDestroyCursor(new_icon, 0); + free(icon_bits); + free(notifications_dst_path); + return ret; +} + void dbus_finalize() { if (connection != NULL) @@ -965,6 +1177,8 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) ntdll_wcstoumbs(icon_data->szTip, wcslen(icon_data->szTip) + 1, icon->tiptext, ARRAY_SIZE(icon->tiptext), FALSE); if (icon_data->uFlags & NIF_STATE) 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);
icon->version = icon_data->uVersion; if (!p_dbus_connection_try_register_object_path(connection, object_name, ¬ification_vtable, icon, &error)) @@ -1049,6 +1263,10 @@ BOOL modify_icon(const NOTIFYICONDATAW* icon_data) ntdll_wcstoumbs(icon_data->szTip, wcslen(icon_data->szTip) + 1, icon->tiptext, ARRAY_SIZE(icon->tiptext), FALSE); if (!send_signal_to_item(object_path, "NewTitle")) goto err; } + + 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); + return TRUE; err: return FALSE;
From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
--- dlls/winesni.drv/dbus.c | 90 ++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 24 deletions(-)
diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c index 732b4948377..2d14263a5a1 100644 --- a/dlls/winesni.drv/dbus.c +++ b/dlls/winesni.drv/dbus.c @@ -113,16 +113,17 @@ DBUS_FUNCS; /* an individual systray icon */ struct tray_icon { - HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */ - HICON hIcon; - void* icon_bitmap; - UINT icon_width; - UINT icon_height; - UINT state; /* state flags */ - UINT id; /* the unique id given by the app */ - UINT callback_message; - char tiptext[128 * 3]; /* tooltip text */ - UINT version; /* notify icon api version */ + HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */ + HICON hIcon; + void* icon_bitmap; + UINT icon_width; + UINT icon_height; + UINT state; /* state flags */ + UINT id; /* the unique id given by the app */ + UINT callback_message; + char tiptext[128 * 3]; /* tooltip text */ + UINT version; /* notify icon api version */ + pthread_mutex_t mutex; /* mutex */ };
@@ -147,8 +148,6 @@ static const char *icon_name_field = "IconName"; static const char *title_field = "Title"; static const char *category_field = "Category"; static const char *id_field = "Id"; -/*TODO: sync it*/ -/*static pthread_mutex_t status_notifier_item_mutex = PTHREAD_MUTEX_INITIALIZER;*/
BOOL load_dbus_functions(void) { @@ -688,6 +687,7 @@ DBusHandlerResult get_all_tray_properties(DBusConnection* conn, DBusMessage *mes if (!reply) goto fail;
p_dbus_message_iter_init_append(reply, &iter); + pthread_mutex_lock((pthread_mutex_t*)&icon->mutex);
if (!p_dbus_message_iter_open_container(&iter, 'a', "{sv}", &aIter)) goto fail;
@@ -818,11 +818,17 @@ DBusHandlerResult get_all_tray_properties(DBusConnection* conn, DBusMessage *mes p_dbus_message_iter_close_container(&aIter, &eIter); p_dbus_message_iter_close_container(&iter, &aIter);
- if (!p_dbus_connection_send(conn, reply, &serial)) goto fail; + pthread_mutex_unlock((pthread_mutex_t*)&icon->mutex); + + if (!p_dbus_connection_send(conn, reply, &serial)) + goto send_fail; + p_dbus_connection_flush(conn); p_dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; fail: + pthread_mutex_unlock((pthread_mutex_t*)&icon->mutex); +send_fail: p_dbus_message_unref(reply);
return notification_send_error(conn, message, "org.freedesktop.DBus.Error.Failed", "got an error while processing properties"); @@ -838,6 +844,7 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage DBusMessage* reply = NULL; DBusMessageIter iter, vIter; unsigned serial = 0; + DBusHandlerResult ret = DBUS_HANDLER_RESULT_HANDLED; DBusError error; p_dbus_error_init(&error); if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_STRING, &interface_name, @@ -855,6 +862,9 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage snprintf(error_message, sizeof(error_message), "unsupported interface %s", interface_name); return notification_send_error (conn, message, "org.freedesktop.DBus.Error.UnknownProperty", error_message); } + + pthread_mutex_lock((pthread_mutex_t*)&icon->mutex); + if (strcmp(property_name, title_field) == 0) { reply = p_dbus_message_new_method_return(message); @@ -862,7 +872,8 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) { p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_get; } handle_title(conn, &vIter, icon); p_dbus_message_iter_close_container(&iter, &vIter); @@ -874,7 +885,8 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) { p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_get; } handle_id(conn, &vIter, icon); p_dbus_message_iter_close_container(&iter, &vIter); @@ -886,7 +898,8 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage if (!p_dbus_message_iter_open_container(&iter, 'v', "a(iiay)", &vIter)) { p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_get; } handle_icon(conn, &vIter, icon); p_dbus_message_iter_close_container(&iter, &vIter); @@ -898,7 +911,8 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage if (!p_dbus_message_iter_open_container(&iter, 'v', "s", &vIter)) { p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_get; } handle_status(conn, &vIter, icon); p_dbus_message_iter_close_container(&iter, &vIter); @@ -906,17 +920,23 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage else { char error_message[128]; + pthread_mutex_unlock((pthread_mutex_t*)&icon->mutex); snprintf(error_message, sizeof(error_message), "interface doesn't have the property %s", property_name); return notification_send_error (conn, message, "org.freedesktop.DBus.Error.UnknownProperty", error_message); } + if (!p_dbus_connection_send(conn, reply, &serial)) { p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_get; } + p_dbus_connection_flush(conn); p_dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + err_get: + pthread_mutex_unlock((pthread_mutex_t*)&icon->mutex); + return ret; } else if (p_dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll")) { @@ -1017,6 +1037,7 @@ void unregister_notification_handler(DBusConnection* conn, void* 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); } @@ -1165,6 +1186,9 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) memset(icon, 0, sizeof(*icon)); icon->id = icon_data->uID; icon->owner = icon_data->hWnd; + if (pthread_mutex_init(&icon->mutex, NULL)) { + goto fail; + } if (icon_data->uFlags & NIF_ICON) { if (!get_icon_data(icon_data, icon)) goto fail; @@ -1201,6 +1225,7 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) fail: if (icon && icon->hIcon) NtUserDestroyCursor(icon->hIcon, 0); if (icon && icon->icon_bitmap) free(icon->icon_bitmap); + if (icon) pthread_mutex_destroy(&icon->mutex); free(icon); if (registered) p_dbus_connection_unregister_object_path(connection, object_name); @@ -1228,19 +1253,23 @@ 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)) return FALSE;
+ pthread_mutex_lock(&icon->mutex); + if (icon_data->uFlags & NIF_ICON) { if (!get_icon_data(icon_data, icon)) goto err; - if (!send_signal_to_item(object_path, "NewIcon")) - goto err; + pending_signals[signal_count++] = "NewIcon"; }
if (icon_data->uFlags & NIF_MESSAGE) @@ -1254,14 +1283,23 @@ BOOL modify_icon(const NOTIFYICONDATAW* icon_data) if (new_state != icon->state) { icon->state = new_state; - if (!send_signal_to_item(object_path, "NewStatus")) goto err; + pending_signals[signal_count++] = "NewStatus"; } }
if (icon_data->uFlags & NIF_TIP) { ntdll_wcstoumbs(icon_data->szTip, wcslen(icon_data->szTip) + 1, icon->tiptext, ARRAY_SIZE(icon->tiptext), FALSE); - if (!send_signal_to_item(object_path, "NewTitle")) goto err; + pending_signals[signal_count++] = "NewTitle"; + } + + pthread_mutex_unlock(&icon->mutex); + + /* send the signals */ + for (i = 0; i < signal_count; i++) + { + if (!send_signal_to_item(object_path, pending_signals[i])) + goto err_post_unlock; }
if (icon_data->uFlags & NIF_INFO && icon_data->cbSize >= NOTIFYICONDATAA_V2_SIZE) @@ -1269,6 +1307,8 @@ BOOL modify_icon(const NOTIFYICONDATAW* icon_data)
return TRUE; err: + pthread_mutex_unlock(&icon->mutex); +err_post_unlock: return FALSE; }
@@ -1282,8 +1322,10 @@ BOOL set_icon_version(const NOTIFYICONDATAW* icon_data)
if (!p_dbus_connection_get_object_path_data(connection, object_path, (void**)&icon)) return FALSE; - + pthread_mutex_lock(&icon->mutex); icon->version = icon_data->uVersion; + pthread_mutex_unlock(&icon->mutex); + return TRUE; }
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 | 289 +++++++++++++++++++++++----------------- 1 file changed, 164 insertions(+), 125 deletions(-)
diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c index 2d14263a5a1..19aa762d3f0 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 ) @@ -251,7 +230,8 @@ static DBusHandlerResult name_owner_filter( DBusConnection *ctx, DBusMessage *ms old_path = status_notifier_dst_path; status_notifier_dst_path = strdup(new_path); free(old_path); - if (status_notifier_dst_path[0] != '\0') register_items(ctx); + if (status_notifier_dst_path[0] != '\0') + restore_items(ctx); } } cleanup: @@ -259,7 +239,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; @@ -309,9 +289,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); watcher_interface_name = interface_name; @@ -325,22 +305,22 @@ BOOL get_notifier_watcher_owner(void) p_dbus_error_init( &error ); 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); @@ -389,7 +369,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]; @@ -409,7 +389,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; info_text[0] = 0; @@ -559,10 +539,10 @@ err:
void dbus_finalize() { - if (connection != NULL) + if (global_connection != NULL) { - p_dbus_connection_close(connection); - p_dbus_connection_unref(connection); + p_dbus_connection_close(global_connection); + p_dbus_connection_unref(global_connection); } if (dbus_module != NULL) { @@ -1031,59 +1011,102 @@ 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; + 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 struct tray_icon *get_icon(HWND owner, UINT id) +{ + struct tray_icon *this;
- while ( p_dbus_connection_get_dispatch_status ( connection ) == DBUS_DISPATCH_DATA_REMAINS ) + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY( this, &sni_list, struct tray_icon, entry ) + { + if ((this->id == id) && (this->owner == owner)) { - p_dbus_connection_dispatch ( connection ) ; + pthread_mutex_unlock(&list_mutex); + return this; } } + pthread_mutex_unlock(&list_mutex); + return NULL; }
-static BOOL register_notification_item(const char* object_name) +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 ); @@ -1125,7 +1148,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; @@ -1174,24 +1197,37 @@ 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;
memset(icon, 0, sizeof(*icon)); icon->id = icon_data->uID; icon->owner = icon_data->hWnd; - if (pthread_mutex_init(&icon->mutex, NULL)) { + 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)) goto fail; + if (!get_icon_data(icon_data, icon)) + { + WARN("failed to get icon info\n" ); + goto fail; + } } if (icon_data->uFlags & NIF_MESSAGE) { @@ -1202,25 +1238,24 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) if (icon_data->uFlags & NIF_STATE) 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)) + !register_notification_item(connection)) { - WARN("failed to register item %s\n", object_name); - p_dbus_connection_unregister_object_path(connection, object_name); + 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); @@ -1228,39 +1263,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); + + free(icon); + return TRUE; +}
- if (!p_dbus_connection_unregister_object_path(connection, object_name)) +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 ) { - WARN("failed register object %s: %s\n", error.name, error.message); - p_dbus_error_free( &error ); - return FALSE; + if ((this->id == icon_data->uID) && (this->owner == icon_data->hWnd)) + { + list_remove(&this->entry); + icon = this; + break; + } } - return TRUE; + 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;
pthread_mutex_lock(&icon->mutex); @@ -1298,13 +1347,12 @@ 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: pthread_mutex_unlock(&icon->mutex); @@ -1314,13 +1362,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; pthread_mutex_lock(&icon->mutex); icon->version = icon_data->uVersion; @@ -1331,23 +1375,18 @@ 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); - - if (!p_dbus_connection_list_registered(connection, object_prefix, &child_entries) || child_entries == NULL) - return FALSE; + struct tray_icon *this, *next;
- child_entry_ptr = child_entries; - while (*child_entry_ptr != NULL) + pthread_mutex_lock(&list_mutex); + LIST_FOR_EACH_ENTRY_SAFE( this, next, &sni_list, struct tray_icon, entry ) { - 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++; + 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; }
From: Sergei Chernyadyev 1892-Cherser-s@users.noreply.gitlab.winehq.org
--- dlls/winesni.drv/Makefile.in | 2 +- dlls/winesni.drv/dbus.c | 159 +++++++++++++++++++++++++++-------- 2 files changed, 127 insertions(+), 34 deletions(-)
diff --git a/dlls/winesni.drv/Makefile.in b/dlls/winesni.drv/Makefile.in index ed73bf9092b..a3ab6f1075a 100644 --- a/dlls/winesni.drv/Makefile.in +++ b/dlls/winesni.drv/Makefile.in @@ -2,7 +2,7 @@ MODULE = winesni.drv UNIXLIB = winesni.so IMPORTS = uuid win32u UNIX_CFLAGS = $(DBUS_CFLAGS) $(HAL_CFLAGS) -UNIX_LIBS = -lwin32u +UNIX_LIBS = -lwin32u $(PTHREAD_LIBS)
SOURCES = \ dllmain.c \ diff --git a/dlls/winesni.drv/dbus.c b/dlls/winesni.drv/dbus.c index 19aa762d3f0..b04eddd05b2 100644 --- a/dlls/winesni.drv/dbus.c +++ b/dlls/winesni.drv/dbus.c @@ -73,6 +73,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(winesni); DO_FUNC(dbus_connection_unref); \ DO_FUNC(dbus_connection_get_object_path_data); \ DO_FUNC(dbus_connection_get_unix_fd); \ + DO_FUNC(dbus_connection_set_watch_functions); \ + DO_FUNC(dbus_watch_get_unix_fd); \ + DO_FUNC(dbus_watch_handle); \ + DO_FUNC(dbus_watch_get_flags); \ + DO_FUNC(dbus_watch_get_enabled); \ DO_FUNC(dbus_error_free); \ DO_FUNC(dbus_error_init); \ DO_FUNC(dbus_error_is_set); \ @@ -127,11 +132,14 @@ struct tray_icon char tiptext[128 * 3]; /* tooltip text */ UINT version; /* notify icon api version */ DBusConnection* connection; + DBusWatch* watch; + int watch_fd; + UINT watch_flags; pthread_mutex_t mutex; /* mutex */ };
static struct list sni_list = LIST_INIT( sni_list ); -static pthread_mutex_t list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t list_mutex;
#define BALLOON_SHOW_MIN_TIMEOUT 10000 #define BALLOON_SHOW_MAX_TIMEOUT 30000 @@ -146,6 +154,10 @@ static const char* freedesktop_item_interface_name = "org.freedesktop.StatusNoti static const char* item_interface_name = "org.kde.StatusNotifierItem";
static DBusConnection *global_connection; +static DBusWatch *global_connection_watch; +static int global_connection_watch_fd; +static UINT global_connection_watch_flags; + static char* status_notifier_dst_path = NULL;
static const char *status_field = "Status"; @@ -258,8 +270,6 @@ static BOOL get_owner_for_interface(DBusConnection* connection, const char* inte if (!p_dbus_connection_send_with_reply (connection, msg, &pending, -1)) goto err; if (!pending) goto err;
- p_dbus_connection_flush(connection); - p_dbus_message_unref(msg);
p_dbus_pending_call_block(pending); @@ -299,12 +309,76 @@ BOOL get_notifier_watcher_owner_for_interface(DBusConnection* connection, const return TRUE; }
+ +static dbus_bool_t add_watch(DBusWatch *w, void *data) +{ + int fd; + unsigned int flags, poll_flags; + if (!p_dbus_watch_get_enabled(w)) + return TRUE; + + fd = p_dbus_watch_get_unix_fd(w); + flags = p_dbus_watch_get_flags(w); + poll_flags = 0; + + if (flags & DBUS_WATCH_READABLE) + poll_flags |= POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + poll_flags |= POLLOUT; + + pthread_mutex_lock(&list_mutex); + if (data != NULL) { + struct tray_icon *icon = (struct tray_icon *) data; + icon->watch_fd = fd; + icon->watch_flags = poll_flags; + icon->watch = w; + } else { + /* global connection */ + global_connection_watch_fd = fd; + global_connection_watch_flags = poll_flags; + global_connection_watch = w; + } + pthread_mutex_unlock(&list_mutex); + + return TRUE; +} +static void remove_watch(DBusWatch *w, void *data) +{ + pthread_mutex_lock(&list_mutex); + if (data != NULL) { + struct tray_icon *icon = (struct tray_icon *) data; + icon->watch_fd = 0; + icon->watch_flags = 0; + icon->watch = NULL; + } else { + /* global connection */ + global_connection_watch_fd = 0; + global_connection_watch_flags = 0; + global_connection_watch = NULL; + } + pthread_mutex_unlock(&list_mutex); +} + + +static void toggle_watch(DBusWatch *w, void *data) +{ + if (p_dbus_watch_get_enabled(w)) + add_watch(w, data); + else + remove_watch(w, data); +} + BOOL get_notifier_watcher_owner(void) { DBusError error; + pthread_mutexattr_t attr; p_dbus_error_init( &error ); if (!p_dbus_threads_init_default()) goto err;
+ pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( &list_mutex, &attr ); + if (!(global_connection = p_dbus_bus_get_private( DBUS_BUS_SESSION, &error ))) { WARN("failed to get system dbus connection: %s\n", error.message ); @@ -312,6 +386,12 @@ BOOL get_notifier_watcher_owner(void) goto err; }
+ if (!p_dbus_connection_set_watch_functions(global_connection, add_watch, remove_watch, + toggle_watch, NULL, NULL)) { + WARN("dbus_set_watch_functions() failed\n"); + goto err; + } + 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)) { @@ -327,9 +407,11 @@ BOOL get_notifier_watcher_owner(void) p_dbus_error_free( &error); goto err; } + pthread_mutexattr_destroy( &attr ); return TRUE;
err: + pthread_mutexattr_destroy( &attr ); return FALSE; }
@@ -513,8 +595,6 @@ static BOOL send_notification(DBusConnection* connection, const WCHAR* title, co goto err; if (!pending) goto err;
- p_dbus_connection_flush(connection); - p_dbus_message_unref(msg);
p_dbus_pending_call_block(pending); @@ -541,9 +621,11 @@ void dbus_finalize() { if (global_connection != NULL) { + p_dbus_connection_flush(global_connection); p_dbus_connection_close(global_connection); p_dbus_connection_unref(global_connection); } + pthread_mutex_destroy(&list_mutex); if (dbus_module != NULL) { dlclose(dbus_module); @@ -652,7 +734,7 @@ static DBusHandlerResult notification_send_error(DBusConnection *conn, DBusMessa p_dbus_message_unref(reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } - p_dbus_connection_flush(conn); + p_dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -803,7 +885,6 @@ DBusHandlerResult get_all_tray_properties(DBusConnection* conn, DBusMessage *mes if (!p_dbus_connection_send(conn, reply, &serial)) goto send_fail;
- p_dbus_connection_flush(conn); p_dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; fail: @@ -912,7 +993,6 @@ DBusHandlerResult notification_message_handler(DBusConnection *conn, DBusMessage goto err_get; }
- p_dbus_connection_flush(conn); p_dbus_message_unref(reply); err_get: pthread_mutex_unlock((pthread_mutex_t*)&icon->mutex); @@ -1019,40 +1099,44 @@ const DBusObjectPathVTable notification_vtable =
void run_dbus_loop(void) { - int dbus_conn_fd; DBusConnection* conns[128]; + DBusWatch* watches[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; + fd_count = 0; pthread_mutex_lock(&list_mutex); + if (global_connection_watch_fd) + { + conns[fd_count] = p_dbus_connection_ref(global_connection); + watches[fd_count] = global_connection_watch; + fd_info[fd_count++] = (struct pollfd) { + .fd = global_connection_watch_fd, + .events = global_connection_watch_flags, + .revents = 0, + }; + } 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); + if (!icon->watch_fd) + continue; conns[fd_count] = p_dbus_connection_ref(icon->connection); + watches[fd_count] = icon->watch; fd_info[fd_count++] = (struct pollfd) { - .fd = sni_dbus_conn_fd, - .events = POLLIN, + .fd = icon->watch_fd, + .events = icon->watch_flags, .revents = 0, }; } pthread_mutex_unlock(&list_mutex); - /* TODO: utilize DBusWatch */ + poll_ret = poll(fd_ptr, fd_count, 100); if (poll_ret == 0) continue; @@ -1063,17 +1147,21 @@ void run_dbus_loop(void) } 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; + if (fd_info[i].revents & (POLLERR | POLLHUP | POLLNVAL)) continue; + if (fd_info[i].revents & POLLIN) { + p_dbus_watch_handle(watches[i], DBUS_WATCH_READABLE); 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]); + if (fd_info[i].revents & POLLOUT) { + p_dbus_watch_handle(watches[i], DBUS_WATCH_WRITABLE); + } + } + for ( i = 0; i < fd_count; i++ ) + { + p_dbus_connection_unref(conns[i]); } } } @@ -1122,9 +1210,7 @@ static BOOL register_notification_item(DBusConnection* connection)
if (!p_dbus_connection_send_with_reply (connection, msg, &pending, -1)) goto err; - if (!pending) goto err;
- p_dbus_connection_flush(connection); p_dbus_message_unref(msg);
p_dbus_pending_call_block(pending); @@ -1165,8 +1251,6 @@ static BOOL send_signal_to_item(DBusConnection* connection, const char* signal_n return FALSE; }
- p_dbus_connection_flush(connection); - p_dbus_message_unref(msg);
return TRUE; @@ -1209,6 +1293,7 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) p_dbus_error_free( &error ); goto fail; } + icon = malloc(sizeof(struct tray_icon)); if (!icon) goto fail;
@@ -1221,6 +1306,13 @@ BOOL add_icon(const NOTIFYICONDATAW* icon_data) WARN("failed to initialize mutex\n" ); goto fail; } + + if (!p_dbus_connection_set_watch_functions(connection, add_watch, remove_watch, + toggle_watch, icon, NULL)) { + WARN("dbus_set_watch_functions() failed\n"); + goto fail; + } + if (icon_data->uFlags & NIF_ICON) { if (!get_icon_data(icon_data, icon)) @@ -1272,9 +1364,11 @@ fail: BOOL cleanup_icon(struct tray_icon* icon) { pthread_mutex_lock(&icon->mutex); + p_dbus_connection_flush(icon->connection); 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); @@ -1387,6 +1481,5 @@ BOOL cleanup_icons(HWND owner) } } pthread_mutex_unlock(&list_mutex); - return TRUE; }
Aida Jonikienė (@DodoGTA) commented about dlls/winesni.drv/Makefile.in:
+MODULE = winesni.drv +UNIXLIB = winesni.so +IMPORTS = uuid win32u +UNIX_CFLAGS = $(DBUS_CFLAGS) $(HAL_CFLAGS) +UNIX_LIBS = -lwin32u $(PTHREAD_LIBS)
+SOURCES = \
- dllmain.c \
- unixlib.c \
- dbus.c \
- systray.c \
image.c \
- version.rc \
```suggestion:-5+5 dbus.c \ dllmain.c \ image.c \ systray.c \ unixlib.c \ version.rc ```
Can you list the files in an alphabetical order, remove the last backslash and fix a minor indentation issue (so Wine doesn't modify your Makefile.in file)?