Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .ci/Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ brew 'osm-gps-map'
brew 'portmidi'
brew 'potrace'
brew 'pugixml'
brew 'sdl2'
brew 'sdl3'
brew 'shared-mime-info'
brew 'curl'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ jobs:
libraw-dev \
librsvg2-dev \
libsaxon-java \
libsdl2-dev \
libsdl3-dev \
libsecret-1-dev \
libsqlite3-dev \
libtiff5-dev \
Expand Down Expand Up @@ -275,7 +275,7 @@ jobs:
portmidi:p
potrace:p
pugixml:p
SDL2:p
sdl3:p
sqlite3:p
zlib:p
update: true
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
libpugixml-dev \
librsvg2-dev \
libsaxon-java \
libsdl2-dev \
libsdl3-dev \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nightly build will fail here because neither the Ubuntu 22.04 runner (on which we build the x86_64 AppImage) nor the Ubuntu 24.04 runner (on which we build the aarch64 AppImage) have libsdl3.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about removing this line and doing something like FindONNXRuntime.cmake, check system pkg -> prev built pkg -> last resort vendor a minimal SDL3?

libsecret-1-dev \
libsqlite3-dev \
libtiff5-dev \
Expand Down Expand Up @@ -264,7 +264,7 @@ jobs:
portmidi:p
potrace:p
pugixml:p
SDL2:p
sdl3:p
sqlite3:p
webp-pixbuf-loader:p
zlib:p
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ RUN sudo apt-get update && \
libpugixml-dev \
librsvg2-dev \
libsaxon-java \
libsdl2-dev \
libsdl3-dev \
libsecret-1-dev \
libsqlite3-dev \
libtiff5-dev \
Expand Down
2 changes: 1 addition & 1 deletion DefineOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ option(BUILD_CURVE_TOOLS "Build tools for generating base and tone curves" OFF)
option(USE_GMIC "Use G'MIC image processing framework." ON)
option(USE_ICU "Use ICU - International Components for Unicode." ON)
option(FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." OFF)
option(USE_SDL2 "Enable SDL2 support" ON)
option(USE_SDL3 "Enable SDL3 support" ON)

if (USE_OPENCL)
option(TESTBUILD_OPENCL_PROGRAMS "Test-compile OpenCL programs (needs LLVM and Clang 7+)" ON)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ Optional dependencies (minimum version):
Optional dependencies (no version requirement):
* colord, Xatom *(for fetching the system display color profile)*
* PortMidi *(for MIDI input support)*
* SDL2 *(for gamepad input support)*
* SDL3 *(for gamepad input support)*
* CUPS *(for print mode support)*
* OpenEXR *(for EXR import & export)*
* OpenJPEG *(for JPEG 2000 import & export)*
Expand Down
9 changes: 0 additions & 9 deletions packaging/macosx/3_make_hb_darktable_package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,6 @@ for dtSharedObj in $dtSharedObjDirs; do
cp -LR "$homebrewHome"/lib/"$dtSharedObj"/* "$dtResourcesDir"/lib/"$dtSharedObj"/
done

# Homebrew's `sdl2` is sdl2-compat, which dlopen()s libSDL3 at runtime —
# otool can't see that, so install_dependencies misses it; copy explicitly
sdl3Source="$homebrewHome/opt/sdl3/lib/libSDL3.dylib"
if [[ -f "$sdl3Source" ]]; then
cp -L "$sdl3Source" "$dtResourcesDir"/lib/libSDL3.dylib
install_name_tool -id "@executable_path/../Resources/lib/libSDL3.dylib" \
"$dtResourcesDir"/lib/libSDL3.dylib || true
fi

dtSharedObjDirs="libgphoto2 libgphoto2_port"
for dtSharedObj in $dtSharedObjDirs; do
mkdir "$dtResourcesDir"/lib/"$dtSharedObj"
Expand Down
2 changes: 1 addition & 1 deletion packaging/macosx/BUILD-ARM64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ How to make disk image with darktable application bundle (64 bit ARM only):
They will need some tuning, so before you install anything add this line to /opt/local/etc/macports/variants.conf:
+no_gnome +no_x11 +quartz -x11 -gnome -gfortran
Install required dependencies:
$ sudo port install exiv2 libgphoto2 gtk-osx-application-gtk3 lensfun librsvg openexr json-glib GraphicsMagick openjpeg webp libsecret pugixml osm-gps-map adwaita-icon-theme intltool iso-codes libomp gmic-lib libheif portmidi libsdl2 libavif libjxl potrace
$ sudo port install exiv2 libgphoto2 gtk-osx-application-gtk3 lensfun librsvg openexr json-glib GraphicsMagick openjpeg webp libsecret pugixml osm-gps-map adwaita-icon-theme intltool iso-codes libomp gmic-lib libheif portmidi libsdl3 libavif libjxl potrace
Clone darktable git repository (in this example into ~/src):
$ mkdir ~/src
$ cd ~/src
Expand Down
2 changes: 1 addition & 1 deletion packaging/macosx/BUILD.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ How to make disk image with darktable application bundle (64 bit Intel only):
$ curl -Lo ~/ports/x11/pango/files/patch-meson-examples-tests.diff https://github.com/macports/macports-ports/raw/26241fac142ac2bbe2a9071918ff20b301c66f4b/x11/pango/files/patch-meson-examples-tests.diff
$ portindex ~/ports

$ sudo port install exiv2 libgphoto2 gtk-osx-application-gtk3 lensfun librsvg openexr json-glib GraphicsMagick openjpeg webp libsecret pugixml osm-gps-map adwaita-icon-theme intltool iso-codes libomp gmic-lib libheif portmidi libsdl2 libavif libjxl potrace
$ sudo port install exiv2 libgphoto2 gtk-osx-application-gtk3 lensfun librsvg openexr json-glib GraphicsMagick openjpeg webp libsecret pugixml osm-gps-map adwaita-icon-theme intltool iso-codes libomp gmic-lib libheif portmidi libsdl3 libavif libjxl potrace
Clone darktable git repository (in this example into ~/src):
$ mkdir ~/src
$ cd ~/src
Expand Down
2 changes: 1 addition & 1 deletion packaging/nix/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
buildInputs =
with pkgs;
[
SDL2
SDL3
adwaita-icon-theme
cairo
curl
Expand Down
2 changes: 1 addition & 1 deletion packaging/windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The steps to build the darktable Windows executable and make the installer are a

* Install required and recommended dependencies for darktable:
```bash
pacman -S --needed mingw-w64-ucrt-x86_64-{libxslt,python-jsonschema,curl,drmingw,exiv2,gettext,gmic,graphicsmagick,gtk3,icu,imath,iso-codes,lcms2,lensfun,libavif,libarchive,libgphoto2,libheif,libjpeg-turbo,libjxl,libpng,libraw,librsvg,libsecret,libtiff,libwebp,libxml2,lua54,openexr,openjpeg2,osm-gps-map,portmidi,potrace,pugixml,SDL2,sqlite3,webp-pixbuf-loader,zlib}
pacman -S --needed mingw-w64-ucrt-x86_64-{libxslt,python-jsonschema,curl,drmingw,exiv2,gettext,gmic,graphicsmagick,gtk3,icu,imath,iso-codes,lcms2,lensfun,libavif,libarchive,libgphoto2,libheif,libjpeg-turbo,libjxl,libpng,libraw,librsvg,libsecret,libtiff,libwebp,libxml2,lua54,openexr,openjpeg2,osm-gps-map,portmidi,potrace,pugixml,sdl3,sqlite3,webp-pixbuf-loader,zlib}
```

* Install the optional tool for building an installer image (currently x64 only):
Expand Down
14 changes: 7 additions & 7 deletions src/libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ if(PortMidi_FOUND)
target_link_libraries (midi ${PortMidi_LIBRARY})
endif()

if(USE_SDL2)
find_package(SDL2)
if(SDL2_FOUND)
if(USE_SDL3)
find_package(SDL3)
if(SDL3_FOUND)
add_definitions("-DHAVE_SDL")
set(MODULES ${MODULES} gamepad)
add_library(gamepad MODULE "tools/gamepad.c")
if(TARGET SDL2::SDL2)
target_link_libraries(gamepad SDL2::SDL2)
if(TARGET SDL3::SDL3)
target_link_libraries(gamepad SDL3::SDL3)
else()
include_directories(${SDL2_INCLUDE_DIRS})
target_link_libraries(gamepad ${SDL2_LIBRARIES})
include_directories(${SDL3_INCLUDE_DIRS})
target_link_libraries(gamepad ${SDL3_LIBRARIES})
endif()
endif()
endif()
Expand Down
101 changes: 58 additions & 43 deletions src/libs/tools/gamepad.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ DT_MODULE(1)

#ifdef HAVE_SDL

#include <SDL.h>
#include <SDL3/SDL.h>

const char *name(dt_lib_module_t *self)
{
Expand All @@ -50,12 +50,13 @@ uint32_t container(dt_lib_module_t *self)
typedef struct dt_gamepad_device_t
{
dt_input_device_t id;
SDL_GameController *controller;
SDL_Gamepad *controller;
Uint32 timestamp;
int value[SDL_CONTROLLER_AXIS_MAX];
int location[SDL_CONTROLLER_AXIS_MAX];
int value[SDL_GAMEPAD_AXIS_COUNT];
int location[SDL_GAMEPAD_AXIS_COUNT];
} dt_gamepad_device_t;

// SDL3 face buttons are south/east/west/north but share indices 0–3 with former a/b/x/y
static const char *_button_names[]
= { N_("button a"), N_("button b"), N_("button x"), N_("button y"),
N_("button back"), N_("button guide"), N_("button start"),
Expand All @@ -65,10 +66,14 @@ static const char *_button_names[]
N_("left trigger"), N_("right trigger"),
NULL };

static const struct { const char *alias; guint key; } _button_aliases[]
= { { N_("button south"), 0 }, { N_("button east"), 1 }, { N_("button west"), 2 }, { N_("button north"), 3 },
{ NULL, 0 } };

static gchar *_key_to_string(const guint key,
const gboolean display)
{
const gchar *name = key < SDL_CONTROLLER_BUTTON_MAX + 2 ? _button_names[key] : N_("invalid gamepad button");
const gchar *name = key < SDL_GAMEPAD_BUTTON_COUNT + 2 ? _button_names[key] : N_("invalid gamepad button");
return g_strdup(display ? _(name) : name);
}

Expand All @@ -82,6 +87,13 @@ static gboolean _string_to_key(const gchar *string,
else
(*key)++;

for(int i = 0; _button_aliases[i].alias; i++)
if(!strcmp(_button_aliases[i].alias, string))
{
*key = _button_aliases[i].key;
return TRUE;
}

return FALSE;
}

Expand All @@ -93,7 +105,7 @@ static const char *_move_names[]
static gchar *_move_to_string(const guint move,
const gboolean display)
{
const gchar *name = move < SDL_CONTROLLER_AXIS_MAX + 4 /* diagonals */ ? _move_names[move] : N_("invalid gamepad axis");
const gchar *name = move < SDL_GAMEPAD_AXIS_COUNT + 4 /* diagonals */ ? _move_names[move] : N_("invalid gamepad axis");
return g_strdup(display ? _(name) : name);
}

