Skip to content

Commit 3f2226a

Browse files
GamesTrapslouken
authored andcommitted
Add progress bar support for Linux
1 parent e90f7ac commit 3f2226a

File tree

9 files changed

+215
-0
lines changed

9 files changed

+215
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
17361736
sdl_sources(
17371737
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_dbus.c"
17381738
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_system_theme.c"
1739+
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.c"
17391740
)
17401741
endif()
17411742

docs/README-wayland.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ encounter limitations or behavior that is different from other windowing systems
5959
`SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your
6060
application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`.
6161

62+
### The application progress bar can't be set via ```SDL_SetWindowProgressState()``` or ```SDL_SetWindowProgressValue()```
63+
64+
- Only some Desktop Environemnts support the underlying API. Known compatible DEs: Unity, KDE
65+
- The underlying API requires a desktop entry file, aka a `.desktop` file.
66+
Please see the [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for
67+
more information on the format of this file. Note that if your application manually sets the application ID via the
68+
`SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your
69+
application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`.
70+
6271
### Keyboard grabs don't work when running under XWayland
6372

6473
- On GNOME based desktops, the dconf setting `org/gnome/mutter/wayland/xwayland-allow-grabs` must be enabled.

src/core/linux/SDL_dbus.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static bool LoadDBUSSyms(void)
6868
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal);
6969
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path);
7070
SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call);
71+
SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *), message_new_signal);
7172
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args);
7273
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist);
7374
SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append);

src/core/linux/SDL_dbus.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct SDL_DBusContext
6767
dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *);
6868
dbus_bool_t (*message_has_path)(DBusMessage *, const char *);
6969
DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
70+
DBusMessage *(*message_new_signal)(const char *, const char *, const char *);
7071
dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
7172
dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
7273
void (*message_iter_init_append)(DBusMessage *, DBusMessageIter *);

src/core/linux/SDL_progressbar.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
Simple DirectMedia Layer
3+
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
2. Altered source versions must be plainly marked as such, and must not be
18+
misrepresented as being the original software.
19+
3. This notice may not be removed or altered from any source distribution.
20+
*/
21+
#include "SDL_progressbar.h"
22+
#include "SDL_internal.h"
23+
24+
#include "SDL_dbus.h"
25+
26+
#ifdef SDL_USE_LIBDBUS
27+
28+
#include <unistd.h>
29+
30+
#include "../unix/SDL_appid.h"
31+
32+
#define UnityLauncherAPI_DBUS_INTERFACE "com.canonical.Unity.LauncherEntry"
33+
#define UnityLauncherAPI_DBUS_SIGNAL "Update"
34+
35+
static char *GetDBUSObjectPath()
36+
{
37+
char *app_id = SDL_strdup(SDL_GetAppID());
38+
39+
if (!app_id) {
40+
return NULL;
41+
}
42+
43+
// Sanitize exe_name to make it a legal D-Bus path element
44+
for (char *p = app_id; *p; ++p) {
45+
if (!SDL_isalnum(*p)) {
46+
*p = '_';
47+
}
48+
}
49+
50+
// Ensure it starts with a letter or underscore
51+
if (!SDL_isalpha(app_id[0]) && app_id[0] != '_') {
52+
SDL_memmove(app_id + 1, app_id, SDL_strlen(app_id) + 1);
53+
app_id[0] = '_';
54+
}
55+
56+
// Create full path
57+
char path[1024];
58+
SDL_snprintf(path, sizeof(path), "/org/libsdl/%s_%d", app_id, getpid());
59+
60+
SDL_free(app_id);
61+
62+
return SDL_strdup(path);
63+
}
64+
65+
static char *GetAppDesktopPath()
66+
{
67+
const char *desktop_suffix = ".desktop";
68+
const char *app_id = SDL_GetAppID();
69+
const size_t desktop_path_total_length = SDL_strlen(app_id) + SDL_strlen(desktop_suffix) + 1;
70+
char *desktop_path = (char *)SDL_malloc(desktop_path_total_length);
71+
if (!desktop_path) {
72+
return NULL;
73+
}
74+
*desktop_path = '\0';
75+
SDL_strlcat(desktop_path, app_id, desktop_path_total_length);
76+
SDL_strlcat(desktop_path, desktop_suffix, desktop_path_total_length);
77+
78+
return desktop_path;
79+
}
80+
81+
static int ShouldShowProgress(SDL_ProgressState progressState)
82+
{
83+
if (progressState == SDL_PROGRESS_STATE_INVALID ||
84+
progressState == SDL_PROGRESS_STATE_NONE) {
85+
return 0;
86+
}
87+
88+
// Unity LauncherAPI only supports "normal" display of progress
89+
return 1;
90+
}
91+
92+
bool DBUS_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window)
93+
{
94+
// Signal signature:
95+
// signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties)
96+
97+
SDL_DBusContext *dbus = SDL_DBus_GetContext();
98+
99+
if (!dbus || !dbus->session_conn) {
100+
return false;
101+
}
102+
103+
char *objectPath = GetDBUSObjectPath();
104+
if (!objectPath) {
105+
return false;
106+
}
107+
108+
DBusMessage *msg = dbus->message_new_signal(objectPath, UnityLauncherAPI_DBUS_INTERFACE, UnityLauncherAPI_DBUS_SIGNAL);
109+
if (!msg) {
110+
SDL_free(objectPath);
111+
return false;
112+
}
113+
114+
char *desktop_path = GetAppDesktopPath();
115+
if (!desktop_path) {
116+
dbus->message_unref(msg);
117+
SDL_free(objectPath);
118+
return false;
119+
}
120+
121+
const char *progress_visible_str = "progress-visible";
122+
const char *progress_str = "progress";
123+
int dbus_type_boolean_str = DBUS_TYPE_BOOLEAN;
124+
int dbus_type_double_str = DBUS_TYPE_DOUBLE;
125+
126+
const int progress_visible = ShouldShowProgress(window->progress_state);
127+
double progress = (double)window->progress_value;
128+
129+
DBusMessageIter args, props;
130+
dbus->message_iter_init_append(msg, &args);
131+
dbus->message_iter_append_basic(&args, DBUS_TYPE_STRING, &desktop_path); // Setup app_uri paramter
132+
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &props); // Setup properties parameter
133+
DBusMessageIter key_it, value_it;
134+
// Set progress visible property
135+
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
136+
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_visible_str); // Append progress-visible key data
137+
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, (const char *)&dbus_type_boolean_str, &value_it);
138+
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_BOOLEAN, &progress_visible); // Append progress-visible value data
139+
dbus->message_iter_close_container(&key_it, &value_it);
140+
dbus->message_iter_close_container(&props, &key_it);
141+
// Set progress value property
142+
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
143+
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_str); // Append progress key data
144+
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, (const char *)&dbus_type_double_str, &value_it);
145+
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_DOUBLE, &progress); // Append progress value data
146+
dbus->message_iter_close_container(&key_it, &value_it);
147+
dbus->message_iter_close_container(&props, &key_it);
148+
dbus->message_iter_close_container(&args, &props);
149+
150+
dbus->connection_send(dbus->session_conn, msg, NULL);
151+
152+
SDL_free(desktop_path);
153+
dbus->message_unref(msg);
154+
SDL_free(objectPath);
155+
156+
return true;
157+
}
158+
159+
#endif // SDL_USE_LIBDBUS

