diff options
| -rw-r--r-- | handmade_platform.h | 327 | ||||
| -rwxr-xr-x | install.sh | 16 | ||||
| -rw-r--r-- | linux_handmade.cpp | 1846 | ||||
| -rw-r--r-- | linux_handmade.h | 92 | ||||
| -rw-r--r-- | linux_profile.h | 45 | ||||
| -rw-r--r-- | linux_x11_keysyms_to_strings.c | 2048 | ||||
| -rw-r--r-- | shmximage.cpp | 64 | ||||
| -rw-r--r-- | spall.h | 358 | ||||
| -rw-r--r-- | todo.txt | 50 |
9 files changed, 4846 insertions, 0 deletions
diff --git a/handmade_platform.h b/handmade_platform.h new file mode 100644 index 0000000..bc3f84a --- /dev/null +++ b/handmade_platform.h @@ -0,0 +1,327 @@ +/* date = May 12th 2025 10:53 am */ + +#ifndef HANDMADE_PLATFORM_H +#define HANDMADE_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* + NOTE(casey): + + HANDMADE_INTERNAL: + 0 - Build for public release + 1 - Build for developer only + + HANDMADE_SLOW: + 0 - Not slow code allowed! + 1 - Slow code welcome. + */ + +#include <stdint.h> +#include <stddef.h> + +#if !defined(COMPILER_MSVC) +#define COMPILER_MSVC 0 +#endif + +#if !defined(COMPILER_LLVM) +#define COMPILER_LLVM 0 +#endif + +#if !defined(COMPILER_GNU) +#define COMPILER_GNU 0 +#endif + +#if !COMPILER_MSVC && !COMPILER_LLVM && !COMPILER_GNU +#if _MSC_VER +#undef COMPILER_MSVC +#define COMPILER_MSVC 1 +#elif __GNUC__ +#undef COMPILER_GNU +#define COMPILER_GNU 1 +#else + // TODO(casey): More compilerz!!! +#endif +#endif + +#define internal static +#define local_persist static +#define global_variable static + +#define Pi32 3.14159265359f + + // TODO(casey): Complete assertion macro - don't worry everyone! +#if HANDMADE_SLOW +# if HANDMADE_INTERNAL && OS_LINUX +# define Assert(Expression) if(!(Expression)) { __asm__ volatile("int3"); } +# else +# define Assert(Expression) if(!(Expression)) {*(int *)0 = 0; } +# endif +#else +# define Assert(Expression) +#endif + +#define DebugBreakOnce do { local_persist b32 X = false; Assert(X); X = true; } while(0) + +#define NullExpression { int X = 0; } + +#define Kilobytes(Value) ((Value)*1024LL) +#define Megabytes(Value) (Kilobytes(Value)*1024LL) +#define Gigabytes(Value) (Megabytes(Value)*1024LL) +#define Terabytes(Value) (Gigabytes(Value)*1024LL) + +#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0])) +#define Min(A, B) (((A) < (B)) ? (A) : (B)) +#define Max(A, B) (((A) > (B)) ? (A) : (B)) + + // TODO(casey): swap, min, max ... macros??? + + typedef int8_t s8; + typedef int16_t s16; + typedef int32_t s32; + typedef int64_t s64; + typedef s32 b32; + + typedef uint8_t u8; + typedef uint16_t u16; + typedef uint32_t u32; + typedef uint64_t u64; + + typedef size_t psize; + typedef s32 rune; + + typedef float r32; + typedef double r64; + + typedef struct thread_context + { + int Placeholder; + } thread_context; + + /* + NOTE(casey): Services that the platform layer provides to the game + */ + /* IMPORTANT(casey): + + These are NOT for doing anything in the shipping game - they are + blocking and the write doesn't protect against lost data! + */ + struct debug_platform_read_file_result + { + psize ContentsSize; + void *Contents; + }; + +#define DEBUG_PLATFORM_FREE_FILE_MEMORY(Name) void Name(thread_context *Thread, void *Memory, psize MemorySize) + typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(debug_platform_free_file_memory); + +#define DEBUG_PLATFORM_READ_ENTIRE_FILE(Name) debug_platform_read_file_result Name(thread_context *Thread, char *FileName) + typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(debug_platform_read_entire_file); + +#define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(Name) b32 Name(thread_context *Thread, char *FileName, psize MemorySize, void *Memory) + typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(debug_platform_write_entire_file); + +#define PLATFORM_RUN_COMMAND_AND_GET_OUTPUT(Name) psize Name(thread_context *Thread, char *OutputBuffer, char *Command[]) + typedef PLATFORM_RUN_COMMAND_AND_GET_OUTPUT(platform_run_command_and_get_output); + +#define PLATFORM_GET_WALL_CLOCK(Name) struct timespec Name(void) + typedef PLATFORM_GET_WALL_CLOCK(platform_get_wall_clock); + +#define PLATFORM_LOG(Name) void Name(char *Text) + typedef PLATFORM_LOG(platform_log); + +#define PLATFORM + + /* + NOTE(casey): Services that the game provides to the platform layer. + (this may expand in the future - sound on separate thread, etc.) + */ + + // FOUR THINGS - timing, controller/keyboard input, bitmap buffer to use, sound buffer to use + + // TODO(casey): In the future, rendering _specifically_ will become a three-tiered abstraction!!! + typedef struct game_offscreen_buffer + { + // NOTE(casey): Pixels are alwasy 32-bits wide, Memory Order BB GG RR XX + void *Memory; + s32 Width; + s32 Height; + s32 Pitch; + s32 BytesPerPixel; + } game_offscreen_buffer; + + typedef struct game_sound_output_buffer + { + s32 SamplesPerSecond; + s32 SampleCount; + s16 *Samples; + } game_sound_output_buffer; + + typedef struct game_text_button + { + rune Codepoint; + // TODO(luca): Use flag and bits. + b32 Control; + b32 Shift; + b32 Alt; + } game_text_button; + + typedef struct game_button_state + { + s32 HalfTransitionCount; + b32 EndedDown; + } game_button_state; + + typedef struct game_controller_input + { + b32 IsConnected; + + struct + { + u32 Count; + game_text_button Buffer[64]; + } Text; + + b32 IsAnalog; + r32 StickAverageX; + r32 StickAverageY; + + union + { + game_button_state Buttons[12]; + struct + { + game_button_state MoveUp; + game_button_state MoveDown; + game_button_state MoveLeft; + game_button_state MoveRight; + + game_button_state ActionUp; + game_button_state ActionDown; + game_button_state ActionLeft; + game_button_state ActionRight; + + game_button_state LeftShoulder; + game_button_state RightShoulder; + + game_button_state Back; + game_button_state Start; + + // NOTE(casey): All buttons must be added above this line + + game_button_state Terminator; + }; + }; + } game_controller_input; + + typedef enum + { + PlatformCursorShape_None = 0, + PlatformCursorShape_Grab, + } platform_cursor_shape; + + typedef enum + { + PlatformMouseButton_Left = 0, + PlatformMouseButton_Right, + PlatformMouseButton_Middle, + PlatformMouseButton_ScrollUp, + PlatformMouseButton_ScrollDown, + PlatformMouseButton_Count + } platform_mouse_buttons; + + typedef struct game_input + { + game_button_state MouseButtons[PlatformMouseButton_Count]; + s32 MouseX, MouseY, MouseZ; + + r32 dtForFrame; + + game_controller_input Controllers[5]; + } game_input; + + inline b32 WasPressed(game_button_state State) + { + b32 Result = ((State.HalfTransitionCount > 1) || + (State.HalfTransitionCount == 1 && State.EndedDown)); + return Result; + } + + //- Threading + typedef struct platform_work_queue platform_work_queue; + +#define PLATFORM_WORK_QUEUE_CALLBACK(Name) void Name(platform_work_queue *Queue, void *Data) + typedef PLATFORM_WORK_QUEUE_CALLBACK(platform_work_queue_callback); + struct platform_work_queue_entry + { + platform_work_queue_callback *Callback; + void *Data; + }; + + typedef void platform_add_entry(platform_work_queue *Queue, platform_work_queue_callback *CallBack, void *Data); + typedef void platform_complete_all_work(platform_work_queue *Queue); + //- + +#define PLATFORM_CHANGE_CURSOR(name) void name(platform_cursor_shape Shape) + typedef PLATFORM_CHANGE_CURSOR(platform_change_cursor); + + typedef struct game_memory + { + b32 IsInitialized; + + psize PermanentStorageSize; + void *PermanentStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup + + psize TransientStorageSize; + void *TransientStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup + + platform_work_queue *HighPriorityQueue; + + platform_add_entry *PlatformAddEntry; + platform_complete_all_work *PlatformCompleteAllWork; + platform_run_command_and_get_output *PlatformRunCommandAndGetOutput; + platform_log *PlatformLog; + platform_change_cursor *PlatformChangeCursor; + platform_get_wall_clock *PlatformGetWallClock; + + debug_platform_free_file_memory *DEBUGPlatformFreeFileMemory; + debug_platform_read_entire_file *DEBUGPlatformReadEntireFile; + debug_platform_write_entire_file *DEBUGPlatformWriteEntireFile; + } game_memory; + +#define GAME_UPDATE_AND_RENDER(name) void name(thread_context *Thread, game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer) + typedef GAME_UPDATE_AND_RENDER(game_update_and_render); + + // NOTE(casey): At the moment, this has to be a very fast function, it cannot be + // more than a millisecond or so. + // TODO(casey): Reduce the pressure on this function's performance by measuring it + // or asking about it, etc. +#define GAME_GET_SOUND_SAMPLES(name) void name(thread_context *Thread, game_memory *Memory, game_sound_output_buffer *SoundBuffer) + typedef GAME_GET_SOUND_SAMPLES(game_get_sound_samples); + + inline u32 + SafeTruncateUInt64(u64 Value) + { + // TODO(casey): Defines for maximum values + Assert(Value <= 0xFFFFFFFF); + u32 Result = (u32)Value; + return(Result); + } + + inline game_controller_input *GetController(game_input *Input, u32 ControllerIndex) + { + Assert(ControllerIndex < ArrayCount(Input->Controllers)); + + game_controller_input *Result = &Input->Controllers[ControllerIndex]; + return(Result); + } + + global_variable platform_log *Log; + +#ifdef __cplusplus +} +#endif + +#endif //HANDMADE_PLATFORM_H diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..551a283 --- /dev/null +++ b/install.sh @@ -0,0 +1,16 @@ + +CurrentDir="$(readlink -f ".")" + +ScriptDir="$(dirname "$(readlink -f "$0")")" +cd "$ScriptDir" + +Dir="$CurrentDir/linuxhmh" +rm -rf "$Dir" +mkdir "$Dir" + +Files="$(find "$ScriptDir" -type f \( -iname '*.cpp' -o -iname '*.c' -o -iname '*.h' \))" + +for File in $Files +do + ln -sf "$File" "$Dir" +done diff --git a/linux_handmade.cpp b/linux_handmade.cpp new file mode 100644 index 0000000..7340efc --- /dev/null +++ b/linux_handmade.cpp @@ -0,0 +1,1846 @@ +#include <stdio.h> +//- POSIX +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <semaphore.h> +#include <pthread.h> +#include <signal.h> +//- GCC +#include <x86gprintrin.h> +#include <xmmintrin.h> +//- Linux +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <linux/limits.h> +#include <linux/input.h> +#include <alsa/asoundlib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysymdef.h> +#include <X11/extensions/Xrandr.h> +#include <X11/cursorfont.h> +//- Handmade +#include "handmade_platform.h" +#include "linux_handmade.h" +#include "linux_profile.h" +#include "linux_x11_keysyms_to_strings.c" + +#define ALSA_RECOVER_SILENT true +#define MAX_PLAYER_COUNT 4 + +// NOTE(luca): Bits are layed out over multiple bytes. This macro checks which byte the bit will be set in. +#define IsEvdevBitSet(Bit, Array) (Array[(Bit) / 8] & (1 << ((Bit) % 8))) +#define BytesNeededForBits(Bits) ((Bits + 7) / 8) + +// TODO(luca): Use C11 atomics. + +#define InterlockedIncrement(Pointer, Value) __sync_fetch_and_add(Pointer, Value) +// NOTE(luca): I used to use __sync_synchronize(), but that would add an extra +//`lock or` instruction. (See Compiler Explorer). +#define ReadWriteBarrier __asm__ __volatile__ ("" : : : "memory") +#define InterlockedCompareExchange(Pointer, Exchange, Comparand) \ +__sync_val_compare_and_swap(Pointer, Comparand, Exchange) + +global_variable b32 GlobalRunning; +global_variable b32 GlobalPaused; + +global_variable Window GlobalWindowHandle; +global_variable Display *GlobalDisplayHandle; + +#define MemoryCopy memcpy +#define MemorySet memset + +psize StringLength(char *String) +{ + psize Result = 0; + + while(*String++) Result++; + + return Result; +} + +void CatStrings(size_t SourceACount, char *SourceA, + size_t SourceBCount, char *SourceB, + size_t DestCount, char *Dest) +{ + for(size_t Index = 0; + Index < SourceACount; + Index++) + { + *Dest++ = *SourceA++; + } + + for(size_t Index = 0; + Index < SourceBCount; + Index++) + { + *Dest++ = *SourceB++; + } +} + +internal rune +ConvertUTF8StringToRune(u8 UTF8String[4]) +{ + rune Codepoint = 0; + + if((UTF8String[0] & 0x80) == 0x00) + { + Codepoint = UTF8String[0]; + } + else if((UTF8String[0] & 0xE0) == 0xC0) + { + Codepoint = ( + ((UTF8String[0] & 0x1F) << 6*1) | + ((UTF8String[1] & 0x3F) << 6*0) + ); + } + else if((UTF8String[0] & 0xF0) == 0xE0) + { + Codepoint = ( + ((UTF8String[0] & 0x0F) << 6*2) | + ((UTF8String[1] & 0x3F) << 6*1) | + ((UTF8String[2] & 0x3F) << 6*0) + ); + } + else if((UTF8String[0] & 0xF8) == 0xF8) + { + Codepoint = ( + ((UTF8String[0] & 0x0E) << 6*3) | + ((UTF8String[1] & 0x3F) << 6*2) | + ((UTF8String[2] & 0x3F) << 6*1) | + ((UTF8String[3] & 0x3F) << 6*0) + ); + } + else + { + Assert(0); + } + + return Codepoint; +} + +struct linux_init_alsa_result +{ + snd_pcm_t *PCMHandle; + snd_pcm_hw_params_t *PCMParams; +}; + +internal linux_init_alsa_result LinuxInitALSA() +{ + linux_init_alsa_result Result = {}; + + int PCMResult = 0; + if((PCMResult = snd_pcm_open(&Result.PCMHandle, "default", + SND_PCM_STREAM_PLAYBACK, 0)) == 0) + { + snd_pcm_hw_params_alloca(&Result.PCMParams); + snd_pcm_hw_params_any(Result.PCMHandle, Result.PCMParams); + u32 ChannelCount = 2; + u32 SampleRate = 48000; + + if((PCMResult = snd_pcm_hw_params_set_access(Result.PCMHandle, Result.PCMParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { + // TODO(luca): Logging + // snd_strerror(pcm) + } + if((PCMResult = snd_pcm_hw_params_set_format(Result.PCMHandle, Result.PCMParams, SND_PCM_FORMAT_S16_LE)) < 0) + { + // TODO(luca): Logging + } + if((PCMResult = snd_pcm_hw_params_set_channels(Result.PCMHandle, Result.PCMParams, ChannelCount)) < 0) + { + // TODO(luca): Logging + } + if((PCMResult = snd_pcm_hw_params_set_rate_near(Result.PCMHandle, Result.PCMParams, &SampleRate, 0)) < 0) + { + // TODO(luca): Logging + } + if((PCMResult = snd_pcm_nonblock(Result.PCMHandle, 1)) < 0) + { + // TODO(luca): Logging + } + if((PCMResult = snd_pcm_reset(Result.PCMHandle)) < 0) + { + // TODO(luca): Logging + } + if((PCMResult = snd_pcm_hw_params(Result.PCMHandle, Result.PCMParams)) < 0) + { + // TODO(luca): Logging + } + + } + else + { + // TODO(luca): Logging + } + + return Result; +} + +internal void +LinuxSigINTHandler(int SigNum) +{ + GlobalRunning = false; +} + +//- Threading +internal void +LinuxAddEntry(platform_work_queue *Queue, platform_work_queue_callback *Callback, void *Data) +{ + // TODO(casey): Switch to InterlockedCompareExchange eventually so that any thread can add. + u32 NewNextEntryToWrite = ((Queue->NextEntryToWrite + 1) % ArrayCount(Queue->Entries)); + Assert(NewNextEntryToWrite != Queue->NextEntryToRead); + platform_work_queue_entry *Entry = Queue->Entries + Queue->NextEntryToWrite; + Entry->Data = Data; + Entry->Callback = Callback; + Queue->CompletionGoal++; + Queue->NextEntryToWrite = NewNextEntryToWrite; + ReadWriteBarrier; + s32 Ret = sem_post(&Queue->SemaphoreHandle); + Assert(Ret == 0); +} + +internal b32 +LinuxDoNextWorkQueueEntry(platform_work_queue *Queue) +{ + b32 WeShouldSleep = false; + + u32 OriginalNextEntryToRead = Queue->NextEntryToRead; + u32 NewNextEntryToRead = (OriginalNextEntryToRead + 1) % ArrayCount(Queue->Entries); + if(Queue->NextEntryToRead != Queue->NextEntryToWrite) + { + u32 Index = InterlockedCompareExchange(&Queue->NextEntryToRead, + NewNextEntryToRead, + OriginalNextEntryToRead); + if(Index == OriginalNextEntryToRead) + { + platform_work_queue_entry Entry = Queue->Entries[Index]; + Entry.Callback(Queue, Entry.Data); + InterlockedIncrement(&Queue->CompletionCount, 1); + } + } + else + { + WeShouldSleep = true; + } + + return WeShouldSleep; +} + +internal void +LinuxCompleteAllWork(platform_work_queue *Queue) +{ + while((Queue->CompletionGoal != Queue->CompletionCount)) + { + LinuxDoNextWorkQueueEntry(Queue); + } + + Queue->CompletionGoal = 0; + Queue->CompletionCount = 0; +} + +void* ThreadProc(void *UserData) +{ + linux_thread_info *ThreadInfo = (linux_thread_info *)UserData; + + while(1) + { + if(LinuxDoNextWorkQueueEntry(ThreadInfo->Queue)) + { + sem_wait(&ThreadInfo->Queue->SemaphoreHandle); + } + } +} + +internal s64 +LinuxGetThreadsCount(void) +{ + s64 Threads = sysconf(_SC_NPROCESSORS_CONF); + return Threads; +} + +//- Gamepads +internal void +LinuxGetAxisInfo(linux_gamepad *GamePad, linux_gamepad_axes_enum Axis, int AbsAxis) +{ + input_absinfo AxesInfo = {}; + if(ioctl(GamePad->File, EVIOCGABS(AbsAxis), &AxesInfo) != -1) + { + GamePad->Axes[Axis].Minimum = AxesInfo.minimum; + GamePad->Axes[Axis].Maximum = AxesInfo.maximum; + GamePad->Axes[Axis].Fuzz = AxesInfo.fuzz; + GamePad->Axes[Axis].Flat = AxesInfo.flat; + } +} + +PLATFORM_WORK_QUEUE_CALLBACK(LinuxThreadCloseFD) +{ + s32 File = (s32)((u64)(Data)); + + s32 Ret = close(File); + if(Ret) + { + // TODO(luca): Logging + } +} + +internal void +LinuxOpenGamePad(platform_work_queue *Queue, char *FilePath, linux_gamepad *GamePad) +{ + GamePad->File = open(FilePath, O_RDWR|O_NONBLOCK); + + if(GamePad->File != -1) + { + int Version = 0; + int IsCompatible = true; + + // TODO(luca): Check versions + ioctl(GamePad->File, EVIOCGVERSION, &Version); + ioctl(GamePad->File, EVIOCGNAME(sizeof(GamePad->Name)), GamePad->Name); + + char SupportedEventBits[BytesNeededForBits(EV_MAX)] = {}; + if(ioctl(GamePad->File, EVIOCGBIT(0, sizeof(SupportedEventBits)), SupportedEventBits) != -1) + { + if(!IsEvdevBitSet(EV_ABS, SupportedEventBits)) + { + // TODO(luca): Logging + IsCompatible = false; + } + if(!IsEvdevBitSet(EV_KEY, SupportedEventBits)) + { + // TODO(luca): Logging + IsCompatible = false; + } + } + + char SupportedKeyBits[BytesNeededForBits(KEY_MAX)] = {}; + if(ioctl(GamePad->File, EVIOCGBIT(EV_KEY , sizeof(SupportedKeyBits)), SupportedKeyBits) != -1) + { + if(!IsEvdevBitSet(BTN_GAMEPAD, SupportedKeyBits)) + { + // TODO(luca): Logging + IsCompatible = false; + } + } + + GamePad->SupportsRumble = IsEvdevBitSet(EV_FF, SupportedEventBits); + + if(IsCompatible) + { + // NOTE(luca): Map evdev axes to my enum. + LinuxGetAxisInfo(GamePad, LSTICKX, ABS_X); + LinuxGetAxisInfo(GamePad, LSTICKY, ABS_Y); + LinuxGetAxisInfo(GamePad, RSTICKX, ABS_RX); + LinuxGetAxisInfo(GamePad, RSTICKY, ABS_RY); + LinuxGetAxisInfo(GamePad, LSHOULDER, ABS_Z); + LinuxGetAxisInfo(GamePad, RSHOULDER, ABS_RZ); + LinuxGetAxisInfo(GamePad, DPADX, ABS_HAT0X); + LinuxGetAxisInfo(GamePad, DPADY, ABS_HAT0Y); + + MemoryCopy(GamePad->FilePath, FilePath, StringLength(FilePath)); + } + else + { + LinuxAddEntry(Queue, LinuxThreadCloseFD, (void *)GamePad->File); + *GamePad = {}; + GamePad->File = -1; + } + } +} + +// TODO(luca): Make this work in the case of multiple displays. +internal r32 LinuxGetMonitorRefreshRate(Display *DisplayHandle, Window RootWindow) +{ + r32 Result = 0; + + void *LibraryHandle = dlopen("libXrandr.so.2", RTLD_NOW); + + if(LibraryHandle) + { + typedef XRRScreenResources *xrr_get_screen_resources(Display *Display, Window Window); + typedef XRRCrtcInfo *xrr_get_crtc_info(Display* Display, XRRScreenResources *Resources, RRCrtc Crtc); + + xrr_get_screen_resources *XRRGetScreenResources = (xrr_get_screen_resources *)dlsym(LibraryHandle, "XRRGetScreenResources"); + xrr_get_crtc_info *XRRGetCrtcInfo = (xrr_get_crtc_info *)dlsym(LibraryHandle, "XRRGetCrtcInfo"); + + XRRScreenResources *ScreenResources = XRRGetScreenResources(DisplayHandle, RootWindow); + + RRMode ActiveModeID = 0; + for(int CRTCIndex = 0; + CRTCIndex< ScreenResources->ncrtc; + CRTCIndex++) + { + XRRCrtcInfo *CRTCInfo = XRRGetCrtcInfo(DisplayHandle, ScreenResources, ScreenResources->crtcs[CRTCIndex]); + if(CRTCInfo->mode) + { + ActiveModeID = CRTCInfo->mode; + } + } + + r32 ActiveRate = 0; + for(int ModeIndex = 0; + ModeIndex < ScreenResources->nmode; + ModeIndex++) + { + XRRModeInfo ModeInfo = ScreenResources->modes[ModeIndex]; + if(ModeInfo.id == ActiveModeID) + { + Assert(ActiveRate == 0); + Result = (r32)ModeInfo.dotClock / ((r32)ModeInfo.hTotal * (r32)ModeInfo.vTotal); + } + } + + dlclose(LibraryHandle); + } + + return Result; +} + +internal void LinuxBuildFileNameFromExecutable(char *Dest, linux_state *State, char *FileName) +{ + char *Path = State->ExecutablePath; + + size_t LastSlash = 0; + for(char *Scan = Path; + *Scan; + Scan++) + { + if(*Scan == '/') + { + LastSlash = Scan - Path; + } + } + + for(size_t Index = 0; + Index < LastSlash + 1; + Index++) + { + *Dest++ = *Path++; + } + + while(*FileName) + *Dest++ = *FileName++; +} + +GAME_UPDATE_AND_RENDER(LinuxGameUpdateAndRenderStub) {} +GAME_GET_SOUND_SAMPLES(LinuxGameGetSoundSamplesStub) {} + +internal linux_game_code LinuxLoadGameCode(char *LibraryPath) +{ + linux_game_code Result = {}; + + Result.LibraryHandle = dlopen(LibraryPath, RTLD_NOW); + if(Result.LibraryHandle) + { + Result.UpdateAndRender = (game_update_and_render *)dlsym(Result.LibraryHandle, "GameUpdateAndRender"); + Result.GetSoundSamples = (game_get_sound_samples *)dlsym(Result.LibraryHandle, "GameGetSoundSamples"); + } + else + { + printf("dlerror: %s\n", dlerror()); + Result.UpdateAndRender = (game_update_and_render *)LinuxGameUpdateAndRenderStub; + Result.GetSoundSamples = (game_get_sound_samples *)LinuxGameGetSoundSamplesStub; + } + + return Result; +} + +internal void LinuxUnloadGameCode(linux_game_code *Game) +{ + if(Game->LibraryHandle) + { + dlclose(Game->LibraryHandle); + } +} + +internal void LinuxProcessKeyPress(game_button_state *ButtonState, b32 IsDown) +{ + if(ButtonState->EndedDown != IsDown) + { + ButtonState->EndedDown = IsDown; + ButtonState->HalfTransitionCount++; + } +} + +internal inline +linux_replay_buffer *LinuxGetReplayBuffer(linux_state *State, int SlotIndex) +{ + linux_replay_buffer *Result = 0; + + Assert(SlotIndex < (int)ArrayCount(State->ReplayBuffers)); + Assert(SlotIndex >= 0); + + Result = &State->ReplayBuffers[SlotIndex]; + return Result; +} + +internal inline +void LinuxAppendToReplayBuffer(linux_replay_buffer *Buffer, void *Memory, size_t Size) +{ + Assert(Buffer->MemorySize > Buffer->Pos + Size); + + MemoryCopy(Buffer->Memory + Buffer->Pos, Memory, Size); + Buffer->Pos += Size; +} + +internal +void LinuxBeginRecordingInput(linux_state *State, int SlotIndex) +{ + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, SlotIndex); + + if(Buffer->Memory == 0) + { + // NOTE(luca): Amount of frames of input to pre-allocate for. + int FramesCount = 2048; + Buffer->MemorySize = State->TotalSize + 2048 * sizeof(game_input); + Buffer->Memory = (char *)mmap(0, Buffer->MemorySize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS| + MAP_SHARED, -1, 0); + if(Buffer->Memory == MAP_FAILED) + { + *Buffer = {}; + } + } + + if(Buffer->Memory) + { + LinuxAppendToReplayBuffer(Buffer, State->GameMemoryBlock, State->TotalSize); + State->InputRecordingIndex = SlotIndex; + } + +} + +internal void LinuxEndRecordingInput(linux_state *State, int SlotIndex) +{ + Assert(State->InputRecordingIndex); + + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, SlotIndex); + Buffer->Size = Buffer->Pos; + + State->InputRecordingIndex = 0; +} + +internal void +LinuxBeginInputPlayBack(linux_state *State, int SlotIndex) +{ + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, SlotIndex); + + MemoryCopy(State->GameMemoryBlock, Buffer->Memory, State->TotalSize); + Buffer->Pos = State->TotalSize; + + State->InputPlayingIndex = SlotIndex; +} + +internal void LinuxEndInputPlayBack(linux_state *State) +{ + Assert(State->InputPlayingIndex); + + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, State->InputPlayingIndex); +} + +internal void LinuxRecordInput(linux_state *State, game_input *Input) +{ + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, State->InputRecordingIndex); + + LinuxAppendToReplayBuffer(Buffer, (char *)Input, sizeof(*Input)); +} + +internal void LinuxPlayBackInput(linux_state *State, game_input *Input) +{ + Assert(State->InputPlayingIndex); + + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, State->InputPlayingIndex); + MemoryCopy(Input, (Buffer->Memory + Buffer->Pos), sizeof(*Input)); + Buffer->Pos += sizeof(*Input); + + // NOTE(luca): This is where we do looping. + if(Buffer->Pos >= Buffer->Size) + { + LinuxEndInputPlayBack(State); + LinuxBeginInputPlayBack(State, State->InputPlayingIndex); + } +} + +internal void LinuxHideCursor(Display *DisplayHandle, Window WindowHandle) +{ + XColor black = {}; + char NoData[8] = {}; + + Pixmap BitmapNoData = XCreateBitmapFromData(DisplayHandle, WindowHandle, NoData, 8, 8); + Cursor InvisibleCursor = XCreatePixmapCursor(DisplayHandle, BitmapNoData, BitmapNoData, + &black, &black, 0, 0); + XDefineCursor(DisplayHandle, WindowHandle, InvisibleCursor); + XFreeCursor(DisplayHandle, InvisibleCursor); + XFreePixmap(DisplayHandle, BitmapNoData); +} + +internal void LinuxShowCursor(Display *DisplayHandle, Window WindowHandle) +{ + XUndefineCursor(DisplayHandle, WindowHandle); +} + +internal void LinuxSetSizeHint(Display *DisplayHandle, Window WindowHandle, + int MinWidth, int MinHeight, + int MaxWidth, int MaxHeight) +{ + XSizeHints Hints = {}; + if(MinWidth > 0 && MinHeight > 0) Hints.flags |= PMinSize; + if(MaxWidth > 0 && MaxHeight > 0) Hints.flags |= PMaxSize; + + Hints.min_width = MinWidth; + Hints.min_height = MinHeight; + Hints.max_width = MaxWidth; + Hints.max_height = MaxHeight; + + XSetWMNormalHints(DisplayHandle, WindowHandle, &Hints); +} + +internal void LinuxProcessPendingMessages(Display *DisplayHandle, Window WindowHandle, + XIC InputContext, Atom WM_DELETE_WINDOW, linux_state *State, game_input *Input, game_controller_input *KeyboardController) +{ + XEvent WindowEvent = {}; + while(XPending(DisplayHandle) > 0) + { + XNextEvent(DisplayHandle, &WindowEvent); + b32 FilteredEvent = XFilterEvent(&WindowEvent, 0); + if(FilteredEvent) + { + Assert(WindowEvent.type == KeyPress || WindowEvent.type == KeyRelease); + } + + switch(WindowEvent.type) + { + case KeyPress: + case KeyRelease: + { + //- How text input works + // The needs: + // 1. Preserve game buttons, so that we can switch between a "game mode" or + // "text input mode". + // 2. Text input using the input method of the user which should allow for utf8 characters. + // 3. Hotkey support. Eg. quickly navigating text. + // 3 will be supported by 2 for code reuse. + // + // We are going to send a buffer text button presses to the game layer, this solves these + // issues: + // - Pressing the same key multiple times in one frame. + // - Having modifiers be specific to each key press. + // - Not having to create a button record for each possible character in the structure. + // - Key events come in one at a time in the event loop, thus we need to have a buffer for + // multiple keys pressed on a single frame. + // + // We store a count along the buffer and in the buffer we store the utf8 codepoint and its + // modifiers. + // The app code is responsible for traversing this buffer and applying the logic. + + // The problem of input methods and hotkeys: + // Basically the problem is that if we allow the input method and combo's that could be + // filtered by the input method it won't seem consistent to the user. + // So we don't allow key bound to the input method to have an effect and we only pass key + // inputs that have not been filtered. + // + // In the platform layer we handle the special case were the input methods creates non- + // printable characters and we decompose those key inputs since non-printable characters + // have no use anymore. + + // Extra: + // - I refuse to check which keys bind to what modifiers. It's not important. + + // - Handy resources: + // - https://www.coderstool.com/unicode-text-converter + // - man Compose(5). + // - https://en.wikipedia.org/wiki/Control_key#History + + KeySym Symbol = XLookupKeysym(&WindowEvent.xkey, 0); + b32 IsDown = (WindowEvent.type == KeyPress); + + // TODO(luca): Refresh mappings. + // NOTE(luca): Only KeyPress events see man page of Xutf8LookupString(). And skip filtered events for text input, but keep them for controller. + if(IsDown && !FilteredEvent) + { + // TODO(luca): Choose a better error value. + rune Codepoint = L'ù'; + u8 LookupBuffer[4] = {}; + Status LookupStatus = {}; + + s32 BytesLookepdUp = Xutf8LookupString(InputContext, &WindowEvent.xkey, + (char *)&LookupBuffer, ArrayCount(LookupBuffer), + 0, &LookupStatus); + Assert(LookupStatus != XBufferOverflow); + Assert(BytesLookepdUp <= 4); + + if(LookupStatus!= XLookupNone && + LookupStatus!= XLookupKeySym) + { + if(BytesLookepdUp) + { + Assert(KeyboardController->Text.Count < ArrayCount(KeyboardController->Text.Buffer)); + + Codepoint = ConvertUTF8StringToRune(LookupBuffer); + + // NOTE(luca): Input methods might produce non printable characters (< ' '). If this + // happens we try to "decompose" the key input. + if(Codepoint < ' ' && Codepoint >= 0) + { + if(Symbol >= XK_space) + { + Codepoint = (char)(' ' + (Symbol - XK_space)); + } + } + + // NOTE(luca): Since this is only for text input we pass Return and Backspace as codepoints. + if((Codepoint >= ' ' || Codepoint < 0) || + Codepoint == '\r' || Codepoint == '\b' || Codepoint == '\n') + { + game_text_button *TextButton = &KeyboardController->Text.Buffer[KeyboardController->Text.Count++]; + TextButton->Codepoint = Codepoint; + TextButton->Shift = (WindowEvent.xkey.state & ShiftMask); + TextButton->Control = (WindowEvent.xkey.state & ControlMask); + TextButton->Alt = (WindowEvent.xkey.state & Mod1Mask); +#if 0 + printf("%d bytes '%c' %d (%c|%c|%c)\n", + BytesLookepdUp, + ((Codepoint >= ' ') ? (char)Codepoint : '\0'), + Codepoint, + ((WindowEvent.xkey.state & ShiftMask) ? 'S' : ' '), + ((WindowEvent.xkey.state & ControlMask) ? 'C' : ' '), + ((WindowEvent.xkey.state & Mod1Mask) ? 'A' : ' ')); +#endif + } + else + { + // TODO(luca): Logging + } + + } + } + } + + if(0) {} + else if(Symbol == XK_w) + { + LinuxProcessKeyPress(&KeyboardController->MoveUp, IsDown); + } + else if(Symbol == XK_a) + { + LinuxProcessKeyPress(&KeyboardController->MoveLeft, IsDown); + } + else if(Symbol == XK_r) + { + LinuxProcessKeyPress(&KeyboardController->MoveDown, IsDown); + } + else if(Symbol == XK_s) + { + LinuxProcessKeyPress(&KeyboardController->MoveRight, IsDown); + } + else if(Symbol == XK_Up) + { + LinuxProcessKeyPress(&KeyboardController->ActionUp, IsDown); + } + else if(Symbol == XK_Left) + { + LinuxProcessKeyPress(&KeyboardController->ActionLeft, IsDown); + } + else if(Symbol == XK_Down) + { + LinuxProcessKeyPress(&KeyboardController->ActionDown, IsDown); + } + else if(Symbol == XK_Right) + { + LinuxProcessKeyPress(&KeyboardController->ActionRight, IsDown); + } + else if(Symbol == XK_space) + { + LinuxProcessKeyPress(&KeyboardController->Start, IsDown); + } + else if((WindowEvent.xkey.state & Mod1Mask) && + (Symbol == XK_p)) + { + if(IsDown) + { + GlobalPaused = !GlobalPaused; + } + } + else if((WindowEvent.xkey.state & Mod1Mask) && + (Symbol == XK_l)) + { + if(IsDown) + { + if(State->InputPlayingIndex == 0) + { + if(State->InputRecordingIndex == 0) + { + PROFILE_START_LABEL("begin recording"); + LinuxBeginRecordingInput(State, 1); + PROFILE_END; + } + else + { + PROFILE_START_LABEL("end recording"); + LinuxEndRecordingInput(State, 1); + PROFILE_END; + PROFILE_START_LABEL("begin playback"); + LinuxBeginInputPlayBack(State, 1); + PROFILE_END; + } + } + else + { + // TODO(luca): Reset buttons so they aren't held? + for(u32 ButtonIndex = 0; + ButtonIndex < ArrayCount(KeyboardController->Buttons); + ButtonIndex++) + { + KeyboardController->Buttons[ButtonIndex] = {}; + } + PROFILE_START_LABEL("end playback"); + LinuxEndInputPlayBack(State); + PROFILE_END; + // TODO(luca): Save to a file and free + linux_replay_buffer *Buffer = LinuxGetReplayBuffer(State, State->InputPlayingIndex); + + int Res = munmap(Buffer->Memory, Buffer->MemorySize); + if(Res == 0) + { + *Buffer = {}; + } + else + { + Buffer->Pos = 0; + Buffer->Size = 0; + } + State->InputPlayingIndex = 0; + + } + } + } + else if((WindowEvent.xkey.state & Mod1Mask) && + (Symbol == XK_Return)) + { + if(IsDown) + { + // NOTE(luca): Something like EWMH hints exists, which could be used to set the window to fullscreen instead: https://www.tonyobryan.com//index.php?article=9 + // TODO(luca): Move to LinuxState + + // Notify the window manager that we want to remove window decorations. + struct hints + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; + }; + + s32 XRet = 0; + s32 Width, Height, X, Y; + hints Hints = {}; + Atom property = XInternAtom(DisplayHandle, "_MOTIF_WM_HINTS", True); + + // TODO(luca): Pass in actual width/height of window and max resolution of screen and modify these values accordingly. + if(!State->IsFullScreen) + { + Hints.flags = 2; + Hints.decorations = false; + + Width = 1920; + Height = 1080; + X = 0; + Y = 0; + + /* + // Grab mouse and keyboard input. + XRet = XMapRaised(DisplayHandle, WindowHandle); // Sometimes works. + XRet = XGrabPointer(DisplayHandle, WindowHandle, true, 0, GrabModeAsync, GrabModeAsync, WindowHandle, 0L, CurrentTime); + XRet = XGrabKeyboard(DisplayHandle, WindowHandle, false, GrabModeAsync, GrabModeAsync, CurrentTime); + */ + + LinuxSetSizeHint(DisplayHandle, WindowHandle, Width, Height, Width, Height); + } + else + { + Hints.flags = 2; + Hints.decorations = true; + + Width = 1920/2; + Height = 1080/2; + X = 1920 - Width - 10; + Y = 10; + + XRet = XUngrabKeyboard(DisplayHandle, CurrentTime); + XRet = XUngrabPointer(DisplayHandle, CurrentTime); + LinuxSetSizeHint(DisplayHandle, WindowHandle, 0, 0, 0, 0); + } + + XRet = XChangeProperty(DisplayHandle, WindowHandle, + property, property, + 32, PropModeReplace, (unsigned char*)&Hints, 5); + XRet = XMoveResizeWindow(DisplayHandle, WindowHandle, X, Y, Width, Height); + + State->IsFullScreen = !State->IsFullScreen; + } + } + else if((WindowEvent.xkey.state & Mod1Mask) && + (Symbol == XK_F4)) + { + GlobalRunning = false; + } + } break; + + case ButtonPress: + case ButtonRelease: + { + b32 IsDown = (WindowEvent.type == ButtonPress); + u32 Button = WindowEvent.xbutton.button; + + if(0) {} + else if(Button == Button1) + { + LinuxProcessKeyPress(&Input->MouseButtons[PlatformMouseButton_Left], IsDown); + } + else if(Button == Button2) + { + LinuxProcessKeyPress(&Input->MouseButtons[PlatformMouseButton_Middle], IsDown); + } + else if(Button == Button3) + { + LinuxProcessKeyPress(&Input->MouseButtons[PlatformMouseButton_Right], IsDown); + } + else if(Button == Button4) + { + LinuxProcessKeyPress(&Input->MouseButtons[PlatformMouseButton_ScrollUp], IsDown); + } + else if(Button == Button5) + { + LinuxProcessKeyPress(&Input->MouseButtons[PlatformMouseButton_ScrollDown], IsDown); + } + } break; + + case DestroyNotify: + { + XDestroyWindowEvent *Event = (XDestroyWindowEvent *)&WindowEvent; + if(Event->window == WindowHandle) + { + GlobalRunning = false; + } + } break; + + case ClientMessage: + { + XClientMessageEvent *Event = (XClientMessageEvent *)&WindowEvent; + if((Atom)Event->data.l[0] == WM_DELETE_WINDOW) + { + XDestroyWindow(DisplayHandle, WindowHandle); + GlobalRunning = false; + } + } break; + + case EnterNotify: + { + //LinuxHideCursor(DisplayHandle, WindowHandle); + } break; + + case LeaveNotify: + { + //LinuxShowCursor(DisplayHandle, WindowHandle); + } break; + + case ConfigureNotify: + { + XConfigureEvent Event = WindowEvent.xconfigure; + + b32 SizeDidNotChange = ((!State->IsFullScreen && (Event.width == 1920/2 && Event.height == 1080/2)) || + (State->IsFullScreen && (Event.width == 1920 && Event.height == 1080))); + b32 ChangedToFS = ((State->IsFullScreen) && (Event.x == 0 && Event.y == 0)); + + // TODO(luca): Only do this on resize. +#if 0 + if(!SizeDidNotChange || ChangedToFS) + { + GC GraphicContext = XCreateGC(DisplayHandle, WindowHandle, GCForeground, &(XGCValues){.foreground = 0xFF00FF}); + s32 XRet = XFillRectangle(DisplayHandle, WindowHandle, + GraphicContext, 0, 0, 1920, 1080); + } +#endif + + } break; + + } + } +} + +DEBUG_PLATFORM_READ_ENTIRE_FILE(DEBUGPlatformReadEntireFile) +{ + debug_platform_read_file_result Result = {}; + + int File = open(FileName, O_RDONLY); + if(File != -1) + { + struct stat FileStats = {}; + fstat(File, &FileStats); + Result.ContentsSize = FileStats.st_size; + Result.Contents = mmap(0, FileStats.st_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, File, 0); + + close(File); + } + + return Result; +} + +PLATFORM_LOG(PlatformLog) +{ + printf("%s\n", Text); +} + +PLATFORM_RUN_COMMAND_AND_GET_OUTPUT(PlatformRunCommandAndGetOutput) +{ + psize Result = 0; + + int HandlesLink[2] = {0}; + int WaitStatus = 0; + pid_t ChildPID = 0; + int Ret = 0; + + char *FilePath = Command[0]; + int AccessMode = F_OK | X_OK; + Ret = access(FilePath, AccessMode); + + if(Ret == 0) + { + Ret = pipe(HandlesLink); + if(Ret != -1) + { + ChildPID = fork(); + if(ChildPID != -1) + { + if(ChildPID == 0) + { + dup2(HandlesLink[1], STDOUT_FILENO); + execve(Command[0], Command, 0); + } + else + { + wait(&WaitStatus); + + Result = read(HandlesLink[0], OutputBuffer, 4096); + if(Result == -1) + { + Result = 0; + } + } + + } + else + { + // TODO: Logging + } + } + else + { + // TODO: Logging + } + } + else + { + // TODO: Logging + } + + return Result; +} + +PLATFORM_CHANGE_CURSOR(PlatformChangeCursor) +{ + unsigned int XShape; + + switch(Shape) + { + case PlatformCursorShape_None: + { + } break;; + case PlatformCursorShape_Grab: + { + XShape = XC_hand2; + } break; + } + + if(Shape != PlatformCursorShape_None) + { + Cursor FontCursor = XCreateFontCursor(GlobalDisplayHandle, XShape); + XDefineCursor(GlobalDisplayHandle, GlobalWindowHandle, FontCursor); + } + else + { + XUndefineCursor(GlobalDisplayHandle, GlobalWindowHandle); + } + +} + +DEBUG_PLATFORM_FREE_FILE_MEMORY(DEBUGPlatformFreeFileMemory) +{ + munmap(Memory, MemorySize); +} + +DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DEBUGPlatformWriteEntireFile) +{ + b32 Result = false; + + int FD = open(FileName, O_CREAT|O_WRONLY|O_TRUNC, 00600); + if(FD != -1) + { + if(write(FD, Memory, MemorySize) != MemorySize) + { + Result = true; + } + + close(FD); + } + + return Result; +} + +internal struct timespec LinuxGetLastWriteTime(char *FilePath) +{ + struct timespec Result = {}; + + struct stat LibraryFileStats = {}; + if(!stat(FilePath, &LibraryFileStats)) + { + Result = LibraryFileStats.st_mtim; + } + + return Result; +} + +internal struct timespec LinuxGetWallClock() +{ + struct timespec Counter = {}; + clock_gettime(CLOCK_MONOTONIC, &Counter); + return Counter; +} + +internal s64 LinuxGetNSecondsElapsed(struct timespec Start, struct timespec End) +{ + s64 Result = 0; + Result = ((s64)End.tv_sec*1000000000 + (s64)End.tv_nsec) - ((s64)Start.tv_sec*1000000000 + (s64)Start.tv_nsec); + return Result; +} + +internal r32 LinuxGetSecondsElapsed(struct timespec Start, struct timespec End) +{ + r32 Result = 0; + Result = ((r32)LinuxGetNSecondsElapsed(Start, End)/1000.0f/1000.0f/1000.0f); + + return Result; +} + +internal r32 LinuxNormalizeAxisValue(s32 Value, linux_gamepad_axis Axis) +{ + r32 Result = 0; + if(Value) + { + // ((value - min / max) - 0.5) * 2 + r32 Normalized = ((r32)((r32)(Value - Axis.Minimum) / (r32)(Axis.Maximum - Axis.Minimum)) - 0.5f)*2; + Result = Normalized; + } + Assert(Result <= 1.0f && Result >= -1.0f); + + return Result; +} + +void LinuxDebugVerticalLine(game_offscreen_buffer *Buffer, int X, int Y, u32 Color) +{ + int Height = 32; + + if(X <= Buffer->Width && X >= 0 && + Y <= Buffer->Height - Height && Y <= 0) + { + u8 *Row = (u8 *)Buffer->Memory + Y*Buffer->Pitch + X*Buffer->BytesPerPixel; + while(Height--) + { + *(u32 *)Row = Color; + Row += Buffer->Pitch; + } + } +} + +internal void +LinuxChangeToExecutableDirectory(char *Args[]) +{ + char *ExePath = Args[0]; + s32 Length = (s32)StringLength(ExePath); + char ExecutableDirPath[Length]; + s32 LastSlash = 0; + for(s32 At = 0; + At < Length; + At++) + { + if(ExePath[At] == '/') + { + LastSlash = At; + } + } + MemoryCopy(ExecutableDirPath, ExePath, LastSlash); + ExecutableDirPath[LastSlash] = 0; + if(chdir(ExecutableDirPath) == -1) + { + // TODO(luca): Logging + } +} + +//~ Main +int +main(int ArgC, char *Args[]) +{ + + GlobalRunning = true; + signal(SIGINT, LinuxSigINTHandler); + + linux_state LinuxState = {}; + + // NOTE(luca): Change to executable's current directory so that all file paths can be + // consistent and relative. + LinuxChangeToExecutableDirectory(Args); + + u32 ThreadCount = (u32)LinuxGetThreadsCount()/2 - 1; + u32 InitialCount = 0; + s32 Ret = 0; + + platform_work_queue Queue = {}; + + Ret = sem_init(&Queue.SemaphoreHandle, 0, InitialCount); + Assert(Ret == 0); + + linux_thread_info ThreadInfo[ThreadCount - 1]; + for(u32 ThreadIndex = 0; + ThreadIndex < ArrayCount(ThreadInfo); + ThreadIndex++) + { + linux_thread_info *Info = &ThreadInfo[ThreadIndex]; + *Info = {}; + Info->Queue = &Queue; + Info->LogicalThreadIndex = ThreadIndex; + pthread_t ThreadHandle = 0; + s32 Ret = pthread_create(&ThreadHandle, 0, ThreadProc, Info); + if(Ret != 0) + { + // TODO(luca): Logging. + } + } + + //- X + s32 XRet = 0; + Display *DisplayHandle = XOpenDisplay(0); + + if(DisplayHandle) + { + GlobalDisplayHandle = DisplayHandle; + Window RootWindow = XDefaultRootWindow(DisplayHandle); + s32 Screen = XDefaultScreen(DisplayHandle); +#if HANDMADE_SMALL_RESOLUTION + s32 Width = 1920/2; + s32 Height = 1080/2; +#else + s32 Width = 1920; + s32 Height = 1080; +#endif + s32 ScreenBitDepth = 24; + XVisualInfo WindowVisualInfo = {}; + if(XMatchVisualInfo(DisplayHandle, Screen, ScreenBitDepth, TrueColor, &WindowVisualInfo)) + { + XSetWindowAttributes WindowAttributes = {}; + WindowAttributes.bit_gravity = StaticGravity; +#if HANDMADE_INTERNAL + WindowAttributes.background_pixel = 0xFF00FF; +#endif + WindowAttributes.colormap = XCreateColormap(DisplayHandle, RootWindow, WindowVisualInfo.visual, AllocNone); + WindowAttributes.event_mask = (StructureNotifyMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask); + u64 WindowAttributeMask = CWBitGravity | CWBackPixel | CWColormap | CWEventMask; + + Window WindowHandle = XCreateWindow(DisplayHandle, RootWindow, + 1920 - Width - 10, 10, + Width, Height, + 0, + WindowVisualInfo.depth, InputOutput, + WindowVisualInfo.visual, WindowAttributeMask, &WindowAttributes); + if(WindowHandle) + { + GlobalWindowHandle = WindowHandle; + XRet = XStoreName(DisplayHandle, WindowHandle, "Handmade Window"); + + // NOTE(luca): If we set the MaxWidth and MaxHeigth to the same values as MinWidth and MinHeight there is a bug on dwm where it won't update the window decorations when trying to remove them. + // In the future we will allow resizing to any size so this does not matter that much. + + LinuxSetSizeHint(DisplayHandle, WindowHandle, 0, 0, 0, 0); + + // NOTE(luca): Tiling window managers should treat windows with the WM_TRANSIENT_FOR property as pop-up windows. This way we ensure that we will be a floating window. This works on my setup (dwm). + XRet = XSetTransientForHint(DisplayHandle, WindowHandle, RootWindow); + + Atom WM_DELETE_WINDOW = XInternAtom(DisplayHandle, "WM_DELETE_WINDOW", False); + if(!XSetWMProtocols(DisplayHandle, WindowHandle, &WM_DELETE_WINDOW, 1)) + { + // TODO(luca): Logging + } + + XClassHint ClassHint = {}; + ClassHint.res_name = "Handmade Window"; + ClassHint.res_class = "Handmade Window"; + XSetClassHint(DisplayHandle, WindowHandle, &ClassHint); + + XSetLocaleModifiers(""); + + XIM InputMethod = XOpenIM(DisplayHandle, 0, 0, 0); + if(!InputMethod){ + XSetLocaleModifiers("@im=none"); + InputMethod = XOpenIM(DisplayHandle, 0, 0, 0); + } + XIC InputContext = XCreateIC(InputMethod, + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, WindowHandle, + XNFocusWindow, WindowHandle, + NULL); + XSetICFocus(InputContext); + + int BitsPerPixel = 32; + int BytesPerPixel = BitsPerPixel/8; + int WindowBufferSize = Width*Height*BytesPerPixel; + // TODO(luca): Get rid of this malloc!!! + char *WindowBuffer = (char *)malloc(WindowBufferSize); + + XImage *WindowImage = XCreateImage(DisplayHandle, WindowVisualInfo.visual, WindowVisualInfo.depth, ZPixmap, 0, WindowBuffer, Width, Height, BitsPerPixel, 0); + GC DefaultGC = DefaultGC(DisplayHandle, Screen); + + //- Game memory + + linux_state LinuxState = {}; + MemoryCopy(LinuxState.ExecutablePath, Args[0], StringLength(Args[0])); + + char LibraryFullPath[] = "./handmade.so"; + linux_game_code Game = LinuxLoadGameCode(LibraryFullPath); + Game.LibraryLastWriteTime = LinuxGetLastWriteTime(LibraryFullPath); + + thread_context ThreadContext = {}; + + game_memory GameMemory = {}; + GameMemory.PermanentStorageSize = Megabytes(64); + GameMemory.TransientStorageSize = Gigabytes(1); + GameMemory.HighPriorityQueue = &Queue; + GameMemory.PlatformAddEntry = LinuxAddEntry; + GameMemory.PlatformCompleteAllWork = LinuxCompleteAllWork; + GameMemory.PlatformLog = PlatformLog; + GameMemory.PlatformChangeCursor = PlatformChangeCursor; + GameMemory.PlatformRunCommandAndGetOutput = PlatformRunCommandAndGetOutput; + GameMemory.PlatformGetWallClock = LinuxGetWallClock; + GameMemory.DEBUGPlatformReadEntireFile = DEBUGPlatformReadEntireFile; + GameMemory.DEBUGPlatformFreeFileMemory = DEBUGPlatformFreeFileMemory; + GameMemory.DEBUGPlatformWriteEntireFile = DEBUGPlatformWriteEntireFile; + +#if HANDMADE_INTERNAL + void *BaseAddress = (void *)Terabytes(2); +#else + void *BaseAddress = 0; +#endif + // TODO(casey): TransientStorage needs to be broken into game transient and cache transient + LinuxState.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize; + LinuxState.GameMemoryBlock = mmap(BaseAddress, LinuxState.TotalSize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); + GameMemory.PermanentStorage = LinuxState.GameMemoryBlock; + GameMemory.TransientStorage = (u8 *)GameMemory.PermanentStorage + GameMemory.PermanentStorageSize; + + game_input Input[2] = {}; + game_input *NewInput = &Input[0]; + game_input *OldInput = &Input[1]; + + linux_gamepad GamePads[MAX_PLAYER_COUNT] = {}; + for(int GamePadIndex = 0; + GamePadIndex < MAX_PLAYER_COUNT; + GamePadIndex++) + { + GamePads[GamePadIndex].File = -1; + } + + char EventDirectoryName[] = "/dev/input/"; + + // TODO(luca): Use homemade instaed of libc. + DIR *EventDirectory = opendir(EventDirectoryName); + struct dirent *Entry = 0; + int GamePadIndex = 0; + while((Entry = readdir(EventDirectory))) + { + if(!strncmp(Entry->d_name, "event", sizeof("event") - 1)) + { + char FilePath[PATH_MAX] = {}; + CatStrings(sizeof(EventDirectoryName) - 1, EventDirectoryName, + StringLength(Entry->d_name), Entry->d_name, + sizeof(FilePath) - 1, FilePath); + if(GamePadIndex < MAX_PLAYER_COUNT) + { + linux_gamepad *GamePadAt = &GamePads[GamePadIndex]; + LinuxOpenGamePad(&Queue, FilePath, GamePadAt); + if(GamePadAt->File != -1) + { +#if 0 + for(int AxesIndex = 0; + AxesIndex < AXES_COUNT; + AxesIndex++) + { + linux_gamepad_axis Axis = GamePadAt->Axes[AxesIndex]; + printf("min: %d, max: %d, fuzz: %d, flat: %d\n", Axis.Minimum, Axis.Maximum, Axis.Fuzz, Axis.Flat); + } +#endif + GamePadIndex++; + } + } + else + { + // TODO(luca): Logging + } + } + } + + LinuxCompleteAllWork(&Queue); + + game_offscreen_buffer OffscreenBuffer = {}; + OffscreenBuffer.Memory = WindowBuffer; + OffscreenBuffer.Width = Width; + OffscreenBuffer.Height = Height; + OffscreenBuffer.BytesPerPixel = BytesPerPixel; + OffscreenBuffer.Pitch = Width*BytesPerPixel; + + int LastFramesWritten = 0; + unsigned int SampleRate, ChannelCount, PeriodTime, SampleCount; + snd_pcm_status_t *PCMStatus = 0; + snd_pcm_t* PCMHandle = 0; + snd_pcm_hw_params_t *PCMParams = 0; + snd_pcm_uframes_t PeriodSize = 0; + snd_pcm_uframes_t PCMBufferSize = 0; + + linux_init_alsa_result ALSAInit = LinuxInitALSA(); + PCMHandle = ALSAInit.PCMHandle; + PCMParams = ALSAInit.PCMParams; + snd_pcm_hw_params_get_channels(PCMParams, &ChannelCount); + snd_pcm_hw_params_get_rate(PCMParams, &SampleRate, 0); + snd_pcm_hw_params_get_period_size(PCMParams, &PeriodSize, 0); + snd_pcm_hw_params_get_period_time(PCMParams, &PeriodTime, NULL); + snd_pcm_hw_params_get_buffer_size(PCMParams, &PCMBufferSize); + snd_pcm_status_malloc(&PCMStatus); + +#if 0 + { + Assert(0); + u32 Value, Result; + snd_pcm_uframes_t Frames; + Result = snd_pcm_hw_params_get_buffer_time_min(PCMParams, &Value, 0); + Result = snd_pcm_hw_params_get_buffer_size_min(PCMParams, &Frames); + Frames = PCMBufferSize/2; + Result = snd_pcm_hw_params_set_period_size_near(PCMHandle, PCMParams, &Frames, 0); + Result = snd_pcm_hw_params_get_period_size(PCMParams, &PeriodSize, 0); + } +#endif + + char AudioSamples[PCMBufferSize]; + u64 Periods = 2; + u32 BytesPerSample = (sizeof(s16)*ChannelCount); + +#if HANDMADE_FORCE_UPDATEHZ + r32 GameUpdateHz = HANDMADE_FORCE_UPDATEHZ; +#elif 1 + r32 GameUpdateHz = 30; +#else + r32 GameUpdateHz = LinuxGetMonitorRefreshRate(DisplayHandle, RootWindow); +#endif + +#if HANDMADE_PROFILING + if(!spall_init_file("linux_handmade.spall", 1, &spall_ctx)) + { + printf("Failed to setup spall?\n"); + } + u8 SpallBackingBuffer[1 * 1024 * 1024] = {}; + spall_buffer.data = SpallBackingBuffer; + spall_buffer.length = sizeof(SpallBackingBuffer) - 1; + if(!spall_buffer_init(&spall_ctx, &spall_buffer)) + { + printf("Failed to init spall buffer?\n"); + return 1; + } + PlatformGetWallClock = LinuxGetWallClock; +#endif + + + XRet = XMapWindow(DisplayHandle, WindowHandle); + XRet = XFlush(DisplayHandle); + + struct timespec LastCounter = LinuxGetWallClock(); + struct timespec FlipWallClock = LinuxGetWallClock(); + r32 TargetSecondsPerFrame = 1.0f / GameUpdateHz; + + u64 LastCycleCount = __rdtsc(); + while(GlobalRunning) + { + PROFILE_START_LABEL("Inputs"); + NewInput->dtForFrame = TargetSecondsPerFrame; + +#if HANDMADE_INTERNAL + // NOTE(luca): Because gcc will first create an empty file and then write into it we skip trying to reload when the file is empty. + struct stat FileStats = {}; + stat(LibraryFullPath, &FileStats); + if(FileStats.st_size) + { + s64 SecondsElapsed = LinuxGetNSecondsElapsed(Game.LibraryLastWriteTime, FileStats.st_mtim) / 1000/1000; + if(SecondsElapsed > 0) + { + LinuxUnloadGameCode(&Game); + Game = LinuxLoadGameCode(LibraryFullPath); + Game.LibraryLastWriteTime = FileStats.st_mtim; + } + } +#endif + + game_controller_input *OldKeyboardController = GetController(OldInput, 0); + game_controller_input *NewKeyboardController = GetController(NewInput, 0); + NewKeyboardController->IsConnected = true; + + NewKeyboardController->Text.Count = 0; + for(u32 ButtonIndex = 0; + ButtonIndex < ArrayCount(NewKeyboardController->Buttons); + ButtonIndex++) + { + NewKeyboardController->Buttons[ButtonIndex].HalfTransitionCount = 0; + } + for(u32 ButtonIndex = 0; + ButtonIndex < PlatformMouseButton_Count; + ButtonIndex++) + { + NewInput->MouseButtons[ButtonIndex].EndedDown = OldInput->MouseButtons[ButtonIndex].EndedDown; + NewInput->MouseButtons[ButtonIndex].HalfTransitionCount = 0; + } + + LinuxProcessPendingMessages(DisplayHandle, WindowHandle, InputContext, WM_DELETE_WINDOW, + &LinuxState, NewInput, NewKeyboardController); + + // TODO(luca): Use MotionNotify events instead so we query this less frequently. + s32 MouseX = 0, MouseY = 0, MouseZ = 0; + u32 MouseMask = 0; + u64 Ignored; + if(XQueryPointer(DisplayHandle, WindowHandle, + &Ignored, &Ignored, (int *)&Ignored, (int *)&Ignored, + &MouseX, &MouseY, &MouseMask)) + { + if(MouseX <= OffscreenBuffer.Width && + MouseX >= 0 && + MouseY <= OffscreenBuffer.Height && + MouseY >= 0) + { + NewInput->MouseY = MouseY; + NewInput->MouseX = MouseX; + } + } + + for(int GamePadIndex = 0; + GamePadIndex < MAX_PLAYER_COUNT; + GamePadIndex++) + { + linux_gamepad *GamePadAt = &GamePads[GamePadIndex]; + + if(GamePadAt->File != -1) + { + game_controller_input *OldController = GetController(OldInput, GamePadIndex + 1); + game_controller_input *NewController = GetController(NewInput, GamePadIndex + 1); + NewController->IsConnected = true; + + // TODO(luca): Cross frame values!!! + struct input_event InputEvents[64] = {}; + psize BytesRead = 0; + + BytesRead = read(GamePadAt->File, InputEvents, sizeof(InputEvents)); + if(BytesRead != -1) + { + for(u32 EventIndex = 0; + EventIndex < ArrayCount(InputEvents); + EventIndex++) + { + struct input_event EventAt = InputEvents[EventIndex]; + + switch(EventAt.type) + { + case EV_KEY: + { + b32 IsDown = EventAt.value; + if(0) {} + else if(EventAt.code == BTN_A) + { + NewController->ActionDown.EndedDown = IsDown; + } + else if(EventAt.code == BTN_B) + { + NewController->ActionRight.EndedDown = IsDown; + } + else if(EventAt.code == BTN_X) + { + NewController->ActionLeft.EndedDown = IsDown; + } + else if(EventAt.code == BTN_Y) + { + NewController->ActionUp.EndedDown = IsDown; + } + else if(EventAt.code == BTN_START) + { + NewController->Start.EndedDown = IsDown; + } + else if(EventAt.code == BTN_BACK) + { + NewController->Back.EndedDown = IsDown; + } + } break; + + case EV_ABS: + { + if(0) {} + else if(EventAt.code == ABS_X) + { + NewController->IsAnalog = true; + + NewController->StickAverageX = LinuxNormalizeAxisValue(EventAt.value, GamePadAt->Axes[LSTICKX]); + } + else if(EventAt.code == ABS_Y) + { + NewController->StickAverageY = -1.0f * LinuxNormalizeAxisValue(EventAt.value, GamePadAt->Axes[LSTICKY]); + } + else if(EventAt.code == ABS_HAT0X) + { + NewController->StickAverageX = (r32)EventAt.value; + NewController->IsAnalog = false; + } + else if(EventAt.code == ABS_HAT0Y) + { + NewController->StickAverageY = -(r32)EventAt.value; + NewController->IsAnalog = false; + } + } break; +#if 0 + if(EventAt.type) printf("%d %d %d\n", EventAt.type, EventAt.code, EventAt.value); +#endif + } + } + } + } + } + PROFILE_END; + + if(!GlobalPaused) + { + PROFILE_START_LABEL("Recording"); + if(LinuxState.InputRecordingIndex) + { + LinuxRecordInput(&LinuxState, NewInput); + } + if(LinuxState.InputPlayingIndex) + { + LinuxPlayBackInput(&LinuxState, NewInput); + } + PROFILE_END; + // NOTE(luca): Clear buffer + + PROFILE_START_LABEL("Clear buffer"); + MemorySet(WindowBuffer, 0, WindowBufferSize); + PROFILE_END; + + if(Game.UpdateAndRender) + { + PROFILE_START_LABEL("Update and Render"); + Game.UpdateAndRender(&ThreadContext, &GameMemory, NewInput, &OffscreenBuffer); + PROFILE_END; + } + + + /* NOTE(luca): How sound works + +Check the delay +Check the available frames in buffer + +1. Too few audio frames in buffer for current frame. +-> Add more + +2. Too many frames available +-> Add less / drain? + +3. Fill first time? +-> Check delay +-> Maybe we should do this every frame? +-> Output needed frames to not have lag, this means to output maybe two frames? + +TODO +- check if delay never changes +- check if buffersize never changes +-> cache these values +*/ + + PROFILE_START_LABEL("Sound"); + + r32 SamplesToWrite = 0; + local_persist b32 AudioFillFirstTime = true; + + r32 SingleFrameOfAudioFrames = TargetSecondsPerFrame*(r32)SampleRate; + + if(AudioFillFirstTime) + { + struct timespec WorkCounter = LinuxGetWallClock(); + r32 WorkSecondsElapsed = LinuxGetSecondsElapsed(LastCounter, WorkCounter); + r32 SecondsLeftUntilFlip = TargetSecondsPerFrame - WorkSecondsElapsed; + + if(SecondsLeftUntilFlip > 0) + { + SamplesToWrite = (r32)SampleRate*(TargetSecondsPerFrame + SecondsLeftUntilFlip); + } + else + { + SamplesToWrite = SingleFrameOfAudioFrames; + } + + AudioFillFirstTime = false; + } + else + { + SamplesToWrite = SingleFrameOfAudioFrames; + } + +#if 0 + snd_pcm_sframes_t AvailableFrames = 0; + snd_pcm_sframes_t DelayFrames; + + //snd_pcm_avail_update(PCMHandle); + AvailableFrames = snd_pcm_avail(PCMHandle); + snd_pcm_delay(PCMHandle, &DelayFrames); + + //printf("PeriodSize: %lu, PeriodTime: %d, BufferSize: %lu\n", PeriodSize, PeriodTime, PCMBufferSize); + printf("BeingWritten: %lu, Avail: %ld, Delay: %ld, ToWrite: %d\n", PCMBufferSize - AvailableFrames, AvailableFrames, DelayFrames, (s32)SamplesToWrite); +#endif + + game_sound_output_buffer SoundBuffer = {}; + SoundBuffer.SamplesPerSecond = SampleRate; + SoundBuffer.SampleCount = (s32)SamplesToWrite; + // TODO(luca): This overflows according to the sanitizer. + SoundBuffer.Samples = (s16 *)AudioSamples; + +#if 0 + if(Game.GetSoundSamples) + { + Game.GetSoundSamples(&ThreadContext, &GameMemory, &SoundBuffer); + } + LastFramesWritten = snd_pcm_writei(PCMHandle, SoundBuffer.Samples, SoundBuffer.SampleCount); +#endif + + if(LastFramesWritten < 0) + { + // TODO(luca): Logging + // NOTE(luca): We might want to silence in case of overruns ahead of time. We also probably want to handle latency differently here. + snd_pcm_recover(PCMHandle, LastFramesWritten, ALSA_RECOVER_SILENT); + + // underrun + if(LastFramesWritten == -EPIPE) + { + AudioFillFirstTime = true; + } + } + PROFILE_END; + } + + PROFILE_START_LABEL("Sleep"); +#if 0 + printf("Expected: %d, Delay: %4d, Being written: %5lu, Written: %d, Ptr: %lu\n", + (int)SamplesToWrite, DelayFrames, PCMBufferSize - AvailableFrames, LastFramesWritten, PCMSoundStatus->hw_ptr); +#endif + + + struct timespec WorkCounter = LinuxGetWallClock(); + +#if 1 + r32 WorkMSPerFrame = (r32)((r32)LinuxGetNSecondsElapsed(LastCounter, WorkCounter)/1000000.f); + printf("%.2fms/f\n", WorkMSPerFrame); +#endif + + r32 SecondsElapsedForFrame = LinuxGetSecondsElapsed(LastCounter, WorkCounter); + if(SecondsElapsedForFrame < TargetSecondsPerFrame) + { + r32 SleepUS = ((TargetSecondsPerFrame - 0.001f - SecondsElapsedForFrame)*1000000.0f); + if(SleepUS > 0) + { + // TODO(luca): Intrinsic + usleep((u32)SleepUS); + } + else + { + // TODO(luca): Logging + } + + r32 TestSecondsElapsedForFrame = (r32)(LinuxGetSecondsElapsed(LastCounter, LinuxGetWallClock())); + if(TestSecondsElapsedForFrame < TargetSecondsPerFrame) + { + // TODO(luca): Log missed sleep + } + + // NOTE(luca): This is to help against sleep granularity. + while(SecondsElapsedForFrame < TargetSecondsPerFrame) + { + SecondsElapsedForFrame = LinuxGetSecondsElapsed(LastCounter, LinuxGetWallClock()); + } + } + else + { + // TODO(luca): Log missed frame rate! + } + + struct timespec EndCounter = LinuxGetWallClock(); + r32 MSPerFrame = (r32)((r32)LinuxGetNSecondsElapsed(LastCounter, EndCounter)/1000000.f); + LastCounter = EndCounter; + PROFILE_END; + + PROFILE_START_LABEL("Image"); + + XPutImage(DisplayHandle, WindowHandle, DefaultGC, WindowImage, 0, 0, 0, 0, + Width, Height); + + PROFILE_END; + FlipWallClock = LinuxGetWallClock(); + + game_input *TempInput = NewInput; + NewInput = OldInput; + TempInput = NewInput; + +#if 0 + u64 EndCycleCount = __rdtsc(); + u64 CyclesElapsed = EndCycleCount - LastCycleCount; + LastCycleCount = EndCycleCount; + + r64 FPS = 0; + r64 MCPF = (r64)(CyclesElapsed/(1000.0f*1000.0f)); + + printf("%.2fms/f %.2ff/s %.2fmc/f\n", MSPerFrame, FPS, MCPF); +#endif + } + +#if HANDMADE_PROFILING + spall_buffer_quit(&spall_ctx, &spall_buffer); + spall_quit(&spall_ctx); +#endif + + } + else + { + // TODO: Log this bad WindowHandle + } + } + else + { + // TODO: Log this could not match visual info + } + } + else + { + // TODO: Log could not get x connection + } + return 0; +} diff --git a/linux_handmade.h b/linux_handmade.h new file mode 100644 index 0000000..d5f1063 --- /dev/null +++ b/linux_handmade.h @@ -0,0 +1,92 @@ +/* date = April 15th 2025 4:50 pm */ + +#ifndef LINUX_HANDMADE_H +#define LINUX_HANDMADE_H + +struct linux_game_code +{ + game_update_and_render *UpdateAndRender; + game_get_sound_samples *GetSoundSamples; + + void *LibraryHandle; + struct timespec LibraryLastWriteTime; +}; + +enum linux_gamepad_axes_enum +{ + LSTICKX, + LSTICKY, + RSTICKX, + RSTICKY, + LSHOULDER, + RSHOULDER, + DPADX, + DPADY, + AXES_COUNT +}; + +struct linux_gamepad_axis +{ + s32 Minimum; + s32 Maximum; + s32 Fuzz; + s32 Flat; +}; + +struct linux_gamepad +{ + // TODO(luca): rename to FileHandle + int File; + char FilePath[PATH_MAX]; + + char Name[256]; + int SupportsRumble; + linux_gamepad_axis Axes[AXES_COUNT]; +}; + +struct linux_replay_buffer +{ + size_t Pos; + size_t Size; + char *Memory; + size_t MemorySize; +}; +struct linux_state +{ + Display *DisplayHandle; + + b32 IsFullScreen; + + int InputPlayingIndex; + int InputRecordingIndex; + linux_replay_buffer ReplayBuffers[5]; + + char ExecutablePath[PATH_MAX]; + + size_t TotalSize; + void *GameMemoryBlock; +}; + +struct linux_thread_info +{ + u64 LogicalThreadIndex; + platform_work_queue *Queue; +}; + +struct platform_work_queue +{ + sem_t SemaphoreHandle; + + volatile u32 CompletionGoal; + volatile u32 CompletionCount; + + volatile u32 NextEntryToWrite; + volatile u32 NextEntryToRead; + platform_work_queue_entry Entries[256]; +}; + +//- Prototypes +// for sapall +internal struct timespec LinuxGetWallClock(void); + +#endif //LINUX_HANDMADE_H diff --git a/linux_profile.h b/linux_profile.h new file mode 100644 index 0000000..d36c859 --- /dev/null +++ b/linux_profile.h @@ -0,0 +1,45 @@ +/* date = June 17th 2025 3:06 pm */ + +#ifndef LINUX_PROFILE_H +#define LINUX_PROFILE_H + +#if HANDMADE_PROFILING + +#include "handmade_platform.h" + +#include "spall.h" +global_variable SpallProfile spall_ctx; +global_variable SpallBuffer spall_buffer; +global_variable char SpallNumberBuffer[9]; + +#define PROFILE_START_LABEL(Label) \ +spall_buffer_begin(&spall_ctx, &spall_buffer, Label, strlen(Label), LinuxGetWallClockMSec()) +#define PROFILE_START_LINE sprintf(SpallNumberBuffer, "%d", __LINE__); \ +PROFILE_START_LABEL(SpallNumberBuffer) +#define PROFILE_START_FUNCTION PROFILE_START_LABEL(__FUNCTION__) +#define PROFILE_START PROFILE_START_LINE +#define PROFILE_END spall_buffer_end(&spall_ctx, &spall_buffer, LinuxGetWallClockMSec()); + +typedef struct timespec platform_get_wall_clock(void); + +global_variable platform_get_wall_clock *PlatformGetWallClock; +timespec LinuxGetWallClock(void); + +internal +r64 LinuxGetWallClockMSec(void) +{ + r64 Result = 0; + timespec Spec = PlatformGetWallClock(); + Result = (((r64)Spec.tv_sec) * 1000000) + (((r64)Spec.tv_nsec) / 1000); + return Result; +} + +#else +#define PROFILE_START +#define PROFILE_START_LABEL(Name) +#define PROFILE_START_LINE +#define PROFILE_START_FUNCTION +#define PROFILE_END +#endif + +#endif //LINUX_PROFILE_H diff --git a/linux_x11_keysyms_to_strings.c b/linux_x11_keysyms_to_strings.c new file mode 100644 index 0000000..f05289d --- /dev/null +++ b/linux_x11_keysyms_to_strings.c @@ -0,0 +1,2048 @@ +#define XK_MISCELLANY +#define XK_XKB_KEYS +#define XK_3270 +#define XK_LATIN1 +#define XK_LATIN2 +#define XK_LATIN3 +#define XK_LATIN4 +#define XK_LATIN8 +#define XK_LATIN9 +#define XK_KATAKANA +#define XK_ARABIC +#define XK_CYRILLIC +#define XK_GREEK +#define XK_TECHNICAL +#define XK_SPECIAL +#define XK_PUBLISHING +#define XK_APL +#define XK_HEBREW +#define XK_THAI +#define XK_KOREAN +#define XK_ARMENIAN +#define XK_GEORGIAN +#define XK_CAUCASUS +#define XK_VIETNAMESE +#define XK_CURRENCY +#define XK_MATHEMATICAL +#define XK_BRAILLE +#define XK_SINHALA + +#include <X11/keysymdef.h> + +internal char * +LinuxReturnStringForSymbol(KeySym Symbol) +{ + switch(Symbol) + { + case XK_VoidSymbol: return "VoidSymbol"; break; + case XK_BackSpace: return "BackSpace"; break; + case XK_Tab: return "Tab"; break; + case XK_Linefeed: return "Linefeed"; break; + case XK_Clear: return "Clear"; break; + case XK_Return: return "Return"; break; + case XK_Pause: return "Pause"; break; + case XK_Scroll_Lock: return "Scroll_Lock"; break; + case XK_Sys_Req: return "Sys_Req"; break; + case XK_Escape: return "Escape"; break; + case XK_Delete: return "Delete"; break; + case XK_Multi_key: return "Multi_key"; break; + case XK_Codeinput: return "Codeinput"; break; + case XK_SingleCandidate: return "SingleCandidate"; break; + case XK_MultipleCandidate: return "MultipleCandidate"; break; + case XK_PreviousCandidate: return "PreviousCandidate"; break; + case XK_Kanji: return "Kanji"; break; + case XK_Muhenkan: return "Muhenkan"; break; + case XK_Henkan_Mode: return "Henkan_Mode"; break; + case XK_Romaji: return "Romaji"; break; + case XK_Hiragana: return "Hiragana"; break; + case XK_Katakana: return "Katakana"; break; + case XK_Hiragana_Katakana: return "Hiragana_Katakana"; break; + case XK_Zenkaku: return "Zenkaku"; break; + case XK_Hankaku: return "Hankaku"; break; + case XK_Zenkaku_Hankaku: return "Zenkaku_Hankaku"; break; + case XK_Touroku: return "Touroku"; break; + case XK_Massyo: return "Massyo"; break; + case XK_Kana_Lock: return "Kana_Lock"; break; + case XK_Kana_Shift: return "Kana_Shift"; break; + case XK_Eisu_Shift: return "Eisu_Shift"; break; + case XK_Eisu_toggle: return "Eisu_toggle"; break; + case XK_Home: return "Home"; break; + case XK_Left: return "Left"; break; + case XK_Up: return "Up"; break; + case XK_Right: return "Right"; break; + case XK_Down: return "Down"; break; + case XK_Prior: return "Prior"; break; + case XK_Next: return "Next"; break; + case XK_End: return "End"; break; + case XK_Begin: return "Begin"; break; + case XK_Select: return "Select"; break; + case XK_Print: return "Print"; break; + case XK_Execute: return "Execute"; break; + case XK_Insert: return "Insert"; break; + case XK_Undo: return "Undo"; break; + case XK_Redo: return "Redo"; break; + case XK_Menu: return "Menu"; break; + case XK_Find: return "Find"; break; + case XK_Cancel: return "Cancel"; break; + case XK_Help: return "Help"; break; + case XK_Break: return "Break"; break; + case XK_Mode_switch: return "Mode_switch"; break; + case XK_Num_Lock: return "Num_Lock"; break; + case XK_KP_Space: return "KP_Space"; break; + case XK_KP_Tab: return "KP_Tab"; break; + case XK_KP_Enter: return "KP_Enter"; break; + case XK_KP_F1: return "KP_F1"; break; + case XK_KP_F2: return "KP_F2"; break; + case XK_KP_F3: return "KP_F3"; break; + case XK_KP_F4: return "KP_F4"; break; + case XK_KP_Home: return "KP_Home"; break; + case XK_KP_Left: return "KP_Left"; break; + case XK_KP_Up: return "KP_Up"; break; + case XK_KP_Right: return "KP_Right"; break; + case XK_KP_Down: return "KP_Down"; break; + case XK_KP_Prior: return "KP_Prior"; break; + case XK_KP_Next: return "KP_Next"; break; + case XK_KP_End: return "KP_End"; break; + case XK_KP_Begin: return "KP_Begin"; break; + case XK_KP_Insert: return "KP_Insert"; break; + case XK_KP_Delete: return "KP_Delete"; break; + case XK_KP_Equal: return "KP_Equal"; break; + case XK_KP_Multiply: return "KP_Multiply"; break; + case XK_KP_Add: return "KP_Add"; break; + case XK_KP_Separator: return "KP_Separator"; break; + case XK_KP_Subtract: return "KP_Subtract"; break; + case XK_KP_Decimal: return "KP_Decimal"; break; + case XK_KP_Divide: return "KP_Divide"; break; + case XK_KP_0: return "KP_0"; break; + case XK_KP_1: return "KP_1"; break; + case XK_KP_2: return "KP_2"; break; + case XK_KP_3: return "KP_3"; break; + case XK_KP_4: return "KP_4"; break; + case XK_KP_5: return "KP_5"; break; + case XK_KP_6: return "KP_6"; break; + case XK_KP_7: return "KP_7"; break; + case XK_KP_8: return "KP_8"; break; + case XK_KP_9: return "KP_9"; break; + case XK_F1: return "F1"; break; + case XK_F2: return "F2"; break; + case XK_F3: return "F3"; break; + case XK_F4: return "F4"; break; + case XK_F5: return "F5"; break; + case XK_F6: return "F6"; break; + case XK_F7: return "F7"; break; + case XK_F8: return "F8"; break; + case XK_F9: return "F9"; break; + case XK_F10: return "F10"; break; + case XK_F11: return "F11"; break; + case XK_F12: return "F12"; break; + case XK_F13: return "F13"; break; + case XK_F14: return "F14"; break; + case XK_F15: return "F15"; break; + case XK_F16: return "F16"; break; + case XK_F17: return "F17"; break; + case XK_F18: return "F18"; break; + case XK_F19: return "F19"; break; + case XK_F20: return "F20"; break; + case XK_F21: return "F21"; break; + case XK_F22: return "F22"; break; + case XK_F23: return "F23"; break; + case XK_F24: return "F24"; break; + case XK_F25: return "F25"; break; + case XK_F26: return "F26"; break; + case XK_F27: return "F27"; break; + case XK_F28: return "F28"; break; + case XK_F29: return "F29"; break; + case XK_F30: return "F30"; break; + case XK_F31: return "F31"; break; + case XK_F32: return "F32"; break; + case XK_F33: return "F33"; break; + case XK_F34: return "F34"; break; + case XK_F35: return "F35"; break; + case XK_Shift_L: return "Shift_L"; break; + case XK_Shift_R: return "Shift_R"; break; + case XK_Control_L: return "Control_L"; break; + case XK_Control_R: return "Control_R"; break; + case XK_Caps_Lock: return "Caps_Lock"; break; + case XK_Shift_Lock: return "Shift_Lock"; break; + case XK_Meta_L: return "Meta_L"; break; + case XK_Meta_R: return "Meta_R"; break; + case XK_Alt_L: return "Alt_L"; break; + case XK_Alt_R: return "Alt_R"; break; + case XK_Super_L: return "Super_L"; break; + case XK_Super_R: return "Super_R"; break; + case XK_Hyper_L: return "Hyper_L"; break; + case XK_Hyper_R: return "Hyper_R"; break; + case XK_ISO_Lock: return "ISO_Lock"; break; + case XK_ISO_Level2_Latch: return "ISO_Level2_Latch"; break; + case XK_ISO_Level3_Shift: return "ISO_Level3_Shift"; break; + case XK_ISO_Level3_Latch: return "ISO_Level3_Latch"; break; + case XK_ISO_Level3_Lock: return "ISO_Level3_Lock"; break; + case XK_ISO_Level5_Shift: return "ISO_Level5_Shift"; break; + case XK_ISO_Level5_Latch: return "ISO_Level5_Latch"; break; + case XK_ISO_Level5_Lock: return "ISO_Level5_Lock"; break; + case XK_ISO_Group_Latch: return "ISO_Group_Latch"; break; + case XK_ISO_Group_Lock: return "ISO_Group_Lock"; break; + case XK_ISO_Next_Group: return "ISO_Next_Group"; break; + case XK_ISO_Next_Group_Lock: return "ISO_Next_Group_Lock"; break; + case XK_ISO_Prev_Group: return "ISO_Prev_Group"; break; + case XK_ISO_Prev_Group_Lock: return "ISO_Prev_Group_Lock"; break; + case XK_ISO_First_Group: return "ISO_First_Group"; break; + case XK_ISO_First_Group_Lock: return "ISO_First_Group_Lock"; break; + case XK_ISO_Last_Group: return "ISO_Last_Group"; break; + case XK_ISO_Last_Group_Lock: return "ISO_Last_Group_Lock"; break; + case XK_ISO_Left_Tab: return "ISO_Left_Tab"; break; + case XK_ISO_Move_Line_Up: return "ISO_Move_Line_Up"; break; + case XK_ISO_Move_Line_Down: return "ISO_Move_Line_Down"; break; + case XK_ISO_Partial_Line_Up: return "ISO_Partial_Line_Up"; break; + case XK_ISO_Partial_Line_Down: return "ISO_Partial_Line_Down"; break; + case XK_ISO_Partial_Space_Left: return "ISO_Partial_Space_Left"; break; + case XK_ISO_Partial_Space_Right: return "ISO_Partial_Space_Right"; break; + case XK_ISO_Set_Margin_Left: return "ISO_Set_Margin_Left"; break; + case XK_ISO_Set_Margin_Right: return "ISO_Set_Margin_Right"; break; + case XK_ISO_Release_Margin_Left: return "ISO_Release_Margin_Left"; break; + case XK_ISO_Release_Margin_Right: return "ISO_Release_Margin_Right"; break; + case XK_ISO_Release_Both_Margins: return "ISO_Release_Both_Margins"; break; + case XK_ISO_Fast_Cursor_Left: return "ISO_Fast_Cursor_Left"; break; + case XK_ISO_Fast_Cursor_Right: return "ISO_Fast_Cursor_Right"; break; + case XK_ISO_Fast_Cursor_Up: return "ISO_Fast_Cursor_Up"; break; + case XK_ISO_Fast_Cursor_Down: return "ISO_Fast_Cursor_Down"; break; + case XK_ISO_Continuous_Underline: return "ISO_Continuous_Underline"; break; + case XK_ISO_Discontinuous_Underline: return "ISO_Discontinuous_Underline"; break; + case XK_ISO_Emphasize: return "ISO_Emphasize"; break; + case XK_ISO_Center_Object: return "ISO_Center_Object"; break; + case XK_ISO_Enter: return "ISO_Enter"; break; + case XK_dead_grave: return "dead_grave"; break; + case XK_dead_acute: return "dead_acute"; break; + case XK_dead_circumflex: return "dead_circumflex"; break; + case XK_dead_tilde: return "dead_tilde"; break; + case XK_dead_macron: return "dead_macron"; break; + case XK_dead_breve: return "dead_breve"; break; + case XK_dead_abovedot: return "dead_abovedot"; break; + case XK_dead_diaeresis: return "dead_diaeresis"; break; + case XK_dead_abovering: return "dead_abovering"; break; + case XK_dead_doubleacute: return "dead_doubleacute"; break; + case XK_dead_caron: return "dead_caron"; break; + case XK_dead_cedilla: return "dead_cedilla"; break; + case XK_dead_ogonek: return "dead_ogonek"; break; + case XK_dead_iota: return "dead_iota"; break; + case XK_dead_voiced_sound: return "dead_voiced_sound"; break; + case XK_dead_semivoiced_sound: return "dead_semivoiced_sound"; break; + case XK_dead_belowdot: return "dead_belowdot"; break; + case XK_dead_hook: return "dead_hook"; break; + case XK_dead_horn: return "dead_horn"; break; + case XK_dead_stroke: return "dead_stroke"; break; + case XK_dead_abovecomma: return "dead_abovecomma"; break; + case XK_dead_abovereversedcomma: return "dead_abovereversedcomma"; break; + case XK_dead_doublegrave: return "dead_doublegrave"; break; + case XK_dead_belowring: return "dead_belowring"; break; + case XK_dead_belowmacron: return "dead_belowmacron"; break; + case XK_dead_belowcircumflex: return "dead_belowcircumflex"; break; + case XK_dead_belowtilde: return "dead_belowtilde"; break; + case XK_dead_belowbreve: return "dead_belowbreve"; break; + case XK_dead_belowdiaeresis: return "dead_belowdiaeresis"; break; + case XK_dead_invertedbreve: return "dead_invertedbreve"; break; + case XK_dead_belowcomma: return "dead_belowcomma"; break; + case XK_dead_currency: return "dead_currency"; break; + case XK_dead_lowline: return "dead_lowline"; break; + case XK_dead_aboveverticalline: return "dead_aboveverticalline"; break; + case XK_dead_belowverticalline: return "dead_belowverticalline"; break; + case XK_dead_longsolidusoverlay: return "dead_longsolidusoverlay"; break; + case XK_dead_a: return "dead_a"; break; + case XK_dead_A: return "dead_A"; break; + case XK_dead_e: return "dead_e"; break; + case XK_dead_E: return "dead_E"; break; + case XK_dead_i: return "dead_i"; break; + case XK_dead_I: return "dead_I"; break; + case XK_dead_o: return "dead_o"; break; + case XK_dead_O: return "dead_O"; break; + case XK_dead_u: return "dead_u"; break; + case XK_dead_U: return "dead_U"; break; + case XK_dead_small_schwa: return "dead_small_schwa"; break; + case XK_dead_capital_schwa: return "dead_capital_schwa"; break; + case XK_dead_greek: return "dead_greek"; break; + case XK_dead_hamza: return "dead_hamza"; break; + case XK_First_Virtual_Screen: return "First_Virtual_Screen"; break; + case XK_Prev_Virtual_Screen: return "Prev_Virtual_Screen"; break; + case XK_Next_Virtual_Screen: return "Next_Virtual_Screen"; break; + case XK_Last_Virtual_Screen: return "Last_Virtual_Screen"; break; + case XK_Terminate_Server: return "Terminate_Server"; break; + case XK_AccessX_Enable: return "AccessX_Enable"; break; + case XK_AccessX_Feedback_Enable: return "AccessX_Feedback_Enable"; break; + case XK_RepeatKeys_Enable: return "RepeatKeys_Enable"; break; + case XK_SlowKeys_Enable: return "SlowKeys_Enable"; break; + case XK_BounceKeys_Enable: return "BounceKeys_Enable"; break; + case XK_StickyKeys_Enable: return "StickyKeys_Enable"; break; + case XK_MouseKeys_Enable: return "MouseKeys_Enable"; break; + case XK_MouseKeys_Accel_Enable: return "MouseKeys_Accel_Enable"; break; + case XK_Overlay1_Enable: return "Overlay1_Enable"; break; + case XK_Overlay2_Enable: return "Overlay2_Enable"; break; + case XK_AudibleBell_Enable: return "AudibleBell_Enable"; break; + case XK_Pointer_Left: return "Pointer_Left"; break; + case XK_Pointer_Right: return "Pointer_Right"; break; + case XK_Pointer_Up: return "Pointer_Up"; break; + case XK_Pointer_Down: return "Pointer_Down"; break; + case XK_Pointer_UpLeft: return "Pointer_UpLeft"; break; + case XK_Pointer_UpRight: return "Pointer_UpRight"; break; + case XK_Pointer_DownLeft: return "Pointer_DownLeft"; break; + case XK_Pointer_DownRight: return "Pointer_DownRight"; break; + case XK_Pointer_Button_Dflt: return "Pointer_Button_Dflt"; break; + case XK_Pointer_Button1: return "Pointer_Button1"; break; + case XK_Pointer_Button2: return "Pointer_Button2"; break; + case XK_Pointer_Button3: return "Pointer_Button3"; break; + case XK_Pointer_Button4: return "Pointer_Button4"; break; + case XK_Pointer_Button5: return "Pointer_Button5"; break; + case XK_Pointer_DblClick_Dflt: return "Pointer_DblClick_Dflt"; break; + case XK_Pointer_DblClick1: return "Pointer_DblClick1"; break; + case XK_Pointer_DblClick2: return "Pointer_DblClick2"; break; + case XK_Pointer_DblClick3: return "Pointer_DblClick3"; break; + case XK_Pointer_DblClick4: return "Pointer_DblClick4"; break; + case XK_Pointer_DblClick5: return "Pointer_DblClick5"; break; + case XK_Pointer_Drag_Dflt: return "Pointer_Drag_Dflt"; break; + case XK_Pointer_Drag1: return "Pointer_Drag1"; break; + case XK_Pointer_Drag2: return "Pointer_Drag2"; break; + case XK_Pointer_Drag3: return "Pointer_Drag3"; break; + case XK_Pointer_Drag4: return "Pointer_Drag4"; break; + case XK_Pointer_Drag5: return "Pointer_Drag5"; break; + case XK_Pointer_EnableKeys: return "Pointer_EnableKeys"; break; + case XK_Pointer_Accelerate: return "Pointer_Accelerate"; break; + case XK_Pointer_DfltBtnNext: return "Pointer_DfltBtnNext"; break; + case XK_Pointer_DfltBtnPrev: return "Pointer_DfltBtnPrev"; break; + case XK_ch: return "ch"; break; + case XK_Ch: return "Ch"; break; + case XK_CH: return "CH"; break; + case XK_c_h: return "c_h"; break; + case XK_C_h: return "C_h"; break; + case XK_C_H: return "C_H"; break; + case XK_3270_Duplicate: return "3270_Duplicate"; break; + case XK_3270_FieldMark: return "3270_FieldMark"; break; + case XK_3270_Right2: return "3270_Right2"; break; + case XK_3270_Left2: return "3270_Left2"; break; + case XK_3270_BackTab: return "3270_BackTab"; break; + case XK_3270_EraseEOF: return "3270_EraseEOF"; break; + case XK_3270_EraseInput: return "3270_EraseInput"; break; + case XK_3270_Reset: return "3270_Reset"; break; + case XK_3270_Quit: return "3270_Quit"; break; + case XK_3270_PA1: return "3270_PA1"; break; + case XK_3270_PA2: return "3270_PA2"; break; + case XK_3270_PA3: return "3270_PA3"; break; + case XK_3270_Test: return "3270_Test"; break; + case XK_3270_Attn: return "3270_Attn"; break; + case XK_3270_CursorBlink: return "3270_CursorBlink"; break; + case XK_3270_AltCursor: return "3270_AltCursor"; break; + case XK_3270_KeyClick: return "3270_KeyClick"; break; + case XK_3270_Jump: return "3270_Jump"; break; + case XK_3270_Ident: return "3270_Ident"; break; + case XK_3270_Rule: return "3270_Rule"; break; + case XK_3270_Copy: return "3270_Copy"; break; + case XK_3270_Play: return "3270_Play"; break; + case XK_3270_Setup: return "3270_Setup"; break; + case XK_3270_Record: return "3270_Record"; break; + case XK_3270_ChangeScreen: return "3270_ChangeScreen"; break; + case XK_3270_DeleteWord: return "3270_DeleteWord"; break; + case XK_3270_ExSelect: return "3270_ExSelect"; break; + case XK_3270_CursorSelect: return "3270_CursorSelect"; break; + case XK_3270_PrintScreen: return "3270_PrintScreen"; break; + case XK_3270_Enter: return "3270_Enter"; break; + case XK_space: return "space"; break; + case XK_exclam: return "exclam"; break; + case XK_quotedbl: return "quotedbl"; break; + case XK_numbersign: return "numbersign"; break; + case XK_dollar: return "dollar"; break; + case XK_percent: return "percent"; break; + case XK_ampersand: return "ampersand"; break; + case XK_apostrophe: return "apostrophe"; break; + case XK_parenleft: return "parenleft"; break; + case XK_parenright: return "parenright"; break; + case XK_asterisk: return "asterisk"; break; + case XK_plus: return "plus"; break; + case XK_comma: return "comma"; break; + case XK_minus: return "minus"; break; + case XK_period: return "period"; break; + case XK_slash: return "slash"; break; + case XK_0: return "0"; break; + case XK_1: return "1"; break; + case XK_2: return "2"; break; + case XK_3: return "3"; break; + case XK_4: return "4"; break; + case XK_5: return "5"; break; + case XK_6: return "6"; break; + case XK_7: return "7"; break; + case XK_8: return "8"; break; + case XK_9: return "9"; break; + case XK_colon: return "colon"; break; + case XK_semicolon: return "semicolon"; break; + case XK_less: return "less"; break; + case XK_equal: return "equal"; break; + case XK_greater: return "greater"; break; + case XK_question: return "question"; break; + case XK_at: return "at"; break; + case XK_A: return "A"; break; + case XK_B: return "B"; break; + case XK_C: return "C"; break; + case XK_D: return "D"; break; + case XK_E: return "E"; break; + case XK_F: return "F"; break; + case XK_G: return "G"; break; + case XK_H: return "H"; break; + case XK_I: return "I"; break; + case XK_J: return "J"; break; + case XK_K: return "K"; break; + case XK_L: return "L"; break; + case XK_M: return "M"; break; + case XK_N: return "N"; break; + case XK_O: return "O"; break; + case XK_P: return "P"; break; + case XK_Q: return "Q"; break; + case XK_R: return "R"; break; + case XK_S: return "S"; break; + case XK_T: return "T"; break; + case XK_U: return "U"; break; + case XK_V: return "V"; break; + case XK_W: return "W"; break; + case XK_X: return "X"; break; + case XK_Y: return "Y"; break; + case XK_Z: return "Z"; break; + case XK_bracketleft: return "bracketleft"; break; + case XK_backslash: return "backslash"; break; + case XK_bracketright: return "bracketright"; break; + case XK_asciicircum: return "asciicircum"; break; + case XK_underscore: return "underscore"; break; + case XK_grave: return "grave"; break; + case XK_a: return "a"; break; + case XK_b: return "b"; break; + case XK_c: return "c"; break; + case XK_d: return "d"; break; + case XK_e: return "e"; break; + case XK_f: return "f"; break; + case XK_g: return "g"; break; + case XK_h: return "h"; break; + case XK_i: return "i"; break; + case XK_j: return "j"; break; + case XK_k: return "k"; break; + case XK_l: return "l"; break; + case XK_m: return "m"; break; + case XK_n: return "n"; break; + case XK_o: return "o"; break; + case XK_p: return "p"; break; + case XK_q: return "q"; break; + case XK_r: return "r"; break; + case XK_s: return "s"; break; + case XK_t: return "t"; break; + case XK_u: return "u"; break; + case XK_v: return "v"; break; + case XK_w: return "w"; break; + case XK_x: return "x"; break; + case XK_y: return "y"; break; + case XK_z: return "z"; break; + case XK_braceleft: return "braceleft"; break; + case XK_bar: return "bar"; break; + case XK_braceright: return "braceright"; break; + case XK_asciitilde: return "asciitilde"; break; + case XK_nobreakspace: return "nobreakspace"; break; + case XK_exclamdown: return "exclamdown"; break; + case XK_cent: return "cent"; break; + case XK_sterling: return "sterling"; break; + case XK_currency: return "currency"; break; + case XK_yen: return "yen"; break; + case XK_brokenbar: return "brokenbar"; break; + case XK_section: return "section"; break; + case XK_diaeresis: return "diaeresis"; break; + case XK_copyright: return "copyright"; break; + case XK_ordfeminine: return "ordfeminine"; break; + case XK_guillemotleft: return "guillemotleft"; break; + case XK_notsign: return "notsign"; break; + case XK_hyphen: return "hyphen"; break; + case XK_registered: return "registered"; break; + case XK_macron: return "macron"; break; + case XK_degree: return "degree"; break; + case XK_plusminus: return "plusminus"; break; + case XK_twosuperior: return "twosuperior"; break; + case XK_threesuperior: return "threesuperior"; break; + case XK_acute: return "acute"; break; + case XK_mu: return "mu"; break; + case XK_paragraph: return "paragraph"; break; + case XK_periodcentered: return "periodcentered"; break; + case XK_cedilla: return "cedilla"; break; + case XK_onesuperior: return "onesuperior"; break; + case XK_masculine: return "masculine"; break; + case XK_guillemotright: return "guillemotright"; break; + case XK_onequarter: return "onequarter"; break; + case XK_onehalf: return "onehalf"; break; + case XK_threequarters: return "threequarters"; break; + case XK_questiondown: return "questiondown"; break; + case XK_Agrave: return "Agrave"; break; + case XK_Aacute: return "Aacute"; break; + case XK_Acircumflex: return "Acircumflex"; break; + case XK_Atilde: return "Atilde"; break; + case XK_Adiaeresis: return "Adiaeresis"; break; + case XK_Aring: return "Aring"; break; + case XK_AE: return "AE"; break; + case XK_Ccedilla: return "Ccedilla"; break; + case XK_Egrave: return "Egrave"; break; + case XK_Eacute: return "Eacute"; break; + case XK_Ecircumflex: return "Ecircumflex"; break; + case XK_Ediaeresis: return "Ediaeresis"; break; + case XK_Igrave: return "Igrave"; break; + case XK_Iacute: return "Iacute"; break; + case XK_Icircumflex: return "Icircumflex"; break; + case XK_Idiaeresis: return "Idiaeresis"; break; + case XK_ETH: return "ETH"; break; + case XK_Ntilde: return "Ntilde"; break; + case XK_Ograve: return "Ograve"; break; + case XK_Oacute: return "Oacute"; break; + case XK_Ocircumflex: return "Ocircumflex"; break; + case XK_Otilde: return "Otilde"; break; + case XK_Odiaeresis: return "Odiaeresis"; break; + case XK_multiply: return "multiply"; break; + case XK_Oslash: return "Oslash"; break; + case XK_Ugrave: return "Ugrave"; break; + case XK_Uacute: return "Uacute"; break; + case XK_Ucircumflex: return "Ucircumflex"; break; + case XK_Udiaeresis: return "Udiaeresis"; break; + case XK_Yacute: return "Yacute"; break; + case XK_THORN: return "THORN"; break; + case XK_ssharp: return "ssharp"; break; + case XK_agrave: return "agrave"; break; + case XK_aacute: return "aacute"; break; + case XK_acircumflex: return "acircumflex"; break; + case XK_atilde: return "atilde"; break; + case XK_adiaeresis: return "adiaeresis"; break; + case XK_aring: return "aring"; break; + case XK_ae: return "ae"; break; + case XK_ccedilla: return "ccedilla"; break; + case XK_egrave: return "egrave"; break; + case XK_eacute: return "eacute"; break; + case XK_ecircumflex: return "ecircumflex"; break; + case XK_ediaeresis: return "ediaeresis"; break; + case XK_igrave: return "igrave"; break; + case XK_iacute: return "iacute"; break; + case XK_icircumflex: return "icircumflex"; break; + case XK_idiaeresis: return "idiaeresis"; break; + case XK_eth: return "eth"; break; + case XK_ntilde: return "ntilde"; break; + case XK_ograve: return "ograve"; break; + case XK_oacute: return "oacute"; break; + case XK_ocircumflex: return "ocircumflex"; break; + case XK_otilde: return "otilde"; break; + case XK_odiaeresis: return "odiaeresis"; break; + case XK_division: return "division"; break; + case XK_oslash: return "oslash"; break; + case XK_ugrave: return "ugrave"; break; + case XK_uacute: return "uacute"; break; + case XK_ucircumflex: return "ucircumflex"; break; + case XK_udiaeresis: return "udiaeresis"; break; + case XK_yacute: return "yacute"; break; + case XK_thorn: return "thorn"; break; + case XK_ydiaeresis: return "ydiaeresis"; break; + case XK_Aogonek: return "Aogonek"; break; + case XK_breve: return "breve"; break; + case XK_Lstroke: return "Lstroke"; break; + case XK_Lcaron: return "Lcaron"; break; + case XK_Sacute: return "Sacute"; break; + case XK_Scaron: return "Scaron"; break; + case XK_Tcaron: return "Tcaron"; break; + case XK_Zacute: return "Zacute"; break; + case XK_Zcaron: return "Zcaron"; break; + case XK_Zabovedot: return "Zabovedot"; break; + case XK_aogonek: return "aogonek"; break; + case XK_ogonek: return "ogonek"; break; + case XK_lstroke: return "lstroke"; break; + case XK_lcaron: return "lcaron"; break; + case XK_sacute: return "sacute"; break; + case XK_caron: return "caron"; break; + case XK_scaron: return "scaron"; break; + case XK_scedilla: return "scedilla"; break; + case XK_tcaron: return "tcaron"; break; + case XK_zacute: return "zacute"; break; + case XK_doubleacute: return "doubleacute"; break; + case XK_zcaron: return "zcaron"; break; + case XK_zabovedot: return "zabovedot"; break; + case XK_Racute: return "Racute"; break; + case XK_Abreve: return "Abreve"; break; + case XK_Lacute: return "Lacute"; break; + case XK_Cacute: return "Cacute"; break; + case XK_Ccaron: return "Ccaron"; break; + case XK_Eogonek: return "Eogonek"; break; + case XK_Ecaron: return "Ecaron"; break; + case XK_Dcaron: return "Dcaron"; break; + case XK_Dstroke: return "Dstroke"; break; + case XK_Nacute: return "Nacute"; break; + case XK_Ncaron: return "Ncaron"; break; + case XK_Odoubleacute: return "Odoubleacute"; break; + case XK_Rcaron: return "Rcaron"; break; + case XK_Uring: return "Uring"; break; + case XK_Udoubleacute: return "Udoubleacute"; break; + case XK_Tcedilla: return "Tcedilla"; break; + case XK_racute: return "racute"; break; + case XK_abreve: return "abreve"; break; + case XK_lacute: return "lacute"; break; + case XK_cacute: return "cacute"; break; + case XK_ccaron: return "ccaron"; break; + case XK_eogonek: return "eogonek"; break; + case XK_ecaron: return "ecaron"; break; + case XK_dcaron: return "dcaron"; break; + case XK_dstroke: return "dstroke"; break; + case XK_nacute: return "nacute"; break; + case XK_ncaron: return "ncaron"; break; + case XK_odoubleacute: return "odoubleacute"; break; + case XK_rcaron: return "rcaron"; break; + case XK_uring: return "uring"; break; + case XK_udoubleacute: return "udoubleacute"; break; + case XK_tcedilla: return "tcedilla"; break; + case XK_abovedot: return "abovedot"; break; + case XK_Hstroke: return "Hstroke"; break; + case XK_Hcircumflex: return "Hcircumflex"; break; + case XK_Iabovedot: return "Iabovedot"; break; + case XK_Gbreve: return "Gbreve"; break; + case XK_Jcircumflex: return "Jcircumflex"; break; + case XK_hstroke: return "hstroke"; break; + case XK_hcircumflex: return "hcircumflex"; break; + case XK_idotless: return "idotless"; break; + case XK_gbreve: return "gbreve"; break; + case XK_jcircumflex: return "jcircumflex"; break; + case XK_Cabovedot: return "Cabovedot"; break; + case XK_Ccircumflex: return "Ccircumflex"; break; + case XK_Gabovedot: return "Gabovedot"; break; + case XK_Gcircumflex: return "Gcircumflex"; break; + case XK_Ubreve: return "Ubreve"; break; + case XK_Scircumflex: return "Scircumflex"; break; + case XK_cabovedot: return "cabovedot"; break; + case XK_ccircumflex: return "ccircumflex"; break; + case XK_gabovedot: return "gabovedot"; break; + case XK_gcircumflex: return "gcircumflex"; break; + case XK_ubreve: return "ubreve"; break; + case XK_scircumflex: return "scircumflex"; break; + case XK_kra: return "kra"; break; + case XK_Rcedilla: return "Rcedilla"; break; + case XK_Itilde: return "Itilde"; break; + case XK_Lcedilla: return "Lcedilla"; break; + case XK_Emacron: return "Emacron"; break; + case XK_Gcedilla: return "Gcedilla"; break; + case XK_Tslash: return "Tslash"; break; + case XK_rcedilla: return "rcedilla"; break; + case XK_itilde: return "itilde"; break; + case XK_lcedilla: return "lcedilla"; break; + case XK_emacron: return "emacron"; break; + case XK_gcedilla: return "gcedilla"; break; + case XK_tslash: return "tslash"; break; + case XK_ENG: return "ENG"; break; + case XK_eng: return "eng"; break; + case XK_Amacron: return "Amacron"; break; + case XK_Iogonek: return "Iogonek"; break; + case XK_Eabovedot: return "Eabovedot"; break; + case XK_Imacron: return "Imacron"; break; + case XK_Ncedilla: return "Ncedilla"; break; + case XK_Omacron: return "Omacron"; break; + case XK_Kcedilla: return "Kcedilla"; break; + case XK_Uogonek: return "Uogonek"; break; + case XK_Utilde: return "Utilde"; break; + case XK_Umacron: return "Umacron"; break; + case XK_amacron: return "amacron"; break; + case XK_iogonek: return "iogonek"; break; + case XK_eabovedot: return "eabovedot"; break; + case XK_imacron: return "imacron"; break; + case XK_ncedilla: return "ncedilla"; break; + case XK_omacron: return "omacron"; break; + case XK_kcedilla: return "kcedilla"; break; + case XK_uogonek: return "uogonek"; break; + case XK_utilde: return "utilde"; break; + case XK_umacron: return "umacron"; break; + case XK_Wcircumflex: return "Wcircumflex"; break; + case XK_wcircumflex: return "wcircumflex"; break; + case XK_Ycircumflex: return "Ycircumflex"; break; + case XK_ycircumflex: return "ycircumflex"; break; + case XK_Babovedot: return "Babovedot"; break; + case XK_babovedot: return "babovedot"; break; + case XK_Dabovedot: return "Dabovedot"; break; + case XK_dabovedot: return "dabovedot"; break; + case XK_Fabovedot: return "Fabovedot"; break; + case XK_fabovedot: return "fabovedot"; break; + case XK_Mabovedot: return "Mabovedot"; break; + case XK_mabovedot: return "mabovedot"; break; + case XK_Pabovedot: return "Pabovedot"; break; + case XK_pabovedot: return "pabovedot"; break; + case XK_Sabovedot: return "Sabovedot"; break; + case XK_sabovedot: return "sabovedot"; break; + case XK_Tabovedot: return "Tabovedot"; break; + case XK_tabovedot: return "tabovedot"; break; + case XK_Wgrave: return "Wgrave"; break; + case XK_wgrave: return "wgrave"; break; + case XK_Wacute: return "Wacute"; break; + case XK_wacute: return "wacute"; break; + case XK_Wdiaeresis: return "Wdiaeresis"; break; + case XK_wdiaeresis: return "wdiaeresis"; break; + case XK_Ygrave: return "Ygrave"; break; + case XK_ygrave: return "ygrave"; break; + case XK_OE: return "OE"; break; + case XK_oe: return "oe"; break; + case XK_Ydiaeresis: return "Ydiaeresis"; break; + case XK_overline: return "overline"; break; + case XK_kana_fullstop: return "kana_fullstop"; break; + case XK_kana_openingbracket: return "kana_openingbracket"; break; + case XK_kana_closingbracket: return "kana_closingbracket"; break; + case XK_kana_comma: return "kana_comma"; break; + case XK_kana_conjunctive: return "kana_conjunctive"; break; + case XK_kana_WO: return "kana_WO"; break; + case XK_kana_a: return "kana_a"; break; + case XK_kana_i: return "kana_i"; break; + case XK_kana_u: return "kana_u"; break; + case XK_kana_e: return "kana_e"; break; + case XK_kana_o: return "kana_o"; break; + case XK_kana_ya: return "kana_ya"; break; + case XK_kana_yu: return "kana_yu"; break; + case XK_kana_yo: return "kana_yo"; break; + case XK_kana_tsu: return "kana_tsu"; break; + case XK_prolongedsound: return "prolongedsound"; break; + case XK_kana_A: return "kana_A"; break; + case XK_kana_I: return "kana_I"; break; + case XK_kana_U: return "kana_U"; break; + case XK_kana_E: return "kana_E"; break; + case XK_kana_O: return "kana_O"; break; + case XK_kana_KA: return "kana_KA"; break; + case XK_kana_KI: return "kana_KI"; break; + case XK_kana_KU: return "kana_KU"; break; + case XK_kana_KE: return "kana_KE"; break; + case XK_kana_KO: return "kana_KO"; break; + case XK_kana_SA: return "kana_SA"; break; + case XK_kana_SHI: return "kana_SHI"; break; + case XK_kana_SU: return "kana_SU"; break; + case XK_kana_SE: return "kana_SE"; break; + case XK_kana_SO: return "kana_SO"; break; + case XK_kana_TA: return "kana_TA"; break; + case XK_kana_CHI: return "kana_CHI"; break; + case XK_kana_TSU: return "kana_TSU"; break; + case XK_kana_TE: return "kana_TE"; break; + case XK_kana_TO: return "kana_TO"; break; + case XK_kana_NA: return "kana_NA"; break; + case XK_kana_NI: return "kana_NI"; break; + case XK_kana_NU: return "kana_NU"; break; + case XK_kana_NE: return "kana_NE"; break; + case XK_kana_NO: return "kana_NO"; break; + case XK_kana_HA: return "kana_HA"; break; + case XK_kana_HI: return "kana_HI"; break; + case XK_kana_FU: return "kana_FU"; break; + case XK_kana_HE: return "kana_HE"; break; + case XK_kana_HO: return "kana_HO"; break; + case XK_kana_MA: return "kana_MA"; break; + case XK_kana_MI: return "kana_MI"; break; + case XK_kana_MU: return "kana_MU"; break; + case XK_kana_ME: return "kana_ME"; break; + case XK_kana_MO: return "kana_MO"; break; + case XK_kana_YA: return "kana_YA"; break; + case XK_kana_YU: return "kana_YU"; break; + case XK_kana_YO: return "kana_YO"; break; + case XK_kana_RA: return "kana_RA"; break; + case XK_kana_RI: return "kana_RI"; break; + case XK_kana_RU: return "kana_RU"; break; + case XK_kana_RE: return "kana_RE"; break; + case XK_kana_RO: return "kana_RO"; break; + case XK_kana_WA: return "kana_WA"; break; + case XK_kana_N: return "kana_N"; break; + case XK_voicedsound: return "voicedsound"; break; + case XK_semivoicedsound: return "semivoicedsound"; break; + case XK_Farsi_0: return "Farsi_0"; break; + case XK_Farsi_1: return "Farsi_1"; break; + case XK_Farsi_2: return "Farsi_2"; break; + case XK_Farsi_3: return "Farsi_3"; break; + case XK_Farsi_4: return "Farsi_4"; break; + case XK_Farsi_5: return "Farsi_5"; break; + case XK_Farsi_6: return "Farsi_6"; break; + case XK_Farsi_7: return "Farsi_7"; break; + case XK_Farsi_8: return "Farsi_8"; break; + case XK_Farsi_9: return "Farsi_9"; break; + case XK_Arabic_percent: return "Arabic_percent"; break; + case XK_Arabic_superscript_alef: return "Arabic_superscript_alef"; break; + case XK_Arabic_tteh: return "Arabic_tteh"; break; + case XK_Arabic_peh: return "Arabic_peh"; break; + case XK_Arabic_tcheh: return "Arabic_tcheh"; break; + case XK_Arabic_ddal: return "Arabic_ddal"; break; + case XK_Arabic_rreh: return "Arabic_rreh"; break; + case XK_Arabic_comma: return "Arabic_comma"; break; + case XK_Arabic_fullstop: return "Arabic_fullstop"; break; + case XK_Arabic_0: return "Arabic_0"; break; + case XK_Arabic_1: return "Arabic_1"; break; + case XK_Arabic_2: return "Arabic_2"; break; + case XK_Arabic_3: return "Arabic_3"; break; + case XK_Arabic_4: return "Arabic_4"; break; + case XK_Arabic_5: return "Arabic_5"; break; + case XK_Arabic_6: return "Arabic_6"; break; + case XK_Arabic_7: return "Arabic_7"; break; + case XK_Arabic_8: return "Arabic_8"; break; + case XK_Arabic_9: return "Arabic_9"; break; + case XK_Arabic_semicolon: return "Arabic_semicolon"; break; + case XK_Arabic_question_mark: return "Arabic_question_mark"; break; + case XK_Arabic_hamza: return "Arabic_hamza"; break; + case XK_Arabic_maddaonalef: return "Arabic_maddaonalef"; break; + case XK_Arabic_hamzaonalef: return "Arabic_hamzaonalef"; break; + case XK_Arabic_hamzaonwaw: return "Arabic_hamzaonwaw"; break; + case XK_Arabic_hamzaunderalef: return "Arabic_hamzaunderalef"; break; + case XK_Arabic_hamzaonyeh: return "Arabic_hamzaonyeh"; break; + case XK_Arabic_alef: return "Arabic_alef"; break; + case XK_Arabic_beh: return "Arabic_beh"; break; + case XK_Arabic_tehmarbuta: return "Arabic_tehmarbuta"; break; + case XK_Arabic_teh: return "Arabic_teh"; break; + case XK_Arabic_theh: return "Arabic_theh"; break; + case XK_Arabic_jeem: return "Arabic_jeem"; break; + case XK_Arabic_hah: return "Arabic_hah"; break; + case XK_Arabic_khah: return "Arabic_khah"; break; + case XK_Arabic_dal: return "Arabic_dal"; break; + case XK_Arabic_thal: return "Arabic_thal"; break; + case XK_Arabic_ra: return "Arabic_ra"; break; + case XK_Arabic_zain: return "Arabic_zain"; break; + case XK_Arabic_seen: return "Arabic_seen"; break; + case XK_Arabic_sheen: return "Arabic_sheen"; break; + case XK_Arabic_sad: return "Arabic_sad"; break; + case XK_Arabic_dad: return "Arabic_dad"; break; + case XK_Arabic_tah: return "Arabic_tah"; break; + case XK_Arabic_zah: return "Arabic_zah"; break; + case XK_Arabic_ain: return "Arabic_ain"; break; + case XK_Arabic_ghain: return "Arabic_ghain"; break; + case XK_Arabic_tatweel: return "Arabic_tatweel"; break; + case XK_Arabic_feh: return "Arabic_feh"; break; + case XK_Arabic_qaf: return "Arabic_qaf"; break; + case XK_Arabic_kaf: return "Arabic_kaf"; break; + case XK_Arabic_lam: return "Arabic_lam"; break; + case XK_Arabic_meem: return "Arabic_meem"; break; + case XK_Arabic_noon: return "Arabic_noon"; break; + case XK_Arabic_ha: return "Arabic_ha"; break; + case XK_Arabic_waw: return "Arabic_waw"; break; + case XK_Arabic_alefmaksura: return "Arabic_alefmaksura"; break; + case XK_Arabic_yeh: return "Arabic_yeh"; break; + case XK_Arabic_fathatan: return "Arabic_fathatan"; break; + case XK_Arabic_dammatan: return "Arabic_dammatan"; break; + case XK_Arabic_kasratan: return "Arabic_kasratan"; break; + case XK_Arabic_fatha: return "Arabic_fatha"; break; + case XK_Arabic_damma: return "Arabic_damma"; break; + case XK_Arabic_kasra: return "Arabic_kasra"; break; + case XK_Arabic_shadda: return "Arabic_shadda"; break; + case XK_Arabic_sukun: return "Arabic_sukun"; break; + case XK_Arabic_madda_above: return "Arabic_madda_above"; break; + case XK_Arabic_hamza_above: return "Arabic_hamza_above"; break; + case XK_Arabic_hamza_below: return "Arabic_hamza_below"; break; + case XK_Arabic_jeh: return "Arabic_jeh"; break; + case XK_Arabic_veh: return "Arabic_veh"; break; + case XK_Arabic_keheh: return "Arabic_keheh"; break; + case XK_Arabic_gaf: return "Arabic_gaf"; break; + case XK_Arabic_noon_ghunna: return "Arabic_noon_ghunna"; break; + case XK_Arabic_heh_doachashmee: return "Arabic_heh_doachashmee"; break; + case XK_Farsi_yeh: return "Farsi_yeh"; break; + case XK_Arabic_yeh_baree: return "Arabic_yeh_baree"; break; + case XK_Arabic_heh_goal: return "Arabic_heh_goal"; break; + case XK_Cyrillic_GHE_bar: return "Cyrillic_GHE_bar"; break; + case XK_Cyrillic_ghe_bar: return "Cyrillic_ghe_bar"; break; + case XK_Cyrillic_ZHE_descender: return "Cyrillic_ZHE_descender"; break; + case XK_Cyrillic_zhe_descender: return "Cyrillic_zhe_descender"; break; + case XK_Cyrillic_KA_descender: return "Cyrillic_KA_descender"; break; + case XK_Cyrillic_ka_descender: return "Cyrillic_ka_descender"; break; + case XK_Cyrillic_KA_vertstroke: return "Cyrillic_KA_vertstroke"; break; + case XK_Cyrillic_ka_vertstroke: return "Cyrillic_ka_vertstroke"; break; + case XK_Cyrillic_EN_descender: return "Cyrillic_EN_descender"; break; + case XK_Cyrillic_en_descender: return "Cyrillic_en_descender"; break; + case XK_Cyrillic_U_straight: return "Cyrillic_U_straight"; break; + case XK_Cyrillic_u_straight: return "Cyrillic_u_straight"; break; + case XK_Cyrillic_U_straight_bar: return "Cyrillic_U_straight_bar"; break; + case XK_Cyrillic_u_straight_bar: return "Cyrillic_u_straight_bar"; break; + case XK_Cyrillic_HA_descender: return "Cyrillic_HA_descender"; break; + case XK_Cyrillic_ha_descender: return "Cyrillic_ha_descender"; break; + case XK_Cyrillic_CHE_descender: return "Cyrillic_CHE_descender"; break; + case XK_Cyrillic_che_descender: return "Cyrillic_che_descender"; break; + case XK_Cyrillic_CHE_vertstroke: return "Cyrillic_CHE_vertstroke"; break; + case XK_Cyrillic_che_vertstroke: return "Cyrillic_che_vertstroke"; break; + case XK_Cyrillic_SHHA: return "Cyrillic_SHHA"; break; + case XK_Cyrillic_shha: return "Cyrillic_shha"; break; + case XK_Cyrillic_SCHWA: return "Cyrillic_SCHWA"; break; + case XK_Cyrillic_schwa: return "Cyrillic_schwa"; break; + case XK_Cyrillic_I_macron: return "Cyrillic_I_macron"; break; + case XK_Cyrillic_i_macron: return "Cyrillic_i_macron"; break; + case XK_Cyrillic_O_bar: return "Cyrillic_O_bar"; break; + case XK_Cyrillic_o_bar: return "Cyrillic_o_bar"; break; + case XK_Cyrillic_U_macron: return "Cyrillic_U_macron"; break; + case XK_Cyrillic_u_macron: return "Cyrillic_u_macron"; break; + case XK_Serbian_dje: return "Serbian_dje"; break; + case XK_Macedonia_gje: return "Macedonia_gje"; break; + case XK_Cyrillic_io: return "Cyrillic_io"; break; + case XK_Ukrainian_ie: return "Ukrainian_ie"; break; + case XK_Macedonia_dse: return "Macedonia_dse"; break; + case XK_Ukrainian_i: return "Ukrainian_i"; break; + case XK_Ukrainian_yi: return "Ukrainian_yi"; break; + case XK_Cyrillic_je: return "Cyrillic_je"; break; + case XK_Cyrillic_lje: return "Cyrillic_lje"; break; + case XK_Cyrillic_nje: return "Cyrillic_nje"; break; + case XK_Serbian_tshe: return "Serbian_tshe"; break; + case XK_Macedonia_kje: return "Macedonia_kje"; break; + case XK_Ukrainian_ghe_with_upturn: return "Ukrainian_ghe_with_upturn"; break; + case XK_Byelorussian_shortu: return "Byelorussian_shortu"; break; + case XK_Cyrillic_dzhe: return "Cyrillic_dzhe"; break; + case XK_numerosign: return "numerosign"; break; + case XK_Serbian_DJE: return "Serbian_DJE"; break; + case XK_Macedonia_GJE: return "Macedonia_GJE"; break; + case XK_Cyrillic_IO: return "Cyrillic_IO"; break; + case XK_Ukrainian_IE: return "Ukrainian_IE"; break; + case XK_Macedonia_DSE: return "Macedonia_DSE"; break; + case XK_Ukrainian_I: return "Ukrainian_I"; break; + case XK_Ukrainian_YI: return "Ukrainian_YI"; break; + case XK_Cyrillic_JE: return "Cyrillic_JE"; break; + case XK_Cyrillic_LJE: return "Cyrillic_LJE"; break; + case XK_Cyrillic_NJE: return "Cyrillic_NJE"; break; + case XK_Serbian_TSHE: return "Serbian_TSHE"; break; + case XK_Macedonia_KJE: return "Macedonia_KJE"; break; + case XK_Ukrainian_GHE_WITH_UPTURN: return "Ukrainian_GHE_WITH_UPTURN"; break; + case XK_Byelorussian_SHORTU: return "Byelorussian_SHORTU"; break; + case XK_Cyrillic_DZHE: return "Cyrillic_DZHE"; break; + case XK_Cyrillic_yu: return "Cyrillic_yu"; break; + case XK_Cyrillic_a: return "Cyrillic_a"; break; + case XK_Cyrillic_be: return "Cyrillic_be"; break; + case XK_Cyrillic_tse: return "Cyrillic_tse"; break; + case XK_Cyrillic_de: return "Cyrillic_de"; break; + case XK_Cyrillic_ie: return "Cyrillic_ie"; break; + case XK_Cyrillic_ef: return "Cyrillic_ef"; break; + case XK_Cyrillic_ghe: return "Cyrillic_ghe"; break; + case XK_Cyrillic_ha: return "Cyrillic_ha"; break; + case XK_Cyrillic_i: return "Cyrillic_i"; break; + case XK_Cyrillic_shorti: return "Cyrillic_shorti"; break; + case XK_Cyrillic_ka: return "Cyrillic_ka"; break; + case XK_Cyrillic_el: return "Cyrillic_el"; break; + case XK_Cyrillic_em: return "Cyrillic_em"; break; + case XK_Cyrillic_en: return "Cyrillic_en"; break; + case XK_Cyrillic_o: return "Cyrillic_o"; break; + case XK_Cyrillic_pe: return "Cyrillic_pe"; break; + case XK_Cyrillic_ya: return "Cyrillic_ya"; break; + case XK_Cyrillic_er: return "Cyrillic_er"; break; + case XK_Cyrillic_es: return "Cyrillic_es"; break; + case XK_Cyrillic_te: return "Cyrillic_te"; break; + case XK_Cyrillic_u: return "Cyrillic_u"; break; + case XK_Cyrillic_zhe: return "Cyrillic_zhe"; break; + case XK_Cyrillic_ve: return "Cyrillic_ve"; break; + case XK_Cyrillic_softsign: return "Cyrillic_softsign"; break; + case XK_Cyrillic_yeru: return "Cyrillic_yeru"; break; + case XK_Cyrillic_ze: return "Cyrillic_ze"; break; + case XK_Cyrillic_sha: return "Cyrillic_sha"; break; + case XK_Cyrillic_e: return "Cyrillic_e"; break; + case XK_Cyrillic_shcha: return "Cyrillic_shcha"; break; + case XK_Cyrillic_che: return "Cyrillic_che"; break; + case XK_Cyrillic_hardsign: return "Cyrillic_hardsign"; break; + case XK_Cyrillic_YU: return "Cyrillic_YU"; break; + case XK_Cyrillic_A: return "Cyrillic_A"; break; + case XK_Cyrillic_BE: return "Cyrillic_BE"; break; + case XK_Cyrillic_TSE: return "Cyrillic_TSE"; break; + case XK_Cyrillic_DE: return "Cyrillic_DE"; break; + case XK_Cyrillic_IE: return "Cyrillic_IE"; break; + case XK_Cyrillic_EF: return "Cyrillic_EF"; break; + case XK_Cyrillic_GHE: return "Cyrillic_GHE"; break; + case XK_Cyrillic_HA: return "Cyrillic_HA"; break; + case XK_Cyrillic_I: return "Cyrillic_I"; break; + case XK_Cyrillic_SHORTI: return "Cyrillic_SHORTI"; break; + case XK_Cyrillic_KA: return "Cyrillic_KA"; break; + case XK_Cyrillic_EL: return "Cyrillic_EL"; break; + case XK_Cyrillic_EM: return "Cyrillic_EM"; break; + case XK_Cyrillic_EN: return "Cyrillic_EN"; break; + case XK_Cyrillic_O: return "Cyrillic_O"; break; + case XK_Cyrillic_PE: return "Cyrillic_PE"; break; + case XK_Cyrillic_YA: return "Cyrillic_YA"; break; + case XK_Cyrillic_ER: return "Cyrillic_ER"; break; + case XK_Cyrillic_ES: return "Cyrillic_ES"; break; + case XK_Cyrillic_TE: return "Cyrillic_TE"; break; + case XK_Cyrillic_U: return "Cyrillic_U"; break; + case XK_Cyrillic_ZHE: return "Cyrillic_ZHE"; break; + case XK_Cyrillic_VE: return "Cyrillic_VE"; break; + case XK_Cyrillic_SOFTSIGN: return "Cyrillic_SOFTSIGN"; break; + case XK_Cyrillic_YERU: return "Cyrillic_YERU"; break; + case XK_Cyrillic_ZE: return "Cyrillic_ZE"; break; + case XK_Cyrillic_SHA: return "Cyrillic_SHA"; break; + case XK_Cyrillic_E: return "Cyrillic_E"; break; + case XK_Cyrillic_SHCHA: return "Cyrillic_SHCHA"; break; + case XK_Cyrillic_CHE: return "Cyrillic_CHE"; break; + case XK_Cyrillic_HARDSIGN: return "Cyrillic_HARDSIGN"; break; + case XK_Greek_ALPHAaccent: return "Greek_ALPHAaccent"; break; + case XK_Greek_EPSILONaccent: return "Greek_EPSILONaccent"; break; + case XK_Greek_ETAaccent: return "Greek_ETAaccent"; break; + case XK_Greek_IOTAaccent: return "Greek_IOTAaccent"; break; + case XK_Greek_IOTAdieresis: return "Greek_IOTAdieresis"; break; + case XK_Greek_OMICRONaccent: return "Greek_OMICRONaccent"; break; + case XK_Greek_UPSILONaccent: return "Greek_UPSILONaccent"; break; + case XK_Greek_UPSILONdieresis: return "Greek_UPSILONdieresis"; break; + case XK_Greek_OMEGAaccent: return "Greek_OMEGAaccent"; break; + case XK_Greek_accentdieresis: return "Greek_accentdieresis"; break; + case XK_Greek_horizbar: return "Greek_horizbar"; break; + case XK_Greek_alphaaccent: return "Greek_alphaaccent"; break; + case XK_Greek_epsilonaccent: return "Greek_epsilonaccent"; break; + case XK_Greek_etaaccent: return "Greek_etaaccent"; break; + case XK_Greek_iotaaccent: return "Greek_iotaaccent"; break; + case XK_Greek_iotadieresis: return "Greek_iotadieresis"; break; + case XK_Greek_iotaaccentdieresis: return "Greek_iotaaccentdieresis"; break; + case XK_Greek_omicronaccent: return "Greek_omicronaccent"; break; + case XK_Greek_upsilonaccent: return "Greek_upsilonaccent"; break; + case XK_Greek_upsilondieresis: return "Greek_upsilondieresis"; break; + case XK_Greek_upsilonaccentdieresis: return "Greek_upsilonaccentdieresis"; break; + case XK_Greek_omegaaccent: return "Greek_omegaaccent"; break; + case XK_Greek_ALPHA: return "Greek_ALPHA"; break; + case XK_Greek_BETA: return "Greek_BETA"; break; + case XK_Greek_GAMMA: return "Greek_GAMMA"; break; + case XK_Greek_DELTA: return "Greek_DELTA"; break; + case XK_Greek_EPSILON: return "Greek_EPSILON"; break; + case XK_Greek_ZETA: return "Greek_ZETA"; break; + case XK_Greek_ETA: return "Greek_ETA"; break; + case XK_Greek_THETA: return "Greek_THETA"; break; + case XK_Greek_IOTA: return "Greek_IOTA"; break; + case XK_Greek_KAPPA: return "Greek_KAPPA"; break; + case XK_Greek_LAMDA: return "Greek_LAMDA"; break; + case XK_Greek_MU: return "Greek_MU"; break; + case XK_Greek_NU: return "Greek_NU"; break; + case XK_Greek_XI: return "Greek_XI"; break; + case XK_Greek_OMICRON: return "Greek_OMICRON"; break; + case XK_Greek_PI: return "Greek_PI"; break; + case XK_Greek_RHO: return "Greek_RHO"; break; + case XK_Greek_SIGMA: return "Greek_SIGMA"; break; + case XK_Greek_TAU: return "Greek_TAU"; break; + case XK_Greek_UPSILON: return "Greek_UPSILON"; break; + case XK_Greek_PHI: return "Greek_PHI"; break; + case XK_Greek_CHI: return "Greek_CHI"; break; + case XK_Greek_PSI: return "Greek_PSI"; break; + case XK_Greek_OMEGA: return "Greek_OMEGA"; break; + case XK_Greek_alpha: return "Greek_alpha"; break; + case XK_Greek_beta: return "Greek_beta"; break; + case XK_Greek_gamma: return "Greek_gamma"; break; + case XK_Greek_delta: return "Greek_delta"; break; + case XK_Greek_epsilon: return "Greek_epsilon"; break; + case XK_Greek_zeta: return "Greek_zeta"; break; + case XK_Greek_eta: return "Greek_eta"; break; + case XK_Greek_theta: return "Greek_theta"; break; + case XK_Greek_iota: return "Greek_iota"; break; + case XK_Greek_kappa: return "Greek_kappa"; break; + case XK_Greek_lamda: return "Greek_lamda"; break; + case XK_Greek_mu: return "Greek_mu"; break; + case XK_Greek_nu: return "Greek_nu"; break; + case XK_Greek_xi: return "Greek_xi"; break; + case XK_Greek_omicron: return "Greek_omicron"; break; + case XK_Greek_pi: return "Greek_pi"; break; + case XK_Greek_rho: return "Greek_rho"; break; + case XK_Greek_sigma: return "Greek_sigma"; break; + case XK_Greek_finalsmallsigma: return "Greek_finalsmallsigma"; break; + case XK_Greek_tau: return "Greek_tau"; break; + case XK_Greek_upsilon: return "Greek_upsilon"; break; + case XK_Greek_phi: return "Greek_phi"; break; + case XK_Greek_chi: return "Greek_chi"; break; + case XK_Greek_psi: return "Greek_psi"; break; + case XK_Greek_omega: return "Greek_omega"; break; + case XK_leftradical: return "leftradical"; break; + case XK_topleftradical: return "topleftradical"; break; + case XK_horizconnector: return "horizconnector"; break; + case XK_topintegral: return "topintegral"; break; + case XK_botintegral: return "botintegral"; break; + case XK_vertconnector: return "vertconnector"; break; + case XK_topleftsqbracket: return "topleftsqbracket"; break; + case XK_botleftsqbracket: return "botleftsqbracket"; break; + case XK_toprightsqbracket: return "toprightsqbracket"; break; + case XK_botrightsqbracket: return "botrightsqbracket"; break; + case XK_topleftparens: return "topleftparens"; break; + case XK_botleftparens: return "botleftparens"; break; + case XK_toprightparens: return "toprightparens"; break; + case XK_botrightparens: return "botrightparens"; break; + case XK_leftmiddlecurlybrace: return "leftmiddlecurlybrace"; break; + case XK_rightmiddlecurlybrace: return "rightmiddlecurlybrace"; break; + case XK_topleftsummation: return "topleftsummation"; break; + case XK_botleftsummation: return "botleftsummation"; break; + case XK_topvertsummationconnector: return "topvertsummationconnector"; break; + case XK_botvertsummationconnector: return "botvertsummationconnector"; break; + case XK_toprightsummation: return "toprightsummation"; break; + case XK_botrightsummation: return "botrightsummation"; break; + case XK_rightmiddlesummation: return "rightmiddlesummation"; break; + case XK_lessthanequal: return "lessthanequal"; break; + case XK_notequal: return "notequal"; break; + case XK_greaterthanequal: return "greaterthanequal"; break; + case XK_integral: return "integral"; break; + case XK_therefore: return "therefore"; break; + case XK_variation: return "variation"; break; + case XK_infinity: return "infinity"; break; + case XK_nabla: return "nabla"; break; + case XK_approximate: return "approximate"; break; + case XK_similarequal: return "similarequal"; break; + case XK_ifonlyif: return "ifonlyif"; break; + case XK_implies: return "implies"; break; + case XK_identical: return "identical"; break; + case XK_radical: return "radical"; break; + case XK_includedin: return "includedin"; break; + case XK_includes: return "includes"; break; + case XK_intersection: return "intersection"; break; + case XK_union: return "union"; break; + case XK_logicaland: return "logicaland"; break; + case XK_logicalor: return "logicalor"; break; + case XK_partialderivative: return "partialderivative"; break; + case XK_function: return "function"; break; + case XK_leftarrow: return "leftarrow"; break; + case XK_uparrow: return "uparrow"; break; + case XK_rightarrow: return "rightarrow"; break; + case XK_downarrow: return "downarrow"; break; + case XK_blank: return "blank"; break; + case XK_soliddiamond: return "soliddiamond"; break; + case XK_checkerboard: return "checkerboard"; break; + case XK_ht: return "ht"; break; + case XK_ff: return "ff"; break; + case XK_cr: return "cr"; break; + case XK_lf: return "lf"; break; + case XK_nl: return "nl"; break; + case XK_vt: return "vt"; break; + case XK_lowrightcorner: return "lowrightcorner"; break; + case XK_uprightcorner: return "uprightcorner"; break; + case XK_upleftcorner: return "upleftcorner"; break; + case XK_lowleftcorner: return "lowleftcorner"; break; + case XK_crossinglines: return "crossinglines"; break; + case XK_horizlinescan1: return "horizlinescan1"; break; + case XK_horizlinescan3: return "horizlinescan3"; break; + case XK_horizlinescan5: return "horizlinescan5"; break; + case XK_horizlinescan7: return "horizlinescan7"; break; + case XK_horizlinescan9: return "horizlinescan9"; break; + case XK_leftt: return "leftt"; break; + case XK_rightt: return "rightt"; break; + case XK_bott: return "bott"; break; + case XK_topt: return "topt"; break; + case XK_vertbar: return "vertbar"; break; + case XK_emspace: return "emspace"; break; + case XK_enspace: return "enspace"; break; + case XK_em3space: return "em3space"; break; + case XK_em4space: return "em4space"; break; + case XK_digitspace: return "digitspace"; break; + case XK_punctspace: return "punctspace"; break; + case XK_thinspace: return "thinspace"; break; + case XK_hairspace: return "hairspace"; break; + case XK_emdash: return "emdash"; break; + case XK_endash: return "endash"; break; + case XK_signifblank: return "signifblank"; break; + case XK_ellipsis: return "ellipsis"; break; + case XK_doubbaselinedot: return "doubbaselinedot"; break; + case XK_onethird: return "onethird"; break; + case XK_twothirds: return "twothirds"; break; + case XK_onefifth: return "onefifth"; break; + case XK_twofifths: return "twofifths"; break; + case XK_threefifths: return "threefifths"; break; + case XK_fourfifths: return "fourfifths"; break; + case XK_onesixth: return "onesixth"; break; + case XK_fivesixths: return "fivesixths"; break; + case XK_careof: return "careof"; break; + case XK_figdash: return "figdash"; break; + case XK_leftanglebracket: return "leftanglebracket"; break; + case XK_decimalpoint: return "decimalpoint"; break; + case XK_rightanglebracket: return "rightanglebracket"; break; + case XK_marker: return "marker"; break; + case XK_oneeighth: return "oneeighth"; break; + case XK_threeeighths: return "threeeighths"; break; + case XK_fiveeighths: return "fiveeighths"; break; + case XK_seveneighths: return "seveneighths"; break; + case XK_trademark: return "trademark"; break; + case XK_signaturemark: return "signaturemark"; break; + case XK_trademarkincircle: return "trademarkincircle"; break; + case XK_leftopentriangle: return "leftopentriangle"; break; + case XK_rightopentriangle: return "rightopentriangle"; break; + case XK_emopencircle: return "emopencircle"; break; + case XK_emopenrectangle: return "emopenrectangle"; break; + case XK_leftsinglequotemark: return "leftsinglequotemark"; break; + case XK_rightsinglequotemark: return "rightsinglequotemark"; break; + case XK_leftdoublequotemark: return "leftdoublequotemark"; break; + case XK_rightdoublequotemark: return "rightdoublequotemark"; break; + case XK_prescription: return "prescription"; break; + case XK_permille: return "permille"; break; + case XK_minutes: return "minutes"; break; + case XK_seconds: return "seconds"; break; + case XK_latincross: return "latincross"; break; + case XK_hexagram: return "hexagram"; break; + case XK_filledrectbullet: return "filledrectbullet"; break; + case XK_filledlefttribullet: return "filledlefttribullet"; break; + case XK_filledrighttribullet: return "filledrighttribullet"; break; + case XK_emfilledcircle: return "emfilledcircle"; break; + case XK_emfilledrect: return "emfilledrect"; break; + case XK_enopencircbullet: return "enopencircbullet"; break; + case XK_enopensquarebullet: return "enopensquarebullet"; break; + case XK_openrectbullet: return "openrectbullet"; break; + case XK_opentribulletup: return "opentribulletup"; break; + case XK_opentribulletdown: return "opentribulletdown"; break; + case XK_openstar: return "openstar"; break; + case XK_enfilledcircbullet: return "enfilledcircbullet"; break; + case XK_enfilledsqbullet: return "enfilledsqbullet"; break; + case XK_filledtribulletup: return "filledtribulletup"; break; + case XK_filledtribulletdown: return "filledtribulletdown"; break; + case XK_leftpointer: return "leftpointer"; break; + case XK_rightpointer: return "rightpointer"; break; + case XK_club: return "club"; break; + case XK_diamond: return "diamond"; break; + case XK_heart: return "heart"; break; + case XK_maltesecross: return "maltesecross"; break; + case XK_dagger: return "dagger"; break; + case XK_doubledagger: return "doubledagger"; break; + case XK_checkmark: return "checkmark"; break; + case XK_ballotcross: return "ballotcross"; break; + case XK_musicalsharp: return "musicalsharp"; break; + case XK_musicalflat: return "musicalflat"; break; + case XK_malesymbol: return "malesymbol"; break; + case XK_femalesymbol: return "femalesymbol"; break; + case XK_telephone: return "telephone"; break; + case XK_telephonerecorder: return "telephonerecorder"; break; + case XK_phonographcopyright: return "phonographcopyright"; break; + case XK_caret: return "caret"; break; + case XK_singlelowquotemark: return "singlelowquotemark"; break; + case XK_doublelowquotemark: return "doublelowquotemark"; break; + case XK_cursor: return "cursor"; break; + case XK_leftcaret: return "leftcaret"; break; + case XK_rightcaret: return "rightcaret"; break; + case XK_downcaret: return "downcaret"; break; + case XK_upcaret: return "upcaret"; break; + case XK_overbar: return "overbar"; break; + case XK_downtack: return "downtack"; break; + case XK_upshoe: return "upshoe"; break; + case XK_downstile: return "downstile"; break; + case XK_underbar: return "underbar"; break; + case XK_jot: return "jot"; break; + case XK_quad: return "quad"; break; + case XK_uptack: return "uptack"; break; + case XK_circle: return "circle"; break; + case XK_upstile: return "upstile"; break; + case XK_downshoe: return "downshoe"; break; + case XK_rightshoe: return "rightshoe"; break; + case XK_leftshoe: return "leftshoe"; break; + case XK_lefttack: return "lefttack"; break; + case XK_righttack: return "righttack"; break; + case XK_hebrew_doublelowline: return "hebrew_doublelowline"; break; + case XK_hebrew_aleph: return "hebrew_aleph"; break; + case XK_hebrew_bet: return "hebrew_bet"; break; + case XK_hebrew_gimel: return "hebrew_gimel"; break; + case XK_hebrew_dalet: return "hebrew_dalet"; break; + case XK_hebrew_he: return "hebrew_he"; break; + case XK_hebrew_waw: return "hebrew_waw"; break; + case XK_hebrew_zain: return "hebrew_zain"; break; + case XK_hebrew_chet: return "hebrew_chet"; break; + case XK_hebrew_tet: return "hebrew_tet"; break; + case XK_hebrew_yod: return "hebrew_yod"; break; + case XK_hebrew_finalkaph: return "hebrew_finalkaph"; break; + case XK_hebrew_kaph: return "hebrew_kaph"; break; + case XK_hebrew_lamed: return "hebrew_lamed"; break; + case XK_hebrew_finalmem: return "hebrew_finalmem"; break; + case XK_hebrew_mem: return "hebrew_mem"; break; + case XK_hebrew_finalnun: return "hebrew_finalnun"; break; + case XK_hebrew_nun: return "hebrew_nun"; break; + case XK_hebrew_samech: return "hebrew_samech"; break; + case XK_hebrew_ayin: return "hebrew_ayin"; break; + case XK_hebrew_finalpe: return "hebrew_finalpe"; break; + case XK_hebrew_pe: return "hebrew_pe"; break; + case XK_hebrew_finalzade: return "hebrew_finalzade"; break; + case XK_hebrew_zade: return "hebrew_zade"; break; + case XK_hebrew_qoph: return "hebrew_qoph"; break; + case XK_hebrew_resh: return "hebrew_resh"; break; + case XK_hebrew_shin: return "hebrew_shin"; break; + case XK_hebrew_taw: return "hebrew_taw"; break; + case XK_Thai_kokai: return "Thai_kokai"; break; + case XK_Thai_khokhai: return "Thai_khokhai"; break; + case XK_Thai_khokhuat: return "Thai_khokhuat"; break; + case XK_Thai_khokhwai: return "Thai_khokhwai"; break; + case XK_Thai_khokhon: return "Thai_khokhon"; break; + case XK_Thai_khorakhang: return "Thai_khorakhang"; break; + case XK_Thai_ngongu: return "Thai_ngongu"; break; + case XK_Thai_chochan: return "Thai_chochan"; break; + case XK_Thai_choching: return "Thai_choching"; break; + case XK_Thai_chochang: return "Thai_chochang"; break; + case XK_Thai_soso: return "Thai_soso"; break; + case XK_Thai_chochoe: return "Thai_chochoe"; break; + case XK_Thai_yoying: return "Thai_yoying"; break; + case XK_Thai_dochada: return "Thai_dochada"; break; + case XK_Thai_topatak: return "Thai_topatak"; break; + case XK_Thai_thothan: return "Thai_thothan"; break; + case XK_Thai_thonangmontho: return "Thai_thonangmontho"; break; + case XK_Thai_thophuthao: return "Thai_thophuthao"; break; + case XK_Thai_nonen: return "Thai_nonen"; break; + case XK_Thai_dodek: return "Thai_dodek"; break; + case XK_Thai_totao: return "Thai_totao"; break; + case XK_Thai_thothung: return "Thai_thothung"; break; + case XK_Thai_thothahan: return "Thai_thothahan"; break; + case XK_Thai_thothong: return "Thai_thothong"; break; + case XK_Thai_nonu: return "Thai_nonu"; break; + case XK_Thai_bobaimai: return "Thai_bobaimai"; break; + case XK_Thai_popla: return "Thai_popla"; break; + case XK_Thai_phophung: return "Thai_phophung"; break; + case XK_Thai_fofa: return "Thai_fofa"; break; + case XK_Thai_phophan: return "Thai_phophan"; break; + case XK_Thai_fofan: return "Thai_fofan"; break; + case XK_Thai_phosamphao: return "Thai_phosamphao"; break; + case XK_Thai_moma: return "Thai_moma"; break; + case XK_Thai_yoyak: return "Thai_yoyak"; break; + case XK_Thai_rorua: return "Thai_rorua"; break; + case XK_Thai_ru: return "Thai_ru"; break; + case XK_Thai_loling: return "Thai_loling"; break; + case XK_Thai_lu: return "Thai_lu"; break; + case XK_Thai_wowaen: return "Thai_wowaen"; break; + case XK_Thai_sosala: return "Thai_sosala"; break; + case XK_Thai_sorusi: return "Thai_sorusi"; break; + case XK_Thai_sosua: return "Thai_sosua"; break; + case XK_Thai_hohip: return "Thai_hohip"; break; + case XK_Thai_lochula: return "Thai_lochula"; break; + case XK_Thai_oang: return "Thai_oang"; break; + case XK_Thai_honokhuk: return "Thai_honokhuk"; break; + case XK_Thai_paiyannoi: return "Thai_paiyannoi"; break; + case XK_Thai_saraa: return "Thai_saraa"; break; + case XK_Thai_maihanakat: return "Thai_maihanakat"; break; + case XK_Thai_saraaa: return "Thai_saraaa"; break; + case XK_Thai_saraam: return "Thai_saraam"; break; + case XK_Thai_sarai: return "Thai_sarai"; break; + case XK_Thai_saraii: return "Thai_saraii"; break; + case XK_Thai_saraue: return "Thai_saraue"; break; + case XK_Thai_sarauee: return "Thai_sarauee"; break; + case XK_Thai_sarau: return "Thai_sarau"; break; + case XK_Thai_sarauu: return "Thai_sarauu"; break; + case XK_Thai_phinthu: return "Thai_phinthu"; break; + case XK_Thai_maihanakat_maitho: return "Thai_maihanakat_maitho"; break; + case XK_Thai_baht: return "Thai_baht"; break; + case XK_Thai_sarae: return "Thai_sarae"; break; + case XK_Thai_saraae: return "Thai_saraae"; break; + case XK_Thai_sarao: return "Thai_sarao"; break; + case XK_Thai_saraaimaimuan: return "Thai_saraaimaimuan"; break; + case XK_Thai_saraaimaimalai: return "Thai_saraaimaimalai"; break; + case XK_Thai_lakkhangyao: return "Thai_lakkhangyao"; break; + case XK_Thai_maiyamok: return "Thai_maiyamok"; break; + case XK_Thai_maitaikhu: return "Thai_maitaikhu"; break; + case XK_Thai_maiek: return "Thai_maiek"; break; + case XK_Thai_maitho: return "Thai_maitho"; break; + case XK_Thai_maitri: return "Thai_maitri"; break; + case XK_Thai_maichattawa: return "Thai_maichattawa"; break; + case XK_Thai_thanthakhat: return "Thai_thanthakhat"; break; + case XK_Thai_nikhahit: return "Thai_nikhahit"; break; + case XK_Thai_leksun: return "Thai_leksun"; break; + case XK_Thai_leknung: return "Thai_leknung"; break; + case XK_Thai_leksong: return "Thai_leksong"; break; + case XK_Thai_leksam: return "Thai_leksam"; break; + case XK_Thai_leksi: return "Thai_leksi"; break; + case XK_Thai_lekha: return "Thai_lekha"; break; + case XK_Thai_lekhok: return "Thai_lekhok"; break; + case XK_Thai_lekchet: return "Thai_lekchet"; break; + case XK_Thai_lekpaet: return "Thai_lekpaet"; break; + case XK_Thai_lekkao: return "Thai_lekkao"; break; + case XK_Hangul: return "Hangul"; break; + case XK_Hangul_Start: return "Hangul_Start"; break; + case XK_Hangul_End: return "Hangul_End"; break; + case XK_Hangul_Hanja: return "Hangul_Hanja"; break; + case XK_Hangul_Jamo: return "Hangul_Jamo"; break; + case XK_Hangul_Romaja: return "Hangul_Romaja"; break; + case XK_Hangul_Jeonja: return "Hangul_Jeonja"; break; + case XK_Hangul_Banja: return "Hangul_Banja"; break; + case XK_Hangul_PreHanja: return "Hangul_PreHanja"; break; + case XK_Hangul_PostHanja: return "Hangul_PostHanja"; break; + case XK_Hangul_Special: return "Hangul_Special"; break; + case XK_Hangul_Kiyeog: return "Hangul_Kiyeog"; break; + case XK_Hangul_SsangKiyeog: return "Hangul_SsangKiyeog"; break; + case XK_Hangul_KiyeogSios: return "Hangul_KiyeogSios"; break; + case XK_Hangul_Nieun: return "Hangul_Nieun"; break; + case XK_Hangul_NieunJieuj: return "Hangul_NieunJieuj"; break; + case XK_Hangul_NieunHieuh: return "Hangul_NieunHieuh"; break; + case XK_Hangul_Dikeud: return "Hangul_Dikeud"; break; + case XK_Hangul_SsangDikeud: return "Hangul_SsangDikeud"; break; + case XK_Hangul_Rieul: return "Hangul_Rieul"; break; + case XK_Hangul_RieulKiyeog: return "Hangul_RieulKiyeog"; break; + case XK_Hangul_RieulMieum: return "Hangul_RieulMieum"; break; + case XK_Hangul_RieulPieub: return "Hangul_RieulPieub"; break; + case XK_Hangul_RieulSios: return "Hangul_RieulSios"; break; + case XK_Hangul_RieulTieut: return "Hangul_RieulTieut"; break; + case XK_Hangul_RieulPhieuf: return "Hangul_RieulPhieuf"; break; + case XK_Hangul_RieulHieuh: return "Hangul_RieulHieuh"; break; + case XK_Hangul_Mieum: return "Hangul_Mieum"; break; + case XK_Hangul_Pieub: return "Hangul_Pieub"; break; + case XK_Hangul_SsangPieub: return "Hangul_SsangPieub"; break; + case XK_Hangul_PieubSios: return "Hangul_PieubSios"; break; + case XK_Hangul_Sios: return "Hangul_Sios"; break; + case XK_Hangul_SsangSios: return "Hangul_SsangSios"; break; + case XK_Hangul_Ieung: return "Hangul_Ieung"; break; + case XK_Hangul_Jieuj: return "Hangul_Jieuj"; break; + case XK_Hangul_SsangJieuj: return "Hangul_SsangJieuj"; break; + case XK_Hangul_Cieuc: return "Hangul_Cieuc"; break; + case XK_Hangul_Khieuq: return "Hangul_Khieuq"; break; + case XK_Hangul_Tieut: return "Hangul_Tieut"; break; + case XK_Hangul_Phieuf: return "Hangul_Phieuf"; break; + case XK_Hangul_Hieuh: return "Hangul_Hieuh"; break; + case XK_Hangul_A: return "Hangul_A"; break; + case XK_Hangul_AE: return "Hangul_AE"; break; + case XK_Hangul_YA: return "Hangul_YA"; break; + case XK_Hangul_YAE: return "Hangul_YAE"; break; + case XK_Hangul_EO: return "Hangul_EO"; break; + case XK_Hangul_E: return "Hangul_E"; break; + case XK_Hangul_YEO: return "Hangul_YEO"; break; + case XK_Hangul_YE: return "Hangul_YE"; break; + case XK_Hangul_O: return "Hangul_O"; break; + case XK_Hangul_WA: return "Hangul_WA"; break; + case XK_Hangul_WAE: return "Hangul_WAE"; break; + case XK_Hangul_OE: return "Hangul_OE"; break; + case XK_Hangul_YO: return "Hangul_YO"; break; + case XK_Hangul_U: return "Hangul_U"; break; + case XK_Hangul_WEO: return "Hangul_WEO"; break; + case XK_Hangul_WE: return "Hangul_WE"; break; + case XK_Hangul_WI: return "Hangul_WI"; break; + case XK_Hangul_YU: return "Hangul_YU"; break; + case XK_Hangul_EU: return "Hangul_EU"; break; + case XK_Hangul_YI: return "Hangul_YI"; break; + case XK_Hangul_I: return "Hangul_I"; break; + case XK_Hangul_J_Kiyeog: return "Hangul_J_Kiyeog"; break; + case XK_Hangul_J_SsangKiyeog: return "Hangul_J_SsangKiyeog"; break; + case XK_Hangul_J_KiyeogSios: return "Hangul_J_KiyeogSios"; break; + case XK_Hangul_J_Nieun: return "Hangul_J_Nieun"; break; + case XK_Hangul_J_NieunJieuj: return "Hangul_J_NieunJieuj"; break; + case XK_Hangul_J_NieunHieuh: return "Hangul_J_NieunHieuh"; break; + case XK_Hangul_J_Dikeud: return "Hangul_J_Dikeud"; break; + case XK_Hangul_J_Rieul: return "Hangul_J_Rieul"; break; + case XK_Hangul_J_RieulKiyeog: return "Hangul_J_RieulKiyeog"; break; + case XK_Hangul_J_RieulMieum: return "Hangul_J_RieulMieum"; break; + case XK_Hangul_J_RieulPieub: return "Hangul_J_RieulPieub"; break; + case XK_Hangul_J_RieulSios: return "Hangul_J_RieulSios"; break; + case XK_Hangul_J_RieulTieut: return "Hangul_J_RieulTieut"; break; + case XK_Hangul_J_RieulPhieuf: return "Hangul_J_RieulPhieuf"; break; + case XK_Hangul_J_RieulHieuh: return "Hangul_J_RieulHieuh"; break; + case XK_Hangul_J_Mieum: return "Hangul_J_Mieum"; break; + case XK_Hangul_J_Pieub: return "Hangul_J_Pieub"; break; + case XK_Hangul_J_PieubSios: return "Hangul_J_PieubSios"; break; + case XK_Hangul_J_Sios: return "Hangul_J_Sios"; break; + case XK_Hangul_J_SsangSios: return "Hangul_J_SsangSios"; break; + case XK_Hangul_J_Ieung: return "Hangul_J_Ieung"; break; + case XK_Hangul_J_Jieuj: return "Hangul_J_Jieuj"; break; + case XK_Hangul_J_Cieuc: return "Hangul_J_Cieuc"; break; + case XK_Hangul_J_Khieuq: return "Hangul_J_Khieuq"; break; + case XK_Hangul_J_Tieut: return "Hangul_J_Tieut"; break; + case XK_Hangul_J_Phieuf: return "Hangul_J_Phieuf"; break; + case XK_Hangul_J_Hieuh: return "Hangul_J_Hieuh"; break; + case XK_Hangul_RieulYeorinHieuh: return "Hangul_RieulYeorinHieuh"; break; + case XK_Hangul_SunkyeongeumMieum: return "Hangul_SunkyeongeumMieum"; break; + case XK_Hangul_SunkyeongeumPieub: return "Hangul_SunkyeongeumPieub"; break; + case XK_Hangul_PanSios: return "Hangul_PanSios"; break; + case XK_Hangul_KkogjiDalrinIeung: return "Hangul_KkogjiDalrinIeung"; break; + case XK_Hangul_SunkyeongeumPhieuf: return "Hangul_SunkyeongeumPhieuf"; break; + case XK_Hangul_YeorinHieuh: return "Hangul_YeorinHieuh"; break; + case XK_Hangul_AraeA: return "Hangul_AraeA"; break; + case XK_Hangul_AraeAE: return "Hangul_AraeAE"; break; + case XK_Hangul_J_PanSios: return "Hangul_J_PanSios"; break; + case XK_Hangul_J_KkogjiDalrinIeung: return "Hangul_J_KkogjiDalrinIeung"; break; + case XK_Hangul_J_YeorinHieuh: return "Hangul_J_YeorinHieuh"; break; + case XK_Korean_Won: return "Korean_Won"; break; + case XK_Armenian_ligature_ew: return "Armenian_ligature_ew"; break; + case XK_Armenian_full_stop: return "Armenian_full_stop"; break; + case XK_Armenian_separation_mark: return "Armenian_separation_mark"; break; + case XK_Armenian_hyphen: return "Armenian_hyphen"; break; + case XK_Armenian_exclam: return "Armenian_exclam"; break; + case XK_Armenian_accent: return "Armenian_accent"; break; + case XK_Armenian_question: return "Armenian_question"; break; + case XK_Armenian_AYB: return "Armenian_AYB"; break; + case XK_Armenian_ayb: return "Armenian_ayb"; break; + case XK_Armenian_BEN: return "Armenian_BEN"; break; + case XK_Armenian_ben: return "Armenian_ben"; break; + case XK_Armenian_GIM: return "Armenian_GIM"; break; + case XK_Armenian_gim: return "Armenian_gim"; break; + case XK_Armenian_DA: return "Armenian_DA"; break; + case XK_Armenian_da: return "Armenian_da"; break; + case XK_Armenian_YECH: return "Armenian_YECH"; break; + case XK_Armenian_yech: return "Armenian_yech"; break; + case XK_Armenian_ZA: return "Armenian_ZA"; break; + case XK_Armenian_za: return "Armenian_za"; break; + case XK_Armenian_E: return "Armenian_E"; break; + case XK_Armenian_e: return "Armenian_e"; break; + case XK_Armenian_AT: return "Armenian_AT"; break; + case XK_Armenian_at: return "Armenian_at"; break; + case XK_Armenian_TO: return "Armenian_TO"; break; + case XK_Armenian_to: return "Armenian_to"; break; + case XK_Armenian_ZHE: return "Armenian_ZHE"; break; + case XK_Armenian_zhe: return "Armenian_zhe"; break; + case XK_Armenian_INI: return "Armenian_INI"; break; + case XK_Armenian_ini: return "Armenian_ini"; break; + case XK_Armenian_LYUN: return "Armenian_LYUN"; break; + case XK_Armenian_lyun: return "Armenian_lyun"; break; + case XK_Armenian_KHE: return "Armenian_KHE"; break; + case XK_Armenian_khe: return "Armenian_khe"; break; + case XK_Armenian_TSA: return "Armenian_TSA"; break; + case XK_Armenian_tsa: return "Armenian_tsa"; break; + case XK_Armenian_KEN: return "Armenian_KEN"; break; + case XK_Armenian_ken: return "Armenian_ken"; break; + case XK_Armenian_HO: return "Armenian_HO"; break; + case XK_Armenian_ho: return "Armenian_ho"; break; + case XK_Armenian_DZA: return "Armenian_DZA"; break; + case XK_Armenian_dza: return "Armenian_dza"; break; + case XK_Armenian_GHAT: return "Armenian_GHAT"; break; + case XK_Armenian_ghat: return "Armenian_ghat"; break; + case XK_Armenian_TCHE: return "Armenian_TCHE"; break; + case XK_Armenian_tche: return "Armenian_tche"; break; + case XK_Armenian_MEN: return "Armenian_MEN"; break; + case XK_Armenian_men: return "Armenian_men"; break; + case XK_Armenian_HI: return "Armenian_HI"; break; + case XK_Armenian_hi: return "Armenian_hi"; break; + case XK_Armenian_NU: return "Armenian_NU"; break; + case XK_Armenian_nu: return "Armenian_nu"; break; + case XK_Armenian_SHA: return "Armenian_SHA"; break; + case XK_Armenian_sha: return "Armenian_sha"; break; + case XK_Armenian_VO: return "Armenian_VO"; break; + case XK_Armenian_vo: return "Armenian_vo"; break; + case XK_Armenian_CHA: return "Armenian_CHA"; break; + case XK_Armenian_cha: return "Armenian_cha"; break; + case XK_Armenian_PE: return "Armenian_PE"; break; + case XK_Armenian_pe: return "Armenian_pe"; break; + case XK_Armenian_JE: return "Armenian_JE"; break; + case XK_Armenian_je: return "Armenian_je"; break; + case XK_Armenian_RA: return "Armenian_RA"; break; + case XK_Armenian_ra: return "Armenian_ra"; break; + case XK_Armenian_SE: return "Armenian_SE"; break; + case XK_Armenian_se: return "Armenian_se"; break; + case XK_Armenian_VEV: return "Armenian_VEV"; break; + case XK_Armenian_vev: return "Armenian_vev"; break; + case XK_Armenian_TYUN: return "Armenian_TYUN"; break; + case XK_Armenian_tyun: return "Armenian_tyun"; break; + case XK_Armenian_RE: return "Armenian_RE"; break; + case XK_Armenian_re: return "Armenian_re"; break; + case XK_Armenian_TSO: return "Armenian_TSO"; break; + case XK_Armenian_tso: return "Armenian_tso"; break; + case XK_Armenian_VYUN: return "Armenian_VYUN"; break; + case XK_Armenian_vyun: return "Armenian_vyun"; break; + case XK_Armenian_PYUR: return "Armenian_PYUR"; break; + case XK_Armenian_pyur: return "Armenian_pyur"; break; + case XK_Armenian_KE: return "Armenian_KE"; break; + case XK_Armenian_ke: return "Armenian_ke"; break; + case XK_Armenian_O: return "Armenian_O"; break; + case XK_Armenian_o: return "Armenian_o"; break; + case XK_Armenian_FE: return "Armenian_FE"; break; + case XK_Armenian_fe: return "Armenian_fe"; break; + case XK_Armenian_apostrophe: return "Armenian_apostrophe"; break; + case XK_Georgian_an: return "Georgian_an"; break; + case XK_Georgian_ban: return "Georgian_ban"; break; + case XK_Georgian_gan: return "Georgian_gan"; break; + case XK_Georgian_don: return "Georgian_don"; break; + case XK_Georgian_en: return "Georgian_en"; break; + case XK_Georgian_vin: return "Georgian_vin"; break; + case XK_Georgian_zen: return "Georgian_zen"; break; + case XK_Georgian_tan: return "Georgian_tan"; break; + case XK_Georgian_in: return "Georgian_in"; break; + case XK_Georgian_kan: return "Georgian_kan"; break; + case XK_Georgian_las: return "Georgian_las"; break; + case XK_Georgian_man: return "Georgian_man"; break; + case XK_Georgian_nar: return "Georgian_nar"; break; + case XK_Georgian_on: return "Georgian_on"; break; + case XK_Georgian_par: return "Georgian_par"; break; + case XK_Georgian_zhar: return "Georgian_zhar"; break; + case XK_Georgian_rae: return "Georgian_rae"; break; + case XK_Georgian_san: return "Georgian_san"; break; + case XK_Georgian_tar: return "Georgian_tar"; break; + case XK_Georgian_un: return "Georgian_un"; break; + case XK_Georgian_phar: return "Georgian_phar"; break; + case XK_Georgian_khar: return "Georgian_khar"; break; + case XK_Georgian_ghan: return "Georgian_ghan"; break; + case XK_Georgian_qar: return "Georgian_qar"; break; + case XK_Georgian_shin: return "Georgian_shin"; break; + case XK_Georgian_chin: return "Georgian_chin"; break; + case XK_Georgian_can: return "Georgian_can"; break; + case XK_Georgian_jil: return "Georgian_jil"; break; + case XK_Georgian_cil: return "Georgian_cil"; break; + case XK_Georgian_char: return "Georgian_char"; break; + case XK_Georgian_xan: return "Georgian_xan"; break; + case XK_Georgian_jhan: return "Georgian_jhan"; break; + case XK_Georgian_hae: return "Georgian_hae"; break; + case XK_Georgian_he: return "Georgian_he"; break; + case XK_Georgian_hie: return "Georgian_hie"; break; + case XK_Georgian_we: return "Georgian_we"; break; + case XK_Georgian_har: return "Georgian_har"; break; + case XK_Georgian_hoe: return "Georgian_hoe"; break; + case XK_Georgian_fi: return "Georgian_fi"; break; + case XK_Xabovedot: return "Xabovedot"; break; + case XK_Ibreve: return "Ibreve"; break; + case XK_Zstroke: return "Zstroke"; break; + case XK_Gcaron: return "Gcaron"; break; + case XK_Ocaron: return "Ocaron"; break; + case XK_Obarred: return "Obarred"; break; + case XK_xabovedot: return "xabovedot"; break; + case XK_ibreve: return "ibreve"; break; + case XK_zstroke: return "zstroke"; break; + case XK_gcaron: return "gcaron"; break; + case XK_ocaron: return "ocaron"; break; + case XK_obarred: return "obarred"; break; + case XK_SCHWA: return "SCHWA"; break; + case XK_schwa: return "schwa"; break; + case XK_EZH: return "EZH"; break; + case XK_ezh: return "ezh"; break; + case XK_Lbelowdot: return "Lbelowdot"; break; + case XK_lbelowdot: return "lbelowdot"; break; + case XK_Abelowdot: return "Abelowdot"; break; + case XK_abelowdot: return "abelowdot"; break; + case XK_Ahook: return "Ahook"; break; + case XK_ahook: return "ahook"; break; + case XK_Acircumflexacute: return "Acircumflexacute"; break; + case XK_acircumflexacute: return "acircumflexacute"; break; + case XK_Acircumflexgrave: return "Acircumflexgrave"; break; + case XK_acircumflexgrave: return "acircumflexgrave"; break; + case XK_Acircumflexhook: return "Acircumflexhook"; break; + case XK_acircumflexhook: return "acircumflexhook"; break; + case XK_Acircumflextilde: return "Acircumflextilde"; break; + case XK_acircumflextilde: return "acircumflextilde"; break; + case XK_Acircumflexbelowdot: return "Acircumflexbelowdot"; break; + case XK_acircumflexbelowdot: return "acircumflexbelowdot"; break; + case XK_Abreveacute: return "Abreveacute"; break; + case XK_abreveacute: return "abreveacute"; break; + case XK_Abrevegrave: return "Abrevegrave"; break; + case XK_abrevegrave: return "abrevegrave"; break; + case XK_Abrevehook: return "Abrevehook"; break; + case XK_abrevehook: return "abrevehook"; break; + case XK_Abrevetilde: return "Abrevetilde"; break; + case XK_abrevetilde: return "abrevetilde"; break; + case XK_Abrevebelowdot: return "Abrevebelowdot"; break; + case XK_abrevebelowdot: return "abrevebelowdot"; break; + case XK_Ebelowdot: return "Ebelowdot"; break; + case XK_ebelowdot: return "ebelowdot"; break; + case XK_Ehook: return "Ehook"; break; + case XK_ehook: return "ehook"; break; + case XK_Etilde: return "Etilde"; break; + case XK_etilde: return "etilde"; break; + case XK_Ecircumflexacute: return "Ecircumflexacute"; break; + case XK_ecircumflexacute: return "ecircumflexacute"; break; + case XK_Ecircumflexgrave: return "Ecircumflexgrave"; break; + case XK_ecircumflexgrave: return "ecircumflexgrave"; break; + case XK_Ecircumflexhook: return "Ecircumflexhook"; break; + case XK_ecircumflexhook: return "ecircumflexhook"; break; + case XK_Ecircumflextilde: return "Ecircumflextilde"; break; + case XK_ecircumflextilde: return "ecircumflextilde"; break; + case XK_Ecircumflexbelowdot: return "Ecircumflexbelowdot"; break; + case XK_ecircumflexbelowdot: return "ecircumflexbelowdot"; break; + case XK_Ihook: return "Ihook"; break; + case XK_ihook: return "ihook"; break; + case XK_Ibelowdot: return "Ibelowdot"; break; + case XK_ibelowdot: return "ibelowdot"; break; + case XK_Obelowdot: return "Obelowdot"; break; + case XK_obelowdot: return "obelowdot"; break; + case XK_Ohook: return "Ohook"; break; + case XK_ohook: return "ohook"; break; + case XK_Ocircumflexacute: return "Ocircumflexacute"; break; + case XK_ocircumflexacute: return "ocircumflexacute"; break; + case XK_Ocircumflexgrave: return "Ocircumflexgrave"; break; + case XK_ocircumflexgrave: return "ocircumflexgrave"; break; + case XK_Ocircumflexhook: return "Ocircumflexhook"; break; + case XK_ocircumflexhook: return "ocircumflexhook"; break; + case XK_Ocircumflextilde: return "Ocircumflextilde"; break; + case XK_ocircumflextilde: return "ocircumflextilde"; break; + case XK_Ocircumflexbelowdot: return "Ocircumflexbelowdot"; break; + case XK_ocircumflexbelowdot: return "ocircumflexbelowdot"; break; + case XK_Ohornacute: return "Ohornacute"; break; + case XK_ohornacute: return "ohornacute"; break; + case XK_Ohorngrave: return "Ohorngrave"; break; + case XK_ohorngrave: return "ohorngrave"; break; + case XK_Ohornhook: return "Ohornhook"; break; + case XK_ohornhook: return "ohornhook"; break; + case XK_Ohorntilde: return "Ohorntilde"; break; + case XK_ohorntilde: return "ohorntilde"; break; + case XK_Ohornbelowdot: return "Ohornbelowdot"; break; + case XK_ohornbelowdot: return "ohornbelowdot"; break; + case XK_Ubelowdot: return "Ubelowdot"; break; + case XK_ubelowdot: return "ubelowdot"; break; + case XK_Uhook: return "Uhook"; break; + case XK_uhook: return "uhook"; break; + case XK_Uhornacute: return "Uhornacute"; break; + case XK_uhornacute: return "uhornacute"; break; + case XK_Uhorngrave: return "Uhorngrave"; break; + case XK_uhorngrave: return "uhorngrave"; break; + case XK_Uhornhook: return "Uhornhook"; break; + case XK_uhornhook: return "uhornhook"; break; + case XK_Uhorntilde: return "Uhorntilde"; break; + case XK_uhorntilde: return "uhorntilde"; break; + case XK_Uhornbelowdot: return "Uhornbelowdot"; break; + case XK_uhornbelowdot: return "uhornbelowdot"; break; + case XK_Ybelowdot: return "Ybelowdot"; break; + case XK_ybelowdot: return "ybelowdot"; break; + case XK_Yhook: return "Yhook"; break; + case XK_yhook: return "yhook"; break; + case XK_Ytilde: return "Ytilde"; break; + case XK_ytilde: return "ytilde"; break; + case XK_Ohorn: return "Ohorn"; break; + case XK_ohorn: return "ohorn"; break; + case XK_Uhorn: return "Uhorn"; break; + case XK_uhorn: return "uhorn"; break; + case XK_combining_tilde: return "combining_tilde"; break; + case XK_combining_grave: return "combining_grave"; break; + case XK_combining_acute: return "combining_acute"; break; + case XK_combining_hook: return "combining_hook"; break; + case XK_combining_belowdot: return "combining_belowdot"; break; + case XK_EcuSign: return "EcuSign"; break; + case XK_ColonSign: return "ColonSign"; break; + case XK_CruzeiroSign: return "CruzeiroSign"; break; + case XK_FFrancSign: return "FFrancSign"; break; + case XK_LiraSign: return "LiraSign"; break; + case XK_MillSign: return "MillSign"; break; + case XK_NairaSign: return "NairaSign"; break; + case XK_PesetaSign: return "PesetaSign"; break; + case XK_RupeeSign: return "RupeeSign"; break; + case XK_WonSign: return "WonSign"; break; + case XK_NewSheqelSign: return "NewSheqelSign"; break; + case XK_DongSign: return "DongSign"; break; + case XK_EuroSign: return "EuroSign"; break; + case XK_zerosuperior: return "zerosuperior"; break; + case XK_foursuperior: return "foursuperior"; break; + case XK_fivesuperior: return "fivesuperior"; break; + case XK_sixsuperior: return "sixsuperior"; break; + case XK_sevensuperior: return "sevensuperior"; break; + case XK_eightsuperior: return "eightsuperior"; break; + case XK_ninesuperior: return "ninesuperior"; break; + case XK_zerosubscript: return "zerosubscript"; break; + case XK_onesubscript: return "onesubscript"; break; + case XK_twosubscript: return "twosubscript"; break; + case XK_threesubscript: return "threesubscript"; break; + case XK_foursubscript: return "foursubscript"; break; + case XK_fivesubscript: return "fivesubscript"; break; + case XK_sixsubscript: return "sixsubscript"; break; + case XK_sevensubscript: return "sevensubscript"; break; + case XK_eightsubscript: return "eightsubscript"; break; + case XK_ninesubscript: return "ninesubscript"; break; + case XK_partdifferential: return "partdifferential"; break; + case XK_emptyset: return "emptyset"; break; + case XK_elementof: return "elementof"; break; + case XK_notelementof: return "notelementof"; break; + case XK_containsas: return "containsas"; break; + case XK_squareroot: return "squareroot"; break; + case XK_cuberoot: return "cuberoot"; break; + case XK_fourthroot: return "fourthroot"; break; + case XK_dintegral: return "dintegral"; break; + case XK_tintegral: return "tintegral"; break; + case XK_because: return "because"; break; + case XK_approxeq: return "approxeq"; break; + case XK_notapproxeq: return "notapproxeq"; break; + case XK_notidentical: return "notidentical"; break; + case XK_stricteq: return "stricteq"; break; + case XK_braille_dot_1: return "braille_dot_1"; break; + case XK_braille_dot_2: return "braille_dot_2"; break; + case XK_braille_dot_3: return "braille_dot_3"; break; + case XK_braille_dot_4: return "braille_dot_4"; break; + case XK_braille_dot_5: return "braille_dot_5"; break; + case XK_braille_dot_6: return "braille_dot_6"; break; + case XK_braille_dot_7: return "braille_dot_7"; break; + case XK_braille_dot_8: return "braille_dot_8"; break; + case XK_braille_dot_9: return "braille_dot_9"; break; + case XK_braille_dot_10: return "braille_dot_10"; break; + case XK_braille_blank: return "braille_blank"; break; + case XK_braille_dots_1: return "braille_dots_1"; break; + case XK_braille_dots_2: return "braille_dots_2"; break; + case XK_braille_dots_12: return "braille_dots_12"; break; + case XK_braille_dots_3: return "braille_dots_3"; break; + case XK_braille_dots_13: return "braille_dots_13"; break; + case XK_braille_dots_23: return "braille_dots_23"; break; + case XK_braille_dots_123: return "braille_dots_123"; break; + case XK_braille_dots_4: return "braille_dots_4"; break; + case XK_braille_dots_14: return "braille_dots_14"; break; + case XK_braille_dots_24: return "braille_dots_24"; break; + case XK_braille_dots_124: return "braille_dots_124"; break; + case XK_braille_dots_34: return "braille_dots_34"; break; + case XK_braille_dots_134: return "braille_dots_134"; break; + case XK_braille_dots_234: return "braille_dots_234"; break; + case XK_braille_dots_1234: return "braille_dots_1234"; break; + case XK_braille_dots_5: return "braille_dots_5"; break; + case XK_braille_dots_15: return "braille_dots_15"; break; + case XK_braille_dots_25: return "braille_dots_25"; break; + case XK_braille_dots_125: return "braille_dots_125"; break; + case XK_braille_dots_35: return "braille_dots_35"; break; + case XK_braille_dots_135: return "braille_dots_135"; break; + case XK_braille_dots_235: return "braille_dots_235"; break; + case XK_braille_dots_1235: return "braille_dots_1235"; break; + case XK_braille_dots_45: return "braille_dots_45"; break; + case XK_braille_dots_145: return "braille_dots_145"; break; + case XK_braille_dots_245: return "braille_dots_245"; break; + case XK_braille_dots_1245: return "braille_dots_1245"; break; + case XK_braille_dots_345: return "braille_dots_345"; break; + case XK_braille_dots_1345: return "braille_dots_1345"; break; + case XK_braille_dots_2345: return "braille_dots_2345"; break; + case XK_braille_dots_12345: return "braille_dots_12345"; break; + case XK_braille_dots_6: return "braille_dots_6"; break; + case XK_braille_dots_16: return "braille_dots_16"; break; + case XK_braille_dots_26: return "braille_dots_26"; break; + case XK_braille_dots_126: return "braille_dots_126"; break; + case XK_braille_dots_36: return "braille_dots_36"; break; + case XK_braille_dots_136: return "braille_dots_136"; break; + case XK_braille_dots_236: return "braille_dots_236"; break; + case XK_braille_dots_1236: return "braille_dots_1236"; break; + case XK_braille_dots_46: return "braille_dots_46"; break; + case XK_braille_dots_146: return "braille_dots_146"; break; + case XK_braille_dots_246: return "braille_dots_246"; break; + case XK_braille_dots_1246: return "braille_dots_1246"; break; + case XK_braille_dots_346: return "braille_dots_346"; break; + case XK_braille_dots_1346: return "braille_dots_1346"; break; + case XK_braille_dots_2346: return "braille_dots_2346"; break; + case XK_braille_dots_12346: return "braille_dots_12346"; break; + case XK_braille_dots_56: return "braille_dots_56"; break; + case XK_braille_dots_156: return "braille_dots_156"; break; + case XK_braille_dots_256: return "braille_dots_256"; break; + case XK_braille_dots_1256: return "braille_dots_1256"; break; + case XK_braille_dots_356: return "braille_dots_356"; break; + case XK_braille_dots_1356: return "braille_dots_1356"; break; + case XK_braille_dots_2356: return "braille_dots_2356"; break; + case XK_braille_dots_12356: return "braille_dots_12356"; break; + case XK_braille_dots_456: return "braille_dots_456"; break; + case XK_braille_dots_1456: return "braille_dots_1456"; break; + case XK_braille_dots_2456: return "braille_dots_2456"; break; + case XK_braille_dots_12456: return "braille_dots_12456"; break; + case XK_braille_dots_3456: return "braille_dots_3456"; break; + case XK_braille_dots_13456: return "braille_dots_13456"; break; + case XK_braille_dots_23456: return "braille_dots_23456"; break; + case XK_braille_dots_123456: return "braille_dots_123456"; break; + case XK_braille_dots_7: return "braille_dots_7"; break; + case XK_braille_dots_17: return "braille_dots_17"; break; + case XK_braille_dots_27: return "braille_dots_27"; break; + case XK_braille_dots_127: return "braille_dots_127"; break; + case XK_braille_dots_37: return "braille_dots_37"; break; + case XK_braille_dots_137: return "braille_dots_137"; break; + case XK_braille_dots_237: return "braille_dots_237"; break; + case XK_braille_dots_1237: return "braille_dots_1237"; break; + case XK_braille_dots_47: return "braille_dots_47"; break; + case XK_braille_dots_147: return "braille_dots_147"; break; + case XK_braille_dots_247: return "braille_dots_247"; break; + case XK_braille_dots_1247: return "braille_dots_1247"; break; + case XK_braille_dots_347: return "braille_dots_347"; break; + case XK_braille_dots_1347: return "braille_dots_1347"; break; + case XK_braille_dots_2347: return "braille_dots_2347"; break; + case XK_braille_dots_12347: return "braille_dots_12347"; break; + case XK_braille_dots_57: return "braille_dots_57"; break; + case XK_braille_dots_157: return "braille_dots_157"; break; + case XK_braille_dots_257: return "braille_dots_257"; break; + case XK_braille_dots_1257: return "braille_dots_1257"; break; + case XK_braille_dots_357: return "braille_dots_357"; break; + case XK_braille_dots_1357: return "braille_dots_1357"; break; + case XK_braille_dots_2357: return "braille_dots_2357"; break; + case XK_braille_dots_12357: return "braille_dots_12357"; break; + case XK_braille_dots_457: return "braille_dots_457"; break; + case XK_braille_dots_1457: return "braille_dots_1457"; break; + case XK_braille_dots_2457: return "braille_dots_2457"; break; + case XK_braille_dots_12457: return "braille_dots_12457"; break; + case XK_braille_dots_3457: return "braille_dots_3457"; break; + case XK_braille_dots_13457: return "braille_dots_13457"; break; + case XK_braille_dots_23457: return "braille_dots_23457"; break; + case XK_braille_dots_123457: return "braille_dots_123457"; break; + case XK_braille_dots_67: return "braille_dots_67"; break; + case XK_braille_dots_167: return "braille_dots_167"; break; + case XK_braille_dots_267: return "braille_dots_267"; break; + case XK_braille_dots_1267: return "braille_dots_1267"; break; + case XK_braille_dots_367: return "braille_dots_367"; break; + case XK_braille_dots_1367: return "braille_dots_1367"; break; + case XK_braille_dots_2367: return "braille_dots_2367"; break; + case XK_braille_dots_12367: return "braille_dots_12367"; break; + case XK_braille_dots_467: return "braille_dots_467"; break; + case XK_braille_dots_1467: return "braille_dots_1467"; break; + case XK_braille_dots_2467: return "braille_dots_2467"; break; + case XK_braille_dots_12467: return "braille_dots_12467"; break; + case XK_braille_dots_3467: return "braille_dots_3467"; break; + case XK_braille_dots_13467: return "braille_dots_13467"; break; + case XK_braille_dots_23467: return "braille_dots_23467"; break; + case XK_braille_dots_123467: return "braille_dots_123467"; break; + case XK_braille_dots_567: return "braille_dots_567"; break; + case XK_braille_dots_1567: return "braille_dots_1567"; break; + case XK_braille_dots_2567: return "braille_dots_2567"; break; + case XK_braille_dots_12567: return "braille_dots_12567"; break; + case XK_braille_dots_3567: return "braille_dots_3567"; break; + case XK_braille_dots_13567: return "braille_dots_13567"; break; + case XK_braille_dots_23567: return "braille_dots_23567"; break; + case XK_braille_dots_123567: return "braille_dots_123567"; break; + case XK_braille_dots_4567: return "braille_dots_4567"; break; + case XK_braille_dots_14567: return "braille_dots_14567"; break; + case XK_braille_dots_24567: return "braille_dots_24567"; break; + case XK_braille_dots_124567: return "braille_dots_124567"; break; + case XK_braille_dots_34567: return "braille_dots_34567"; break; + case XK_braille_dots_134567: return "braille_dots_134567"; break; + case XK_braille_dots_234567: return "braille_dots_234567"; break; + case XK_braille_dots_1234567: return "braille_dots_1234567"; break; + case XK_braille_dots_8: return "braille_dots_8"; break; + case XK_braille_dots_18: return "braille_dots_18"; break; + case XK_braille_dots_28: return "braille_dots_28"; break; + case XK_braille_dots_128: return "braille_dots_128"; break; + case XK_braille_dots_38: return "braille_dots_38"; break; + case XK_braille_dots_138: return "braille_dots_138"; break; + case XK_braille_dots_238: return "braille_dots_238"; break; + case XK_braille_dots_1238: return "braille_dots_1238"; break; + case XK_braille_dots_48: return "braille_dots_48"; break; + case XK_braille_dots_148: return "braille_dots_148"; break; + case XK_braille_dots_248: return "braille_dots_248"; break; + case XK_braille_dots_1248: return "braille_dots_1248"; break; + case XK_braille_dots_348: return "braille_dots_348"; break; + case XK_braille_dots_1348: return "braille_dots_1348"; break; + case XK_braille_dots_2348: return "braille_dots_2348"; break; + case XK_braille_dots_12348: return "braille_dots_12348"; break; + case XK_braille_dots_58: return "braille_dots_58"; break; + case XK_braille_dots_158: return "braille_dots_158"; break; + case XK_braille_dots_258: return "braille_dots_258"; break; + case XK_braille_dots_1258: return "braille_dots_1258"; break; + case XK_braille_dots_358: return "braille_dots_358"; break; + case XK_braille_dots_1358: return "braille_dots_1358"; break; + case XK_braille_dots_2358: return "braille_dots_2358"; break; + case XK_braille_dots_12358: return "braille_dots_12358"; break; + case XK_braille_dots_458: return "braille_dots_458"; break; + case XK_braille_dots_1458: return "braille_dots_1458"; break; + case XK_braille_dots_2458: return "braille_dots_2458"; break; + case XK_braille_dots_12458: return "braille_dots_12458"; break; + case XK_braille_dots_3458: return "braille_dots_3458"; break; + case XK_braille_dots_13458: return "braille_dots_13458"; break; + case XK_braille_dots_23458: return "braille_dots_23458"; break; + case XK_braille_dots_123458: return "braille_dots_123458"; break; + case XK_braille_dots_68: return "braille_dots_68"; break; + case XK_braille_dots_168: return "braille_dots_168"; break; + case XK_braille_dots_268: return "braille_dots_268"; break; + case XK_braille_dots_1268: return "braille_dots_1268"; break; + case XK_braille_dots_368: return "braille_dots_368"; break; + case XK_braille_dots_1368: return "braille_dots_1368"; break; + case XK_braille_dots_2368: return "braille_dots_2368"; break; + case XK_braille_dots_12368: return "braille_dots_12368"; break; + case XK_braille_dots_468: return "braille_dots_468"; break; + case XK_braille_dots_1468: return "braille_dots_1468"; break; + case XK_braille_dots_2468: return "braille_dots_2468"; break; + case XK_braille_dots_12468: return "braille_dots_12468"; break; + case XK_braille_dots_3468: return "braille_dots_3468"; break; + case XK_braille_dots_13468: return "braille_dots_13468"; break; + case XK_braille_dots_23468: return "braille_dots_23468"; break; + case XK_braille_dots_123468: return "braille_dots_123468"; break; + case XK_braille_dots_568: return "braille_dots_568"; break; + case XK_braille_dots_1568: return "braille_dots_1568"; break; + case XK_braille_dots_2568: return "braille_dots_2568"; break; + case XK_braille_dots_12568: return "braille_dots_12568"; break; + case XK_braille_dots_3568: return "braille_dots_3568"; break; + case XK_braille_dots_13568: return "braille_dots_13568"; break; + case XK_braille_dots_23568: return "braille_dots_23568"; break; + case XK_braille_dots_123568: return "braille_dots_123568"; break; + case XK_braille_dots_4568: return "braille_dots_4568"; break; + case XK_braille_dots_14568: return "braille_dots_14568"; break; + case XK_braille_dots_24568: return "braille_dots_24568"; break; + case XK_braille_dots_124568: return "braille_dots_124568"; break; + case XK_braille_dots_34568: return "braille_dots_34568"; break; + case XK_braille_dots_134568: return "braille_dots_134568"; break; + case XK_braille_dots_234568: return "braille_dots_234568"; break; + case XK_braille_dots_1234568: return "braille_dots_1234568"; break; + case XK_braille_dots_78: return "braille_dots_78"; break; + case XK_braille_dots_178: return "braille_dots_178"; break; + case XK_braille_dots_278: return "braille_dots_278"; break; + case XK_braille_dots_1278: return "braille_dots_1278"; break; + case XK_braille_dots_378: return "braille_dots_378"; break; + case XK_braille_dots_1378: return "braille_dots_1378"; break; + case XK_braille_dots_2378: return "braille_dots_2378"; break; + case XK_braille_dots_12378: return "braille_dots_12378"; break; + case XK_braille_dots_478: return "braille_dots_478"; break; + case XK_braille_dots_1478: return "braille_dots_1478"; break; + case XK_braille_dots_2478: return "braille_dots_2478"; break; + case XK_braille_dots_12478: return "braille_dots_12478"; break; + case XK_braille_dots_3478: return "braille_dots_3478"; break; + case XK_braille_dots_13478: return "braille_dots_13478"; break; + case XK_braille_dots_23478: return "braille_dots_23478"; break; + case XK_braille_dots_123478: return "braille_dots_123478"; break; + case XK_braille_dots_578: return "braille_dots_578"; break; + case XK_braille_dots_1578: return "braille_dots_1578"; break; + case XK_braille_dots_2578: return "braille_dots_2578"; break; + case XK_braille_dots_12578: return "braille_dots_12578"; break; + case XK_braille_dots_3578: return "braille_dots_3578"; break; + case XK_braille_dots_13578: return "braille_dots_13578"; break; + case XK_braille_dots_23578: return "braille_dots_23578"; break; + case XK_braille_dots_123578: return "braille_dots_123578"; break; + case XK_braille_dots_4578: return "braille_dots_4578"; break; + case XK_braille_dots_14578: return "braille_dots_14578"; break; + case XK_braille_dots_24578: return "braille_dots_24578"; break; + case XK_braille_dots_124578: return "braille_dots_124578"; break; + case XK_braille_dots_34578: return "braille_dots_34578"; break; + case XK_braille_dots_134578: return "braille_dots_134578"; break; + case XK_braille_dots_234578: return "braille_dots_234578"; break; + case XK_braille_dots_1234578: return "braille_dots_1234578"; break; + case XK_braille_dots_678: return "braille_dots_678"; break; + case XK_braille_dots_1678: return "braille_dots_1678"; break; + case XK_braille_dots_2678: return "braille_dots_2678"; break; + case XK_braille_dots_12678: return "braille_dots_12678"; break; + case XK_braille_dots_3678: return "braille_dots_3678"; break; + case XK_braille_dots_13678: return "braille_dots_13678"; break; + case XK_braille_dots_23678: return "braille_dots_23678"; break; + case XK_braille_dots_123678: return "braille_dots_123678"; break; + case XK_braille_dots_4678: return "braille_dots_4678"; break; + case XK_braille_dots_14678: return "braille_dots_14678"; break; + case XK_braille_dots_24678: return "braille_dots_24678"; break; + case XK_braille_dots_124678: return "braille_dots_124678"; break; + case XK_braille_dots_34678: return "braille_dots_34678"; break; + case XK_braille_dots_134678: return "braille_dots_134678"; break; + case XK_braille_dots_234678: return "braille_dots_234678"; break; + case XK_braille_dots_1234678: return "braille_dots_1234678"; break; + case XK_braille_dots_5678: return "braille_dots_5678"; break; + case XK_braille_dots_15678: return "braille_dots_15678"; break; + case XK_braille_dots_25678: return "braille_dots_25678"; break; + case XK_braille_dots_125678: return "braille_dots_125678"; break; + case XK_braille_dots_35678: return "braille_dots_35678"; break; + case XK_braille_dots_135678: return "braille_dots_135678"; break; + case XK_braille_dots_235678: return "braille_dots_235678"; break; + case XK_braille_dots_1235678: return "braille_dots_1235678"; break; + case XK_braille_dots_45678: return "braille_dots_45678"; break; + case XK_braille_dots_145678: return "braille_dots_145678"; break; + case XK_braille_dots_245678: return "braille_dots_245678"; break; + case XK_braille_dots_1245678: return "braille_dots_1245678"; break; + case XK_braille_dots_345678: return "braille_dots_345678"; break; + case XK_braille_dots_1345678: return "braille_dots_1345678"; break; + case XK_braille_dots_2345678: return "braille_dots_2345678"; break; + case XK_braille_dots_12345678: return "braille_dots_12345678"; break; + case XK_Sinh_ng: return "Sinh_ng"; break; + case XK_Sinh_h2: return "Sinh_h2"; break; + case XK_Sinh_a: return "Sinh_a"; break; + case XK_Sinh_aa: return "Sinh_aa"; break; + case XK_Sinh_ae: return "Sinh_ae"; break; + case XK_Sinh_aee: return "Sinh_aee"; break; + case XK_Sinh_i: return "Sinh_i"; break; + case XK_Sinh_ii: return "Sinh_ii"; break; + case XK_Sinh_u: return "Sinh_u"; break; + case XK_Sinh_uu: return "Sinh_uu"; break; + case XK_Sinh_ri: return "Sinh_ri"; break; + case XK_Sinh_rii: return "Sinh_rii"; break; + case XK_Sinh_lu: return "Sinh_lu"; break; + case XK_Sinh_luu: return "Sinh_luu"; break; + case XK_Sinh_e: return "Sinh_e"; break; + case XK_Sinh_ee: return "Sinh_ee"; break; + case XK_Sinh_ai: return "Sinh_ai"; break; + case XK_Sinh_o: return "Sinh_o"; break; + case XK_Sinh_oo: return "Sinh_oo"; break; + case XK_Sinh_au: return "Sinh_au"; break; + case XK_Sinh_ka: return "Sinh_ka"; break; + case XK_Sinh_kha: return "Sinh_kha"; break; + case XK_Sinh_ga: return "Sinh_ga"; break; + case XK_Sinh_gha: return "Sinh_gha"; break; + case XK_Sinh_ng2: return "Sinh_ng2"; break; + case XK_Sinh_nga: return "Sinh_nga"; break; + case XK_Sinh_ca: return "Sinh_ca"; break; + case XK_Sinh_cha: return "Sinh_cha"; break; + case XK_Sinh_ja: return "Sinh_ja"; break; + case XK_Sinh_jha: return "Sinh_jha"; break; + case XK_Sinh_nya: return "Sinh_nya"; break; + case XK_Sinh_jnya: return "Sinh_jnya"; break; + case XK_Sinh_nja: return "Sinh_nja"; break; + case XK_Sinh_tta: return "Sinh_tta"; break; + case XK_Sinh_ttha: return "Sinh_ttha"; break; + case XK_Sinh_dda: return "Sinh_dda"; break; + case XK_Sinh_ddha: return "Sinh_ddha"; break; + case XK_Sinh_nna: return "Sinh_nna"; break; + case XK_Sinh_ndda: return "Sinh_ndda"; break; + case XK_Sinh_tha: return "Sinh_tha"; break; + case XK_Sinh_thha: return "Sinh_thha"; break; + case XK_Sinh_dha: return "Sinh_dha"; break; + case XK_Sinh_dhha: return "Sinh_dhha"; break; + case XK_Sinh_na: return "Sinh_na"; break; + case XK_Sinh_ndha: return "Sinh_ndha"; break; + case XK_Sinh_pa: return "Sinh_pa"; break; + case XK_Sinh_pha: return "Sinh_pha"; break; + case XK_Sinh_ba: return "Sinh_ba"; break; + case XK_Sinh_bha: return "Sinh_bha"; break; + case XK_Sinh_ma: return "Sinh_ma"; break; + case XK_Sinh_mba: return "Sinh_mba"; break; + case XK_Sinh_ya: return "Sinh_ya"; break; + case XK_Sinh_ra: return "Sinh_ra"; break; + case XK_Sinh_la: return "Sinh_la"; break; + case XK_Sinh_va: return "Sinh_va"; break; + case XK_Sinh_sha: return "Sinh_sha"; break; + case XK_Sinh_ssha: return "Sinh_ssha"; break; + case XK_Sinh_sa: return "Sinh_sa"; break; + case XK_Sinh_ha: return "Sinh_ha"; break; + case XK_Sinh_lla: return "Sinh_lla"; break; + case XK_Sinh_fa: return "Sinh_fa"; break; + case XK_Sinh_al: return "Sinh_al"; break; + case XK_Sinh_aa2: return "Sinh_aa2"; break; + case XK_Sinh_ae2: return "Sinh_ae2"; break; + case XK_Sinh_aee2: return "Sinh_aee2"; break; + case XK_Sinh_i2: return "Sinh_i2"; break; + case XK_Sinh_ii2: return "Sinh_ii2"; break; + case XK_Sinh_u2: return "Sinh_u2"; break; + case XK_Sinh_uu2: return "Sinh_uu2"; break; + case XK_Sinh_ru2: return "Sinh_ru2"; break; + case XK_Sinh_e2: return "Sinh_e2"; break; + case XK_Sinh_ee2: return "Sinh_ee2"; break; + case XK_Sinh_ai2: return "Sinh_ai2"; break; + case XK_Sinh_o2: return "Sinh_o2"; break; + case XK_Sinh_oo2: return "Sinh_oo2"; break; + case XK_Sinh_au2: return "Sinh_au2"; break; + case XK_Sinh_lu2: return "Sinh_lu2"; break; + case XK_Sinh_ruu2: return "Sinh_ruu2"; break; + case XK_Sinh_luu2: return "Sinh_luu2"; break; + case XK_Sinh_kunddaliya: return "Sinh_kunddaliya"; break; + default: return ""; break; + } +} diff --git a/shmximage.cpp b/shmximage.cpp new file mode 100644 index 0000000..ea60ab0 --- /dev/null +++ b/shmximage.cpp @@ -0,0 +1,64 @@ +static int shm_error; +static int (*X_handler)(Display *, XErrorEvent *) = NULL; + +static bool have_mitshm(Display *dpy) +{ + // Only use shared memory on local X servers + return X11_XShmQueryExtension(dpy) ? SDL_X11_HAVE_SHM : false; +} + +static int shm_errhandler(Display *d, XErrorEvent *e) +{ + if (e->error_code == BadAccess) { + shm_error = True; + return 0; + } + return X_handler(d, e); +} + + +{ + if (have_mitshm(display)) { + XShmSegmentInfo shminfo; + + shminfo.shmid = shmget(IPC_PRIVATE, (size_t)h * (*pitch), IPC_CREAT | 0777); + if (shminfo.shmid >= 0) { + shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + if (shminfo.shmaddr != (char *)-1) { + shm_error = False; + X_handler = X11_XSetErrorHandler(shm_errhandler); + X11_XShmAttach(display, shminfo); + XSync(display, False); + X11_XSetErrorHandler(X_handler); + if (shm_error) { + shmdt(shminfo.shmaddr); + } + } else { + shm_error = True; + } + shmctl(shminfo.shmid, IPC_RMID, NULL); + } else { + shm_error = True; + } + if (!shm_error){ + ximage = X11_XShmCreateImage(display, data->visual, + vinfo.depth, ZPixmap, + shminfo.shmaddr, shminfo, + w, h); + if (!ximage) { + X11_XShmDetach(display, shminfo); + X11_XSync(display, False); + shmdt(shminfo.shmaddr); + } else { + // Done! + ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst; + *pixels = shminfo.shmaddr; + return true; + } + } + } +} + +X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage, x, y, x, y, w, h, False); + @@ -0,0 +1,358 @@ +// SPDX-FileCopyrightText: © 2023 Phillip Trudeau-Tavara <pmttavara@protonmail.com> +// SPDX-License-Identifier: MIT + +/* + +TODO: Optional Helper APIs: + + - Compression API: would require a mutexed lockable context (yuck...) + - Either using a ZIP library, a name cache + TIDPID cache, or both (but ZIP is likely more than enough!!!) + - begin()/end() writes compressed chunks to a caller-determined destination + - The destination can be the buffered-writing API or a custom user destination + - Ultimately need to take a lock with some granularity... can that be the caller's responsibility? + + - Counter Event: should allow tracking arbitrary named values with a single event, for memory and frame profiling + + - Ring-buffer API + spall_ring_init + spall_ring_emit_begin + spall_ring_emit_end + spall_ring_flush +*/ + +#ifndef SPALL_H +#define SPALL_H + +#if !defined(_MSC_VER) || defined(__clang__) +#define SPALL_NOINSTRUMENT __attribute__((no_instrument_function)) +#define SPALL_FORCEINLINE __attribute__((always_inline)) +#else +#define _CRT_SECURE_NO_WARNINGS +#define SPALL_NOINSTRUMENT // Can't noinstrument on MSVC! +#define SPALL_FORCEINLINE __forceinline +#endif + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +#define SPALL_FN static inline SPALL_NOINSTRUMENT +#define SPALL_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define SPALL_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#pragma pack(push, 1) + +typedef struct SpallHeader { + uint64_t magic_header; // = 0x0BADF00D + uint64_t version; // = 3 + double timestamp_unit; + uint64_t must_be_0; +} SpallHeader; + +typedef enum { + SpallEventType_Invalid = 0, + SpallEventType_Custom_Data = 1, // Basic readers can skip this. + SpallEventType_StreamOver = 2, + + SpallEventType_Begin = 3, + SpallEventType_End = 4, + SpallEventType_Instant = 5, + + SpallEventType_Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency. + SpallEventType_Pad_Skip = 7, + + SpallEventType_NameProcess = 8, + SpallEventType_NameThread = 9, +} SpallEventType; + +typedef struct SpallBufferHeader { + uint32_t size; + uint32_t tid; + uint32_t pid; + uint64_t first_ts; +} SpallBufferHeader; + +typedef struct SpallBeginEvent { + uint8_t type; // = SpallEventType_Begin + uint64_t when; + + uint8_t name_length; + uint8_t args_length; +} SpallBeginEvent; + +typedef struct SpallBeginEventMax { + SpallBeginEvent event; + char name_bytes[255]; + char args_bytes[255]; +} SpallBeginEventMax; + +typedef struct SpallEndEvent { + uint8_t type; // = SpallEventType_End + uint64_t when; +} SpallEndEvent; + +typedef struct SpallPadSkipEvent { + uint8_t type; // = SpallEventType_Pad_Skip + uint32_t size; +} SpallPadSkipEvent; + +typedef struct SpallNameContainerEvent { + uint8_t type; // = SpallEventType_NameThread/Process + uint8_t name_length; +} SpallNameContainerEvent; + +typedef struct SpallNameContainerEventMax { + SpallNameContainerEvent event; + char name_bytes[255]; +} SpallNameContainerEventMax; + +#pragma pack(pop) + +typedef struct SpallProfile SpallProfile; + +// Important!: If you define your own callbacks, mark them SPALL_NOINSTRUMENT! +typedef bool (*SpallWriteCallback)(SpallProfile *self, const void *data, size_t length); +typedef bool (*SpallFlushCallback)(SpallProfile *self); +typedef void (*SpallCloseCallback)(SpallProfile *self); + +struct SpallProfile { + double timestamp_unit; + SpallWriteCallback write; + SpallFlushCallback flush; + SpallCloseCallback close; + void *data; +}; + +// Important!: If you are writing Begin/End events, then do NOT write +// events for the same PID + TID pair on different buffers!!! +typedef struct SpallBuffer { + void *data; + size_t length; + uint32_t tid; + uint32_t pid; + + // Internal data - don't assign this + size_t head; + uint64_t first_ts; +} SpallBuffer; + +#ifdef __cplusplus +extern "C" { +#endif + +SPALL_FN SPALL_FORCEINLINE bool spall__file_write(SpallProfile *ctx, const void *p, size_t n) { + if (fwrite(p, n, 1, (FILE *)ctx->data) != 1) return false; + return true; +} +SPALL_FN bool spall__file_flush(SpallProfile *ctx) { + if (fflush((FILE *)ctx->data)) return false; + return true; +} +SPALL_FN void spall__file_close(SpallProfile *ctx) { + fclose((FILE *)ctx->data); + ctx->data = NULL; +} + +SPALL_FN SPALL_FORCEINLINE bool spall__buffer_flush(SpallProfile *ctx, SpallBuffer *wb, uint64_t ts) { + wb->first_ts = SPALL_MAX(wb->first_ts, ts); + + SpallBufferHeader hdr; + hdr.size = wb->head - sizeof(SpallBufferHeader); + hdr.pid = wb->pid; + hdr.tid = wb->tid; + hdr.first_ts = wb->first_ts; + + memcpy(wb->data, &hdr, sizeof(hdr)); + + if (!ctx->write(ctx, wb->data, wb->head)) return false; + wb->head = sizeof(SpallBufferHeader); + return true; +} + +SPALL_FN bool spall_buffer_flush(SpallProfile *ctx, SpallBuffer *wb) { + if (!spall__buffer_flush(ctx, wb, 0)) return false; + return true; +} + +SPALL_FN bool spall_buffer_quit(SpallProfile *ctx, SpallBuffer *wb) { + if (!spall_buffer_flush(ctx, wb)) return false; + return true; +} + +SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timestamp_unit) { + size_t header_size = sizeof(SpallHeader); + if (header_size > rem_size) { + return 0; + } + + SpallHeader *header = (SpallHeader *)buffer; + header->magic_header = 0x0BADF00D; + header->version = 3; + header->timestamp_unit = timestamp_unit; + header->must_be_0 = 0; + return header_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) { + SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer; + uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?) + uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?) + + size_t ev_size = sizeof(SpallBeginEvent) + trunc_name_len + trunc_args_len; + if (ev_size > rem_size) { + return 0; + } + + ev->event.type = SpallEventType_Begin; + ev->event.when = when; + ev->event.name_length = trunc_name_len; + ev->event.args_length = trunc_args_len; + memcpy(ev->name_bytes, name, trunc_name_len); + memcpy(ev->name_bytes + trunc_name_len, args, trunc_args_len); + + return ev_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, uint64_t when) { + size_t ev_size = sizeof(SpallEndEvent); + if (ev_size > rem_size) { + return 0; + } + + SpallEndEvent *ev = (SpallEndEvent *)buffer; + ev->type = SpallEventType_End; + ev->when = when; + + return ev_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_name(void *buffer, size_t rem_size, const char *name, int32_t name_len, SpallEventType type) { + SpallNameContainerEventMax *ev = (SpallNameContainerEventMax *)buffer; + uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?) + + size_t ev_size = sizeof(SpallNameContainerEvent) + trunc_name_len; + if (ev_size > rem_size) { + return 0; + } + + ev->event.type = type; + ev->event.name_length = trunc_name_len; + memcpy(ev->name_bytes, name, trunc_name_len); + + return ev_size; +} + +SPALL_FN void spall_quit(SpallProfile *ctx) { + if (!ctx) return; + if (ctx->close) ctx->close(ctx); + + memset(ctx, 0, sizeof(*ctx)); +} + +SPALL_FN bool spall_init_callbacks(double timestamp_unit, + SpallWriteCallback write, + SpallFlushCallback flush, + SpallCloseCallback close, + void *userdata, + SpallProfile *ctx) { + + if (timestamp_unit < 0) return false; + + memset(ctx, 0, sizeof(*ctx)); + ctx->timestamp_unit = timestamp_unit; + ctx->data = userdata; + ctx->write = write; + ctx->flush = flush; + ctx->close = close; + + SpallHeader header; + size_t len = spall_build_header(&header, sizeof(header), timestamp_unit); + if (!ctx->write(ctx, &header, len)) { + spall_quit(ctx); + return false; + } + + return true; +} + +SPALL_FN bool spall_init_file(const char* filename, double timestamp_unit, SpallProfile *ctx) { + if (!filename) return false; + + FILE *f = fopen(filename, "wb"); // TODO: handle utf8 and long paths on windows + if (f) { // basically freopen() but we don't want to force users to lug along another macro define + fclose(f); + f = fopen(filename, "ab"); + } + if (!f) { return false; } + + return spall_init_callbacks(timestamp_unit, spall__file_write, spall__file_flush, spall__file_close, (void *)f, ctx); +} + +SPALL_FN bool spall_flush(SpallProfile *ctx) { + if (!ctx->flush(ctx)) return false; + return true; +} + +SPALL_FN bool spall_buffer_init(SpallProfile *ctx, SpallBuffer *wb) { + // Fails if buffer is not big enough to contain at least one event! + if (wb->length < sizeof(SpallBufferHeader) + sizeof(SpallBeginEventMax)) { + return false; + } + + wb->head = sizeof(SpallBufferHeader); + return true; +} + + +SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) { + if ((wb->head + sizeof(SpallBeginEventMax)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, when)) { + return false; + } + } + + wb->head += spall_build_begin((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, args, args_len, when); + + return true; +} + +SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, uint64_t when) { + return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when); +} + +SPALL_FN bool spall_buffer_end(SpallProfile *ctx, SpallBuffer *wb, uint64_t when) { + if ((wb->head + sizeof(SpallEndEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, when)) { + return false; + } + } + + wb->head += spall_build_end((char *)wb->data + wb->head, wb->length - wb->head, when); + return true; +} + +SPALL_FN bool spall_buffer_name_thread(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) { + if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, 0)) { + return false; + } + } + + wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameThread); + return true; +} + +SPALL_FN bool spall_buffer_name_process(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) { + if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, 0)) { + return false; + } + } + + wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameProcess); + return true; +} + +#ifdef __cplusplus +} +#endif + +#endif // SPALL_H diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..74461e9 --- /dev/null +++ b/todo.txt @@ -0,0 +1,50 @@ +- Pause program while the window is not focused? +- Debug markers for sound (delay, avail, pointers, timestamps) +- Sound: Lagfree playback + recovery after live reload +- Replay: faster LinuxBeginRecordingInput() + memory mapped file. +- Better memory sparseness by using growing virtual memory where when the page is touched it is allocated. (reserved but not committed). +- Get rid of libasound.so & xlib + - or load it at runtime + - or link statically +- Fix fullscreen showing old contents after resize + - Fill the new size with background_pixel color + - Draw borders around the content +- Dynamic resizing + - Or resize when fullscreen +- Mouse polling is not fast enough for dragging. + - Be able to have a debug version of mouse polling where you can see each a line of where the mouse was. +- XPutImage takes 5ms, we could speed it up by using (https://stackoverflow.com/questions/61212999/is-it-possible-to-reduce-the-draw-delay-in-this-x11-program). + + + +x Own printf implementation for debugging using stb_sprintf +x Toggle fullscreen +x Threading +x Input: Cross frame values, half transition count and average. +x Replay: Multiple slots +x Input: Detect new controllers / disconnects. +x Hide cursor +x Replay +x Hot reload +x Gamepad Input +x Get Refresh rate +x Keyboard Input +x Timing +x File IO +x input: IsConnected bug: if checking on the property the inputs will be played multiple times. +x Sound (Play sinewave) + +To fix the issue of updating from multiple projects to the server we clone only once +and in projects we do the submodule off the filesystem. + + We need to make it so changes are updated automatically. + + Install a post-commit hook in the project's submodule that: + 1. -> Pushes the changes to the filesystem repo + + Install a post-receive hook in the filesystem repository that: + 1. -> Pushes the changes to the server repo + 2. -> Saves the repo it's coming from if it is new + 3. -> Push the changes to all projects saved + + So we create a install.sh script. |