Expand Down Expand Up @@ -126,7 +138,7 @@ static void _process_axis_timestep(dt_gamepad_device_t *gamepad,
if(timestamp > gamepad->timestamp)
{
Uint32 time = timestamp - gamepad->timestamp;
for(SDL_GameControllerAxis axis = SDL_CONTROLLER_AXIS_LEFTX; axis <= SDL_CONTROLLER_AXIS_RIGHTY; axis++)
for(SDL_GamepadAxis axis = SDL_GAMEPAD_AXIS_LEFTX; axis <= SDL_GAMEPAD_AXIS_RIGHTY; axis++)
{
if(abs(gamepad->value[axis]) > 4000)
gamepad->location[axis] += time * gamepad->value[axis];
Expand All @@ -145,7 +157,7 @@ static void _process_axis_and_send(dt_gamepad_device_t *gamepad,

for(int side = 0; side < 2; side++)
{
int stick = SDL_CONTROLLER_AXIS_LEFTX + 2 * side;
int stick = SDL_GAMEPAD_AXIS_LEFTX + 2 * side;

gdouble angle = gamepad->location[stick] / (0.001 + gamepad->location[stick + 1]);

Expand All @@ -170,7 +182,7 @@ static void _process_axis_and_send(dt_gamepad_device_t *gamepad,
}
else
{
gamepad->location[stick] += size * step_size * angle;
gamepad->location[stick] += size * step_size * angle;
dt_shortcut_move(gamepad->id, timestamp, stick + ((angle < 0) ? 5 : 4), size);
}
}
Expand All @@ -188,14 +200,14 @@ static gboolean _poll_devices(gpointer user_data)
dt_gamepad_device_t *gamepad = NULL;
SDL_JoystickID prev_which = -1;

while(SDL_PollEvent(&event) > 0 )
while(SDL_PollEvent(&event))
{
num_events++;

if(event.cbutton.which != prev_which)
if(event.gbutton.which != prev_which)
{
prev_which = event.cbutton.which;
SDL_GameController *controller = SDL_GameControllerFromInstanceID(prev_which);
prev_which = event.gbutton.which;
SDL_Gamepad *controller = SDL_GetGamepadFromID(prev_which);
gamepad = NULL;
for(GSList *gamepads = self->data; gamepads; gamepads = gamepads->next)
if(((dt_gamepad_device_t *)(gamepads->data))->controller == controller)
Expand All @@ -208,55 +220,55 @@ static gboolean _poll_devices(gpointer user_data)

switch(event.type)
{
case SDL_CONTROLLERBUTTONDOWN:
dt_print(DT_DEBUG_INPUT, "SDL button down event time %d id %d button %hhd state %hhd", event.cbutton.timestamp, event.cbutton.which, event.cbutton.button, event.cbutton.state);
_process_axis_and_send(gamepad, event.cbutton.timestamp);
dt_shortcut_key_press(gamepad->id, event.cbutton.timestamp, event.cbutton.button);
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
dt_print(DT_DEBUG_INPUT, "SDL button down event time %u id %u button %hhd down %hhd", (guint)SDL_NS_TO_MS(event.gbutton.timestamp), (guint)event.gbutton.which, event.gbutton.button, event.gbutton.down);
_process_axis_and_send(gamepad, SDL_NS_TO_MS(event.gbutton.timestamp));
dt_shortcut_key_press(gamepad->id, SDL_NS_TO_MS(event.gbutton.timestamp), event.gbutton.button);

break;
case SDL_CONTROLLERBUTTONUP:
dt_print(DT_DEBUG_INPUT, "SDL button up event time %d id %d button %hhd state %hhd", event.cbutton.timestamp, event.cbutton.which, event.cbutton.button, event.cbutton.state);
_process_axis_and_send(gamepad, event.cbutton.timestamp);
dt_shortcut_key_release(gamepad->id, event.cbutton.timestamp, event.cbutton.button);
case SDL_EVENT_GAMEPAD_BUTTON_UP:
dt_print(DT_DEBUG_INPUT, "SDL button up event time %u id %u button %hhd down %hhd", (guint)SDL_NS_TO_MS(event.gbutton.timestamp), (guint)event.gbutton.which, event.gbutton.button, event.gbutton.down);
_process_axis_and_send(gamepad, SDL_NS_TO_MS(event.gbutton.timestamp));
dt_shortcut_key_release(gamepad->id, SDL_NS_TO_MS(event.gbutton.timestamp), event.gbutton.button);
break;
case SDL_CONTROLLERAXISMOTION:
dt_print(DT_DEBUG_INPUT, "SDL axis event type %d time %d id %d axis %hhd value %hd", event.caxis.type, event.caxis.timestamp, event.caxis.which, event.caxis.axis, event.caxis.value);
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
dt_print(DT_DEBUG_INPUT, "SDL axis event type %u time %u id %u axis %hhd value %hd", event.gaxis.type, (guint)SDL_NS_TO_MS(event.gaxis.timestamp), (guint)event.gaxis.which, event.gaxis.axis, event.gaxis.value);

if(event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
if(event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)
{
int trigger = event.caxis.axis - SDL_CONTROLLER_AXIS_TRIGGERLEFT;
if(event.caxis.value / 10500 > gamepad->value[event.caxis.axis])
int trigger = event.gaxis.axis - SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
if(event.gaxis.value / 10500 > gamepad->value[event.gaxis.axis])
{
dt_shortcut_key_release(gamepad->id, event.cbutton.timestamp, SDL_CONTROLLER_BUTTON_MAX + trigger);
dt_shortcut_key_press(gamepad->id, event.cbutton.timestamp, SDL_CONTROLLER_BUTTON_MAX + trigger);
gamepad->value[event.caxis.axis] = event.caxis.value / 10500;
dt_shortcut_key_release(gamepad->id, SDL_NS_TO_MS(event.gbutton.timestamp), SDL_GAMEPAD_BUTTON_COUNT + trigger);
dt_shortcut_key_press(gamepad->id, SDL_NS_TO_MS(event.gbutton.timestamp), SDL_GAMEPAD_BUTTON_COUNT + trigger);
gamepad->value[event.gaxis.axis] = event.gaxis.value / 10500;
}
else if(event.caxis.value / 9500 < gamepad->value[event.caxis.axis])
else if(event.gaxis.value / 9500 < gamepad->value[event.gaxis.axis])
{
dt_shortcut_key_release(gamepad->id, event.cbutton.timestamp, SDL_CONTROLLER_BUTTON_MAX + trigger);
gamepad->value[event.caxis.axis] = event.caxis.value / 9500;
dt_shortcut_key_release(gamepad->id, SDL_NS_TO_MS(event.gbutton.timestamp), SDL_GAMEPAD_BUTTON_COUNT + trigger);
gamepad->value[event.gaxis.axis] = event.gaxis.value / 9500;
}
}
else
{
_process_axis_timestep(gamepad, event.caxis.timestamp);
gamepad->value[event.caxis.axis] = event.caxis.value;
_process_axis_timestep(gamepad, SDL_NS_TO_MS(event.gaxis.timestamp));
gamepad->value[event.gaxis.axis] = event.gaxis.value;
}
break;
case SDL_CONTROLLERDEVICEADDED:
case SDL_EVENT_GAMEPAD_ADDED:
break;
}
}

for(GSList *gamepads = self->data; gamepads; gamepads = gamepads->next) _process_axis_and_send(gamepads->data, SDL_GetTicks());

if(num_events) dt_print(DT_DEBUG_INPUT, "sdl num_events: %d time: %u", num_events, SDL_GetTicks());
if(num_events) dt_print(DT_DEBUG_INPUT, "sdl num_events: %d time: %u", num_events, (guint)SDL_GetTicks());
return G_SOURCE_CONTINUE;
}

static void _gamepad_open_devices(dt_lib_module_t *self)
{
if(SDL_Init(SDL_INIT_GAMECONTROLLER))
if(!SDL_Init(SDL_INIT_GAMEPAD))
{
dt_print(DT_DEBUG_ALWAYS, "[_gamepad_open_devices] ERROR initialising SDL");
return;
Expand All @@ -266,22 +278,24 @@ static void _gamepad_open_devices(dt_lib_module_t *self)

dt_input_device_t id = dt_register_input_driver(self, &_driver_definition);

for(int i = 0; i < SDL_NumJoysticks() && i < 10; i++)
int count = 0;
SDL_JoystickID *ids = SDL_GetGamepads(&count);
if(ids)
{
if(SDL_IsGameController(i))
for(int i = 0; i < count && i < 10; i++)
{
SDL_GameController *controller = SDL_GameControllerOpen(i);
SDL_Gamepad *controller = SDL_OpenGamepad(ids[i]);

if(!controller)
{
dt_print(DT_DEBUG_ALWAYS, "[_gamepad_open_devices] ERROR opening game controller '%s'",
SDL_GameControllerNameForIndex(i));
SDL_GetGamepadNameForID(ids[i]));
continue;
}
else
{
dt_print(DT_DEBUG_ALWAYS, "[_gamepad_open_devices] opened game controller '%s'",
SDL_GameControllerNameForIndex(i));
SDL_GetGamepadNameForID(ids[i]));
}

dt_gamepad_device_t *gamepad = g_malloc0(sizeof(dt_gamepad_device_t));
Expand All @@ -291,6 +305,7 @@ static void _gamepad_open_devices(dt_lib_module_t *self)

self->data = g_slist_append(self->data, gamepad);
}
SDL_free(ids);
}
if(self->data)
{
Expand All @@ -301,7 +316,7 @@ static void _gamepad_open_devices(dt_lib_module_t *self)

static void _gamepad_device_free(dt_gamepad_device_t *gamepad)
{
SDL_GameControllerClose(gamepad->controller);
SDL_CloseGamepad(gamepad->controller);

g_free(gamepad);
}
Expand Down
Loading