mirror of https://github.com/libsdl-org/SDL.git
Compare commits
28 Commits
51dc29359a
...
d6d81eeac3
| Author | SHA1 | Date |
|---|---|---|
|
|
d6d81eeac3 | |
|
|
038a3806eb | |
|
|
e6c2649afc | |
|
|
e80d084766 | |
|
|
6aedc488d3 | |
|
|
81e3066303 | |
|
|
7d9fd48557 | |
|
|
e68f5ca99a | |
|
|
051ce0ff89 | |
|
|
5fcc83d93b | |
|
|
1bd5110ff0 | |
|
|
eb04219efe | |
|
|
d06b6e42d2 | |
|
|
c19ad189dc | |
|
|
7882e60f0e | |
|
|
e4e29b8601 | |
|
|
d7939abf42 | |
|
|
390fe65323 | |
|
|
ca9b7c8ea3 | |
|
|
c04624972e | |
|
|
b4ac5f43f5 | |
|
|
3896b1b3f4 | |
|
|
efed3c63b3 | |
|
|
d6bae53341 | |
|
|
f62c982bcf | |
|
|
b833c618a9 | |
|
|
9dc6c6e4d4 | |
|
|
4f79d4c94d |
|
|
@ -9,6 +9,7 @@ versionfname = include/SDL3/SDL_version.h
|
|||
versionmajorregex = \A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z
|
||||
versionminorregex = \A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z
|
||||
versionmicroregex = \A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z
|
||||
apipropertyregex = \A\s*\#\s*define\s+SDL_PROP_
|
||||
selectheaderregex = \ASDL.*?\.h\Z
|
||||
projecturl = https://libsdl.org/
|
||||
wikiurl = https://wiki.libsdl.org
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ my $wikisubdir = '';
|
|||
my $incsubdir = 'include';
|
||||
my $readmesubdir = undef;
|
||||
my $apiprefixregex = undef;
|
||||
my $apipropertyregex = undef;
|
||||
my $versionfname = 'include/SDL_version.h';
|
||||
my $versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z';
|
||||
my $versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z';
|
||||
|
|
@ -110,6 +111,7 @@ if (defined $optionsfname) {
|
|||
$srcpath = $val, next if $key eq 'srcpath';
|
||||
$wikipath = $val, next if $key eq 'wikipath';
|
||||
$apiprefixregex = $val, next if $key eq 'apiprefixregex';
|
||||
$apipropertyregex = $val, next if $key eq 'apipropertyregex';
|
||||
$projectfullname = $val, next if $key eq 'projectfullname';
|
||||
$projectshortname = $val, next if $key eq 'projectshortname';
|
||||
$wikisubdir = $val, next if $key eq 'wikisubdir';
|
||||
|
|
@ -1366,7 +1368,7 @@ while (my $d = readdir(DH)) {
|
|||
# update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
|
||||
|
||||
# At Sam's request, don't list property defines with functions. (See #9440)
|
||||
my $is_property = /\A\s*\#\s*define\s+SDL_PROP_/;
|
||||
my $is_property = (defined $apipropertyregex) ? /$apipropertyregex/ : 0;
|
||||
if (!$is_property) {
|
||||
if ($blank_lines > 0) {
|
||||
while ($blank_lines > 0) {
|
||||
|
|
|
|||
|
|
@ -1021,7 +1021,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream);
|
|||
/**
|
||||
* Query an audio stream for its currently-bound device.
|
||||
*
|
||||
* This reports the audio device that an audio stream is currently bound to.
|
||||
* This reports the logical audio device that an audio stream is currently
|
||||
* bound to.
|
||||
*
|
||||
* If not bound, or invalid, this returns zero, which is not a valid device
|
||||
* ID.
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@ typedef enum SDL_EventType
|
|||
/* 0x201 was SDL_SYSWMEVENT, reserve the number for sdl2-compat */
|
||||
SDL_EVENT_WINDOW_SHOWN = 0x202, /**< Window has been shown */
|
||||
SDL_EVENT_WINDOW_HIDDEN, /**< Window has been hidden */
|
||||
SDL_EVENT_WINDOW_EXPOSED, /**< Window has been exposed and should be redrawn, and can be redrawn directly from event watchers for this event */
|
||||
SDL_EVENT_WINDOW_EXPOSED, /**< Window has been exposed and should be redrawn, and can be redrawn directly from event watchers for this event.
|
||||
data1 is 1 for live-resize expose events, 0 otherwise. */
|
||||
SDL_EVENT_WINDOW_MOVED, /**< Window has been moved to data1, data2 */
|
||||
SDL_EVENT_WINDOW_RESIZED, /**< Window has been resized to data1xdata2 */
|
||||
SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED,/**< The pixel size of the window has changed to data1xdata2 */
|
||||
|
|
|
|||
|
|
@ -823,7 +823,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS8(SDL_IOStream *src, Sint8 *value);
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -846,7 +846,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU16LE(SDL_IOStream *src, Uint16 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -869,7 +869,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -892,7 +892,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU16BE(SDL_IOStream *src, Uint16 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -915,7 +915,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -938,7 +938,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU32LE(SDL_IOStream *src, Uint32 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -961,7 +961,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -984,7 +984,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU32BE(SDL_IOStream *src, Uint32 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -1007,7 +1007,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -1030,7 +1030,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU64LE(SDL_IOStream *src, Uint64 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -1053,7 +1053,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
@ -1076,7 +1076,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_ReadU64BE(SDL_IOStream *src, Uint64 *value)
|
|||
*
|
||||
* \param src the stream from which to read data.
|
||||
* \param value a pointer filled in with the data read.
|
||||
* \returns true on successful write or false on failure; call SDL_GetError()
|
||||
* \returns true on successful read or false on failure; call SDL_GetError()
|
||||
* for more information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
|
|
|
|||
|
|
@ -1415,6 +1415,7 @@ static int SDLCALL RecordingAudioThread(void *devicep) // thread entry point
|
|||
typedef struct CountAudioDevicesData
|
||||
{
|
||||
int devs_seen;
|
||||
int devs_skipped;
|
||||
const int num_devices;
|
||||
SDL_AudioDeviceID *result;
|
||||
const bool recording;
|
||||
|
|
@ -1430,8 +1431,14 @@ static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table
|
|||
const bool isphysical = !!(devid & (1<<1));
|
||||
if (isphysical && (devid_recording == data->recording)) {
|
||||
SDL_assert(data->devs_seen < data->num_devices);
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the device_hash_lock here.
|
||||
const bool zombie = SDL_GetAtomicInt(&device->zombie) != 0;
|
||||
if (zombie) {
|
||||
data->devs_skipped++;
|
||||
} else {
|
||||
data->result[data->devs_seen++] = devid;
|
||||
}
|
||||
}
|
||||
return true; // keep iterating.
|
||||
}
|
||||
|
||||
|
|
@ -1446,10 +1453,11 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording)
|
|||
num_devices = SDL_GetAtomicInt(recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count);
|
||||
result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID));
|
||||
if (result) {
|
||||
CountAudioDevicesData data = { 0, num_devices, result, recording };
|
||||
CountAudioDevicesData data = { 0, 0, num_devices, result, recording };
|
||||
SDL_IterateHashTable(current_audio.device_hash, CountAudioDevices, &data);
|
||||
SDL_assert(data.devs_seen == num_devices);
|
||||
result[data.devs_seen] = 0; // null-terminated.
|
||||
SDL_assert((data.devs_seen + data.devs_skipped) == num_devices);
|
||||
num_devices = data.devs_seen; // might be less if we skipped any.
|
||||
result[num_devices] = 0; // null-terminated.
|
||||
}
|
||||
}
|
||||
SDL_UnlockRWLock(current_audio.device_hash_lock);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_alsa_audio.h"
|
||||
#include "../../core/linux/SDL_udev.h"
|
||||
|
||||
#if SDL_ALSA_DEBUG
|
||||
#define LOGDEBUG(...) SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "ALSA: " __VA_ARGS__)
|
||||
|
|
@ -348,28 +349,25 @@ static char *get_pcm_str(void *handle)
|
|||
// This function waits until it is possible to write a full sound buffer
|
||||
static bool ALSA_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const int fulldelay = (int) ((((Uint64) device->sample_frames) * 1000) / device->spec.freq);
|
||||
const int delay = SDL_max(fulldelay, 10);
|
||||
const int sample_frames = device->sample_frames;
|
||||
const int fulldelay = (int) ((((Uint64) sample_frames) * 1000) / device->spec.freq);
|
||||
const int delay = SDL_clamp(fulldelay, 1, 5);
|
||||
|
||||
while (!SDL_GetAtomicInt(&device->shutdown)) {
|
||||
const int rc = ALSA_snd_pcm_wait(device->hidden->pcm, delay);
|
||||
if (rc < 0 && (rc != -EAGAIN)) {
|
||||
const int rc = ALSA_snd_pcm_avail(device->hidden->pcm);
|
||||
if (rc < 0) {
|
||||
const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0);
|
||||
if (status < 0) {
|
||||
// Hmm, not much we can do - abort
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA: snd_pcm_wait failed (unrecoverable): %s", ALSA_snd_strerror(rc));
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA wait failed (unrecoverable): %s", ALSA_snd_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
break; // ready to go!
|
||||
if (rc >= sample_frames) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Timed out! Make sure we aren't shutting down and then wait again.
|
||||
SDL_Delay(delay);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -431,8 +429,11 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
|||
SDL_assert((buflen % frame_size) == 0);
|
||||
|
||||
const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm);
|
||||
const int total_frames = SDL_min(buflen / frame_size, total_available);
|
||||
if (total_available == 0) {
|
||||
return 0; // go back to WaitDevice and try again.
|
||||
}
|
||||
|
||||
const int total_frames = SDL_min(buflen / frame_size, total_available);
|
||||
const int rc = ALSA_snd_pcm_readi(device->hidden->pcm, buffer, total_frames);
|
||||
|
||||
SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it!
|
||||
|
|
@ -1445,6 +1446,65 @@ static int SDLCALL ALSA_HotplugThread(void *arg)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
|
||||
static bool udev_initialized;
|
||||
|
||||
static void ALSA_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
|
||||
{
|
||||
if (!devpath) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (udev_type) {
|
||||
case SDL_UDEV_DEVICEADDED:
|
||||
ALSA_HotplugIteration(NULL, NULL);
|
||||
break;
|
||||
|
||||
case SDL_UDEV_DEVICEREMOVED:
|
||||
ALSA_HotplugIteration(NULL, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ALSA_start_udev()
|
||||
{
|
||||
udev_initialized = SDL_UDEV_Init();
|
||||
if (udev_initialized) {
|
||||
// Set up the udev callback
|
||||
if (!SDL_UDEV_AddCallback(ALSA_udev_callback)) {
|
||||
SDL_UDEV_Quit();
|
||||
udev_initialized = false;
|
||||
}
|
||||
}
|
||||
return udev_initialized;
|
||||
}
|
||||
|
||||
static void ALSA_stop_udev()
|
||||
{
|
||||
if (udev_initialized) {
|
||||
SDL_UDEV_DelCallback(ALSA_udev_callback);
|
||||
SDL_UDEV_Quit();
|
||||
udev_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool ALSA_start_udev()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ALSA_stop_udev()
|
||||
{
|
||||
}
|
||||
|
||||
#endif // SDL_USE_LIBUDEV
|
||||
|
||||
static void ALSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
|
||||
{
|
||||
ALSA_guess_device_prefix();
|
||||
|
|
@ -1460,12 +1520,14 @@ static void ALSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevi
|
|||
*default_recording = SDL_AddAudioDevice(/*recording=*/true, "ALSA default recording device", NULL, (void *)&default_recording_handle);
|
||||
}
|
||||
|
||||
if (!ALSA_start_udev()) {
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
SDL_SetAtomicInt(&ALSA_hotplug_shutdown, 0);
|
||||
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL);
|
||||
// if the thread doesn't spin, oh well, you just don't get further hotplug events.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void ALSA_DeinitializeStart(void)
|
||||
{
|
||||
|
|
@ -1479,6 +1541,7 @@ static void ALSA_DeinitializeStart(void)
|
|||
ALSA_hotplug_thread = NULL;
|
||||
}
|
||||
#endif
|
||||
ALSA_stop_udev();
|
||||
|
||||
// Shutting down! Clean up any data we've gathered.
|
||||
for (dev = hotplug_devices; dev; dev = next) {
|
||||
|
|
|
|||
|
|
@ -371,10 +371,10 @@ static const SDL_Scancode xfree86_scancode_table2[] = {
|
|||
/* 188, 0x0bc */ SDL_SCANCODE_F18, // XF86Launch9
|
||||
/* 189, 0x0bd */ SDL_SCANCODE_F19, // NoSymbol
|
||||
/* 190, 0x0be */ SDL_SCANCODE_F20, // XF86AudioMicMute
|
||||
/* 191, 0x0bf */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadToggle
|
||||
/* 192, 0x0c0 */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadOn
|
||||
/* 193, 0x0c1 */ SDL_SCANCODE_UNKNOWN, // XF86TouchpadOff
|
||||
/* 194, 0x0c2 */ SDL_SCANCODE_UNKNOWN, // NoSymbol
|
||||
/* 191, 0x0bf */ SDL_SCANCODE_F21, // XF86TouchpadToggle
|
||||
/* 192, 0x0c0 */ SDL_SCANCODE_F22, // XF86TouchpadOn
|
||||
/* 193, 0x0c1 */ SDL_SCANCODE_F23, // XF86TouchpadOff
|
||||
/* 194, 0x0c2 */ SDL_SCANCODE_F24, // NoSymbol
|
||||
/* 195, 0x0c3 */ SDL_SCANCODE_MODE, // Mode_switch
|
||||
/* 196, 0x0c4 */ SDL_SCANCODE_UNKNOWN, // NoSymbol
|
||||
/* 197, 0x0c5 */ SDL_SCANCODE_UNKNOWN, // NoSymbol
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ extern "C" {
|
|||
#define DETACH_KERNEL_DRIVER
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:5287) /* operands are different enum types */
|
||||
#endif
|
||||
|
||||
/* Uncomment to enable the retrieval of Usage and Usage Page in
|
||||
hid_enumerate(). Warning, on platforms different from FreeBSD
|
||||
this is very invasive as it requires the detach
|
||||
|
|
@ -2144,6 +2149,10 @@ uint16_t get_usb_code_for_current_locale(void)
|
|||
return 0x0;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -841,6 +841,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
|
|||
case 80:
|
||||
case 81:
|
||||
case 85:
|
||||
case 105:
|
||||
// Vader series of controllers have C/Z buttons
|
||||
SDL_strlcat(mapping_string, "misc2:b15,misc3:b16,", sizeof(mapping_string));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -95,19 +95,18 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
|
|||
{
|
||||
SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context;
|
||||
|
||||
for (int attempt = 0; ctx->deviceID == 0 && attempt < 3; ++attempt) {
|
||||
// Detecting the Vader 2 can take over 1000 read retries, so be generous here
|
||||
for (int attempt = 0; ctx->deviceID == 0 && attempt < 30; ++attempt) {
|
||||
const Uint8 request[] = { FLYDIGI_CMD_REPORT_ID, FLYDIGI_GET_INFO_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int size = SDL_hid_write(device->dev, request, sizeof(request));
|
||||
if (size < 0) {
|
||||
break;
|
||||
}
|
||||
// This write will occasionally return -1, so ignore failure here and try again
|
||||
(void)SDL_hid_write(device->dev, request, sizeof(request));
|
||||
|
||||
// Read the reply
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
SDL_Delay(1);
|
||||
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0);
|
||||
int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0);
|
||||
if (size < 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -211,6 +210,7 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
|
|||
ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER_PRO4_WIRED_NS;
|
||||
break;
|
||||
case 85:
|
||||
case 105:
|
||||
HIDAPI_SetDeviceName(device, "Flydigi Vader 4 Pro");
|
||||
ctx->has_cz = true;
|
||||
ctx->sensors_supported = true;
|
||||
|
|
@ -218,6 +218,7 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
|
|||
ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER_PRO4_WIRED_NS;
|
||||
break;
|
||||
default:
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Unknown FlyDigi controller with ID %d, name '%s'", ctx->deviceID, device->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -979,7 +979,7 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
|
|||
/* Stick calibration values are 12-bits each and are packed by bit
|
||||
* For whatever reason the fields are in a different order for each stick
|
||||
* Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
|
||||
* Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
|
||||
* Right: X-Center, Y-Center, X-Max, Y-Max, X-Min, Y-Min
|
||||
*/
|
||||
|
||||
// Left stick
|
||||
|
|
@ -993,10 +993,10 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
|
|||
// Right stick
|
||||
ctx->m_StickCalData[1].axis[0].sCenter = ((pRightStickCal[1] << 8) & 0xF00) | pRightStickCal[0]; // X Axis center
|
||||
ctx->m_StickCalData[1].axis[1].sCenter = (pRightStickCal[2] << 4) | (pRightStickCal[1] >> 4); // Y Axis center
|
||||
ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis min below center
|
||||
ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis min below center
|
||||
ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis max above center
|
||||
ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis max above center
|
||||
ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis max above center
|
||||
ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis max above center
|
||||
ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis min below center
|
||||
ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis min below center
|
||||
|
||||
// Filter out any values that were uninitialized (0xFFF) in the SPI read
|
||||
for (stick = 0; stick < 2; ++stick) {
|
||||
|
|
@ -1109,14 +1109,17 @@ static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, i
|
|||
{
|
||||
sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter;
|
||||
|
||||
if (sRawValue >= 0) {
|
||||
if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
|
||||
ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
|
||||
}
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16);
|
||||
} else {
|
||||
if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
|
||||
ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
|
||||
}
|
||||
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
|
||||
|
|
@ -1126,14 +1129,17 @@ static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nSt
|
|||
|
||||
sRawValue -= usJoystickCenter;
|
||||
|
||||
if (sRawValue >= 0) {
|
||||
if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) {
|
||||
ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue;
|
||||
}
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16);
|
||||
} else {
|
||||
if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) {
|
||||
ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue;
|
||||
}
|
||||
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
|
||||
return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
|
||||
|
|
|
|||
|
|
@ -820,6 +820,35 @@ static SceGxmTextureAddrMode TranslateAddressMode(SDL_TextureAddressMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
static void ClampCliprectToViewport(SDL_Rect *clip, const SDL_Rect *viewport)
|
||||
{
|
||||
int max_x_v, max_y_v, max_x_c, max_y_c;
|
||||
|
||||
if (clip->x < 0) {
|
||||
clip->w += clip->x;
|
||||
clip->x = 0;
|
||||
}
|
||||
|
||||
if (clip->y < 0) {
|
||||
clip->h += clip->y;
|
||||
clip->y = 0;
|
||||
}
|
||||
|
||||
max_x_c = clip->x + clip->w;
|
||||
max_y_c = clip->y + clip->h;
|
||||
|
||||
max_x_v = viewport->x + viewport->w;
|
||||
max_y_v = viewport->y + viewport->h;
|
||||
|
||||
if (max_x_c > max_x_v) {
|
||||
clip->w -= (max_x_v - max_x_c);
|
||||
}
|
||||
|
||||
if (max_y_c > max_y_v) {
|
||||
clip->h -= (max_y_v - max_y_c);
|
||||
}
|
||||
}
|
||||
|
||||
static bool SetDrawState(VITA_GXM_RenderData *data, const SDL_RenderCommand *cmd)
|
||||
{
|
||||
SDL_Texture *texture = cmd->data.draw.texture;
|
||||
|
|
@ -862,9 +891,13 @@ static bool SetDrawState(VITA_GXM_RenderData *data, const SDL_RenderCommand *cmd
|
|||
data->drawstate.cliprect_enabled_dirty = false;
|
||||
}
|
||||
|
||||
if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) {
|
||||
const SDL_Rect *rect = &data->drawstate.cliprect;
|
||||
set_clip_rectangle(data, rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
|
||||
if ((data->drawstate.cliprect_enabled || data->drawstate.viewport_is_set) && data->drawstate.cliprect_dirty) {
|
||||
SDL_Rect rect;
|
||||
SDL_copyp(&rect, &data->drawstate.cliprect);
|
||||
if (data->drawstate.viewport_is_set) {
|
||||
ClampCliprectToViewport(&rect, &data->drawstate.viewport);
|
||||
}
|
||||
set_clip_rectangle(data, rect.x, rect.y, rect.x + rect.w, rect.y + rect.h);
|
||||
data->drawstate.cliprect_dirty = false;
|
||||
}
|
||||
|
||||
|
|
@ -952,19 +985,30 @@ static void VITA_GXM_InvalidateCachedState(SDL_Renderer *renderer)
|
|||
static bool VITA_GXM_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
|
||||
{
|
||||
VITA_GXM_RenderData *data = (VITA_GXM_RenderData *)renderer->internal;
|
||||
int w, h;
|
||||
|
||||
StartDrawing(renderer);
|
||||
|
||||
data->drawstate.target = renderer->target;
|
||||
if (!data->drawstate.target) {
|
||||
int w, h;
|
||||
SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
|
||||
} else {
|
||||
float fw, fh;
|
||||
if (!SDL_GetTextureSize(renderer->target, &fw, &fh)) {
|
||||
w = data->drawstate.drawablew;
|
||||
h = data->drawstate.drawableh;
|
||||
} else {
|
||||
w = (int)SDL_roundf(fw);
|
||||
h = (int)SDL_roundf(fh);
|
||||
}
|
||||
}
|
||||
|
||||
if ((w != data->drawstate.drawablew) || (h != data->drawstate.drawableh)) {
|
||||
data->drawstate.viewport_dirty = true; // if the window dimensions changed, invalidate the current viewport, etc.
|
||||
data->drawstate.cliprect_dirty = true;
|
||||
data->drawstate.drawablew = w;
|
||||
data->drawstate.drawableh = h;
|
||||
}
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
switch (cmd->command) {
|
||||
|
|
@ -976,6 +1020,16 @@ static bool VITA_GXM_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *
|
|||
SDL_copyp(viewport, &cmd->data.viewport.rect);
|
||||
data->drawstate.viewport_dirty = true;
|
||||
data->drawstate.cliprect_dirty = true;
|
||||
data->drawstate.viewport_is_set = viewport->x != 0 || viewport->y != 0 || viewport->w != data->drawstate.drawablew || viewport->h != data->drawstate.drawableh;
|
||||
if (!data->drawstate.cliprect_enabled) {
|
||||
if (data->drawstate.viewport_is_set) {
|
||||
SDL_copyp(&data->drawstate.cliprect, viewport);
|
||||
data->drawstate.cliprect.x = 0;
|
||||
data->drawstate.cliprect.y = 0;
|
||||
} else {
|
||||
data->drawstate.cliprect_enabled_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -983,9 +1037,15 @@ static bool VITA_GXM_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *
|
|||
case SDL_RENDERCMD_SETCLIPRECT:
|
||||
{
|
||||
const SDL_Rect *rect = &cmd->data.cliprect.rect;
|
||||
const SDL_Rect *viewport = &data->drawstate.viewport;
|
||||
if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
|
||||
data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
|
||||
data->drawstate.cliprect_enabled_dirty = true;
|
||||
if (!data->drawstate.cliprect_enabled && data->drawstate.viewport_is_set) {
|
||||
SDL_copyp(&data->drawstate.cliprect, viewport);
|
||||
data->drawstate.cliprect.x = 0;
|
||||
data->drawstate.cliprect.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof(*rect)) != 0) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ typedef struct
|
|||
{
|
||||
SDL_Rect viewport;
|
||||
bool viewport_dirty;
|
||||
bool viewport_is_set;
|
||||
SDL_Texture *texture;
|
||||
SDL_Texture *target;
|
||||
SDL_FColor color;
|
||||
|
|
|
|||
|
|
@ -3672,6 +3672,10 @@ bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent)
|
|||
CHECK_WINDOW_NOT_POPUP(parent, false);
|
||||
}
|
||||
|
||||
if (window == parent) {
|
||||
return SDL_SetError("Cannot set the parent of a window to itself.");
|
||||
}
|
||||
|
||||
if (!_this->SetWindowParent) {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
|
@ -4135,7 +4139,7 @@ void SDL_OnWindowLiveResizeUpdate(SDL_Window *window)
|
|||
SDL_IterateMainCallbacks(false);
|
||||
} else {
|
||||
// Send an expose event so the application can redraw
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 1, 0);
|
||||
}
|
||||
|
||||
SDL_PumpEventMaintenance();
|
||||
|
|
|
|||
|
|
@ -110,8 +110,7 @@ void Android_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* Android_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *Android_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForAndroid[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
|
||||
|
|
|
|||
|
|
@ -161,8 +161,7 @@ void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForCocoa[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
|
||||
|
|
|
|||
|
|
@ -67,6 +67,23 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
|||
|
||||
// Destroy the curso GBM BO.
|
||||
if (dispdata->cursor_bo) {
|
||||
SDL_VideoData *viddata = (SDL_VideoData *) _this->internal;
|
||||
if (viddata->is_atomic) {
|
||||
if (dispdata->cursor_plane) {
|
||||
// Unset the the cursor BO from the cursor plane.
|
||||
KMSDRM_PlaneInfo info;
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
drm_atomic_set_plane_props(&info);
|
||||
// Wait until the cursor is unset from the cursor plane before destroying it's BO.
|
||||
if (drm_atomic_commit(_this, true, false)) {
|
||||
SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse.");
|
||||
}
|
||||
// Free the cursor plane, on which the cursor was being shown.
|
||||
free_plane(&dispdata->cursor_plane);
|
||||
}
|
||||
}
|
||||
|
||||
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
|
||||
dispdata->cursor_bo = NULL;
|
||||
dispdata->cursor_bo_drm_fd = -1;
|
||||
|
|
@ -78,11 +95,14 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
|||
build a window and assign a display to it. */
|
||||
bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display)
|
||||
{
|
||||
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
|
||||
if (viddata->is_atomic) {
|
||||
setup_plane(dev, &dispdata->cursor_plane, DRM_PLANE_TYPE_CURSOR);
|
||||
}
|
||||
|
||||
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
|
||||
GBM_FORMAT_ARGB8888,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
|
||||
|
|
@ -121,15 +141,29 @@ static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
|
|||
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = video_device->internal;
|
||||
|
||||
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, 0, 0, 0);
|
||||
if (viddata->is_atomic) {
|
||||
if (dispdata->cursor_plane) {
|
||||
KMSDRM_PlaneInfo info;
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
// The rest of the members are zeroed, so this takes away the cursor from the cursor plane.
|
||||
drm_atomic_set_plane_props(&info);
|
||||
if (drm_atomic_commit(video_device, true, false)) {
|
||||
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 0, 0, 0);
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dump a cursor buffer to a display's DRM cursor BO.
|
||||
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
|
||||
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Mouse *mouse, SDL_Cursor *cursor)
|
||||
{
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
SDL_CursorData *curdata = cursor->internal;
|
||||
|
|
@ -173,22 +207,42 @@ static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (viddata->is_atomic) {
|
||||
// Get the fb_id for the GBM BO so we can show it on the cursor plane.
|
||||
KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
|
||||
KMSDRM_PlaneInfo info;
|
||||
|
||||
// Show the GBM BO buffer on the cursor plane.
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = dispdata->cursor_w;
|
||||
info.src_h = dispdata->cursor_h;
|
||||
info.crtc_x = ((int32_t) SDL_roundf(mouse->x)) - curdata->hot_x;
|
||||
info.crtc_y = ((int32_t) SDL_roundf(mouse->y)) - curdata->hot_y;
|
||||
info.crtc_w = curdata->w;
|
||||
info.crtc_h = curdata->h;
|
||||
drm_atomic_set_plane_props(&info);
|
||||
if (drm_atomic_commit(video_device, true, false)) {
|
||||
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
// Put the GBM BO buffer on screen using the DRM interface.
|
||||
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
|
||||
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
|
||||
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
|
||||
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h);
|
||||
} else {
|
||||
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
|
||||
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
|
||||
}
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (ready_buffer) {
|
||||
SDL_free(ready_buffer);
|
||||
}
|
||||
|
|
@ -316,7 +370,7 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
|
|||
if (cursor) {
|
||||
/* Dump the cursor to the display DRM cursor BO so it becomes visible
|
||||
on that display. */
|
||||
result = KMSDRM_DumpCursorToBO(display, cursor);
|
||||
result = KMSDRM_DumpCursorToBO(display, mouse, cursor);
|
||||
} else {
|
||||
// Hide the cursor on that display.
|
||||
result = KMSDRM_RemoveCursorFromBO(display);
|
||||
|
|
@ -327,6 +381,19 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void drm_atomic_movecursor(const SDL_CursorData *curdata, uint16_t x, uint16_t y)
|
||||
{
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
if (dispdata->cursor_plane) { // We can't move a non-existing cursor, but that's ok.
|
||||
// Do we have a set of changes already in the making? If not, allocate a new one.
|
||||
if (!dispdata->atomic_req) {
|
||||
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
|
||||
}
|
||||
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
|
||||
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
|
||||
}
|
||||
}
|
||||
|
||||
static bool KMSDRM_WarpMouseGlobal(float x, float y)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
|
|
@ -340,17 +407,25 @@ static bool KMSDRM_WarpMouseGlobal(float x, float y)
|
|||
|
||||
// And now update the cursor graphic position on screen.
|
||||
if (dispdata->cursor_bo) {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)x, (int)y);
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
if (viddata->is_atomic) {
|
||||
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
|
||||
drm_atomic_movecursor(curdata, (uint16_t) (int) x, (uint16_t) (int) y);
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)x, (int)y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
}
|
||||
} else {
|
||||
return SDL_SetError("No mouse or current cursor.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y)
|
||||
|
|
@ -394,16 +469,29 @@ static bool KMSDRM_MoveCursor(SDL_Cursor *cursor)
|
|||
if (mouse && mouse->cur_cursor && mouse->focus) {
|
||||
SDL_Window *window = mouse->focus;
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
|
||||
if (!dispdata->cursor_bo) {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
}
|
||||
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)mouse->x, (int)mouse->y);
|
||||
if (viddata->is_atomic) {
|
||||
/* !!! FIXME: Some programs expect cursor movement even while they don't do SwapWindow() calls,
|
||||
and since we ride on the atomic_commit() in SwapWindow() for cursor movement,
|
||||
cursor won't move in these situations. We could do an atomic_commit() here
|
||||
for each cursor movement request, but it cripples the movement to 30FPS,
|
||||
so a future solution is needed. SDLPoP "QUIT?" menu is an example of this
|
||||
situation. */
|
||||
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
|
||||
drm_atomic_movecursor(curdata, (uint16_t) (int) mouse->x, (uint16_t) (int) mouse->y);
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)mouse->x, (int)mouse->y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,25 @@
|
|||
#include "SDL_kmsdrmdyn.h"
|
||||
#include <errno.h>
|
||||
|
||||
#define VOID2U64(x) ((uint64_t)(size_t)(x))
|
||||
|
||||
#ifndef EGL_PLATFORM_GBM_MESA
|
||||
#define EGL_PLATFORM_GBM_MESA 0x31D7
|
||||
#endif
|
||||
|
||||
#ifndef EGL_SYNC_NATIVE_FENCE_ANDROID
|
||||
#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144
|
||||
#endif
|
||||
|
||||
#ifndef EGL_SYNC_NATIVE_FENCE_FD_ANDROID
|
||||
#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145
|
||||
#endif
|
||||
|
||||
#ifndef EGL_NO_NATIVE_FENCE_FD_ANDROID
|
||||
#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1
|
||||
#endif
|
||||
|
||||
|
||||
// EGL implementation of SDL OpenGL support
|
||||
|
||||
void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor)
|
||||
|
|
@ -83,7 +98,263 @@ bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
static EGLSyncKHR create_fence(SDL_VideoDevice *_this, int fd)
|
||||
{
|
||||
EGLint attrib_list[] = {
|
||||
EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
EGLSyncKHR fence = _this->egl_data->eglCreateSyncKHR(_this->egl_data->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list);
|
||||
|
||||
SDL_assert(fence);
|
||||
return fence;
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
/* Comments about buffer access protection mechanism (=fences) are the ones boxed. */
|
||||
/* Also, DON'T remove the asserts: if a fence-related call fails, it's better that */
|
||||
/* program exits immediately, or we could leave KMS waiting for a failed/missing */
|
||||
/* fence forever. */
|
||||
/***********************************************************************************/
|
||||
static bool KMSDRM_GLES_SwapWindowFenced(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = ((SDL_WindowData *) window->internal);
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
KMSDRM_FBInfo *fb;
|
||||
KMSDRM_PlaneInfo info;
|
||||
bool modesetting = false;
|
||||
|
||||
SDL_zero(info);
|
||||
|
||||
/******************************************************************/
|
||||
/* Create the GPU-side FENCE OBJECT. It will be inserted into the */
|
||||
/* GL CMDSTREAM exactly at the end of the gl commands that form a */
|
||||
/* frame.(KMS will have to wait on it before doing a pageflip.) */
|
||||
/******************************************************************/
|
||||
dispdata->gpu_fence = create_fence(_this, EGL_NO_NATIVE_FENCE_FD_ANDROID);
|
||||
SDL_assert(dispdata->gpu_fence);
|
||||
|
||||
/******************************************************************/
|
||||
/* eglSwapBuffers flushes the fence down the GL CMDSTREAM, so we */
|
||||
/* know for sure it's there now. */
|
||||
/* Also it marks, at EGL level, the buffer that we want to become */
|
||||
/* the new front buffer. (Remember that won't really happen until */
|
||||
/* we request a pageflip at the KMS level and it completes. */
|
||||
/******************************************************************/
|
||||
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
|
||||
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* EXPORT the GPU-side FENCE OBJECT to the fence INPUT FD, so we */
|
||||
/* can pass it into the kernel. Atomic ioctl will pass the */
|
||||
/* in-fence fd into the kernel, thus telling KMS that it has to */
|
||||
/* wait for GPU to finish rendering the frame (remember where we */
|
||||
/* put the fence in the GL CMDSTREAM) before doing the changes */
|
||||
/* requested in the atomic ioct (the pageflip in this case). */
|
||||
/* (We export the GPU-side FENCE OJECT to the fence INPUT FD now, */
|
||||
/* not sooner, because now we are sure that the GPU-side fence is */
|
||||
/* in the CMDSTREAM to be lifted when the CMDSTREAM to this point */
|
||||
/* is completed). */
|
||||
/******************************************************************/
|
||||
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID (_this->egl_data->egl_display, dispdata->gpu_fence);
|
||||
|
||||
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence);
|
||||
SDL_assert(dispdata->kms_in_fence_fd != -1);
|
||||
|
||||
/* Lock the buffer that is marked by eglSwapBuffers() to become the
|
||||
next front buffer (so it can not be chosen by EGL as back buffer
|
||||
to draw on), and get a handle to it to request the pageflip on it.
|
||||
REMEMBER that gbm_surface_lock_front_buffer() ALWAYS has to be
|
||||
called after eglSwapBuffers(). */
|
||||
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
|
||||
if (!windata->next_bo) {
|
||||
return SDL_SetError("Failed to lock frontbuffer");
|
||||
}
|
||||
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb) {
|
||||
return SDL_SetError("Failed to get a new framebuffer from BO");
|
||||
}
|
||||
|
||||
/* Add the pageflip to the request list. */
|
||||
info.plane = dispdata->display_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch
|
||||
info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch
|
||||
info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch
|
||||
info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch
|
||||
info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch
|
||||
|
||||
drm_atomic_set_plane_props(&info);
|
||||
|
||||
/*****************************************************************/
|
||||
/* Tell the display (KMS) that it will have to wait on the fence */
|
||||
/* for the GPU-side FENCE. */
|
||||
/* */
|
||||
/* Since KMS is a kernel thing, we have to pass an FD into */
|
||||
/* the kernel, and get another FD out of the kernel. */
|
||||
/* */
|
||||
/* 1) To pass the GPU-side fence into the kernel, we set the */
|
||||
/* INPUT FD as the IN_FENCE_FD prop of the PRIMARY PLANE. */
|
||||
/* This FD tells KMS (the kernel) to wait for the GPU-side fence.*/
|
||||
/* */
|
||||
/* 2) To get the KMS-side fence out of the kernel, we set the */
|
||||
/* OUTPUT FD as the OUT_FEWNCE_FD prop of the CRTC. */
|
||||
/* This FD will be later imported as a FENCE OBJECT which will be*/
|
||||
/* used to tell the GPU to wait for KMS to complete the changes */
|
||||
/* requested in atomic_commit (the pageflip in this case). */
|
||||
/*****************************************************************/
|
||||
if (dispdata->kms_in_fence_fd != -1)
|
||||
{
|
||||
add_plane_property(dispdata->atomic_req, dispdata->display_plane,
|
||||
"IN_FENCE_FD", dispdata->kms_in_fence_fd);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc,
|
||||
"OUT_FENCE_PTR", VOID2U64(&dispdata->kms_out_fence_fd));
|
||||
}
|
||||
|
||||
/* Do we have a pending modesetting? If so, set the necessary
|
||||
props so it's included in the incoming atomic commit. */
|
||||
if (windata->egl_surface_dirty) {
|
||||
// !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too.
|
||||
KMSDRM_CreateSurfaces(_this, window);
|
||||
|
||||
uint32_t blob_id;
|
||||
SDL_VideoData *viddata = (SDL_VideoData *)_this->internal;
|
||||
|
||||
add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id);
|
||||
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "MODE_ID", blob_id);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "active", 1);
|
||||
modesetting = true;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/* Issue a non-blocking atomic commit: for triple buffering, */
|
||||
/* this must not block so the game can start building another */
|
||||
/* frame, even if the just-requested pageflip hasnt't completed. */
|
||||
/*****************************************************************/
|
||||
if (drm_atomic_commit(_this, false, modesetting)) {
|
||||
return SDL_SetError("Failed to issue atomic commit on pageflip");
|
||||
}
|
||||
|
||||
/* Release the previous front buffer so EGL can chose it as back buffer
|
||||
and render on it again. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
/* Take note of the buffer about to become front buffer, so next
|
||||
time we come here we can free it like we just did with the previous
|
||||
front buffer. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
/****************************************************************/
|
||||
/* Import the KMS-side FENCE OUTPUT FD from the kernel to the */
|
||||
/* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */
|
||||
/****************************************************************/
|
||||
dispdata->kms_fence = create_fence(_this, dispdata->kms_out_fence_fd);
|
||||
SDL_assert(dispdata->kms_fence);
|
||||
|
||||
/****************************************************************/
|
||||
/* "Delete" the fence OUTPUT FD, because we already have the */
|
||||
/* KMS FENCE OBJECT, the fence itself is away from us, on the */
|
||||
/* kernel side. */
|
||||
/****************************************************************/
|
||||
dispdata->kms_out_fence_fd = -1;
|
||||
|
||||
/*****************************************************************/
|
||||
/* Tell the GPU to wait on the fence for the KMS-side FENCE, */
|
||||
/* which means waiting until the requested pageflip is completed.*/
|
||||
/*****************************************************************/
|
||||
_this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_GLES_SwapWindowDoubleBuffered(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = ((SDL_WindowData *) window->internal);
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
KMSDRM_FBInfo *fb;
|
||||
KMSDRM_PlaneInfo info;
|
||||
bool modesetting = false;
|
||||
|
||||
SDL_zero(info);
|
||||
|
||||
/**********************************************************************************/
|
||||
/* In double-buffer mode, atomic_commit will always be synchronous/blocking (ie: */
|
||||
/* won't return until the requested changes are really done). */
|
||||
/* Also, there's no need to fence KMS or the GPU, because we won't be entering */
|
||||
/* game loop again (hence not building or executing a new cmdstring) until */
|
||||
/* pageflip is done, so we don't need to protect the KMS/GPU access to the buffer.*/
|
||||
/**********************************************************************************/
|
||||
|
||||
/* Mark, at EGL level, the buffer that we want to become the new front buffer.
|
||||
It won't really happen until we request a pageflip at the KMS level and it
|
||||
completes. */
|
||||
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
|
||||
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
|
||||
}
|
||||
/* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer
|
||||
(so it can not be chosen by EGL as back buffer to draw on), and get a handle to it,
|
||||
to request the pageflip on it. */
|
||||
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
|
||||
if (!windata->next_bo) {
|
||||
return SDL_SetError("Failed to lock frontbuffer");
|
||||
}
|
||||
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb) {
|
||||
return SDL_SetError("Failed to get a new framebuffer BO");
|
||||
}
|
||||
|
||||
/* Add the pageflip to the request list. */
|
||||
info.plane = dispdata->display_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch
|
||||
info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch
|
||||
info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch
|
||||
info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch
|
||||
info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch
|
||||
|
||||
drm_atomic_set_plane_props(&info);
|
||||
|
||||
/* Do we have a pending modesetting? If so, set the necessary
|
||||
props so it's included in the incoming atomic commit. */
|
||||
if (windata->egl_surface_dirty) {
|
||||
// !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too.
|
||||
KMSDRM_CreateSurfaces(_this, window);
|
||||
|
||||
uint32_t blob_id;
|
||||
|
||||
SDL_VideoData *viddata = (SDL_VideoData *)_this->internal;
|
||||
|
||||
add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id);
|
||||
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "MODE_ID", blob_id);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc, "active", 1);
|
||||
modesetting = true;
|
||||
}
|
||||
|
||||
/* Issue the one and only atomic commit where all changes will be requested!
|
||||
Blocking for double buffering: won't return until completed. */
|
||||
if (drm_atomic_commit(_this, true, modesetting)) {
|
||||
return SDL_SetError("Failed to issue atomic commit on pageflip");
|
||||
}
|
||||
|
||||
/* Release last front buffer so EGL can chose it as back buffer and render on it again. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
|
||||
/* Take note of current front buffer, so we can free it next time we come here. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_GLES_SwapWindowLegacy(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
SDL_WindowData *windata = window->internal;
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
|
|
@ -116,13 +387,12 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
// Release the previous front buffer
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
windata->bo = NULL;
|
||||
}
|
||||
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
/* Mark a buffer to become the next front buffer.
|
||||
This won't happen until pagelip completes. */
|
||||
This won't happen until pageflip completes. */
|
||||
if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display,
|
||||
windata->egl_surface))) {
|
||||
return SDL_SetError("eglSwapBuffers failed");
|
||||
|
|
@ -147,8 +417,8 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
drmModePageFlip can be used the CRTC has to be configured to use
|
||||
the current connector and mode with drmModeSetCrtc */
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
|
||||
dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0,
|
||||
&dispdata->connector->connector_id, 1, &dispdata->mode);
|
||||
dispdata->crtc.crtc->crtc_id, fb_info->fb_id, 0, 0,
|
||||
&dispdata->connector.connector->connector_id, 1, &dispdata->mode);
|
||||
|
||||
if (ret) {
|
||||
return SDL_SetError("Could not set videomode on CRTC.");
|
||||
|
|
@ -170,7 +440,7 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
|
||||
}
|
||||
|
||||
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc.crtc->crtc_id,
|
||||
fb_info->fb_id, flip_flags, &windata->waiting_for_flip);
|
||||
|
||||
if (ret == 0) {
|
||||
|
|
@ -198,6 +468,26 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = (SDL_WindowData *) window->internal;
|
||||
|
||||
if (windata->swap_window == NULL) {
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
if (viddata->is_atomic) {
|
||||
// We want the fenced version by default, but it needs extensions.
|
||||
if ( (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) || (!SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_ANDROID_native_fence_sync")) ) {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowDoubleBuffered;
|
||||
} else {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowFenced;
|
||||
}
|
||||
} else {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowLegacy;
|
||||
}
|
||||
}
|
||||
return windata->swap_window(_this, window);
|
||||
}
|
||||
|
||||
SDL_EGL_MakeCurrent_impl(KMSDRM)
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_KMSDRM
|
||||
|
|
|
|||
|
|
@ -137,6 +137,16 @@ SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_offset,(struct gbm_bo *bo, int plane))
|
|||
SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_stride_for_plane,(struct gbm_bo *bo, int plane))
|
||||
SDL_KMSDRM_SYM_OPT(union gbm_bo_handle,gbm_bo_get_handle_for_plane,(struct gbm_bo *bo, int plane))
|
||||
|
||||
|
||||
SDL_KMSDRM_MODULE(ATOMIC)
|
||||
SDL_KMSDRM_SYM(int,drmIoctl,(int fd, unsigned long request, void *arg))
|
||||
SDL_KMSDRM_SYM(drmModeAtomicReqPtr,drmModeAtomicAlloc,(void))
|
||||
SDL_KMSDRM_SYM(void,drmModeAtomicFree,(drmModeAtomicReqPtr req))
|
||||
SDL_KMSDRM_SYM(int,drmModeAtomicCommit,(int fd,drmModeAtomicReqPtr req,uint32_t flags,void *user_data))
|
||||
SDL_KMSDRM_SYM(int,drmModeAtomicAddProperty,(drmModeAtomicReqPtr req,uint32_t object_id,uint32_t property_id,uint64_t value))
|
||||
SDL_KMSDRM_SYM(int,drmModeCreatePropertyBlob,(int fd,const void *data,size_t size,uint32_t *id))
|
||||
|
||||
|
||||
#undef SDL_KMSDRM_MODULE
|
||||
#undef SDL_KMSDRM_SYM
|
||||
#undef SDL_KMSDRM_SYM_CONST
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@ static char kmsdrm_dri_devname[8];
|
|||
static int kmsdrm_dri_devnamesize = 0;
|
||||
static char kmsdrm_dri_cardpath[32];
|
||||
|
||||
/* for older KMSDRM headers... */
|
||||
#ifndef DRM_FORMAT_MOD_VENDOR_NONE
|
||||
#define DRM_FORMAT_MOD_VENDOR_NONE 0
|
||||
#endif
|
||||
#ifndef DRM_FORMAT_MOD_LINEAR
|
||||
#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
|
||||
#endif
|
||||
|
||||
#ifndef EGL_PLATFORM_GBM_MESA
|
||||
#define EGL_PLATFORM_GBM_MESA 0x31D7
|
||||
#endif
|
||||
|
|
@ -191,6 +199,409 @@ static void CalculateRefreshRate(drmModeModeInfo *mode, int *numerator, int *den
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
int add_connector_property(drmModeAtomicReq *req, connector *conn, const char *name, uint64_t value)
|
||||
{
|
||||
unsigned int i;
|
||||
int prop_id = 0;
|
||||
|
||||
for (i = 0 ; i < conn->props->count_props ; i++) {
|
||||
if (SDL_strcmp(conn->props_info[i]->name, name) == 0) {
|
||||
prop_id = conn->props_info[i]->prop_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_id < 0) {
|
||||
SDL_SetError("no connector property: %s", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return KMSDRM_drmModeAtomicAddProperty(req, conn->connector->connector_id, prop_id, value);
|
||||
}
|
||||
|
||||
int add_crtc_property(drmModeAtomicReq *req, crtc *c, const char *name, uint64_t value)
|
||||
{
|
||||
unsigned int i;
|
||||
int prop_id = -1;
|
||||
|
||||
for (i = 0 ; i < c->props->count_props ; i++) {
|
||||
if (SDL_strcmp(c->props_info[i]->name, name) == 0) {
|
||||
prop_id = c->props_info[i]->prop_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_id < 0) {
|
||||
SDL_SetError("no crtc property: %s", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return KMSDRM_drmModeAtomicAddProperty(req, c->crtc->crtc_id, prop_id, value);
|
||||
}
|
||||
|
||||
int add_plane_property(drmModeAtomicReq *req, plane *p, const char *name, uint64_t value)
|
||||
{
|
||||
unsigned int i;
|
||||
int prop_id = -1;
|
||||
|
||||
for (i = 0 ; i < p->props->count_props ; i++) {
|
||||
if (SDL_strcmp(p->props_info[i]->name, name) == 0) {
|
||||
prop_id = p->props_info[i]->prop_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_id < 0) {
|
||||
SDL_SetError("no plane property: %s", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return KMSDRM_drmModeAtomicAddProperty(req, p->plane->plane_id, prop_id, value);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void print_plane_info(_THIS, drmModePlanePtr plane)
|
||||
{
|
||||
char *plane_type;
|
||||
drmModeRes *resources;
|
||||
uint32_t type = 0;
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
||||
int i;
|
||||
|
||||
drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
||||
plane->plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
|
||||
/* Search the plane props for the plane type. */
|
||||
for (i = 0; i < props->count_props; i++) {
|
||||
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[i]);
|
||||
if ((strcmp(p->name, "type") == 0)) {
|
||||
type = props->prop_values[i];
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreeProperty(p);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DRM_PLANE_TYPE_OVERLAY:
|
||||
plane_type = "overlay";
|
||||
break;
|
||||
|
||||
case DRM_PLANE_TYPE_PRIMARY:
|
||||
plane_type = "primary";
|
||||
break;
|
||||
|
||||
case DRM_PLANE_TYPE_CURSOR:
|
||||
plane_type = "cursor";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Remember that to present a plane on screen, it has to be
|
||||
connected to a CRTC so the CRTC scans it,
|
||||
scales it, etc... and presents it on screen. */
|
||||
|
||||
/* Now we look for the CRTCs supported by the plane. */
|
||||
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
||||
if (!resources)
|
||||
return;
|
||||
|
||||
printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ", plane->plane_id, plane_type, plane->crtc_id);
|
||||
for (i = 0; i < resources->count_crtcs; i++) {
|
||||
if (plane->possible_crtcs & (1 << i)) {
|
||||
uint32_t crtc_id = resources->crtcs[i];
|
||||
printf ("%d", crtc_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("\n\n");
|
||||
}
|
||||
|
||||
static void get_planes_info(_THIS)
|
||||
{
|
||||
drmModePlaneResPtr plane_resources;
|
||||
uint32_t i;
|
||||
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
|
||||
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
|
||||
if (!plane_resources) {
|
||||
printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
printf("--Number of planes found: %d-- \n", plane_resources->count_planes);
|
||||
printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc.crtc->crtc_id);
|
||||
|
||||
/* Iterate on all the available planes. */
|
||||
for (i = 0; (i < plane_resources->count_planes); i++) {
|
||||
|
||||
uint32_t plane_id = plane_resources->planes[i];
|
||||
|
||||
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
||||
if (!plane) {
|
||||
printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Print plane info. */
|
||||
print_plane_info(_this, plane);
|
||||
KMSDRM_drmModeFreePlane(plane);
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreePlaneResources(plane_resources);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Get the plane_id of a plane that is of the specified plane type (primary,
|
||||
overlay, cursor...) and can use specified CRTC. */
|
||||
static int get_plane_id(SDL_VideoDevice *_this, unsigned int crtc_id, uint32_t plane_type)
|
||||
{
|
||||
drmModeRes *resources = NULL;
|
||||
drmModePlaneResPtr plane_resources = NULL;
|
||||
uint32_t i, j;
|
||||
unsigned int crtc_index = 0;
|
||||
int ret = -EINVAL;
|
||||
int found = 0;
|
||||
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal);
|
||||
|
||||
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
||||
|
||||
/* Get the crtc_index for the current CRTC.
|
||||
It's needed to find out if a plane supports the CRTC. */
|
||||
for (i = 0; i < resources->count_crtcs; i++) {
|
||||
if (resources->crtcs[i] == crtc_id) {
|
||||
crtc_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
|
||||
if (!plane_resources) {
|
||||
return SDL_SetError("drmModeGetPlaneResources failed.");
|
||||
}
|
||||
|
||||
/* Iterate on all the available planes. */
|
||||
for (i = 0; (i < plane_resources->count_planes) && !found; i++) {
|
||||
|
||||
uint32_t plane_id = plane_resources->planes[i];
|
||||
|
||||
drmModePlanePtr _plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
||||
if (!_plane) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* See if the current CRTC is available for this plane. */
|
||||
if (_plane->possible_crtcs & (1 << crtc_index)) {
|
||||
|
||||
drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(
|
||||
viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
ret = plane_id;
|
||||
|
||||
/* Iterate on the plane props to find the type of the plane,
|
||||
to see if it's of the type we want. */
|
||||
for (j = 0; j < props->count_props; j++) {
|
||||
|
||||
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
||||
props->props[j]);
|
||||
|
||||
if ((strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) {
|
||||
/* found our plane, use that: */
|
||||
found = 1;
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreeProperty(p);
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreeObjectProperties(props);
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreePlane(_plane);
|
||||
}
|
||||
|
||||
KMSDRM_drmModeFreePlaneResources(plane_resources);
|
||||
KMSDRM_drmModeFreeResources(resources);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup a plane and it's props. */
|
||||
bool setup_plane(SDL_VideoDevice *_this, plane **_plane, uint32_t plane_type)
|
||||
{
|
||||
uint32_t plane_id;
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal);
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
bool ret = true;
|
||||
|
||||
*_plane = SDL_calloc(1, sizeof(**_plane));
|
||||
if (!(*_plane)) {
|
||||
ret = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get plane ID for a given CRTC and plane type. */
|
||||
plane_id = get_plane_id(_this, dispdata->crtc.crtc->crtc_id, plane_type);
|
||||
|
||||
if (!plane_id) {
|
||||
ret = SDL_SetError("Invalid Plane ID");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get the DRM plane itself. */
|
||||
(*_plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
||||
|
||||
/* Get the DRM plane properties. */
|
||||
if ((*_plane)->plane) {
|
||||
unsigned int i;
|
||||
(*_plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
||||
(*_plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
(*_plane)->props_info = SDL_calloc((*_plane)->props->count_props, sizeof(*(*_plane)->props_info));
|
||||
|
||||
if ( !((*_plane)->props_info) ) {
|
||||
ret = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < (*_plane)->props->count_props; i++) {
|
||||
(*_plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, (*_plane)->props->props[i]);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (!ret) {
|
||||
if (*_plane) {
|
||||
SDL_free(*_plane);
|
||||
*_plane = NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Free a plane and it's props. */
|
||||
void free_plane(plane **_plane)
|
||||
{
|
||||
if (*_plane) {
|
||||
if ((*_plane)->plane) {
|
||||
KMSDRM_drmModeFreePlane((*_plane)->plane);
|
||||
(*_plane)->plane = NULL;
|
||||
}
|
||||
if ((*_plane)->props_info) {
|
||||
SDL_free((*_plane)->props_info);
|
||||
(*_plane)->props_info = NULL;
|
||||
}
|
||||
SDL_free(*_plane);
|
||||
*_plane = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/* The most important ATOMIC fn of the backend. */
|
||||
/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */
|
||||
/* over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected */
|
||||
/* to the same PLANE). */
|
||||
/* Think of a plane as a "frame" sorrounding a picture, where the "picture" */
|
||||
/* is the buffer, and we move the "frame" from a picture to another, */
|
||||
/* and the one that has the "frame" is the one sent over to the screen */
|
||||
/* via the CONNECTOR->ENCODER system. */
|
||||
/* Think of a PLANE as being "in the middle", it's the CENTRAL part */
|
||||
/* bewteen the CRTC and the BUFFER that is shown on screen. */
|
||||
/* What we do here is connect a PLANE to a CRTC and a BUFFER. */
|
||||
/* -ALWAYS set the CRTC_ID and FB_ID attribs of a plane at the same time, */
|
||||
/* meaning IN THE SAME atomic request. */
|
||||
/* -And NEVER destroy a GBM surface whose buffers are being read by a plane: */
|
||||
/* first, move the plane away from those buffers and ONLY THEN destroy the */
|
||||
/* buffers and/or the GBM surface containig them. */
|
||||
/**********************************************************************************/
|
||||
void
|
||||
drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info)
|
||||
{
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
|
||||
/* Do we have a set of changes already in the making? If not, allocate a new one. */
|
||||
if (!dispdata->atomic_req) {
|
||||
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
|
||||
}
|
||||
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "FB_ID", info->fb_id);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_ID", info->crtc_id);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "SRC_W", info->src_w << 16);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "SRC_H", info->src_h << 16);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "SRC_X", info->src_x);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "SRC_Y", info->src_y);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_W", info->crtc_w);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_H", info->crtc_h);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_X", info->crtc_x);
|
||||
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_Y", info->crtc_y);
|
||||
}
|
||||
|
||||
int drm_atomic_commit(SDL_VideoDevice *_this, bool blocking, bool allow_modeset)
|
||||
{
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal);
|
||||
uint32_t atomic_flags = 0;
|
||||
int ret;
|
||||
|
||||
if (!blocking) {
|
||||
atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||
}
|
||||
|
||||
if (allow_modeset) {
|
||||
atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
}
|
||||
|
||||
/* Never issue a new atomic commit if previous has not yet completed,
|
||||
or it will error. */
|
||||
drm_atomic_waitpending(_this);
|
||||
|
||||
ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req,
|
||||
atomic_flags, NULL);
|
||||
|
||||
if (ret) {
|
||||
SDL_SetError("Atomic commit failed, returned %d.", ret);
|
||||
/* Uncomment this for fast-debugging */
|
||||
#if 0
|
||||
printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno));
|
||||
#endif
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dispdata->kms_in_fence_fd != -1) {
|
||||
close(dispdata->kms_in_fence_fd);
|
||||
dispdata->kms_in_fence_fd = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
|
||||
dispdata->atomic_req = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
drm_atomic_waitpending(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
|
||||
/* Will return immediately if we have already destroyed the fence, because we NULL-ify it just after.
|
||||
Also, will return immediately in double-buffer mode, because kms_fence will alsawys be NULL. */
|
||||
if (dispdata->kms_fence) {
|
||||
EGLint status;
|
||||
|
||||
do {
|
||||
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
|
||||
dispdata->kms_fence, 0, EGL_FOREVER_KHR);
|
||||
} while (status != EGL_CONDITION_SATISFIED_KHR);
|
||||
|
||||
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
|
||||
dispdata->kms_fence = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool KMSDRM_Available(void)
|
||||
{
|
||||
#ifdef SDL_PLATFORM_OPENBSD
|
||||
|
|
@ -519,14 +930,14 @@ static drmModeModeInfo *KMSDRM_GetClosestDisplayMode(SDL_VideoDisplay *display,
|
|||
{
|
||||
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
drmModeConnector *connector = dispdata->connector;
|
||||
drmModeConnector *conn = dispdata->connector.connector;
|
||||
|
||||
SDL_DisplayMode closest;
|
||||
drmModeModeInfo *drm_mode;
|
||||
|
||||
if (SDL_GetClosestFullscreenDisplayMode(display->id, width, height, 0.0f, false, &closest)) {
|
||||
const SDL_DisplayModeData *modedata = closest.internal;
|
||||
drm_mode = &connector->modes[modedata->mode_index];
|
||||
drm_mode = &conn->modes[modedata->mode_index];
|
||||
return drm_mode;
|
||||
} else {
|
||||
return NULL;
|
||||
|
|
@ -542,6 +953,11 @@ static bool KMSDRM_DropMaster(SDL_VideoDevice *_this)
|
|||
{
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
|
||||
if (viddata->is_atomic) { // turn off atomic support until we are in control again.
|
||||
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0);
|
||||
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
|
||||
}
|
||||
|
||||
/* Check if we have DRM master to begin with */
|
||||
if (KMSDRM_drmAuthMagic(viddata->drm_fd, 0) == -EACCES) {
|
||||
/* Nope, nothing to do then */
|
||||
|
|
@ -568,15 +984,15 @@ static void KMSDRM_DeinitDisplays(SDL_VideoDevice *_this)
|
|||
dispdata = SDL_GetDisplayDriverData(displays[i]);
|
||||
|
||||
// Free connector
|
||||
if (dispdata && dispdata->connector) {
|
||||
KMSDRM_drmModeFreeConnector(dispdata->connector);
|
||||
dispdata->connector = NULL;
|
||||
if (dispdata && dispdata->connector.connector) {
|
||||
KMSDRM_drmModeFreeConnector(dispdata->connector.connector);
|
||||
dispdata->connector.connector = NULL;
|
||||
}
|
||||
|
||||
// Free CRTC
|
||||
if (dispdata && dispdata->crtc) {
|
||||
KMSDRM_drmModeFreeCrtc(dispdata->crtc);
|
||||
dispdata->crtc = NULL;
|
||||
if (dispdata && dispdata->crtc.crtc) {
|
||||
KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc);
|
||||
dispdata->crtc.crtc = NULL;
|
||||
}
|
||||
}
|
||||
SDL_free(displays);
|
||||
|
|
@ -795,14 +1211,14 @@ static int KMSDRM_CrtcGetOrientation(uint32_t drm_fd, uint32_t crtc_id)
|
|||
|
||||
/* Gets a DRM connector, builds an SDL_Display with it, and adds it to the
|
||||
list of SDL Displays in _this->displays[] */
|
||||
static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connector, drmModeRes *resources)
|
||||
static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *conn, drmModeRes *resources)
|
||||
{
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
SDL_DisplayData *dispdata = NULL;
|
||||
SDL_VideoDisplay display = { 0 };
|
||||
SDL_DisplayModeData *modedata = NULL;
|
||||
drmModeEncoder *encoder = NULL;
|
||||
drmModeCrtc *crtc = NULL;
|
||||
drmModeCrtc *_crtc = NULL;
|
||||
const char *connector_type = NULL;
|
||||
SDL_DisplayID display_id;
|
||||
SDL_PropertiesID display_properties;
|
||||
|
|
@ -823,6 +1239,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
to sane values. */
|
||||
dispdata->cursor_bo = NULL;
|
||||
dispdata->cursor_bo_drm_fd = -1;
|
||||
dispdata->kms_out_fence_fd = -1;
|
||||
|
||||
/* Since we create and show the default cursor on KMSDRM_InitMouse(),
|
||||
and we call KMSDRM_InitMouse() when we create a window, we have to know
|
||||
|
|
@ -839,7 +1256,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
continue;
|
||||
}
|
||||
|
||||
if (encoder->encoder_id == connector->encoder_id) {
|
||||
if (encoder->encoder_id == conn->encoder_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -857,13 +1274,13 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < connector->count_encoders; j++) {
|
||||
if (connector->encoders[j] == encoder->encoder_id) {
|
||||
for (j = 0; j < conn->count_encoders; j++) {
|
||||
if (conn->encoders[j] == encoder->encoder_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j != connector->count_encoders) {
|
||||
if (j != conn->count_encoders) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -878,21 +1295,21 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
}
|
||||
|
||||
// Try to find a CRTC connected to this encoder
|
||||
crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
||||
_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
||||
|
||||
/* If no CRTC was connected to the encoder, find the first CRTC
|
||||
that is supported by the encoder, and use that. */
|
||||
if (!crtc) {
|
||||
if (!_crtc) {
|
||||
for (i = 0; i < resources->count_crtcs; i++) {
|
||||
if (encoder->possible_crtcs & (1 << i)) {
|
||||
encoder->crtc_id = resources->crtcs[i];
|
||||
crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
||||
_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!crtc) {
|
||||
if (!_crtc) {
|
||||
ret = SDL_SetError("No CRTC found for connector.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
@ -900,10 +1317,10 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
// Find the index of the mode attached to this CRTC
|
||||
mode_index = -1;
|
||||
|
||||
for (i = 0; i < connector->count_modes; i++) {
|
||||
drmModeModeInfo *mode = &connector->modes[i];
|
||||
for (i = 0; i < conn->count_modes; i++) {
|
||||
drmModeModeInfo *mode = &conn->modes[i];
|
||||
|
||||
if (!SDL_memcmp(mode, &crtc->mode, sizeof(crtc->mode))) {
|
||||
if (!SDL_memcmp(mode, &_crtc->mode, sizeof(_crtc->mode))) {
|
||||
mode_index = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -913,8 +1330,8 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
int current_area, largest_area = 0;
|
||||
|
||||
// Find the preferred mode or the highest resolution mode
|
||||
for (i = 0; i < connector->count_modes; i++) {
|
||||
drmModeModeInfo *mode = &connector->modes[i];
|
||||
for (i = 0; i < conn->count_modes; i++) {
|
||||
drmModeModeInfo *mode = &conn->modes[i];
|
||||
|
||||
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_index = i;
|
||||
|
|
@ -928,7 +1345,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
}
|
||||
}
|
||||
if (mode_index != -1) {
|
||||
crtc->mode = connector->modes[mode_index];
|
||||
_crtc->mode = conn->modes[mode_index];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -948,9 +1365,9 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
/* Get the mode currently setup for this display,
|
||||
which is the mode currently setup on the CRTC
|
||||
we found for the active connector. */
|
||||
dispdata->mode = crtc->mode;
|
||||
dispdata->original_mode = crtc->mode;
|
||||
dispdata->fullscreen_mode = crtc->mode;
|
||||
dispdata->mode = _crtc->mode;
|
||||
dispdata->original_mode = _crtc->mode;
|
||||
dispdata->fullscreen_mode = _crtc->mode;
|
||||
|
||||
if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0) {
|
||||
ret = SDL_SetError("Couldn't get a valid connector videomode.");
|
||||
|
|
@ -958,24 +1375,48 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
}
|
||||
|
||||
// Store the connector and crtc for this display.
|
||||
dispdata->connector = connector;
|
||||
dispdata->crtc = crtc;
|
||||
dispdata->connector.connector = conn;
|
||||
dispdata->crtc.crtc = _crtc;
|
||||
|
||||
// save previous vrr state
|
||||
dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, crtc->crtc_id);
|
||||
dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, _crtc->crtc_id);
|
||||
// try to enable vrr
|
||||
if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, connector->connector_id, "VRR_CAPABLE")) {
|
||||
if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, conn->connector_id, "VRR_CAPABLE")) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Enabling VRR");
|
||||
KMSDRM_CrtcSetVrr(viddata->drm_fd, crtc->crtc_id, true);
|
||||
KMSDRM_CrtcSetVrr(viddata->drm_fd, _crtc->crtc_id, true);
|
||||
}
|
||||
|
||||
// Set the name by the connector type, if possible
|
||||
if (KMSDRM_drmModeGetConnectorTypeName) {
|
||||
connector_type = KMSDRM_drmModeGetConnectorTypeName(connector->connector_type);
|
||||
connector_type = KMSDRM_drmModeGetConnectorTypeName(conn->connector_type);
|
||||
if (connector_type == NULL) {
|
||||
connector_type = "Unknown";
|
||||
}
|
||||
SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, connector->connector_type_id);
|
||||
SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, conn->connector_type_id);
|
||||
}
|
||||
|
||||
dispdata->crtc.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, _crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
|
||||
dispdata->crtc.props_info = SDL_calloc(dispdata->crtc.props->count_props, sizeof(*dispdata->crtc.props_info));
|
||||
if (!dispdata->crtc.props_info) {
|
||||
ret = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < dispdata->crtc.props->count_props; i++) {
|
||||
dispdata->crtc.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, dispdata->crtc.props->props[i]);
|
||||
}
|
||||
|
||||
/* Get connector properties */
|
||||
dispdata->connector.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, conn->connector_id, DRM_MODE_OBJECT_CONNECTOR);
|
||||
dispdata->connector.props_info = SDL_calloc(dispdata->connector.props->count_props, sizeof(*dispdata->connector.props_info));
|
||||
if (!dispdata->connector.props_info) {
|
||||
ret = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < dispdata->connector.props->count_props; i++) {
|
||||
dispdata->connector.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
||||
dispdata->connector.props->props[i]);
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
|
|
@ -1010,7 +1451,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, crtc->crtc_id);
|
||||
orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, _crtc->crtc_id);
|
||||
display_properties = SDL_GetDisplayProperties(display_id);
|
||||
SDL_SetNumberProperty(display_properties, SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER, orientation);
|
||||
|
||||
|
|
@ -1021,14 +1462,15 @@ cleanup:
|
|||
if (ret) {
|
||||
// Error (complete) cleanup
|
||||
if (dispdata) {
|
||||
if (dispdata->connector) {
|
||||
KMSDRM_drmModeFreeConnector(dispdata->connector);
|
||||
dispdata->connector = NULL;
|
||||
if (dispdata->connector.connector) {
|
||||
KMSDRM_drmModeFreeConnector(dispdata->connector.connector);
|
||||
}
|
||||
if (dispdata->crtc) {
|
||||
KMSDRM_drmModeFreeCrtc(dispdata->crtc);
|
||||
dispdata->crtc = NULL;
|
||||
if (dispdata->crtc.crtc) {
|
||||
KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc);
|
||||
}
|
||||
SDL_free(dispdata->connector.props_info);
|
||||
SDL_free(dispdata->crtc.props_info);
|
||||
SDL_free(dispdata->display_plane);
|
||||
SDL_free(dispdata);
|
||||
}
|
||||
}
|
||||
|
|
@ -1077,13 +1519,22 @@ static void KMSDRM_SortDisplays(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
static bool set_client_atomic_caps(int fd)
|
||||
{
|
||||
if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
|
||||
return false; // no atomic modesetting support.
|
||||
} else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
|
||||
return false; // no universal planes support.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Initializes the list of SDL displays: we build a new display for each
|
||||
connecter connector we find.
|
||||
This is to be called early, in VideoInit(), because it gets us
|
||||
the videomode information, which SDL needs immediately after VideoInit(). */
|
||||
static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this)
|
||||
{
|
||||
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
drmModeRes *resources = NULL;
|
||||
uint64_t async_pageflip = 0;
|
||||
|
|
@ -1104,6 +1555,11 @@ static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this)
|
|||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
|
||||
|
||||
// Set ATOMIC & UNIVERSAL PLANES compatibility
|
||||
viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd);
|
||||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "DRM FD (%d) %s atomic", viddata->drm_fd, viddata->is_atomic ? "SUPPORTS" : "DOES NOT SUPPORT");
|
||||
|
||||
// Get all of the available connectors / devices / crtcs
|
||||
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
||||
if (!resources) {
|
||||
|
|
@ -1114,22 +1570,20 @@ static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this)
|
|||
/* Iterate on the available connectors. For every connected connector,
|
||||
we create an SDL_Display and add it to the list of SDL Displays. */
|
||||
for (i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector *connector = KMSDRM_drmModeGetConnector(viddata->drm_fd,
|
||||
resources->connectors[i]);
|
||||
|
||||
if (!connector) {
|
||||
drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
|
||||
if (!conn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes) {
|
||||
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
|
||||
/* If it's a connected connector with available videomodes, try to add
|
||||
an SDL Display representing it. KMSDRM_AddDisplay() is purposely void,
|
||||
so if it fails (no encoder for connector, no valid video mode for
|
||||
connector etc...) we can keep looking for connected connectors. */
|
||||
KMSDRM_AddDisplay(_this, connector, resources);
|
||||
KMSDRM_AddDisplay(_this, conn, resources);
|
||||
} else {
|
||||
// If it's not, free it now.
|
||||
KMSDRM_drmModeFreeConnector(connector);
|
||||
KMSDRM_drmModeFreeConnector(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1194,6 +1648,8 @@ static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata)
|
|||
}
|
||||
}
|
||||
|
||||
viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd);
|
||||
|
||||
// Set the FD as current DRM master.
|
||||
KMSDRM_drmSetMaster(viddata->drm_fd);
|
||||
|
||||
|
|
@ -1201,6 +1657,13 @@ static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata)
|
|||
viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
|
||||
if (!viddata->gbm_dev) {
|
||||
result = SDL_SetError("Couldn't create gbm device.");
|
||||
} else {
|
||||
// Setup the display plane. ONLY do this after dispdata has the right
|
||||
// crtc and connector, because these are used in this function.
|
||||
result = setup_plane(_this, &dispdata->display_plane, DRM_PLANE_TYPE_PRIMARY);
|
||||
if (!result) {
|
||||
SDL_SetError("can't find suitable display plane.");
|
||||
}
|
||||
}
|
||||
|
||||
viddata->gbm_init = true;
|
||||
|
|
@ -1213,6 +1676,12 @@ static void KMSDRM_GBMDeinit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata)
|
|||
{
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
|
||||
// Free display plane
|
||||
free_plane(&dispdata->display_plane);
|
||||
|
||||
// Free cursor plane (if still not freed)
|
||||
free_plane(&dispdata->cursor_plane);
|
||||
|
||||
/* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
|
||||
already called when we get here. */
|
||||
if (viddata->gbm_dev) {
|
||||
|
|
@ -1242,19 +1711,86 @@ static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
/**********************************************/
|
||||
// KMSDRM_WaitPageflip(_this, windata);
|
||||
|
||||
if (viddata->is_atomic) {
|
||||
|
||||
/* TODO : Continue investigating why this doesn't work. We should do this instead
|
||||
of making the display plane point to the TTY console, which isn't there
|
||||
after creating and destroying a Vulkan window. */
|
||||
|
||||
#if 0 // (note that this code has bitrotted a little, in addition to TODO comment above.)
|
||||
/* Disconnect the connector from the CRTC (remember: several connectors
|
||||
can read a CRTC), deactivate the CRTC, and set the PRIMARY PLANE props
|
||||
CRTC_ID and FB_ID to 0. Then we can destroy the GBM buffers and surface. */
|
||||
add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
|
||||
add_crtc_property(dispdata->atomic_req, dispdata->crtc , "MODE_ID", 0);
|
||||
add_crtc_property(dispdata->atomic_req, dispdata->crtc , "active", 0);
|
||||
/**********************************************/
|
||||
/* Wait for last issued pageflip to complete. */
|
||||
/**********************************************/
|
||||
KMSDRM_WaitPageFlip(_this, windata, -1);
|
||||
|
||||
plane_info.plane = dispdata->display_plane;
|
||||
plane_info.crtc_id = 0;
|
||||
plane_info.fb_id = 0;
|
||||
/***********************************************************************/
|
||||
/* Restore the original CRTC configuration: configue the crtc with the */
|
||||
/* original video mode and make it point to the original TTY buffer. */
|
||||
/***********************************************************************/
|
||||
|
||||
drm_atomic_set_plane_props(&plane_info);
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
|
||||
&dispdata->original_mode);
|
||||
|
||||
/* Issue atomic commit that is blocking and allows modesetting. */
|
||||
if (drm_atomic_commit(_this, true, true)) {
|
||||
SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
|
||||
/* If we failed to set the original mode, try to set the connector prefered mode. */
|
||||
if (ret && (dispdata->crtc->mode_valid == 0)) {
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
|
||||
&dispdata->original_mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
/************************************************************/
|
||||
/* Make the display plane point to the original TTY buffer. */
|
||||
/* We have to configure it's input and output scaling */
|
||||
/* parameters accordingly. */
|
||||
/************************************************************/
|
||||
|
||||
KMSDRM_PlaneInfo plane_info;
|
||||
SDL_zero(plane_info);
|
||||
plane_info.plane = dispdata->display_plane;
|
||||
plane_info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
plane_info.fb_id = dispdata->crtc.crtc->buffer_id;
|
||||
plane_info.src_w = dispdata->original_mode.hdisplay;
|
||||
plane_info.src_h = dispdata->original_mode.vdisplay;
|
||||
plane_info.crtc_w = dispdata->original_mode.hdisplay;
|
||||
plane_info.crtc_h = dispdata->original_mode.vdisplay;
|
||||
|
||||
drm_atomic_set_plane_props(&plane_info);
|
||||
|
||||
if (drm_atomic_commit(_this, true, false)) {
|
||||
SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
// Restore the original CRTC configuration: configure the crtc with the
|
||||
// original video mode and make it point to the original TTY buffer.
|
||||
/************************************************************************/
|
||||
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id,
|
||||
dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1,
|
||||
&dispdata->original_mode);
|
||||
|
||||
// If we failed to set the original mode, try to set the connector preferred mode.
|
||||
if (ret && (dispdata->crtc->mode_valid == 0)) {
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
|
||||
if (ret && (dispdata->crtc.crtc->mode_valid == 0)) {
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id,
|
||||
dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1,
|
||||
&dispdata->original_mode);
|
||||
}
|
||||
|
||||
|
|
@ -1462,6 +1998,10 @@ bool KMSDRM_VideoInit(SDL_VideoDevice *_this)
|
|||
result = SDL_SetError("error getting KMSDRM displays information");
|
||||
}
|
||||
|
||||
#if 0 // Use this if you ever need to see info on all available planes.
|
||||
get_planes_info(_this);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_INPUT_LINUXEV
|
||||
SDL_EVDEV_Init();
|
||||
SDL_EVDEV_SetVTSwitchCallbacks(KMSDRM_ReleaseVT, _this, KMSDRM_AcquireVT, _this);
|
||||
|
|
@ -1501,7 +2041,7 @@ void KMSDRM_VideoQuit(SDL_VideoDevice *_this)
|
|||
bool KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
||||
{
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
drmModeConnector *conn = dispdata->connector;
|
||||
drmModeConnector *conn = dispdata->connector.connector;
|
||||
SDL_DisplayMode mode;
|
||||
int i;
|
||||
|
||||
|
|
@ -1534,7 +2074,7 @@ bool KMSDRM_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SD
|
|||
SDL_VideoData *viddata = _this->internal;
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
SDL_DisplayModeData *modedata = mode->internal;
|
||||
drmModeConnector *conn = dispdata->connector;
|
||||
drmModeConnector *conn = dispdata->connector.connector;
|
||||
int i;
|
||||
|
||||
// Don't do anything if we are in Vulkan mode.
|
||||
|
|
@ -1570,7 +2110,7 @@ void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
}
|
||||
|
||||
// restore vrr state
|
||||
KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc->crtc_id, dispdata->saved_vrr);
|
||||
KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc.crtc->crtc_id, dispdata->saved_vrr);
|
||||
|
||||
viddata = windata->viddata;
|
||||
|
||||
|
|
@ -1632,7 +2172,7 @@ void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
|
||||
/**********************************************************************/
|
||||
// We simply IGNORE if it's a fullscreen window, window->flags don't
|
||||
// reflect it: if it's fullscreen, KMSDRM_SetWindwoFullscreen() will
|
||||
// reflect it: if it's fullscreen, KMSDRM_SetWindowFullscreen() will
|
||||
// be called by SDL later, and we can manage it there.
|
||||
/**********************************************************************/
|
||||
bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// Atomic KMSDRM backend originally written by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_kmsdrmvideo_h
|
||||
|
|
@ -32,6 +34,7 @@
|
|||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#ifndef DRM_FORMAT_MOD_INVALID
|
||||
#define DRM_FORMAT_MOD_INVALID 0x00ffffffffffffffULL
|
||||
|
|
@ -72,6 +75,24 @@
|
|||
#define GBM_BO_USE_LINEAR (1 << 4)
|
||||
#endif
|
||||
|
||||
typedef struct plane {
|
||||
drmModePlane *plane;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} plane;
|
||||
|
||||
typedef struct crtc {
|
||||
drmModeCrtc *crtc;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} crtc;
|
||||
|
||||
typedef struct connector {
|
||||
drmModeConnector *connector;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} connector;
|
||||
|
||||
struct SDL_VideoData
|
||||
{
|
||||
int devindex; // device index that was passed on creation
|
||||
|
|
@ -92,6 +113,7 @@ struct SDL_VideoData
|
|||
open 1 FD and create 1 gbm device. */
|
||||
bool gbm_init;
|
||||
|
||||
bool is_atomic; // true if atomic interfaces are supported.
|
||||
};
|
||||
|
||||
struct SDL_DisplayModeData
|
||||
|
|
@ -101,8 +123,11 @@ struct SDL_DisplayModeData
|
|||
|
||||
struct SDL_DisplayData
|
||||
{
|
||||
drmModeConnector *connector;
|
||||
drmModeCrtc *crtc;
|
||||
plane *display_plane;
|
||||
plane *cursor_plane;
|
||||
crtc crtc;
|
||||
connector connector;
|
||||
|
||||
drmModeModeInfo mode;
|
||||
drmModeModeInfo original_mode;
|
||||
drmModeModeInfo fullscreen_mode;
|
||||
|
|
@ -118,6 +143,15 @@ struct SDL_DisplayData
|
|||
int cursor_bo_drm_fd;
|
||||
uint64_t cursor_w, cursor_h;
|
||||
|
||||
/* Central atomic request list, used for the prop
|
||||
changeset related to pageflip in SwapWindow. */
|
||||
drmModeAtomicReq *atomic_req;
|
||||
|
||||
int kms_in_fence_fd;
|
||||
int kms_out_fence_fd;
|
||||
EGLSyncKHR kms_fence;
|
||||
EGLSyncKHR gpu_fence;
|
||||
|
||||
bool default_cursor_init;
|
||||
};
|
||||
|
||||
|
|
@ -137,6 +171,9 @@ struct SDL_WindowData
|
|||
|
||||
EGLSurface egl_surface;
|
||||
bool egl_surface_dirty;
|
||||
|
||||
/* This dictates what approach we'll use for SwapBuffers. */
|
||||
bool (*swap_window)(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
};
|
||||
|
||||
typedef struct KMSDRM_FBInfo
|
||||
|
|
@ -145,12 +182,37 @@ typedef struct KMSDRM_FBInfo
|
|||
uint32_t fb_id; // DRM framebuffer ID
|
||||
} KMSDRM_FBInfo;
|
||||
|
||||
typedef struct KMSDRM_PlaneInfo
|
||||
{
|
||||
struct plane *plane;
|
||||
uint32_t fb_id;
|
||||
uint32_t crtc_id;
|
||||
int32_t src_x;
|
||||
int32_t src_y;
|
||||
int32_t src_w;
|
||||
int32_t src_h;
|
||||
int32_t crtc_x;
|
||||
int32_t crtc_y;
|
||||
int32_t crtc_w;
|
||||
int32_t crtc_h;
|
||||
} KMSDRM_PlaneInfo;
|
||||
|
||||
// Helper functions
|
||||
extern bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern KMSDRM_FBInfo *KMSDRM_FBFromBO(SDL_VideoDevice *_this, struct gbm_bo *bo);
|
||||
extern KMSDRM_FBInfo *KMSDRM_FBFromBO2(SDL_VideoDevice *_this, struct gbm_bo *bo, int w, int h);
|
||||
extern bool KMSDRM_WaitPageflip(SDL_VideoDevice *_this, SDL_WindowData *windata);
|
||||
|
||||
// Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c
|
||||
void drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info);
|
||||
void drm_atomic_waitpending(SDL_VideoDevice *_this);
|
||||
int drm_atomic_commit(SDL_VideoDevice *_this, bool blocking, bool allow_modeset);
|
||||
int add_plane_property(drmModeAtomicReq *req, struct plane *plane, const char *name, uint64_t value);
|
||||
int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc, const char *name, uint64_t value);
|
||||
int add_connector_property(drmModeAtomicReq *req, struct connector *connector, const char *name, uint64_t value);
|
||||
bool setup_plane(SDL_VideoDevice *_this, struct plane **plane, uint32_t plane_type);
|
||||
void free_plane(struct plane **plane);
|
||||
|
||||
/****************************************************************************/
|
||||
// SDL_VideoDevice functions declaration
|
||||
/****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -140,8 +140,7 @@ void KMSDRM_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
// members of the VkInstanceCreateInfo struct passed to
|
||||
// vkCreateInstance().
|
||||
/*********************************************************************/
|
||||
char const* const* KMSDRM_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *KMSDRM_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForKMSDRM[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
|
||||
|
|
@ -171,7 +170,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
|||
uint32_t display_count;
|
||||
uint32_t mode_count;
|
||||
uint32_t plane_count;
|
||||
uint32_t plane = UINT32_MAX;
|
||||
uint32_t _plane = UINT32_MAX;
|
||||
|
||||
VkPhysicalDevice *physical_devices = NULL;
|
||||
VkPhysicalDeviceProperties *device_props = NULL;
|
||||
|
|
@ -450,13 +449,13 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
|||
vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps);
|
||||
if (plane_caps.supportedAlpha == alpha_mode) {
|
||||
// Yep, this plane is alright.
|
||||
plane = i;
|
||||
_plane = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find an appropriate plane, error out.
|
||||
if (plane == UINT32_MAX) {
|
||||
if (_plane == UINT32_MAX) {
|
||||
SDL_SetError("Vulkan couldn't find an appropriate plane.");
|
||||
goto clean;
|
||||
}
|
||||
|
|
@ -471,7 +470,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
|||
SDL_zero(display_plane_surface_create_info);
|
||||
display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
display_plane_surface_create_info.displayMode = display_mode;
|
||||
display_plane_surface_create_info.planeIndex = plane;
|
||||
display_plane_surface_create_info.planeIndex = _plane;
|
||||
display_plane_surface_create_info.imageExtent = image_size;
|
||||
display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
display_plane_surface_create_info.alphaMode = alpha_mode;
|
||||
|
|
|
|||
|
|
@ -167,8 +167,7 @@ void UIKit_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* UIKit_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *UIKit_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForUIKit[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME
|
||||
|
|
|
|||
|
|
@ -117,8 +117,7 @@ void VIVANTE_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* VIVANTE_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *VIVANTE_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForVivante[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
|
||||
|
|
|
|||
|
|
@ -701,6 +701,10 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
|
|||
float fAmount = (float)amount / WHEEL_DELTA;
|
||||
SDL_SendMouseWheel(WIN_GetEventTimestamp(), window, mouseID, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL);
|
||||
}
|
||||
|
||||
/* Invalidate the mouse button flags. If we don't do this then disabling raw input
|
||||
will cause held down mouse buttons to persist when released. */
|
||||
windowdata->mouse_button_flags = (WPARAM)-1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,9 @@ static void GAMEINPUT_InitialMouseReading(WIN_GameInputData *data, SDL_Window *w
|
|||
bool down = ((state.buttons & mask) != 0);
|
||||
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
|
||||
}
|
||||
|
||||
// Invalidate mouse button flags
|
||||
window->internal->mouse_button_flags = (WPARAM)-1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,6 +311,9 @@ static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *wind
|
|||
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate mouse button flags
|
||||
window->internal->mouse_button_flags = (WPARAM)-1;
|
||||
}
|
||||
if (delta.wheelX || delta.wheelY) {
|
||||
float fAmountX = (float)delta.wheelX / WHEEL_DELTA;
|
||||
|
|
|
|||
|
|
@ -110,8 +110,7 @@ void WIN_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForWin32[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME
|
||||
|
|
|
|||
|
|
@ -144,8 +144,7 @@ void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
char const * const *X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
|
||||
{
|
||||
SDL_VideoData *videoData = _this->internal;
|
||||
if (videoData->vulkan_xlib_xcb_library) {
|
||||
|
|
|
|||
|
|
@ -1611,8 +1611,7 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
|
|||
has_gyro = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO);
|
||||
|
||||
if (has_accel || has_gyro) {
|
||||
const float gyro_sensor_rate = has_gyro ? SDL_GetGamepadSensorDataRate(gamepad, SDL_SENSOR_GYRO) : 0;
|
||||
const int SENSOR_UPDATE_INTERVAL_MS = gyro_sensor_rate > 0.0f ? (int)( 1000.0f / gyro_sensor_rate ) : 100;
|
||||
const int SENSOR_UPDATE_INTERVAL_MS = 100;
|
||||
Uint64 now = SDL_GetTicks();
|
||||
|
||||
if (now >= ctx->last_sensor_update + SENSOR_UPDATE_INTERVAL_MS) {
|
||||
|
|
@ -1622,6 +1621,7 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
|
|||
if (has_gyro) {
|
||||
SDL_GetGamepadSensorData(gamepad, SDL_SENSOR_GYRO, ctx->gyro_data, SDL_arraysize(ctx->gyro_data));
|
||||
}
|
||||
ctx->last_sensor_update = now;
|
||||
}
|
||||
|
||||
if (has_accel) {
|
||||
|
|
@ -1639,8 +1639,7 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
|
|||
SDLTest_DrawString(ctx->renderer, x + center + 2.0f, y, text);
|
||||
|
||||
|
||||
/* Display a smoothed version of the above for the sake of turntable tests */
|
||||
|
||||
/* Display the testcontroller tool's evaluation of drift. This is also useful to get an average rate of turn in calibrated turntable tests. */
|
||||
if (ctx->gyro_drift_correction_data[0] != 0.0f && ctx->gyro_drift_correction_data[2] != 0.0f && ctx->gyro_drift_correction_data[2] != 0.0f )
|
||||
{
|
||||
y += ctx->button_height + 2.0f;
|
||||
|
|
@ -1652,7 +1651,7 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
|
|||
|
||||
}
|
||||
|
||||
ctx->last_sensor_update = now;
|
||||
|
||||
}
|
||||
}
|
||||
SDL_free(mapping);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ extern void DestroyGamepadButton(GamepadButton *ctx);
|
|||
|
||||
/* Gyro element Display */
|
||||
/* If you want to calbirate against a known rotation (i.e. a turn table test) Increase ACCELEROMETER_NOISE_THRESHOLD to about 5, or drift correction will be constantly reset.*/
|
||||
#define ACCELEROMETER_NOISE_THRESHOLD 0.125f
|
||||
#define ACCELEROMETER_NOISE_THRESHOLD 0.5f
|
||||
typedef struct Quaternion Quaternion;
|
||||
typedef struct GyroDisplay GyroDisplay;
|
||||
|
||||
|
|
|
|||
|
|
@ -1409,11 +1409,13 @@ static void UpdateGamepadOrientation( Uint64 delta_time_ns )
|
|||
|
||||
static void HandleGamepadSensorEvent( SDL_Event* event )
|
||||
{
|
||||
if (!controller)
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller->id != event->gsensor.which)
|
||||
if (controller->id != event->gsensor.which) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->gsensor.sensor == SDL_SENSOR_GYRO) {
|
||||
HandleGamepadGyroEvent(event);
|
||||
|
|
|
|||
|
|
@ -679,6 +679,16 @@ void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID p
|
|||
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex);
|
||||
}
|
||||
|
||||
#if LIBAVUTIL_VERSION_MAJOR >= 59
|
||||
static void AddQueueFamily(AVVulkanDeviceContext *ctx, int idx, int num, VkQueueFlagBits flags)
|
||||
{
|
||||
AVVulkanDeviceQueueFamily *entry = &ctx->qf[ctx->nb_qf++];
|
||||
entry->idx = idx;
|
||||
entry->num = num;
|
||||
entry->flags = flags;
|
||||
}
|
||||
#endif /* LIBAVUTIL_VERSION_MAJOR */
|
||||
|
||||
void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx)
|
||||
{
|
||||
ctx->get_proc_addr = context->vkGetInstanceProcAddr;
|
||||
|
|
@ -690,6 +700,12 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon
|
|||
ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount;
|
||||
ctx->enabled_dev_extensions = context->deviceExtensions;
|
||||
ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount;
|
||||
#if LIBAVUTIL_VERSION_MAJOR >= 59
|
||||
AddQueueFamily(ctx, context->graphicsQueueFamilyIndex, context->graphicsQueueCount, VK_QUEUE_GRAPHICS_BIT);
|
||||
AddQueueFamily(ctx, context->transferQueueFamilyIndex, context->transferQueueCount, VK_QUEUE_TRANSFER_BIT);
|
||||
AddQueueFamily(ctx, context->computeQueueFamilyIndex, context->computeQueueCount, VK_QUEUE_COMPUTE_BIT);
|
||||
AddQueueFamily(ctx, context->decodeQueueFamilyIndex, context->decodeQueueCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR);
|
||||
#else
|
||||
ctx->queue_family_index = context->graphicsQueueFamilyIndex;
|
||||
ctx->nb_graphics_queues = context->graphicsQueueCount;
|
||||
ctx->queue_family_tx_index = context->transferQueueFamilyIndex;
|
||||
|
|
@ -700,6 +716,7 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon
|
|||
ctx->nb_encode_queues = 0;
|
||||
ctx->queue_family_decode_index = context->decodeQueueFamilyIndex;
|
||||
ctx->nb_decode_queues = context->decodeQueueCount;
|
||||
#endif /* LIBAVUTIL_VERSION_MAJOR */
|
||||
}
|
||||
|
||||
static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer)
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
if (e.key.key == SDLK_M) {
|
||||
if (SDL_SetWindowParent(w2, w2)) {
|
||||
if (SDL_SetWindowParent(w2, w1)) {
|
||||
if (SDL_SetWindowModal(w2, true)) {
|
||||
SDL_SetWindowTitle(w2, "Modal Window");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue