Merge commit '411251c6242b04119edc41ce83f09f0714e2d32b' as 'external/SDL'
This commit is contained in:
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_core_unsupported.h"
|
||||
|
||||
#ifndef SDL_VIDEO_DRIVER_X11
|
||||
|
||||
void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !SDL_VIDEO_DRIVER_X11 */
|
||||
|
||||
#ifndef SDL_PLATFORM_LINUX
|
||||
|
||||
bool SDL_SetLinuxThreadPriority(Sint64 threadID, int priority)
|
||||
{
|
||||
(void)threadID;
|
||||
(void)priority;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_SetLinuxThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)
|
||||
{
|
||||
(void)threadID;
|
||||
(void)sdlPriority;
|
||||
(void)schedPolicy;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
#endif /* !SDL_PLATFORM_LINUX */
|
||||
|
||||
#ifndef SDL_PLATFORM_GDK
|
||||
|
||||
void SDL_GDKSuspendComplete(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_GetGDKDefaultUser(XUserHandle *outUserHandle)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_GDKSuspendGPU(SDL_GPUDevice *device)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_GDKResumeGPU(SDL_GPUDevice *device)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_GDKSuspendRenderer(SDL_Renderer *renderer)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_GDKResumeRenderer(SDL_Renderer *renderer)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !SDL_PLATFORM_GDK */
|
||||
|
||||
#if !defined(SDL_PLATFORM_WINDOWS)
|
||||
|
||||
bool SDL_RegisterApp(const char *name, Uint32 style, void *hInst)
|
||||
{
|
||||
(void)name;
|
||||
(void)style;
|
||||
(void)hInst;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_SetWindowsMessageHook(void *callback, void *userdata)
|
||||
{
|
||||
(void)callback;
|
||||
(void)userdata;
|
||||
SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_UnregisterApp(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
}
|
||||
|
||||
#endif /* !SDL_PLATFORM_WINDOWS */
|
||||
|
||||
#ifndef SDL_PLATFORM_ANDROID
|
||||
|
||||
void SDL_SendAndroidBackButton(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
}
|
||||
|
||||
void *SDL_GetAndroidActivity(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *SDL_GetAndroidCachePath(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *SDL_GetAndroidExternalStoragePath(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Uint32 SDL_GetAndroidExternalStorageState(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return 0;
|
||||
}
|
||||
const char *SDL_GetAndroidInternalStoragePath(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *SDL_GetAndroidJNIEnv(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool SDL_RequestAndroidPermission(const char *permission, SDL_RequestAndroidPermissionCallback cb, void *userdata)
|
||||
{
|
||||
(void)permission;
|
||||
(void)cb;
|
||||
(void)userdata;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_SendAndroidMessage(Uint32 command, int param)
|
||||
{
|
||||
(void)command;
|
||||
(void)param;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_ShowAndroidToast(const char *message, int duration, int gravity, int xoffset, int yoffset)
|
||||
{
|
||||
(void)message;
|
||||
(void)duration;
|
||||
(void)gravity;
|
||||
(void)xoffset;
|
||||
(void)yoffset;
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
int SDL_GetAndroidSDKVersion(void)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_IsChromebook(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL_IsDeXMode(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return false;
|
||||
}
|
||||
|
||||
Sint32 JNI_OnLoad(JavaVM *vm, void *reserved)
|
||||
{
|
||||
(void)vm;
|
||||
(void)reserved;
|
||||
return 0x00010004; // JNI_VERSION_1_4
|
||||
}
|
||||
#endif
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_VIDEO_DRIVER_X11
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata);
|
||||
#endif
|
||||
|
||||
#ifndef SDL_PLATFORM_LINUX
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_SetLinuxThreadPriority(Sint64 threadID, int priority);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_SetLinuxThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy);
|
||||
#endif
|
||||
|
||||
#if !defined(SDL_PLATFORM_GDK)
|
||||
typedef struct XUserHandle XUserHandle;
|
||||
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendComplete(void);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_GetGDKDefaultUser(XUserHandle *outUserHandle);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendGPU(SDL_GPUDevice *device);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeGPU(SDL_GPUDevice *device);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeRenderer(SDL_Renderer *renderer);
|
||||
#endif /* !SDL_PLATFORM_GDK */
|
||||
|
||||
#if !defined(SDL_PLATFORM_WINDOWS)
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_RegisterApp(const char *name, Uint32 style, void *hInst);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_SetWindowsMessageHook(void *callback, void *userdata);
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_UnregisterApp(void);
|
||||
#endif /* !SDL_PLATFORM_WINDOWS */
|
||||
|
||||
#if !defined(SDL_PLATFORM_ANDROID)
|
||||
|
||||
typedef void *SDL_RequestAndroidPermissionCallback;
|
||||
typedef void *JavaVM;
|
||||
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_SendAndroidBackButton(void);
|
||||
extern SDL_DECLSPEC void * SDLCALL SDL_GetAndroidActivity(void);
|
||||
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAndroidCachePath(void);
|
||||
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAndroidExternalStoragePath(void);
|
||||
extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAndroidExternalStorageState(void);
|
||||
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAndroidInternalStoragePath(void);
|
||||
extern SDL_DECLSPEC void * SDLCALL SDL_GetAndroidJNIEnv(void);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_RequestAndroidPermission(const char *permission, SDL_RequestAndroidPermissionCallback cb, void *userdata);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_SendAndroidMessage(Uint32 command, int param);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_ShowAndroidToast(const char *message, int duration, int gravity, int xoffset, int yoffset);
|
||||
extern SDL_DECLSPEC int SDLCALL SDL_GetAndroidSDKVersion(void);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_IsChromebook(void);
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_IsDeXMode(void);
|
||||
extern SDL_DECLSPEC Sint32 SDLCALL JNI_OnLoad(JavaVM *vm, void *reserved);
|
||||
#endif /* !SDL_PLATFORM_ANDROID */
|
||||
+3426
File diff suppressed because it is too large
Load Diff
+168
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_android_h
|
||||
#define SDL_android_h
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
extern "C" {
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
||||
|
||||
#include <EGL/eglplatform.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "../../audio/SDL_sysaudio.h"
|
||||
|
||||
// this appears to be broken right now (on Android, not SDL, I think...?).
|
||||
#define ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 0
|
||||
|
||||
// Life cycle
|
||||
typedef enum
|
||||
{
|
||||
SDL_ANDROID_LIFECYCLE_WAKE,
|
||||
SDL_ANDROID_LIFECYCLE_PAUSE,
|
||||
SDL_ANDROID_LIFECYCLE_RESUME,
|
||||
SDL_ANDROID_LIFECYCLE_LOWMEMORY,
|
||||
SDL_ANDROID_LIFECYCLE_DESTROY,
|
||||
SDL_NUM_ANDROID_LIFECYCLE_EVENTS
|
||||
} SDL_AndroidLifecycleEvent;
|
||||
|
||||
void Android_SendLifecycleEvent(SDL_AndroidLifecycleEvent event);
|
||||
bool Android_WaitLifecycleEvent(SDL_AndroidLifecycleEvent *event, Sint64 timeoutNS);
|
||||
|
||||
void Android_LockActivityMutex(void);
|
||||
void Android_UnlockActivityMutex(void);
|
||||
|
||||
void Android_SetAllowRecreateActivity(bool enabled);
|
||||
|
||||
// Interface from the SDL library into the Android Java activity
|
||||
extern void Android_JNI_SetActivityTitle(const char *title);
|
||||
extern void Android_JNI_SetWindowStyle(bool fullscreen);
|
||||
extern void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint);
|
||||
extern void Android_JNI_MinimizeWindow(void);
|
||||
extern bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
|
||||
|
||||
extern bool Android_JNI_GetAccelerometerValues(float values[3]);
|
||||
extern void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect);
|
||||
extern void Android_JNI_HideScreenKeyboard(void);
|
||||
extern ANativeWindow *Android_JNI_GetNativeWindow(void);
|
||||
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
|
||||
|
||||
// Audio support
|
||||
void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
|
||||
void Android_StopAudioHotplug(void);
|
||||
extern void Android_AudioThreadInit(SDL_AudioDevice *device);
|
||||
|
||||
// Detecting device type
|
||||
extern bool Android_IsDeXMode(void);
|
||||
extern bool Android_IsChromebook(void);
|
||||
|
||||
bool Android_JNI_FileOpen(void **puserdata, const char *fileName, const char *mode);
|
||||
Sint64 Android_JNI_FileSize(void *userdata);
|
||||
Sint64 Android_JNI_FileSeek(void *userdata, Sint64 offset, SDL_IOWhence whence);
|
||||
size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOStatus *status);
|
||||
size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status);
|
||||
bool Android_JNI_FileClose(void *userdata);
|
||||
bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata);
|
||||
bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info);
|
||||
|
||||
// Environment support
|
||||
void Android_JNI_GetManifestEnvironmentVariables(void);
|
||||
int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode);
|
||||
|
||||
// Clipboard support
|
||||
bool Android_JNI_SetClipboardText(const char *text);
|
||||
char *Android_JNI_GetClipboardText(void);
|
||||
bool Android_JNI_HasClipboardText(void);
|
||||
|
||||
// Power support
|
||||
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent);
|
||||
|
||||
// Joystick support
|
||||
void Android_JNI_PollInputDevices(void);
|
||||
void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue);
|
||||
void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled);
|
||||
|
||||
// Haptic support
|
||||
void Android_JNI_PollHapticDevices(void);
|
||||
void Android_JNI_HapticRun(int device_id, float intensity, int length);
|
||||
void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length);
|
||||
void Android_JNI_HapticStop(int device_id);
|
||||
|
||||
// Video
|
||||
bool Android_JNI_SuspendScreenSaver(bool suspend);
|
||||
|
||||
// Touch support
|
||||
void Android_JNI_InitTouch(void);
|
||||
|
||||
// Threads
|
||||
#include <jni.h>
|
||||
JNIEnv *Android_JNI_GetEnv(void);
|
||||
bool Android_JNI_SetupThread(void);
|
||||
|
||||
// Locale
|
||||
bool Android_JNI_GetLocale(char *buf, size_t buflen);
|
||||
|
||||
// Generic messages
|
||||
bool Android_JNI_SendMessage(int command, int param);
|
||||
|
||||
// MessageBox
|
||||
bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
|
||||
|
||||
// Cursor support
|
||||
int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
|
||||
void Android_JNI_DestroyCustomCursor(int cursorID);
|
||||
bool Android_JNI_SetCustomCursor(int cursorID);
|
||||
bool Android_JNI_SetSystemCursor(int cursorID);
|
||||
|
||||
// Relative mouse support
|
||||
bool Android_JNI_SupportsRelativeMouse(void);
|
||||
bool Android_JNI_SetRelativeMouseEnabled(bool enabled);
|
||||
|
||||
// Show toast notification
|
||||
bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset);
|
||||
|
||||
bool Android_JNI_OpenURL(const char *url);
|
||||
|
||||
int SDL_GetAndroidSDKVersion(void);
|
||||
|
||||
bool SDL_IsAndroidTablet(void);
|
||||
bool SDL_IsAndroidTV(void);
|
||||
|
||||
// File Dialogs
|
||||
bool Android_JNI_OpenFileDialog(SDL_DialogFileCallback callback, void *userdata,
|
||||
const SDL_DialogFileFilter *filters, int nfilters, bool forwrite,
|
||||
bool multiple);
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
||||
|
||||
#endif // SDL_android_h
|
||||
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_DOS)
|
||||
|
||||
#include "SDL_dos.h"
|
||||
|
||||
void *DOS_AllocateConventionalMemory(const int len, _go32_dpmi_seginfo *seginfo)
|
||||
{
|
||||
seginfo->size = (len + 15) / 16; // this is in "paragraphs"
|
||||
if (_go32_dpmi_allocate_dos_memory(seginfo) != 0) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
// No need to lock: DPMI 0.9 §3.3 guarantees the first megabyte is always
|
||||
// committed and locked. CWSDPMI (the DJGPP DPMI host) doesn't support
|
||||
// virtual memory at all, so conventional memory is never paged out.
|
||||
return DOS_PhysicalToLinear(seginfo->rm_segment * 16);
|
||||
}
|
||||
|
||||
void *DOS_AllocateDMAMemory(const int len, _go32_dpmi_seginfo *seginfo)
|
||||
{
|
||||
// ISA DMA transfers cannot cross a 64 KB physical page boundary (hardware
|
||||
// limitation of the 8237 DMA controller). Allocating 2× the requested size
|
||||
// guarantees at least one contiguous `len`-byte region that doesn't straddle
|
||||
// a boundary. This is the standard technique used by Allegro, MIDAS, and
|
||||
// every other DOS audio library; allocate-check-retry would add complexity
|
||||
// for zero benefit.
|
||||
uint8_t *ptr = (uint8_t *)DOS_AllocateConventionalMemory(len * 2, seginfo);
|
||||
if (!ptr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if we're past the end of a page, use the second half of the block.
|
||||
const uint32_t physical = (seginfo->rm_segment * 16);
|
||||
if ((physical >> 16) != ((physical + len) >> 16)) {
|
||||
ptr += len;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void DOS_FreeConventionalMemory(_go32_dpmi_seginfo *seginfo)
|
||||
{
|
||||
_go32_dpmi_free_dos_memory(seginfo);
|
||||
}
|
||||
|
||||
char *DOS_GetFarPtrCString(const Uint32 segoffset)
|
||||
{
|
||||
if (!segoffset) { // let's just treat this as a NULL pointer.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const unsigned long ofs = (unsigned long)(((segoffset & 0xFFFF0000) >> 12) + (segoffset & 0xFFFF));
|
||||
size_t len;
|
||||
|
||||
for (len = 0; _farpeekb(_dos_ds, ofs + len) != '\0'; len++) {
|
||||
}
|
||||
|
||||
len++; // null terminator.
|
||||
char *retval = SDL_malloc(len);
|
||||
if (!retval) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
retval[i] = (char)_farpeekb(_dos_ds, ofs + i);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DOS_HookInterrupt(int irq, DOS_InterruptHookFn fn, DOS_InterruptHook *hook)
|
||||
{
|
||||
SDL_assert(irq >= 0 && irq <= 15);
|
||||
SDL_assert(fn != NULL);
|
||||
SDL_assert(hook != NULL);
|
||||
hook->fn = fn;
|
||||
hook->irq = irq;
|
||||
hook->interrupt_vector = DOS_IRQToVector(irq);
|
||||
hook->irq_handler_seginfo.pm_selector = _go32_my_cs();
|
||||
hook->irq_handler_seginfo.pm_offset = (uint32_t)fn;
|
||||
_go32_dpmi_get_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->original_irq_handler_seginfo);
|
||||
_go32_dpmi_chain_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->irq_handler_seginfo);
|
||||
|
||||
// enable interrupt on the correct PIC
|
||||
if (irq > 7) {
|
||||
outportb(PIC2_DATA, inportb(PIC2_DATA) & ~(1 << (irq - 8))); // unmask on slave PIC
|
||||
outportb(PIC1_DATA, inportb(PIC1_DATA) & ~(1 << 2)); // ensure cascade (IRQ2) is unmasked
|
||||
} else {
|
||||
outportb(PIC1_DATA, inportb(PIC1_DATA) & ~(1 << irq)); // unmask on master PIC
|
||||
}
|
||||
}
|
||||
|
||||
void DOS_UnhookInterrupt(DOS_InterruptHook *hook, bool disable_interrupt)
|
||||
{
|
||||
if (!hook || !hook->fn) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_assert(hook->interrupt_vector > 0);
|
||||
SDL_assert(hook->irq > 0);
|
||||
|
||||
if (disable_interrupt) {
|
||||
if (hook->irq > 7) {
|
||||
outportb(PIC2_DATA, inportb(PIC2_DATA) | (1 << (hook->irq - 8))); // mask on slave PIC
|
||||
} else {
|
||||
outportb(PIC1_DATA, inportb(PIC1_DATA) | (1 << hook->irq)); // mask on master PIC
|
||||
}
|
||||
}
|
||||
|
||||
_go32_dpmi_set_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->original_irq_handler_seginfo);
|
||||
|
||||
// Note: _go32_dpmi_chain_protected_mode_interrupt_vector internally
|
||||
// allocates a wrapper, but it is NOT safe to free it with
|
||||
// _go32_dpmi_free_iret_wrapper — that function expects a wrapper
|
||||
// allocated by _go32_dpmi_allocate_iret_wrapper, and calling it on
|
||||
// a chain-allocated wrapper causes a page fault on exit. The wrapper
|
||||
// is a few bytes of conventional memory that the OS reclaims when the
|
||||
// process terminates, so the leak is harmless.
|
||||
|
||||
SDL_zerop(hook);
|
||||
}
|
||||
|
||||
#endif // defined(SDL_PLATFORM_DOS)
|
||||
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dos_h_
|
||||
#define SDL_dos_h_
|
||||
|
||||
#include <sys/farptr.h>
|
||||
|
||||
// our MS-DOS port depends on the "fat DS" trick. djgpp docs try to warn you
|
||||
// away from this, but if it was good enough for Quake 1, it's good enough
|
||||
// for us!
|
||||
#include <sys/nearptr.h>
|
||||
|
||||
// We are obviously a 32-bit protected mode (DPMI) program.
|
||||
#include <dpmi.h>
|
||||
|
||||
// this is djgpp-specific.
|
||||
#include <go32.h>
|
||||
|
||||
// this is DOS PC stuff, like interrupts and Intel i/o ports.
|
||||
#include <pc.h>
|
||||
|
||||
// 8259 PIC (Programmable Interrupt Controller) ports and commands
|
||||
#define PIC1_COMMAND 0x20 // master PIC command port
|
||||
#define PIC1_DATA 0x21 // master PIC data (mask) port
|
||||
#define PIC2_COMMAND 0xA0 // slave PIC command port
|
||||
#define PIC2_DATA 0xA1 // slave PIC data (mask) port
|
||||
#define PIC_EOI 0x20 // end-of-interrupt command
|
||||
|
||||
// Lock a range of code so it won't be paged out during interrupts.
|
||||
// Usage: DOS_LockCode(function_name, function_end_label)
|
||||
// The function_end_label must be defined immediately after the function.
|
||||
#define DOS_LockCode(start, end) \
|
||||
_go32_dpmi_lock_code((void *)(start), (char *)(end) - (char *)(start))
|
||||
|
||||
// Lock a range of data so it won't be paged out during interrupts.
|
||||
#define DOS_LockData(var, size) \
|
||||
_go32_dpmi_lock_data((void *)&(var), (size))
|
||||
|
||||
// Lock a single variable.
|
||||
#define DOS_LockVariable(var) \
|
||||
DOS_LockData(var, sizeof(var))
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This uses the "fat DS" trick to convert a physical address to a valid
|
||||
// C pointer usable from protected mode.
|
||||
SDL_FORCE_INLINE void *DOS_PhysicalToLinear(const Uint32 physical)
|
||||
{
|
||||
__djgpp_nearptr_enable(); // We need to re-enable this for large applications to work.
|
||||
return (void *)(physical + __djgpp_conventional_base);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Uint32 DOS_LinearToPhysical(void *linear)
|
||||
{
|
||||
return ((Uint32)linear) - __djgpp_conventional_base;
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE int DOS_IRQToVector(int irq)
|
||||
{
|
||||
return irq + ((irq > 7) ? 104 : 8);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE void DOS_DisableInterrupts(void)
|
||||
{
|
||||
__asm__ __volatile__("cli\n");
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE void DOS_EnableInterrupts(void)
|
||||
{
|
||||
__asm__ __volatile__("sti\n");
|
||||
}
|
||||
|
||||
// Grab a single byte from a segment:offset.
|
||||
SDL_FORCE_INLINE Uint8 DOS_PeekUint8(const Uint32 segoffset)
|
||||
{
|
||||
return (Uint8)_farpeekb(_dos_ds, ((segoffset & 0xFFFF0000) >> 12) + (segoffset & 0xFFFF));
|
||||
}
|
||||
|
||||
// Grab a single 16-bit word from a segment:offset.
|
||||
SDL_FORCE_INLINE Uint16 DOS_PeekUint16(const Uint32 segoffset)
|
||||
{
|
||||
return (Uint16)_farpeekw(_dos_ds, ((segoffset & 0xFFFF0000) >> 12) + (segoffset & 0xFFFF));
|
||||
}
|
||||
|
||||
// Grab a single 32-bit dword from a segment:offset.
|
||||
SDL_FORCE_INLINE Uint32 DOS_PeekUint32(const Uint32 segoffset)
|
||||
{
|
||||
return (Uint32)_farpeekl(_dos_ds, ((segoffset & 0xFFFF0000) >> 12) + (segoffset & 0xFFFF));
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE void DOS_EndOfInterrupt(int irq)
|
||||
{
|
||||
if (irq > 7) {
|
||||
outportb(PIC2_COMMAND, PIC_EOI);
|
||||
}
|
||||
outportb(PIC1_COMMAND, PIC_EOI);
|
||||
}
|
||||
|
||||
// Allocate memory under the 640k line; various real mode services and DMA transfers need this.
|
||||
// malloc() returns data above 640k because we're a protected mode, 32-bit process, so this is
|
||||
// only for specific needs.
|
||||
extern void *DOS_AllocateConventionalMemory(const int len, _go32_dpmi_seginfo *seginfo);
|
||||
|
||||
// Allocate conventional memory suitable for DMA transfers.
|
||||
extern void *DOS_AllocateDMAMemory(const int len, _go32_dpmi_seginfo *seginfo);
|
||||
|
||||
// Free conventional (or DMA, which _is_ conventional) memory.
|
||||
extern void DOS_FreeConventionalMemory(_go32_dpmi_seginfo *seginfo);
|
||||
|
||||
// Get a SDL_malloc'd copy of a null-terminated string located at a real-mode segment:offset. This makes no promises about character encoding.
|
||||
char *DOS_GetFarPtrCString(const Uint32 segoffset);
|
||||
|
||||
typedef void (*DOS_InterruptHookFn)(void);
|
||||
typedef struct DOS_InterruptHook
|
||||
{
|
||||
DOS_InterruptHookFn fn;
|
||||
int irq;
|
||||
int interrupt_vector; // this is the _vector_ number, not the IRQ number!
|
||||
_go32_dpmi_seginfo irq_handler_seginfo;
|
||||
_go32_dpmi_seginfo original_irq_handler_seginfo;
|
||||
|
||||
} DOS_InterruptHook;
|
||||
|
||||
void DOS_HookInterrupt(int irq, DOS_InterruptHookFn fn, DOS_InterruptHook *hook); // `irq` is the IRQ number, not the interrupt vector number!
|
||||
void DOS_UnhookInterrupt(DOS_InterruptHook *hook, bool disable_interrupt);
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_dos_h_
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PLATFORM_DOS
|
||||
|
||||
#include "SDL_dos.h"
|
||||
#include "SDL_dos_scheduler.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
/* DJGPP's jmp_buf is defined as:
|
||||
typedef struct __jmp_buf {
|
||||
unsigned long __eax, __ebx, __ecx, __edx, __esi;
|
||||
unsigned long __edi, __ebp, __esp, __eip, __eflags;
|
||||
unsigned short __cs, __ds, __es, __fs, __gs, __ss;
|
||||
unsigned long __sigmask, __signum, __exception_ptr;
|
||||
unsigned char __fpu_state[108];
|
||||
} jmp_buf[1];
|
||||
We patch __esp, __ebp, and __eip to bootstrap new thread contexts. */
|
||||
|
||||
// Thread table — static array, no dynamic allocation needed
|
||||
static DOS_ThreadContext threads[DOS_MAX_THREADS];
|
||||
static int current_thread = 0; // Index of currently running thread
|
||||
static bool scheduler_initialized = false;
|
||||
|
||||
// Find the next runnable thread using round-robin scheduling.
|
||||
// Returns thread ID, or -1 if no other thread is runnable.
|
||||
static int FindNextRunnable(int start)
|
||||
{
|
||||
for (int i = 1; i <= DOS_MAX_THREADS; i++) {
|
||||
int idx = (start + i) % DOS_MAX_THREADS;
|
||||
if (threads[idx].state == DOS_THREAD_READY) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return -1; // No other thread is runnable
|
||||
}
|
||||
|
||||
// Trampoline function that runs on a new thread's stack.
|
||||
// This is jumped to from DOS_Yield when the new thread runs for the first time.
|
||||
// We use a global to pass the thread ID since we just switched stacks.
|
||||
static volatile int trampoline_thread_id;
|
||||
|
||||
static void ThreadTrampoline(void)
|
||||
{
|
||||
int tid = trampoline_thread_id;
|
||||
DOS_ThreadContext *ctx = &threads[tid];
|
||||
|
||||
// Run the user's thread function
|
||||
int result = ctx->entry_fn(ctx->entry_arg);
|
||||
|
||||
// Thread is done
|
||||
DOS_ExitThread(result);
|
||||
|
||||
// Should never reach here
|
||||
for (;;) {
|
||||
}
|
||||
}
|
||||
|
||||
void DOS_SchedulerInit(void)
|
||||
{
|
||||
if (scheduler_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_memset(threads, 0, sizeof(threads));
|
||||
|
||||
// Thread 0 is the main thread (already running)
|
||||
threads[0].state = DOS_THREAD_RUNNING;
|
||||
threads[0].id = 0;
|
||||
threads[0].join_waiter = -1;
|
||||
current_thread = 0;
|
||||
|
||||
scheduler_initialized = true;
|
||||
|
||||
_go32_dpmi_lock_data((void *)threads, sizeof(threads));
|
||||
_go32_dpmi_lock_data((void *)¤t_thread, sizeof(current_thread));
|
||||
_go32_dpmi_lock_data((void *)&scheduler_initialized, sizeof(scheduler_initialized));
|
||||
_go32_dpmi_lock_data((void *)&trampoline_thread_id, sizeof(trampoline_thread_id));
|
||||
}
|
||||
|
||||
void DOS_SchedulerQuit(void)
|
||||
{
|
||||
// Clean up any remaining threads
|
||||
for (int i = 1; i < DOS_MAX_THREADS; i++) {
|
||||
if (threads[i].state != DOS_THREAD_FREE) {
|
||||
SDL_assert(threads[i].state == DOS_THREAD_FINISHED);
|
||||
DOS_DestroyThread(i);
|
||||
}
|
||||
}
|
||||
|
||||
scheduler_initialized = false;
|
||||
}
|
||||
|
||||
int DOS_CreateThread(int (*fn)(void *), void *arg, size_t stack_size)
|
||||
{
|
||||
if (!scheduler_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find a free slot
|
||||
int tid = -1;
|
||||
for (int i = 1; i < DOS_MAX_THREADS; i++) {
|
||||
if (threads[i].state == DOS_THREAD_FREE) {
|
||||
tid = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tid < 0) {
|
||||
return -1; // No free slots
|
||||
}
|
||||
|
||||
if (stack_size == 0) {
|
||||
stack_size = DOS_DEFAULT_STACK_SIZE;
|
||||
}
|
||||
|
||||
// Allocate stack
|
||||
void *stack = SDL_malloc(stack_size);
|
||||
if (!stack) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock the stack memory so it won't be paged out.
|
||||
// This is important for context switches — we can't take a page fault
|
||||
// while switching stacks.
|
||||
_go32_dpmi_lock_data(stack, stack_size);
|
||||
|
||||
DOS_ThreadContext *ctx = &threads[tid];
|
||||
SDL_memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->id = tid;
|
||||
ctx->state = DOS_THREAD_READY;
|
||||
ctx->stack_base = stack;
|
||||
ctx->stack_size = stack_size;
|
||||
ctx->entry_fn = fn;
|
||||
ctx->entry_arg = arg;
|
||||
ctx->finished = false;
|
||||
ctx->join_waiter = -1;
|
||||
|
||||
// Set up the initial context. We use setjmp to save a template context,
|
||||
// then modify the stack pointer to point to our new stack.
|
||||
//
|
||||
// The trick: we setjmp here to capture register state, then manually
|
||||
// patch the saved __esp and __eip in the jmp_buf struct to point to
|
||||
// our new stack and trampoline function.
|
||||
if (setjmp(ctx->env) == 0) {
|
||||
// Patch the saved context to use our new stack and trampoline.
|
||||
// Stack grows downward, so SP starts at the top.
|
||||
// Align to 16 bytes for ABI compliance.
|
||||
Uint8 *stack_top = (Uint8 *)stack + stack_size;
|
||||
stack_top = (Uint8 *)((uintptr_t)stack_top & ~0xFUL); // 16-byte align
|
||||
|
||||
// Leave room for a fake return address (the trampoline never returns,
|
||||
// but the ABI expects one on the stack at function entry)
|
||||
stack_top -= sizeof(void *);
|
||||
*(void **)stack_top = NULL; // Fake return address
|
||||
|
||||
ctx->env[0].__esp = (unsigned long)(uintptr_t)stack_top;
|
||||
ctx->env[0].__ebp = (unsigned long)(uintptr_t)stack_top;
|
||||
ctx->env[0].__eip = (unsigned long)(uintptr_t)ThreadTrampoline;
|
||||
} else {
|
||||
SDL_assert(!"Unreachable");
|
||||
}
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
void DOS_Yield(void)
|
||||
{
|
||||
if (!scheduler_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
int next = FindNextRunnable(current_thread);
|
||||
if (next < 0) {
|
||||
return; // No other runnable thread, continue current
|
||||
}
|
||||
|
||||
int prev = current_thread;
|
||||
|
||||
// Save current context and switch
|
||||
if (setjmp(threads[prev].env) == 0) {
|
||||
// Mark previous thread as READY (unless it's BLOCKED or FINISHED)
|
||||
if (threads[prev].state == DOS_THREAD_RUNNING) {
|
||||
threads[prev].state = DOS_THREAD_READY;
|
||||
}
|
||||
|
||||
// Switch to next thread
|
||||
current_thread = next;
|
||||
threads[next].state = DOS_THREAD_RUNNING;
|
||||
|
||||
// For new threads that haven't run yet, set the trampoline ID
|
||||
trampoline_thread_id = next;
|
||||
|
||||
longjmp(threads[next].env, 1);
|
||||
}
|
||||
// else: we've been switched back to — just return
|
||||
}
|
||||
|
||||
void DOS_ExitThread(int status)
|
||||
{
|
||||
DOS_ThreadContext *ctx = &threads[current_thread];
|
||||
ctx->exit_status = status;
|
||||
ctx->finished = true;
|
||||
ctx->state = DOS_THREAD_FINISHED;
|
||||
|
||||
// Wake up anyone waiting to join this thread
|
||||
if (ctx->join_waiter >= 0 && ctx->join_waiter < DOS_MAX_THREADS) {
|
||||
DOS_WakeThread(ctx->join_waiter);
|
||||
}
|
||||
|
||||
// Find another thread to run. We can't return — our stack frame
|
||||
// belongs to the thread that just exited.
|
||||
int next = FindNextRunnable(current_thread);
|
||||
if (next >= 0) {
|
||||
current_thread = next;
|
||||
threads[next].state = DOS_THREAD_RUNNING;
|
||||
trampoline_thread_id = next;
|
||||
longjmp(threads[next].env, 1);
|
||||
}
|
||||
|
||||
// If no other thread is runnable and we're not the main thread,
|
||||
// this is a problem. Spin-wait for someone to become runnable.
|
||||
// (This shouldn't happen in practice — the main thread should
|
||||
// always be runnable or waiting.)
|
||||
for (;;) {
|
||||
__asm__ __volatile__("hlt"); // Wait for interrupt before retrying
|
||||
next = FindNextRunnable(current_thread);
|
||||
if (next >= 0) {
|
||||
current_thread = next;
|
||||
threads[next].state = DOS_THREAD_RUNNING;
|
||||
trampoline_thread_id = next;
|
||||
longjmp(threads[next].env, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DOS_JoinThread(int thread_id)
|
||||
{
|
||||
if (thread_id < 0 || thread_id >= DOS_MAX_THREADS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DOS_ThreadContext *target = &threads[thread_id];
|
||||
|
||||
// If already finished, just return the status
|
||||
if (target->finished) {
|
||||
return target->exit_status;
|
||||
}
|
||||
|
||||
// Register ourselves as the join waiter
|
||||
target->join_waiter = current_thread;
|
||||
|
||||
// Block until the target thread finishes
|
||||
while (!target->finished) {
|
||||
DOS_BlockCurrentThread();
|
||||
}
|
||||
|
||||
return target->exit_status;
|
||||
}
|
||||
|
||||
int DOS_GetCurrentThreadID(void)
|
||||
{
|
||||
if (!scheduler_initialized) {
|
||||
return 1;
|
||||
}
|
||||
return current_thread + 1;
|
||||
}
|
||||
|
||||
void DOS_WakeThread(int thread_id)
|
||||
{
|
||||
if (thread_id >= 0 && thread_id < DOS_MAX_THREADS) {
|
||||
if (threads[thread_id].state == DOS_THREAD_BLOCKED) {
|
||||
threads[thread_id].state = DOS_THREAD_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DOS_BlockCurrentThread(void)
|
||||
{
|
||||
threads[current_thread].state = DOS_THREAD_BLOCKED;
|
||||
DOS_Yield();
|
||||
}
|
||||
|
||||
void DOS_DestroyThread(int thread_id)
|
||||
{
|
||||
if (thread_id <= 0 || thread_id >= DOS_MAX_THREADS) {
|
||||
return; // Can't destroy main thread (0) or invalid IDs
|
||||
}
|
||||
|
||||
DOS_ThreadContext *ctx = &threads[thread_id];
|
||||
if (ctx->stack_base) {
|
||||
SDL_free(ctx->stack_base);
|
||||
ctx->stack_base = NULL;
|
||||
}
|
||||
ctx->state = DOS_THREAD_FREE;
|
||||
}
|
||||
|
||||
#endif // SDL_PLATFORM_DOS
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_dos_scheduler_h_
|
||||
#define SDL_dos_scheduler_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Maximum number of cooperative threads. DOS doesn't need many —
|
||||
// typically just main thread + audio thread + maybe a loading thread.
|
||||
#define DOS_MAX_THREADS 16
|
||||
|
||||
// Default stack size for new threads (64 KB)
|
||||
#define DOS_DEFAULT_STACK_SIZE (64 * 1024)
|
||||
|
||||
// Thread states
|
||||
typedef enum
|
||||
{
|
||||
DOS_THREAD_FREE = 0, // Slot is available
|
||||
DOS_THREAD_READY, // Runnable
|
||||
DOS_THREAD_RUNNING, // Currently executing
|
||||
DOS_THREAD_BLOCKED, // Waiting on a semaphore/mutex
|
||||
DOS_THREAD_FINISHED // Thread function returned
|
||||
} DOS_ThreadState;
|
||||
|
||||
// Per-thread context
|
||||
typedef struct DOS_ThreadContext
|
||||
{
|
||||
jmp_buf env; // Saved CPU state for context switch
|
||||
DOS_ThreadState state;
|
||||
int id; // Thread ID (index into thread table)
|
||||
void *stack_base; // malloc'd stack memory
|
||||
size_t stack_size;
|
||||
int exit_status; // Return value from thread function
|
||||
|
||||
// Entry point
|
||||
int (*entry_fn)(void *);
|
||||
void *entry_arg;
|
||||
|
||||
// Join support
|
||||
volatile bool finished; // Set when thread function returns
|
||||
int join_waiter; // ID of thread waiting in WaitThread, or -1
|
||||
} DOS_ThreadContext;
|
||||
|
||||
// Initialize the scheduler. Must be called before any thread operations.
|
||||
// Registers the calling context as thread 0 (the main thread).
|
||||
void DOS_SchedulerInit(void);
|
||||
|
||||
// Shut down the scheduler.
|
||||
void DOS_SchedulerQuit(void);
|
||||
|
||||
// Create a new thread. Returns thread ID (>0) on success, -1 on failure.
|
||||
// The thread starts in READY state and will run when yielded to.
|
||||
int DOS_CreateThread(int (*fn)(void *), void *arg, size_t stack_size);
|
||||
|
||||
// Yield the current thread's timeslice. Switches to the next runnable thread
|
||||
// using round-robin scheduling. If no other thread is runnable, returns
|
||||
// immediately (no-op). This is the core cooperative scheduling primitive.
|
||||
void DOS_Yield(void);
|
||||
|
||||
// Mark a thread as finished and yield. Called when a thread's entry
|
||||
// function returns.
|
||||
void DOS_ExitThread(int status);
|
||||
|
||||
// Block until the specified thread finishes. Returns the thread's exit status.
|
||||
int DOS_JoinThread(int thread_id);
|
||||
|
||||
// Get the current thread's ID.
|
||||
int DOS_GetCurrentThreadID(void);
|
||||
|
||||
// Mark a thread as READY (used by semaphore signal to wake a blocked thread).
|
||||
void DOS_WakeThread(int thread_id);
|
||||
|
||||
// Mark the current thread as BLOCKED and yield (used by semaphore wait).
|
||||
void DOS_BlockCurrentThread(void);
|
||||
|
||||
// Destroy a thread's resources (stack, etc). Thread must be FINISHED or FREE.
|
||||
void DOS_DestroyThread(int thread_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_dos_scheduler_h_
|
||||
@@ -0,0 +1,167 @@
|
||||
#include <sys/kbio.h>
|
||||
|
||||
/* *INDENT-OFF* */ // clang-format off
|
||||
/*
|
||||
* Automatically generated from /usr/share/vt/keymaps/us.acc.kbd.
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
static keymap_t keymap_default_us_acc = { 0x6d, {
|
||||
/* alt
|
||||
* scan cntrl alt alt cntrl
|
||||
* code base shift cntrl shift alt shift cntrl shift spcl flgs
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
/*00*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*01*/{{ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, DBG, }, 0x03,0x00 },
|
||||
/*02*/{{ '1', '!', NOP, NOP, '1', '!', NOP, NOP, }, 0x33,0x00 },
|
||||
/*03*/{{ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, }, 0x00,0x00 },
|
||||
/*04*/{{ '3', '#', NOP, NOP, '3', '#', NOP, NOP, }, 0x33,0x00 },
|
||||
/*05*/{{ '4', '$', NOP, NOP, '4', '$', NOP, NOP, }, 0x33,0x00 },
|
||||
/*06*/{{ '5', '%', NOP, NOP, '5', '%', NOP, NOP, }, 0x33,0x00 },
|
||||
/*07*/{{ '6', '^', 0x1E, 0x1E, '6', DCIR, 0x1E, 0x1E, }, 0x04,0x00 },
|
||||
/*08*/{{ '7', '&', NOP, NOP, '7', '&', NOP, NOP, }, 0x33,0x00 },
|
||||
/*09*/{{ '8', '*', NOP, NOP, '8', DRIN, NOP, NOP, }, 0x37,0x00 },
|
||||
/*0a*/{{ '9', '(', NOP, NOP, '9', '(', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0b*/{{ '0', ')', NOP, NOP, '0', ')', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0c*/{{ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, }, 0x00,0x00 },
|
||||
/*0d*/{{ '=', '+', NOP, NOP, '=', '+', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0e*/{{ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, }, 0x00,0x00 },
|
||||
/*0f*/{{ 0x09, BTAB, NEXT, NEXT, 0x09, BTAB, NOP, NOP, }, 0x77,0x00 },
|
||||
/*10*/{{ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, }, 0x00,0x01 },
|
||||
/*11*/{{ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, }, 0x00,0x01 },
|
||||
/*12*/{{ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, }, 0x00,0x01 },
|
||||
/*13*/{{ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, }, 0x00,0x01 },
|
||||
/*14*/{{ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, }, 0x00,0x01 },
|
||||
/*15*/{{ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, }, 0x00,0x01 },
|
||||
/*16*/{{ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, }, 0x00,0x01 },
|
||||
/*17*/{{ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, }, 0x00,0x01 },
|
||||
/*18*/{{ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, }, 0x00,0x01 },
|
||||
/*19*/{{ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, }, 0x00,0x01 },
|
||||
/*1a*/{{ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, }, 0x00,0x00 },
|
||||
/*1b*/{{ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, }, 0x00,0x00 },
|
||||
/*1c*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*1d*/{{ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, }, 0xFF,0x00 },
|
||||
/*1e*/{{ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, }, 0x00,0x01 },
|
||||
/*1f*/{{ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, }, 0x00,0x01 },
|
||||
/*20*/{{ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, }, 0x00,0x01 },
|
||||
/*21*/{{ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, }, 0x00,0x01 },
|
||||
/*22*/{{ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, }, 0x00,0x01 },
|
||||
/*23*/{{ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, }, 0x00,0x01 },
|
||||
/*24*/{{ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, }, 0x00,0x01 },
|
||||
/*25*/{{ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, }, 0x00,0x01 },
|
||||
/*26*/{{ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, }, 0x00,0x01 },
|
||||
/*27*/{{ ';', ':', NOP, NOP, ';', ':', NOP, NOP, }, 0x33,0x00 },
|
||||
/*28*/{{ '\'', '"', NOP, NOP, DACU, DUML, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*29*/{{ '`', '~', NOP, NOP, DGRA, DTIL, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*2a*/{{ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, }, 0xFF,0x00 },
|
||||
/*2b*/{{ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, }, 0x00,0x00 },
|
||||
/*2c*/{{ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, }, 0x00,0x01 },
|
||||
/*2d*/{{ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, }, 0x00,0x01 },
|
||||
/*2e*/{{ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, }, 0x00,0x01 },
|
||||
/*2f*/{{ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, }, 0x00,0x01 },
|
||||
/*30*/{{ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, }, 0x00,0x01 },
|
||||
/*31*/{{ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, }, 0x00,0x01 },
|
||||
/*32*/{{ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, }, 0x00,0x01 },
|
||||
/*33*/{{ ',', '<', NOP, NOP, DCED, '<', NOP, NOP, }, 0x3B,0x00 },
|
||||
/*34*/{{ '.', '>', NOP, NOP, '.', '>', NOP, NOP, }, 0x33,0x00 },
|
||||
/*35*/{{ '/', '?', NOP, NOP, '/', '?', NOP, NOP, }, 0x33,0x00 },
|
||||
/*36*/{{ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, }, 0xFF,0x00 },
|
||||
/*37*/{{ '*', '*', '*', '*', '*', '*', '*', '*', }, 0x00,0x00 },
|
||||
/*38*/{{ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, }, 0xFF,0x00 },
|
||||
/*39*/{{ ' ', ' ', 0x00, 0x00, ' ', ' ', SUSP, SUSP, }, 0x03,0x00 },
|
||||
/*3a*/{{ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, }, 0xFF,0x00 },
|
||||
/*3b*/{{ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11),}, 0xFF,0x00 },
|
||||
/*3c*/{{ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12),}, 0xFF,0x00 },
|
||||
/*3d*/{{ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13),}, 0xFF,0x00 },
|
||||
/*3e*/{{ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14),}, 0xFF,0x00 },
|
||||
/*3f*/{{ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15),}, 0xFF,0x00 },
|
||||
/*40*/{{ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16),}, 0xFF,0x00 },
|
||||
/*41*/{{ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7),}, 0xFF,0x00 },
|
||||
/*42*/{{ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8),}, 0xFF,0x00 },
|
||||
/*43*/{{ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9),}, 0xFF,0x00 },
|
||||
/*44*/{{ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10),}, 0xFF,0x00 },
|
||||
/*45*/{{ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, }, 0xFF,0x00 },
|
||||
/*46*/{{ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, }, 0xFF,0x00 },
|
||||
/*47*/{{ F(49), '7', '7', '7', '7', '7', '7', '7', }, 0x80,0x02 },
|
||||
/*48*/{{ F(50), '8', '8', '8', '8', '8', '8', '8', }, 0x80,0x02 },
|
||||
/*49*/{{ F(51), '9', '9', '9', '9', '9', '9', '9', }, 0x80,0x02 },
|
||||
/*4a*/{{ F(52), '-', '-', '-', '-', '-', '-', '-', }, 0x80,0x02 },
|
||||
/*4b*/{{ F(53), '4', '4', '4', '4', '4', '4', '4', }, 0x80,0x02 },
|
||||
/*4c*/{{ F(54), '5', '5', '5', '5', '5', '5', '5', }, 0x80,0x02 },
|
||||
/*4d*/{{ F(55), '6', '6', '6', '6', '6', '6', '6', }, 0x80,0x02 },
|
||||
/*4e*/{{ F(56), '+', '+', '+', '+', '+', '+', '+', }, 0x80,0x02 },
|
||||
/*4f*/{{ F(57), '1', '1', '1', '1', '1', '1', '1', }, 0x80,0x02 },
|
||||
/*50*/{{ F(58), '2', '2', '2', '2', '2', '2', '2', }, 0x80,0x02 },
|
||||
/*51*/{{ F(59), '3', '3', '3', '3', '3', '3', '3', }, 0x80,0x02 },
|
||||
/*52*/{{ F(60), '0', '0', '0', '0', '0', '0', '0', }, 0x80,0x02 },
|
||||
/*53*/{{ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, }, 0x03,0x02 },
|
||||
/*54*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*55*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*56*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*57*/{{ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11),}, 0xFF,0x00 },
|
||||
/*58*/{{ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12),}, 0xFF,0x00 },
|
||||
/*59*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*5a*/{{ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, }, 0xFF,0x00 },
|
||||
/*5b*/{{ '/', '/', '/', '/', '/', '/', '/', '/', }, 0x00,0x02 },
|
||||
/*5c*/{{ NEXT, NEXT, NOP, NOP, DBG, DBG, DBG, DBG, }, 0xFF,0x00 },
|
||||
/*5d*/{{ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, }, 0xFF,0x00 },
|
||||
/*5e*/{{ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49),}, 0xFF,0x00 },
|
||||
/*5f*/{{ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50),}, 0xFF,0x00 },
|
||||
/*60*/{{ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51),}, 0xFF,0x00 },
|
||||
/*61*/{{ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53),}, 0xFF,0x00 },
|
||||
/*62*/{{ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55),}, 0xFF,0x00 },
|
||||
/*63*/{{ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57),}, 0xFF,0x00 },
|
||||
/*64*/{{ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58),}, 0xFF,0x00 },
|
||||
/*65*/{{ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59),}, 0xFF,0x00 },
|
||||
/*66*/{{ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60),}, 0xFF,0x00 },
|
||||
/*67*/{{ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61),}, 0xFF,0x00 },
|
||||
/*68*/{{ SPSC, SPSC, SUSP, SUSP, NOP, NOP, SUSP, SUSP, }, 0xFF,0x00 },
|
||||
/*69*/{{ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62),}, 0xFF,0x00 },
|
||||
/*6a*/{{ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63),}, 0xFF,0x00 },
|
||||
/*6b*/{{ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64),}, 0xFF,0x00 },
|
||||
/*6c*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
} };
|
||||
|
||||
static accentmap_t accentmap_default_us_acc = { 11, {
|
||||
// dgra=0
|
||||
{ '`', { { 'a',0xe0 }, { 'A',0xc0 }, { 'e',0xe8 }, { 'E',0xc8 },
|
||||
{ 'i',0xec }, { 'I',0xcc }, { 'o',0xf2 }, { 'O',0xd2 },
|
||||
{ 'u',0xf9 }, { 'U',0xd9 }, }, },
|
||||
// dacu=1
|
||||
{ 0xb4, { { 'a',0xe1 }, { 'A',0xc1 }, { 'e',0xe9 }, { 'E',0xc9 },
|
||||
{ 'i',0xed }, { 'I',0xcd }, { 'o',0xf3 }, { 'O',0xd3 },
|
||||
{ 'u',0xfa }, { 'U',0xda }, { 'y',0xfd }, { 'Y',0xdd }, }, },
|
||||
// dcir=2
|
||||
{ '^', { { 'a',0xe2 }, { 'A',0xc2 }, { 'e',0xea }, { 'E',0xca },
|
||||
{ 'i',0xee }, { 'I',0xce }, { 'o',0xf4 }, { 'O',0xd4 },
|
||||
{ 'u',0xfb }, { 'U',0xdb }, }, },
|
||||
// dtil=3
|
||||
{ '~', { { 'a',0xe3 }, { 'A',0xc3 }, { 'n',0xf1 }, { 'N',0xd1 },
|
||||
{ 'o',0xf5 }, { 'O',0xd5 }, }, },
|
||||
// dmac=4
|
||||
{ 0x00 },
|
||||
// dbre=5
|
||||
{ 0x00 },
|
||||
// ddot=6
|
||||
{ 0x00 },
|
||||
// duml=7
|
||||
{ 0xa8, { { 'a',0xe4 }, { 'A',0xc4 }, { 'e',0xeb }, { 'E',0xcb },
|
||||
{ 'i',0xef }, { 'I',0xcf }, { 'o',0xf6 }, { 'O',0xd6 },
|
||||
{ 'u',0xfc }, { 'U',0xdc }, { 'y',0xff }, }, },
|
||||
// dsla=8
|
||||
{ 0x00 },
|
||||
// drin=9
|
||||
{ 0xb0, { { 'a',0xe5 }, { 'A',0xc5 }, }, },
|
||||
// dced=10
|
||||
{ 0xb8, { { 'c',0xe7 }, { 'C',0xc7 }, }, },
|
||||
// dapo=11
|
||||
{ 0x00 },
|
||||
// ddac=12
|
||||
{ 0x00 },
|
||||
// dogo=13
|
||||
{ 0x00 },
|
||||
// dcar=14
|
||||
{ 0x00 },
|
||||
} };
|
||||
|
||||
/* *INDENT-ON* */ // clang-format on
|
||||
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "../linux/SDL_evdev_kbd.h"
|
||||
|
||||
#ifdef SDL_INPUT_FBSDKBIO
|
||||
|
||||
// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/consio.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "SDL_evdev_kbd_default_keyaccmap.h"
|
||||
|
||||
#ifndef K_OFF
|
||||
#define K_OFF 0x04
|
||||
#endif
|
||||
|
||||
typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
|
||||
|
||||
|
||||
/*
|
||||
* Keyboard State
|
||||
*/
|
||||
|
||||
struct SDL_EVDEV_keyboard_state
|
||||
{
|
||||
int console_fd;
|
||||
int keyboard_fd;
|
||||
bool muted;
|
||||
unsigned long old_kbd_mode;
|
||||
unsigned short **key_maps;
|
||||
keymap_t *key_map;
|
||||
keyboard_info_t *kbInfo;
|
||||
unsigned char shift_down[4]; // shift state counters..
|
||||
bool dead_key_next;
|
||||
int npadch; // -1 or number assembled on pad
|
||||
accentmap_t *accents;
|
||||
unsigned int diacr;
|
||||
bool rep; // flag telling character repeat
|
||||
unsigned char lockstate;
|
||||
unsigned char ledflagstate;
|
||||
char shift_state;
|
||||
char text[128];
|
||||
unsigned int text_len;
|
||||
void (*vt_release_callback)(void *);
|
||||
void *vt_release_callback_data;
|
||||
void (*vt_acquire_callback)(void *);
|
||||
void *vt_acquire_callback_data;
|
||||
};
|
||||
|
||||
static bool SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
return ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0;
|
||||
}
|
||||
|
||||
static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
|
||||
static int kbd_cleanup_sigactions_installed = 0;
|
||||
static int kbd_cleanup_atexit_installed = 0;
|
||||
|
||||
static struct sigaction old_sigaction[NSIG];
|
||||
|
||||
static int fatal_signals[] = {
|
||||
// Handlers for SIGTERM and SIGINT are installed in SDL_InitQuit.
|
||||
SIGHUP, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
|
||||
SIGSYS
|
||||
};
|
||||
|
||||
static void vt_update_mouse(SDL_EVDEV_keyboard_state *kbd, int operation)
|
||||
{
|
||||
struct mouse_info mData;
|
||||
|
||||
SDL_zero(mData);
|
||||
mData.operation = operation;
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
}
|
||||
|
||||
static void kbd_cleanup(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
|
||||
if (!kbd) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = NULL;
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
if (kbd->keyboard_fd != kbd->console_fd) {
|
||||
close(kbd->keyboard_fd);
|
||||
}
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
vt_update_mouse(kbd, true);
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_reraise_signal(int sig)
|
||||
{
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
|
||||
void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
|
||||
|
||||
static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
|
||||
{
|
||||
struct sigaction *old_action_p = &(old_sigaction[signum]);
|
||||
sigset_t sigset;
|
||||
|
||||
// Restore original signal handler before going any further.
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
|
||||
// Unmask current signal.
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, signum);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
|
||||
// Save original signal info and context for archeologists.
|
||||
SDL_EVDEV_kdb_cleanup_siginfo = info;
|
||||
SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
|
||||
|
||||
// Restore keyboard.
|
||||
kbd_cleanup();
|
||||
|
||||
// Reraise signal.
|
||||
SDL_EVDEV_kbd_reraise_signal(signum);
|
||||
}
|
||||
|
||||
static void kbd_unregister_emerg_cleanup(void)
|
||||
{
|
||||
int tabidx;
|
||||
|
||||
kbd_cleanup_state = NULL;
|
||||
|
||||
if (!kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 0;
|
||||
|
||||
for (tabidx = 0; tabidx < SDL_arraysize(fatal_signals); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction cur_action;
|
||||
int signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
|
||||
// Examine current signal action
|
||||
if (sigaction(signum, NULL, &cur_action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if action installed and not modified
|
||||
if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restore original action
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_cleanup_atexit(void)
|
||||
{
|
||||
// Restore keyboard.
|
||||
kbd_cleanup();
|
||||
|
||||
// Try to restore signal handlers in case shared library is being unloaded
|
||||
kbd_unregister_emerg_cleanup();
|
||||
}
|
||||
|
||||
static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
int tabidx;
|
||||
|
||||
if (kbd_cleanup_state) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = kbd;
|
||||
|
||||
if (!kbd_cleanup_atexit_installed) {
|
||||
/* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
|
||||
* functions that are called when the shared library is unloaded.
|
||||
* -- man atexit(3)
|
||||
*/
|
||||
atexit(kbd_cleanup_atexit);
|
||||
kbd_cleanup_atexit_installed = 1;
|
||||
}
|
||||
|
||||
if (kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 1;
|
||||
|
||||
for (tabidx = 0; tabidx < SDL_arraysize(fatal_signals); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction new_action;
|
||||
int signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
if (sigaction(signum, NULL, old_action_p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip SIGHUP and SIGPIPE if handler is already installed
|
||||
* - assume the handler will do the cleanup
|
||||
*/
|
||||
if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_action = *old_action_p;
|
||||
new_action.sa_flags |= SA_SIGINFO;
|
||||
new_action.sa_sigaction = &kbd_cleanup_signal_action;
|
||||
sigaction(signum, &new_action, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
VT_SIGNAL_NONE,
|
||||
VT_SIGNAL_RELEASE,
|
||||
VT_SIGNAL_ACQUIRE,
|
||||
};
|
||||
static int vt_release_signal;
|
||||
static int vt_acquire_signal;
|
||||
static SDL_AtomicInt vt_signal_pending;
|
||||
SDL_AtomicInt vt_current;
|
||||
|
||||
typedef void (*signal_handler)(int signum);
|
||||
|
||||
|
||||
static void kbd_vt_release_signal_action(int signum)
|
||||
{
|
||||
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE);
|
||||
SDL_SetAtomicInt(&vt_current, VT_THEIRS);
|
||||
}
|
||||
|
||||
static void kbd_vt_acquire_signal_action(int signum)
|
||||
{
|
||||
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE);
|
||||
SDL_SetAtomicInt(&vt_current, VT_OURS);
|
||||
}
|
||||
|
||||
static bool setup_vt_signal(int signum, signal_handler handler)
|
||||
{
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction new_action;
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
SDL_zero(new_action);
|
||||
new_action.sa_handler = handler;
|
||||
new_action.sa_flags = SA_RESTART;
|
||||
if (sigaction(signum, &new_action, old_action_p) < 0) {
|
||||
return false;
|
||||
}
|
||||
if (old_action_p->sa_handler != SIG_DFL) {
|
||||
// This signal is already in use
|
||||
if (signum == SIGUSR1 || signum == SIGUSR2) {
|
||||
/*
|
||||
* For the moment we have no other options in FreeBSD because
|
||||
* the vt(4) will not accept signal numbers over 32.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void kbd_vt_quit(int console_fd)
|
||||
{
|
||||
struct vt_mode mode;
|
||||
|
||||
if (vt_release_signal) {
|
||||
sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL);
|
||||
vt_release_signal = 0;
|
||||
}
|
||||
if (vt_acquire_signal) {
|
||||
sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL);
|
||||
vt_acquire_signal = 0;
|
||||
}
|
||||
|
||||
SDL_zero(mode);
|
||||
mode.mode = VT_AUTO;
|
||||
ioctl(console_fd, VT_SETMODE, &mode);
|
||||
}
|
||||
|
||||
static bool kbd_vt_init(int console_fd)
|
||||
{
|
||||
struct vt_mode mode;
|
||||
|
||||
if (setup_vt_signal(SIGUSR1, kbd_vt_release_signal_action)) {
|
||||
vt_release_signal = SIGUSR1;
|
||||
}
|
||||
if (setup_vt_signal(SIGUSR2, kbd_vt_acquire_signal_action)) {
|
||||
vt_acquire_signal = SIGUSR2;
|
||||
}
|
||||
if (!vt_release_signal || !vt_acquire_signal ) {
|
||||
kbd_vt_quit(console_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_zero(mode);
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = vt_release_signal;
|
||||
mode.acqsig = vt_acquire_signal;
|
||||
mode.frsig = SIGIO;
|
||||
if (ioctl(console_fd, VT_SETMODE, &mode) < 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed VT_SETMODE ioctl: %s", strerror(errno));
|
||||
kbd_vt_quit(console_fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd;
|
||||
char flag_state;
|
||||
|
||||
kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
|
||||
if (!kbd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kbd->npadch = -1;
|
||||
|
||||
// This might fail if we're not connected to a tty (e.g. on the Steam Link)
|
||||
kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
kbd->shift_state = 0;
|
||||
|
||||
kbd->accents = SDL_calloc(1, sizeof(accentmap_t));
|
||||
kbd->key_map = SDL_calloc(1, sizeof(keymap_t));
|
||||
kbd->kbInfo = SDL_calloc(1, sizeof(keyboard_info_t));
|
||||
|
||||
ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
|
||||
kbd->ledflagstate = flag_state;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0) {
|
||||
SDL_free(kbd->accents);
|
||||
kbd->accents = &accentmap_default_us_acc;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
|
||||
// Set the keyboard in XLATE mode and load the keymaps
|
||||
ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
|
||||
if (!SDL_EVDEV_kbd_load_keymaps(kbd)) {
|
||||
SDL_free(kbd->key_map);
|
||||
kbd->key_map = &keymap_default_us_acc;
|
||||
}
|
||||
|
||||
if (!kbd_vt_init(kbd->console_fd)) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "kbd_vt_init failed");
|
||||
}
|
||||
|
||||
kbd->keyboard_fd = kbd->console_fd;
|
||||
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
|
||||
kbd_register_emerg_cleanup(kbd);
|
||||
}
|
||||
}
|
||||
|
||||
vt_update_mouse(kbd, MOUSE_HIDE);
|
||||
|
||||
return kbd;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
if (!kbd) {
|
||||
return;
|
||||
}
|
||||
|
||||
kbd_vt_quit(kbd->console_fd);
|
||||
|
||||
vt_update_mouse(kbd, MOUSE_SHOW);
|
||||
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
if (kbd->keyboard_fd >= 0) {
|
||||
// Restore the original keyboard mode
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
|
||||
close(kbd->keyboard_fd);
|
||||
if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0) {
|
||||
// Give back keyboard.
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
}
|
||||
kbd->console_fd = kbd->keyboard_fd = -1;
|
||||
}
|
||||
|
||||
SDL_free(kbd);
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
|
||||
{
|
||||
struct termios tios;
|
||||
|
||||
SDL_zero(tios);
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (muted == state->muted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcgetattr(state->console_fd, &tios) == -1) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not get terminal mode: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (muted) {
|
||||
if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
|
||||
ioctl(state->console_fd, KDSKBMODE, K_OFF);
|
||||
cfmakeraw(&tios);
|
||||
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
|
||||
kbd_register_emerg_cleanup(state);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
cfmakesane(&tios);
|
||||
ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
|
||||
}
|
||||
|
||||
if (tcsetattr(state->console_fd, TCSAFLUSH, &tios) == -1) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not set terminal mode to %s: %s", muted ? "raw" : "sane", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
state->muted = muted;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data)
|
||||
{
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->vt_release_callback = release_callback;
|
||||
state->vt_release_callback_data = release_callback_data;
|
||||
state->vt_acquire_callback = acquire_callback;
|
||||
state->vt_acquire_callback_data = acquire_callback_data;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
int signal_pending = SDL_GetAtomicInt(&vt_signal_pending);
|
||||
|
||||
if (signal_pending != VT_SIGNAL_NONE) {
|
||||
if (signal_pending == VT_SIGNAL_RELEASE) {
|
||||
if (state->vt_release_callback) {
|
||||
vt_update_mouse(state, MOUSE_SHOW);
|
||||
state->vt_release_callback(state->vt_release_callback_data);
|
||||
}
|
||||
ioctl(state->console_fd, VT_RELDISP, 1);
|
||||
} else {
|
||||
if (state->vt_acquire_callback) {
|
||||
state->vt_acquire_callback(state->vt_acquire_callback_data);
|
||||
vt_update_mouse(state, MOUSE_HIDE);
|
||||
}
|
||||
ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ);
|
||||
}
|
||||
SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper Functions.
|
||||
*/
|
||||
static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
// c is already part of a UTF-8 sequence and safe to add as a character
|
||||
if (kbd->text_len < (sizeof(kbd->text) - 1)) {
|
||||
kbd->text[kbd->text_len++] = (char)c;
|
||||
}
|
||||
}
|
||||
|
||||
static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
if (c < 0x80)
|
||||
/* 0******* */
|
||||
put_queue(kbd, c);
|
||||
else if (c < 0x800) {
|
||||
/* 110***** 10****** */
|
||||
put_queue(kbd, 0xc0 | (c >> 6));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x10000) {
|
||||
if (c >= 0xD800 && c < 0xE000) {
|
||||
return;
|
||||
}
|
||||
if (c == 0xFFFF) {
|
||||
return;
|
||||
}
|
||||
/* 1110**** 10****** 10****** */
|
||||
put_queue(kbd, 0xe0 | (c >> 12));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x110000) {
|
||||
/* 11110*** 10****** 10****** 10****** */
|
||||
put_queue(kbd, 0xf0 | (c >> 18));
|
||||
put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a combining character DIACR here, followed by the character CH.
|
||||
* If the combination occurs in the table, return the corresponding value.
|
||||
* Otherwise, if CH is a space or equals DIACR, return DIACR.
|
||||
* Otherwise, conclude that DIACR was not combining after all,
|
||||
* queue it and return CH.
|
||||
*/
|
||||
static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
|
||||
{
|
||||
unsigned int d = kbd->diacr;
|
||||
unsigned int i, j;
|
||||
|
||||
kbd->diacr = 0;
|
||||
|
||||
for (i = 0; i < kbd->accents->n_accs; i++) {
|
||||
if (kbd->accents->acc[i].accchar == d) {
|
||||
for (j = 0; j < NUM_ACCENTCHARS; ++j) {
|
||||
if (kbd->accents->acc[i].map[j][0] == 0) { // end of table
|
||||
break;
|
||||
}
|
||||
if (kbd->accents->acc[i].map[j][0] == ch) {
|
||||
return kbd->accents->acc[i].map[j][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == ' ' || ch == d) {
|
||||
put_utf8(kbd, d);
|
||||
return 0;
|
||||
}
|
||||
put_utf8(kbd, d);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
return (kbd->ledflagstate & flag) != 0;
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate ^= flag;
|
||||
ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
/*
|
||||
* Special function handlers
|
||||
*/
|
||||
|
||||
static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
|
||||
{
|
||||
if (up_flag) {
|
||||
return; // no action, if this is a key release
|
||||
}
|
||||
|
||||
if (kbd->diacr) {
|
||||
value = handle_diacr(kbd, value);
|
||||
}
|
||||
|
||||
if (kbd->dead_key_next) {
|
||||
kbd->dead_key_next = false;
|
||||
kbd->diacr = value;
|
||||
return;
|
||||
}
|
||||
put_utf8(kbd, value);
|
||||
}
|
||||
|
||||
static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
|
||||
{
|
||||
if (up_flag)
|
||||
return;
|
||||
|
||||
kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
|
||||
}
|
||||
|
||||
static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
int old_state = kbd->shift_state;
|
||||
|
||||
if (kbd->rep)
|
||||
return;
|
||||
|
||||
if (up_flag) {
|
||||
/*
|
||||
* handle the case that two shift or control
|
||||
* keys are depressed simultaneously
|
||||
*/
|
||||
if (kbd->shift_down[value]) {
|
||||
kbd->shift_down[value]--;
|
||||
}
|
||||
} else
|
||||
kbd->shift_down[value]++;
|
||||
|
||||
if (kbd->shift_down[value])
|
||||
kbd->shift_state |= (1 << value);
|
||||
else
|
||||
kbd->shift_state &= ~(1 << value);
|
||||
|
||||
// kludge
|
||||
if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
|
||||
put_utf8(kbd, kbd->npadch);
|
||||
kbd->npadch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
|
||||
{
|
||||
keymap_t key_map;
|
||||
struct keyent_t keysym;
|
||||
unsigned int final_key_state;
|
||||
unsigned int map_from_key_sym;
|
||||
|
||||
if (!kbd) {
|
||||
return;
|
||||
}
|
||||
|
||||
key_map = *kbd->key_map;
|
||||
|
||||
kbd->rep = (down == 2);
|
||||
|
||||
if (keycode < NUM_KEYS) {
|
||||
if (keycode >= 89 && keycode <= 95) {
|
||||
// These constitute unprintable language-related keys, so ignore them.
|
||||
return;
|
||||
}
|
||||
if (keycode > 95) {
|
||||
keycode -= 7;
|
||||
}
|
||||
if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8)) {
|
||||
keycode += ALTGR_OFFSET;
|
||||
}
|
||||
keysym = key_map.key[keycode];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
final_key_state = kbd->shift_state & 0x7;
|
||||
if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
|
||||
map_from_key_sym = keysym.map[final_key_state];
|
||||
if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
|
||||
// Special function.
|
||||
if (map_from_key_sym == 0)
|
||||
return; // Nothing to do.
|
||||
if (map_from_key_sym & SPCLKEY) {
|
||||
map_from_key_sym &= ~SPCLKEY;
|
||||
}
|
||||
if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
|
||||
// Accent function.
|
||||
unsigned int accent_index = map_from_key_sym - F_ACC;
|
||||
if (kbd->accents->acc[accent_index].accchar != 0) {
|
||||
k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
|
||||
}
|
||||
} else {
|
||||
switch (map_from_key_sym) {
|
||||
case ASH: // alt/meta shift
|
||||
k_shift(kbd, 3, down == 0);
|
||||
break;
|
||||
case LSHA: // left shift + alt lock
|
||||
case RSHA: // right shift + alt lock
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
SDL_FALLTHROUGH;
|
||||
case LSH: // left shift
|
||||
case RSH: // right shift
|
||||
k_shift(kbd, 0, down == 0);
|
||||
break;
|
||||
case LCTRA: // left ctrl + alt lock
|
||||
case RCTRA: // right ctrl + alt lock
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
SDL_FALLTHROUGH;
|
||||
case LCTR: // left ctrl
|
||||
case RCTR: // right ctrl
|
||||
k_shift(kbd, 1, down == 0);
|
||||
break;
|
||||
case LALTA: // left alt + alt lock
|
||||
case RALTA: // right alt + alt lock
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
SDL_FALLTHROUGH;
|
||||
case LALT: // left alt
|
||||
case RALT: // right alt
|
||||
k_shift(kbd, 2, down == 0);
|
||||
break;
|
||||
case ALK: // alt lock
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
break;
|
||||
case CLK: // caps lock
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, CLKED);
|
||||
}
|
||||
break;
|
||||
case NLK: // num lock
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, NLKED);
|
||||
}
|
||||
break;
|
||||
case SLK: // scroll lock
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, SLKED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
|
||||
if (kbd->diacr) {
|
||||
kbd->diacr = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
|
||||
k_self(kbd, map_from_key_sym, !down);
|
||||
}
|
||||
}
|
||||
|
||||
if (kbd->text_len > 0) {
|
||||
kbd->text[kbd->text_len] = '\0';
|
||||
SDL_SendKeyboardText(kbd->text);
|
||||
kbd->text_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_INPUT_FBSDKBIO
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../windows/SDL_windows.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
}
|
||||
#include <XGameRuntime.h>
|
||||
#include <xsapi-c/services_c.h>
|
||||
#include <appnotify.h>
|
||||
|
||||
static XTaskQueueHandle GDK_GlobalTaskQueue;
|
||||
|
||||
PAPPSTATE_REGISTRATION hPLM = {};
|
||||
PAPPCONSTRAIN_REGISTRATION hCPLM = {};
|
||||
HANDLE plmSuspendComplete = nullptr;
|
||||
|
||||
extern "C"
|
||||
bool SDL_GetGDKTaskQueue(XTaskQueueHandle *outTaskQueue)
|
||||
{
|
||||
// If this is the first call, first create the global task queue.
|
||||
if (!GDK_GlobalTaskQueue) {
|
||||
HRESULT hr;
|
||||
|
||||
hr = XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool,
|
||||
XTaskQueueDispatchMode::Manual,
|
||||
&GDK_GlobalTaskQueue);
|
||||
if (FAILED(hr)) {
|
||||
return SDL_SetError("[GDK] Could not create global task queue");
|
||||
}
|
||||
|
||||
// The initial call gets the non-duplicated handle so they can clean it up
|
||||
*outTaskQueue = GDK_GlobalTaskQueue;
|
||||
} else {
|
||||
// Duplicate the global task queue handle into outTaskQueue
|
||||
if (FAILED(XTaskQueueDuplicateHandle(GDK_GlobalTaskQueue, outTaskQueue))) {
|
||||
return SDL_SetError("[GDK] Unable to acquire global task queue");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void GDK_DispatchTaskQueue(void)
|
||||
{
|
||||
/* If there is no global task queue, don't do anything.
|
||||
* This gives the option to opt-out for those who want to handle everything themselves.
|
||||
*/
|
||||
if (GDK_GlobalTaskQueue) {
|
||||
// Dispatch any callbacks which are ready.
|
||||
while (XTaskQueueDispatch(GDK_GlobalTaskQueue, XTaskQueuePort::Completion, 0))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
bool GDK_RegisterChangeNotifications(void)
|
||||
{
|
||||
// Register suspend/resume handling
|
||||
plmSuspendComplete = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
if (!plmSuspendComplete) {
|
||||
return SDL_SetError("[GDK] Unable to create plmSuspendComplete event");
|
||||
}
|
||||
auto rascn = [](BOOLEAN quiesced, PVOID context) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppStateChangeNotification handler");
|
||||
if (quiesced) {
|
||||
ResetEvent(plmSuspendComplete);
|
||||
SDL_SendAppEvent(SDL_EVENT_DID_ENTER_BACKGROUND);
|
||||
|
||||
// To defer suspension, we must wait to exit this callback.
|
||||
// IMPORTANT: The app must call SDL_GDKSuspendComplete() to release this lock.
|
||||
(void)WaitForSingleObject(plmSuspendComplete, INFINITE);
|
||||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppStateChangeNotification handler: plmSuspendComplete event signaled.");
|
||||
} else {
|
||||
SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_FOREGROUND);
|
||||
}
|
||||
};
|
||||
if (RegisterAppStateChangeNotification(rascn, NULL, &hPLM)) {
|
||||
return SDL_SetError("[GDK] Unable to call RegisterAppStateChangeNotification");
|
||||
}
|
||||
|
||||
// Register constrain/unconstrain handling
|
||||
auto raccn = [](BOOLEAN constrained, PVOID context) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppConstrainedChangeNotification handler");
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
if (_this) {
|
||||
if (constrained && !((_this->windows) && _this->windows->text_input_active)) {
|
||||
SDL_SetKeyboardFocus(NULL);
|
||||
} else {
|
||||
SDL_SetKeyboardFocus(_this->windows);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (RegisterAppConstrainedChangeNotification(raccn, NULL, &hCPLM)) {
|
||||
return SDL_SetError("[GDK] Unable to call RegisterAppConstrainedChangeNotification");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void GDK_UnregisterChangeNotifications(void)
|
||||
{
|
||||
// Unregister suspend/resume handling
|
||||
UnregisterAppStateChangeNotification(hPLM);
|
||||
CloseHandle(plmSuspendComplete);
|
||||
|
||||
// Unregister constrain/unconstrain handling
|
||||
UnregisterAppConstrainedChangeNotification(hCPLM);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void SDL_GDKSuspendComplete()
|
||||
{
|
||||
if (plmSuspendComplete) {
|
||||
SetEvent(plmSuspendComplete);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
bool SDL_GetGDKDefaultUser(XUserHandle *outUserHandle)
|
||||
{
|
||||
XAsyncBlock block = { 0 };
|
||||
HRESULT result;
|
||||
|
||||
if (FAILED(result = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, &block))) {
|
||||
return WIN_SetErrorFromHRESULT("XUserAddAsync", result);
|
||||
}
|
||||
|
||||
do {
|
||||
result = XUserAddResult(&block, outUserHandle);
|
||||
} while (result == E_PENDING);
|
||||
if (FAILED(result)) {
|
||||
return WIN_SetErrorFromHRESULT("XUserAddResult", result);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// This is called from WIN_PumpEvents on GDK
|
||||
extern void GDK_DispatchTaskQueue(void);
|
||||
|
||||
extern bool GDK_RegisterChangeNotifications(void);
|
||||
extern void GDK_UnregisterChangeNotifications(void);
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_BAPP_H
|
||||
#define SDL_BAPP_H
|
||||
|
||||
#include <Path.h>
|
||||
#include <InterfaceKit.h>
|
||||
#include <LocaleRoster.h>
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
#include <OpenGLKit.h>
|
||||
#endif
|
||||
|
||||
#include "../../video/haiku/SDL_bkeyboard.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// Local includes
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../video/haiku/SDL_bframebuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class SDL_BLooper;
|
||||
class SDL_BWin;
|
||||
|
||||
// Message constants
|
||||
enum ToSDL
|
||||
{
|
||||
// Intercepted by BWindow on its way to BView
|
||||
BAPP_MOUSE_MOVED,
|
||||
BAPP_MOUSE_BUTTON,
|
||||
BAPP_MOUSE_WHEEL,
|
||||
BAPP_KEY,
|
||||
BAPP_REPAINT, // from _UPDATE_
|
||||
// From BWindow
|
||||
BAPP_MAXIMIZE, // from B_ZOOM
|
||||
BAPP_MINIMIZE,
|
||||
BAPP_RESTORE, // TODO: IMPLEMENT!
|
||||
BAPP_SHOW,
|
||||
BAPP_HIDE,
|
||||
BAPP_MOUSE_FOCUS, // caused by MOUSE_MOVE
|
||||
BAPP_KEYBOARD_FOCUS, // from WINDOW_ACTIVATED
|
||||
BAPP_WINDOW_CLOSE_REQUESTED,
|
||||
BAPP_WINDOW_MOVED,
|
||||
BAPP_WINDOW_RESIZED,
|
||||
BAPP_SCREEN_CHANGED
|
||||
};
|
||||
|
||||
|
||||
extern "C" SDL_BLooper *SDL_Looper;
|
||||
|
||||
|
||||
// Create a descendant of BLooper
|
||||
class SDL_BLooper : public BLooper
|
||||
{
|
||||
public:
|
||||
SDL_BLooper(const char *name) : BLooper(name)
|
||||
{
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
_current_context = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~SDL_BLooper()
|
||||
{
|
||||
}
|
||||
|
||||
// Event-handling functions
|
||||
virtual void MessageReceived(BMessage *message)
|
||||
{
|
||||
// Sort out SDL-related messages
|
||||
switch (message->what) {
|
||||
case BAPP_MOUSE_MOVED:
|
||||
_HandleMouseMove(message);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_BUTTON:
|
||||
_HandleMouseButton(message);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_WHEEL:
|
||||
_HandleMouseWheel(message);
|
||||
break;
|
||||
|
||||
case BAPP_KEY:
|
||||
_HandleKey(message);
|
||||
break;
|
||||
|
||||
case BAPP_REPAINT:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_EXPOSED);
|
||||
break;
|
||||
|
||||
case BAPP_MAXIMIZE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_MAXIMIZED);
|
||||
break;
|
||||
|
||||
case BAPP_MINIMIZE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_MINIMIZED);
|
||||
break;
|
||||
|
||||
case BAPP_SHOW:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_SHOWN);
|
||||
break;
|
||||
|
||||
case BAPP_HIDE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_HIDDEN);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_FOCUS:
|
||||
_HandleMouseFocus(message);
|
||||
break;
|
||||
|
||||
case BAPP_KEYBOARD_FOCUS:
|
||||
_HandleKeyboardFocus(message);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_CLOSE_REQUESTED:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_CLOSE_REQUESTED);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_MOVED:
|
||||
_HandleWindowMoved(message);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_RESIZED:
|
||||
_HandleWindowResized(message);
|
||||
break;
|
||||
|
||||
case B_LOCALE_CHANGED:
|
||||
SDL_SendLocaleChangedEvent();
|
||||
break;
|
||||
|
||||
case BAPP_SCREEN_CHANGED:
|
||||
// TODO: Handle screen resize or workspace change
|
||||
break;
|
||||
|
||||
default:
|
||||
BLooper::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Window creation/destruction methods
|
||||
int32 GetID(SDL_Window *win)
|
||||
{
|
||||
int32 i;
|
||||
for (i = 0; i < _GetNumWindowSlots(); ++i) {
|
||||
if (GetSDLWindow(i) == NULL) {
|
||||
_SetSDLWindow(win, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Expand the vector if all slots are full
|
||||
if (i == _GetNumWindowSlots()) {
|
||||
_PushBackWindow(win);
|
||||
return i;
|
||||
}
|
||||
|
||||
// TODO: error handling
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: Bad coding practice, but I can't include SDL_BWin.h here. Is
|
||||
there another way to do this? */
|
||||
void ClearID(SDL_BWin *bwin); // Defined in SDL_BeApp.cc
|
||||
|
||||
SDL_Window *GetSDLWindow(int32 winID)
|
||||
{
|
||||
return _window_map[winID];
|
||||
}
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
BGLView *GetCurrentContext()
|
||||
{
|
||||
return _current_context;
|
||||
}
|
||||
|
||||
void SetCurrentContext(BGLView *newContext)
|
||||
{
|
||||
if (_current_context)
|
||||
_current_context->UnlockGL();
|
||||
_current_context = newContext;
|
||||
if (_current_context)
|
||||
_current_context->LockGL();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Event management
|
||||
void _HandleBasicWindowEvent(BMessage *msg, SDL_EventType sdlEventType)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
if (
|
||||
!_GetWinID(msg, &winID)) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, sdlEventType, 0, 0);
|
||||
}
|
||||
|
||||
void _HandleMouseMove(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 x = 0, y = 0;
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("x", &x) != B_OK || // x movement
|
||||
msg->FindInt32("y", &y) != B_OK // y movement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
|
||||
// Simple relative mode support for mouse.
|
||||
if (SDL_GetMouse()->relative_mode) {
|
||||
int winWidth, winHeight, winPosX, winPosY;
|
||||
SDL_GetWindowSize(win, &winWidth, &winHeight);
|
||||
SDL_GetWindowPosition(win, &winPosX, &winPosY);
|
||||
int dx = x - (winWidth / 2);
|
||||
int dy = y - (winHeight / 2);
|
||||
SDL_SendMouseMotion(0, win, SDL_DEFAULT_MOUSE_ID, SDL_GetMouse()->relative_mode, (float)dx, (float)dy);
|
||||
set_mouse_position((winPosX + winWidth / 2), (winPosY + winHeight / 2));
|
||||
if (!be_app->IsCursorHidden())
|
||||
be_app->HideCursor();
|
||||
} else {
|
||||
SDL_SendMouseMotion(0, win, SDL_DEFAULT_MOUSE_ID, false, (float)x, (float)y);
|
||||
if (SDL_CursorVisible() && be_app->IsCursorHidden())
|
||||
be_app->ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleMouseButton(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 button;
|
||||
bool down;
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("button-id", &button) != B_OK ||
|
||||
msg->FindBool("button-down", &down) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendMouseButton(0, win, SDL_DEFAULT_MOUSE_ID, button, down);
|
||||
}
|
||||
|
||||
void _HandleMouseWheel(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 xTicks, yTicks;
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("xticks", &xTicks) != B_OK ||
|
||||
msg->FindInt32("yticks", &yTicks) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendMouseWheel(0, win, SDL_DEFAULT_MOUSE_ID, xTicks, -yTicks, SDL_MOUSEWHEEL_NORMAL);
|
||||
}
|
||||
|
||||
void _HandleKey(BMessage *msg)
|
||||
{
|
||||
int32 scancode;
|
||||
bool down;
|
||||
if (
|
||||
msg->FindInt32("key-scancode", &scancode) != B_OK ||
|
||||
msg->FindBool("key-down", &down) != B_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, scancode, HAIKU_GetScancodeFromBeKey(scancode), down);
|
||||
|
||||
if (down) {
|
||||
SDL_Window *win = SDL_GetKeyboardFocus();
|
||||
if (win && SDL_TextInputActive(win)) {
|
||||
const int8 *keyUtf8;
|
||||
ssize_t count;
|
||||
if (msg->FindData("key-utf8", B_INT8_TYPE, (const void **)&keyUtf8, &count) == B_OK) {
|
||||
char text[64];
|
||||
SDL_zeroa(text);
|
||||
SDL_memcpy(text, keyUtf8, count);
|
||||
SDL_SendKeyboardText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleMouseFocus(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
bool bSetFocus; // If false, lose focus
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindBool("focusGained", &bSetFocus) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
if (bSetFocus) {
|
||||
SDL_SetMouseFocus(win);
|
||||
} else if (SDL_GetMouseFocus() == win) {
|
||||
// Only lose all focus if this window was the current focus
|
||||
SDL_SetMouseFocus(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleKeyboardFocus(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
bool bSetFocus; // If false, lose focus
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindBool("focusGained", &bSetFocus) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
if (bSetFocus) {
|
||||
SDL_SetKeyboardFocus(win);
|
||||
} else if (SDL_GetKeyboardFocus() == win) {
|
||||
// Only lose all focus if this window was the current focus
|
||||
SDL_SetKeyboardFocus(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleWindowMoved(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 xPos, yPos;
|
||||
// Get the window id and new x/y position of the window
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("window-x", &xPos) != B_OK ||
|
||||
msg->FindInt32("window-y", &yPos) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, SDL_EVENT_WINDOW_MOVED, xPos, yPos);
|
||||
}
|
||||
|
||||
void _HandleWindowResized(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 w, h;
|
||||
// Get the window id ]and new x/y position of the window
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("window-w", &w) != B_OK ||
|
||||
msg->FindInt32("window-h", &h) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, SDL_EVENT_WINDOW_RESIZED, w, h);
|
||||
}
|
||||
|
||||
bool _GetWinID(BMessage *msg, int32 *winID)
|
||||
{
|
||||
return msg->FindInt32("window-id", winID) == B_OK;
|
||||
}
|
||||
|
||||
/* Vector functions: Wraps vector stuff in case we need to change
|
||||
implementation */
|
||||
void _SetSDLWindow(SDL_Window *win, int32 winID)
|
||||
{
|
||||
_window_map[winID] = win;
|
||||
}
|
||||
|
||||
int32 _GetNumWindowSlots()
|
||||
{
|
||||
return _window_map.size();
|
||||
}
|
||||
|
||||
void _PopBackWindow()
|
||||
{
|
||||
_window_map.pop_back();
|
||||
}
|
||||
|
||||
void _PushBackWindow(SDL_Window *win)
|
||||
{
|
||||
_window_map.push_back(win);
|
||||
}
|
||||
|
||||
// Members
|
||||
std::vector<SDL_Window *> _window_map; // Keeps track of SDL_Windows by index-id
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
BGLView *_current_context;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PLATFORM_HAIKU
|
||||
|
||||
// Handle the BeApp specific portions of the application
|
||||
|
||||
#include <AppKit.h>
|
||||
#include <storage/AppFileInfo.h>
|
||||
#include <storage/Path.h>
|
||||
#include <storage/Entry.h>
|
||||
#include <storage/File.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
|
||||
#include "SDL_BApp.h" // SDL_BLooper class definition
|
||||
#include "SDL_BeApp.h"
|
||||
|
||||
#include "../../video/haiku/SDL_BWin.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
// Flag to tell whether or not the Be application and looper are active or not
|
||||
static int SDL_BeAppActive = 0;
|
||||
static SDL_Thread *SDL_AppThread = NULL;
|
||||
SDL_BLooper *SDL_Looper = NULL;
|
||||
|
||||
|
||||
// Default application signature
|
||||
const char *SDL_signature = "application/x-SDL-executable";
|
||||
|
||||
|
||||
// Create a descendant of BApplication
|
||||
class SDL_BApp : public BApplication {
|
||||
public:
|
||||
SDL_BApp(const char *signature) :
|
||||
BApplication(signature) {
|
||||
}
|
||||
|
||||
|
||||
virtual ~SDL_BApp() {
|
||||
}
|
||||
|
||||
|
||||
virtual void RefsReceived(BMessage *message) {
|
||||
entry_ref entryRef;
|
||||
for (int32 i = 0; message->FindRef("refs", i, &entryRef) == B_OK; i++) {
|
||||
BPath referencePath = BPath(&entryRef);
|
||||
SDL_SendDropFile(NULL, NULL, referencePath.Path());
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static int StartBeApp(void *unused)
|
||||
{
|
||||
std::unique_ptr<BApplication> App;
|
||||
|
||||
(void)unused;
|
||||
// dig resources for correct signature
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
if (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
||||
BFile f(info.name, O_RDONLY);
|
||||
if (f.InitCheck() == B_OK) {
|
||||
BAppFileInfo app_info(&f);
|
||||
if (app_info.InitCheck() == B_OK) {
|
||||
char sig[B_MIME_TYPE_LENGTH];
|
||||
if (app_info.GetSignature(sig) == B_OK) {
|
||||
SDL_signature = strndup(sig, B_MIME_TYPE_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App = std::unique_ptr<BApplication>(new SDL_BApp(SDL_signature));
|
||||
|
||||
App->Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool StartBeLooper()
|
||||
{
|
||||
if (!be_app) {
|
||||
SDL_AppThread = SDL_CreateThread(StartBeApp, "SDLApplication", NULL);
|
||||
if (!SDL_AppThread) {
|
||||
return SDL_SetError("Couldn't create BApplication thread");
|
||||
}
|
||||
|
||||
do {
|
||||
SDL_Delay(10);
|
||||
} while ((!be_app) || be_app->IsLaunching());
|
||||
}
|
||||
|
||||
SDL_Looper = new SDL_BLooper("SDLLooper");
|
||||
SDL_Looper->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the Be Application, if it's not already started
|
||||
bool SDL_InitBeApp(void)
|
||||
{
|
||||
// Create the BApplication that handles appserver interaction
|
||||
if (SDL_BeAppActive <= 0) {
|
||||
if (!StartBeLooper()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark the application active
|
||||
SDL_BeAppActive = 0;
|
||||
}
|
||||
|
||||
// Increment the application reference count
|
||||
++SDL_BeAppActive;
|
||||
|
||||
// The app is running, and we're ready to go
|
||||
return true;
|
||||
}
|
||||
|
||||
// Quit the Be Application, if there's nothing left to do
|
||||
void SDL_QuitBeApp(void)
|
||||
{
|
||||
// Decrement the application reference count
|
||||
--SDL_BeAppActive;
|
||||
|
||||
// If the reference count reached zero, clean up the app
|
||||
if (SDL_BeAppActive == 0) {
|
||||
SDL_Looper->Lock();
|
||||
SDL_Looper->Quit();
|
||||
SDL_Looper = NULL;
|
||||
if (SDL_AppThread) {
|
||||
if (be_app != NULL) { // Not tested
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
SDL_WaitThread(SDL_AppThread, NULL);
|
||||
SDL_AppThread = NULL;
|
||||
}
|
||||
// be_app should now be NULL since be_app has quit
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// SDL_BApp functions
|
||||
void SDL_BLooper::ClearID(SDL_BWin *bwin) {
|
||||
_SetSDLWindow(NULL, bwin->GetID());
|
||||
int32 i = _GetNumWindowSlots() - 1;
|
||||
while (i >= 0 && GetSDLWindow(i) == NULL) {
|
||||
_PopBackWindow();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_PLATFORM_HAIKU
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Handle the BeApp specific portions of the application
|
||||
|
||||
// Initialize the Be Application, if it's not already started
|
||||
extern bool SDL_InitBeApp(void);
|
||||
|
||||
// Quit the Be Application, if there's nothing left to do
|
||||
extern void SDL_QuitBeApp(void);
|
||||
|
||||
// Be Application Signature
|
||||
extern const char *SDL_signature;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+2039
File diff suppressed because it is too large
Load Diff
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dbus_h_
|
||||
#define SDL_dbus_h_
|
||||
|
||||
#ifdef HAVE_DBUS_DBUS_H
|
||||
#define SDL_USE_LIBDBUS 1
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "../../SDL_list.h"
|
||||
#include "../../SDL_menu.h"
|
||||
|
||||
#ifndef DBUS_TIMEOUT_USE_DEFAULT
|
||||
#define DBUS_TIMEOUT_USE_DEFAULT -1
|
||||
#endif
|
||||
#ifndef DBUS_TIMEOUT_INFINITE
|
||||
#define DBUS_TIMEOUT_INFINITE ((int)0x7fffffff)
|
||||
#endif
|
||||
#ifndef DBUS_TYPE_UNIX_FD
|
||||
#define DBUS_TYPE_UNIX_FD ((int)'h')
|
||||
#endif
|
||||
#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
|
||||
#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
|
||||
#endif
|
||||
|
||||
typedef struct SDL_DBusContext
|
||||
{
|
||||
DBusConnection *session_conn;
|
||||
DBusConnection *system_conn;
|
||||
|
||||
DBusConnection *(*bus_get_private)(DBusBusType, DBusError *);
|
||||
dbus_bool_t (*bus_register)(DBusConnection *, DBusError *);
|
||||
void (*bus_add_match)(DBusConnection *, const char *, DBusError *);
|
||||
void (*bus_remove_match)(DBusConnection *, const char *, DBusError *);
|
||||
const char *(*bus_get_unique_name)(DBusConnection *);
|
||||
DBusConnection *(*connection_open_private)(const char *, DBusError *);
|
||||
void (*connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t);
|
||||
dbus_bool_t (*connection_get_is_connected)(DBusConnection *);
|
||||
dbus_bool_t (*connection_add_filter)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction);
|
||||
dbus_bool_t (*connection_remove_filter)(DBusConnection *, DBusHandleMessageFunction, void *);
|
||||
dbus_bool_t (*connection_try_register_object_path)(DBusConnection *, const char *,
|
||||
const DBusObjectPathVTable *, void *, DBusError *);
|
||||
dbus_bool_t (*connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *);
|
||||
DBusMessage *(*connection_send_with_reply_and_block)(DBusConnection *, DBusMessage *, int, DBusError *);
|
||||
void (*connection_close)(DBusConnection *);
|
||||
void (*connection_ref)(DBusConnection *);
|
||||
void (*connection_unref)(DBusConnection *);
|
||||
void (*connection_flush)(DBusConnection *);
|
||||
dbus_bool_t (*connection_read_write)(DBusConnection *, int);
|
||||
dbus_bool_t (*connection_read_write_dispatch)(DBusConnection *, int);
|
||||
DBusDispatchStatus (*connection_dispatch)(DBusConnection *);
|
||||
dbus_bool_t (*type_is_fixed)(int);
|
||||
dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *);
|
||||
dbus_bool_t (*message_has_path)(DBusMessage *, const char *);
|
||||
DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
|
||||
DBusMessage *(*message_new_signal)(const char *, const char *, const char *);
|
||||
void (*message_set_no_reply)(DBusMessage *, dbus_bool_t);
|
||||
dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
|
||||
dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
|
||||
void (*message_iter_init_append)(DBusMessage *, DBusMessageIter *);
|
||||
dbus_bool_t (*message_iter_open_container)(DBusMessageIter *, int, const char *, DBusMessageIter *);
|
||||
dbus_bool_t (*message_iter_append_basic)(DBusMessageIter *, int, const void *);
|
||||
dbus_bool_t (*message_iter_close_container)(DBusMessageIter *, DBusMessageIter *);
|
||||
dbus_bool_t (*message_get_args)(DBusMessage *, DBusError *, int, ...);
|
||||
dbus_bool_t (*message_get_args_valist)(DBusMessage *, DBusError *, int, va_list);
|
||||
dbus_bool_t (*message_iter_init)(DBusMessage *, DBusMessageIter *);
|
||||
dbus_bool_t (*message_iter_next)(DBusMessageIter *);
|
||||
void (*message_iter_get_basic)(DBusMessageIter *, void *);
|
||||
int (*message_iter_get_arg_type)(DBusMessageIter *);
|
||||
void (*message_iter_recurse)(DBusMessageIter *, DBusMessageIter *);
|
||||
void (*message_unref)(DBusMessage *);
|
||||
dbus_bool_t (*threads_init_default)(void);
|
||||
void (*error_init)(DBusError *);
|
||||
dbus_bool_t (*error_is_set)(const DBusError *);
|
||||
dbus_bool_t (*error_has_name)(const DBusError *, const char *);
|
||||
void (*error_free)(DBusError *);
|
||||
char *(*get_local_machine_id)(void);
|
||||
char *(*try_get_local_machine_id)(DBusError *);
|
||||
void (*free)(void *);
|
||||
void (*free_string_array)(char **);
|
||||
void (*shutdown)(void);
|
||||
|
||||
/* New symbols for SNI and menu export */
|
||||
int (*bus_request_name)(DBusConnection *, const char *, unsigned int, DBusError *);
|
||||
dbus_bool_t (*message_is_method_call)(DBusMessage *, const char *, const char *);
|
||||
DBusMessage *(*message_new_error)(DBusMessage *, const char *, const char *);
|
||||
DBusMessage *(*message_new_method_return)(DBusMessage *);
|
||||
dbus_bool_t (*message_iter_append_fixed_array)(DBusMessageIter *, int, const void *, int);
|
||||
void (*message_iter_get_fixed_array)(DBusMessageIter *, void *, int *);
|
||||
dbus_bool_t (*connection_unregister_object_path)(DBusConnection *, const char *);
|
||||
dbus_bool_t (*connection_get_object_path_data)(DBusConnection *, const char *, void **);
|
||||
} SDL_DBusContext;
|
||||
|
||||
extern void SDL_DBus_Init(void);
|
||||
extern void SDL_DBus_Quit(void);
|
||||
extern SDL_DBusContext *SDL_DBus_GetContext(void);
|
||||
|
||||
// These use the built-in Session connection.
|
||||
extern bool SDL_DBus_CallMethod(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
// save_reply must be non-NULL if it's a string property
|
||||
extern bool SDL_DBus_QueryProperty(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result);
|
||||
|
||||
// These use whatever connection you like.
|
||||
extern bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
// save_reply must be non-NULL if it's a string property
|
||||
extern bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result);
|
||||
|
||||
// Used to free any reply returned from SDL_DBus_CallMethod() and SDL_DBus_QueryProperty()
|
||||
extern void SDL_DBus_FreeReply(DBusMessage **saved_reply);
|
||||
|
||||
extern void SDL_DBus_ScreensaverTickle(void);
|
||||
extern bool SDL_DBus_ScreensaverInhibit(bool inhibit);
|
||||
|
||||
extern bool SDL_DBus_OpenURI(const char *uri, const char *window_id, const char *activation_token);
|
||||
|
||||
extern void SDL_DBus_PumpEvents(void);
|
||||
extern char *SDL_DBus_GetLocalMachineId(void);
|
||||
|
||||
extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);
|
||||
|
||||
extern int SDL_DBus_CameraPortalRequestAccess(void);
|
||||
|
||||
// Menu export functions
|
||||
#define SDL_DBUS_UPDATE_MENU_FLAGS_NONE 0
|
||||
extern SDL_MenuItem *SDL_DBus_CreateMenuItem(void);
|
||||
extern const char *SDL_DBus_ExportMenu(SDL_DBusContext *ctx, DBusConnection *conn, SDL_ListNode *menu);
|
||||
extern void SDL_DBus_UpdateMenu(SDL_DBusContext *ctx, DBusConnection *conn, SDL_ListNode *menu, const char *path, void (*cb)(SDL_ListNode *, const char *, void *), void *cbdata, unsigned char flags);
|
||||
extern void SDL_DBus_RegisterMenuOpenCallback(SDL_ListNode *menu, bool (*cb)(SDL_ListNode *, void *), void *cbdata);
|
||||
extern void SDL_DBus_TransferMenuItemProperties(SDL_MenuItem *src, SDL_MenuItem *dst);
|
||||
extern void SDL_DBus_RetractMenu(SDL_DBusContext *ctx, DBusConnection *conn, const char **path);
|
||||
|
||||
#endif // HAVE_DBUS_DBUS_H
|
||||
|
||||
#endif // SDL_dbus_h_
|
||||
+1050
File diff suppressed because it is too large
Load Diff
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_evdev_h_
|
||||
#define SDL_evdev_h_
|
||||
|
||||
#ifdef SDL_INPUT_LINUXEV
|
||||
|
||||
struct input_event;
|
||||
|
||||
extern bool SDL_EVDEV_Init(void);
|
||||
extern void SDL_EVDEV_Quit(void);
|
||||
extern void SDL_EVDEV_SetVTSwitchCallbacks(void (*release_callback)(void *), void *release_callback_data,
|
||||
void (*acquire_callback)(void *), void *acquire_callback_data);
|
||||
extern int SDL_EVDEV_GetDeviceCount(int device_class);
|
||||
extern void SDL_EVDEV_Poll(void);
|
||||
extern Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event);
|
||||
|
||||
#endif // SDL_INPUT_LINUXEV
|
||||
|
||||
#endif // SDL_evdev_h_
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 2020 Collabora Ltd.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_evdev_capabilities.h"
|
||||
|
||||
#ifdef HAVE_LINUX_INPUT_H
|
||||
|
||||
// missing defines in older Linux kernel headers
|
||||
#ifndef BTN_TRIGGER_HAPPY
|
||||
#define BTN_TRIGGER_HAPPY 0x2c0
|
||||
#endif
|
||||
#ifndef BTN_DPAD_UP
|
||||
#define BTN_DPAD_UP 0x220
|
||||
#endif
|
||||
#ifndef KEY_ALS_TOGGLE
|
||||
#define KEY_ALS_TOGGLE 0x230
|
||||
#endif
|
||||
|
||||
extern int
|
||||
SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)],
|
||||
const unsigned long bitmask_ev[NBITS(EV_MAX)],
|
||||
const unsigned long bitmask_abs[NBITS(ABS_MAX)],
|
||||
const unsigned long bitmask_key[NBITS(KEY_MAX)],
|
||||
const unsigned long bitmask_rel[NBITS(REL_MAX)])
|
||||
{
|
||||
struct range
|
||||
{
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
};
|
||||
|
||||
// key code ranges above BTN_MISC (start is inclusive, stop is exclusive)
|
||||
static const struct range high_key_blocks[] = {
|
||||
{ KEY_OK, BTN_DPAD_UP },
|
||||
{ KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY }
|
||||
};
|
||||
|
||||
int devclass = 0;
|
||||
unsigned long keyboard_mask;
|
||||
|
||||
// If the kernel specifically says it's an accelerometer, believe it
|
||||
if (test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props)) {
|
||||
return SDL_UDEV_DEVICE_ACCELEROMETER;
|
||||
}
|
||||
|
||||
// We treat pointing sticks as indistinguishable from mice
|
||||
if (test_bit(INPUT_PROP_POINTING_STICK, bitmask_props)) {
|
||||
return SDL_UDEV_DEVICE_MOUSE;
|
||||
}
|
||||
|
||||
// We treat buttonpads as equivalent to touchpads
|
||||
if (test_bit(INPUT_PROP_TOPBUTTONPAD, bitmask_props) ||
|
||||
test_bit(INPUT_PROP_BUTTONPAD, bitmask_props) ||
|
||||
test_bit(INPUT_PROP_SEMI_MT, bitmask_props)) {
|
||||
return SDL_UDEV_DEVICE_TOUCHPAD;
|
||||
}
|
||||
|
||||
// X, Y, Z axes but no buttons probably means an accelerometer
|
||||
if (test_bit(EV_ABS, bitmask_ev) &&
|
||||
test_bit(ABS_X, bitmask_abs) &&
|
||||
test_bit(ABS_Y, bitmask_abs) &&
|
||||
test_bit(ABS_Z, bitmask_abs) &&
|
||||
!test_bit(EV_KEY, bitmask_ev)) {
|
||||
return SDL_UDEV_DEVICE_ACCELEROMETER;
|
||||
}
|
||||
|
||||
/* RX, RY, RZ axes but no buttons probably means a gyro or
|
||||
* accelerometer (we don't distinguish) */
|
||||
if (test_bit(EV_ABS, bitmask_ev) &&
|
||||
test_bit(ABS_RX, bitmask_abs) &&
|
||||
test_bit(ABS_RY, bitmask_abs) &&
|
||||
test_bit(ABS_RZ, bitmask_abs) &&
|
||||
!test_bit(EV_KEY, bitmask_ev)) {
|
||||
return SDL_UDEV_DEVICE_ACCELEROMETER;
|
||||
}
|
||||
|
||||
if (test_bit(EV_ABS, bitmask_ev) &&
|
||||
test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
|
||||
if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
|
||||
; // ID_INPUT_TABLET
|
||||
} else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
|
||||
devclass |= SDL_UDEV_DEVICE_TOUCHPAD; // ID_INPUT_TOUCHPAD
|
||||
} else if (test_bit(BTN_MOUSE, bitmask_key)) {
|
||||
devclass |= SDL_UDEV_DEVICE_MOUSE; // ID_INPUT_MOUSE
|
||||
} else if (test_bit(BTN_TOUCH, bitmask_key)) {
|
||||
/* TODO: better determining between touchscreen and multitouch touchpad,
|
||||
see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
|
||||
devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; // ID_INPUT_TOUCHSCREEN
|
||||
}
|
||||
|
||||
if (test_bit(BTN_TRIGGER, bitmask_key) ||
|
||||
test_bit(BTN_A, bitmask_key) ||
|
||||
test_bit(BTN_1, bitmask_key) ||
|
||||
test_bit(ABS_RX, bitmask_abs) ||
|
||||
test_bit(ABS_RY, bitmask_abs) ||
|
||||
test_bit(ABS_RZ, bitmask_abs) ||
|
||||
test_bit(ABS_THROTTLE, bitmask_abs) ||
|
||||
test_bit(ABS_RUDDER, bitmask_abs) ||
|
||||
test_bit(ABS_WHEEL, bitmask_abs) ||
|
||||
test_bit(ABS_GAS, bitmask_abs) ||
|
||||
test_bit(ABS_BRAKE, bitmask_abs)) {
|
||||
devclass |= SDL_UDEV_DEVICE_JOYSTICK; // ID_INPUT_JOYSTICK
|
||||
}
|
||||
}
|
||||
|
||||
if (test_bit(EV_REL, bitmask_ev) &&
|
||||
test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
|
||||
test_bit(BTN_MOUSE, bitmask_key)) {
|
||||
devclass |= SDL_UDEV_DEVICE_MOUSE; // ID_INPUT_MOUSE
|
||||
}
|
||||
|
||||
if (test_bit(EV_KEY, bitmask_ev)) {
|
||||
unsigned i;
|
||||
unsigned long found = 0;
|
||||
|
||||
for (i = 0; i < BTN_MISC / BITS_PER_LONG; ++i) {
|
||||
found |= bitmask_key[i];
|
||||
}
|
||||
// If there are no keys in the lower block, check the higher blocks
|
||||
if (!found) {
|
||||
unsigned block;
|
||||
for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
|
||||
for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
|
||||
if (test_bit(i, bitmask_key)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found > 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_HAS_KEYS; // ID_INPUT_KEY
|
||||
}
|
||||
}
|
||||
|
||||
/* the first 32 bits are ESC, numbers, and Q to D, so if we have all of
|
||||
* those, consider it to be a fully-featured keyboard;
|
||||
* do not test KEY_RESERVED, though */
|
||||
keyboard_mask = 0xFFFFFFFE;
|
||||
if ((bitmask_key[0] & keyboard_mask) == keyboard_mask) {
|
||||
devclass |= SDL_UDEV_DEVICE_KEYBOARD; // ID_INPUT_KEYBOARD
|
||||
}
|
||||
|
||||
return devclass;
|
||||
}
|
||||
|
||||
#endif // HAVE_LINUX_INPUT_H
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 2020 Collabora Ltd.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_evdev_capabilities_h_
|
||||
#define SDL_evdev_capabilities_h_
|
||||
|
||||
#ifdef HAVE_LINUX_INPUT_H
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#ifndef INPUT_PROP_SEMI_MT
|
||||
#define INPUT_PROP_SEMI_MT 0x03
|
||||
#endif
|
||||
#ifndef INPUT_PROP_TOPBUTTONPAD
|
||||
#define INPUT_PROP_TOPBUTTONPAD 0x04
|
||||
#endif
|
||||
#ifndef INPUT_PROP_POINTING_STICK
|
||||
#define INPUT_PROP_POINTING_STICK 0x05
|
||||
#endif
|
||||
#ifndef INPUT_PROP_ACCELEROMETER
|
||||
#define INPUT_PROP_ACCELEROMETER 0x06
|
||||
#endif
|
||||
#ifndef INPUT_PROP_MAX
|
||||
#define INPUT_PROP_MAX 0x1f
|
||||
#endif
|
||||
|
||||
// A device can be any combination of these classes
|
||||
typedef enum
|
||||
{
|
||||
SDL_UDEV_DEVICE_UNKNOWN = 0x0000,
|
||||
SDL_UDEV_DEVICE_MOUSE = 0x0001,
|
||||
SDL_UDEV_DEVICE_KEYBOARD = 0x0002,
|
||||
SDL_UDEV_DEVICE_JOYSTICK = 0x0004,
|
||||
SDL_UDEV_DEVICE_SOUND = 0x0008,
|
||||
SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010,
|
||||
SDL_UDEV_DEVICE_ACCELEROMETER = 0x0020,
|
||||
SDL_UDEV_DEVICE_TOUCHPAD = 0x0040,
|
||||
SDL_UDEV_DEVICE_HAS_KEYS = 0x0080,
|
||||
SDL_UDEV_DEVICE_VIDEO_CAPTURE = 0x0100,
|
||||
} SDL_UDEV_deviceclass;
|
||||
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
#define NBITS(x) ((((x)-1) / BITS_PER_LONG) + 1)
|
||||
#define EVDEV_OFF(x) ((x) % BITS_PER_LONG)
|
||||
#define EVDEV_LONG(x) ((x) / BITS_PER_LONG)
|
||||
#define test_bit(bit, array) ((array[EVDEV_LONG(bit)] >> EVDEV_OFF(bit)) & 1)
|
||||
|
||||
extern int SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)],
|
||||
const unsigned long bitmask_ev[NBITS(EV_MAX)],
|
||||
const unsigned long bitmask_abs[NBITS(ABS_MAX)],
|
||||
const unsigned long bitmask_key[NBITS(KEY_MAX)],
|
||||
const unsigned long bitmask_rel[NBITS(REL_MAX)]);
|
||||
|
||||
#endif // HAVE_LINUX_INPUT_H
|
||||
|
||||
#endif // SDL_evdev_capabilities_h_
|
||||
+995
@@ -0,0 +1,995 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_evdev_kbd.h"
|
||||
|
||||
#ifdef SDL_INPUT_LINUXKD
|
||||
|
||||
// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/keyboard.h>
|
||||
#include <linux/vt.h>
|
||||
#include <linux/tiocl.h> // for TIOCL_GETSHIFTSTATE
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "SDL_evdev_kbd_default_accents.h"
|
||||
#include "SDL_evdev_kbd_default_keymap.h"
|
||||
|
||||
// These are not defined in older Linux kernel headers
|
||||
#ifndef K_UNICODE
|
||||
#define K_UNICODE 0x03
|
||||
#endif
|
||||
#ifndef K_OFF
|
||||
#define K_OFF 0x04
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handler Tables.
|
||||
*/
|
||||
|
||||
#define K_HANDLERS \
|
||||
k_self, k_fn, k_spec, k_pad, \
|
||||
k_dead, k_cons, k_cur, k_shift, \
|
||||
k_meta, k_ascii, k_lock, k_lowercase, \
|
||||
k_slock, k_dead2, k_brl, k_ignore
|
||||
|
||||
typedef void(k_handler_fn)(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag);
|
||||
static k_handler_fn K_HANDLERS;
|
||||
static k_handler_fn *k_handler[16] = { K_HANDLERS };
|
||||
|
||||
typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
|
||||
static void fn_enter(SDL_EVDEV_keyboard_state *kbd);
|
||||
static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd);
|
||||
static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd);
|
||||
static void fn_num(SDL_EVDEV_keyboard_state *kbd);
|
||||
static void fn_compose(SDL_EVDEV_keyboard_state *kbd);
|
||||
|
||||
static fn_handler_fn *fn_handler[] = {
|
||||
NULL, fn_enter, NULL, NULL,
|
||||
NULL, NULL, NULL, fn_caps_toggle,
|
||||
fn_num, NULL, NULL, NULL,
|
||||
NULL, fn_caps_on, fn_compose, NULL,
|
||||
NULL, NULL, NULL, fn_num
|
||||
};
|
||||
|
||||
/*
|
||||
* Keyboard State
|
||||
*/
|
||||
|
||||
struct SDL_EVDEV_keyboard_state
|
||||
{
|
||||
int console_fd;
|
||||
bool muted;
|
||||
int old_kbd_mode;
|
||||
unsigned short **key_maps;
|
||||
unsigned char shift_down[NR_SHIFT]; // shift state counters..
|
||||
bool dead_key_next;
|
||||
int npadch; // -1 or number assembled on pad
|
||||
struct kbdiacrs *accents;
|
||||
unsigned int diacr;
|
||||
bool rep; // flag telling character repeat
|
||||
unsigned char lockstate;
|
||||
unsigned char slockstate;
|
||||
unsigned char ledflagstate;
|
||||
char shift_state;
|
||||
char text[128];
|
||||
unsigned int text_len;
|
||||
void (*vt_release_callback)(void *);
|
||||
void *vt_release_callback_data;
|
||||
void (*vt_acquire_callback)(void *);
|
||||
void *vt_acquire_callback_data;
|
||||
};
|
||||
|
||||
#ifdef DUMP_ACCENTS
|
||||
static void SDL_EVDEV_dump_accents(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printf("static struct kbdiacrs default_accents = {\n");
|
||||
printf(" %d,\n", kbd->accents->kb_cnt);
|
||||
printf(" {\n");
|
||||
for (i = 0; i < kbd->accents->kb_cnt; ++i) {
|
||||
struct kbdiacr *diacr = &kbd->accents->kbdiacr[i];
|
||||
printf(" { 0x%.2x, 0x%.2x, 0x%.2x },\n",
|
||||
diacr->diacr, diacr->base, diacr->result);
|
||||
}
|
||||
while (i < 256) {
|
||||
printf(" { 0x00, 0x00, 0x00 },\n");
|
||||
++i;
|
||||
}
|
||||
printf(" }\n");
|
||||
printf("};\n");
|
||||
}
|
||||
#endif // DUMP_ACCENTS
|
||||
|
||||
#ifdef DUMP_KEYMAP
|
||||
static void SDL_EVDEV_dump_keymap(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
|
||||
if (kbd->key_maps[i]) {
|
||||
printf("static unsigned short default_key_map_%d[NR_KEYS] = {", i);
|
||||
for (j = 0; j < NR_KEYS; ++j) {
|
||||
if ((j % 8) == 0) {
|
||||
printf("\n ");
|
||||
}
|
||||
printf("0x%.4x, ", kbd->key_maps[i][j]);
|
||||
}
|
||||
printf("\n};\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
printf("static unsigned short *default_key_maps[MAX_NR_KEYMAPS] = {\n");
|
||||
for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
|
||||
if (kbd->key_maps[i]) {
|
||||
printf(" default_key_map_%d,\n", i);
|
||||
} else {
|
||||
printf(" NULL,\n");
|
||||
}
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
#endif // DUMP_KEYMAP
|
||||
|
||||
static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
|
||||
static int kbd_cleanup_sigactions_installed = 0;
|
||||
static int kbd_cleanup_atexit_installed = 0;
|
||||
|
||||
static struct sigaction old_sigaction[NSIG];
|
||||
|
||||
static int fatal_signals[] = {
|
||||
// Handlers for SIGTERM and SIGINT are installed in SDL_InitQuit.
|
||||
SIGHUP, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
|
||||
SIGSYS
|
||||
};
|
||||
|
||||
static void kbd_cleanup(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
|
||||
if (!kbd) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = NULL;
|
||||
|
||||
ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
}
|
||||
|
||||
static void SDL_EVDEV_kbd_reraise_signal(int sig)
|
||||
{
|
||||
(void)raise(sig);
|
||||
}
|
||||
|
||||
static siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
|
||||
static void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
|
||||
|
||||
static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
|
||||
{
|
||||
struct sigaction *old_action_p = &(old_sigaction[signum]);
|
||||
sigset_t sigset;
|
||||
|
||||
// Restore original signal handler before going any further.
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
|
||||
// Unmask current signal.
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, signum);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
|
||||
// Save original signal info and context for archeologists.
|
||||
SDL_EVDEV_kdb_cleanup_siginfo = info;
|
||||
SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
|
||||
|
||||
// Restore keyboard.
|
||||
kbd_cleanup();
|
||||
|
||||
// Reraise signal.
|
||||
SDL_EVDEV_kbd_reraise_signal(signum);
|
||||
}
|
||||
|
||||
static void kbd_unregister_emerg_cleanup(void)
|
||||
{
|
||||
int tabidx;
|
||||
|
||||
kbd_cleanup_state = NULL;
|
||||
|
||||
if (!kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 0;
|
||||
|
||||
for (tabidx = 0; tabidx < SDL_arraysize(fatal_signals); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction cur_action;
|
||||
int signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
|
||||
// Examine current signal action
|
||||
if (sigaction(signum, NULL, &cur_action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if action installed and not modified
|
||||
if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restore original action
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_cleanup_atexit(void)
|
||||
{
|
||||
// Restore keyboard.
|
||||
kbd_cleanup();
|
||||
|
||||
// Try to restore signal handlers in case shared library is being unloaded
|
||||
kbd_unregister_emerg_cleanup();
|
||||
}
|
||||
|
||||
static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
int tabidx;
|
||||
|
||||
if (kbd_cleanup_state) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = kbd;
|
||||
|
||||
if (!kbd_cleanup_atexit_installed) {
|
||||
/* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
|
||||
* functions that are called when the shared library is unloaded.
|
||||
* -- man atexit(3)
|
||||
*/
|
||||
(void)atexit(kbd_cleanup_atexit);
|
||||
kbd_cleanup_atexit_installed = 1;
|
||||
}
|
||||
|
||||
if (kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 1;
|
||||
|
||||
for (tabidx = 0; tabidx < SDL_arraysize(fatal_signals); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction new_action;
|
||||
int signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
if (sigaction(signum, NULL, old_action_p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip SIGHUP and SIGPIPE if handler is already installed
|
||||
* - assume the handler will do the cleanup
|
||||
*/
|
||||
if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_action = *old_action_p;
|
||||
new_action.sa_flags |= SA_SIGINFO;
|
||||
new_action.sa_sigaction = &kbd_cleanup_signal_action;
|
||||
sigaction(signum, &new_action, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
VT_SIGNAL_NONE,
|
||||
VT_SIGNAL_RELEASE,
|
||||
VT_SIGNAL_ACQUIRE,
|
||||
};
|
||||
static int vt_release_signal;
|
||||
static int vt_acquire_signal;
|
||||
static SDL_AtomicInt vt_signal_pending;
|
||||
|
||||
typedef void (*signal_handler)(int signum);
|
||||
|
||||
static void kbd_vt_release_signal_action(int signum)
|
||||
{
|
||||
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE);
|
||||
}
|
||||
|
||||
static void kbd_vt_acquire_signal_action(int signum)
|
||||
{
|
||||
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE);
|
||||
}
|
||||
|
||||
static bool setup_vt_signal(int signum, signal_handler handler)
|
||||
{
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction new_action;
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
SDL_zero(new_action);
|
||||
new_action.sa_handler = handler;
|
||||
new_action.sa_flags = SA_RESTART;
|
||||
if (sigaction(signum, &new_action, old_action_p) < 0) {
|
||||
return false;
|
||||
}
|
||||
if (old_action_p->sa_handler != SIG_DFL) {
|
||||
// This signal is already in use
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int find_free_signal(signal_handler handler)
|
||||
{
|
||||
#ifdef SIGRTMIN
|
||||
int i;
|
||||
|
||||
for (i = SIGRTMIN + 2; i <= SIGRTMAX; ++i) {
|
||||
if (setup_vt_signal(i, handler)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (setup_vt_signal(SIGUSR1, handler)) {
|
||||
return SIGUSR1;
|
||||
}
|
||||
if (setup_vt_signal(SIGUSR2, handler)) {
|
||||
return SIGUSR2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbd_vt_quit(int console_fd)
|
||||
{
|
||||
struct vt_mode mode;
|
||||
|
||||
if (vt_release_signal) {
|
||||
sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL);
|
||||
vt_release_signal = 0;
|
||||
}
|
||||
if (vt_acquire_signal) {
|
||||
sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL);
|
||||
vt_acquire_signal = 0;
|
||||
}
|
||||
|
||||
SDL_zero(mode);
|
||||
mode.mode = VT_AUTO;
|
||||
ioctl(console_fd, VT_SETMODE, &mode);
|
||||
}
|
||||
|
||||
static bool kbd_vt_init(int console_fd)
|
||||
{
|
||||
struct vt_mode mode;
|
||||
|
||||
vt_release_signal = find_free_signal(kbd_vt_release_signal_action);
|
||||
vt_acquire_signal = find_free_signal(kbd_vt_acquire_signal_action);
|
||||
if (!vt_release_signal || !vt_acquire_signal ) {
|
||||
kbd_vt_quit(console_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_zero(mode);
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = vt_release_signal;
|
||||
mode.acqsig = vt_acquire_signal;
|
||||
mode.frsig = SIGIO;
|
||||
if (ioctl(console_fd, VT_SETMODE, &mode) < 0) {
|
||||
kbd_vt_quit(console_fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void kbd_vt_update(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
int signal_pending = SDL_GetAtomicInt(&vt_signal_pending);
|
||||
if (signal_pending != VT_SIGNAL_NONE) {
|
||||
if (signal_pending == VT_SIGNAL_RELEASE) {
|
||||
if (state->vt_release_callback) {
|
||||
state->vt_release_callback(state->vt_release_callback_data);
|
||||
}
|
||||
ioctl(state->console_fd, VT_RELDISP, 1);
|
||||
} else {
|
||||
if (state->vt_acquire_callback) {
|
||||
state->vt_acquire_callback(state->vt_acquire_callback_data);
|
||||
}
|
||||
ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ);
|
||||
}
|
||||
SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd;
|
||||
char flag_state;
|
||||
char kbtype;
|
||||
char shift_state[sizeof(long)] = { TIOCL_GETSHIFTSTATE, 0 };
|
||||
|
||||
kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(*kbd));
|
||||
if (!kbd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This might fail if we're not connected to a tty (e.g. on the Steam Link)
|
||||
kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
|
||||
if (!((ioctl(kbd->console_fd, KDGKBTYPE, &kbtype) == 0) && ((kbtype == KB_101) || (kbtype == KB_84)))) {
|
||||
close(kbd->console_fd);
|
||||
kbd->console_fd = -1;
|
||||
}
|
||||
|
||||
kbd->npadch = -1;
|
||||
|
||||
if (ioctl(kbd->console_fd, TIOCLINUX, shift_state) == 0) {
|
||||
kbd->shift_state = *shift_state;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBLED, &flag_state) == 0) {
|
||||
kbd->ledflagstate = flag_state;
|
||||
}
|
||||
|
||||
kbd->accents = &default_accents;
|
||||
kbd->key_maps = default_key_maps;
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
|
||||
// Set the keyboard in UNICODE mode and load the keymaps
|
||||
ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
|
||||
}
|
||||
|
||||
kbd_vt_init(kbd->console_fd);
|
||||
|
||||
return kbd;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
|
||||
{
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (muted == state->muted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (muted) {
|
||||
if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
|
||||
/* Mute the keyboard so keystrokes only generate evdev events
|
||||
* and do not leak through to the console
|
||||
*/
|
||||
ioctl(state->console_fd, KDSKBMODE, K_OFF);
|
||||
|
||||
/* Make sure to restore keyboard if application fails to call
|
||||
* SDL_Quit before exit or fatal signal is raised.
|
||||
*/
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
|
||||
kbd_register_emerg_cleanup(state);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
// Restore the original keyboard mode
|
||||
ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
|
||||
}
|
||||
state->muted = muted;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data)
|
||||
{
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->vt_release_callback = release_callback;
|
||||
state->vt_release_callback_data = release_callback_data;
|
||||
state->vt_acquire_callback = acquire_callback;
|
||||
state->vt_acquire_callback_data = acquire_callback_data;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
kbd_vt_update(state);
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_EVDEV_kbd_set_muted(state, false);
|
||||
|
||||
kbd_vt_quit(state->console_fd);
|
||||
|
||||
if (state->console_fd >= 0) {
|
||||
close(state->console_fd);
|
||||
state->console_fd = -1;
|
||||
}
|
||||
|
||||
if (state->key_maps && state->key_maps != default_key_maps) {
|
||||
int i;
|
||||
for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
|
||||
SDL_free(state->key_maps[i]);
|
||||
}
|
||||
SDL_free(state->key_maps);
|
||||
}
|
||||
|
||||
SDL_free(state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper Functions.
|
||||
*/
|
||||
static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
// c is already part of a UTF-8 sequence and safe to add as a character
|
||||
if (kbd->text_len < (sizeof(kbd->text) - 1)) {
|
||||
kbd->text[kbd->text_len++] = (char)c;
|
||||
}
|
||||
}
|
||||
|
||||
static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
if (c < 0x80) {
|
||||
put_queue(kbd, c); /* 0******* */
|
||||
} else if (c < 0x800) {
|
||||
/* 110***** 10****** */
|
||||
put_queue(kbd, 0xc0 | (c >> 6));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x10000) {
|
||||
if (c >= 0xD800 && c < 0xE000) {
|
||||
return;
|
||||
}
|
||||
if (c == 0xFFFF) {
|
||||
return;
|
||||
}
|
||||
/* 1110**** 10****** 10****** */
|
||||
put_queue(kbd, 0xe0 | (c >> 12));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x110000) {
|
||||
/* 11110*** 10****** 10****** 10****** */
|
||||
put_queue(kbd, 0xf0 | (c >> 18));
|
||||
put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a combining character DIACR here, followed by the character CH.
|
||||
* If the combination occurs in the table, return the corresponding value.
|
||||
* Otherwise, if CH is a space or equals DIACR, return DIACR.
|
||||
* Otherwise, conclude that DIACR was not combining after all,
|
||||
* queue it and return CH.
|
||||
*/
|
||||
static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
|
||||
{
|
||||
unsigned int d = kbd->diacr;
|
||||
unsigned int i;
|
||||
|
||||
kbd->diacr = 0;
|
||||
|
||||
if (kbd->console_fd >= 0)
|
||||
if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
|
||||
// No worries, we'll use the default accent table
|
||||
}
|
||||
|
||||
for (i = 0; i < kbd->accents->kb_cnt; i++) {
|
||||
if (kbd->accents->kbdiacr[i].diacr == d &&
|
||||
kbd->accents->kbdiacr[i].base == ch) {
|
||||
return kbd->accents->kbdiacr[i].result;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == ' ' || ch == d) {
|
||||
return d;
|
||||
}
|
||||
|
||||
put_utf8(kbd, d);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
return (kbd->ledflagstate & flag) != 0;
|
||||
}
|
||||
|
||||
static void set_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate |= flag;
|
||||
ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
static void clr_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate &= ~flag;
|
||||
ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->lockstate ^= 1 << flag;
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->slockstate ^= 1 << flag;
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate ^= flag;
|
||||
ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
/*
|
||||
* Special function handlers
|
||||
*/
|
||||
|
||||
static void fn_enter(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
if (kbd->diacr) {
|
||||
put_utf8(kbd, kbd->diacr);
|
||||
kbd->diacr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
if (kbd->rep) {
|
||||
return;
|
||||
}
|
||||
|
||||
chg_vc_kbd_led(kbd, K_CAPSLOCK);
|
||||
}
|
||||
|
||||
static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
if (kbd->rep) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_vc_kbd_led(kbd, K_CAPSLOCK);
|
||||
}
|
||||
|
||||
static void fn_num(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
if (!kbd->rep) {
|
||||
chg_vc_kbd_led(kbd, K_NUMLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
static void fn_compose(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
kbd->dead_key_next = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special key handlers
|
||||
*/
|
||||
|
||||
static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_spec(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
if (up_flag) {
|
||||
return;
|
||||
}
|
||||
if (value >= SDL_arraysize(fn_handler)) {
|
||||
return;
|
||||
}
|
||||
if (fn_handler[value]) {
|
||||
fn_handler[value](kbd);
|
||||
}
|
||||
}
|
||||
|
||||
static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
if (up_flag) {
|
||||
return; // no action, if this is a key release
|
||||
}
|
||||
|
||||
if (kbd->diacr) {
|
||||
value = handle_diacr(kbd, value);
|
||||
}
|
||||
|
||||
if (kbd->dead_key_next) {
|
||||
kbd->dead_key_next = false;
|
||||
kbd->diacr = value;
|
||||
return;
|
||||
}
|
||||
put_utf8(kbd, value);
|
||||
}
|
||||
|
||||
static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
|
||||
{
|
||||
if (up_flag) {
|
||||
return;
|
||||
}
|
||||
|
||||
kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
|
||||
}
|
||||
|
||||
static void k_dead(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
const unsigned char ret_diacr[NR_DEAD] = { '`', '\'', '^', '~', '"', ',' };
|
||||
|
||||
k_deadunicode(kbd, ret_diacr[value], up_flag);
|
||||
}
|
||||
|
||||
static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
k_deadunicode(kbd, value, up_flag);
|
||||
}
|
||||
|
||||
static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_pad(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
static const char pad_chars[] = "0123456789+-*/\015,.?()#";
|
||||
|
||||
if (up_flag) {
|
||||
return; // no action, if this is a key release
|
||||
}
|
||||
|
||||
if (!vc_kbd_led(kbd, K_NUMLOCK)) {
|
||||
// unprintable action
|
||||
return;
|
||||
}
|
||||
|
||||
put_queue(kbd, pad_chars[value]);
|
||||
}
|
||||
|
||||
static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
int old_state = kbd->shift_state;
|
||||
|
||||
if (kbd->rep) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Mimic typewriter:
|
||||
* a CapsShift key acts like Shift but undoes CapsLock
|
||||
*/
|
||||
if (value == KVAL(K_CAPSSHIFT)) {
|
||||
value = KVAL(K_SHIFT);
|
||||
if (!up_flag) {
|
||||
clr_vc_kbd_led(kbd, K_CAPSLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
if (up_flag) {
|
||||
/*
|
||||
* handle the case that two shift or control
|
||||
* keys are depressed simultaneously
|
||||
*/
|
||||
if (kbd->shift_down[value]) {
|
||||
kbd->shift_down[value]--;
|
||||
}
|
||||
} else {
|
||||
kbd->shift_down[value]++;
|
||||
}
|
||||
|
||||
if (kbd->shift_down[value]) {
|
||||
kbd->shift_state |= (1 << value);
|
||||
} else {
|
||||
kbd->shift_state &= ~(1 << value);
|
||||
}
|
||||
|
||||
// kludge
|
||||
if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
|
||||
put_utf8(kbd, kbd->npadch);
|
||||
kbd->npadch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
static void k_ascii(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
int base;
|
||||
|
||||
if (up_flag) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 10) {
|
||||
// decimal input of code, while Alt depressed
|
||||
base = 10;
|
||||
} else {
|
||||
// hexadecimal input of code, while AltGr depressed
|
||||
value -= 10;
|
||||
base = 16;
|
||||
}
|
||||
|
||||
if (kbd->npadch == -1) {
|
||||
kbd->npadch = value;
|
||||
} else {
|
||||
kbd->npadch = kbd->npadch * base + value;
|
||||
}
|
||||
}
|
||||
|
||||
static void k_lock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
if (up_flag || kbd->rep) {
|
||||
return;
|
||||
}
|
||||
|
||||
chg_vc_kbd_lock(kbd, value);
|
||||
}
|
||||
|
||||
static void k_slock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
k_shift(kbd, value, up_flag);
|
||||
if (up_flag || kbd->rep) {
|
||||
return;
|
||||
}
|
||||
|
||||
chg_vc_kbd_slock(kbd, value);
|
||||
// try to make Alt, oops, AltGr and such work
|
||||
if (!kbd->key_maps[kbd->lockstate ^ kbd->slockstate]) {
|
||||
kbd->slockstate = 0;
|
||||
chg_vc_kbd_slock(kbd, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
|
||||
{
|
||||
unsigned char shift_final;
|
||||
unsigned char type;
|
||||
unsigned short *key_map;
|
||||
unsigned short keysym;
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->rep = (down == 2);
|
||||
|
||||
shift_final = (state->shift_state | state->slockstate) ^ state->lockstate;
|
||||
key_map = state->key_maps[shift_final];
|
||||
if (!key_map) {
|
||||
// Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state
|
||||
state->shift_state = 0;
|
||||
state->slockstate = 0;
|
||||
state->lockstate = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (keycode < NR_KEYS) {
|
||||
if (state->console_fd < 0) {
|
||||
keysym = key_map[keycode];
|
||||
} else {
|
||||
struct kbentry kbe;
|
||||
kbe.kb_table = shift_final;
|
||||
kbe.kb_index = keycode;
|
||||
if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
|
||||
keysym = (kbe.kb_value ^ 0xf000);
|
||||
else
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
type = KTYP(keysym);
|
||||
|
||||
if (type < 0xf0) {
|
||||
if (down) {
|
||||
put_utf8(state, keysym);
|
||||
}
|
||||
} else {
|
||||
type -= 0xf0;
|
||||
|
||||
// if type is KT_LETTER then it can be affected by Caps Lock
|
||||
if (type == KT_LETTER) {
|
||||
type = KT_LATIN;
|
||||
|
||||
if (vc_kbd_led(state, K_CAPSLOCK)) {
|
||||
shift_final = shift_final ^ (1 << KG_SHIFT);
|
||||
key_map = state->key_maps[shift_final];
|
||||
if (key_map) {
|
||||
if (state->console_fd < 0) {
|
||||
keysym = key_map[keycode];
|
||||
} else {
|
||||
struct kbentry kbe;
|
||||
kbe.kb_table = shift_final;
|
||||
kbe.kb_index = keycode;
|
||||
if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
|
||||
keysym = (kbe.kb_value ^ 0xf000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(*k_handler[type])(state, keysym & 0xff, !down);
|
||||
|
||||
if (type != KT_SLOCK) {
|
||||
state->slockstate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->text_len > 0) {
|
||||
state->text[state->text_len] = '\0';
|
||||
SDL_SendKeyboardText(state->text);
|
||||
state->text_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#elif !defined(SDL_INPUT_FBSDKBIO) // !SDL_INPUT_LINUXKD
|
||||
|
||||
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // SDL_INPUT_LINUXKD
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_evdev_kbd_h_
|
||||
#define SDL_evdev_kbd_h_
|
||||
|
||||
#ifdef SDL_INPUT_FBSDKBIO
|
||||
enum {
|
||||
VT_OURS,
|
||||
VT_THEIRS,
|
||||
};
|
||||
extern SDL_AtomicInt vt_current;
|
||||
#endif
|
||||
|
||||
struct SDL_EVDEV_keyboard_state;
|
||||
typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;
|
||||
|
||||
extern SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void);
|
||||
extern void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted);
|
||||
extern void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data);
|
||||
extern void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state);
|
||||
extern void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down);
|
||||
extern void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state);
|
||||
|
||||
#endif // SDL_evdev_kbd_h_
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
static struct kbdiacrs default_accents = {
|
||||
68,
|
||||
{
|
||||
{ 0x60, 0x41, 0xc0 },
|
||||
{ 0x60, 0x61, 0xe0 },
|
||||
{ 0x27, 0x41, 0xc1 },
|
||||
{ 0x27, 0x61, 0xe1 },
|
||||
{ 0x5e, 0x41, 0xc2 },
|
||||
{ 0x5e, 0x61, 0xe2 },
|
||||
{ 0x7e, 0x41, 0xc3 },
|
||||
{ 0x7e, 0x61, 0xe3 },
|
||||
{ 0x22, 0x41, 0xc4 },
|
||||
{ 0x22, 0x61, 0xe4 },
|
||||
{ 0x4f, 0x41, 0xc5 },
|
||||
{ 0x6f, 0x61, 0xe5 },
|
||||
{ 0x30, 0x41, 0xc5 },
|
||||
{ 0x30, 0x61, 0xe5 },
|
||||
{ 0x41, 0x41, 0xc5 },
|
||||
{ 0x61, 0x61, 0xe5 },
|
||||
{ 0x41, 0x45, 0xc6 },
|
||||
{ 0x61, 0x65, 0xe6 },
|
||||
{ 0x2c, 0x43, 0xc7 },
|
||||
{ 0x2c, 0x63, 0xe7 },
|
||||
{ 0x60, 0x45, 0xc8 },
|
||||
{ 0x60, 0x65, 0xe8 },
|
||||
{ 0x27, 0x45, 0xc9 },
|
||||
{ 0x27, 0x65, 0xe9 },
|
||||
{ 0x5e, 0x45, 0xca },
|
||||
{ 0x5e, 0x65, 0xea },
|
||||
{ 0x22, 0x45, 0xcb },
|
||||
{ 0x22, 0x65, 0xeb },
|
||||
{ 0x60, 0x49, 0xcc },
|
||||
{ 0x60, 0x69, 0xec },
|
||||
{ 0x27, 0x49, 0xcd },
|
||||
{ 0x27, 0x69, 0xed },
|
||||
{ 0x5e, 0x49, 0xce },
|
||||
{ 0x5e, 0x69, 0xee },
|
||||
{ 0x22, 0x49, 0xcf },
|
||||
{ 0x22, 0x69, 0xef },
|
||||
{ 0x2d, 0x44, 0xd0 },
|
||||
{ 0x2d, 0x64, 0xf0 },
|
||||
{ 0x7e, 0x4e, 0xd1 },
|
||||
{ 0x7e, 0x6e, 0xf1 },
|
||||
{ 0x60, 0x4f, 0xd2 },
|
||||
{ 0x60, 0x6f, 0xf2 },
|
||||
{ 0x27, 0x4f, 0xd3 },
|
||||
{ 0x27, 0x6f, 0xf3 },
|
||||
{ 0x5e, 0x4f, 0xd4 },
|
||||
{ 0x5e, 0x6f, 0xf4 },
|
||||
{ 0x7e, 0x4f, 0xd5 },
|
||||
{ 0x7e, 0x6f, 0xf5 },
|
||||
{ 0x22, 0x4f, 0xd6 },
|
||||
{ 0x22, 0x6f, 0xf6 },
|
||||
{ 0x2f, 0x4f, 0xd8 },
|
||||
{ 0x2f, 0x6f, 0xf8 },
|
||||
{ 0x60, 0x55, 0xd9 },
|
||||
{ 0x60, 0x75, 0xf9 },
|
||||
{ 0x27, 0x55, 0xda },
|
||||
{ 0x27, 0x75, 0xfa },
|
||||
{ 0x5e, 0x55, 0xdb },
|
||||
{ 0x5e, 0x75, 0xfb },
|
||||
{ 0x22, 0x55, 0xdc },
|
||||
{ 0x22, 0x75, 0xfc },
|
||||
{ 0x27, 0x59, 0xdd },
|
||||
{ 0x27, 0x79, 0xfd },
|
||||
{ 0x54, 0x48, 0xde },
|
||||
{ 0x74, 0x68, 0xfe },
|
||||
{ 0x73, 0x73, 0xdf },
|
||||
{ 0x22, 0x79, 0xff },
|
||||
{ 0x73, 0x7a, 0xdf },
|
||||
{ 0x69, 0x6a, 0xff },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x00 },
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
+440
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SDL_fcitx.h"
|
||||
#include "../../video/SDL_sysvideo.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
#include "../../core/unix/SDL_appid.h"
|
||||
#include "SDL_dbus.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
#include "../../video/x11/SDL_x11video.h"
|
||||
#endif
|
||||
|
||||
#define FCITX_DBUS_SERVICE "org.freedesktop.portal.Fcitx"
|
||||
|
||||
#define FCITX_IM_DBUS_PATH "/org/freedesktop/portal/inputmethod"
|
||||
|
||||
#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod1"
|
||||
#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext1"
|
||||
|
||||
#define DBUS_TIMEOUT 500
|
||||
|
||||
typedef struct FcitxClient
|
||||
{
|
||||
SDL_DBusContext *dbus;
|
||||
|
||||
char *ic_path;
|
||||
|
||||
int id;
|
||||
|
||||
SDL_Rect cursor_rect;
|
||||
} FcitxClient;
|
||||
|
||||
static FcitxClient fcitx_client;
|
||||
|
||||
static const char *GetAppName(void)
|
||||
{
|
||||
const char *exe_name = SDL_GetExeName();
|
||||
if (exe_name) {
|
||||
return exe_name;
|
||||
}
|
||||
return "SDL_App";
|
||||
}
|
||||
|
||||
static size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus,
|
||||
DBusMessage *msg,
|
||||
char **ret,
|
||||
Sint32 *start_pos,
|
||||
Sint32 *end_pos)
|
||||
{
|
||||
char *text = NULL, *subtext;
|
||||
size_t text_bytes = 0;
|
||||
DBusMessageIter iter, array, sub;
|
||||
Sint32 p_start_pos = -1;
|
||||
Sint32 p_end_pos = -1;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
// Message type is a(si)i, we only need string part
|
||||
if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
|
||||
size_t pos = 0;
|
||||
// First pass: calculate string length
|
||||
dbus->message_iter_recurse(&iter, &array);
|
||||
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
|
||||
dbus->message_iter_recurse(&array, &sub);
|
||||
subtext = NULL;
|
||||
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
|
||||
dbus->message_iter_get_basic(&sub, &subtext);
|
||||
if (subtext && *subtext) {
|
||||
text_bytes += SDL_strlen(subtext);
|
||||
}
|
||||
}
|
||||
dbus->message_iter_next(&sub);
|
||||
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_INT32 && p_end_pos == -1) {
|
||||
// Type is a bit field defined as follows:
|
||||
// bit 3: Underline, bit 4: HighLight, bit 5: DontCommit,
|
||||
// bit 6: Bold, bit 7: Strike, bit 8: Italic
|
||||
Sint32 type;
|
||||
dbus->message_iter_get_basic(&sub, &type);
|
||||
// We only consider highlight
|
||||
if (type & (1 << 4)) {
|
||||
if (p_start_pos == -1) {
|
||||
p_start_pos = pos;
|
||||
}
|
||||
} else if (p_start_pos != -1 && p_end_pos == -1) {
|
||||
p_end_pos = pos;
|
||||
}
|
||||
}
|
||||
dbus->message_iter_next(&array);
|
||||
if (subtext && *subtext) {
|
||||
pos += SDL_utf8strlen(subtext);
|
||||
}
|
||||
}
|
||||
if (p_start_pos != -1 && p_end_pos == -1) {
|
||||
p_end_pos = pos;
|
||||
}
|
||||
if (text_bytes) {
|
||||
text = SDL_malloc(text_bytes + 1);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
char *pivot = text;
|
||||
// Second pass: join all the sub string
|
||||
dbus->message_iter_recurse(&iter, &array);
|
||||
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
|
||||
dbus->message_iter_recurse(&array, &sub);
|
||||
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
|
||||
dbus->message_iter_get_basic(&sub, &subtext);
|
||||
if (subtext && *subtext) {
|
||||
size_t length = SDL_strlen(subtext);
|
||||
SDL_strlcpy(pivot, subtext, length + 1);
|
||||
pivot += length;
|
||||
}
|
||||
}
|
||||
dbus->message_iter_next(&array);
|
||||
}
|
||||
} else {
|
||||
text_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = text;
|
||||
*start_pos = p_start_pos;
|
||||
*end_pos = p_end_pos;
|
||||
return text_bytes;
|
||||
}
|
||||
|
||||
static Sint32 Fcitx_GetPreeditCursorByte(SDL_DBusContext *dbus, DBusMessage *msg)
|
||||
{
|
||||
Sint32 byte = -1;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
|
||||
dbus->message_iter_next(&iter);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus->message_iter_get_basic(&iter, &byte);
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
|
||||
{
|
||||
SDL_DBusContext *dbus = (SDL_DBusContext *)data;
|
||||
|
||||
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
|
||||
DBusMessageIter iter;
|
||||
const char *text = NULL;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
dbus->message_iter_get_basic(&iter, &text);
|
||||
|
||||
SDL_SendKeyboardText(text);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {
|
||||
char *text = NULL;
|
||||
Sint32 start_pos, end_pos;
|
||||
size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text, &start_pos, &end_pos);
|
||||
if (text_bytes) {
|
||||
if (start_pos == -1) {
|
||||
Sint32 byte_pos = Fcitx_GetPreeditCursorByte(dbus, msg);
|
||||
start_pos = byte_pos >= 0 ? SDL_utf8strnlen(text, byte_pos) : -1;
|
||||
}
|
||||
SDL_SendEditingText(text, start_pos, end_pos >= 0 ? end_pos - start_pos : -1);
|
||||
SDL_free(text);
|
||||
} else {
|
||||
SDL_SendEditingText("", 0, 0);
|
||||
}
|
||||
|
||||
SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static void FcitxClientICCallMethod(FcitxClient *client, const char *method)
|
||||
{
|
||||
if (!client->ic_path) {
|
||||
return;
|
||||
}
|
||||
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static void SDLCALL Fcitx_SetCapabilities(void *data,
|
||||
const char *name,
|
||||
const char *old_val,
|
||||
const char *hint)
|
||||
{
|
||||
FcitxClient *client = (FcitxClient *)data;
|
||||
Uint64 caps = 0;
|
||||
if (!client->ic_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hint && SDL_strstr(hint, "composition")) {
|
||||
caps |= (1 << 1); // Preedit Flag
|
||||
caps |= (1 << 4); // Formatted Preedit Flag
|
||||
}
|
||||
if (hint && SDL_strstr(hint, "candidates")) {
|
||||
// FIXME, turn off native candidate rendering
|
||||
}
|
||||
|
||||
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, char **ic_path)
|
||||
{
|
||||
const char *program = "program";
|
||||
bool result = false;
|
||||
|
||||
if (dbus && dbus->session_conn) {
|
||||
DBusMessage *msg = dbus->message_new_method_call(FCITX_DBUS_SERVICE, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateInputContext");
|
||||
if (msg) {
|
||||
DBusMessage *reply = NULL;
|
||||
DBusMessageIter args, array, sub;
|
||||
dbus->message_iter_init_append(msg, &args);
|
||||
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(ss)", &array);
|
||||
dbus->message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &sub);
|
||||
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &program);
|
||||
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appname);
|
||||
dbus->message_iter_close_container(&array, &sub);
|
||||
dbus->message_iter_close_container(&args, &array);
|
||||
reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
|
||||
if (reply) {
|
||||
if (dbus->message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, ic_path, DBUS_TYPE_INVALID)) {
|
||||
result = true;
|
||||
}
|
||||
dbus->message_unref(reply);
|
||||
}
|
||||
dbus->message_unref(msg);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool FcitxClientCreateIC(FcitxClient *client)
|
||||
{
|
||||
const char *appname = GetAppName();
|
||||
char *ic_path = NULL;
|
||||
SDL_DBusContext *dbus = client->dbus;
|
||||
|
||||
// SDL_DBus_CallMethod cannot handle a(ss) type, call dbus function directly
|
||||
if (!FcitxCreateInputContext(dbus, appname, &ic_path)) {
|
||||
ic_path = NULL; // just in case.
|
||||
}
|
||||
|
||||
if (ic_path) {
|
||||
SDL_free(client->ic_path);
|
||||
client->ic_path = SDL_strdup(ic_path);
|
||||
|
||||
dbus->bus_add_match(dbus->session_conn,
|
||||
"type='signal', interface='org.fcitx.Fcitx.InputContext1'",
|
||||
NULL);
|
||||
dbus->connection_add_filter(dbus->session_conn,
|
||||
&DBus_MessageFilter, dbus,
|
||||
NULL);
|
||||
dbus->connection_flush(dbus->session_conn);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, Fcitx_SetCapabilities, client);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Uint32 Fcitx_ModState(void)
|
||||
{
|
||||
Uint32 fcitx_mods = 0;
|
||||
SDL_Keymod sdl_mods = SDL_GetModState();
|
||||
|
||||
if (sdl_mods & SDL_KMOD_SHIFT) {
|
||||
fcitx_mods |= (1 << 0);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_CAPS) {
|
||||
fcitx_mods |= (1 << 1);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_CTRL) {
|
||||
fcitx_mods |= (1 << 2);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_ALT) {
|
||||
fcitx_mods |= (1 << 3);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_NUM) {
|
||||
fcitx_mods |= (1 << 4);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_MODE) {
|
||||
fcitx_mods |= (1 << 7);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_LGUI) {
|
||||
fcitx_mods |= (1 << 6);
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_RGUI) {
|
||||
fcitx_mods |= (1 << 28);
|
||||
}
|
||||
|
||||
return fcitx_mods;
|
||||
}
|
||||
|
||||
bool SDL_Fcitx_Init(void)
|
||||
{
|
||||
fcitx_client.dbus = SDL_DBus_GetContext();
|
||||
|
||||
fcitx_client.cursor_rect.x = -1;
|
||||
fcitx_client.cursor_rect.y = -1;
|
||||
fcitx_client.cursor_rect.w = 0;
|
||||
fcitx_client.cursor_rect.h = 0;
|
||||
|
||||
return FcitxClientCreateIC(&fcitx_client);
|
||||
}
|
||||
|
||||
void SDL_Fcitx_Quit(void)
|
||||
{
|
||||
FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
|
||||
if (fcitx_client.ic_path) {
|
||||
SDL_free(fcitx_client.ic_path);
|
||||
fcitx_client.ic_path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_Fcitx_SetFocus(bool focused)
|
||||
{
|
||||
if (focused) {
|
||||
FcitxClientICCallMethod(&fcitx_client, "FocusIn");
|
||||
} else {
|
||||
FcitxClientICCallMethod(&fcitx_client, "FocusOut");
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_Fcitx_Reset(void)
|
||||
{
|
||||
FcitxClientICCallMethod(&fcitx_client, "Reset");
|
||||
}
|
||||
|
||||
bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
|
||||
{
|
||||
Uint32 mod_state = Fcitx_ModState();
|
||||
Uint32 handled = false;
|
||||
Uint32 is_release = !down;
|
||||
Uint32 event_time = 0;
|
||||
|
||||
if (!fcitx_client.ic_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_DBus_CallMethod(NULL, FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
|
||||
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &mod_state, DBUS_TYPE_BOOLEAN, &is_release, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) {
|
||||
if (handled) {
|
||||
SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDL_Fcitx_UpdateTextInputArea(SDL_Window *window)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
SDL_Rect *cursor = &fcitx_client.cursor_rect;
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll use a square at the text input cursor location for the cursor_rect
|
||||
cursor->x = window->text_input_rect.x + window->text_input_cursor;
|
||||
cursor->y = window->text_input_rect.y;
|
||||
cursor->w = window->text_input_rect.h;
|
||||
cursor->h = window->text_input_rect.h;
|
||||
|
||||
SDL_GetWindowPosition(window, &x, &y);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
{
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
Display *x_disp = (Display *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
int x_screen = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, 0);
|
||||
Window x_win = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
Window unused;
|
||||
if (x_disp && x_win) {
|
||||
X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
|
||||
// move to bottom left
|
||||
int w = 0, h = 0;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
cursor->x = 0;
|
||||
cursor->y = h;
|
||||
}
|
||||
|
||||
x += cursor->x;
|
||||
y += cursor->y;
|
||||
|
||||
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
|
||||
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &cursor->w, DBUS_TYPE_INT32, &cursor->h, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
void SDL_Fcitx_PumpEvents(void)
|
||||
{
|
||||
SDL_DBusContext *dbus = fcitx_client.dbus;
|
||||
DBusConnection *conn = dbus->session_conn;
|
||||
|
||||
dbus->connection_read_write(conn, 0);
|
||||
|
||||
while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
|
||||
// Do nothing, actual work happens in DBus_MessageFilter
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_fcitx_h_
|
||||
#define SDL_fcitx_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
extern bool SDL_Fcitx_Init(void);
|
||||
extern void SDL_Fcitx_Quit(void);
|
||||
extern void SDL_Fcitx_SetFocus(bool focused);
|
||||
extern void SDL_Fcitx_Reset(void);
|
||||
extern bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
|
||||
extern void SDL_Fcitx_UpdateTextInputArea(SDL_Window *window);
|
||||
extern void SDL_Fcitx_PumpEvents(void);
|
||||
|
||||
#endif // SDL_fcitx_h_
|
||||
+743
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef HAVE_IBUS_IBUS_H
|
||||
#include "SDL_ibus.h"
|
||||
#include "SDL_dbus.h"
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
|
||||
#include "../../video/SDL_sysvideo.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
#include "../../video/x11/SDL_x11video.h"
|
||||
#endif
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static const char IBUS_PATH[] = "/org/freedesktop/IBus";
|
||||
|
||||
static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
|
||||
static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
|
||||
static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
|
||||
|
||||
static const char IBUS_PORTAL_SERVICE[] = "org.freedesktop.portal.IBus";
|
||||
static const char IBUS_PORTAL_INTERFACE[] = "org.freedesktop.IBus.Portal";
|
||||
static const char IBUS_PORTAL_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
|
||||
|
||||
static const char *ibus_service = NULL;
|
||||
static const char *ibus_interface = NULL;
|
||||
static const char *ibus_input_interface = NULL;
|
||||
static char *input_ctx_path = NULL;
|
||||
static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 };
|
||||
static DBusConnection *ibus_conn = NULL;
|
||||
static bool ibus_is_portal_interface = false;
|
||||
static char *ibus_addr_file = NULL;
|
||||
static int inotify_fd = -1, inotify_wd = -1;
|
||||
|
||||
static Uint32 IBus_ModState(void)
|
||||
{
|
||||
Uint32 ibus_mods = 0;
|
||||
SDL_Keymod sdl_mods = SDL_GetModState();
|
||||
|
||||
// Not sure about MOD3, MOD4 and HYPER mappings
|
||||
if (sdl_mods & SDL_KMOD_LSHIFT) {
|
||||
ibus_mods |= IBUS_SHIFT_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_CAPS) {
|
||||
ibus_mods |= IBUS_LOCK_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_LCTRL) {
|
||||
ibus_mods |= IBUS_CONTROL_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_LALT) {
|
||||
ibus_mods |= IBUS_MOD1_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_NUM) {
|
||||
ibus_mods |= IBUS_MOD2_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_MODE) {
|
||||
ibus_mods |= IBUS_MOD5_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_LGUI) {
|
||||
ibus_mods |= IBUS_SUPER_MASK;
|
||||
}
|
||||
if (sdl_mods & SDL_KMOD_RGUI) {
|
||||
ibus_mods |= IBUS_META_MASK;
|
||||
}
|
||||
|
||||
return ibus_mods;
|
||||
}
|
||||
|
||||
static bool IBus_EnterVariant(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
|
||||
DBusMessageIter *inside, const char *struct_id, size_t id_size)
|
||||
{
|
||||
DBusMessageIter sub;
|
||||
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_recurse(iter, &sub);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_recurse(&sub, inside);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(inside) != DBUS_TYPE_STRING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_get_basic(inside, &struct_id);
|
||||
if (!struct_id || SDL_strncmp(struct_id, struct_id, id_size) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IBus_GetDecorationPosition(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
|
||||
Uint32 *start_pos, Uint32 *end_pos)
|
||||
{
|
||||
DBusMessageIter sub1, sub2, array;
|
||||
|
||||
if (!IBus_EnterVariant(conn, iter, dbus, &sub1, "IBusText", sizeof("IBusText"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_next(&sub1);
|
||||
dbus->message_iter_next(&sub1);
|
||||
dbus->message_iter_next(&sub1);
|
||||
|
||||
if (!IBus_EnterVariant(conn, &sub1, dbus, &sub2, "IBusAttrList", sizeof("IBusAttrList"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_next(&sub2);
|
||||
dbus->message_iter_next(&sub2);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_recurse(&sub2, &array);
|
||||
|
||||
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_VARIANT) {
|
||||
DBusMessageIter sub;
|
||||
if (IBus_EnterVariant(conn, &array, dbus, &sub, "IBusAttribute", sizeof("IBusAttribute"))) {
|
||||
Uint32 type;
|
||||
|
||||
dbus->message_iter_next(&sub);
|
||||
dbus->message_iter_next(&sub);
|
||||
|
||||
// From here on, the structure looks like this:
|
||||
// Uint32 type: 1=underline, 2=foreground, 3=background
|
||||
// Uint32 value: for underline it's 0=NONE, 1=SINGLE, 2=DOUBLE,
|
||||
// 3=LOW, 4=ERROR
|
||||
// for foreground and background it's a color
|
||||
// Uint32 start_index: starting position for the style (utf8-char)
|
||||
// Uint32 end_index: end position for the style (utf8-char)
|
||||
|
||||
dbus->message_iter_get_basic(&sub, &type);
|
||||
// We only use the background type to determine the selection
|
||||
if (type == 3) {
|
||||
Uint32 start = -1;
|
||||
dbus->message_iter_next(&sub);
|
||||
dbus->message_iter_next(&sub);
|
||||
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
|
||||
dbus->message_iter_get_basic(&sub, &start);
|
||||
dbus->message_iter_next(&sub);
|
||||
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
|
||||
dbus->message_iter_get_basic(&sub, end_pos);
|
||||
*start_pos = start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbus->message_iter_next(&array);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus)
|
||||
{
|
||||
// The text we need is nested weirdly, use dbus-monitor to see the structure better
|
||||
const char *text = NULL;
|
||||
DBusMessageIter sub;
|
||||
|
||||
if (!IBus_EnterVariant(conn, iter, dbus, &sub, "IBusText", sizeof("IBusText"))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus->message_iter_next(&sub);
|
||||
dbus->message_iter_next(&sub);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
|
||||
return NULL;
|
||||
}
|
||||
dbus->message_iter_get_basic(&sub, &text);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
static bool IBus_GetVariantCursorPos(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
|
||||
Uint32 *pos)
|
||||
{
|
||||
dbus->message_iter_next(iter);
|
||||
|
||||
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->message_iter_get_basic(iter, pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DBusHandlerResult IBus_MessageHandler(DBusConnection *conn, DBusMessage *msg, void *user_data)
|
||||
{
|
||||
SDL_DBusContext *dbus = (SDL_DBusContext *)user_data;
|
||||
|
||||
if (dbus->message_is_signal(msg, ibus_input_interface, "CommitText")) {
|
||||
DBusMessageIter iter;
|
||||
const char *text;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
text = IBus_GetVariantText(conn, &iter, dbus);
|
||||
|
||||
SDL_SendKeyboardText(text);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
if (dbus->message_is_signal(msg, ibus_input_interface, "UpdatePreeditText")) {
|
||||
DBusMessageIter iter;
|
||||
const char *text;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
text = IBus_GetVariantText(conn, &iter, dbus);
|
||||
|
||||
if (text) {
|
||||
Uint32 pos, start_pos, end_pos;
|
||||
bool has_pos = false;
|
||||
bool has_dec_pos = false;
|
||||
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
has_dec_pos = IBus_GetDecorationPosition(conn, &iter, dbus, &start_pos, &end_pos);
|
||||
if (!has_dec_pos) {
|
||||
dbus->message_iter_init(msg, &iter);
|
||||
has_pos = IBus_GetVariantCursorPos(conn, &iter, dbus, &pos);
|
||||
}
|
||||
|
||||
if (has_dec_pos) {
|
||||
SDL_SendEditingText(text, start_pos, end_pos - start_pos);
|
||||
} else if (has_pos) {
|
||||
SDL_SendEditingText(text, pos, -1);
|
||||
} else {
|
||||
SDL_SendEditingText(text, -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
if (dbus->message_is_signal(msg, ibus_input_interface, "HidePreeditText")) {
|
||||
SDL_SendEditingText("", 0, 0);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static char *IBus_ReadAddressFromFile(const char *file_path)
|
||||
{
|
||||
char addr_buf[1024];
|
||||
bool success = false;
|
||||
FILE *addr_file;
|
||||
|
||||
addr_file = fopen(file_path, "r");
|
||||
if (!addr_file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (fgets(addr_buf, sizeof(addr_buf), addr_file)) {
|
||||
if (SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=") - 1) == 0) {
|
||||
size_t sz = SDL_strlen(addr_buf);
|
||||
if (addr_buf[sz - 1] == '\n') {
|
||||
addr_buf[sz - 1] = 0;
|
||||
}
|
||||
if (addr_buf[sz - 2] == '\r') {
|
||||
addr_buf[sz - 2] = 0;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(void)fclose(addr_file);
|
||||
|
||||
if (success) {
|
||||
return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1));
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char *IBus_GetDBusAddressFilename(void)
|
||||
{
|
||||
SDL_DBusContext *dbus;
|
||||
const char *disp_env;
|
||||
char config_dir[PATH_MAX];
|
||||
char *display = NULL;
|
||||
const char *addr;
|
||||
const char *conf_env;
|
||||
char *key;
|
||||
char file_path[PATH_MAX];
|
||||
const char *host;
|
||||
char *disp_num, *screen_num;
|
||||
|
||||
if (ibus_addr_file) {
|
||||
return SDL_strdup(ibus_addr_file);
|
||||
}
|
||||
|
||||
dbus = SDL_DBus_GetContext();
|
||||
if (!dbus) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Use this environment variable if it exists.
|
||||
addr = SDL_getenv("IBUS_ADDRESS");
|
||||
if (addr && *addr) {
|
||||
return SDL_strdup(addr);
|
||||
}
|
||||
|
||||
/* Otherwise, we have to get the hostname, display, machine id, config dir
|
||||
and look up the address from a filepath using all those bits, eek. */
|
||||
disp_env = SDL_getenv("DISPLAY");
|
||||
|
||||
if (!disp_env || !*disp_env) {
|
||||
display = SDL_strdup(":0.0");
|
||||
} else {
|
||||
display = SDL_strdup(disp_env);
|
||||
}
|
||||
|
||||
host = display;
|
||||
disp_num = SDL_strrchr(display, ':');
|
||||
screen_num = SDL_strrchr(display, '.');
|
||||
|
||||
if (!disp_num) {
|
||||
SDL_free(display);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*disp_num = 0;
|
||||
disp_num++;
|
||||
|
||||
if (screen_num) {
|
||||
*screen_num = 0;
|
||||
}
|
||||
|
||||
if (!*host) {
|
||||
const char *session = SDL_getenv("XDG_SESSION_TYPE");
|
||||
if (session && SDL_strcmp(session, "wayland") == 0) {
|
||||
host = "unix-wayland";
|
||||
} else {
|
||||
host = "unix";
|
||||
}
|
||||
}
|
||||
|
||||
SDL_zeroa(config_dir);
|
||||
|
||||
conf_env = SDL_getenv("XDG_CONFIG_HOME");
|
||||
if (conf_env && *conf_env) {
|
||||
SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
|
||||
} else {
|
||||
const char *home_env = SDL_getenv("HOME");
|
||||
if (!home_env || !*home_env) {
|
||||
SDL_free(display);
|
||||
return NULL;
|
||||
}
|
||||
(void)SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
|
||||
}
|
||||
|
||||
key = SDL_DBus_GetLocalMachineId();
|
||||
|
||||
if (!key) {
|
||||
SDL_free(display);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_zeroa(file_path);
|
||||
(void)SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s",
|
||||
config_dir, key, host, disp_num);
|
||||
dbus->free(key);
|
||||
SDL_free(display);
|
||||
|
||||
return SDL_strdup(file_path);
|
||||
}
|
||||
|
||||
static bool IBus_CheckConnection(SDL_DBusContext *dbus);
|
||||
|
||||
static void SDLCALL IBus_SetCapabilities(void *data, const char *name, const char *old_val,
|
||||
const char *hint)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (IBus_CheckConnection(dbus)) {
|
||||
Uint32 caps = IBUS_CAP_FOCUS;
|
||||
|
||||
if (hint && SDL_strstr(hint, "composition")) {
|
||||
caps |= IBUS_CAP_PREEDIT_TEXT;
|
||||
}
|
||||
if (hint && SDL_strstr(hint, "candidates")) {
|
||||
// FIXME, turn off native candidate rendering
|
||||
}
|
||||
|
||||
SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCapabilities",
|
||||
DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IBus_SetupConnection(SDL_DBusContext *dbus, const char *addr)
|
||||
{
|
||||
const char *client_name = "SDL3_Application";
|
||||
DBusMessage *reply = NULL;
|
||||
const char *path = NULL;
|
||||
bool result = false;
|
||||
DBusObjectPathVTable ibus_vtable;
|
||||
|
||||
SDL_zero(ibus_vtable);
|
||||
ibus_vtable.message_function = &IBus_MessageHandler;
|
||||
|
||||
/* try the portal interface first. Modern systems have this in general,
|
||||
and sandbox things like FlakPak and Snaps, etc, require it. */
|
||||
|
||||
ibus_is_portal_interface = true;
|
||||
ibus_service = IBUS_PORTAL_SERVICE;
|
||||
ibus_interface = IBUS_PORTAL_INTERFACE;
|
||||
ibus_input_interface = IBUS_PORTAL_INPUT_INTERFACE;
|
||||
ibus_conn = dbus->session_conn;
|
||||
|
||||
result = SDL_DBus_CallMethodOnConnection(ibus_conn, &reply, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
|
||||
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
|
||||
if (!result) {
|
||||
ibus_is_portal_interface = false;
|
||||
ibus_service = IBUS_SERVICE;
|
||||
ibus_interface = IBUS_INTERFACE;
|
||||
ibus_input_interface = IBUS_INPUT_INTERFACE;
|
||||
ibus_conn = dbus->connection_open_private(addr, NULL);
|
||||
|
||||
if (!ibus_conn) {
|
||||
return false; // oh well.
|
||||
}
|
||||
|
||||
dbus->connection_flush(ibus_conn);
|
||||
|
||||
if (!dbus->bus_register(ibus_conn, NULL)) {
|
||||
ibus_conn = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus->connection_flush(ibus_conn);
|
||||
|
||||
result = SDL_DBus_CallMethodOnConnection(ibus_conn, &reply, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
|
||||
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
|
||||
} else {
|
||||
// reusing dbus->session_conn
|
||||
dbus->connection_ref(ibus_conn);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
char matchstr[128];
|
||||
(void)SDL_snprintf(matchstr, sizeof(matchstr), "type='signal',interface='%s'", ibus_input_interface);
|
||||
SDL_free(input_ctx_path);
|
||||
input_ctx_path = SDL_strdup(path);
|
||||
SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
|
||||
dbus->bus_add_match(ibus_conn, matchstr, NULL);
|
||||
dbus->connection_try_register_object_path(ibus_conn, input_ctx_path, &ibus_vtable, dbus, NULL);
|
||||
dbus->connection_flush(ibus_conn);
|
||||
}
|
||||
SDL_DBus_FreeReply(&reply);
|
||||
|
||||
SDL_Window *window = SDL_GetKeyboardFocus();
|
||||
if (SDL_TextInputActive(window)) {
|
||||
SDL_IBus_SetFocus(true);
|
||||
SDL_IBus_UpdateTextInputArea(window);
|
||||
} else {
|
||||
SDL_IBus_SetFocus(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool IBus_CheckConnection(SDL_DBusContext *dbus)
|
||||
{
|
||||
if (!dbus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (inotify_fd > 0 && inotify_wd > 0) {
|
||||
char buf[1024];
|
||||
ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
|
||||
if (readsize > 0) {
|
||||
|
||||
char *p;
|
||||
bool file_updated = false;
|
||||
|
||||
for (p = buf; p < buf + readsize; /**/) {
|
||||
struct inotify_event *event = (struct inotify_event *)p;
|
||||
if (event->len > 0) {
|
||||
char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
|
||||
if (!addr_file_no_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) {
|
||||
file_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p += sizeof(struct inotify_event) + event->len;
|
||||
}
|
||||
|
||||
if (file_updated) {
|
||||
char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
|
||||
if (addr) {
|
||||
bool result = IBus_SetupConnection(dbus, addr);
|
||||
SDL_free(addr);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL_IBus_Init(void)
|
||||
{
|
||||
bool result = false;
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (dbus) {
|
||||
char *addr_file = IBus_GetDBusAddressFilename();
|
||||
char *addr;
|
||||
char *addr_file_dir;
|
||||
|
||||
if (!addr_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addr = IBus_ReadAddressFromFile(addr_file);
|
||||
if (!addr) {
|
||||
SDL_free(addr_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_free(ibus_addr_file);
|
||||
ibus_addr_file = SDL_strdup(addr_file);
|
||||
|
||||
if (inotify_fd < 0) {
|
||||
inotify_fd = inotify_init();
|
||||
fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
addr_file_dir = SDL_strrchr(addr_file, '/');
|
||||
if (addr_file_dir) {
|
||||
*addr_file_dir = 0;
|
||||
}
|
||||
|
||||
inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
|
||||
SDL_free(addr_file);
|
||||
|
||||
result = IBus_SetupConnection(dbus, addr);
|
||||
SDL_free(addr);
|
||||
|
||||
// don't use the addr_file if using the portal interface.
|
||||
if (result && ibus_is_portal_interface) {
|
||||
if (inotify_fd > 0) {
|
||||
if (inotify_wd > 0) {
|
||||
inotify_rm_watch(inotify_fd, inotify_wd);
|
||||
inotify_wd = -1;
|
||||
}
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDL_IBus_Quit(void)
|
||||
{
|
||||
SDL_DBusContext *dbus;
|
||||
|
||||
if (input_ctx_path) {
|
||||
SDL_free(input_ctx_path);
|
||||
input_ctx_path = NULL;
|
||||
}
|
||||
|
||||
if (ibus_addr_file) {
|
||||
SDL_free(ibus_addr_file);
|
||||
ibus_addr_file = NULL;
|
||||
}
|
||||
|
||||
dbus = SDL_DBus_GetContext();
|
||||
|
||||
// if using portal, ibus_conn == session_conn; don't release it here.
|
||||
if (dbus && ibus_conn && !ibus_is_portal_interface) {
|
||||
dbus->connection_close(ibus_conn);
|
||||
dbus->connection_unref(ibus_conn);
|
||||
}
|
||||
|
||||
ibus_conn = NULL;
|
||||
ibus_service = NULL;
|
||||
ibus_interface = NULL;
|
||||
ibus_input_interface = NULL;
|
||||
ibus_is_portal_interface = false;
|
||||
|
||||
if (inotify_fd > 0 && inotify_wd > 0) {
|
||||
inotify_rm_watch(inotify_fd, inotify_wd);
|
||||
inotify_wd = -1;
|
||||
}
|
||||
|
||||
// !!! FIXME: should we close(inotify_fd) here?
|
||||
|
||||
SDL_RemoveHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
|
||||
|
||||
SDL_zero(ibus_cursor_rect);
|
||||
}
|
||||
|
||||
static void IBus_SimpleMessage(const char *method)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if ((input_ctx_path) && (IBus_CheckConnection(dbus))) {
|
||||
SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, method, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IBus_SetFocus(bool focused)
|
||||
{
|
||||
const char *method = focused ? "FocusIn" : "FocusOut";
|
||||
IBus_SimpleMessage(method);
|
||||
}
|
||||
|
||||
void SDL_IBus_Reset(void)
|
||||
{
|
||||
IBus_SimpleMessage("Reset");
|
||||
}
|
||||
|
||||
bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
|
||||
{
|
||||
Uint32 result = 0;
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (IBus_CheckConnection(dbus)) {
|
||||
Uint32 mods = IBus_ModState();
|
||||
Uint32 ibus_keycode = keycode - 8;
|
||||
if (!down) {
|
||||
mods |= (1 << 30); // IBUS_RELEASE_MASK
|
||||
}
|
||||
if (!SDL_DBus_CallMethodOnConnection(ibus_conn, NULL, ibus_service, input_ctx_path, ibus_input_interface, "ProcessKeyEvent",
|
||||
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &ibus_keycode, DBUS_TYPE_UINT32, &mods, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_BOOLEAN, &result, DBUS_TYPE_INVALID)) {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
|
||||
|
||||
return (result != 0);
|
||||
}
|
||||
|
||||
void SDL_IBus_UpdateTextInputArea(SDL_Window *window)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
SDL_DBusContext *dbus;
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll use a square at the text input cursor location for the ibus_cursor
|
||||
ibus_cursor_rect.x = window->text_input_rect.x + window->text_input_cursor;
|
||||
ibus_cursor_rect.y = window->text_input_rect.y;
|
||||
ibus_cursor_rect.w = window->text_input_rect.h;
|
||||
ibus_cursor_rect.h = window->text_input_rect.h;
|
||||
|
||||
SDL_GetWindowPosition(window, &x, &y);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
{
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
Display *x_disp = (Display *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
int x_screen = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, 0);
|
||||
Window x_win = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
Window unused;
|
||||
|
||||
if (x_disp && x_win) {
|
||||
X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
x += ibus_cursor_rect.x;
|
||||
y += ibus_cursor_rect.y;
|
||||
|
||||
dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (IBus_CheckConnection(dbus)) {
|
||||
SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCursorLocation",
|
||||
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &ibus_cursor_rect.w, DBUS_TYPE_INT32, &ibus_cursor_rect.h, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IBus_PumpEvents(void)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (IBus_CheckConnection(dbus)) {
|
||||
dbus->connection_read_write(ibus_conn, 0);
|
||||
|
||||
while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) {
|
||||
// Do nothing, actual work happens in IBus_MessageHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_USE_LIBDBUS
|
||||
|
||||
#endif
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ibus_h_
|
||||
#define SDL_ibus_h_
|
||||
|
||||
#ifdef HAVE_IBUS_IBUS_H
|
||||
#define SDL_USE_IBUS 1
|
||||
#include <ibus.h>
|
||||
|
||||
extern bool SDL_IBus_Init(void);
|
||||
extern void SDL_IBus_Quit(void);
|
||||
|
||||
// Lets the IBus server know about changes in window focus
|
||||
extern void SDL_IBus_SetFocus(bool focused);
|
||||
|
||||
// Closes the candidate list and resets any text currently being edited
|
||||
extern void SDL_IBus_Reset(void);
|
||||
|
||||
/* Sends a keypress event to IBus, returns true if IBus used this event to
|
||||
update its candidate list or change input methods. PumpEvents should be
|
||||
called some time after this, to receive the TextInput / TextEditing event back. */
|
||||
extern bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
|
||||
|
||||
/* Update the position of IBus' candidate list. If rect is NULL then this will
|
||||
just reposition it relative to the focused window's new position. */
|
||||
extern void SDL_IBus_UpdateTextInputArea(SDL_Window *window);
|
||||
|
||||
/* Checks DBus for new IBus events, and calls SDL_SendKeyboardText /
|
||||
SDL_SendEditingText for each event it finds */
|
||||
extern void SDL_IBus_PumpEvents(void);
|
||||
|
||||
#endif // HAVE_IBUS_IBUS_H
|
||||
|
||||
#endif // SDL_ibus_h_
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_ime.h"
|
||||
#include "SDL_ibus.h"
|
||||
#include "SDL_fcitx.h"
|
||||
|
||||
typedef bool (*SDL_IME_Init_t)(void);
|
||||
typedef void (*SDL_IME_Quit_t)(void);
|
||||
typedef void (*SDL_IME_SetFocus_t)(bool);
|
||||
typedef void (*SDL_IME_Reset_t)(void);
|
||||
typedef bool (*SDL_IME_ProcessKeyEvent_t)(Uint32, Uint32, bool down);
|
||||
typedef void (*SDL_IME_UpdateTextInputArea_t)(SDL_Window *window);
|
||||
typedef void (*SDL_IME_PumpEvents_t)(void);
|
||||
|
||||
static SDL_IME_Init_t SDL_IME_Init_Real = NULL;
|
||||
static SDL_IME_Quit_t SDL_IME_Quit_Real = NULL;
|
||||
static SDL_IME_SetFocus_t SDL_IME_SetFocus_Real = NULL;
|
||||
static SDL_IME_Reset_t SDL_IME_Reset_Real = NULL;
|
||||
static SDL_IME_ProcessKeyEvent_t SDL_IME_ProcessKeyEvent_Real = NULL;
|
||||
static SDL_IME_UpdateTextInputArea_t SDL_IME_UpdateTextInputArea_Real = NULL;
|
||||
static SDL_IME_PumpEvents_t SDL_IME_PumpEvents_Real = NULL;
|
||||
|
||||
static void InitIME(void)
|
||||
{
|
||||
static bool inited = false;
|
||||
#ifdef HAVE_FCITX
|
||||
const char *im_module = SDL_getenv("SDL_IM_MODULE");
|
||||
const char *xmodifiers = SDL_getenv("XMODIFIERS");
|
||||
#endif
|
||||
|
||||
if (inited == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
inited = true;
|
||||
|
||||
// See if fcitx IME support is being requested
|
||||
#ifdef HAVE_FCITX
|
||||
if (!SDL_IME_Init_Real &&
|
||||
((im_module && SDL_strcmp(im_module, "fcitx") == 0) ||
|
||||
(!im_module && xmodifiers && SDL_strstr(xmodifiers, "@im=fcitx") != NULL))) {
|
||||
SDL_IME_Init_Real = SDL_Fcitx_Init;
|
||||
SDL_IME_Quit_Real = SDL_Fcitx_Quit;
|
||||
SDL_IME_SetFocus_Real = SDL_Fcitx_SetFocus;
|
||||
SDL_IME_Reset_Real = SDL_Fcitx_Reset;
|
||||
SDL_IME_ProcessKeyEvent_Real = SDL_Fcitx_ProcessKeyEvent;
|
||||
SDL_IME_UpdateTextInputArea_Real = SDL_Fcitx_UpdateTextInputArea;
|
||||
SDL_IME_PumpEvents_Real = SDL_Fcitx_PumpEvents;
|
||||
}
|
||||
#endif // HAVE_FCITX
|
||||
|
||||
// default to IBus
|
||||
#ifdef HAVE_IBUS_IBUS_H
|
||||
if (!SDL_IME_Init_Real) {
|
||||
SDL_IME_Init_Real = SDL_IBus_Init;
|
||||
SDL_IME_Quit_Real = SDL_IBus_Quit;
|
||||
SDL_IME_SetFocus_Real = SDL_IBus_SetFocus;
|
||||
SDL_IME_Reset_Real = SDL_IBus_Reset;
|
||||
SDL_IME_ProcessKeyEvent_Real = SDL_IBus_ProcessKeyEvent;
|
||||
SDL_IME_UpdateTextInputArea_Real = SDL_IBus_UpdateTextInputArea;
|
||||
SDL_IME_PumpEvents_Real = SDL_IBus_PumpEvents;
|
||||
}
|
||||
#endif // HAVE_IBUS_IBUS_H
|
||||
}
|
||||
|
||||
bool SDL_IME_Init(void)
|
||||
{
|
||||
InitIME();
|
||||
|
||||
if (SDL_IME_Init_Real) {
|
||||
if (SDL_IME_Init_Real()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// uhoh, the IME implementation's init failed! Disable IME support.
|
||||
SDL_IME_Init_Real = NULL;
|
||||
SDL_IME_Quit_Real = NULL;
|
||||
SDL_IME_SetFocus_Real = NULL;
|
||||
SDL_IME_Reset_Real = NULL;
|
||||
SDL_IME_ProcessKeyEvent_Real = NULL;
|
||||
SDL_IME_UpdateTextInputArea_Real = NULL;
|
||||
SDL_IME_PumpEvents_Real = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDL_IME_Quit(void)
|
||||
{
|
||||
if (SDL_IME_Quit_Real) {
|
||||
SDL_IME_Quit_Real();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IME_SetFocus(bool focused)
|
||||
{
|
||||
if (SDL_IME_SetFocus_Real) {
|
||||
SDL_IME_SetFocus_Real(focused);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IME_Reset(void)
|
||||
{
|
||||
if (SDL_IME_Reset_Real) {
|
||||
SDL_IME_Reset_Real();
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
|
||||
{
|
||||
if (SDL_IME_ProcessKeyEvent_Real) {
|
||||
return SDL_IME_ProcessKeyEvent_Real(keysym, keycode, down);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDL_IME_UpdateTextInputArea(SDL_Window *window)
|
||||
{
|
||||
if (SDL_IME_UpdateTextInputArea_Real) {
|
||||
SDL_IME_UpdateTextInputArea_Real(window);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IME_PumpEvents(void)
|
||||
{
|
||||
if (SDL_IME_PumpEvents_Real) {
|
||||
SDL_IME_PumpEvents_Real();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_ime_h_
|
||||
#define SDL_ime_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
extern bool SDL_IME_Init(void);
|
||||
extern void SDL_IME_Quit(void);
|
||||
extern void SDL_IME_SetFocus(bool focused);
|
||||
extern void SDL_IME_Reset(void);
|
||||
extern bool SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
|
||||
extern void SDL_IME_UpdateTextInputArea(SDL_Window *window);
|
||||
extern void SDL_IME_PumpEvents(void);
|
||||
|
||||
#endif // SDL_ime_h_
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_progressbar.h"
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_dbus.h"
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../unix/SDL_appid.h"
|
||||
|
||||
#define UnityLauncherAPI_DBUS_INTERFACE "com.canonical.Unity.LauncherEntry"
|
||||
#define UnityLauncherAPI_DBUS_SIGNAL "Update"
|
||||
|
||||
static char *GetDBUSObjectPath(void)
|
||||
{
|
||||
char *app_id = SDL_strdup(SDL_GetAppID());
|
||||
|
||||
if (!app_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Sanitize exe_name to make it a legal D-Bus path element
|
||||
for (char *p = app_id; *p; ++p) {
|
||||
if (!SDL_isalnum(*p)) {
|
||||
*p = '_';
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure it starts with a letter or underscore
|
||||
if (!SDL_isalpha(app_id[0]) && app_id[0] != '_') {
|
||||
app_id = SDL_realloc(app_id, SDL_strlen(app_id) + 2);
|
||||
if (!app_id) {
|
||||
return NULL;
|
||||
}
|
||||
SDL_memmove(app_id + 1, app_id, SDL_strlen(app_id) + 1);
|
||||
app_id[0] = '_';
|
||||
}
|
||||
|
||||
// Create full path
|
||||
char *path;
|
||||
if (SDL_asprintf(&path, "/org/libsdl/%s_%d", app_id, getpid()) < 0) {
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
SDL_free(app_id);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *GetAppDesktopPath(void)
|
||||
{
|
||||
const char *desktop_suffix = ".desktop";
|
||||
const char *app_id = SDL_GetAppID();
|
||||
const size_t desktop_path_total_length = SDL_strlen(app_id) + SDL_strlen(desktop_suffix) + 1;
|
||||
char *desktop_path = (char *)SDL_malloc(desktop_path_total_length);
|
||||
if (!desktop_path) {
|
||||
return NULL;
|
||||
}
|
||||
*desktop_path = '\0';
|
||||
SDL_strlcat(desktop_path, app_id, desktop_path_total_length);
|
||||
SDL_strlcat(desktop_path, desktop_suffix, desktop_path_total_length);
|
||||
|
||||
return desktop_path;
|
||||
}
|
||||
|
||||
static int ShouldShowProgress(SDL_ProgressState progressState)
|
||||
{
|
||||
if (progressState == SDL_PROGRESS_STATE_INVALID ||
|
||||
progressState == SDL_PROGRESS_STATE_NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unity LauncherAPI only supports "normal" display of progress
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool DBUS_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
// Signal signature:
|
||||
// signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties)
|
||||
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (!dbus || !dbus->session_conn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *objectPath = GetDBUSObjectPath();
|
||||
if (!objectPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessage *msg = dbus->message_new_signal(objectPath, UnityLauncherAPI_DBUS_INTERFACE, UnityLauncherAPI_DBUS_SIGNAL);
|
||||
if (!msg) {
|
||||
SDL_free(objectPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *desktop_path = GetAppDesktopPath();
|
||||
if (!desktop_path) {
|
||||
dbus->message_unref(msg);
|
||||
SDL_free(objectPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *progress_visible_str = "progress-visible";
|
||||
const char *progress_str = "progress";
|
||||
|
||||
const int progress_visible = ShouldShowProgress(window->progress_state);
|
||||
double progress = (double)window->progress_value;
|
||||
|
||||
DBusMessageIter args, props;
|
||||
dbus->message_iter_init_append(msg, &args);
|
||||
dbus->message_iter_append_basic(&args, DBUS_TYPE_STRING, &desktop_path); // Setup app_uri parameter
|
||||
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &props); // Setup properties parameter
|
||||
DBusMessageIter key_it, value_it;
|
||||
// Set progress visible property
|
||||
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
|
||||
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_visible_str); // Append progress-visible key data
|
||||
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, "b", &value_it);
|
||||
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_BOOLEAN, &progress_visible); // Append progress-visible value data
|
||||
dbus->message_iter_close_container(&key_it, &value_it);
|
||||
dbus->message_iter_close_container(&props, &key_it);
|
||||
// Set progress value property
|
||||
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
|
||||
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_str); // Append progress key data
|
||||
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, "d", &value_it);
|
||||
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_DOUBLE, &progress); // Append progress value data
|
||||
dbus->message_iter_close_container(&key_it, &value_it);
|
||||
dbus->message_iter_close_container(&props, &key_it);
|
||||
dbus->message_iter_close_container(&args, &props);
|
||||
|
||||
dbus->connection_send(dbus->session_conn, msg, NULL);
|
||||
|
||||
SDL_free(desktop_path);
|
||||
dbus->message_unref(msg);
|
||||
SDL_free(objectPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SDL_USE_LIBDBUS
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_prograssbar_h_
|
||||
#define SDL_prograssbar_h_
|
||||
|
||||
#include "../../video/SDL_sysvideo.h"
|
||||
#include "SDL_internal.h"
|
||||
|
||||
extern bool DBUS_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
#endif // SDL_prograssbar_h_
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_dbus.h"
|
||||
#include "SDL_system_theme.h"
|
||||
#include "../../video/SDL_sysvideo.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
|
||||
#define PORTAL_PATH "/org/freedesktop/portal/desktop"
|
||||
#define PORTAL_INTERFACE "org.freedesktop.portal.Settings"
|
||||
#define PORTAL_METHOD "Read"
|
||||
|
||||
#define SIGNAL_INTERFACE "org.freedesktop.portal.Settings"
|
||||
#define SIGNAL_NAMESPACE "org.freedesktop.appearance"
|
||||
#define SIGNAL_NAME "SettingChanged"
|
||||
#define SIGNAL_KEY "color-scheme"
|
||||
|
||||
typedef struct SystemThemeData
|
||||
{
|
||||
SDL_DBusContext *dbus;
|
||||
SDL_SystemTheme theme;
|
||||
} SystemThemeData;
|
||||
|
||||
static SystemThemeData system_theme_data;
|
||||
|
||||
static bool DBus_ExtractThemeVariant(DBusMessageIter *iter, SDL_SystemTheme *theme) {
|
||||
SDL_DBusContext *dbus = system_theme_data.dbus;
|
||||
Uint32 color_scheme;
|
||||
DBusMessageIter variant_iter;
|
||||
|
||||
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
|
||||
return false;
|
||||
dbus->message_iter_recurse(iter, &variant_iter);
|
||||
if (dbus->message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32)
|
||||
return false;
|
||||
dbus->message_iter_get_basic(&variant_iter, &color_scheme);
|
||||
switch (color_scheme) {
|
||||
case 0:
|
||||
*theme = SDL_SYSTEM_THEME_UNKNOWN;
|
||||
break;
|
||||
case 1:
|
||||
*theme = SDL_SYSTEM_THEME_DARK;
|
||||
break;
|
||||
case 2:
|
||||
*theme = SDL_SYSTEM_THEME_LIGHT;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data) {
|
||||
SDL_DBusContext *dbus = (SDL_DBusContext *)data;
|
||||
|
||||
if (dbus->message_is_signal(msg, SIGNAL_INTERFACE, SIGNAL_NAME)) {
|
||||
DBusMessageIter signal_iter;
|
||||
const char *namespace, *key;
|
||||
|
||||
dbus->message_iter_init(msg, &signal_iter);
|
||||
// Check if the parameters are what we expect
|
||||
if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
|
||||
goto not_our_signal;
|
||||
dbus->message_iter_get_basic(&signal_iter, &namespace);
|
||||
if (SDL_strcmp(SIGNAL_NAMESPACE, namespace) != 0)
|
||||
goto not_our_signal;
|
||||
|
||||
if (!dbus->message_iter_next(&signal_iter))
|
||||
goto not_our_signal;
|
||||
|
||||
if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
|
||||
goto not_our_signal;
|
||||
dbus->message_iter_get_basic(&signal_iter, &key);
|
||||
if (SDL_strcmp(SIGNAL_KEY, key) != 0)
|
||||
goto not_our_signal;
|
||||
|
||||
if (!dbus->message_iter_next(&signal_iter))
|
||||
goto not_our_signal;
|
||||
|
||||
if (!DBus_ExtractThemeVariant(&signal_iter, &system_theme_data.theme))
|
||||
goto not_our_signal;
|
||||
|
||||
SDL_SetSystemTheme(system_theme_data.theme);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
not_our_signal:
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
bool SDL_SystemTheme_Init(void)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
DBusMessage *msg;
|
||||
static const char *namespace = SIGNAL_NAMESPACE;
|
||||
static const char *key = SIGNAL_KEY;
|
||||
|
||||
system_theme_data.theme = SDL_SYSTEM_THEME_UNKNOWN;
|
||||
system_theme_data.dbus = dbus;
|
||||
if (!dbus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = dbus->message_new_method_call(PORTAL_DESTINATION, PORTAL_PATH, PORTAL_INTERFACE, PORTAL_METHOD);
|
||||
if (msg) {
|
||||
if (dbus->message_append_args(msg, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
|
||||
DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
|
||||
if (reply) {
|
||||
DBusMessageIter reply_iter, variant_outer_iter;
|
||||
|
||||
dbus->message_iter_init(reply, &reply_iter);
|
||||
// The response has signature <<u>>
|
||||
if (dbus->message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_VARIANT)
|
||||
goto incorrect_type;
|
||||
dbus->message_iter_recurse(&reply_iter, &variant_outer_iter);
|
||||
if (!DBus_ExtractThemeVariant(&variant_outer_iter, &system_theme_data.theme))
|
||||
goto incorrect_type;
|
||||
incorrect_type:
|
||||
dbus->message_unref(reply);
|
||||
}
|
||||
}
|
||||
dbus->message_unref(msg);
|
||||
}
|
||||
|
||||
dbus->bus_add_match(dbus->session_conn,
|
||||
"type='signal', interface='"SIGNAL_INTERFACE"',"
|
||||
"member='"SIGNAL_NAME"', arg0='"SIGNAL_NAMESPACE"',"
|
||||
"arg1='"SIGNAL_KEY"'", NULL);
|
||||
dbus->connection_add_filter(dbus->session_conn,
|
||||
&DBus_MessageFilter, dbus, NULL);
|
||||
dbus->connection_flush(dbus->session_conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_SystemTheme SDL_SystemTheme_Get(void)
|
||||
{
|
||||
return system_theme_data.theme;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_system_theme_h_
|
||||
#define SDL_system_theme_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
extern bool SDL_SystemTheme_Init(void);
|
||||
extern SDL_SystemTheme SDL_SystemTheme_Get(void);
|
||||
|
||||
#endif // SDL_system_theme_h_
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PLATFORM_LINUX
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// RLIMIT_RTTIME requires kernel >= 2.6.25 and is in glibc >= 2.14
|
||||
#ifndef RLIMIT_RTTIME
|
||||
#define RLIMIT_RTTIME 15
|
||||
#endif
|
||||
// SCHED_RESET_ON_FORK is in kernel >= 2.6.32.
|
||||
#ifndef SCHED_RESET_ON_FORK
|
||||
#define SCHED_RESET_ON_FORK 0x40000000
|
||||
#endif
|
||||
|
||||
#include "SDL_dbus.h"
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
|
||||
// d-bus queries to org.freedesktop.RealtimeKit1.
|
||||
#define RTKIT_DBUS_NODE "org.freedesktop.RealtimeKit1"
|
||||
#define RTKIT_DBUS_PATH "/org/freedesktop/RealtimeKit1"
|
||||
#define RTKIT_DBUS_INTERFACE "org.freedesktop.RealtimeKit1"
|
||||
|
||||
// d-bus queries to the XDG portal interface to RealtimeKit1
|
||||
#define XDG_PORTAL_DBUS_NODE "org.freedesktop.portal.Desktop"
|
||||
#define XDG_PORTAL_DBUS_PATH "/org/freedesktop/portal/desktop"
|
||||
#define XDG_PORTAL_DBUS_INTERFACE "org.freedesktop.portal.Realtime"
|
||||
|
||||
static bool rtkit_use_session_conn;
|
||||
static const char *rtkit_dbus_node;
|
||||
static const char *rtkit_dbus_path;
|
||||
static const char *rtkit_dbus_interface;
|
||||
|
||||
static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT;
|
||||
static Sint32 rtkit_min_nice_level = -20;
|
||||
static Sint32 rtkit_max_realtime_priority = 99;
|
||||
static Sint64 rtkit_max_rttime_usec = 200000;
|
||||
|
||||
/*
|
||||
* Checking that the RTTimeUSecMax property exists and is an int64 confirms that:
|
||||
* - The desktop portal exists and supports the realtime interface.
|
||||
* - The realtime interface is new enough to have the required bug fixes applied.
|
||||
*/
|
||||
static bool realtime_portal_supported(DBusConnection *conn)
|
||||
{
|
||||
Sint64 res;
|
||||
return SDL_DBus_QueryPropertyOnConnection(conn, NULL, XDG_PORTAL_DBUS_NODE, XDG_PORTAL_DBUS_PATH, XDG_PORTAL_DBUS_INTERFACE,
|
||||
"RTTimeUSecMax", DBUS_TYPE_INT64, &res);
|
||||
}
|
||||
|
||||
static void set_rtkit_interface(void)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
// xdg-desktop-portal works in all instances, so check for it first.
|
||||
if (dbus && realtime_portal_supported(dbus->session_conn)) {
|
||||
rtkit_use_session_conn = true;
|
||||
rtkit_dbus_node = XDG_PORTAL_DBUS_NODE;
|
||||
rtkit_dbus_path = XDG_PORTAL_DBUS_PATH;
|
||||
rtkit_dbus_interface = XDG_PORTAL_DBUS_INTERFACE;
|
||||
} else { // Fall back to the standard rtkit interface in all other cases.
|
||||
rtkit_use_session_conn = false;
|
||||
rtkit_dbus_node = RTKIT_DBUS_NODE;
|
||||
rtkit_dbus_path = RTKIT_DBUS_PATH;
|
||||
rtkit_dbus_interface = RTKIT_DBUS_INTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
static DBusConnection *get_rtkit_dbus_connection(void)
|
||||
{
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
|
||||
if (dbus) {
|
||||
return rtkit_use_session_conn ? dbus->session_conn : dbus->system_conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rtkit_initialize(void)
|
||||
{
|
||||
DBusConnection *dbus_conn;
|
||||
|
||||
set_rtkit_interface();
|
||||
dbus_conn = get_rtkit_dbus_connection();
|
||||
|
||||
// Try getting minimum nice level: this is often greater than PRIO_MIN (-20).
|
||||
if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, NULL, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MinNiceLevel",
|
||||
DBUS_TYPE_INT32, &rtkit_min_nice_level)) {
|
||||
rtkit_min_nice_level = -20;
|
||||
}
|
||||
|
||||
// Try getting maximum realtime priority: this can be less than the POSIX default (99).
|
||||
if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, NULL, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MaxRealtimePriority",
|
||||
DBUS_TYPE_INT32, &rtkit_max_realtime_priority)) {
|
||||
rtkit_max_realtime_priority = 99;
|
||||
}
|
||||
|
||||
// Try getting maximum rttime allowed by rtkit: exceeding this value will result in SIGKILL
|
||||
if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, NULL, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "RTTimeUSecMax",
|
||||
DBUS_TYPE_INT64, &rtkit_max_rttime_usec)) {
|
||||
rtkit_max_rttime_usec = 200000;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rtkit_initialize_realtime_thread(void)
|
||||
{
|
||||
// Following is an excerpt from rtkit README that outlines the requirements
|
||||
// a thread must meet before making rtkit requests:
|
||||
//
|
||||
// * Only clients with RLIMIT_RTTIME set will get RT scheduling
|
||||
//
|
||||
// * RT scheduling will only be handed out to processes with
|
||||
// SCHED_RESET_ON_FORK set to guarantee that the scheduling
|
||||
// settings cannot 'leak' to child processes, thus making sure
|
||||
// that 'RT fork bombs' cannot be used to bypass RLIMIT_RTTIME
|
||||
// and take the system down.
|
||||
//
|
||||
// * Limits are enforced on all user controllable resources, only
|
||||
// a maximum number of users, processes, threads can request RT
|
||||
// scheduling at the same time.
|
||||
//
|
||||
// * Only a limited number of threads may be made RT in a
|
||||
// specific time frame.
|
||||
//
|
||||
// * Client authorization is verified with PolicyKit
|
||||
|
||||
int err;
|
||||
struct rlimit rlimit;
|
||||
int nLimit = RLIMIT_RTTIME;
|
||||
pid_t nPid = 0; // self
|
||||
int nSchedPolicy = sched_getscheduler(nPid) | SCHED_RESET_ON_FORK;
|
||||
struct sched_param schedParam;
|
||||
|
||||
SDL_zero(schedParam);
|
||||
|
||||
// Requirement #1: Set RLIMIT_RTTIME
|
||||
err = getrlimit(nLimit, &rlimit);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Current rtkit allows a max of 200ms right now
|
||||
rlimit.rlim_max = rtkit_max_rttime_usec;
|
||||
rlimit.rlim_cur = rlimit.rlim_max / 2;
|
||||
err = setrlimit(nLimit, &rlimit);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requirement #2: Add SCHED_RESET_ON_FORK to the scheduler policy
|
||||
err = sched_getparam(nPid, &schedParam);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
err = sched_setscheduler(nPid, nSchedPolicy, &schedParam);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rtkit_setpriority_nice(pid_t thread, int nice_level)
|
||||
{
|
||||
DBusConnection *dbus_conn;
|
||||
Uint64 pid = (Uint64)getpid();
|
||||
Uint64 tid = (Uint64)thread;
|
||||
Sint32 nice = (Sint32)nice_level;
|
||||
|
||||
pthread_once(&rtkit_initialize_once, rtkit_initialize);
|
||||
dbus_conn = get_rtkit_dbus_connection();
|
||||
|
||||
if (nice < rtkit_min_nice_level) {
|
||||
nice = rtkit_min_nice_level;
|
||||
}
|
||||
|
||||
if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn, NULL,
|
||||
rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadHighPriorityWithPID",
|
||||
DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_INT32, &nice, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rtkit_setpriority_realtime(pid_t thread, int rt_priority)
|
||||
{
|
||||
DBusConnection *dbus_conn;
|
||||
Uint64 pid = (Uint64)getpid();
|
||||
Uint64 tid = (Uint64)thread;
|
||||
Uint32 priority = (Uint32)rt_priority;
|
||||
|
||||
pthread_once(&rtkit_initialize_once, rtkit_initialize);
|
||||
dbus_conn = get_rtkit_dbus_connection();
|
||||
|
||||
if (priority > rtkit_max_realtime_priority) {
|
||||
priority = rtkit_max_realtime_priority;
|
||||
}
|
||||
|
||||
// We always perform the thread state changes necessary for rtkit.
|
||||
// This wastes some system calls if the state is already set but
|
||||
// typically code sets a thread priority and leaves it so it's
|
||||
// not expected that this wasted effort will be an issue.
|
||||
// We also do not quit if this fails, we let the rtkit request
|
||||
// go through to determine whether it really needs to fail or not.
|
||||
rtkit_initialize_realtime_thread();
|
||||
|
||||
if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn, NULL,
|
||||
rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadRealtimeWithPID",
|
||||
DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_UINT32, &priority, DBUS_TYPE_INVALID,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
|
||||
#define rtkit_max_realtime_priority 99
|
||||
|
||||
#endif // dbus
|
||||
#endif // threads
|
||||
|
||||
// this is a public symbol, so it has to exist even if threads are disabled.
|
||||
bool SDL_SetLinuxThreadPriority(Sint64 threadID, int priority)
|
||||
{
|
||||
#ifdef SDL_THREADS_DISABLED
|
||||
return SDL_Unsupported();
|
||||
#else
|
||||
if (setpriority(PRIO_PROCESS, (id_t)threadID, priority) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
/* Note that this fails you most likely:
|
||||
* Have your process's scheduler incorrectly configured.
|
||||
See the requirements at:
|
||||
http://git.0pointer.net/rtkit.git/tree/README#n16
|
||||
* Encountered dbus/polkit security restrictions. Note
|
||||
that the RealtimeKit1 dbus endpoint is inaccessible
|
||||
over ssh connections for most common distro configs.
|
||||
You might want to check your local config for details:
|
||||
/usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
|
||||
|
||||
README and sample code at: http://git.0pointer.net/rtkit.git
|
||||
*/
|
||||
if (rtkit_setpriority_nice((pid_t)threadID, priority)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SDL_SetError("setpriority() failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is a public symbol, so it has to exist even if threads are disabled.
|
||||
bool SDL_SetLinuxThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)
|
||||
{
|
||||
#ifdef SDL_THREADS_DISABLED
|
||||
return SDL_Unsupported();
|
||||
#else
|
||||
int osPriority;
|
||||
|
||||
if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {
|
||||
if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {
|
||||
osPriority = 1;
|
||||
} else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {
|
||||
osPriority = rtkit_max_realtime_priority * 3 / 4;
|
||||
} else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
|
||||
osPriority = rtkit_max_realtime_priority;
|
||||
} else {
|
||||
osPriority = rtkit_max_realtime_priority / 2;
|
||||
}
|
||||
} else {
|
||||
if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {
|
||||
osPriority = 19;
|
||||
} else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {
|
||||
osPriority = -10;
|
||||
} else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
|
||||
osPriority = -20;
|
||||
} else {
|
||||
osPriority = 0;
|
||||
}
|
||||
|
||||
if (setpriority(PRIO_PROCESS, (id_t)threadID, osPriority) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
/* Note that this fails you most likely:
|
||||
* Have your process's scheduler incorrectly configured.
|
||||
See the requirements at:
|
||||
http://git.0pointer.net/rtkit.git/tree/README#n16
|
||||
* Encountered dbus/polkit security restrictions. Note
|
||||
that the RealtimeKit1 dbus endpoint is inaccessible
|
||||
over ssh connections for most common distro configs.
|
||||
You might want to check your local config for details:
|
||||
/usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
|
||||
|
||||
README and sample code at: http://git.0pointer.net/rtkit.git
|
||||
*/
|
||||
if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {
|
||||
if (rtkit_setpriority_realtime((pid_t)threadID, osPriority)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (rtkit_setpriority_nice((pid_t)threadID, osPriority)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return SDL_SetError("setpriority() failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SDL_PLATFORM_LINUX
|
||||
+667
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
/*
|
||||
* To list the properties of a device, try something like:
|
||||
* udevadm info -a -n snd/hwC0D0 (for a sound card)
|
||||
* udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
|
||||
* udevadm info --query=property -n input/event2
|
||||
*/
|
||||
#include "SDL_udev.h"
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "SDL_evdev_capabilities.h"
|
||||
#include "../unix/SDL_poll.h"
|
||||
|
||||
#define SDL_UDEV_FALLBACK_LIBS "libudev.so.1", "libudev.so.0"
|
||||
|
||||
static const char *SDL_UDEV_LIBS[] = { SDL_UDEV_FALLBACK_LIBS };
|
||||
|
||||
#ifdef SDL_UDEV_DYNAMIC
|
||||
#define SDL_UDEV_DLNOTE_LIBS SDL_UDEV_DYNAMIC, SDL_UDEV_FALLBACK_LIBS
|
||||
#else
|
||||
#define SDL_UDEV_DLNOTE_LIBS SDL_UDEV_FALLBACK_LIBS
|
||||
#endif
|
||||
|
||||
SDL_ELF_NOTE_DLOPEN(
|
||||
"events-udev",
|
||||
"Support for events through libudev",
|
||||
SDL_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
SDL_UDEV_DLNOTE_LIBS
|
||||
)
|
||||
|
||||
static SDL_UDEV_PrivateData *_this = NULL;
|
||||
|
||||
static bool SDL_UDEV_load_sym(const char *fn, void **addr);
|
||||
static bool SDL_UDEV_load_syms(void);
|
||||
static bool SDL_UDEV_hotplug_update_available(void);
|
||||
static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len);
|
||||
static int guess_device_class(struct udev_device *dev);
|
||||
static int device_class(struct udev_device *dev);
|
||||
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
|
||||
|
||||
static bool SDL_UDEV_load_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(_this->udev_handle, fn);
|
||||
if (!*addr) {
|
||||
// Don't call SDL_SetError(): SDL_LoadFunction already did.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SDL_UDEV_load_syms(void)
|
||||
{
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_UDEV_SYM(x) \
|
||||
if (!SDL_UDEV_load_sym(#x, (void **)(char *)&_this->syms.x)) \
|
||||
return false
|
||||
|
||||
#define SDL_UDEV_SYM_OPTIONAL(x) \
|
||||
SDL_UDEV_load_sym(#x, (void **)(char *)&_this->syms.x);
|
||||
|
||||
SDL_UDEV_SYM(udev_device_get_action);
|
||||
SDL_UDEV_SYM(udev_device_get_devnode);
|
||||
SDL_UDEV_SYM(udev_device_get_driver);
|
||||
SDL_UDEV_SYM(udev_device_get_syspath);
|
||||
SDL_UDEV_SYM(udev_device_get_subsystem);
|
||||
SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype);
|
||||
SDL_UDEV_SYM(udev_device_get_property_value);
|
||||
SDL_UDEV_SYM(udev_device_get_sysattr_value);
|
||||
SDL_UDEV_SYM(udev_device_new_from_syspath);
|
||||
SDL_UDEV_SYM(udev_device_unref);
|
||||
SDL_UDEV_SYM(udev_enumerate_add_match_property);
|
||||
SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
|
||||
SDL_UDEV_SYM(udev_enumerate_get_list_entry);
|
||||
SDL_UDEV_SYM(udev_enumerate_new);
|
||||
SDL_UDEV_SYM(udev_enumerate_scan_devices);
|
||||
SDL_UDEV_SYM(udev_enumerate_unref);
|
||||
SDL_UDEV_SYM(udev_list_entry_get_name);
|
||||
SDL_UDEV_SYM(udev_list_entry_get_value);
|
||||
SDL_UDEV_SYM(udev_list_entry_get_next);
|
||||
SDL_UDEV_SYM(udev_monitor_enable_receiving);
|
||||
SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
|
||||
SDL_UDEV_SYM(udev_monitor_get_fd);
|
||||
SDL_UDEV_SYM(udev_monitor_new_from_netlink);
|
||||
SDL_UDEV_SYM(udev_monitor_receive_device);
|
||||
SDL_UDEV_SYM(udev_monitor_unref);
|
||||
SDL_UDEV_SYM(udev_new);
|
||||
SDL_UDEV_SYM(udev_unref);
|
||||
SDL_UDEV_SYM(udev_device_new_from_devnum);
|
||||
SDL_UDEV_SYM(udev_device_get_devnum);
|
||||
|
||||
SDL_UDEV_SYM_OPTIONAL(udev_hwdb_new);
|
||||
SDL_UDEV_SYM_OPTIONAL(udev_hwdb_unref);
|
||||
SDL_UDEV_SYM_OPTIONAL(udev_hwdb_get_properties_list_entry);
|
||||
|
||||
#undef SDL_UDEV_SYM
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SDL_UDEV_hotplug_update_available(void)
|
||||
{
|
||||
if (_this->udev_mon) {
|
||||
const int fd = _this->syms.udev_monitor_get_fd(_this->udev_mon);
|
||||
if (SDL_IOReady(fd, SDL_IOR_READ, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL_UDEV_Init(void)
|
||||
{
|
||||
if (!_this) {
|
||||
_this = (SDL_UDEV_PrivateData *)SDL_calloc(1, sizeof(*_this));
|
||||
if (!_this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SDL_UDEV_LoadLibrary()) {
|
||||
SDL_UDEV_Quit();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up udev monitoring
|
||||
* Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
|
||||
*/
|
||||
|
||||
_this->udev = _this->syms.udev_new();
|
||||
if (!_this->udev) {
|
||||
SDL_UDEV_Quit();
|
||||
return SDL_SetError("udev_new() failed");
|
||||
}
|
||||
|
||||
_this->udev_mon = _this->syms.udev_monitor_new_from_netlink(_this->udev, "udev");
|
||||
if (!_this->udev_mon) {
|
||||
SDL_UDEV_Quit();
|
||||
return SDL_SetError("udev_monitor_new_from_netlink() failed");
|
||||
}
|
||||
|
||||
_this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
|
||||
_this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
|
||||
_this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "video4linux", NULL);
|
||||
_this->syms.udev_monitor_enable_receiving(_this->udev_mon);
|
||||
|
||||
// Do an initial scan of existing devices
|
||||
SDL_UDEV_Scan();
|
||||
}
|
||||
|
||||
_this->ref_count += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_UDEV_Quit(void)
|
||||
{
|
||||
if (!_this) {
|
||||
return;
|
||||
}
|
||||
|
||||
_this->ref_count -= 1;
|
||||
|
||||
if (_this->ref_count < 1) {
|
||||
|
||||
if (_this->udev_mon) {
|
||||
_this->syms.udev_monitor_unref(_this->udev_mon);
|
||||
_this->udev_mon = NULL;
|
||||
}
|
||||
if (_this->udev) {
|
||||
_this->syms.udev_unref(_this->udev);
|
||||
_this->udev = NULL;
|
||||
}
|
||||
|
||||
// Remove existing devices
|
||||
while (_this->first) {
|
||||
SDL_UDEV_CallbackList *item = _this->first;
|
||||
_this->first = _this->first->next;
|
||||
SDL_free(item);
|
||||
}
|
||||
|
||||
SDL_UDEV_UnloadLibrary();
|
||||
SDL_free(_this);
|
||||
_this = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_UDEV_Scan(void)
|
||||
{
|
||||
struct udev_enumerate *enumerate = NULL;
|
||||
struct udev_list_entry *devs = NULL;
|
||||
struct udev_list_entry *item = NULL;
|
||||
|
||||
if (!_this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
enumerate = _this->syms.udev_enumerate_new(_this->udev);
|
||||
if (!enumerate) {
|
||||
SDL_UDEV_Quit();
|
||||
return SDL_SetError("udev_enumerate_new() failed");
|
||||
}
|
||||
|
||||
_this->syms.udev_enumerate_add_match_subsystem(enumerate, "input");
|
||||
_this->syms.udev_enumerate_add_match_subsystem(enumerate, "sound");
|
||||
_this->syms.udev_enumerate_add_match_subsystem(enumerate, "video4linux");
|
||||
|
||||
_this->syms.udev_enumerate_scan_devices(enumerate);
|
||||
devs = _this->syms.udev_enumerate_get_list_entry(enumerate);
|
||||
for (item = devs; item; item = _this->syms.udev_list_entry_get_next(item)) {
|
||||
const char *path = _this->syms.udev_list_entry_get_name(item);
|
||||
struct udev_device *dev = _this->syms.udev_device_new_from_syspath(_this->udev, path);
|
||||
if (dev) {
|
||||
device_event(SDL_UDEV_DEVICEADDED, dev);
|
||||
_this->syms.udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
|
||||
_this->syms.udev_enumerate_unref(enumerate);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_UDEV_GetProductInfo(const char *device_path, struct input_id *inpid, int *class, char **driver)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char type;
|
||||
struct udev_device *dev;
|
||||
const char *val;
|
||||
int class_temp;
|
||||
|
||||
if (!_this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(device_path, &statbuf) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (S_ISBLK(statbuf.st_mode)) {
|
||||
type = 'b';
|
||||
} else if (S_ISCHR(statbuf.st_mode)) {
|
||||
type = 'c';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
dev = _this->syms.udev_device_new_from_devnum(_this->udev, type, statbuf.st_rdev);
|
||||
if (!dev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID");
|
||||
if (val) {
|
||||
inpid->vendor = (Uint16)SDL_strtol(val, NULL, 16);
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID");
|
||||
if (val) {
|
||||
inpid->product = (Uint16)SDL_strtol(val, NULL, 16);
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION");
|
||||
if (val) {
|
||||
inpid->version = (Uint16)SDL_strtol(val, NULL, 16);
|
||||
}
|
||||
|
||||
if (driver) {
|
||||
val = _this->syms.udev_device_get_driver(dev);
|
||||
if (!val) {
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_USB_DRIVER");
|
||||
}
|
||||
if (val) {
|
||||
*driver = SDL_strdup(val);
|
||||
}
|
||||
}
|
||||
|
||||
class_temp = device_class(dev);
|
||||
if (class_temp) {
|
||||
*class = class_temp;
|
||||
}
|
||||
|
||||
_this->syms.udev_device_unref(dev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char *SDL_UDEV_GetProductSerial(const char *device_path)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char type;
|
||||
struct udev_device *dev;
|
||||
const char *val;
|
||||
char *result = NULL;
|
||||
|
||||
if (!_this) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stat(device_path, &statbuf) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (S_ISBLK(statbuf.st_mode)) {
|
||||
type = 'b';
|
||||
} else if (S_ISCHR(statbuf.st_mode)) {
|
||||
type = 'c';
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = _this->syms.udev_device_new_from_devnum(_this->udev, type, statbuf.st_rdev);
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_SERIAL_SHORT");
|
||||
if (val && *val) {
|
||||
result = SDL_strdup(val);
|
||||
}
|
||||
|
||||
_this->syms.udev_device_unref(dev);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDL_UDEV_UnloadLibrary(void)
|
||||
{
|
||||
if (!_this) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_this->udev_handle) {
|
||||
SDL_UnloadObject(_this->udev_handle);
|
||||
_this->udev_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_UDEV_LoadLibrary(void)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (!_this) {
|
||||
return SDL_SetError("UDEV not initialized");
|
||||
}
|
||||
|
||||
// See if there is a udev library already loaded
|
||||
if (SDL_UDEV_load_syms()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SDL_UDEV_DYNAMIC
|
||||
// Check for the build environment's libudev first
|
||||
if (!_this->udev_handle) {
|
||||
_this->udev_handle = SDL_LoadObject(SDL_UDEV_DYNAMIC);
|
||||
if (_this->udev_handle) {
|
||||
result = SDL_UDEV_load_syms();
|
||||
if (!result) {
|
||||
SDL_UDEV_UnloadLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_this->udev_handle) {
|
||||
for (int i = 0; i < SDL_arraysize(SDL_UDEV_LIBS); i++) {
|
||||
_this->udev_handle = SDL_LoadObject(SDL_UDEV_LIBS[i]);
|
||||
if (_this->udev_handle) {
|
||||
result = SDL_UDEV_load_syms();
|
||||
if (!result) {
|
||||
SDL_UDEV_UnloadLibrary();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_this->udev_handle) {
|
||||
result = false;
|
||||
// Don't call SDL_SetError(): SDL_LoadObject already did.
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
|
||||
{
|
||||
const char *value;
|
||||
char text[4096];
|
||||
char *word;
|
||||
int i;
|
||||
unsigned long v;
|
||||
|
||||
SDL_memset(bitmask, 0, bitmask_len * sizeof(*bitmask));
|
||||
value = _this->syms.udev_device_get_sysattr_value(pdev, attr);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_strlcpy(text, value, sizeof(text));
|
||||
i = 0;
|
||||
while ((word = SDL_strrchr(text, ' ')) != NULL) {
|
||||
v = SDL_strtoul(word + 1, NULL, 16);
|
||||
if (i < bitmask_len) {
|
||||
bitmask[i] = v;
|
||||
}
|
||||
++i;
|
||||
*word = '\0';
|
||||
}
|
||||
v = SDL_strtoul(text, NULL, 16);
|
||||
if (i < bitmask_len) {
|
||||
bitmask[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
static int guess_device_class(struct udev_device *dev)
|
||||
{
|
||||
struct udev_device *pdev;
|
||||
unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)];
|
||||
unsigned long bitmask_ev[NBITS(EV_MAX)];
|
||||
unsigned long bitmask_abs[NBITS(ABS_MAX)];
|
||||
unsigned long bitmask_key[NBITS(KEY_MAX)];
|
||||
unsigned long bitmask_rel[NBITS(REL_MAX)];
|
||||
|
||||
/* walk up the parental chain until we find the real input device; the
|
||||
* argument is very likely a subdevice of this, like eventN */
|
||||
pdev = dev;
|
||||
while (pdev && !_this->syms.udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
|
||||
pdev = _this->syms.udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
|
||||
}
|
||||
if (!pdev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_caps(dev, pdev, "properties", bitmask_props, SDL_arraysize(bitmask_props));
|
||||
get_caps(dev, pdev, "capabilities/ev", bitmask_ev, SDL_arraysize(bitmask_ev));
|
||||
get_caps(dev, pdev, "capabilities/abs", bitmask_abs, SDL_arraysize(bitmask_abs));
|
||||
get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
|
||||
get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
|
||||
|
||||
return SDL_EVDEV_GuessDeviceClass(&bitmask_props[0],
|
||||
&bitmask_ev[0],
|
||||
&bitmask_abs[0],
|
||||
&bitmask_key[0],
|
||||
&bitmask_rel[0]);
|
||||
}
|
||||
|
||||
static int device_class(struct udev_device *dev)
|
||||
{
|
||||
const char *subsystem;
|
||||
const char *val = NULL;
|
||||
int devclass = 0;
|
||||
|
||||
subsystem = _this->syms.udev_device_get_subsystem(dev);
|
||||
if (!subsystem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SDL_strcmp(subsystem, "sound") == 0) {
|
||||
devclass = SDL_UDEV_DEVICE_SOUND;
|
||||
} else if (SDL_strcmp(subsystem, "video4linux") == 0) {
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_V4L_CAPABILITIES");
|
||||
if (val && SDL_strcasestr(val, "capture")) {
|
||||
devclass = SDL_UDEV_DEVICE_VIDEO_CAPTURE;
|
||||
}
|
||||
} else if (SDL_strcmp(subsystem, "input") == 0) {
|
||||
// udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_JOYSTICK;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_ACCELEROMETER;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_MOUSE;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
|
||||
}
|
||||
|
||||
/* The undocumented rule is:
|
||||
- All devices with keys get ID_INPUT_KEY
|
||||
- From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
|
||||
|
||||
Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
|
||||
*/
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEY");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_HAS_KEYS;
|
||||
}
|
||||
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
|
||||
if (val && SDL_strcmp(val, "1") == 0) {
|
||||
devclass |= SDL_UDEV_DEVICE_KEYBOARD;
|
||||
}
|
||||
|
||||
if (devclass == 0) {
|
||||
// Fall back to old style input classes
|
||||
val = _this->syms.udev_device_get_property_value(dev, "ID_CLASS");
|
||||
if (val) {
|
||||
if (SDL_strcmp(val, "joystick") == 0) {
|
||||
devclass = SDL_UDEV_DEVICE_JOYSTICK;
|
||||
} else if (SDL_strcmp(val, "mouse") == 0) {
|
||||
devclass = SDL_UDEV_DEVICE_MOUSE;
|
||||
} else if (SDL_strcmp(val, "kbd") == 0) {
|
||||
devclass = SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_KEYBOARD;
|
||||
}
|
||||
} else {
|
||||
// We could be linked with libudev on a system that doesn't have udev running
|
||||
devclass = guess_device_class(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devclass;
|
||||
}
|
||||
|
||||
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
|
||||
{
|
||||
int devclass = 0;
|
||||
const char *path;
|
||||
SDL_UDEV_CallbackList *item;
|
||||
|
||||
path = _this->syms.udev_device_get_devnode(dev);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == SDL_UDEV_DEVICEADDED) {
|
||||
devclass = device_class(dev);
|
||||
if (!devclass) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// The device has been removed, the class isn't available
|
||||
}
|
||||
|
||||
// Process callbacks
|
||||
for (item = _this->first; item; item = item->next) {
|
||||
item->callback(type, devclass, path);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_UDEV_Poll(void)
|
||||
{
|
||||
struct udev_device *dev = NULL;
|
||||
const char *action = NULL;
|
||||
|
||||
if (!_this) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (SDL_UDEV_hotplug_update_available()) {
|
||||
dev = _this->syms.udev_monitor_receive_device(_this->udev_mon);
|
||||
if (!dev) {
|
||||
break;
|
||||
}
|
||||
action = _this->syms.udev_device_get_action(dev);
|
||||
|
||||
if (action) {
|
||||
if (SDL_strcmp(action, "add") == 0) {
|
||||
device_event(SDL_UDEV_DEVICEADDED, dev);
|
||||
} else if (SDL_strcmp(action, "remove") == 0) {
|
||||
device_event(SDL_UDEV_DEVICEREMOVED, dev);
|
||||
}
|
||||
}
|
||||
|
||||
_this->syms.udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
|
||||
{
|
||||
SDL_UDEV_CallbackList *item;
|
||||
item = (SDL_UDEV_CallbackList *)SDL_calloc(1, sizeof(SDL_UDEV_CallbackList));
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
item->callback = cb;
|
||||
|
||||
if (!_this->last) {
|
||||
_this->first = _this->last = item;
|
||||
} else {
|
||||
_this->last->next = item;
|
||||
_this->last = item;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
|
||||
{
|
||||
SDL_UDEV_CallbackList *item;
|
||||
SDL_UDEV_CallbackList *prev = NULL;
|
||||
|
||||
if (!_this) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (item = _this->first; item; item = item->next) {
|
||||
// found it, remove it.
|
||||
if (item->callback == cb) {
|
||||
if (prev) {
|
||||
prev->next = item->next;
|
||||
} else {
|
||||
SDL_assert(_this->first == item);
|
||||
_this->first = item->next;
|
||||
}
|
||||
if (item == _this->last) {
|
||||
_this->last = prev;
|
||||
}
|
||||
SDL_free(item);
|
||||
return;
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
}
|
||||
|
||||
const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void)
|
||||
{
|
||||
if (!SDL_UDEV_Init()) {
|
||||
SDL_SetError("Could not initialize UDEV");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &_this->syms;
|
||||
}
|
||||
|
||||
void SDL_UDEV_ReleaseUdevSyms(void)
|
||||
{
|
||||
SDL_UDEV_Quit();
|
||||
}
|
||||
|
||||
#endif // SDL_USE_LIBUDEV
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_udev_h_
|
||||
#define SDL_udev_h_
|
||||
|
||||
#if defined(HAVE_LIBUDEV_H) && defined(HAVE_LINUX_INPUT_H)
|
||||
|
||||
#ifndef SDL_USE_LIBUDEV
|
||||
#define SDL_USE_LIBUDEV 1
|
||||
#endif
|
||||
|
||||
#include <libudev.h>
|
||||
#include <linux/input.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* Device type
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SDL_UDEV_DEVICEADDED = 1,
|
||||
SDL_UDEV_DEVICEREMOVED
|
||||
} SDL_UDEV_deviceevent;
|
||||
|
||||
typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
|
||||
|
||||
typedef struct SDL_UDEV_CallbackList
|
||||
{
|
||||
SDL_UDEV_Callback callback;
|
||||
struct SDL_UDEV_CallbackList *next;
|
||||
} SDL_UDEV_CallbackList;
|
||||
|
||||
typedef struct SDL_UDEV_Symbols
|
||||
{
|
||||
const char *(*udev_device_get_action)(struct udev_device *);
|
||||
const char *(*udev_device_get_devnode)(struct udev_device *);
|
||||
const char *(*udev_device_get_driver)(struct udev_device *);
|
||||
const char *(*udev_device_get_syspath)(struct udev_device *);
|
||||
const char *(*udev_device_get_subsystem)(struct udev_device *);
|
||||
struct udev_device *(*udev_device_get_parent_with_subsystem_devtype)(struct udev_device *udev_device, const char *subsystem, const char *devtype);
|
||||
const char *(*udev_device_get_property_value)(struct udev_device *, const char *);
|
||||
const char *(*udev_device_get_sysattr_value)(struct udev_device *udev_device, const char *sysattr);
|
||||
struct udev_device *(*udev_device_new_from_syspath)(struct udev *, const char *);
|
||||
void (*udev_device_unref)(struct udev_device *);
|
||||
int (*udev_enumerate_add_match_property)(struct udev_enumerate *, const char *, const char *);
|
||||
int (*udev_enumerate_add_match_subsystem)(struct udev_enumerate *, const char *);
|
||||
struct udev_list_entry *(*udev_enumerate_get_list_entry)(struct udev_enumerate *);
|
||||
struct udev_enumerate *(*udev_enumerate_new)(struct udev *);
|
||||
int (*udev_enumerate_scan_devices)(struct udev_enumerate *);
|
||||
void (*udev_enumerate_unref)(struct udev_enumerate *);
|
||||
const char *(*udev_list_entry_get_name)(struct udev_list_entry *);
|
||||
const char *(*udev_list_entry_get_value)(struct udev_list_entry *);
|
||||
struct udev_list_entry *(*udev_list_entry_get_next)(struct udev_list_entry *);
|
||||
int (*udev_monitor_enable_receiving)(struct udev_monitor *);
|
||||
int (*udev_monitor_filter_add_match_subsystem_devtype)(struct udev_monitor *, const char *, const char *);
|
||||
int (*udev_monitor_get_fd)(struct udev_monitor *);
|
||||
struct udev_monitor *(*udev_monitor_new_from_netlink)(struct udev *, const char *);
|
||||
struct udev_device *(*udev_monitor_receive_device)(struct udev_monitor *);
|
||||
void (*udev_monitor_unref)(struct udev_monitor *);
|
||||
struct udev *(*udev_new)(void);
|
||||
void (*udev_unref)(struct udev *);
|
||||
struct udev_device *(*udev_device_new_from_devnum)(struct udev *udev, char type, dev_t devnum);
|
||||
dev_t (*udev_device_get_devnum)(struct udev_device *udev_device);
|
||||
|
||||
struct udev_hwdb *(*udev_hwdb_new)(struct udev *udev);
|
||||
struct udev_hwdb *(*udev_hwdb_unref)(struct udev_hwdb *hwdb);
|
||||
struct udev_list_entry *(*udev_hwdb_get_properties_list_entry)(struct udev_hwdb *hwdb, const char *modalias, unsigned flags);
|
||||
|
||||
} SDL_UDEV_Symbols;
|
||||
|
||||
typedef struct SDL_UDEV_PrivateData
|
||||
{
|
||||
const char *udev_library;
|
||||
SDL_SharedObject *udev_handle;
|
||||
struct udev *udev;
|
||||
struct udev_monitor *udev_mon;
|
||||
int ref_count;
|
||||
SDL_UDEV_CallbackList *first, *last;
|
||||
|
||||
// Function pointers
|
||||
SDL_UDEV_Symbols syms;
|
||||
} SDL_UDEV_PrivateData;
|
||||
|
||||
extern bool SDL_UDEV_Init(void);
|
||||
extern void SDL_UDEV_Quit(void);
|
||||
extern void SDL_UDEV_UnloadLibrary(void);
|
||||
extern bool SDL_UDEV_LoadLibrary(void);
|
||||
extern void SDL_UDEV_Poll(void);
|
||||
extern bool SDL_UDEV_Scan(void);
|
||||
extern bool SDL_UDEV_GetProductInfo(const char *device_path, struct input_id *inpid, int *class, char **driver);
|
||||
extern char *SDL_UDEV_GetProductSerial(const char *device_path);
|
||||
extern bool SDL_UDEV_AddCallback(SDL_UDEV_Callback cb);
|
||||
extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
|
||||
extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);
|
||||
extern void SDL_UDEV_ReleaseUdevSyms(void);
|
||||
|
||||
#endif // HAVE_LIBUDEV_H && HAVE_LINUX_INPUT_H
|
||||
|
||||
#endif // SDL_udev_h_
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include <e32std.h>
|
||||
#include <e32svr.h>
|
||||
#include <hal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool NGAGE_IsClassicModel()
|
||||
{
|
||||
int phone_id;
|
||||
HAL::Get(HALData::EMachineUid, phone_id);
|
||||
|
||||
return (0x101f8c19 == phone_id);
|
||||
}
|
||||
|
||||
void NGAGE_DebugPrintf(const char *fmt, ...)
|
||||
{
|
||||
char buffer[512] = { 0 };
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
(void)SDL_vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
TBuf<512> buf;
|
||||
buf.Copy(TPtrC8((TText8 *)buffer));
|
||||
|
||||
RDebug::Print(_L("%S"), &buf);
|
||||
}
|
||||
|
||||
TInt NGAGE_GetFreeHeapMemory()
|
||||
{
|
||||
TInt heap_available = 0;
|
||||
return User::Available(heap_available);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ngage_h
|
||||
#define SDL_ngage_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool NGAGE_IsClassicModel();
|
||||
void NGAGE_DebugPrintf(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_ngage_h */
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
void SDL_WSCONS_Init(void);
|
||||
void SDL_WSCONS_Quit(void);
|
||||
|
||||
void SDL_WSCONS_PumpEvents(void);
|
||||
+948
@@ -0,0 +1,948 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
#include <dev/wscons/wsksymvar.h>
|
||||
#include <dev/wscons/wsksymdef.h>
|
||||
#include "SDL_wscons.h"
|
||||
#include <sys/time.h>
|
||||
#include <dev/wscons/wsconsio.h>
|
||||
#include <dev/wscons/wsdisplay_usl_io.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
|
||||
#ifdef SDL_PLATFORM_NETBSD
|
||||
#define KS_GROUP_Ascii KS_GROUP_Plain
|
||||
#define KS_Cmd_ScrollBack KS_Cmd_ScrollFastUp
|
||||
#define KS_Cmd_ScrollFwd KS_Cmd_ScrollFastDown
|
||||
#endif
|
||||
|
||||
#define RETIFIOCTLERR(x) \
|
||||
if ((x) == -1) { \
|
||||
SDL_free(input); \
|
||||
input = NULL; \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
typedef struct SDL_WSCONS_mouse_input_data SDL_WSCONS_mouse_input_data;
|
||||
extern SDL_WSCONS_mouse_input_data *SDL_WSCONS_Init_Mouse(void);
|
||||
extern void updateMouse(SDL_WSCONS_mouse_input_data *input);
|
||||
extern void SDL_WSCONS_Quit_Mouse(SDL_WSCONS_mouse_input_data *input);
|
||||
|
||||
// Conversion table courtesy of /usr/src/sys/dev/wscons/wskbdutil.c
|
||||
static const unsigned char latin1_to_upper[256] = {
|
||||
// 0 8 1 9 2 a 3 b 4 c 5 d 6 e 7 f
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5
|
||||
0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', // 6
|
||||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // 6
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // 7
|
||||
'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00, // 7
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // a
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // a
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // b
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // b
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // c
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // c
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // d
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // d
|
||||
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // e
|
||||
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, // e
|
||||
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, // f
|
||||
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, // f
|
||||
};
|
||||
|
||||
// Compose table courtesy of /usr/src/sys/dev/wscons/wskbdutil.c
|
||||
static struct SDL_wscons_compose_tab_s
|
||||
{
|
||||
keysym_t elem[2];
|
||||
keysym_t result;
|
||||
} compose_tab[] = {
|
||||
{ { KS_plus, KS_plus }, KS_numbersign },
|
||||
{ { KS_a, KS_a }, KS_at },
|
||||
{ { KS_parenleft, KS_parenleft }, KS_bracketleft },
|
||||
{ { KS_slash, KS_slash }, KS_backslash },
|
||||
{ { KS_parenright, KS_parenright }, KS_bracketright },
|
||||
{ { KS_parenleft, KS_minus }, KS_braceleft },
|
||||
{ { KS_slash, KS_minus }, KS_bar },
|
||||
{ { KS_parenright, KS_minus }, KS_braceright },
|
||||
{ { KS_exclam, KS_exclam }, KS_exclamdown },
|
||||
{ { KS_c, KS_slash }, KS_cent },
|
||||
{ { KS_l, KS_minus }, KS_sterling },
|
||||
{ { KS_y, KS_minus }, KS_yen },
|
||||
{ { KS_s, KS_o }, KS_section },
|
||||
{ { KS_x, KS_o }, KS_currency },
|
||||
{ { KS_c, KS_o }, KS_copyright },
|
||||
{ { KS_less, KS_less }, KS_guillemotleft },
|
||||
{ { KS_greater, KS_greater }, KS_guillemotright },
|
||||
{ { KS_question, KS_question }, KS_questiondown },
|
||||
{ { KS_dead_acute, KS_space }, KS_apostrophe },
|
||||
{ { KS_dead_grave, KS_space }, KS_grave },
|
||||
{ { KS_dead_tilde, KS_space }, KS_asciitilde },
|
||||
{ { KS_dead_circumflex, KS_space }, KS_asciicircum },
|
||||
{ { KS_dead_diaeresis, KS_space }, KS_quotedbl },
|
||||
{ { KS_dead_cedilla, KS_space }, KS_comma },
|
||||
{ { KS_dead_circumflex, KS_A }, KS_Acircumflex },
|
||||
{ { KS_dead_diaeresis, KS_A }, KS_Adiaeresis },
|
||||
{ { KS_dead_grave, KS_A }, KS_Agrave },
|
||||
{ { KS_dead_abovering, KS_A }, KS_Aring },
|
||||
{ { KS_dead_tilde, KS_A }, KS_Atilde },
|
||||
{ { KS_dead_cedilla, KS_C }, KS_Ccedilla },
|
||||
{ { KS_dead_acute, KS_E }, KS_Eacute },
|
||||
{ { KS_dead_circumflex, KS_E }, KS_Ecircumflex },
|
||||
{ { KS_dead_diaeresis, KS_E }, KS_Ediaeresis },
|
||||
{ { KS_dead_grave, KS_E }, KS_Egrave },
|
||||
{ { KS_dead_acute, KS_I }, KS_Iacute },
|
||||
{ { KS_dead_circumflex, KS_I }, KS_Icircumflex },
|
||||
{ { KS_dead_diaeresis, KS_I }, KS_Idiaeresis },
|
||||
{ { KS_dead_grave, KS_I }, KS_Igrave },
|
||||
{ { KS_dead_tilde, KS_N }, KS_Ntilde },
|
||||
{ { KS_dead_acute, KS_O }, KS_Oacute },
|
||||
{ { KS_dead_circumflex, KS_O }, KS_Ocircumflex },
|
||||
{ { KS_dead_diaeresis, KS_O }, KS_Odiaeresis },
|
||||
{ { KS_dead_grave, KS_O }, KS_Ograve },
|
||||
{ { KS_dead_tilde, KS_O }, KS_Otilde },
|
||||
{ { KS_dead_acute, KS_U }, KS_Uacute },
|
||||
{ { KS_dead_circumflex, KS_U }, KS_Ucircumflex },
|
||||
{ { KS_dead_diaeresis, KS_U }, KS_Udiaeresis },
|
||||
{ { KS_dead_grave, KS_U }, KS_Ugrave },
|
||||
{ { KS_dead_acute, KS_Y }, KS_Yacute },
|
||||
{ { KS_dead_acute, KS_a }, KS_aacute },
|
||||
{ { KS_dead_circumflex, KS_a }, KS_acircumflex },
|
||||
{ { KS_dead_diaeresis, KS_a }, KS_adiaeresis },
|
||||
{ { KS_dead_grave, KS_a }, KS_agrave },
|
||||
{ { KS_dead_abovering, KS_a }, KS_aring },
|
||||
{ { KS_dead_tilde, KS_a }, KS_atilde },
|
||||
{ { KS_dead_cedilla, KS_c }, KS_ccedilla },
|
||||
{ { KS_dead_acute, KS_e }, KS_eacute },
|
||||
{ { KS_dead_circumflex, KS_e }, KS_ecircumflex },
|
||||
{ { KS_dead_diaeresis, KS_e }, KS_ediaeresis },
|
||||
{ { KS_dead_grave, KS_e }, KS_egrave },
|
||||
{ { KS_dead_acute, KS_i }, KS_iacute },
|
||||
{ { KS_dead_circumflex, KS_i }, KS_icircumflex },
|
||||
{ { KS_dead_diaeresis, KS_i }, KS_idiaeresis },
|
||||
{ { KS_dead_grave, KS_i }, KS_igrave },
|
||||
{ { KS_dead_tilde, KS_n }, KS_ntilde },
|
||||
{ { KS_dead_acute, KS_o }, KS_oacute },
|
||||
{ { KS_dead_circumflex, KS_o }, KS_ocircumflex },
|
||||
{ { KS_dead_diaeresis, KS_o }, KS_odiaeresis },
|
||||
{ { KS_dead_grave, KS_o }, KS_ograve },
|
||||
{ { KS_dead_tilde, KS_o }, KS_otilde },
|
||||
{ { KS_dead_acute, KS_u }, KS_uacute },
|
||||
{ { KS_dead_circumflex, KS_u }, KS_ucircumflex },
|
||||
{ { KS_dead_diaeresis, KS_u }, KS_udiaeresis },
|
||||
{ { KS_dead_grave, KS_u }, KS_ugrave },
|
||||
{ { KS_dead_acute, KS_y }, KS_yacute },
|
||||
{ { KS_dead_diaeresis, KS_y }, KS_ydiaeresis },
|
||||
{ { KS_quotedbl, KS_A }, KS_Adiaeresis },
|
||||
{ { KS_quotedbl, KS_E }, KS_Ediaeresis },
|
||||
{ { KS_quotedbl, KS_I }, KS_Idiaeresis },
|
||||
{ { KS_quotedbl, KS_O }, KS_Odiaeresis },
|
||||
{ { KS_quotedbl, KS_U }, KS_Udiaeresis },
|
||||
{ { KS_quotedbl, KS_a }, KS_adiaeresis },
|
||||
{ { KS_quotedbl, KS_e }, KS_ediaeresis },
|
||||
{ { KS_quotedbl, KS_i }, KS_idiaeresis },
|
||||
{ { KS_quotedbl, KS_o }, KS_odiaeresis },
|
||||
{ { KS_quotedbl, KS_u }, KS_udiaeresis },
|
||||
{ { KS_quotedbl, KS_y }, KS_ydiaeresis },
|
||||
{ { KS_acute, KS_A }, KS_Aacute },
|
||||
{ { KS_asciicircum, KS_A }, KS_Acircumflex },
|
||||
{ { KS_grave, KS_A }, KS_Agrave },
|
||||
{ { KS_asterisk, KS_A }, KS_Aring },
|
||||
{ { KS_asciitilde, KS_A }, KS_Atilde },
|
||||
{ { KS_cedilla, KS_C }, KS_Ccedilla },
|
||||
{ { KS_acute, KS_E }, KS_Eacute },
|
||||
{ { KS_asciicircum, KS_E }, KS_Ecircumflex },
|
||||
{ { KS_grave, KS_E }, KS_Egrave },
|
||||
{ { KS_acute, KS_I }, KS_Iacute },
|
||||
{ { KS_asciicircum, KS_I }, KS_Icircumflex },
|
||||
{ { KS_grave, KS_I }, KS_Igrave },
|
||||
{ { KS_asciitilde, KS_N }, KS_Ntilde },
|
||||
{ { KS_acute, KS_O }, KS_Oacute },
|
||||
{ { KS_asciicircum, KS_O }, KS_Ocircumflex },
|
||||
{ { KS_grave, KS_O }, KS_Ograve },
|
||||
{ { KS_asciitilde, KS_O }, KS_Otilde },
|
||||
{ { KS_acute, KS_U }, KS_Uacute },
|
||||
{ { KS_asciicircum, KS_U }, KS_Ucircumflex },
|
||||
{ { KS_grave, KS_U }, KS_Ugrave },
|
||||
{ { KS_acute, KS_Y }, KS_Yacute },
|
||||
{ { KS_acute, KS_a }, KS_aacute },
|
||||
{ { KS_asciicircum, KS_a }, KS_acircumflex },
|
||||
{ { KS_grave, KS_a }, KS_agrave },
|
||||
{ { KS_asterisk, KS_a }, KS_aring },
|
||||
{ { KS_asciitilde, KS_a }, KS_atilde },
|
||||
{ { KS_cedilla, KS_c }, KS_ccedilla },
|
||||
{ { KS_acute, KS_e }, KS_eacute },
|
||||
{ { KS_asciicircum, KS_e }, KS_ecircumflex },
|
||||
{ { KS_grave, KS_e }, KS_egrave },
|
||||
{ { KS_acute, KS_i }, KS_iacute },
|
||||
{ { KS_asciicircum, KS_i }, KS_icircumflex },
|
||||
{ { KS_grave, KS_i }, KS_igrave },
|
||||
{ { KS_asciitilde, KS_n }, KS_ntilde },
|
||||
{ { KS_acute, KS_o }, KS_oacute },
|
||||
{ { KS_asciicircum, KS_o }, KS_ocircumflex },
|
||||
{ { KS_grave, KS_o }, KS_ograve },
|
||||
{ { KS_asciitilde, KS_o }, KS_otilde },
|
||||
{ { KS_acute, KS_u }, KS_uacute },
|
||||
{ { KS_asciicircum, KS_u }, KS_ucircumflex },
|
||||
{ { KS_grave, KS_u }, KS_ugrave },
|
||||
{ { KS_acute, KS_y }, KS_yacute },
|
||||
#ifndef SDL_PLATFORM_NETBSD
|
||||
{ { KS_dead_caron, KS_space }, KS_L2_caron },
|
||||
{ { KS_dead_caron, KS_S }, KS_L2_Scaron },
|
||||
{ { KS_dead_caron, KS_Z }, KS_L2_Zcaron },
|
||||
{ { KS_dead_caron, KS_s }, KS_L2_scaron },
|
||||
{ { KS_dead_caron, KS_z }, KS_L2_zcaron }
|
||||
#endif
|
||||
};
|
||||
|
||||
static keysym_t ksym_upcase(keysym_t ksym)
|
||||
{
|
||||
if (ksym >= KS_f1 && ksym <= KS_f20) {
|
||||
return KS_F1 - KS_f1 + ksym;
|
||||
}
|
||||
|
||||
if (KS_GROUP(ksym) == KS_GROUP_Ascii && ksym <= 0xff && latin1_to_upper[ksym] != 0x00) {
|
||||
return latin1_to_upper[ksym];
|
||||
}
|
||||
|
||||
return ksym;
|
||||
}
|
||||
static struct wscons_keycode_to_SDL
|
||||
{
|
||||
keysym_t sourcekey;
|
||||
SDL_Scancode targetKey;
|
||||
} conversion_table[] = {
|
||||
{ KS_Menu, SDL_SCANCODE_APPLICATION },
|
||||
{ KS_Up, SDL_SCANCODE_UP },
|
||||
{ KS_Down, SDL_SCANCODE_DOWN },
|
||||
{ KS_Left, SDL_SCANCODE_LEFT },
|
||||
{ KS_Right, SDL_SCANCODE_RIGHT },
|
||||
{ KS_Hold_Screen, SDL_SCANCODE_SCROLLLOCK },
|
||||
{ KS_Num_Lock, SDL_SCANCODE_NUMLOCKCLEAR },
|
||||
{ KS_Caps_Lock, SDL_SCANCODE_CAPSLOCK },
|
||||
{ KS_BackSpace, SDL_SCANCODE_BACKSPACE },
|
||||
{ KS_space, SDL_SCANCODE_SPACE },
|
||||
{ KS_Delete, SDL_SCANCODE_BACKSPACE },
|
||||
{ KS_Home, SDL_SCANCODE_HOME },
|
||||
{ KS_End, SDL_SCANCODE_END },
|
||||
{ KS_Pause, SDL_SCANCODE_PAUSE },
|
||||
{ KS_Print_Screen, SDL_SCANCODE_PRINTSCREEN },
|
||||
{ KS_Insert, SDL_SCANCODE_INSERT },
|
||||
{ KS_Escape, SDL_SCANCODE_ESCAPE },
|
||||
{ KS_Return, SDL_SCANCODE_RETURN },
|
||||
{ KS_Linefeed, SDL_SCANCODE_RETURN },
|
||||
{ KS_KP_Delete, SDL_SCANCODE_DELETE },
|
||||
{ KS_KP_Insert, SDL_SCANCODE_INSERT },
|
||||
{ KS_Control_L, SDL_SCANCODE_LCTRL },
|
||||
{ KS_Control_R, SDL_SCANCODE_RCTRL },
|
||||
{ KS_Shift_L, SDL_SCANCODE_LSHIFT },
|
||||
{ KS_Shift_R, SDL_SCANCODE_RSHIFT },
|
||||
{ KS_Alt_L, SDL_SCANCODE_LALT },
|
||||
{ KS_Alt_R, SDL_SCANCODE_RALT },
|
||||
{ KS_grave, SDL_SCANCODE_GRAVE },
|
||||
|
||||
{ KS_KP_0, SDL_SCANCODE_KP_0 },
|
||||
{ KS_KP_1, SDL_SCANCODE_KP_1 },
|
||||
{ KS_KP_2, SDL_SCANCODE_KP_2 },
|
||||
{ KS_KP_3, SDL_SCANCODE_KP_3 },
|
||||
{ KS_KP_4, SDL_SCANCODE_KP_4 },
|
||||
{ KS_KP_5, SDL_SCANCODE_KP_5 },
|
||||
{ KS_KP_6, SDL_SCANCODE_KP_6 },
|
||||
{ KS_KP_7, SDL_SCANCODE_KP_7 },
|
||||
{ KS_KP_8, SDL_SCANCODE_KP_8 },
|
||||
{ KS_KP_9, SDL_SCANCODE_KP_9 },
|
||||
{ KS_KP_Enter, SDL_SCANCODE_KP_ENTER },
|
||||
{ KS_KP_Multiply, SDL_SCANCODE_KP_MULTIPLY },
|
||||
{ KS_KP_Add, SDL_SCANCODE_KP_PLUS },
|
||||
{ KS_KP_Subtract, SDL_SCANCODE_KP_MINUS },
|
||||
{ KS_KP_Divide, SDL_SCANCODE_KP_DIVIDE },
|
||||
{ KS_KP_Up, SDL_SCANCODE_UP },
|
||||
{ KS_KP_Down, SDL_SCANCODE_DOWN },
|
||||
{ KS_KP_Left, SDL_SCANCODE_LEFT },
|
||||
{ KS_KP_Right, SDL_SCANCODE_RIGHT },
|
||||
{ KS_KP_Equal, SDL_SCANCODE_KP_EQUALS },
|
||||
{ KS_f1, SDL_SCANCODE_F1 },
|
||||
{ KS_f2, SDL_SCANCODE_F2 },
|
||||
{ KS_f3, SDL_SCANCODE_F3 },
|
||||
{ KS_f4, SDL_SCANCODE_F4 },
|
||||
{ KS_f5, SDL_SCANCODE_F5 },
|
||||
{ KS_f6, SDL_SCANCODE_F6 },
|
||||
{ KS_f7, SDL_SCANCODE_F7 },
|
||||
{ KS_f8, SDL_SCANCODE_F8 },
|
||||
{ KS_f9, SDL_SCANCODE_F9 },
|
||||
{ KS_f10, SDL_SCANCODE_F10 },
|
||||
{ KS_f11, SDL_SCANCODE_F11 },
|
||||
{ KS_f12, SDL_SCANCODE_F12 },
|
||||
{ KS_f13, SDL_SCANCODE_F13 },
|
||||
{ KS_f14, SDL_SCANCODE_F14 },
|
||||
{ KS_f15, SDL_SCANCODE_F15 },
|
||||
{ KS_f16, SDL_SCANCODE_F16 },
|
||||
{ KS_f17, SDL_SCANCODE_F17 },
|
||||
{ KS_f18, SDL_SCANCODE_F18 },
|
||||
{ KS_f19, SDL_SCANCODE_F19 },
|
||||
{ KS_f20, SDL_SCANCODE_F20 },
|
||||
#ifndef SDL_PLATFORM_NETBSD
|
||||
{ KS_f21, SDL_SCANCODE_F21 },
|
||||
{ KS_f22, SDL_SCANCODE_F22 },
|
||||
{ KS_f23, SDL_SCANCODE_F23 },
|
||||
{ KS_f24, SDL_SCANCODE_F24 },
|
||||
#endif
|
||||
{ KS_Meta_L, SDL_SCANCODE_LGUI },
|
||||
{ KS_Meta_R, SDL_SCANCODE_RGUI },
|
||||
{ KS_Zenkaku_Hankaku, SDL_SCANCODE_LANG5 },
|
||||
{ KS_Hiragana_Katakana, SDL_SCANCODE_INTERNATIONAL2 },
|
||||
{ KS_yen, SDL_SCANCODE_INTERNATIONAL3 },
|
||||
{ KS_Henkan, SDL_SCANCODE_INTERNATIONAL4 },
|
||||
{ KS_Muhenkan, SDL_SCANCODE_INTERNATIONAL5 },
|
||||
{ KS_KP_Prior, SDL_SCANCODE_PRIOR },
|
||||
|
||||
{ KS_a, SDL_SCANCODE_A },
|
||||
{ KS_b, SDL_SCANCODE_B },
|
||||
{ KS_c, SDL_SCANCODE_C },
|
||||
{ KS_d, SDL_SCANCODE_D },
|
||||
{ KS_e, SDL_SCANCODE_E },
|
||||
{ KS_f, SDL_SCANCODE_F },
|
||||
{ KS_g, SDL_SCANCODE_G },
|
||||
{ KS_h, SDL_SCANCODE_H },
|
||||
{ KS_i, SDL_SCANCODE_I },
|
||||
{ KS_j, SDL_SCANCODE_J },
|
||||
{ KS_k, SDL_SCANCODE_K },
|
||||
{ KS_l, SDL_SCANCODE_L },
|
||||
{ KS_m, SDL_SCANCODE_M },
|
||||
{ KS_n, SDL_SCANCODE_N },
|
||||
{ KS_o, SDL_SCANCODE_O },
|
||||
{ KS_p, SDL_SCANCODE_P },
|
||||
{ KS_q, SDL_SCANCODE_Q },
|
||||
{ KS_r, SDL_SCANCODE_R },
|
||||
{ KS_s, SDL_SCANCODE_S },
|
||||
{ KS_t, SDL_SCANCODE_T },
|
||||
{ KS_u, SDL_SCANCODE_U },
|
||||
{ KS_v, SDL_SCANCODE_V },
|
||||
{ KS_w, SDL_SCANCODE_W },
|
||||
{ KS_x, SDL_SCANCODE_X },
|
||||
{ KS_y, SDL_SCANCODE_Y },
|
||||
{ KS_z, SDL_SCANCODE_Z },
|
||||
|
||||
{ KS_0, SDL_SCANCODE_0 },
|
||||
{ KS_1, SDL_SCANCODE_1 },
|
||||
{ KS_2, SDL_SCANCODE_2 },
|
||||
{ KS_3, SDL_SCANCODE_3 },
|
||||
{ KS_4, SDL_SCANCODE_4 },
|
||||
{ KS_5, SDL_SCANCODE_5 },
|
||||
{ KS_6, SDL_SCANCODE_6 },
|
||||
{ KS_7, SDL_SCANCODE_7 },
|
||||
{ KS_8, SDL_SCANCODE_8 },
|
||||
{ KS_9, SDL_SCANCODE_9 },
|
||||
{ KS_minus, SDL_SCANCODE_MINUS },
|
||||
{ KS_equal, SDL_SCANCODE_EQUALS },
|
||||
{ KS_Tab, SDL_SCANCODE_TAB },
|
||||
{ KS_KP_Tab, SDL_SCANCODE_KP_TAB },
|
||||
{ KS_apostrophe, SDL_SCANCODE_APOSTROPHE },
|
||||
{ KS_bracketleft, SDL_SCANCODE_LEFTBRACKET },
|
||||
{ KS_bracketright, SDL_SCANCODE_RIGHTBRACKET },
|
||||
{ KS_semicolon, SDL_SCANCODE_SEMICOLON },
|
||||
{ KS_comma, SDL_SCANCODE_COMMA },
|
||||
{ KS_period, SDL_SCANCODE_PERIOD },
|
||||
{ KS_slash, SDL_SCANCODE_SLASH },
|
||||
{ KS_backslash, SDL_SCANCODE_BACKSLASH }
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
SDL_KeyboardID keyboardID;
|
||||
struct wskbd_map_data keymap;
|
||||
int ledstate;
|
||||
int origledstate;
|
||||
int shiftstate[4];
|
||||
int shiftheldstate[8];
|
||||
int lockheldstate[5];
|
||||
kbd_t encoding;
|
||||
char text[128];
|
||||
unsigned int text_len;
|
||||
keysym_t composebuffer[2];
|
||||
unsigned char composelen;
|
||||
int type;
|
||||
} SDL_WSCONS_input_data;
|
||||
|
||||
static SDL_WSCONS_input_data *inputs[4] = { NULL, NULL, NULL, NULL };
|
||||
static SDL_WSCONS_mouse_input_data *mouseInputData = NULL;
|
||||
#define IS_CONTROL_HELD (input->shiftstate[2] > 0)
|
||||
#define IS_ALT_HELD (input->shiftstate[1] > 0)
|
||||
#define IS_SHIFT_HELD ((input->shiftstate[0] > 0) || (input->ledstate & (1 << 5)))
|
||||
|
||||
#define IS_ALTGR_MODE ((input->ledstate & (1 << 4)) || (input->shiftstate[3] > 0))
|
||||
#define IS_NUMLOCK_ON (input->ledstate & LED_NUM)
|
||||
#define IS_SCROLLLOCK_ON (input->ledstate & LED_SCR)
|
||||
#define IS_CAPSLOCK_ON (input->ledstate & LED_CAP)
|
||||
static SDL_WSCONS_input_data *SDL_WSCONS_Init_Keyboard(const char *dev)
|
||||
{
|
||||
#ifdef WSKBDIO_SETVERSION
|
||||
int version = WSKBDIO_EVENT_VERSION;
|
||||
#endif
|
||||
SDL_WSCONS_input_data *input = (SDL_WSCONS_input_data *)SDL_calloc(1, sizeof(SDL_WSCONS_input_data));
|
||||
|
||||
if (!input) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
input->fd = open(dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||
if (input->fd == -1) {
|
||||
SDL_free(input);
|
||||
input = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
input->keyboardID = SDL_GetNextObjectID();
|
||||
SDL_AddKeyboard(input->keyboardID, NULL);
|
||||
|
||||
input->keymap.map = SDL_calloc(KS_NUMKEYCODES, sizeof(struct wscons_keymap));
|
||||
if (!input->keymap.map) {
|
||||
SDL_free(input);
|
||||
return NULL;
|
||||
}
|
||||
input->keymap.maplen = KS_NUMKEYCODES;
|
||||
RETIFIOCTLERR(ioctl(input->fd, WSKBDIO_GETMAP, &input->keymap));
|
||||
RETIFIOCTLERR(ioctl(input->fd, WSKBDIO_GETLEDS, &input->ledstate));
|
||||
input->origledstate = input->ledstate;
|
||||
RETIFIOCTLERR(ioctl(input->fd, WSKBDIO_GETENCODING, &input->encoding));
|
||||
RETIFIOCTLERR(ioctl(input->fd, WSKBDIO_GTYPE, &input->type));
|
||||
#ifdef WSKBDIO_SETVERSION
|
||||
RETIFIOCTLERR(ioctl(input->fd, WSKBDIO_SETVERSION, &version));
|
||||
#endif
|
||||
return input;
|
||||
}
|
||||
|
||||
void SDL_WSCONS_Init(void)
|
||||
{
|
||||
inputs[0] = SDL_WSCONS_Init_Keyboard("/dev/wskbd0");
|
||||
inputs[1] = SDL_WSCONS_Init_Keyboard("/dev/wskbd1");
|
||||
inputs[2] = SDL_WSCONS_Init_Keyboard("/dev/wskbd2");
|
||||
inputs[3] = SDL_WSCONS_Init_Keyboard("/dev/wskbd3");
|
||||
|
||||
mouseInputData = SDL_WSCONS_Init_Mouse();
|
||||
return;
|
||||
}
|
||||
|
||||
void SDL_WSCONS_Quit(void)
|
||||
{
|
||||
int i = 0;
|
||||
SDL_WSCONS_input_data *input = NULL;
|
||||
|
||||
SDL_WSCONS_Quit_Mouse(mouseInputData);
|
||||
mouseInputData = NULL;
|
||||
for (i = 0; i < 4; i++) {
|
||||
input = inputs[i];
|
||||
if (input) {
|
||||
if (input->fd != -1 && input->fd != 0) {
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->origledstate);
|
||||
close(input->fd);
|
||||
input->fd = -1;
|
||||
}
|
||||
SDL_free(input);
|
||||
input = NULL;
|
||||
}
|
||||
inputs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void put_queue(SDL_WSCONS_input_data *kbd, uint c)
|
||||
{
|
||||
// c is already part of a UTF-8 sequence and safe to add as a character
|
||||
if (kbd->text_len < (sizeof(kbd->text) - 1)) {
|
||||
kbd->text[kbd->text_len++] = (char)(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_utf8(SDL_WSCONS_input_data *input, uint c)
|
||||
{
|
||||
if (c < 0x80)
|
||||
/* 0******* */
|
||||
put_queue(input, c);
|
||||
else if (c < 0x800) {
|
||||
/* 110***** 10****** */
|
||||
put_queue(input, 0xc0 | (c >> 6));
|
||||
put_queue(input, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x10000) {
|
||||
if (c >= 0xD800 && c <= 0xF500) {
|
||||
return;
|
||||
}
|
||||
if (c == 0xFFFF) {
|
||||
return;
|
||||
}
|
||||
/* 1110**** 10****** 10****** */
|
||||
put_queue(input, 0xe0 | (c >> 12));
|
||||
put_queue(input, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(input, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x110000) {
|
||||
/* 11110*** 10****** 10****** 10****** */
|
||||
put_queue(input, 0xf0 | (c >> 18));
|
||||
put_queue(input, 0x80 | ((c >> 12) & 0x3f));
|
||||
put_queue(input, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(input, 0x80 | (c & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
static void Translate_to_text(SDL_WSCONS_input_data *input, keysym_t ksym)
|
||||
{
|
||||
if (KS_GROUP(ksym) == KS_GROUP_Keypad) {
|
||||
if (SDL_isprint(ksym & 0xFF)) {
|
||||
ksym &= 0xFF;
|
||||
}
|
||||
}
|
||||
switch (ksym) {
|
||||
case KS_Escape:
|
||||
case KS_Delete:
|
||||
case KS_BackSpace:
|
||||
case KS_Return:
|
||||
case KS_Linefeed:
|
||||
// All of these are unprintable characters. Ignore them
|
||||
break;
|
||||
default:
|
||||
put_utf8(input, ksym);
|
||||
break;
|
||||
}
|
||||
if (input->text_len > 0) {
|
||||
input->text[input->text_len] = '\0';
|
||||
SDL_SendKeyboardText(input->text);
|
||||
// SDL_memset(input->text, 0, sizeof(input->text));
|
||||
input->text_len = 0;
|
||||
input->text[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_t ksym, Uint64 timestamp)
|
||||
{
|
||||
struct wscons_keymap keyDesc = input->keymap.map[ksym];
|
||||
keysym_t *group = &keyDesc.group1[KS_GROUP(keyDesc.group1[0]) == KS_GROUP_Keypad && IS_NUMLOCK_ON ? !IS_SHIFT_HELD : 0];
|
||||
int i = 0;
|
||||
|
||||
// Check command first, then group[0]
|
||||
switch (keyDesc.command) {
|
||||
case KS_Cmd_ScrollBack:
|
||||
{
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, (type == WSCONS_EVENT_KEY_DOWN));
|
||||
return;
|
||||
}
|
||||
case KS_Cmd_ScrollFwd:
|
||||
{
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, (type == WSCONS_EVENT_KEY_DOWN));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < SDL_arraysize(conversion_table); i++) {
|
||||
if (conversion_table[i].sourcekey == group[0]) {
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN));
|
||||
return;
|
||||
}
|
||||
}
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN));
|
||||
}
|
||||
|
||||
static Uint64 GetEventTimestamp(struct timespec *time)
|
||||
{
|
||||
// FIXME: Get the event time in the SDL tick time base
|
||||
return SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
static void updateKeyboard(SDL_WSCONS_input_data *input)
|
||||
{
|
||||
struct wscons_event events[64];
|
||||
int type;
|
||||
int n, i, gindex, acc_i;
|
||||
keysym_t *group;
|
||||
keysym_t ksym, result;
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
if ((n = read(input->fd, events, sizeof(events))) > 0) {
|
||||
n /= sizeof(struct wscons_event);
|
||||
for (i = 0; i < n; i++) {
|
||||
Uint64 timestamp = GetEventTimestamp(&events[i].time);
|
||||
type = events[i].type;
|
||||
switch (type) {
|
||||
case WSCONS_EVENT_KEY_DOWN:
|
||||
{
|
||||
switch (input->keymap.map[events[i].value].group1[0]) {
|
||||
case KS_Hold_Screen:
|
||||
{
|
||||
if (input->lockheldstate[0] >= 1) {
|
||||
break;
|
||||
}
|
||||
input->ledstate ^= LED_SCR;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->lockheldstate[0] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Num_Lock:
|
||||
{
|
||||
if (input->lockheldstate[1] >= 1) {
|
||||
break;
|
||||
}
|
||||
input->ledstate ^= LED_NUM;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->lockheldstate[1] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Caps_Lock:
|
||||
{
|
||||
if (input->lockheldstate[2] >= 1) {
|
||||
break;
|
||||
}
|
||||
input->ledstate ^= LED_CAP;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->lockheldstate[2] = 1;
|
||||
break;
|
||||
}
|
||||
#ifndef SDL_PLATFORM_NETBSD
|
||||
case KS_Mode_Lock:
|
||||
{
|
||||
if (input->lockheldstate[3] >= 1) {
|
||||
break;
|
||||
}
|
||||
input->ledstate ^= 1 << 4;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->lockheldstate[3] = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case KS_Shift_Lock:
|
||||
{
|
||||
if (input->lockheldstate[4] >= 1) {
|
||||
break;
|
||||
}
|
||||
input->ledstate ^= 1 << 5;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->lockheldstate[4] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Shift_L:
|
||||
{
|
||||
if (input->shiftheldstate[0]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[0]++;
|
||||
input->shiftheldstate[0] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Shift_R:
|
||||
{
|
||||
if (input->shiftheldstate[1]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[0]++;
|
||||
input->shiftheldstate[1] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Alt_L:
|
||||
{
|
||||
if (input->shiftheldstate[2]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[1]++;
|
||||
input->shiftheldstate[2] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Alt_R:
|
||||
{
|
||||
if (input->shiftheldstate[3]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[1]++;
|
||||
input->shiftheldstate[3] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Control_L:
|
||||
{
|
||||
if (input->shiftheldstate[4]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[2]++;
|
||||
input->shiftheldstate[4] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Control_R:
|
||||
{
|
||||
if (input->shiftheldstate[5]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[2]++;
|
||||
input->shiftheldstate[5] = 1;
|
||||
break;
|
||||
}
|
||||
case KS_Mode_switch:
|
||||
{
|
||||
if (input->shiftheldstate[6]) {
|
||||
break;
|
||||
}
|
||||
input->shiftstate[3]++;
|
||||
input->shiftheldstate[6] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WSCONS_EVENT_KEY_UP:
|
||||
{
|
||||
switch (input->keymap.map[events[i].value].group1[0]) {
|
||||
case KS_Hold_Screen:
|
||||
{
|
||||
if (input->lockheldstate[0]) {
|
||||
input->lockheldstate[0] = 0;
|
||||
}
|
||||
} break;
|
||||
case KS_Num_Lock:
|
||||
{
|
||||
if (input->lockheldstate[1]) {
|
||||
input->lockheldstate[1] = 0;
|
||||
}
|
||||
} break;
|
||||
case KS_Caps_Lock:
|
||||
{
|
||||
if (input->lockheldstate[2]) {
|
||||
input->lockheldstate[2] = 0;
|
||||
}
|
||||
} break;
|
||||
#ifndef SDL_PLATFORM_NETBSD
|
||||
case KS_Mode_Lock:
|
||||
{
|
||||
if (input->lockheldstate[3]) {
|
||||
input->lockheldstate[3] = 0;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
case KS_Shift_Lock:
|
||||
{
|
||||
if (input->lockheldstate[4]) {
|
||||
input->lockheldstate[4] = 0;
|
||||
}
|
||||
} break;
|
||||
case KS_Shift_L:
|
||||
{
|
||||
input->shiftheldstate[0] = 0;
|
||||
if (input->shiftstate[0]) {
|
||||
input->shiftstate[0]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Shift_R:
|
||||
{
|
||||
input->shiftheldstate[1] = 0;
|
||||
if (input->shiftstate[0]) {
|
||||
input->shiftstate[0]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Alt_L:
|
||||
{
|
||||
input->shiftheldstate[2] = 0;
|
||||
if (input->shiftstate[1]) {
|
||||
input->shiftstate[1]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Alt_R:
|
||||
{
|
||||
input->shiftheldstate[3] = 0;
|
||||
if (input->shiftstate[1]) {
|
||||
input->shiftstate[1]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Control_L:
|
||||
{
|
||||
input->shiftheldstate[4] = 0;
|
||||
if (input->shiftstate[2]) {
|
||||
input->shiftstate[2]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Control_R:
|
||||
{
|
||||
input->shiftheldstate[5] = 0;
|
||||
if (input->shiftstate[2]) {
|
||||
input->shiftstate[2]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KS_Mode_switch:
|
||||
{
|
||||
input->shiftheldstate[6] = 0;
|
||||
if (input->shiftstate[3]) {
|
||||
input->shiftstate[3]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WSCONS_EVENT_ALL_KEYS_UP:
|
||||
for (i = 0; i < SDL_SCANCODE_COUNT; i++) {
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)i, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7) {
|
||||
SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN));
|
||||
} else {
|
||||
Translate_to_keycode(input, type, events[i].value, timestamp);
|
||||
}
|
||||
|
||||
if (type == WSCONS_EVENT_KEY_UP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IS_ALTGR_MODE && !IS_CONTROL_HELD)
|
||||
group = &input->keymap.map[events[i].value].group2[0];
|
||||
else
|
||||
group = &input->keymap.map[events[i].value].group1[0];
|
||||
|
||||
if (IS_NUMLOCK_ON && KS_GROUP(group[1]) == KS_GROUP_Keypad) {
|
||||
gindex = !IS_SHIFT_HELD;
|
||||
ksym = group[gindex];
|
||||
} else {
|
||||
if (IS_CAPSLOCK_ON && !IS_SHIFT_HELD) {
|
||||
gindex = 0;
|
||||
ksym = ksym_upcase(group[0]);
|
||||
} else {
|
||||
gindex = IS_SHIFT_HELD;
|
||||
ksym = group[gindex];
|
||||
}
|
||||
}
|
||||
result = KS_voidSymbol;
|
||||
|
||||
switch (KS_GROUP(ksym)) {
|
||||
case KS_GROUP_Ascii:
|
||||
case KS_GROUP_Keypad:
|
||||
case KS_GROUP_Function:
|
||||
result = ksym;
|
||||
break;
|
||||
case KS_GROUP_Mod:
|
||||
if (ksym == KS_Multi_key) {
|
||||
input->ledstate |= WSKBD_LED_COMPOSE;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->composelen = 2;
|
||||
input->composebuffer[0] = input->composebuffer[1] = 0;
|
||||
}
|
||||
break;
|
||||
case KS_GROUP_Dead:
|
||||
if (input->composelen == 0) {
|
||||
input->ledstate |= WSKBD_LED_COMPOSE;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
input->composelen = 1;
|
||||
input->composebuffer[0] = ksym;
|
||||
input->composebuffer[1] = 0;
|
||||
} else
|
||||
result = ksym;
|
||||
break;
|
||||
}
|
||||
if (result == KS_voidSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input->composelen > 0) {
|
||||
if (input->composelen == 2 && group == &input->keymap.map[events[i].value].group2[0]) {
|
||||
if (input->keymap.map[events[i].value].group2[gindex] == input->keymap.map[events[i].value].group1[gindex]) {
|
||||
input->composelen = 0;
|
||||
input->composebuffer[0] = input->composebuffer[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (input->composelen != 0) {
|
||||
input->composebuffer[2 - input->composelen] = result;
|
||||
if (--input->composelen == 0) {
|
||||
result = KS_voidSymbol;
|
||||
input->ledstate &= ~WSKBD_LED_COMPOSE;
|
||||
ioctl(input->fd, WSKBDIO_SETLEDS, &input->ledstate);
|
||||
for (acc_i = 0; acc_i < SDL_arraysize(compose_tab); acc_i++) {
|
||||
if ((compose_tab[acc_i].elem[0] == input->composebuffer[0] && compose_tab[acc_i].elem[1] == input->composebuffer[1]) || (compose_tab[acc_i].elem[0] == input->composebuffer[1] && compose_tab[acc_i].elem[1] == input->composebuffer[0])) {
|
||||
result = compose_tab[acc_i].result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (KS_GROUP(result) == KS_GROUP_Ascii) {
|
||||
if (IS_CONTROL_HELD) {
|
||||
if ((result >= KS_at && result <= KS_z) || result == KS_space) {
|
||||
result = result & 0x1f;
|
||||
} else if (result == KS_2) {
|
||||
result = 0x00;
|
||||
} else if (result >= KS_3 && result <= KS_7) {
|
||||
result = KS_Escape + (result - KS_3);
|
||||
} else if (result == KS_8) {
|
||||
result = KS_Delete;
|
||||
}
|
||||
}
|
||||
if (IS_ALT_HELD) {
|
||||
if (input->encoding & KB_METAESC) {
|
||||
Translate_to_keycode(input, WSCONS_EVENT_KEY_DOWN, KS_Escape, 0);
|
||||
Translate_to_text(input, result);
|
||||
continue;
|
||||
} else {
|
||||
result |= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
Translate_to_text(input, result);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WSCONS_PumpEvents(void)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
updateKeyboard(inputs[i]);
|
||||
}
|
||||
if (mouseInputData) {
|
||||
updateMouse(mouseInputData);
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
#include <sys/time.h>
|
||||
#include <dev/wscons/wsconsio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
|
||||
typedef struct SDL_WSCONS_mouse_input_data
|
||||
{
|
||||
int fd;
|
||||
SDL_MouseID mouseID;
|
||||
} SDL_WSCONS_mouse_input_data;
|
||||
|
||||
SDL_WSCONS_mouse_input_data *SDL_WSCONS_Init_Mouse(void)
|
||||
{
|
||||
#ifdef WSMOUSEIO_SETVERSION
|
||||
int version = WSMOUSE_EVENT_VERSION;
|
||||
#endif
|
||||
SDL_WSCONS_mouse_input_data *input = SDL_calloc(1, sizeof(SDL_WSCONS_mouse_input_data));
|
||||
|
||||
if (!input) {
|
||||
return NULL;
|
||||
}
|
||||
input->fd = open("/dev/wsmouse", O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||
if (input->fd == -1) {
|
||||
SDL_free(input);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
input->mouseID = SDL_GetNextObjectID();
|
||||
SDL_AddMouse(input->mouseID, NULL);
|
||||
|
||||
#ifdef WSMOUSEIO_SETMODE
|
||||
ioctl(input->fd, WSMOUSEIO_SETMODE, WSMOUSE_COMPAT);
|
||||
#endif
|
||||
#ifdef WSMOUSEIO_SETVERSION
|
||||
ioctl(input->fd, WSMOUSEIO_SETVERSION, &version);
|
||||
#endif
|
||||
return input;
|
||||
}
|
||||
|
||||
static Uint64 GetEventTimestamp(struct timespec *time)
|
||||
{
|
||||
// FIXME: Get the event time in the SDL tick time base
|
||||
return SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
void updateMouse(SDL_WSCONS_mouse_input_data *input)
|
||||
{
|
||||
struct wscons_event events[64];
|
||||
int n;
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
|
||||
if ((n = read(input->fd, events, sizeof(events))) > 0) {
|
||||
int i;
|
||||
n /= sizeof(struct wscons_event);
|
||||
for (i = 0; i < n; i++) {
|
||||
Uint64 timestamp = GetEventTimestamp(&events[i].time);
|
||||
int type = events[i].type;
|
||||
switch (type) {
|
||||
case WSCONS_EVENT_MOUSE_DOWN:
|
||||
case WSCONS_EVENT_MOUSE_UP:
|
||||
{
|
||||
Uint8 button = SDL_BUTTON_LEFT + events[i].value;
|
||||
bool down = (type == WSCONS_EVENT_MOUSE_DOWN);
|
||||
SDL_SendMouseButton(timestamp, mouse->focus, input->mouseID, button, down);
|
||||
break;
|
||||
}
|
||||
case WSCONS_EVENT_MOUSE_DELTA_X:
|
||||
{
|
||||
SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, (float)events[i].value, 0.0f);
|
||||
break;
|
||||
}
|
||||
case WSCONS_EVENT_MOUSE_DELTA_Y:
|
||||
{
|
||||
SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, 0.0f, -(float)events[i].value);
|
||||
break;
|
||||
}
|
||||
case WSCONS_EVENT_MOUSE_DELTA_W:
|
||||
{
|
||||
SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
|
||||
break;
|
||||
}
|
||||
case WSCONS_EVENT_MOUSE_DELTA_Z:
|
||||
{
|
||||
SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WSCONS_Quit_Mouse(SDL_WSCONS_mouse_input_data *input)
|
||||
{
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
close(input->fd);
|
||||
SDL_free(input);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_appid.h"
|
||||
#include <unistd.h>
|
||||
|
||||
const char *SDL_GetExeName(void)
|
||||
{
|
||||
static const char *proc_name = NULL;
|
||||
|
||||
// TODO: Use a fallback if BSD has no mounted procfs (OpenBSD has no procfs at all)
|
||||
if (!proc_name) {
|
||||
#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_HURD)
|
||||
static char linkfile[1024];
|
||||
int linksize;
|
||||
|
||||
#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_HURD)
|
||||
const char *proc_path = "/proc/self/exe";
|
||||
#elif defined(SDL_PLATFORM_FREEBSD)
|
||||
const char *proc_path = "/proc/curproc/file";
|
||||
#elif defined(SDL_PLATFORM_NETBSD)
|
||||
const char *proc_path = "/proc/curproc/exe";
|
||||
#endif
|
||||
linksize = readlink(proc_path, linkfile, sizeof(linkfile) - 1);
|
||||
if (linksize > 0) {
|
||||
linkfile[linksize] = '\0';
|
||||
proc_name = SDL_strrchr(linkfile, '/');
|
||||
if (proc_name) {
|
||||
++proc_name;
|
||||
} else {
|
||||
proc_name = linkfile;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return proc_name;
|
||||
}
|
||||
|
||||
const char *SDL_GetAppID(void)
|
||||
{
|
||||
const char *id_str = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING);
|
||||
|
||||
#ifdef SDL_PLATFORM_LINUX
|
||||
if (!id_str) {
|
||||
id_str = SDL_getenv("FLATPAK_ID");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!id_str) {
|
||||
// If the hint isn't set, try to use the application's executable name
|
||||
id_str = SDL_GetExeName();
|
||||
}
|
||||
|
||||
if (!id_str) {
|
||||
// Finally, use the default we've used forever
|
||||
id_str = "SDL_App";
|
||||
}
|
||||
|
||||
return id_str;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_appid_h_
|
||||
#define SDL_appid_h_
|
||||
|
||||
extern const char *SDL_GetExeName(void);
|
||||
extern const char *SDL_GetAppID(void);
|
||||
|
||||
#endif // SDL_appid_h_
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef HAVE_FRIBIDI_H
|
||||
|
||||
#include "SDL_fribidi.h"
|
||||
|
||||
#ifdef SDL_FRIBIDI_DYNAMIC
|
||||
SDL_ELF_NOTE_DLOPEN(
|
||||
"fribidi",
|
||||
"Bidirectional text support",
|
||||
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
|
||||
SDL_FRIBIDI_DYNAMIC
|
||||
)
|
||||
#endif
|
||||
|
||||
SDL_FriBidi *SDL_FriBidi_Create(void)
|
||||
{
|
||||
SDL_FriBidi *fribidi;
|
||||
|
||||
fribidi = (SDL_FriBidi *)SDL_malloc(sizeof(SDL_FriBidi));
|
||||
if (!fribidi) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SDL_FRIBIDI_DYNAMIC
|
||||
#define SDL_FRIBIDI_LOAD_SYM(x, n, t) x = ((t)SDL_LoadFunction(fribidi->lib, n)); if (!x) { SDL_UnloadObject(fribidi->lib); SDL_free(fribidi); return NULL; }
|
||||
|
||||
fribidi->lib = SDL_LoadObject(SDL_FRIBIDI_DYNAMIC);
|
||||
if (!fribidi->lib) {
|
||||
SDL_free(fribidi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->unicode_to_charset, "fribidi_unicode_to_charset", SDL_FriBidiUnicodeToCharset);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->charset_to_unicode, "fribidi_charset_to_unicode", SDL_FriBidiCharsetToUnicode);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->get_bidi_types, "fribidi_get_bidi_types", SDL_FriBidiGetBidiTypes);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->get_par_direction, "fribidi_get_par_direction", SDL_FriBidiGetParDirection);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->get_par_embedding_levels, "fribidi_get_par_embedding_levels", SDL_FriBidiGetParEmbeddingLevels);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->get_joining_types, "fribidi_get_joining_types", SDL_FriBidiGetJoiningTypes);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->join_arabic, "fribidi_join_arabic", SDL_FriBidiJoinArabic);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->shape, "fribidi_shape", SDL_FriBidiShape);
|
||||
SDL_FRIBIDI_LOAD_SYM(fribidi->reorder_line, "fribidi_reorder_line", SDL_FriBidiReorderLine);
|
||||
#else
|
||||
fribidi->unicode_to_charset = fribidi_unicode_to_charset;
|
||||
fribidi->charset_to_unicode = fribidi_charset_to_unicode;
|
||||
fribidi->get_bidi_types = fribidi_get_bidi_types;
|
||||
fribidi->get_par_direction = fribidi_get_par_direction;
|
||||
fribidi->get_par_embedding_levels = fribidi_get_par_embedding_levels;
|
||||
fribidi->get_joining_types = fribidi_get_joining_types;
|
||||
fribidi->join_arabic = fribidi_join_arabic;
|
||||
fribidi->shape = fribidi_shape;
|
||||
fribidi->reorder_line = fribidi_reorder_line;
|
||||
#endif
|
||||
|
||||
return fribidi;
|
||||
}
|
||||
|
||||
char *SDL_FriBidi_Process(SDL_FriBidi *fribidi, char *utf8, ssize_t utf8_len, bool shaping, FriBidiParType *out_par_type)
|
||||
{
|
||||
FriBidiCharType *types;
|
||||
FriBidiLevel *levels;
|
||||
FriBidiArabicProp *props;
|
||||
FriBidiChar *str;
|
||||
char *result;
|
||||
FriBidiStrIndex len;
|
||||
FriBidiLevel max_level;
|
||||
FriBidiLevel start;
|
||||
FriBidiLevel end;
|
||||
FriBidiParType direction;
|
||||
FriBidiParType str_direction;
|
||||
unsigned int i;
|
||||
unsigned int c;
|
||||
|
||||
if (!fribidi || !utf8) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert to UTF32 */
|
||||
if (utf8_len < 0) {
|
||||
utf8_len = SDL_strlen(utf8);
|
||||
}
|
||||
str = SDL_calloc(SDL_utf8strnlen(utf8, utf8_len), sizeof(FriBidiChar));
|
||||
len = fribidi->charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, utf8, utf8_len, str);
|
||||
|
||||
/* Setup various BIDI structures */
|
||||
direction = FRIBIDI_PAR_LTR;
|
||||
types = NULL;
|
||||
levels = NULL;
|
||||
props = SDL_calloc(len + 1, sizeof(FriBidiArabicProp));
|
||||
levels = SDL_calloc(len + 1, sizeof(FriBidiLevel));
|
||||
types = SDL_calloc(len + 1, sizeof(FriBidiCharType));
|
||||
|
||||
/* Shape */
|
||||
fribidi->get_bidi_types(str, len, types);
|
||||
str_direction = fribidi->get_par_direction(types, len);
|
||||
max_level = fribidi->get_par_embedding_levels(types, len, &direction, levels);
|
||||
if (shaping) {
|
||||
fribidi->get_joining_types(str, len, props);
|
||||
fribidi->join_arabic(types, len, levels, props);
|
||||
fribidi->shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, levels, len, props, str);
|
||||
}
|
||||
|
||||
/* BIDI */
|
||||
for (end = 0, start = 0; end < len; end++) {
|
||||
if (str[end] == '\n' || str[end] == '\r' || str[end] == '\f' || str[end] == '\v' || end == len - 1) {
|
||||
max_level = fribidi->reorder_line(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, types, end - start + 1, start, direction, levels, str, NULL);
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Silence warning */
|
||||
(void)max_level;
|
||||
|
||||
/* Remove fillers */
|
||||
for (i = 0, c = 0; i < len; i++) {
|
||||
if (str[i] != FRIBIDI_CHAR_FILL) {
|
||||
str[c++] = str[i];
|
||||
}
|
||||
}
|
||||
len = c;
|
||||
|
||||
/* Convert back to UTF8 */
|
||||
result = SDL_malloc(len * 4 + 1);
|
||||
fribidi->unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, str, len, result);
|
||||
|
||||
/* Cleanup */
|
||||
SDL_free(levels);
|
||||
SDL_free(props);
|
||||
SDL_free(types);
|
||||
|
||||
/* Return */
|
||||
if (out_par_type) {
|
||||
*out_par_type = str_direction;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDL_FriBidi_Destroy(SDL_FriBidi *fribidi)
|
||||
{
|
||||
if (!fribidi) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SDL_FRIBIDI_DYNAMIC
|
||||
SDL_UnloadObject(fribidi->lib);
|
||||
#endif
|
||||
|
||||
SDL_free(fribidi);
|
||||
}
|
||||
|
||||
#endif
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_fribidi_h_
|
||||
#define SDL_fribidi_h_
|
||||
|
||||
#ifdef HAVE_FRIBIDI_H
|
||||
#include <sys/types.h> // for ssize_t
|
||||
#include <fribidi.h>
|
||||
|
||||
typedef FriBidiStrIndex (*SDL_FriBidiUnicodeToCharset)(FriBidiCharSet, const FriBidiChar *, FriBidiStrIndex, char *);
|
||||
typedef FriBidiStrIndex (*SDL_FriBidiCharsetToUnicode)(FriBidiCharSet, const char *, FriBidiStrIndex, FriBidiChar *);
|
||||
typedef void (*SDL_FriBidiGetBidiTypes)(const FriBidiChar *, const FriBidiStrIndex, FriBidiCharType *);
|
||||
typedef FriBidiParType (*SDL_FriBidiGetParDirection)(const FriBidiCharType *, const FriBidiStrIndex);
|
||||
typedef FriBidiLevel (*SDL_FriBidiGetParEmbeddingLevels)(const FriBidiCharType *, const FriBidiStrIndex, FriBidiParType *, FriBidiLevel *);
|
||||
typedef void (*SDL_FriBidiGetJoiningTypes)(const FriBidiChar *, const FriBidiStrIndex, FriBidiJoiningType *);
|
||||
typedef void (*SDL_FriBidiJoinArabic)(const FriBidiCharType *, const FriBidiStrIndex, const FriBidiLevel *, FriBidiArabicProp *);
|
||||
typedef void (*SDL_FriBidiShape)(FriBidiFlags flags, const FriBidiLevel *, const FriBidiStrIndex, FriBidiArabicProp *, FriBidiChar *str);
|
||||
typedef FriBidiLevel (*SDL_FriBidiReorderLine)(FriBidiFlags flags, const FriBidiCharType *, const FriBidiStrIndex, const FriBidiStrIndex, const FriBidiParType, FriBidiLevel *, FriBidiChar *, FriBidiStrIndex *);
|
||||
|
||||
typedef struct SDL_FriBidi {
|
||||
SDL_SharedObject *lib;
|
||||
SDL_FriBidiUnicodeToCharset unicode_to_charset;
|
||||
SDL_FriBidiCharsetToUnicode charset_to_unicode;
|
||||
SDL_FriBidiGetBidiTypes get_bidi_types;
|
||||
SDL_FriBidiGetParDirection get_par_direction;
|
||||
SDL_FriBidiGetParEmbeddingLevels get_par_embedding_levels;
|
||||
SDL_FriBidiGetJoiningTypes get_joining_types;
|
||||
SDL_FriBidiJoinArabic join_arabic;
|
||||
SDL_FriBidiShape shape;
|
||||
SDL_FriBidiReorderLine reorder_line;
|
||||
} SDL_FriBidi;
|
||||
|
||||
extern SDL_FriBidi *SDL_FriBidi_Create(void);
|
||||
extern char *SDL_FriBidi_Process(SDL_FriBidi *fribidi, char *utf8, ssize_t utf8_len, bool shaping, FriBidiParType *out_par_type);
|
||||
extern void SDL_FriBidi_Destroy(SDL_FriBidi *fribidi);
|
||||
|
||||
#endif // HAVE_FRIBIDI_H
|
||||
|
||||
#endif // SDL_fribidi_h_
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef HAVE_LIBTHAI_H
|
||||
|
||||
#include "SDL_libthai.h"
|
||||
|
||||
#ifdef SDL_LIBTHAI_DYNAMIC
|
||||
SDL_ELF_NOTE_DLOPEN(
|
||||
"Thai",
|
||||
"Thai language support",
|
||||
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
|
||||
SDL_LIBTHAI_DYNAMIC
|
||||
)
|
||||
#endif
|
||||
|
||||
|
||||
SDL_LibThai *SDL_LibThai_Create(void)
|
||||
{
|
||||
SDL_LibThai *th;
|
||||
|
||||
th = (SDL_LibThai *)SDL_malloc(sizeof(SDL_LibThai));
|
||||
if (!th) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SDL_LIBTHAI_DYNAMIC
|
||||
#define SDL_LIBTHAI_LOAD_SYM(a, x, n, t) x = ((t)SDL_LoadFunction(a->lib, n)); if (!x) { SDL_UnloadObject(a->lib); SDL_free(a); return NULL; }
|
||||
|
||||
th->lib = SDL_LoadObject(SDL_LIBTHAI_DYNAMIC);
|
||||
if (!th->lib) {
|
||||
SDL_free(th);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_LIBTHAI_LOAD_SYM(th, th->make_cells, "th_make_cells", SDL_LibThaiMakeCells);
|
||||
#else
|
||||
th->make_cells = th_make_cells;
|
||||
#endif
|
||||
|
||||
return th;
|
||||
}
|
||||
|
||||
void SDL_LibThai_Destroy(SDL_LibThai *th)
|
||||
{
|
||||
if (!th) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SDL_LIBTHAI_DYNAMIC
|
||||
SDL_UnloadObject(th->lib);
|
||||
#endif
|
||||
|
||||
SDL_free(th);
|
||||
}
|
||||
|
||||
#endif
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_libthai_h_
|
||||
#define SDL_libthai_h_
|
||||
|
||||
#ifdef HAVE_LIBTHAI_H
|
||||
#include <thai/thcell.h>
|
||||
|
||||
typedef size_t (*SDL_LibThaiMakeCells)(const thchar_t *s, size_t, struct thcell_t cells[], size_t *, int);
|
||||
|
||||
typedef struct SDL_LibThai {
|
||||
SDL_SharedObject *lib;
|
||||
|
||||
SDL_LibThaiMakeCells make_cells;
|
||||
} SDL_LibThai;
|
||||
|
||||
extern SDL_LibThai *SDL_LibThai_Create(void);
|
||||
extern void SDL_LibThai_Destroy(SDL_LibThai *th);
|
||||
|
||||
#endif // HAVE_LIBTHAI_H
|
||||
|
||||
#endif // SDL_libthai_h_
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_poll.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
int SDL_IOReady(int fd, int flags, Sint64 timeoutNS)
|
||||
{
|
||||
int result;
|
||||
|
||||
SDL_assert(flags & (SDL_IOR_READ | SDL_IOR_WRITE));
|
||||
|
||||
// Note: We don't bother to account for elapsed time if we get EINTR
|
||||
do {
|
||||
struct pollfd info;
|
||||
|
||||
info.fd = fd;
|
||||
info.events = 0;
|
||||
if (flags & SDL_IOR_READ) {
|
||||
info.events |= POLLIN | POLLPRI;
|
||||
}
|
||||
if (flags & SDL_IOR_WRITE) {
|
||||
info.events |= POLLOUT;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
struct timespec *timeout = NULL;
|
||||
struct timespec ts;
|
||||
|
||||
if (timeoutNS >= 0) {
|
||||
ts.tv_sec = SDL_NS_TO_SECONDS(timeoutNS);
|
||||
ts.tv_nsec = timeoutNS - SDL_SECONDS_TO_NS(ts.tv_sec);
|
||||
timeout = &ts;
|
||||
}
|
||||
|
||||
result = ppoll(&info, 1, timeout, NULL);
|
||||
#else
|
||||
int timeoutMS;
|
||||
|
||||
if (timeoutNS > 0) {
|
||||
timeoutMS = (int)SDL_NS_TO_MS(timeoutNS + (SDL_NS_PER_MS - 1));
|
||||
} else if (timeoutNS == 0) {
|
||||
timeoutMS = 0;
|
||||
} else {
|
||||
timeoutMS = -1;
|
||||
}
|
||||
result = poll(&info, 1, timeoutMS);
|
||||
#endif
|
||||
} while (result < 0 && errno == EINTR && !(flags & SDL_IOR_NO_RETRY));
|
||||
|
||||
return result;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_poll_h_
|
||||
#define SDL_poll_h_
|
||||
|
||||
#define SDL_IOR_READ 0x1
|
||||
#define SDL_IOR_WRITE 0x2
|
||||
#define SDL_IOR_NO_RETRY 0x4
|
||||
|
||||
extern int SDL_IOReady(int fd, int flags, Sint64 timeoutNS);
|
||||
|
||||
#endif // SDL_poll_h_
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_directx_h_
|
||||
#define SDL_directx_h_
|
||||
|
||||
// Include all of the DirectX 8.0 headers and adds any necessary tweaks
|
||||
|
||||
#include "SDL_windows.h"
|
||||
#include <mmsystem.h>
|
||||
#ifndef WIN32
|
||||
#define WIN32
|
||||
#endif
|
||||
#undef WINNT
|
||||
|
||||
// Far pointers don't exist in 32-bit code
|
||||
#ifndef FAR
|
||||
#define FAR
|
||||
#endif
|
||||
|
||||
// Error codes not yet included in Win32 API header files
|
||||
#ifndef MAKE_HRESULT
|
||||
#define MAKE_HRESULT(sev, fac, code) \
|
||||
((HRESULT)(((unsigned long)(sev) << 31) | ((unsigned long)(fac) << 16) | ((unsigned long)(code))))
|
||||
#endif
|
||||
|
||||
#ifndef S_OK
|
||||
#define S_OK (HRESULT)0x00000000L
|
||||
#endif
|
||||
|
||||
#ifndef SUCCEEDED
|
||||
#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
|
||||
#endif
|
||||
#ifndef FAILED
|
||||
#define FAILED(x) ((HRESULT)(x) < 0)
|
||||
#endif
|
||||
|
||||
#ifndef E_FAIL
|
||||
#define E_FAIL (HRESULT)0x80000008L
|
||||
#endif
|
||||
#ifndef E_NOINTERFACE
|
||||
#define E_NOINTERFACE (HRESULT)0x80004002L
|
||||
#endif
|
||||
#ifndef E_OUTOFMEMORY
|
||||
#define E_OUTOFMEMORY (HRESULT)0x8007000EL
|
||||
#endif
|
||||
#ifndef E_INVALIDARG
|
||||
#define E_INVALIDARG (HRESULT)0x80070057L
|
||||
#endif
|
||||
#ifndef E_NOTIMPL
|
||||
#define E_NOTIMPL (HRESULT)0x80004001L
|
||||
#endif
|
||||
#ifndef REGDB_E_CLASSNOTREG
|
||||
#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
|
||||
#endif
|
||||
|
||||
// Severity codes
|
||||
#ifndef SEVERITY_ERROR
|
||||
#define SEVERITY_ERROR 1
|
||||
#endif
|
||||
|
||||
// Error facility codes
|
||||
#ifndef FACILITY_WIN32
|
||||
#define FACILITY_WIN32 7
|
||||
#endif
|
||||
|
||||
#ifndef FIELD_OFFSET
|
||||
#define FIELD_OFFSET(type, field) ((LONG) & (((type *)0)->field))
|
||||
#endif
|
||||
|
||||
/* DirectX headers (if it isn't included, I haven't tested it yet)
|
||||
*/
|
||||
// We need these defines to mark what version of DirectX API we use
|
||||
#define DIRECTDRAW_VERSION 0x0700
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#define DIRECTINPUT_VERSION 0x0800 // Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve...
|
||||
|
||||
#ifdef HAVE_DDRAW_H
|
||||
#include <ddraw.h>
|
||||
#endif
|
||||
#ifdef HAVE_DSOUND_H
|
||||
#include <dsound.h>
|
||||
#endif
|
||||
#ifdef HAVE_DINPUT_H
|
||||
#include <dinput.h>
|
||||
#else
|
||||
typedef struct
|
||||
{
|
||||
int unused;
|
||||
} DIDEVICEINSTANCE;
|
||||
#endif
|
||||
|
||||
#endif // SDL_directx_h_
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef HAVE_GAMEINPUT_H
|
||||
|
||||
#include "SDL_windows.h"
|
||||
#include "SDL_gameinput.h"
|
||||
|
||||
static SDL_SharedObject *g_hGameInputDLL;
|
||||
static IGameInput *g_pGameInput;
|
||||
static int g_nGameInputRefCount;
|
||||
|
||||
|
||||
bool SDL_InitGameInput(IGameInput **ppGameInput)
|
||||
{
|
||||
if (g_nGameInputRefCount == 0) {
|
||||
g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
|
||||
if (!g_hGameInputDLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *pfnGameInputCreate)(IGameInput **gameInput);
|
||||
pfnGameInputCreate pGameInputCreate = (pfnGameInputCreate)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
|
||||
if (!pGameInputCreate) {
|
||||
SDL_UnloadObject(g_hGameInputDLL);
|
||||
return false;
|
||||
}
|
||||
|
||||
IGameInput *pGameInput = NULL;
|
||||
HRESULT hr = pGameInputCreate(&pGameInput);
|
||||
if (FAILED(hr)) {
|
||||
SDL_UnloadObject(g_hGameInputDLL);
|
||||
return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
|
||||
}
|
||||
|
||||
#ifdef SDL_PLATFORM_WIN32
|
||||
#if GAMEINPUT_API_VERSION >= 1
|
||||
hr = pGameInput->QueryInterface(IID_IGameInput, (void **)&g_pGameInput);
|
||||
#else
|
||||
// We require GameInput v1.1 or newer
|
||||
hr = E_NOINTERFACE;
|
||||
#endif
|
||||
pGameInput->Release();
|
||||
if (FAILED(hr)) {
|
||||
SDL_UnloadObject(g_hGameInputDLL);
|
||||
return WIN_SetErrorFromHRESULT("GameInput QueryInterface failed", hr);
|
||||
}
|
||||
#else
|
||||
// Assume that the version we get is compatible with the current SDK
|
||||
g_pGameInput = pGameInput;
|
||||
#endif
|
||||
}
|
||||
++g_nGameInputRefCount;
|
||||
|
||||
if (ppGameInput) {
|
||||
*ppGameInput = g_pGameInput;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_QuitGameInput(void)
|
||||
{
|
||||
SDL_assert(g_nGameInputRefCount > 0);
|
||||
|
||||
--g_nGameInputRefCount;
|
||||
if (g_nGameInputRefCount == 0) {
|
||||
if (g_pGameInput) {
|
||||
g_pGameInput->Release();
|
||||
g_pGameInput = NULL;
|
||||
}
|
||||
if (g_hGameInputDLL) {
|
||||
SDL_UnloadObject(g_hGameInputDLL);
|
||||
g_hGameInputDLL = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_GAMEINPUT_H
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_gameinput_h_
|
||||
#define SDL_gameinput_h_
|
||||
|
||||
#ifdef HAVE_GAMEINPUT_H
|
||||
|
||||
#include <gameinput.h>
|
||||
|
||||
#ifndef GAMEINPUT_API_VERSION
|
||||
#define GAMEINPUT_API_VERSION 0
|
||||
#endif
|
||||
|
||||
#if GAMEINPUT_API_VERSION == 3
|
||||
using namespace GameInput::v3;
|
||||
#elif GAMEINPUT_API_VERSION == 2
|
||||
using namespace GameInput::v2;
|
||||
#elif GAMEINPUT_API_VERSION == 1
|
||||
using namespace GameInput::v1;
|
||||
#endif
|
||||
|
||||
extern bool SDL_InitGameInput(IGameInput **ppGameInput);
|
||||
extern void SDL_QuitGameInput(void);
|
||||
|
||||
#endif // HAVE_GAMEINPUT_H
|
||||
|
||||
#endif // SDL_gameinput_h_
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_hid.h"
|
||||
|
||||
HidD_GetAttributes_t SDL_HidD_GetAttributes;
|
||||
HidD_GetString_t SDL_HidD_GetManufacturerString;
|
||||
HidD_GetString_t SDL_HidD_GetProductString;
|
||||
HidP_GetCaps_t SDL_HidP_GetCaps;
|
||||
HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
|
||||
HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
|
||||
HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
|
||||
HidP_GetData_t SDL_HidP_GetData;
|
||||
|
||||
static HMODULE s_pHIDDLL = 0;
|
||||
static int s_HIDDLLRefCount = 0;
|
||||
|
||||
|
||||
bool WIN_LoadHIDDLL(void)
|
||||
{
|
||||
if (s_pHIDDLL) {
|
||||
SDL_assert(s_HIDDLLRefCount > 0);
|
||||
s_HIDDLLRefCount++;
|
||||
return true; // already loaded
|
||||
}
|
||||
|
||||
s_pHIDDLL = LoadLibrary(TEXT("hid.dll"));
|
||||
if (!s_pHIDDLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_assert(s_HIDDLLRefCount == 0);
|
||||
s_HIDDLLRefCount = 1;
|
||||
|
||||
SDL_HidD_GetAttributes = (HidD_GetAttributes_t)GetProcAddress(s_pHIDDLL, "HidD_GetAttributes");
|
||||
SDL_HidD_GetManufacturerString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetManufacturerString");
|
||||
SDL_HidD_GetProductString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetProductString");
|
||||
SDL_HidP_GetCaps = (HidP_GetCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetCaps");
|
||||
SDL_HidP_GetButtonCaps = (HidP_GetButtonCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetButtonCaps");
|
||||
SDL_HidP_GetValueCaps = (HidP_GetValueCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetValueCaps");
|
||||
SDL_HidP_MaxDataListLength = (HidP_MaxDataListLength_t)GetProcAddress(s_pHIDDLL, "HidP_MaxDataListLength");
|
||||
SDL_HidP_GetData = (HidP_GetData_t)GetProcAddress(s_pHIDDLL, "HidP_GetData");
|
||||
if (!SDL_HidD_GetManufacturerString || !SDL_HidD_GetProductString ||
|
||||
!SDL_HidP_GetCaps || !SDL_HidP_GetButtonCaps ||
|
||||
!SDL_HidP_GetValueCaps || !SDL_HidP_MaxDataListLength || !SDL_HidP_GetData) {
|
||||
WIN_UnloadHIDDLL();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WIN_UnloadHIDDLL(void)
|
||||
{
|
||||
if (s_pHIDDLL) {
|
||||
SDL_assert(s_HIDDLLRefCount > 0);
|
||||
if (--s_HIDDLLRefCount == 0) {
|
||||
FreeLibrary(s_pHIDDLL);
|
||||
s_pHIDDLL = NULL;
|
||||
}
|
||||
} else {
|
||||
SDL_assert(s_HIDDLLRefCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
||||
|
||||
// CM_Register_Notification definitions
|
||||
|
||||
#define CR_SUCCESS 0
|
||||
|
||||
DECLARE_HANDLE(HCMNOTIFICATION);
|
||||
typedef HCMNOTIFICATION *PHCMNOTIFICATION;
|
||||
|
||||
typedef enum _CM_NOTIFY_FILTER_TYPE
|
||||
{
|
||||
CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
|
||||
CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
|
||||
CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
|
||||
CM_NOTIFY_FILTER_TYPE_MAX
|
||||
} CM_NOTIFY_FILTER_TYPE, *PCM_NOTIFY_FILTER_TYPE;
|
||||
|
||||
typedef struct _CM_NOTIFY_FILTER
|
||||
{
|
||||
DWORD cbSize;
|
||||
DWORD Flags;
|
||||
CM_NOTIFY_FILTER_TYPE FilterType;
|
||||
DWORD Reserved;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
GUID ClassGuid;
|
||||
} DeviceInterface;
|
||||
struct
|
||||
{
|
||||
HANDLE hTarget;
|
||||
} DeviceHandle;
|
||||
struct
|
||||
{
|
||||
WCHAR InstanceId[200];
|
||||
} DeviceInstance;
|
||||
} u;
|
||||
} CM_NOTIFY_FILTER, *PCM_NOTIFY_FILTER;
|
||||
|
||||
typedef enum _CM_NOTIFY_ACTION
|
||||
{
|
||||
CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
|
||||
CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
|
||||
CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
|
||||
CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
|
||||
CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
|
||||
CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
|
||||
CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
|
||||
CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
|
||||
CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
|
||||
CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
|
||||
CM_NOTIFY_ACTION_MAX
|
||||
} CM_NOTIFY_ACTION, *PCM_NOTIFY_ACTION;
|
||||
|
||||
typedef struct _CM_NOTIFY_EVENT_DATA
|
||||
{
|
||||
CM_NOTIFY_FILTER_TYPE FilterType;
|
||||
DWORD Reserved;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
GUID ClassGuid;
|
||||
WCHAR SymbolicLink[ANYSIZE_ARRAY];
|
||||
} DeviceInterface;
|
||||
struct
|
||||
{
|
||||
GUID EventGuid;
|
||||
LONG NameOffset;
|
||||
DWORD DataSize;
|
||||
BYTE Data[ANYSIZE_ARRAY];
|
||||
} DeviceHandle;
|
||||
struct
|
||||
{
|
||||
WCHAR InstanceId[ANYSIZE_ARRAY];
|
||||
} DeviceInstance;
|
||||
} u;
|
||||
} CM_NOTIFY_EVENT_DATA, *PCM_NOTIFY_EVENT_DATA;
|
||||
|
||||
typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
|
||||
|
||||
typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
|
||||
typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
|
||||
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
|
||||
|
||||
static int s_DeviceNotificationsRequested;
|
||||
static HMODULE cfgmgr32_lib_handle;
|
||||
static CM_Register_NotificationFunc CM_Register_Notification;
|
||||
static CM_Unregister_NotificationFunc CM_Unregister_Notification;
|
||||
static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
|
||||
static Uint64 s_LastDeviceNotification = 1;
|
||||
static HANDLE s_HotplugEvent = INVALID_HANDLE_VALUE;
|
||||
static SDL_AtomicInt s_HotplugRunning;
|
||||
static SDL_Thread *s_HotplugThread;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
// Defined in SDL_windowsevents.c
|
||||
extern void WIN_CheckKeyboardAndMouseHotplug(bool hid_loaded);
|
||||
#endif
|
||||
|
||||
static int SDLCALL DeviceHotplugThread(void *unused)
|
||||
{
|
||||
bool hid_loaded = WIN_LoadHIDDLL();
|
||||
|
||||
// Always run the initial device detection
|
||||
do {
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
WIN_CheckKeyboardAndMouseHotplug(hid_loaded);
|
||||
#endif
|
||||
WaitForSingleObject(s_HotplugEvent, INFINITE);
|
||||
} while (SDL_GetAtomicInt(&s_HotplugRunning));
|
||||
|
||||
if (hid_loaded) {
|
||||
WIN_UnloadHIDDLL();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD CALLBACK SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
|
||||
{
|
||||
if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
|
||||
action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
|
||||
s_LastDeviceNotification = SDL_GetTicksNS();
|
||||
SetEvent(s_HotplugEvent);
|
||||
}
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void WIN_InitDeviceNotification(void)
|
||||
{
|
||||
++s_DeviceNotificationsRequested;
|
||||
if (s_DeviceNotificationsRequested > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the device hotplug thread
|
||||
SDL_SetAtomicInt(&s_HotplugRunning, true);
|
||||
s_HotplugEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
s_HotplugThread = SDL_CreateThread(DeviceHotplugThread, "DeviceHotplugThread", NULL);
|
||||
|
||||
cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
|
||||
if (cfgmgr32_lib_handle) {
|
||||
CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
|
||||
CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
|
||||
if (CM_Register_Notification && CM_Unregister_Notification) {
|
||||
CM_NOTIFY_FILTER notify_filter;
|
||||
|
||||
SDL_zero(notify_filter);
|
||||
notify_filter.cbSize = sizeof(notify_filter);
|
||||
notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
|
||||
notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
|
||||
if (CM_Register_Notification(¬ify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Should we log errors?
|
||||
}
|
||||
|
||||
Uint64 WIN_GetLastDeviceNotification(void)
|
||||
{
|
||||
return s_LastDeviceNotification;
|
||||
}
|
||||
|
||||
void WIN_QuitDeviceNotification(void)
|
||||
{
|
||||
if (--s_DeviceNotificationsRequested > 0) {
|
||||
return;
|
||||
}
|
||||
// Make sure we have balanced calls to init/quit
|
||||
SDL_assert(s_DeviceNotificationsRequested == 0);
|
||||
|
||||
// Stop the device hotplug thread
|
||||
SDL_SetAtomicInt(&s_HotplugRunning, false);
|
||||
SetEvent(s_HotplugEvent);
|
||||
SDL_WaitThread(s_HotplugThread, NULL);
|
||||
s_HotplugThread = NULL;
|
||||
|
||||
if (cfgmgr32_lib_handle) {
|
||||
if (s_DeviceNotificationFuncHandle && CM_Unregister_Notification) {
|
||||
CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
|
||||
s_DeviceNotificationFuncHandle = NULL;
|
||||
}
|
||||
|
||||
FreeLibrary(cfgmgr32_lib_handle);
|
||||
cfgmgr32_lib_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void WIN_InitDeviceNotification(void)
|
||||
{
|
||||
}
|
||||
|
||||
Uint64 WIN_GetLastDeviceNotification( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WIN_QuitDeviceNotification(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_hid_h_
|
||||
#define SDL_hid_h_
|
||||
|
||||
#include "SDL_windows.h"
|
||||
|
||||
typedef LONG NTSTATUS;
|
||||
typedef USHORT USAGE;
|
||||
typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
|
||||
|
||||
typedef struct _HIDD_ATTRIBUTES
|
||||
{
|
||||
ULONG Size;
|
||||
USHORT VendorID;
|
||||
USHORT ProductID;
|
||||
USHORT VersionNumber;
|
||||
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HidP_Input = 0,
|
||||
HidP_Output = 1,
|
||||
HidP_Feature = 2
|
||||
} HIDP_REPORT_TYPE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
USAGE UsagePage;
|
||||
UCHAR ReportID;
|
||||
BOOLEAN IsAlias;
|
||||
USHORT BitField;
|
||||
USHORT LinkCollection;
|
||||
USAGE LinkUsage;
|
||||
USAGE LinkUsagePage;
|
||||
BOOLEAN IsRange;
|
||||
BOOLEAN IsStringRange;
|
||||
BOOLEAN IsDesignatorRange;
|
||||
BOOLEAN IsAbsolute;
|
||||
ULONG Reserved[10];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USAGE UsageMin;
|
||||
USAGE UsageMax;
|
||||
USHORT StringMin;
|
||||
USHORT StringMax;
|
||||
USHORT DesignatorMin;
|
||||
USHORT DesignatorMax;
|
||||
USHORT DataIndexMin;
|
||||
USHORT DataIndexMax;
|
||||
} Range;
|
||||
struct
|
||||
{
|
||||
USAGE Usage;
|
||||
USAGE Reserved1;
|
||||
USHORT StringIndex;
|
||||
USHORT Reserved2;
|
||||
USHORT DesignatorIndex;
|
||||
USHORT Reserved3;
|
||||
USHORT DataIndex;
|
||||
USHORT Reserved4;
|
||||
} NotRange;
|
||||
};
|
||||
} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
USAGE UsagePage;
|
||||
UCHAR ReportID;
|
||||
BOOLEAN IsAlias;
|
||||
USHORT BitField;
|
||||
USHORT LinkCollection;
|
||||
USAGE LinkUsage;
|
||||
USAGE LinkUsagePage;
|
||||
BOOLEAN IsRange;
|
||||
BOOLEAN IsStringRange;
|
||||
BOOLEAN IsDesignatorRange;
|
||||
BOOLEAN IsAbsolute;
|
||||
BOOLEAN HasNull;
|
||||
UCHAR Reserved;
|
||||
USHORT BitSize;
|
||||
USHORT ReportCount;
|
||||
USHORT Reserved2[5];
|
||||
ULONG UnitsExp;
|
||||
ULONG Units;
|
||||
LONG LogicalMin;
|
||||
LONG LogicalMax;
|
||||
LONG PhysicalMin;
|
||||
LONG PhysicalMax;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USAGE UsageMin;
|
||||
USAGE UsageMax;
|
||||
USHORT StringMin;
|
||||
USHORT StringMax;
|
||||
USHORT DesignatorMin;
|
||||
USHORT DesignatorMax;
|
||||
USHORT DataIndexMin;
|
||||
USHORT DataIndexMax;
|
||||
} Range;
|
||||
struct
|
||||
{
|
||||
USAGE Usage;
|
||||
USAGE Reserved1;
|
||||
USHORT StringIndex;
|
||||
USHORT Reserved2;
|
||||
USHORT DesignatorIndex;
|
||||
USHORT Reserved3;
|
||||
USHORT DataIndex;
|
||||
USHORT Reserved4;
|
||||
} NotRange;
|
||||
};
|
||||
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
USAGE Usage;
|
||||
USAGE UsagePage;
|
||||
USHORT InputReportByteLength;
|
||||
USHORT OutputReportByteLength;
|
||||
USHORT FeatureReportByteLength;
|
||||
USHORT Reserved[17];
|
||||
USHORT NumberLinkCollectionNodes;
|
||||
USHORT NumberInputButtonCaps;
|
||||
USHORT NumberInputValueCaps;
|
||||
USHORT NumberInputDataIndices;
|
||||
USHORT NumberOutputButtonCaps;
|
||||
USHORT NumberOutputValueCaps;
|
||||
USHORT NumberOutputDataIndices;
|
||||
USHORT NumberFeatureButtonCaps;
|
||||
USHORT NumberFeatureValueCaps;
|
||||
USHORT NumberFeatureDataIndices;
|
||||
} HIDP_CAPS, *PHIDP_CAPS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
USHORT DataIndex;
|
||||
USHORT Reserved;
|
||||
union
|
||||
{
|
||||
ULONG RawValue;
|
||||
BOOLEAN On;
|
||||
};
|
||||
} HIDP_DATA, *PHIDP_DATA;
|
||||
|
||||
#define HIDP_ERROR_CODES(p1, p2) ((NTSTATUS)(((p1) << 28) | (0x11 << 16) | (p2)))
|
||||
#define HIDP_STATUS_SUCCESS HIDP_ERROR_CODES(0x0, 0x0000)
|
||||
#define HIDP_STATUS_NULL HIDP_ERROR_CODES(0x8, 0x0001)
|
||||
#define HIDP_STATUS_INVALID_PREPARSED_DATA HIDP_ERROR_CODES(0xC, 0x0001)
|
||||
#define HIDP_STATUS_INVALID_REPORT_TYPE HIDP_ERROR_CODES(0xC, 0x0002)
|
||||
#define HIDP_STATUS_INVALID_REPORT_LENGTH HIDP_ERROR_CODES(0xC, 0x0003)
|
||||
#define HIDP_STATUS_USAGE_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x0004)
|
||||
#define HIDP_STATUS_VALUE_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x0005)
|
||||
#define HIDP_STATUS_BAD_LOG_PHY_VALUES HIDP_ERROR_CODES(0xC, 0x0006)
|
||||
#define HIDP_STATUS_BUFFER_TOO_SMALL HIDP_ERROR_CODES(0xC, 0x0007)
|
||||
#define HIDP_STATUS_INTERNAL_ERROR HIDP_ERROR_CODES(0xC, 0x0008)
|
||||
#define HIDP_STATUS_I8042_TRANS_UNKNOWN HIDP_ERROR_CODES(0xC, 0x0009)
|
||||
#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID HIDP_ERROR_CODES(0xC, 0x000A)
|
||||
#define HIDP_STATUS_NOT_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000B)
|
||||
#define HIDP_STATUS_IS_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000C)
|
||||
#define HIDP_STATUS_DATA_INDEX_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x000D)
|
||||
#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x000E)
|
||||
#define HIDP_STATUS_BUTTON_NOT_PRESSED HIDP_ERROR_CODES(0xC, 0x000F)
|
||||
#define HIDP_STATUS_REPORT_DOES_NOT_EXIST HIDP_ERROR_CODES(0xC, 0x0010)
|
||||
#define HIDP_STATUS_NOT_IMPLEMENTED HIDP_ERROR_CODES(0xC, 0x0020)
|
||||
|
||||
extern bool WIN_LoadHIDDLL(void);
|
||||
extern void WIN_UnloadHIDDLL(void);
|
||||
|
||||
typedef BOOLEAN (WINAPI *HidD_GetAttributes_t)(HANDLE HidDeviceObject, PHIDD_ATTRIBUTES Attributes);
|
||||
typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
|
||||
typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
|
||||
typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
|
||||
typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
|
||||
typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData);
|
||||
typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
|
||||
|
||||
extern HidD_GetAttributes_t SDL_HidD_GetAttributes;
|
||||
extern HidD_GetString_t SDL_HidD_GetManufacturerString;
|
||||
extern HidD_GetString_t SDL_HidD_GetProductString;
|
||||
extern HidP_GetCaps_t SDL_HidP_GetCaps;
|
||||
extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
|
||||
extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
|
||||
extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
|
||||
extern HidP_GetData_t SDL_HidP_GetData;
|
||||
|
||||
void WIN_InitDeviceNotification(void);
|
||||
Uint64 WIN_GetLastDeviceNotification(void);
|
||||
void WIN_QuitDeviceNotification(void);
|
||||
|
||||
#endif // SDL_hid_h_
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
|
||||
|
||||
#include "SDL_windows.h"
|
||||
#include "SDL_immdevice.h"
|
||||
#include "../../audio/SDL_sysaudio.h"
|
||||
#include <objbase.h> // For CLSIDFromString
|
||||
|
||||
typedef struct SDL_IMMDevice_HandleData
|
||||
{
|
||||
LPWSTR immdevice_id;
|
||||
GUID directsound_guid;
|
||||
} SDL_IMMDevice_HandleData;
|
||||
|
||||
static const ERole SDL_IMMDevice_role = eConsole; // !!! FIXME: should this be eMultimedia? Should be a hint?
|
||||
|
||||
// This is global to the WASAPI target, to handle hotplug and default device lookup.
|
||||
static IMMDeviceEnumerator *enumerator = NULL;
|
||||
static SDL_IMMDevice_callbacks immcallbacks;
|
||||
|
||||
// PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency.
|
||||
#ifdef PropVariantInit
|
||||
#undef PropVariantInit
|
||||
#endif
|
||||
#define PropVariantInit(p) SDL_zerop(p)
|
||||
|
||||
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
|
||||
/* *INDENT-OFF* */ // clang-format off
|
||||
static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
|
||||
static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
|
||||
static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
|
||||
static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
|
||||
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
|
||||
static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
|
||||
static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
|
||||
/* *INDENT-ON* */ // clang-format on
|
||||
|
||||
static bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
LPCWSTR devid = (LPCWSTR)userdata;
|
||||
if (devid && device && device->handle) {
|
||||
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle;
|
||||
if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
|
||||
{
|
||||
return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
|
||||
}
|
||||
|
||||
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
|
||||
{
|
||||
return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
|
||||
}
|
||||
|
||||
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
|
||||
{
|
||||
return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
|
||||
}
|
||||
|
||||
static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
|
||||
{
|
||||
/* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
|
||||
"SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
|
||||
its own UIs, like Volume Control, etc. */
|
||||
IPropertyStore *props = NULL;
|
||||
*utf8dev = NULL;
|
||||
SDL_zerop(fmt);
|
||||
if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
|
||||
PROPVARIANT var;
|
||||
PropVariantInit(&var);
|
||||
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
|
||||
*utf8dev = WIN_StringToUTF8W(var.pwszVal);
|
||||
}
|
||||
PropVariantClear(&var);
|
||||
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEngine_DeviceFormat, &var))) {
|
||||
SDL_memcpy(fmt, var.blob.pBlobData, SDL_min(var.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE)));
|
||||
}
|
||||
PropVariantClear(&var);
|
||||
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEndpoint_GUID, &var))) {
|
||||
(void)CLSIDFromString(var.pwszVal, guid);
|
||||
}
|
||||
PropVariantClear(&var);
|
||||
IPropertyStore_Release(props);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device && device->handle) {
|
||||
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
|
||||
SDL_free(handle->immdevice_id);
|
||||
SDL_free(handle);
|
||||
device->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||
{
|
||||
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
|
||||
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
|
||||
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
|
||||
available and switch automatically. (!!! FIXME...?) */
|
||||
|
||||
if (!devname) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// see if we already have this one first.
|
||||
SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
|
||||
if (device) {
|
||||
if (SDL_GetAtomicInt(&device->zombie)) {
|
||||
// whoa, it came back! This can happen if you unplug and replug USB headphones while we're still keeping the SDL object alive.
|
||||
// Kill this device's IMMDevice id; the device will go away when the app closes it, or maybe a new default device is chosen
|
||||
// (possibly this reconnected device), so we just want to make sure IMMDevice doesn't try to find the old device by the existing ID string.
|
||||
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
|
||||
SDL_free(handle->immdevice_id);
|
||||
handle->immdevice_id = NULL;
|
||||
device = NULL; // add a new device, below.
|
||||
}
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
|
||||
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *)SDL_calloc(1, sizeof(*handle));
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
handle->immdevice_id = SDL_wcsdup(devid);
|
||||
if (!handle->immdevice_id) {
|
||||
SDL_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
SDL_copyp(&handle->directsound_guid, dsoundguid);
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
SDL_zero(spec);
|
||||
spec.channels = (Uint8)fmt->Format.nChannels;
|
||||
spec.freq = fmt->Format.nSamplesPerSec;
|
||||
spec.format = (force_format != SDL_AUDIO_UNKNOWN) ? force_format : SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
|
||||
|
||||
device = SDL_AddAudioDevice(recording, devname, &spec, handle);
|
||||
|
||||
if (!recording && supports_recording_playback_devices) {
|
||||
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
|
||||
SDL_IMMDevice_HandleData *recording_handle = (SDL_IMMDevice_HandleData *)SDL_malloc(sizeof(*recording_handle));
|
||||
if (!recording_handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
recording_handle->immdevice_id = SDL_wcsdup(devid);
|
||||
if (!recording_handle->immdevice_id) {
|
||||
SDL_free(recording_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_copyp(&recording_handle->directsound_guid, dsoundguid);
|
||||
|
||||
if (!SDL_AddAudioDevice(true, devname, &spec, recording_handle)) {
|
||||
SDL_free(recording_handle->immdevice_id);
|
||||
SDL_free(recording_handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
SDL_free(handle->immdevice_id);
|
||||
SDL_free(handle);
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
|
||||
easy in C++, but we have to tapdance more to make work in C.
|
||||
Thanks to this page for coaching on how to make this work:
|
||||
https://www.codeproject.com/Articles/13601/COM-in-plain-C */
|
||||
|
||||
typedef struct SDLMMNotificationClient
|
||||
{
|
||||
const IMMNotificationClientVtbl *lpVtbl;
|
||||
SDL_AtomicInt refcount;
|
||||
SDL_AudioFormat force_format;
|
||||
bool supports_recording_playback_devices;
|
||||
} SDLMMNotificationClient;
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
|
||||
{
|
||||
if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
|
||||
*ppv = client;
|
||||
client->lpVtbl->AddRef(client);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
|
||||
{
|
||||
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
|
||||
return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
|
||||
}
|
||||
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
|
||||
{
|
||||
// client is a static object; we don't ever free it.
|
||||
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
|
||||
const ULONG rc = SDL_AtomicDecRef(&client->refcount);
|
||||
if (rc == 0) {
|
||||
SDL_SetAtomicInt(&client->refcount, 0); // uhh...
|
||||
return 0;
|
||||
}
|
||||
return rc - 1;
|
||||
}
|
||||
|
||||
// These are the entry points called when WASAPI device endpoints change.
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
if (role == SDL_IMMDevice_role) {
|
||||
immcallbacks.default_audio_device_changed(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
/* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
|
||||
OnDeviceStateChange, making that a better place to deal with device adds. More
|
||||
importantly: the first time you plug in a USB audio device, this callback will
|
||||
fire, but when you unplug it, it isn't removed (it's state changes to NOTPRESENT).
|
||||
Plugging it back in won't fire this callback again. */
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
|
||||
{
|
||||
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
|
||||
IMMDevice *device = NULL;
|
||||
|
||||
if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
|
||||
IMMEndpoint *endpoint = NULL;
|
||||
if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
|
||||
EDataFlow flow;
|
||||
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
|
||||
const bool recording = (flow == eCapture);
|
||||
if (dwNewState == DEVICE_STATE_ACTIVE) {
|
||||
char *utf8dev;
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
GUID dsoundguid;
|
||||
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
|
||||
if (utf8dev) {
|
||||
SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->force_format, client->supports_recording_playback_devices);
|
||||
SDL_free(utf8dev);
|
||||
}
|
||||
} else {
|
||||
immcallbacks.audio_device_disconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
|
||||
}
|
||||
}
|
||||
IMMEndpoint_Release(endpoint);
|
||||
}
|
||||
IMMDevice_Release(device);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
|
||||
{
|
||||
return S_OK; // we don't care about these.
|
||||
}
|
||||
|
||||
static const IMMNotificationClientVtbl notification_client_vtbl = {
|
||||
SDLMMNotificationClient_QueryInterface,
|
||||
SDLMMNotificationClient_AddRef,
|
||||
SDLMMNotificationClient_Release,
|
||||
SDLMMNotificationClient_OnDeviceStateChanged,
|
||||
SDLMMNotificationClient_OnDeviceAdded,
|
||||
SDLMMNotificationClient_OnDeviceRemoved,
|
||||
SDLMMNotificationClient_OnDefaultDeviceChanged,
|
||||
SDLMMNotificationClient_OnPropertyValueChanged
|
||||
};
|
||||
|
||||
static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 }, SDL_AUDIO_UNKNOWN, false };
|
||||
|
||||
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
|
||||
{
|
||||
HRESULT ret;
|
||||
|
||||
// just skip the discussion with COM here.
|
||||
if (!WIN_IsWindowsVistaOrGreater()) {
|
||||
return SDL_SetError("IMMDevice support requires Windows Vista or later");
|
||||
}
|
||||
|
||||
if (FAILED(WIN_CoInitialize())) {
|
||||
return SDL_SetError("IMMDevice: CoInitialize() failed");
|
||||
}
|
||||
|
||||
ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
|
||||
if (FAILED(ret)) {
|
||||
WIN_CoUninitialize();
|
||||
return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
|
||||
}
|
||||
|
||||
if (callbacks) {
|
||||
SDL_copyp(&immcallbacks, callbacks);
|
||||
} else {
|
||||
SDL_zero(immcallbacks);
|
||||
}
|
||||
|
||||
if (!immcallbacks.audio_device_disconnected) {
|
||||
immcallbacks.audio_device_disconnected = SDL_AudioDeviceDisconnected;
|
||||
}
|
||||
if (!immcallbacks.default_audio_device_changed) {
|
||||
immcallbacks.default_audio_device_changed = SDL_DefaultAudioDeviceChanged;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_IMMDevice_Quit(void)
|
||||
{
|
||||
if (enumerator) {
|
||||
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
||||
IMMDeviceEnumerator_Release(enumerator);
|
||||
enumerator = NULL;
|
||||
}
|
||||
|
||||
SDL_zero(immcallbacks);
|
||||
|
||||
WIN_CoUninitialize();
|
||||
}
|
||||
|
||||
bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool recording)
|
||||
{
|
||||
const Uint64 timeout = SDL_GetTicks() + 8000; // intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep.
|
||||
|
||||
SDL_assert(device != NULL);
|
||||
SDL_assert(immdevice != NULL);
|
||||
|
||||
LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
|
||||
SDL_assert(devid != NULL);
|
||||
|
||||
HRESULT ret;
|
||||
while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
|
||||
const Uint64 now = SDL_GetTicks();
|
||||
if (timeout > now) {
|
||||
const Uint64 ticksleft = timeout - now;
|
||||
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); // wait awhile and try again.
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_IMMDevice_GetIsCapture(IMMDevice *device)
|
||||
{
|
||||
bool iscapture = false;
|
||||
IMMEndpoint *endpoint = NULL;
|
||||
if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
|
||||
EDataFlow flow;
|
||||
|
||||
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
|
||||
iscapture = (flow == eCapture);
|
||||
}
|
||||
}
|
||||
|
||||
IMMEndpoint_Release(endpoint);
|
||||
return iscapture;
|
||||
}
|
||||
|
||||
static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||
{
|
||||
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
|
||||
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
|
||||
|
||||
IMMDeviceCollection *collection = NULL;
|
||||
if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, recording ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
|
||||
return;
|
||||
}
|
||||
|
||||
UINT total = 0;
|
||||
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
|
||||
IMMDeviceCollection_Release(collection);
|
||||
return;
|
||||
}
|
||||
|
||||
LPWSTR default_devid = NULL;
|
||||
if (default_device) {
|
||||
IMMDevice *default_immdevice = NULL;
|
||||
const EDataFlow dataflow = recording ? eCapture : eRender;
|
||||
if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
|
||||
LPWSTR devid = NULL;
|
||||
if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
|
||||
default_devid = SDL_wcsdup(devid); // if this fails, oh well.
|
||||
CoTaskMemFree(devid);
|
||||
}
|
||||
IMMDevice_Release(default_immdevice);
|
||||
}
|
||||
}
|
||||
|
||||
for (UINT i = 0; i < total; i++) {
|
||||
IMMDevice *immdevice = NULL;
|
||||
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
|
||||
LPWSTR devid = NULL;
|
||||
if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
|
||||
char *devname = NULL;
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
GUID dsoundguid;
|
||||
SDL_zero(fmt);
|
||||
SDL_zero(dsoundguid);
|
||||
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
|
||||
if (devname) {
|
||||
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid, force_format, supports_recording_playback_devices);
|
||||
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
|
||||
*default_device = sdldevice;
|
||||
}
|
||||
SDL_free(devname);
|
||||
}
|
||||
CoTaskMemFree(devid);
|
||||
}
|
||||
IMMDevice_Release(immdevice);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(default_devid);
|
||||
|
||||
IMMDeviceCollection_Release(collection);
|
||||
}
|
||||
|
||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||
{
|
||||
EnumerateEndpointsForFlow(false, default_playback, force_format, supports_recording_playback_devices);
|
||||
EnumerateEndpointsForFlow(true, default_recording, force_format, supports_recording_playback_devices);
|
||||
|
||||
notification_client.force_format = force_format;
|
||||
notification_client.supports_recording_playback_devices = supports_recording_playback_devices;
|
||||
|
||||
// if this fails, we just won't get hotplug events. Carry on anyhow.
|
||||
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
||||
}
|
||||
|
||||
#endif // defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_IMMDEVICE_H
|
||||
#define SDL_IMMDEVICE_H
|
||||
|
||||
#define COBJMACROS
|
||||
#include <mmdeviceapi.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h
|
||||
|
||||
typedef struct SDL_IMMDevice_callbacks
|
||||
{
|
||||
void (*audio_device_disconnected)(struct SDL_AudioDevice *device);
|
||||
void (*default_audio_device_changed)(struct SDL_AudioDevice *new_default_device);
|
||||
} SDL_IMMDevice_callbacks;
|
||||
|
||||
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
|
||||
void SDL_IMMDevice_Quit(void);
|
||||
bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
|
||||
bool SDL_IMMDevice_GetIsCapture(IMMDevice* device);
|
||||
void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording, SDL_AudioFormat force_format, bool supports_recording_playback_devices);
|
||||
LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
|
||||
LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
|
||||
void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
|
||||
|
||||
#endif // SDL_IMMDEVICE_H
|
||||
+728
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_WINDOWS)
|
||||
|
||||
#include "SDL_windows.h"
|
||||
|
||||
#include "../../video/SDL_surface_c.h"
|
||||
|
||||
#include <shellapi.h> // CommandLineToArgvW()
|
||||
|
||||
#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only)
|
||||
#ifdef HAVE_ROAPI_H
|
||||
#include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only)
|
||||
#else
|
||||
typedef enum RO_INIT_TYPE
|
||||
{
|
||||
RO_INIT_SINGLETHREADED = 0,
|
||||
RO_INIT_MULTITHREADED = 1
|
||||
} RO_INIT_TYPE;
|
||||
#endif
|
||||
|
||||
#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
|
||||
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
|
||||
#endif
|
||||
|
||||
#ifndef WC_ERR_INVALID_CHARS
|
||||
#define WC_ERR_INVALID_CHARS 0x00000080
|
||||
#endif
|
||||
|
||||
// Dark mode support
|
||||
typedef enum {
|
||||
UXTHEME_APPMODE_DEFAULT,
|
||||
UXTHEME_APPMODE_ALLOW_DARK,
|
||||
UXTHEME_APPMODE_FORCE_DARK,
|
||||
UXTHEME_APPMODE_FORCE_LIGHT,
|
||||
UXTHEME_APPMODE_MAX
|
||||
} UxthemePreferredAppMode;
|
||||
|
||||
typedef enum {
|
||||
WCA_UNDEFINED = 0,
|
||||
WCA_USEDARKMODECOLORS = 26,
|
||||
WCA_LAST = 27
|
||||
} WINDOWCOMPOSITIONATTRIB;
|
||||
|
||||
typedef struct {
|
||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
||||
PVOID pvData;
|
||||
SIZE_T cbData;
|
||||
} WINDOWCOMPOSITIONATTRIBDATA;
|
||||
|
||||
typedef struct {
|
||||
ULONG dwOSVersionInfoSize;
|
||||
ULONG dwMajorVersion;
|
||||
ULONG dwMinorVersion;
|
||||
ULONG dwBuildNumber;
|
||||
ULONG dwPlatformId;
|
||||
WCHAR szCSDVersion[128];
|
||||
} NT_OSVERSIONINFOW;
|
||||
|
||||
typedef bool (WINAPI *ShouldAppsUseDarkMode_t)(void);
|
||||
typedef void (WINAPI *AllowDarkModeForWindow_t)(HWND, bool);
|
||||
typedef void (WINAPI *AllowDarkModeForApp_t)(bool);
|
||||
typedef void (WINAPI *RefreshImmersiveColorPolicyState_t)(void);
|
||||
typedef UxthemePreferredAppMode (WINAPI *SetPreferredAppMode_t)(UxthemePreferredAppMode);
|
||||
typedef BOOL (WINAPI *SetWindowCompositionAttribute_t)(HWND, const WINDOWCOMPOSITIONATTRIBDATA *);
|
||||
typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *);
|
||||
|
||||
// Fake window to help with DirectInput events.
|
||||
HWND SDL_HelperWindow = NULL;
|
||||
static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
|
||||
static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow");
|
||||
static ATOM SDL_HelperWindowClass = 0;
|
||||
|
||||
/*
|
||||
* Creates a HelperWindow used for DirectInput.
|
||||
*/
|
||||
bool SDL_HelperWindowCreate(void)
|
||||
{
|
||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||
WNDCLASS wce;
|
||||
|
||||
// Make sure window isn't created twice.
|
||||
if (SDL_HelperWindow != NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the class.
|
||||
SDL_zero(wce);
|
||||
wce.lpfnWndProc = DefWindowProc;
|
||||
wce.lpszClassName = SDL_HelperWindowClassName;
|
||||
wce.hInstance = hInstance;
|
||||
|
||||
// Register the class.
|
||||
SDL_HelperWindowClass = RegisterClass(&wce);
|
||||
if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
|
||||
return WIN_SetError("Unable to create Helper Window Class");
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName,
|
||||
SDL_HelperWindowName,
|
||||
WS_OVERLAPPED, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, HWND_MESSAGE, NULL,
|
||||
hInstance, NULL);
|
||||
if (!SDL_HelperWindow) {
|
||||
UnregisterClass(SDL_HelperWindowClassName, hInstance);
|
||||
return WIN_SetError("Unable to create Helper Window");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys the HelperWindow previously created with SDL_HelperWindowCreate.
|
||||
*/
|
||||
void SDL_HelperWindowDestroy(void)
|
||||
{
|
||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||
|
||||
// Destroy the window.
|
||||
if (SDL_HelperWindow != NULL) {
|
||||
if (DestroyWindow(SDL_HelperWindow) == 0) {
|
||||
WIN_SetError("Unable to destroy Helper Window");
|
||||
return;
|
||||
}
|
||||
SDL_HelperWindow = NULL;
|
||||
}
|
||||
|
||||
// Unregister the class.
|
||||
if (SDL_HelperWindowClass != 0) {
|
||||
if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) {
|
||||
WIN_SetError("Unable to destroy Helper Window Class");
|
||||
return;
|
||||
}
|
||||
SDL_HelperWindowClass = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets an error message based on an HRESULT
|
||||
bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
|
||||
{
|
||||
TCHAR buffer[1024];
|
||||
char *message;
|
||||
TCHAR *p = buffer;
|
||||
DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0,
|
||||
buffer, SDL_arraysize(buffer), NULL);
|
||||
buffer[c] = 0;
|
||||
// kill CR/LF that FormatMessage() sticks at the end
|
||||
while (*p) {
|
||||
if (*p == '\r') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
message = WIN_StringToUTF8(buffer);
|
||||
SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message);
|
||||
SDL_free(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets an error message based on GetLastError()
|
||||
bool WIN_SetError(const char *prefix)
|
||||
{
|
||||
return WIN_SetErrorFromHRESULT(prefix, GetLastError());
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WIN_CoInitialize(void)
|
||||
{
|
||||
/* SDL handles any threading model, so initialize with the default, which
|
||||
is compatible with OLE and if that doesn't work, try multi-threaded mode.
|
||||
|
||||
If you need multi-threaded mode, call CoInitializeEx() before SDL_Init()
|
||||
*/
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
// On Xbox, there's no need to call CoInitializeEx (and it's not implemented)
|
||||
return S_OK;
|
||||
#else
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
if (hr == RPC_E_CHANGED_MODE) {
|
||||
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
}
|
||||
|
||||
// S_FALSE means success, but someone else already initialized.
|
||||
// You still need to call CoUninitialize in this case!
|
||||
if (hr == S_FALSE) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return hr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WIN_CoUninitialize(void)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
FARPROC WIN_LoadComBaseFunction(const char *name)
|
||||
{
|
||||
static bool s_bLoaded;
|
||||
static HMODULE s_hComBase;
|
||||
|
||||
if (!s_bLoaded) {
|
||||
s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
s_bLoaded = true;
|
||||
}
|
||||
if (s_hComBase) {
|
||||
return GetProcAddress(s_hComBase, name);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WIN_RoInitialize(void)
|
||||
{
|
||||
typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType);
|
||||
RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize");
|
||||
if (RoInitializeFunc) {
|
||||
// RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED
|
||||
HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED);
|
||||
if (hr == RPC_E_CHANGED_MODE) {
|
||||
hr = RoInitializeFunc(RO_INIT_MULTITHREADED);
|
||||
}
|
||||
|
||||
// S_FALSE means success, but someone else already initialized.
|
||||
// You still need to call RoUninitialize in this case!
|
||||
if (hr == S_FALSE) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return hr;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
void WIN_RoUninitialize(void)
|
||||
{
|
||||
typedef void(WINAPI * RoUninitialize_t)(void);
|
||||
RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize");
|
||||
if (RoUninitializeFunc) {
|
||||
RoUninitializeFunc();
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
||||
static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
|
||||
{
|
||||
OSVERSIONINFOEXW osvi;
|
||||
DWORDLONG const dwlConditionMask = VerSetConditionMask(
|
||||
VerSetConditionMask(
|
||||
VerSetConditionMask(
|
||||
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
|
||||
VER_MINORVERSION, VER_GREATER_EQUAL),
|
||||
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
|
||||
SDL_zero(osvi);
|
||||
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
||||
osvi.dwMajorVersion = wMajorVersion;
|
||||
osvi.dwMinorVersion = wMinorVersion;
|
||||
osvi.wServicePackMajor = wServicePackMajor;
|
||||
|
||||
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
|
||||
}
|
||||
|
||||
static DWORD WIN_BuildNumber = 0;
|
||||
static BOOL IsWindowsBuildVersionAtLeast(DWORD dwBuildNumber)
|
||||
{
|
||||
if (WIN_BuildNumber != 0) {
|
||||
return (WIN_BuildNumber >= dwBuildNumber);
|
||||
}
|
||||
|
||||
HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
|
||||
if (!ntdll) {
|
||||
return false;
|
||||
}
|
||||
// There is no function to get Windows build number, so let's get it here via RtlGetVersion
|
||||
RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion");
|
||||
NT_OSVERSIONINFOW os_info;
|
||||
os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW);
|
||||
os_info.dwBuildNumber = 0;
|
||||
if (RtlGetVersionFunc) {
|
||||
RtlGetVersionFunc(&os_info);
|
||||
}
|
||||
FreeLibrary(ntdll);
|
||||
|
||||
WIN_BuildNumber = (os_info.dwBuildNumber & ~0xF0000000);
|
||||
return (WIN_BuildNumber >= dwBuildNumber);
|
||||
}
|
||||
#else
|
||||
static BOOL IsWindowsBuildVersionAtLeast(DWORD dwBuildNumber)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// apply some static variables so we only call into the Win32 API once per process for each check.
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
#define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result);
|
||||
#else
|
||||
#define CHECKWINVER(notdesktop_platform_result, test) \
|
||||
static bool checked = false; \
|
||||
static BOOL result = FALSE; \
|
||||
if (!checked) { \
|
||||
result = (test); \
|
||||
checked = true; \
|
||||
} \
|
||||
return result
|
||||
#endif
|
||||
|
||||
BOOL WIN_IsWine(void)
|
||||
{
|
||||
static bool checked;
|
||||
static bool is_wine;
|
||||
|
||||
if (!checked) {
|
||||
HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
|
||||
if (ntdll) {
|
||||
if (GetProcAddress(ntdll, "wine_get_version") != NULL) {
|
||||
is_wine = true;
|
||||
}
|
||||
FreeLibrary(ntdll);
|
||||
}
|
||||
checked = true;
|
||||
}
|
||||
return is_wine;
|
||||
}
|
||||
|
||||
// this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!),
|
||||
// so there's no "OrGreater" as that would always be TRUE. The other functions are here to
|
||||
// ask "can we support a specific feature?" but this function is here to ask "do we need to do
|
||||
// something different for an OS version we probably should abandon?" :)
|
||||
BOOL WIN_IsWindowsXP(void)
|
||||
{
|
||||
CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0));
|
||||
}
|
||||
|
||||
BOOL WIN_IsWindowsVistaOrGreater(void)
|
||||
{
|
||||
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0));
|
||||
}
|
||||
|
||||
BOOL WIN_IsWindows7OrGreater(void)
|
||||
{
|
||||
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0));
|
||||
}
|
||||
|
||||
BOOL WIN_IsWindows8OrGreater(void)
|
||||
{
|
||||
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0));
|
||||
}
|
||||
|
||||
BOOL WIN_IsWindows11OrGreater(void)
|
||||
{
|
||||
return IsWindowsBuildVersionAtLeast(22000);
|
||||
}
|
||||
|
||||
#undef CHECKWINVER
|
||||
|
||||
|
||||
/*
|
||||
WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's
|
||||
longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which
|
||||
will give you a name GUID. The full name is in the Windows Registry under
|
||||
that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories
|
||||
|
||||
Note that drivers can report GUID_NULL for the name GUID, in which case,
|
||||
Windows makes a best effort to fill in those 31 bytes in the usual place.
|
||||
This info summarized from MSDN:
|
||||
|
||||
http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx
|
||||
|
||||
Always look this up in the registry if possible, because the strings are
|
||||
different! At least on Win10, I see "Yeti Stereo Microphone" in the
|
||||
Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh.
|
||||
|
||||
(Also, DirectSound shouldn't be limited to 32 chars, but its device enum
|
||||
has the same problem.)
|
||||
|
||||
WASAPI doesn't need this. This is just for DirectSound/WinMM.
|
||||
*/
|
||||
char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
|
||||
{
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got.
|
||||
#else
|
||||
static const GUID nullguid = { 0 };
|
||||
const unsigned char *ptr;
|
||||
char keystr[128];
|
||||
WCHAR *strw = NULL;
|
||||
bool rc;
|
||||
HKEY hkey;
|
||||
DWORD len = 0;
|
||||
char *result = NULL;
|
||||
|
||||
if (WIN_IsEqualGUID(guid, &nullguid)) {
|
||||
return WIN_StringToUTF8W(name); // No GUID, go with what we've got.
|
||||
}
|
||||
|
||||
ptr = (const unsigned char *)guid;
|
||||
(void)SDL_snprintf(keystr, sizeof(keystr),
|
||||
"System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6],
|
||||
ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
|
||||
|
||||
strw = WIN_UTF8ToStringW(keystr);
|
||||
rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS);
|
||||
SDL_free(strw);
|
||||
if (!rc) {
|
||||
return WIN_StringToUTF8W(name); // oh well.
|
||||
}
|
||||
|
||||
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS);
|
||||
if (!rc) {
|
||||
RegCloseKey(hkey);
|
||||
return WIN_StringToUTF8W(name); // oh well.
|
||||
}
|
||||
|
||||
strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR));
|
||||
if (!strw) {
|
||||
RegCloseKey(hkey);
|
||||
return WIN_StringToUTF8W(name); // oh well.
|
||||
}
|
||||
|
||||
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS);
|
||||
RegCloseKey(hkey);
|
||||
if (!rc) {
|
||||
SDL_free(strw);
|
||||
return WIN_StringToUTF8W(name); // oh well.
|
||||
}
|
||||
|
||||
strw[len / 2] = 0; // make sure it's null-terminated.
|
||||
|
||||
result = WIN_StringToUTF8W(strw);
|
||||
SDL_free(strw);
|
||||
return result ? result : WIN_StringToUTF8W(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
|
||||
{
|
||||
return (SDL_memcmp(a, b, sizeof(*a)) == 0);
|
||||
}
|
||||
|
||||
BOOL WIN_IsEqualIID(REFIID a, REFIID b)
|
||||
{
|
||||
return (SDL_memcmp(a, b, sizeof(*a)) == 0);
|
||||
}
|
||||
|
||||
void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect)
|
||||
{
|
||||
sdlrect->x = winrect->left;
|
||||
sdlrect->w = (winrect->right - winrect->left) + 1;
|
||||
sdlrect->y = winrect->top;
|
||||
sdlrect->h = (winrect->bottom - winrect->top) + 1;
|
||||
}
|
||||
|
||||
void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect)
|
||||
{
|
||||
winrect->left = sdlrect->x;
|
||||
winrect->right = sdlrect->x + sdlrect->w - 1;
|
||||
winrect->top = sdlrect->y;
|
||||
winrect->bottom = sdlrect->y + sdlrect->h - 1;
|
||||
}
|
||||
|
||||
bool WIN_WindowRectValid(const RECT *rect)
|
||||
{
|
||||
// A window can be resized to zero height, but not zero width
|
||||
return (rect->right > 0);
|
||||
}
|
||||
|
||||
void WIN_UpdateDarkModeForHWND(HWND hwnd)
|
||||
{
|
||||
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
||||
if (!IsWindowsBuildVersionAtLeast(17763)) {
|
||||
// Too old to support dark mode
|
||||
return;
|
||||
}
|
||||
HMODULE uxtheme = LoadLibrary(TEXT("uxtheme.dll"));
|
||||
if (!uxtheme) {
|
||||
return;
|
||||
}
|
||||
RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(104));
|
||||
ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132));
|
||||
AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(133));
|
||||
if (!IsWindowsBuildVersionAtLeast(18362)) {
|
||||
AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135));
|
||||
if (AllowDarkModeForAppFunc) {
|
||||
AllowDarkModeForAppFunc(true);
|
||||
}
|
||||
} else {
|
||||
SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135));
|
||||
if (SetPreferredAppModeFunc) {
|
||||
SetPreferredAppModeFunc(UXTHEME_APPMODE_ALLOW_DARK);
|
||||
}
|
||||
}
|
||||
if (RefreshImmersiveColorPolicyStateFunc) {
|
||||
RefreshImmersiveColorPolicyStateFunc();
|
||||
}
|
||||
if (AllowDarkModeForWindowFunc) {
|
||||
AllowDarkModeForWindowFunc(hwnd, true);
|
||||
}
|
||||
BOOL value;
|
||||
// Check dark mode using ShouldAppsUseDarkMode, but use SDL_GetSystemTheme as a fallback
|
||||
if (ShouldAppsUseDarkModeFunc) {
|
||||
value = ShouldAppsUseDarkModeFunc() ? TRUE : FALSE;
|
||||
} else {
|
||||
value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE;
|
||||
}
|
||||
FreeLibrary(uxtheme);
|
||||
if (!IsWindowsBuildVersionAtLeast(18362)) {
|
||||
SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value)));
|
||||
} else {
|
||||
HMODULE user32 = GetModuleHandle(TEXT("user32.dll"));
|
||||
if (user32) {
|
||||
SetWindowCompositionAttribute_t SetWindowCompositionAttributeFunc = (SetWindowCompositionAttribute_t)GetProcAddress(user32, "SetWindowCompositionAttribute");
|
||||
if (SetWindowCompositionAttributeFunc) {
|
||||
WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &value, sizeof(value) };
|
||||
SetWindowCompositionAttributeFunc(hwnd, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HICON WIN_CreateIconFromSurface(SDL_Surface *surface)
|
||||
{
|
||||
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
||||
SDL_Surface *s = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The dimensions will be needed after s is freed */
|
||||
const int width = s->w;
|
||||
const int height = s->h;
|
||||
|
||||
BITMAPINFO bmpInfo;
|
||||
SDL_zero(bmpInfo);
|
||||
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmpInfo.bmiHeader.biWidth = width;
|
||||
bmpInfo.bmiHeader.biHeight = -height; /* Top-down bitmap */
|
||||
bmpInfo.bmiHeader.biPlanes = 1;
|
||||
bmpInfo.bmiHeader.biBitCount = 32;
|
||||
bmpInfo.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
HDC hdc = GetDC(NULL);
|
||||
void *pBits = NULL;
|
||||
HBITMAP hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, &pBits, NULL, 0);
|
||||
if (!hBitmap) {
|
||||
ReleaseDC(NULL, hdc);
|
||||
SDL_DestroySurface(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_memcpy(pBits, s->pixels, width * height * 4);
|
||||
|
||||
SDL_DestroySurface(s);
|
||||
|
||||
HBITMAP hMask = CreateBitmap(width, height, 1, 1, NULL);
|
||||
if (!hMask) {
|
||||
DeleteObject(hBitmap);
|
||||
ReleaseDC(NULL, hdc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HDC hdcMem = CreateCompatibleDC(hdc);
|
||||
HGDIOBJ oldBitmap = SelectObject(hdcMem, hMask);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
BYTE* pixel = (BYTE*)pBits + (y * width + x) * 4;
|
||||
BYTE alpha = pixel[3];
|
||||
COLORREF maskColor = (alpha == 0) ? RGB(0, 0, 0) : RGB(255, 255, 255);
|
||||
SetPixel(hdcMem, x, y, maskColor);
|
||||
}
|
||||
}
|
||||
|
||||
ICONINFO iconInfo;
|
||||
iconInfo.fIcon = TRUE;
|
||||
iconInfo.xHotspot = 0;
|
||||
iconInfo.yHotspot = 0;
|
||||
iconInfo.hbmMask = hMask;
|
||||
iconInfo.hbmColor = hBitmap;
|
||||
|
||||
HICON hIcon = CreateIconIndirect(&iconInfo);
|
||||
|
||||
SelectObject(hdcMem, oldBitmap);
|
||||
DeleteDC(hdcMem);
|
||||
DeleteObject(hBitmap);
|
||||
DeleteObject(hMask);
|
||||
ReleaseDC(NULL, hdc);
|
||||
|
||||
return hIcon;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
|
||||
/* *INDENT-OFF* */ // clang-format off
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
/* *INDENT-ON* */ // clang-format on
|
||||
|
||||
SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
|
||||
{
|
||||
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32;
|
||||
} else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
|
||||
if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32;
|
||||
}
|
||||
}
|
||||
return SDL_AUDIO_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar)
|
||||
{
|
||||
if (WIN_IsWindowsXP()) {
|
||||
dwFlags &= ~WC_ERR_INVALID_CHARS; // not supported before Vista. Without this flag, it will just replace bogus chars with U+FFFD. You're on your own, WinXP.
|
||||
}
|
||||
return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
|
||||
}
|
||||
|
||||
const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated)
|
||||
{
|
||||
// If the provided argv is valid, we pass it to the main function as-is, since it's probably what the user wants.
|
||||
// Otherwise, we take a NULL argv as an instruction for SDL to parse the command line into an argv.
|
||||
// On Windows, when SDL provides the main entry point, argv is always NULL.
|
||||
|
||||
const char *out_of_mem_str = "Out of memory - aborting";
|
||||
const char *proc_err_str = "Error processing command line arguments - aborting";
|
||||
|
||||
*pallocated = NULL;
|
||||
|
||||
if (*pargv) {
|
||||
return NULL; // just go with what was provided, no error message.
|
||||
}
|
||||
|
||||
// We need to be careful about how we allocate/free memory here. We can't use SDL_alloc()/SDL_free()
|
||||
// because the application might have used SDL_SetMemoryFunctions() to change the allocator.
|
||||
LPWSTR *argvw = NULL;
|
||||
char **argv = NULL;
|
||||
|
||||
const LPWSTR command_line = GetCommandLineW();
|
||||
|
||||
// Because of how the Windows command line is structured, we know for sure that the buffer size required to
|
||||
// store all argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal
|
||||
// to the size of the original command line string converted to UTF-8.
|
||||
const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, command_line, -1, NULL, 0, NULL, NULL); // Includes the null terminator
|
||||
if (!argdata_size) {
|
||||
return proc_err_str;
|
||||
}
|
||||
|
||||
int argc = -1;
|
||||
argvw = CommandLineToArgvW(command_line, &argc);
|
||||
if (!argvw || argc < 0) {
|
||||
return out_of_mem_str;
|
||||
}
|
||||
|
||||
// Allocate argv followed by the argument string buffer as one contiguous allocation.
|
||||
argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size);
|
||||
if (!argv) {
|
||||
LocalFree(argvw);
|
||||
return out_of_mem_str;
|
||||
}
|
||||
|
||||
char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv);
|
||||
int argdata_index = 0;
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL);
|
||||
if (!bytes_written) {
|
||||
HeapFree(GetProcessHeap(), 0, argv);
|
||||
LocalFree(argvw);
|
||||
return proc_err_str;
|
||||
}
|
||||
argv[i] = argdata + argdata_index;
|
||||
argdata_index += bytes_written;
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
|
||||
LocalFree(argvw);
|
||||
|
||||
*pargc = argc;
|
||||
*pallocated = argv;
|
||||
*pargv = argv;
|
||||
|
||||
return NULL; // no error string.
|
||||
}
|
||||
|
||||
#endif // defined(SDL_PLATFORM_WINDOWS)
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// This is an include file for windows.h with the SDL build settings
|
||||
|
||||
#ifndef SDL_windows_h_
|
||||
#define SDL_windows_h_
|
||||
|
||||
#ifdef SDL_PLATFORM_WIN32
|
||||
|
||||
#ifndef _WIN32_WINNT_NT4
|
||||
#define _WIN32_WINNT_NT4 0x0400
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WIN2K
|
||||
#define _WIN32_WINNT_WIN2K 0x0500
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WINXP
|
||||
#define _WIN32_WINNT_WINXP 0x0501
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WS03
|
||||
#define _WIN32_WINNT_WS03 0x0502
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_VISTA
|
||||
#define _WIN32_WINNT_VISTA 0x0600
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WIN7
|
||||
#define _WIN32_WINNT_WIN7 0x0601
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WIN8
|
||||
#define _WIN32_WINNT_WIN8 0x0602
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WINBLUE
|
||||
#define _WIN32_WINNT_WINBLUE 0x0603
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WIN10
|
||||
#define _WIN32_WINNT_WIN10 0x0A00
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#ifndef STRICT
|
||||
#define STRICT 1
|
||||
#endif
|
||||
#ifndef UNICODE
|
||||
#define UNICODE 1
|
||||
#endif
|
||||
#undef WINVER
|
||||
#undef _WIN32_WINNT
|
||||
#if defined(SDL_VIDEO_RENDER_D3D12) || defined(HAVE_DXGI1_6_H)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN10 // For D3D12, 0xA00 is required
|
||||
#elif defined(HAVE_SHELLSCALINGAPI_H)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WINBLUE // For DPI support
|
||||
#elif defined(HAVE_ROAPI_H)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN8
|
||||
#elif defined(HAVE_SENSORSAPI_H)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN7
|
||||
#elif defined(HAVE_MMDEVICEAPI_H)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
||||
#else
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WINXP // Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input
|
||||
#endif
|
||||
#define WINVER _WIN32_WINNT
|
||||
|
||||
#elif defined(SDL_PLATFORM_WINGDK)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#ifndef STRICT
|
||||
#define STRICT 1
|
||||
#endif
|
||||
#ifndef UNICODE
|
||||
#define UNICODE 1
|
||||
#endif
|
||||
#undef WINVER
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0xA00
|
||||
#define WINVER _WIN32_WINNT
|
||||
|
||||
#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#ifndef STRICT
|
||||
#define STRICT 1
|
||||
#endif
|
||||
#ifndef UNICODE
|
||||
#define UNICODE 1
|
||||
#endif
|
||||
#undef WINVER
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0xA00
|
||||
#define WINVER _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
// See https://github.com/libsdl-org/SDL/pull/7607
|
||||
// force_align_arg_pointer attribute requires gcc >= 4.2.x.
|
||||
#if defined(__clang__)
|
||||
#define HAVE_FORCE_ALIGN_ARG_POINTER
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
|
||||
#define HAVE_FORCE_ALIGN_ARG_POINTER
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
|
||||
#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define MINGW32_FORCEALIGN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <basetyps.h> // for REFIID with broken mingw.org headers
|
||||
#include <mmreg.h>
|
||||
|
||||
// Routines to convert from UTF8 to native Windows text
|
||||
#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (const char *)(S), (SDL_wcslen(S) + 1) * sizeof(WCHAR))
|
||||
#define WIN_UTF8ToStringW(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
|
||||
// !!! FIXME: UTF8ToString() can just be a SDL_strdup() here.
|
||||
#define WIN_StringToUTF8A(S) SDL_iconv_string("UTF-8", "ASCII", (const char *)(S), (SDL_strlen(S) + 1))
|
||||
#define WIN_UTF8ToStringA(S) SDL_iconv_string("ASCII", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
|
||||
#if UNICODE
|
||||
#define WIN_StringToUTF8 WIN_StringToUTF8W
|
||||
#define WIN_UTF8ToString WIN_UTF8ToStringW
|
||||
#define SDL_tcslen SDL_wcslen
|
||||
#define SDL_tcsstr SDL_wcsstr
|
||||
#else
|
||||
#define WIN_StringToUTF8 WIN_StringToUTF8A
|
||||
#define WIN_UTF8ToString WIN_UTF8ToStringA
|
||||
#define SDL_tcslen SDL_strlen
|
||||
#define SDL_tcsstr SDL_strstr
|
||||
#endif
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Sets an error message based on a given HRESULT
|
||||
extern bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
|
||||
|
||||
// Sets an error message based on GetLastError(). Always returns false.
|
||||
extern bool WIN_SetError(const char *prefix);
|
||||
|
||||
// Load a function from combase.dll
|
||||
FARPROC WIN_LoadComBaseFunction(const char *name);
|
||||
|
||||
// Wrap up the oddities of CoInitialize() into a common function.
|
||||
extern HRESULT WIN_CoInitialize(void);
|
||||
extern void WIN_CoUninitialize(void);
|
||||
|
||||
// Wrap up the oddities of RoInitialize() into a common function.
|
||||
extern HRESULT WIN_RoInitialize(void);
|
||||
extern void WIN_RoUninitialize(void);
|
||||
|
||||
// Returns true if we're running on Wine
|
||||
extern BOOL WIN_IsWine(void);
|
||||
|
||||
// Returns true if we're running on Windows XP (any service pack). DOES NOT CHECK XP "OR GREATER"!
|
||||
extern BOOL WIN_IsWindowsXP(void);
|
||||
|
||||
// Returns true if we're running on Windows Vista and newer
|
||||
extern BOOL WIN_IsWindowsVistaOrGreater(void);
|
||||
|
||||
// Returns true if we're running on Windows 7 and newer
|
||||
extern BOOL WIN_IsWindows7OrGreater(void);
|
||||
|
||||
// Returns true if we're running on Windows 8 and newer
|
||||
extern BOOL WIN_IsWindows8OrGreater(void);
|
||||
|
||||
// Returns true if we're running on Windows 11 and newer
|
||||
extern BOOL WIN_IsWindows11OrGreater(void);
|
||||
|
||||
// You need to SDL_free() the result of this call.
|
||||
extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
|
||||
|
||||
// Checks to see if two GUID are the same.
|
||||
extern BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b);
|
||||
extern BOOL WIN_IsEqualIID(REFIID a, REFIID b);
|
||||
|
||||
// Convert between SDL_rect and RECT
|
||||
extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect);
|
||||
extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
|
||||
|
||||
// Returns false if a window client rect is not valid
|
||||
extern bool WIN_WindowRectValid(const RECT *rect);
|
||||
|
||||
extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
|
||||
|
||||
extern HICON WIN_CreateIconFromSurface(SDL_Surface *surface);
|
||||
|
||||
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
|
||||
|
||||
// WideCharToMultiByte, but with some WinXP management.
|
||||
extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
|
||||
|
||||
// parse out command lines from OS if argv is NULL, otherwise pass through unchanged. `*pallocated` must be HeapFree'd by caller if not NULL on successful return. Returns NULL on success, error string on problems.
|
||||
const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated);
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_windows_h_
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_xinput.h"
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
XInputGetState_t SDL_XInputGetState = NULL;
|
||||
XInputSetState_t SDL_XInputSetState = NULL;
|
||||
XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
|
||||
XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx = NULL;
|
||||
XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL;
|
||||
|
||||
static HMODULE s_pXInputDLL = NULL;
|
||||
static int s_XInputDLLRefCount = 0;
|
||||
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
|
||||
bool WIN_LoadXInputDLL(void)
|
||||
{
|
||||
/* Getting handles to system dlls (via LoadLibrary and its variants) is not
|
||||
* supported on Xbox, thus, pointers to XInput's functions can't be
|
||||
* retrieved via GetProcAddress.
|
||||
*
|
||||
* When on Xbox, assume that XInput is already loaded, and directly map
|
||||
* its XInput.h-declared functions to the SDL_XInput* set of function
|
||||
* pointers.
|
||||
*/
|
||||
SDL_XInputGetState = (XInputGetState_t)XInputGetState;
|
||||
SDL_XInputSetState = (XInputSetState_t)XInputSetState;
|
||||
SDL_XInputGetCapabilities = (XInputGetCapabilities_t)XInputGetCapabilities;
|
||||
SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)XInputGetBatteryInformation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WIN_UnloadXInputDLL(void)
|
||||
{
|
||||
}
|
||||
|
||||
#else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
||||
|
||||
bool WIN_LoadXInputDLL(void)
|
||||
{
|
||||
if (s_pXInputDLL) {
|
||||
SDL_assert(s_XInputDLLRefCount > 0);
|
||||
s_XInputDLLRefCount++;
|
||||
return true; // already loaded
|
||||
}
|
||||
|
||||
/* NOTE: Don't load XinputUap.dll
|
||||
* This is XInput emulation over Windows.Gaming.Input, and has all the
|
||||
* limitations of that API (no devices at startup, no background input, etc.)
|
||||
*/
|
||||
s_pXInputDLL = LoadLibrary(TEXT("XInput1_4.dll")); // 1.4 Ships with Windows 8.
|
||||
if (!s_pXInputDLL) {
|
||||
s_pXInputDLL = LoadLibrary(TEXT("XInput1_3.dll")); // 1.3 can be installed as a redistributable component.
|
||||
}
|
||||
if (!s_pXInputDLL) {
|
||||
// "9.1.0" Ships with Vista and Win7, and is more limited than 1.3+ (e.g. XInputGetStateEx is not available.)
|
||||
s_pXInputDLL = LoadLibrary(TEXT("XInput9_1_0.dll"));
|
||||
}
|
||||
if (!s_pXInputDLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_assert(s_XInputDLLRefCount == 0);
|
||||
s_XInputDLLRefCount = 1;
|
||||
|
||||
// 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think...
|
||||
SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, (LPCSTR)100);
|
||||
if (!SDL_XInputGetState) {
|
||||
SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, "XInputGetState");
|
||||
}
|
||||
SDL_XInputSetState = (XInputSetState_t)GetProcAddress(s_pXInputDLL, "XInputSetState");
|
||||
SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress(s_pXInputDLL, "XInputGetCapabilities");
|
||||
// 108 is the ordinal for _XInputGetCapabilitiesEx, which additionally returns VID/PID of the controller.
|
||||
SDL_XInputGetCapabilitiesEx = (XInputGetCapabilitiesEx_t)GetProcAddress(s_pXInputDLL, (LPCSTR)108);
|
||||
SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)GetProcAddress(s_pXInputDLL, "XInputGetBatteryInformation");
|
||||
if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) {
|
||||
WIN_UnloadXInputDLL();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WIN_UnloadXInputDLL(void)
|
||||
{
|
||||
if (s_pXInputDLL) {
|
||||
SDL_assert(s_XInputDLLRefCount > 0);
|
||||
if (--s_XInputDLLRefCount == 0) {
|
||||
FreeLibrary(s_pXInputDLL);
|
||||
s_pXInputDLL = NULL;
|
||||
}
|
||||
} else {
|
||||
SDL_assert(s_XInputDLLRefCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_xinput_h_
|
||||
#define SDL_xinput_h_
|
||||
|
||||
#include "SDL_windows.h"
|
||||
|
||||
#ifdef HAVE_XINPUT_H
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
// Xbox supports an XInput wrapper which is a C++-only header...
|
||||
#include <math.h> // Required to compile with recent MSVC...
|
||||
#include <XInputOnGameInput.h>
|
||||
using namespace XInputOnGameInput;
|
||||
#else
|
||||
#include <xinput.h>
|
||||
#endif
|
||||
#endif // HAVE_XINPUT_H
|
||||
|
||||
#ifndef XUSER_MAX_COUNT
|
||||
#define XUSER_MAX_COUNT 4
|
||||
#endif
|
||||
#ifndef XUSER_INDEX_ANY
|
||||
#define XUSER_INDEX_ANY 0x000000FF
|
||||
#endif
|
||||
#ifndef XINPUT_CAPS_FFB_SUPPORTED
|
||||
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
|
||||
#endif
|
||||
#ifndef XINPUT_CAPS_WIRELESS
|
||||
#define XINPUT_CAPS_WIRELESS 0x0002
|
||||
#endif
|
||||
|
||||
#ifndef XINPUT_DEVSUBTYPE_UNKNOWN
|
||||
#define XINPUT_DEVSUBTYPE_UNKNOWN 0x00
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_GAMEPAD
|
||||
#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_WHEEL
|
||||
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
|
||||
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
|
||||
#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
|
||||
#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_GUITAR
|
||||
#define XINPUT_DEVSUBTYPE_GUITAR 0x06
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
|
||||
#define XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE 0x07
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
|
||||
#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_GUITAR_BASS
|
||||
#define XINPUT_DEVSUBTYPE_GUITAR_BASS 0x0B
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD
|
||||
#define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
|
||||
#endif
|
||||
|
||||
#ifndef XINPUT_FLAG_GAMEPAD
|
||||
#define XINPUT_FLAG_GAMEPAD 0x01
|
||||
#endif
|
||||
|
||||
#ifndef XINPUT_GAMEPAD_DPAD_UP
|
||||
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_DPAD_DOWN
|
||||
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_DPAD_LEFT
|
||||
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_DPAD_RIGHT
|
||||
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_START
|
||||
#define XINPUT_GAMEPAD_START 0x0010
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_BACK
|
||||
#define XINPUT_GAMEPAD_BACK 0x0020
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_LEFT_THUMB
|
||||
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_RIGHT_THUMB
|
||||
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_LEFT_SHOULDER
|
||||
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_RIGHT_SHOULDER
|
||||
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_A
|
||||
#define XINPUT_GAMEPAD_A 0x1000
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_B
|
||||
#define XINPUT_GAMEPAD_B 0x2000
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_X
|
||||
#define XINPUT_GAMEPAD_X 0x4000
|
||||
#endif
|
||||
#ifndef XINPUT_GAMEPAD_Y
|
||||
#define XINPUT_GAMEPAD_Y 0x8000
|
||||
#endif
|
||||
|
||||
#ifndef XINPUT_GAMEPAD_GUIDE
|
||||
#define XINPUT_GAMEPAD_GUIDE 0x0400
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_DEVTYPE_GAMEPAD
|
||||
#define BATTERY_DEVTYPE_GAMEPAD 0x00
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_TYPE_DISCONNECTED
|
||||
#define BATTERY_TYPE_DISCONNECTED 0x00
|
||||
#endif
|
||||
#ifndef BATTERY_TYPE_WIRED
|
||||
#define BATTERY_TYPE_WIRED 0x01
|
||||
#endif
|
||||
#ifndef BATTERY_TYPE_UNKNOWN
|
||||
#define BATTERY_TYPE_UNKNOWN 0xFF
|
||||
#endif
|
||||
#ifndef BATTERY_LEVEL_EMPTY
|
||||
#define BATTERY_LEVEL_EMPTY 0x00
|
||||
#endif
|
||||
#ifndef BATTERY_LEVEL_LOW
|
||||
#define BATTERY_LEVEL_LOW 0x01
|
||||
#endif
|
||||
#ifndef BATTERY_LEVEL_MEDIUM
|
||||
#define BATTERY_LEVEL_MEDIUM 0x02
|
||||
#endif
|
||||
#ifndef BATTERY_LEVEL_FULL
|
||||
#define BATTERY_LEVEL_FULL 0x03
|
||||
#endif
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// typedef's for XInput structs we use
|
||||
|
||||
|
||||
// This is the same as XINPUT_BATTERY_INFORMATION, but always defined instead of just if WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||
typedef struct
|
||||
{
|
||||
BYTE BatteryType;
|
||||
BYTE BatteryLevel;
|
||||
} XINPUT_BATTERY_INFORMATION_EX;
|
||||
|
||||
#ifndef HAVE_XINPUT_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD wButtons;
|
||||
BYTE bLeftTrigger;
|
||||
BYTE bRightTrigger;
|
||||
SHORT sThumbLX;
|
||||
SHORT sThumbLY;
|
||||
SHORT sThumbRX;
|
||||
SHORT sThumbRY;
|
||||
} XINPUT_GAMEPAD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD dwPacketNumber;
|
||||
XINPUT_GAMEPAD Gamepad;
|
||||
} XINPUT_STATE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD wLeftMotorSpeed;
|
||||
WORD wRightMotorSpeed;
|
||||
} XINPUT_VIBRATION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE Type;
|
||||
BYTE SubType;
|
||||
WORD Flags;
|
||||
XINPUT_GAMEPAD Gamepad;
|
||||
XINPUT_VIBRATION Vibration;
|
||||
} XINPUT_CAPABILITIES;
|
||||
|
||||
#endif // HAVE_XINPUT_H
|
||||
|
||||
// This struct is not defined in XInput headers.
|
||||
typedef struct
|
||||
{
|
||||
XINPUT_CAPABILITIES Capabilities;
|
||||
WORD VendorId;
|
||||
WORD ProductId;
|
||||
WORD ProductVersion;
|
||||
WORD unk1;
|
||||
DWORD unk2;
|
||||
} SDL_XINPUT_CAPABILITIES_EX;
|
||||
|
||||
// Forward decl's for XInput API's we load dynamically and use if available
|
||||
typedef DWORD(WINAPI *XInputGetState_t)(
|
||||
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
|
||||
XINPUT_STATE *pState // [out] Receives the current state
|
||||
);
|
||||
|
||||
typedef DWORD(WINAPI *XInputSetState_t)(
|
||||
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
|
||||
XINPUT_VIBRATION *pVibration // [in, out] The vibration information to send to the controller
|
||||
);
|
||||
|
||||
typedef DWORD(WINAPI *XInputGetCapabilities_t)(
|
||||
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
|
||||
DWORD dwFlags, // [in] Input flags that identify the device type
|
||||
XINPUT_CAPABILITIES *pCapabilities // [out] Receives the capabilities
|
||||
);
|
||||
|
||||
// Only available in XInput 1.4 that is shipped with Windows 8 and newer.
|
||||
typedef DWORD(WINAPI *XInputGetCapabilitiesEx_t)(
|
||||
DWORD dwReserved, // [in] Must be 1
|
||||
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
|
||||
DWORD dwFlags, // [in] Input flags that identify the device type
|
||||
SDL_XINPUT_CAPABILITIES_EX *pCapabilitiesEx // [out] Receives the capabilities
|
||||
);
|
||||
|
||||
typedef DWORD(WINAPI *XInputGetBatteryInformation_t)(
|
||||
DWORD dwUserIndex,
|
||||
BYTE devType,
|
||||
XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation);
|
||||
|
||||
extern bool WIN_LoadXInputDLL(void);
|
||||
extern void WIN_UnloadXInputDLL(void);
|
||||
|
||||
extern XInputGetState_t SDL_XInputGetState;
|
||||
extern XInputSetState_t SDL_XInputSetState;
|
||||
extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
|
||||
extern XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx;
|
||||
extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation;
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define XINPUTGETSTATE SDL_XInputGetState
|
||||
#define XINPUTSETSTATE SDL_XInputSetState
|
||||
#define XINPUTGETCAPABILITIES SDL_XInputGetCapabilities
|
||||
#define XINPUTGETCAPABILITIESEX SDL_XInputGetCapabilitiesEx
|
||||
#define XINPUTGETBATTERYINFORMATION SDL_XInputGetBatteryInformation
|
||||
|
||||
#endif // SDL_xinput_h_
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
#include "winresrc.h"
|
||||
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,5,0,0
|
||||
PRODUCTVERSION 3,5,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "SDL\0"
|
||||
VALUE "FileVersion", "3, 5, 0, 0\0"
|
||||
VALUE "InternalName", "SDL\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2026 Sam Lantinga\0"
|
||||
VALUE "OriginalFilename", "SDL3.dll\0"
|
||||
VALUE "ProductName", "Simple DirectMedia Layer\0"
|
||||
VALUE "ProductVersion", "3, 5, 0, 0\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
Reference in New Issue
Block a user