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;