src/core/linux/SDL_progressbar.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Simple DirectMedia Layer
3+
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
2. Altered source versions must be plainly marked as such, and must not be
18+
misrepresented as being the original software.
19+
3. This notice may not be removed or altered from any source distribution.
20+
*/
21+
22+
#ifndef SDL_prograssbar_h_
23+
#define SDL_prograssbar_h_
24+
25+
#include "../../video/SDL_sysvideo.h"
26+
#include "SDL_internal.h"
27+
28+
extern bool DBUS_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window);
29+
30+
#endif // SDL_prograssbar_h_

src/video/SDL_video.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,6 +2266,12 @@ static void SDL_FinishWindowCreation(SDL_Window *window, SDL_WindowFlags flags)
22662266
SDL_ShowWindow(window);
22672267
}
22682268
}
2269+
2270+
#if defined(SDL_PLATFORM_LINUX)
2271+
// On Linux the progress state is persisted throughout multiple program runs, so reset state on window creation
2272+
SDL_SetWindowProgressState(window, SDL_PROGRESS_STATE_NONE);
2273+
SDL_SetWindowProgressValue(window, 0.0f);
2274+
#endif
22692275
}
22702276

22712277
static bool SDL_ContextNotSupported(const char *name)

src/video/wayland/SDL_waylandvideo.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#ifdef SDL_VIDEO_DRIVER_WAYLAND
2525

2626
#include "../../core/linux/SDL_system_theme.h"
27+
#include "../../core/linux/SDL_progressbar.h"
2728
#include "../../events/SDL_events_c.h"
2829

2930
#include "SDL_waylandclipboard.h"
@@ -629,6 +630,9 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
629630
device->DestroyWindow = Wayland_DestroyWindow;
630631
device->SetWindowHitTest = Wayland_SetWindowHitTest;
631632
device->FlashWindow = Wayland_FlashWindow;
633+
#ifdef SDL_USE_LIBDBUS
634+
device->ApplyWindowProgress = DBUS_ApplyWindowProgress;
635+
#endif // SDL_USE_LIBDBUS
632636
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
633637
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
634638
device->SyncWindow = Wayland_SyncWindow;

src/video/x11/SDL_x11video.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <unistd.h> // For getpid() and readlink()
2626

2727
#include "../../core/linux/SDL_system_theme.h"
28+
#include "../../core/linux/SDL_progressbar.h"
2829
#include "../../events/SDL_keyboard_c.h"
2930
#include "../../events/SDL_mouse_c.h"
3031
#include "../SDL_pixels_c.h"
@@ -204,6 +205,9 @@ static SDL_VideoDevice *X11_CreateDevice(void)
204205
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
205206
device->UpdateWindowShape = X11_UpdateWindowShape;
206207
device->FlashWindow = X11_FlashWindow;
208+
#ifdef SDL_USE_LIBDBUS
209+
device->ApplyWindowProgress = DBUS_ApplyWindowProgress;
210+
#endif // SDL_USE_LIBDBUS
207211
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
208212
device->SetWindowFocusable = X11_SetWindowFocusable;
209213
device->SyncWindow = X11_SyncWindow;

0 commit comments

Comments
 (0)