diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 74c9ef5c..13eade85 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1272,11 +1272,11 @@ extern "C" { #define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR /*! @} */ -#define GLFW_TASKBAR_PROGRESS_NOPROGRESS 0x00 -#define GLFW_TASKBAR_PROGRESS_INDETERMINATE 0x01 -#define GLFW_TASKBAR_PROGRESS_NORMAL 0x02 -#define GLFW_TASKBAR_PROGRESS_ERROR 0x04 -#define GLFW_TASKBAR_PROGRESS_PAUSED 0x08 +#define GLFW_TASKBAR_PROGRESS_NOPROGRESS 0 +#define GLFW_TASKBAR_PROGRESS_INDETERMINATE 1 +#define GLFW_TASKBAR_PROGRESS_NORMAL 2 +#define GLFW_TASKBAR_PROGRESS_ERROR 3 +#define GLFW_TASKBAR_PROGRESS_PAUSED 4 #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01f191c9..32f933ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,8 @@ elseif (WIN32) win32_time.c win32_thread.c) else() target_sources(glfw PRIVATE posix_time.h posix_thread.h posix_module.c - posix_time.c posix_thread.c) + posix_time.c posix_thread.c posix_dbus.h + posix_dbus.c) endif() add_custom_target(update_mappings diff --git a/src/internal.h b/src/internal.h index cc977b3d..22122e81 100644 --- a/src/internal.h +++ b/src/internal.h @@ -877,6 +877,7 @@ struct _GLFWlibrary GLFW_PLATFORM_LIBRARY_WINDOW_STATE GLFW_PLATFORM_LIBRARY_CONTEXT_STATE GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE + GLFW_PLATFORM_LIBRARY_DBUS_STATE }; // Global state shared between compilation units of GLFW diff --git a/src/platform.h b/src/platform.h index 0c593676..9f664003 100644 --- a/src/platform.h +++ b/src/platform.h @@ -153,11 +153,15 @@ #if defined(_WIN32) #include "win32_time.h" #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_WIN32_LIBRARY_TIMER_STATE + #define GLFW_PLATFORM_LIBRARY_DBUS_STATE #elif defined(__APPLE__) #include "cocoa_time.h" #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_COCOA_LIBRARY_TIMER_STATE + #define GLFW_PLATFORM_LIBRARY_DBUS_STATE #else #include "posix_time.h" #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_POSIX_LIBRARY_TIMER_STATE + #include "posix_dbus.h" + #define GLFW_PLATFORM_LIBRARY_DBUS_STATE GLFW_POSIX_LIBRARY_DBUS_STATE #endif diff --git a/src/posix_dbus.c b/src/posix_dbus.c new file mode 100644 index 00000000..0306323f --- /dev/null +++ b/src/posix_dbus.c @@ -0,0 +1,126 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2022 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#define _GNU_SOURCE + +#include "internal.h" + +void _glfwInitDBusPOSIX(void) +{ + //Initialize DBus library functions + _glfw.dbus.handle = NULL; + _glfw.dbus.connection = NULL; + + _glfw.dbus.handle = _glfwPlatformLoadModule("libdbus-1.so.3"); + if (_glfw.dbus.handle) + { + _glfw.dbus.error_init = (PFN_dbus_error_init) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_init"); + _glfw.dbus.error_is_set = (PFN_dbus_error_is_set) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_is_set"); + _glfw.dbus.error_free = (PFN_dbus_error_free) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_free"); + _glfw.dbus.connection_unref = (PFN_dbus_connection_unref) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_unref"); + _glfw.dbus.connection_send = (PFN_dbus_connection_send) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_send"); + _glfw.dbus.connection_flush = (PFN_dbus_connection_flush) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_flush"); + _glfw.dbus.bus_request_name = (PFN_dbus_bus_request_name) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_bus_request_name"); + _glfw.dbus.bus_get = (PFN_dbus_bus_get) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_bus_get"); + _glfw.dbus.message_unref = (PFN_dbus_message_unref) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_unref"); + _glfw.dbus.message_new_signal = (PFN_dbus_message_new_signal) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_new_signal"); + _glfw.dbus.message_iter_init_append = (PFN_dbus_message_iter_init_append) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_init_append"); + _glfw.dbus.message_iter_append_basic = (PFN_dbus_message_iter_append_basic) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_append_basic"); + _glfw.dbus.message_iter_open_container = (PFN_dbus_message_iter_open_container) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_open_container"); + _glfw.dbus.message_iter_close_container = (PFN_dbus_message_iter_close_container) + _glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_close_container"); + + //Initialize DBus connection + dbus_error_init(&_glfw.dbus.error); + _glfw.dbus.connection = dbus_bus_get(DBUS_BUS_SESSION, &_glfw.dbus.error); + + //Check for errors + if(dbus_error_is_set(&_glfw.dbus.error) || !_glfw.dbus.connection) + { + if(dbus_error_is_set(&_glfw.dbus.error)) + dbus_error_free(&_glfw.dbus.error); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to connect to DBus"); + + dbus_connection_unref(_glfw.dbus.connection); + _glfw.dbus.connection = NULL; + } + else + { + //Request name + const int res = dbus_bus_request_name(_glfw.dbus.connection, "org.glfw", DBUS_NAME_FLAG_REPLACE_EXISTING, &_glfw.dbus.error); + + //Check for errors + if(dbus_error_is_set(&_glfw.dbus.error) || res != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + if(dbus_error_is_set(&_glfw.dbus.error)) + dbus_error_free(&_glfw.dbus.error); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to request DBus name"); + + dbus_connection_unref(_glfw.dbus.connection); + _glfw.dbus.connection = NULL; + } + } + } + + if(_glfw.dbus.connection) + { + //Window NULL is safe here because it won't get + //used inside th eSetWindowTaskbarProgress function + _glfw.platform.setWindowTaskbarProgress(NULL, GLFW_TASKBAR_PROGRESS_NOPROGRESS, 0); + } +} + +void _glfwTerminateDBusPOSIX(void) +{ + if (_glfw.dbus.connection) + { + dbus_connection_unref(_glfw.dbus.connection); + _glfw.dbus.connection = NULL; + } + + if (_glfw.dbus.handle) + { + _glfwPlatformFreeModule(_glfw.dbus.handle); + _glfw.dbus.handle = NULL; + } +} diff --git a/src/posix_dbus.h b/src/posix_dbus.h new file mode 100644 index 00000000..58cc13ee --- /dev/null +++ b/src/posix_dbus.h @@ -0,0 +1,131 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +//Taken from DBus docs (https://dbus.freedesktop.org/doc/api/html/index.html) +typedef struct DBusConnection DBusConnection; +typedef struct DBusMessage DBusMessage; +typedef unsigned int dbus_bool_t; +typedef unsigned int dbus_uint32_t; + +enum DBusBusType +{ + DBUS_BUS_SESSION, + DBUS_BUS_SYSTEM, + DBUS_BUS_STARTER +}; + +struct DBusError +{ + const char* name; + const char* message; + unsigned int dummy1 : 1; + unsigned int dummy2 : 1; + unsigned int dummy3 : 1; + unsigned int dummy4 : 1; + unsigned int dummy5 : 1; + void* padding1; +}; + +struct DBusMessageIter +{ + void* dummy1; + void* dummy2; + dbus_uint32_t dummy3; + int dummy4, dummy5, dummy6, dummy7, dummy8, dummy9, dummy10, dummy11; + int pad1; + void* pad2; + void* pad3; +}; + +#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 +#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 +#define DBUS_TYPE_STRING (unsigned int)'s' +#define DBUS_TYPE_ARRAY (unsigned int)'a' +#define DBUS_TYPE_DICT_ENTRY (unsigned int)'e' +#define DBUS_TYPE_VARIANT (unsigned int)'v' +#define DBUS_TYPE_BOOLEAN (unsigned int)'b' +#define DBUS_TYPE_DOUBLE (unsigned int)'d' + +typedef void (* PFN_dbus_error_init)(struct DBusError*); +typedef dbus_bool_t (* PFN_dbus_error_is_set)(const struct DBusError*); +typedef void (* PFN_dbus_error_free)(struct DBusError*); +typedef void (* PFN_dbus_connection_unref)(DBusConnection*); +typedef dbus_bool_t (* PFN_dbus_connection_send)(DBusConnection*, DBusMessage*, dbus_uint32_t*); +typedef void (* PFN_dbus_connection_flush)(DBusConnection*); +typedef int (* PFN_dbus_bus_request_name)(DBusConnection*, const char*, unsigned int, struct DBusError*); +typedef DBusConnection* (* PFN_dbus_bus_get)(enum DBusBusType, struct DBusError*); +typedef void (* PFN_dbus_message_unref)(DBusMessage*); +typedef DBusMessage* (* PFN_dbus_message_new_signal)(const char*, const char*, const char*); +typedef void (* PFN_dbus_message_iter_init_append)(DBusMessage*, struct DBusMessageIter*); +typedef dbus_bool_t (* PFN_dbus_message_iter_append_basic)(struct DBusMessageIter*, int, const void*); +typedef dbus_bool_t (* PFN_dbus_message_iter_open_container)(struct DBusMessageIter*, int, const char*, struct DBusMessageIter*); +typedef dbus_bool_t (* PFN_dbus_message_iter_close_container)(struct DBusMessageIter*, struct DBusMessageIter*); + +#define dbus_error_init _glfw.dbus.error_init +#define dbus_error_is_set _glfw.dbus.error_is_set +#define dbus_error_free _glfw.dbus.error_free +#define dbus_connection_unref _glfw.dbus.connection_unref +#define dbus_connection_send _glfw.dbus.connection_send +#define dbus_connection_flush _glfw.dbus.connection_flush +#define dbus_bus_request_name _glfw.dbus.bus_request_name +#define dbus_bus_get _glfw.dbus.bus_get +#define dbus_message_unref _glfw.dbus.message_unref +#define dbus_message_new_signal _glfw.dbus.message_new_signal +#define dbus_message_iter_init_append _glfw.dbus.message_iter_init_append +#define dbus_message_iter_append_basic _glfw.dbus.message_iter_append_basic +#define dbus_message_iter_open_container _glfw.dbus.message_iter_open_container +#define dbus_message_iter_close_container _glfw.dbus.message_iter_close_container + +#define GLFW_POSIX_LIBRARY_DBUS_STATE _GLFWDBusPOSIX dbus; + +// POSIX-specific dbus data +// +typedef struct _GLFWDBusPOSIX +{ + void* handle; + + PFN_dbus_error_init error_init; + PFN_dbus_error_is_set error_is_set; + PFN_dbus_error_free error_free; + PFN_dbus_connection_unref connection_unref; + PFN_dbus_connection_send connection_send; + PFN_dbus_connection_flush connection_flush; + PFN_dbus_bus_request_name bus_request_name; + PFN_dbus_bus_get bus_get; + PFN_dbus_message_unref message_unref; + PFN_dbus_message_new_signal message_new_signal; + PFN_dbus_message_iter_init_append message_iter_init_append; + PFN_dbus_message_iter_append_basic message_iter_append_basic; + PFN_dbus_message_iter_open_container message_iter_open_container; + PFN_dbus_message_iter_close_container message_iter_close_container; + + DBusConnection* connection; + struct DBusError error; +} _GLFWDBusPOSIX; + +void _glfwInitDBusPOSIX(void); +void _glfwTerminateDBusPOSIX(void); diff --git a/src/win32_window.c b/src/win32_window.c index 5ae05e89..47c1adb0 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1618,7 +1618,28 @@ void _glfwSetWindowTaskbarProgressWin32(_GLFWwindow* window, const int progressS return; } - res = window->win32.TaskbarList->lpVtbl->SetProgressState(window->win32.TaskbarList, window->win32.handle, progressState); + int32_t winProgressState = 0; + switch(progressState) + { + case 1: + winProgressState = 0x1; + break; + case 2: + winProgressState = 0x2; + break; + case 3: + winProgressState = 0x4; + break; + case 4: + winProgressState = 0x8; + break; + case 0: + default: + winProgressState = 0x0; + break; + } + + res = window->win32.TaskbarList->lpVtbl->SetProgressState(window->win32.TaskbarList, window->win32.handle, winProgressState); if (res != S_OK) _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar progress state"); } diff --git a/src/wl_init.c b/src/wl_init.c index d4278c75..c6a4e8c4 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -504,6 +504,8 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) int _glfwInitWayland(void) { + _glfwInitDBusPOSIX(); + // These must be set before any failure checks _glfw.wl.keyRepeatTimerfd = -1; _glfw.wl.cursorTimerfd = -1; @@ -790,5 +792,7 @@ void _glfwTerminateWayland(void) close(_glfw.wl.cursorTimerfd); _glfw_free(_glfw.wl.clipboardString); + + _glfwTerminateDBusPOSIX(); } diff --git a/src/wl_window.c b/src/wl_window.c index 71d5a3e6..af7c0c14 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1901,8 +1901,94 @@ void _glfwSetWindowIconWayland(_GLFWwindow* window, void _glfwSetWindowTaskbarProgress(_GLFWwindow* window, const int progressState, int completed) { - _glfwInputError(GLFW_FEATURE_UNAVAILABLE, - "Wayland: The platform does not support setting the window taskbar progress"); + if(!_glfw.dbus.handle || !_glfw.dbus.connection) + return; + + //Signal signature: + //signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties) + + const dbus_bool_t progressVisible = (taskbarState != GLFW_TASKBAR_PROGRESS_NOPROGRESS); + const double progressValue = (double)completed / 100.0; + + struct DBusMessageIter args; + memset(&args, 0, sizeof(args)); + + //Get name of the running executable + char exeName[PATH_MAX]; + memset(exeName, 0, sizeof(char) * PATH_MAX); + if(readlink("/proc/self/exe", exeName, PATH_MAX) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable"); + return; + } + char* exeNameEnd = strchr(exeName, '\0'); + char* lastFound = strrchr(exeName, '/'); + if(!lastFound || !exeNameEnd) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable"); + return; + } + unsigned int exeNameLength = (exeNameEnd - lastFound) - 1; + + //Create our final desktop file uri + unsigned int desktopFileLength = strlen("application://") + exeNameLength + strlen(".desktop") + 1; + char desktopFile[desktopFileLength]; + memset(desktopFile, 0, sizeof(char) * desktopFileLength); + strcpy(desktopFile, "application://"); + memcpy(desktopFile + strlen("application://"), lastFound + 1, exeNameLength); + strcpy(desktopFile + strlen("application://") + (exeNameLength), ".desktop"); + desktopFile[desktopFileLength - 1] = '\0'; + + DBusMessage* msg = dbus_message_new_signal("/org/glfw", "com.canonical.Unity.LauncherEntry", "Update"); + if(!msg) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create new DBus message"); + return; + } + + dbus_message_iter_init_append(msg, &args); + + //Setup app_uri parameter + const char* desktopFileStr = desktopFile; + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &desktopFileStr); + + //Set properties parameter + struct DBusMessageIter sub1, sub2, sub3; + memset(&sub1, 0, sizeof(sub1)); + memset(&sub2, 0, sizeof(sub2)); + memset(&sub3, 0, sizeof(sub3)); + + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &sub1); + + //Set progress visible property + dbus_message_iter_open_container(&sub1, DBUS_TYPE_DICT_ENTRY, NULL, &sub2); + const char* progressVisibleStr = "progress-visible"; + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &progressVisibleStr); + dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "b", &sub3); + dbus_message_iter_append_basic(&sub3, DBUS_TYPE_BOOLEAN, &progressVisible); + dbus_message_iter_close_container(&sub2, &sub3); + dbus_message_iter_close_container(&sub1, &sub2); + + //Set progress value property + dbus_message_iter_open_container(&sub1, DBUS_TYPE_DICT_ENTRY, NULL, &sub2); + const char* progressValueStr = "progress"; + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &progressValueStr); + dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "d", &sub3); + dbus_message_iter_append_basic(&sub3, DBUS_TYPE_DOUBLE, &progressValue); + dbus_message_iter_close_container(&sub2, &sub3); + dbus_message_iter_close_container(&sub1, &sub2); + + dbus_message_iter_close_container(&args, &sub1); + + //Finally send the signal + unsigned int serial = 0; + if(!dbus_connection_send(_glfw.dbus.connection, msg, &serial)) + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to send DBus message"); + else + dbus_connection_flush(_glfw.dbus.connection); + + //Free the message + dbus_message_unref(msg); } void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) diff --git a/src/x11_init.c b/src/x11_init.c index 29c32794..d66824c0 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1320,6 +1320,8 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) int _glfwInitX11(void) { + _glfwInitDBusPOSIX(); + _glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocClassHint"); _glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints) @@ -1651,5 +1653,7 @@ void _glfwTerminateX11(void) close(_glfw.x11.emptyEventPipe[0]); close(_glfw.x11.emptyEventPipe[1]); } + + _glfwTerminateDBusPOSIX(); } diff --git a/src/x11_window.c b/src/x11_window.c index c2ab5dfa..bd842265 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2150,9 +2150,96 @@ void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* imag XFlush(_glfw.x11.display); } -void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* window, const int taskbarState, int completed) +void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* /*window*/, const int taskbarState, int completed) { - _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "X11: The platform does not support setting the window taskbar progress"); + if(!_glfw.dbus.handle || !_glfw.dbus.connection) + return; + + //Signal signature: + //signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties) + + const dbus_bool_t progressVisible = (taskbarState != GLFW_TASKBAR_PROGRESS_NOPROGRESS); + const double progressValue = (double)completed / 100.0; + + struct DBusMessageIter args; + memset(&args, 0, sizeof(args)); + + //Get name of the running executable + char exeName[PATH_MAX]; + memset(exeName, 0, sizeof(char) * PATH_MAX); + if(readlink("/proc/self/exe", exeName, PATH_MAX) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable"); + return; + } + char* exeNameEnd = strchr(exeName, '\0'); + char* lastFound = strrchr(exeName, '/'); + if(!lastFound || !exeNameEnd) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable"); + return; + } + unsigned int exeNameLength = (exeNameEnd - lastFound) - 1; + + //Create our final desktop file uri + unsigned int desktopFileLength = strlen("application://") + exeNameLength + strlen(".desktop") + 1; + char desktopFile[desktopFileLength]; + memset(desktopFile, 0, sizeof(char) * desktopFileLength); + strcpy(desktopFile, "application://"); + memcpy(desktopFile + strlen("application://"), lastFound + 1, exeNameLength); + strcpy(desktopFile + strlen("application://") + (exeNameLength), ".desktop"); + desktopFile[desktopFileLength - 1] = '\0'; + + DBusMessage* msg = dbus_message_new_signal("/org/glfw", "com.canonical.Unity.LauncherEntry", "Update"); + if(!msg) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create new DBus message"); + return; + } + + dbus_message_iter_init_append(msg, &args); + + //Setup app_uri parameter + const char* desktopFileStr = desktopFile; + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &desktopFileStr); + + //Set properties parameter + struct DBusMessageIter sub1, sub2, sub3; + memset(&sub1, 0, sizeof(sub1)); + memset(&sub2, 0, sizeof(sub2)); + memset(&sub3, 0, sizeof(sub3)); + + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &sub1); + + //Set progress visible property + dbus_message_iter_open_container(&sub1, DBUS_TYPE_DICT_ENTRY, NULL, &sub2); + const char* progressVisibleStr = "progress-visible"; + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &progressVisibleStr); + dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "b", &sub3); + dbus_message_iter_append_basic(&sub3, DBUS_TYPE_BOOLEAN, &progressVisible); + dbus_message_iter_close_container(&sub2, &sub3); + dbus_message_iter_close_container(&sub1, &sub2); + + //Set progress value property + dbus_message_iter_open_container(&sub1, DBUS_TYPE_DICT_ENTRY, NULL, &sub2); + const char* progressValueStr = "progress"; + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &progressValueStr); + dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "d", &sub3); + dbus_message_iter_append_basic(&sub3, DBUS_TYPE_DOUBLE, &progressValue); + dbus_message_iter_close_container(&sub2, &sub3); + dbus_message_iter_close_container(&sub1, &sub2); + + dbus_message_iter_close_container(&args, &sub1); + + //Finally send the signal + unsigned int serial = 0; + if(!dbus_connection_send(_glfw.dbus.connection, msg, &serial)) + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to send DBus message"); + else + dbus_connection_flush(_glfw.dbus.connection); + + //Free the message + dbus_message_unref(msg); } void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)