aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handmade_platform.h327
-rwxr-xr-xinstall.sh16
-rw-r--r--linux_handmade.cpp1846
-rw-r--r--linux_handmade.h92
-rw-r--r--linux_profile.h45
-rw-r--r--linux_x11_keysyms_to_strings.c2048
-rw-r--r--shmximage.cpp64
-rw-r--r--spall.h358
-rw-r--r--todo.txt50
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);
+
diff --git a/spall.h b/spall.h
new file mode 100644
index 0000000..e7f5641
--- /dev/null
+++ b/spall.h
@@ -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.