├── .gitmodules ├── test ├── audio │ ├── arugh.wav │ ├── arughSt.wav │ ├── sample.wav │ ├── african_roomS.wav │ ├── multi_sndfile_player.lua │ ├── audiodrivers.lua │ ├── audiotest.lua │ ├── audiotest_nocallback.lua │ ├── test-wav_async.lua │ ├── sndfile_player.lua │ ├── loopwave.lua │ └── multi_sndfile_player_gui.lua ├── video │ ├── lena.bmp │ └── test-bmp.lua ├── threads │ ├── main.lua │ ├── main_atomic.lua │ └── main2.lua └── camera │ ├── read-and-draw.lua │ ├── read-process-draw.lua │ └── camera_opengl.lua ├── generator ├── generator.bat ├── generator.lua └── cpp2ffi.lua ├── CMakeLists.txt └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SDL"] 2 | path = SDL 3 | url = https://github.com/libsdl-org/SDL.git 4 | -------------------------------------------------------------------------------- /test/audio/arugh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL3/HEAD/test/audio/arugh.wav -------------------------------------------------------------------------------- /test/video/lena.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL3/HEAD/test/video/lena.bmp -------------------------------------------------------------------------------- /test/audio/arughSt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL3/HEAD/test/audio/arughSt.wav -------------------------------------------------------------------------------- /test/audio/sample.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL3/HEAD/test/audio/sample.wav -------------------------------------------------------------------------------- /test/audio/african_roomS.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL3/HEAD/test/audio/african_roomS.wav -------------------------------------------------------------------------------- /generator/generator.bat: -------------------------------------------------------------------------------- 1 | rem set your PATH if necessary for gcc and lua with: 2 | set PATH=C:\anima;C:\mingws\i686-7.2.0-release-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH% 3 | ::set PATH=%PATH%;C:\x86_64-8.1.0-release-posix-seh-rt_v6-rev0\mingw64\bin;C:\luaGL; 4 | ::gcc -E -dD -I ../SDL/include/ ../SDL/include/SDL.h 5 | luajit.exe ./generator.lua 6 | ::type tmp.lua glfw_base.lua > glfw.lua 7 | ::del tmp.lua 8 | 9 | cmd /k 10 | 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(sdl3_ffi) 2 | #to allow install from subdirectory 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | 6 | set(SDL_STATIC OFF CACHE INTERNAL "dont build static!") 7 | 8 | add_subdirectory(SDL EXCLUDE_FROM_ALL) 9 | set_target_properties(SDL3-shared PROPERTIES OUTPUT_NAME "SDL3") 10 | 11 | add_custom_target(snd3_ffi ALL) 12 | add_dependencies(snd3_ffi SDL3-shared) 13 | 14 | install(TARGETS SDL3-shared RUNTIME DESTINATION ${LUAJIT_BIN} 15 | LIBRARY DESTINATION ${LUAJIT_BIN}) 16 | INSTALL(FILES sdl3_ffi.lua DESTINATION ${LUAJIT_BIN}/lua) #sdl3AudioPlayer.lua 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/video/test-bmp.lua: -------------------------------------------------------------------------------- 1 | local sdl = require 'sdl3_ffi' 2 | local ffi = require 'ffi' 3 | local C = ffi.C 4 | 5 | sdl.init(sdl.INIT_VIDEO) 6 | 7 | local window = sdl.createWindow("Hello Lena", 512, 512,0) 8 | 9 | local windowsurface = sdl.getWindowSurface(window) 10 | 11 | local image = sdl.LoadBMP("lena.bmp") 12 | 13 | sdl.BlitSurface(image, nil, windowsurface, nil) 14 | 15 | sdl.updateWindowSurface(window) 16 | sdl.destroySurface(image) 17 | 18 | local running = true 19 | local event = ffi.new('SDL_Event') 20 | while running do 21 | while sdl.pollEvent(event) do 22 | if event.type == sdl.EVENT_QUIT then 23 | running = false 24 | end 25 | end 26 | end 27 | 28 | sdl.destroyWindow(window) 29 | sdl.quit() 30 | -------------------------------------------------------------------------------- /test/threads/main.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | 5 | if (not sdl.init(sdl.INIT_VIDEO)) then 6 | 7 | print(string.format("Error: %s\n", sdl.getError())); 8 | return -1; 9 | end 10 | 11 | --/* Very simple thread - counts 0 to 9 delaying 1000ms between increments */ 12 | local function TestThread() 13 | local sdl = require"sdl3_ffi" 14 | return function(ptr) 15 | local cnt; 16 | for i = 0,9 do 17 | sdl.delay(1000); 18 | print(string.format("\nThread counter: %d", i)); 19 | cnt = i 20 | end 21 | return cnt; 22 | end 23 | end 24 | 25 | local threadReturnValue = ffi.new("int[1]") 26 | 27 | print("\nSimple SDL_CreateThread test:"); 28 | 29 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",nil) 30 | 31 | if (nil == thread) then 32 | local err = sdl.getError() 33 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 34 | else 35 | sdl.waitThread(thread, threadReturnValue); 36 | print(string.format("\nThread returned value: %d", threadReturnValue[0])); 37 | end 38 | 39 | -------------------------------------------------------------------------------- /test/audio/multi_sndfile_player.lua: -------------------------------------------------------------------------------- 1 | 2 | local sdl = require 'sdl2_ffi' 3 | local ffi = require 'ffi' 4 | --https://github.com/sonoro1234/LuaJIT-libsndfile 5 | local sndf = require"sndfile_ffi" 6 | local AudioPlayer = require"sdlAudioPlayer" 7 | 8 | -----------------------main-------------------------------------- 9 | local filename = "african_roomS.wav"; 10 | 11 | --/* Enable standard application logging */ 12 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 13 | 14 | --/* Load the SDL library */ 15 | if (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 16 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 17 | return (1); 18 | end 19 | 20 | --copy specs from file 21 | local info = sndf.get_info(filename) 22 | local audioplayer,err = AudioPlayer({ 23 | --device = device_name, 24 | freq = info.samplerate, 25 | format = sdl.AUDIO_S16SYS, 26 | channels = info.channels, 27 | samples = 1024}) 28 | 29 | --insert several files 30 | for i=1,10 do 31 | --filename, level, timeoffset 32 | audioplayer:insert(filename,(11-i)*0.1,i*0.6) 33 | end 34 | --show them 35 | for node in audioplayer:nodes() do 36 | print("node",node.sf) 37 | end 38 | 39 | --play them 7 secs 40 | audioplayer:start() 41 | sdl.Delay(7000); 42 | --close 43 | audioplayer:close() 44 | sdl.Quit(); 45 | 46 | -------------------------------------------------------------------------------- /test/audio/audiodrivers.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | local function ffistring(cd) 5 | if not cd then 6 | return nil 7 | else 8 | return ffi.string(cd) 9 | end 10 | end 11 | 12 | 13 | print"Audio drivers" 14 | for i = 0, sdl.GetNumAudioDrivers()-1 do 15 | local driver_name = ffistring(sdl.GetAudioDriver(i)); 16 | print(i,driver_name) 17 | sdl.setHint("SDL_AUDIO_DRIVER",driver_name) 18 | if (sdl.InitSubSystem(sdl.INIT_AUDIO)) then 19 | print("current audio driver",ffistring(sdl.getCurrentAudioDriver())) 20 | local num_devices = ffi.new("int[1]") 21 | local devices = sdl.GetAudioPlaybackDevices(num_devices); 22 | print("Audio playback devices count", num_devices[0]) 23 | if (devices) then 24 | for i = 0,num_devices[0]-1 do 25 | local instance_id = devices[i]; 26 | sdl.Log("AudioDevice %f: %s\n", instance_id, ffistring(sdl.GetAudioDeviceName(instance_id))); 27 | end 28 | sdl.free(devices); 29 | end 30 | local devices = sdl.GetAudioRecordingDevices(num_devices); 31 | print("Audio recording devices count", num_devices[0]) 32 | if (devices) then 33 | for i = 0,num_devices[0]-1 do 34 | local instance_id = devices[i]; 35 | sdl.Log("AudioDevice %f: %s\n", instance_id, ffistring(sdl.GetAudioDeviceName(instance_id))); 36 | end 37 | sdl.free(devices); 38 | end 39 | 40 | sdl.QuitSubSystem(sdl.INIT_AUDIO); 41 | end 42 | end 43 | 44 | 45 | sdl.Quit() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuaJIT-SDL3 2 | autogenerated LuaJIT bindings for SDL3 3 | 4 | Set CMake LUAJIT_BIN to the LuaJIT directory for installing. 5 | 6 | See test folder examples for usage 7 | 8 | luajit-async (taken from https://github.com/sonoro1234/luajit-async) needs to be installed for providing lua functions that can be called from another thread (sdl.MakeAudioCallback and sdl.MakeThreadFunc). 9 | 10 | This repo is used in https://github.com/sonoro1234/LuaJIT-ImGui where you get very usefull GUI widgets. 11 | 12 | All above can be found at https://github.com/sonoro1234/anima 13 | 14 | # sdlAudioPlayer 15 | 16 | simple interface for playing sndfile files from disk 17 | 18 | ```lua 19 | local sndf = require"LuaJIT-libsndfile" 20 | local AudioPlayer = require"sdlAudioPlayer" 21 | 22 | --copy specs from file 23 | local info = sndf.get_info(filename) 24 | local audioplayer,err = AudioPlayer({ 25 | --device = device_name, --nil as device 26 | freq = info.samplerate, 27 | format = sdl.AUDIO_S16SYS, 28 | channels = info.channels, 29 | samples = 1024}) 30 | 31 | --insert several files 32 | for i=1,10 do 33 | --filename, level, timeoffset 34 | audioplayer:insert(filename,(11-i)*0.1,i*0.6) 35 | end 36 | --show them 37 | for node in audioplayer:nodes() do 38 | print("node",node.sf) 39 | end 40 | 41 | --play them 7 secs 42 | audioplayer:start() 43 | sdl.Delay(7000); 44 | --close 45 | audioplayer:close() 46 | ``` 47 | -------------------------------------------------------------------------------- /test/threads/main_atomic.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | if not(sdl.init(sdl.INIT_VIDEO)) then 5 | 6 | print(string.format("Error: %s\n", sdl.getError())); 7 | return -1; 8 | end 9 | 10 | local function TestThread() 11 | local ffi = require"ffi" 12 | local sdl = require"sdl3_ffi" 13 | return function(ptr) 14 | local cnt; 15 | local atomic = ffi.cast("SDL_AtomicInt *",ptr) 16 | for i = 0,99 do 17 | sdl.delay(5); 18 | sdl.AddAtomicInt(atomic,1) 19 | local vv = sdl.GetAtomicInt(atomic) 20 | print(string.format("\nThread counter1: %d", vv)); 21 | cnt = i 22 | end 23 | return cnt; 24 | end 25 | end 26 | 27 | local function TestThread2() 28 | local ffi = require"ffi" 29 | local sdl = require"sdl3_ffi" 30 | return function(ptr) 31 | local cnt; 32 | local atomic = ffi.cast("SDL_AtomicInt *",ptr) 33 | for i = 0,99 do 34 | sdl.delay(4); 35 | sdl.AddAtomicInt(atomic,1) 36 | local vv = sdl.GetAtomicInt(atomic) 37 | print(string.format("\nThread counter2: %d", vv)); 38 | cnt = i 39 | end 40 | return cnt; 41 | end 42 | end 43 | 44 | 45 | local data = ffi.new("SDL_AtomicInt[1]") 46 | local threadReturnValue = ffi.new("int[1]") 47 | 48 | print("\nSimple SDL_CreateThread test:"); 49 | 50 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",data[0]) 51 | local thread2 = sdl.createThread(sdl.MakeThreadFunc(TestThread2), "TestThread2",data[0]) 52 | 53 | 54 | if (nil == thread or nil==thread2) then 55 | local err = sdl.getError() 56 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 57 | else 58 | sdl.waitThread(thread, threadReturnValue); 59 | sdl.waitThread(thread2, nil); 60 | print(string.format("\nThread returned value: %d", threadReturnValue[0]),sdl.GetAtomicInt(data),"should be 200"); 61 | end 62 | 63 | sdl.Quit() 64 | 65 | -------------------------------------------------------------------------------- /test/threads/main2.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | if not (sdl.init(sdl.INIT_VIDEO)) then 5 | 6 | print(string.format("Error: %s\n", sdl.getError())); 7 | return -1; 8 | end 9 | 10 | local function TestThread() 11 | local ffi = require"ffi" 12 | local sdl = require"sdl3_ffi" 13 | return function(ptr) 14 | local cnt; 15 | local st = ffi.cast("struct {int data[1];SDL_Mutex *mutex;}*",ptr) 16 | local data = st.data 17 | local mutex = st.mutex 18 | for i = 0,99 do 19 | sdl.delay(5); 20 | local ret = sdl.LockMutex(mutex) 21 | data[0] = data[0] + 1 22 | local vv = data[0] 23 | sdl.UnlockMutex(mutex) 24 | print(string.format("\nThread counter1: %d", vv)); 25 | cnt = i 26 | end 27 | return cnt; 28 | end 29 | end 30 | 31 | local function TestThread2() 32 | local ffi = require"ffi" 33 | local sdl = require"sdl3_ffi" 34 | return function(ptr) 35 | local cnt; 36 | local st = ffi.cast("struct {int data[1];SDL_Mutex *mutex;}*",ptr) 37 | local data = st.data 38 | local mutex = st.mutex 39 | for i = 0,99 do 40 | sdl.delay(4); 41 | local ret = sdl.LockMutex(mutex) 42 | data[0] = data[0] + 1 43 | local vv = data[0] 44 | sdl.UnlockMutex(mutex) 45 | print(string.format("\nThread counter2: %d", vv)); 46 | cnt = i 47 | 48 | end 49 | return cnt; 50 | end 51 | end 52 | 53 | 54 | local data = ffi.new("struct {int data[1];SDL_Mutex *mutex;}") 55 | data.mutex = sdl.createMutex() 56 | local threadReturnValue = ffi.new("int[1]") 57 | 58 | print("\nSimple SDL_CreateThread test:"); 59 | 60 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",data) 61 | local thread2 = sdl.createThread(sdl.MakeThreadFunc(TestThread2), "TestThread2",data) 62 | 63 | 64 | if (nil == thread or nil==thread2) then 65 | local err = sdl.getError() 66 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 67 | else 68 | sdl.waitThread(thread, threadReturnValue); 69 | sdl.waitThread(thread2, nil); 70 | print(string.format("\nThread returned value: %d", threadReturnValue[0]),data.data[0],"should be 200"); 71 | end 72 | 73 | sdl.DestroyMutex(data.mutex) 74 | sdl.Quit() 75 | 76 | -------------------------------------------------------------------------------- /test/audio/audiotest.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | local oldffistring = ffi.string 5 | ffi.string = function(data) 6 | if data == nil then 7 | return "nil" 8 | else 9 | return oldffistring(data) 10 | end 11 | end 12 | 13 | local sampleHz = 48000 14 | -- The audio callbak 15 | local function AudioInit(udatacode) 16 | local ffi = require"ffi" 17 | local sdl = require"sdl3_ffi" 18 | local sin = math.sin 19 | local min = math.min 20 | ffi.cdef(udatacode) 21 | local buflen = 1024 22 | local buf = ffi.new("float[?]",buflen) 23 | local flsize = ffi.sizeof"float" 24 | return function(ud,stream,len,totallen) 25 | local udc = ffi.cast("MyUdata*",ud) 26 | local lenf = len/flsize 27 | while lenf > 0 do 28 | local total = min(lenf,buflen) 29 | for i=0,total-2,2 do 30 | local sample = sin(udc.Phase)*0.05 31 | udc.Phase = udc.Phase + udc.dPhase 32 | buf[i] = sample 33 | buf[i+1] = sample 34 | end 35 | sdl.PutAudioStreamData(stream, buf, total * flsize); 36 | lenf = lenf - total 37 | end 38 | 39 | end 40 | end 41 | 42 | -- the specs 43 | local specs = ffi.new("SDL_AudioSpec[1]",{{sdl.AUDIO_F32,2,sampleHz}}) 44 | --local specs = ffi.new("SDL_AudioSpec[1]",{{sdl.AUDIO_F32,2,30}}) 45 | 46 | 47 | -- to change frequency from this thread 48 | local udatacode = [[typedef struct {double Phase;double dPhase;} MyUdata]] 49 | ffi.cdef(udatacode) 50 | local ud = ffi.new"MyUdata" 51 | local function setFreq(ff) 52 | --sdl.LockAudio() 53 | ud.dPhase = 2 * math.pi * ff / sampleHz 54 | --sdl.UnlockAudio() 55 | end 56 | 57 | 58 | if not (sdl.init(sdl.INIT_AUDIO)) then 59 | print(string.format("Error: %s\n", sdl.getError())); 60 | return -1; 61 | end 62 | 63 | sdl.Log("playing on default device") 64 | 65 | local stream = sdl.openAudioDeviceStream(sdl.AUDIO_DEVICE_DEFAULT_PLAYBACK, specs, sdl.MakeAudioCallback(AudioInit,udatacode), ud) 66 | 67 | if (stream == nil) then 68 | sdl.Log("Failed to open audio: %s", sdl.GetError()); 69 | else 70 | sdl.ResumeAudioDevice(sdl.GetAudioStreamDevice(stream)) -- start audio playing. 71 | for i=1,100 do 72 | setFreq(math.random()*500 + 100) 73 | sdl.Delay(100) 74 | end 75 | sdl.PauseAudioDevice(sdl.GetAudioStreamDevice(stream)) 76 | sdl.CloseAudioDevice(sdl.GetAudioStreamDevice(stream)); 77 | sdl.DestroyAudioStream(stream) 78 | end 79 | sdl.Log("done") 80 | sdl.Quit() -------------------------------------------------------------------------------- /test/audio/audiotest_nocallback.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | 4 | local oldffistring = ffi.string 5 | ffi.string = function(data) 6 | if data == nil then 7 | return "nil" 8 | else 9 | return oldffistring(data) 10 | end 11 | end 12 | 13 | local sampleHz = 48000 14 | 15 | -- the specs 16 | local specs = ffi.new("SDL_AudioSpec[1]",{{sdl.AUDIO_F32,2,sampleHz}}) 17 | --local specs = ffi.new("SDL_AudioSpec[1]",{{sdl.AUDIO_F32,2,30}}) 18 | 19 | 20 | -- to change frequency from this thread 21 | local udatacode = [[typedef struct {double Phase;double dPhase;} MyUdata]] 22 | ffi.cdef(udatacode) 23 | local ud = ffi.new"MyUdata" 24 | local function setFreq(ff) 25 | --sdl.LockAudio() 26 | ud.dPhase = 2 * math.pi * ff / sampleHz 27 | --sdl.UnlockAudio() 28 | end 29 | 30 | 31 | if not (sdl.init(sdl.INIT_AUDIO)) then 32 | print(string.format("Error: %s\n", sdl.getError())); 33 | return -1; 34 | end 35 | 36 | sdl.Log("playing on default device") 37 | 38 | local stream = sdl.openAudioDeviceStream(sdl.AUDIO_DEVICE_DEFAULT_PLAYBACK, specs, nil, nil) 39 | 40 | if (stream == nil) then 41 | sdl.Log("Failed to open audio: %s", sdl.GetError()); 42 | else 43 | sdl.ResumeAudioDevice(sdl.GetAudioStreamDevice(stream)) -- start audio playing. 44 | 45 | local lastTicks = 0 46 | local buflen = 1024 47 | local samples = ffi.new("float[?]",buflen) 48 | local sin = math.sin 49 | local minimum_audio = (sampleHz * ffi.sizeof"float" *2) *0.1; 50 | local total_samples_generated = 0 51 | local done = false; 52 | while (not done) do 53 | local event = ffi.new"SDL_Event" 54 | 55 | while (sdl.PollEvent(event)) do 56 | if (event.type == sdl.EVENT_QUIT) then 57 | done = true; 58 | end 59 | end 60 | --change freq every 100 milliseconds 61 | local ticks = sdl.GetTicks(); 62 | if ticks - lastTicks > 100 then 63 | setFreq(math.random()*500 + 100) 64 | lastTicks = ticks 65 | end 66 | 67 | if (sdl.GetAudioStreamAvailable(stream) < minimum_audio) then 68 | for i = 0,buflen-2,2 do 69 | local sample = sin(ud.Phase)*0.05 70 | ud.Phase = ud.Phase + ud.dPhase 71 | samples[i] = sample 72 | samples[i+1] = sample 73 | end 74 | sdl.PutAudioStreamData(stream, samples, buflen*ffi.sizeof"float")--ffi.sizeof(samples)); 75 | end 76 | end 77 | 78 | sdl.PauseAudioDevice(sdl.GetAudioStreamDevice(stream)) 79 | sdl.CloseAudioDevice(sdl.GetAudioStreamDevice(stream)); 80 | sdl.DestroyAudioStream(stream) 81 | end 82 | sdl.Log("done") 83 | sdl.Quit() -------------------------------------------------------------------------------- /test/audio/test-wav_async.lua: -------------------------------------------------------------------------------- 1 | -- mostly inspired by: 2 | -- http://www.gnurou.org/book/export/html/35 3 | -- example from https://github.com/torch/sdl2-ffi 4 | -- with async callback added 5 | 6 | local sdl = require 'sdl2_ffi' 7 | local ffi = require 'ffi' 8 | 9 | ffi.cdef[[ 10 | void *malloc(size_t size); 11 | void free(void *ptr); 12 | 13 | struct MySound { 14 | Uint8* data; 15 | Uint32 length; 16 | Uint32 pos; 17 | }; 18 | ]] 19 | 20 | local function mixaudio() 21 | local ffi = require"ffi" 22 | return function(userdata, stream, len) 23 | local sound = ffi.cast('struct {uint8_t* data;uint32_t length;uint32_t pos;}*', userdata) 24 | print(string.format("playing len=%d pos=%d\n", sound.length, sound.pos)) 25 | local tocopy = sound.length - sound.pos > len and len or sound.length - sound.pos 26 | ffi.copy(stream, sound.data + sound.pos, tocopy) 27 | sound.pos = sound.pos + tocopy 28 | end 29 | end 30 | 31 | 32 | if sdl.init(sdl.INIT_AUDIO) == -1 then 33 | error('could not initialize SDL') 34 | end 35 | 36 | for i=0,sdl.getNumAudioDrivers()-1 do 37 | print('driver:', ffi.string(sdl.getAudioDriver(i))) 38 | end 39 | 40 | local desired = ffi.new('SDL_AudioSpec') 41 | local obtained = ffi.new('SDL_AudioSpec') 42 | local soundfile = ffi.new('SDL_AudioSpec') 43 | 44 | local sound = ffi.new('struct MySound') 45 | 46 | desired.freq = 44100 47 | desired.format = sdl.AUDIO_U16 48 | desired.channels = 2 49 | desired.samples = 512 50 | desired.callback = sdl.MakeAudioCallback(mixaudio) 51 | desired.userdata = sound 52 | 53 | if sdl.openAudio(desired, obtained) ~= 0 then 54 | error(string.format('could not open audio device: %s', ffi.string(sdl.getError()))) 55 | end 56 | 57 | print(string.format('obtained parameters: format=%s, channels=%s freq=%s size=%s bytes', 58 | bit.band(obtained.format, 0xff), obtained.channels, obtained.freq, obtained.samples)) 59 | 60 | do 61 | local sounddata = ffi.new('Uint8*[1]') 62 | local soundlength = ffi.new('Uint32[1]') 63 | local ret = sdl.LoadWAV("arugh.wav", soundfile, sounddata, soundlength) 64 | print("ret",ret) 65 | if ret == nil then 66 | error(string.format('could not read audio file: %s', ffi.string(sdl.getError()))) 67 | end 68 | sound.data = sounddata[0] 69 | sound.length = soundlength[0] 70 | end 71 | 72 | local cvt = ffi.new('SDL_AudioCVT') 73 | if sdl.buildAudioCVT(cvt, soundfile.format, soundfile.channels, soundfile.freq, 74 | obtained.format, obtained.channels, obtained.freq) < 0 then 75 | error('could not build audio converter') 76 | end 77 | 78 | cvt.buf = ffi.C.malloc(sound.length * cvt.len_mult) 79 | cvt.len = sound.length 80 | ffi.copy(cvt.buf, sound.data, sound.length) 81 | 82 | if sdl.convertAudio(cvt) ~= 0 then 83 | error(string.format('problem during audio conversion: %s', sdl.getError())) 84 | end 85 | 86 | sdl.freeWAV(sound.data) 87 | sound.data = ffi.C.malloc(cvt.len_cvt) 88 | ffi.copy(sound.data, cvt.buf, cvt.len_cvt) 89 | ffi.C.free(cvt.buf) 90 | 91 | sound.length = cvt.len_cvt 92 | print(string.format('converted audio size: %s bytes', sound.length)) 93 | 94 | sdl.pauseAudio(0) 95 | 96 | jit.off() 97 | 98 | while sound.pos < sound.length do end 99 | 100 | jit.on() 101 | 102 | sdl.pauseAudio(1) 103 | 104 | sdl.closeAudio() 105 | 106 | sdl.quit() 107 | -------------------------------------------------------------------------------- /test/audio/sndfile_player.lua: -------------------------------------------------------------------------------- 1 | --[[/* 2 | Copyright (C) 1997-2018 Sam Lantinga 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely. 11 | */ 12 | 13 | /* Program to load a sndfile and playing it using SDL audio */ 14 | 15 | --]] 16 | 17 | local sdl = require 'sdl3_ffi' 18 | local ffi = require 'ffi' 19 | --https://github.com/sonoro1234/LuaJIT-libsndfile 20 | local sf = require"sndfile_ffi" 21 | 22 | 23 | --will run in a separate thread and lua state 24 | local function fillerup(spec) 25 | 26 | local ffi = require"ffi" 27 | local sdl = require"sdl3_ffi" 28 | local sndf = require"sndfile_ffi" 29 | local min = math.min 30 | local floor = math.floor 31 | 32 | local spec = ffi.cast("SDL_AudioSpec*",spec) 33 | 34 | local typebuffer,lenfac = sdl.audio_buffer_type(spec[0]) 35 | local flsize = 1/lenfac 36 | local bufpointer = typebuffer.."*" 37 | local readfunc = "readf_"..typebuffer 38 | print("Init audio:",spec[0].freq,bufpointer,readfunc) 39 | local buflen = 1024 40 | local buf = ffi.new(typebuffer.."[?]",buflen) 41 | return function(ud,stream,len,totlen) 42 | local sf = ffi.cast("SNDFILE_ref*",ud) 43 | local lenf = len*lenfac 44 | assert(lenf == floor(lenf)) 45 | while lenf > 0 do 46 | local total = min(lenf,buflen) 47 | sf[readfunc](sf,buf,total) 48 | sdl.PutAudioStreamData(stream, buf, total * flsize); 49 | lenf = lenf - total 50 | end 51 | end 52 | end 53 | 54 | 55 | local filename = "sample.wav"; 56 | 57 | --/* Enable standard application logging */ 58 | sdl.SetLogPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 59 | 60 | --/* Load the SDL library */ 61 | if not (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS)) then 62 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 63 | return (1); 64 | end 65 | 66 | 67 | local sf1 = sf.Sndfile(filename) 68 | local spec = ffi.new"SDL_AudioSpec[1]" 69 | spec[0].freq = sf1:samplerate(); 70 | spec[0].format = sdl.AUDIO_S16; --try others 71 | spec[0].channels = sf1:channels(); 72 | 73 | 74 | --/* Show the list of available drivers */ 75 | sdl.Log("Available audio drivers:"); 76 | for i = 0,sdl.GetNumAudioDrivers()-1 do 77 | sdl.Log("%i: %s",ffi.new("int", i), sdl.GetAudioDriver(i)); 78 | end 79 | 80 | sdl.Log("Using audio driver: %s\n", sdl.GetCurrentAudioDriver()); 81 | 82 | --/* Initialize fillerup() variables */ 83 | local stream = sdl.OpenAudioDeviceStream(sdl.AUDIO_DEVICE_DEFAULT_PLAYBACK, spec, sdl.MakeAudioCallback(fillerup,spec), sf1); 84 | if (stream==nil) then 85 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", sdl.GetError()); 86 | sf1:close() 87 | quit(2); 88 | end 89 | 90 | --/* Let the audio run */ 91 | sdl.ResumeAudioDevice(sdl.GetAudioStreamDevice(stream)) 92 | 93 | 94 | local done = false; 95 | while (not done) do 96 | local event = ffi.new"SDL_Event" 97 | 98 | while (sdl.PollEvent(event)) do 99 | if (event.type == sdl.EVENT_QUIT) then 100 | done = true; 101 | end 102 | end 103 | sdl.Delay(100); 104 | end 105 | 106 | 107 | --/* Clean up on signal */ 108 | sdl.CloseAudioDevice(sdl.GetAudioStreamDevice(stream)); 109 | sf1:close() 110 | sdl.Quit(); 111 | 112 | -------------------------------------------------------------------------------- /test/camera/read-and-draw.lua: -------------------------------------------------------------------------------- 1 | -- /* 2 | -- * This example code reads frames from a camera and draws it to the screen. 3 | -- * 4 | -- * This is a very simple approach that is often Good Enough. You can get 5 | -- * fancier with this: multiple cameras, front/back facing cameras on phones, 6 | -- * color spaces, choosing formats and framerates...this just requests 7 | -- * _anything_ and goes with what it is handed. 8 | -- * 9 | -- * This code is public domain. Feel free to use it for any purpose! 10 | -- */ 11 | 12 | local sdl = require"sdl3_ffi" 13 | local ffi = require"ffi" 14 | 15 | --/* We will use this renderer to draw into this window every frame. */ 16 | local window = ffi.new("SDL_Window*[1]") 17 | local renderer = ffi.new("SDL_Renderer*[1]") 18 | local camera = ffi.new("SDL_Camera*") 19 | local texture = ffi.new("SDL_Texture*") 20 | 21 | 22 | local devices = ffi.new("SDL_CameraID[1]") 23 | local devcount = ffi.new("int[1]",{0}) 24 | 25 | 26 | if (not sdl.Init(sdl.INIT_VIDEO + sdl.INIT_CAMERA)) then 27 | sdl.Log("Couldn't initialize SDL: %s", sdl.GetError()); 28 | assert(false) 29 | end 30 | 31 | if (not sdl.CreateWindowAndRenderer("examples/camera/read-and-draw", 640, 480, 0, window, renderer)) then 32 | SDL_Log("Couldn't create window/renderer: %s", sdl.GetError()); 33 | return 34 | end 35 | 36 | devices = sdl.GetCameras(devcount); 37 | if (devices == nil) then 38 | sdl.Log("Couldn't enumerate camera devices: %s", sdl.GetError()); 39 | return 40 | elseif (devcount[0] == 0) then 41 | sdl.Log("Couldn't find any camera devices! Please connect a camera and try again."); 42 | return 43 | end 44 | 45 | camera = sdl.OpenCamera(devices[0], NULL); --// just take the first thing we see in any format it wants. 46 | sdl.free(devices); 47 | if (camera == nil) then 48 | sdl.Log("Couldn't open camera: %s", sdl.GetError()); 49 | return 50 | end 51 | 52 | local done = false; 53 | while (not done) do 54 | local event = ffi.new"SDL_Event" 55 | while (sdl.PollEvent(event)) do 56 | if (event.type == sdl.EVENT_QUIT) then 57 | done = true; 58 | elseif (event.type == sdl.EVENT_CAMERA_DEVICE_APPROVED) then 59 | sdl.Log("Camera use approved by user!") 60 | elseif (event.type == sdl.EVENT_CAMERA_DEVICE_DENIED) then 61 | sdl.Log("Camera use denied by user!"); 62 | end 63 | end 64 | --sdl.Delay(100); 65 | local timestampNS = ffi.new("Uint64[1]",{0}) 66 | local frame = sdl.AcquireCameraFrame(camera, timestampNS); 67 | 68 | if (frame ~= nil) then 69 | -- if not sdl.FlipSurface(frame, sdl.FLIP_HORIZONTAL) then 70 | -- sdl.Log("Couldn't fliip frame: %s", sdl.GetError()); 71 | -- end 72 | 73 | --/* Some platforms (like Emscripten) don't know _what_ the camera offers 74 | -- until the user gives permission, so we build the texture and resize 75 | -- the window when we get a first frame from the camera. */ 76 | if (texture == nil ) then 77 | sdl.SetWindowSize(window[0], frame.w, frame.h); --/* Resize the window to match */ 78 | texture = sdl.CreateTexture(renderer[0], frame.format, sdl.TEXTUREACCESS_STREAMING, frame.w, frame.h); 79 | end 80 | 81 | if (texture ~= nil) then 82 | sdl.UpdateTexture(texture, nil, frame.pixels, frame.pitch); 83 | end 84 | 85 | sdl.ReleaseCameraFrame(camera, frame); 86 | end 87 | 88 | sdl.SetRenderDrawColor(renderer[0], 0x99, 0x99, 0x99, 255); 89 | sdl.RenderClear(renderer[0]); 90 | if (texture ~= nil) then --/* draw the latest camera frame, if available. */ 91 | sdl.RenderTexture(renderer[0], texture, NULL, NULL); 92 | end 93 | sdl.RenderPresent(renderer[0]); 94 | end 95 | 96 | sdl.CloseCamera(camera); 97 | sdl.DestroyTexture(texture); 98 | --/* SDL will clean up the window/renderer for us. */ 99 | 100 | 101 | -------------------------------------------------------------------------------- /test/audio/loopwave.lua: -------------------------------------------------------------------------------- 1 | --[[/* 2 | Copyright (C) 1997-2018 Sam Lantinga 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely. 11 | */ 12 | 13 | /* Program to load a wave file and loop playing it using SDL audio */ 14 | 15 | /* loopwaves.c is much more robust in handling WAVE files -- 16 | This is only for simple WAVEs 17 | */ 18 | --]] 19 | 20 | local sdl = require 'sdl3_ffi' 21 | local ffi = require 'ffi' 22 | 23 | 24 | local ud_code = [[ 25 | typedef struct 26 | { 27 | SDL_AudioSpec spec[1]; 28 | Uint8 *sound[1]; /* Pointer to wave data */ 29 | Uint32 soundlen[1]; /* Length of wave data */ 30 | int soundpos; /* Current play position */ 31 | } wave; 32 | ]] 33 | 34 | ffi.cdef(ud_code) 35 | local wave = ffi.new"wave" 36 | local stream; 37 | local device 38 | 39 | --/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ 40 | function quit(rc) 41 | sdl.Quit(); 42 | os.exit(rc); 43 | end 44 | 45 | function close_audio() 46 | if (device ~= 0) then 47 | sdl.CloseAudioDevice(device); 48 | device = 0; 49 | end 50 | end 51 | 52 | 53 | function open_audio() 54 | 55 | --/* Initialize fillerup() variables */ 56 | stream = sdl.OpenAudioDeviceStream(sdl.AUDIO_DEVICE_DEFAULT_PLAYBACK, wave.spec, nil, nil)--, sdl.MakeAudioCallback(fillerup,ud_code), wave); 57 | if (stream == nil) then 58 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", sdl.GetError()); 59 | sdl.FreeWAV(wave.sound); 60 | quit(2); 61 | end 62 | 63 | device = sdl.GetAudioStreamDevice(stream) 64 | --/* Let the audio run */ 65 | sdl.ResumeAudioDevice(sdl.GetAudioStreamDevice(stream)) 66 | end 67 | 68 | function reopen_audio() 69 | close_audio(); 70 | open_audio(); 71 | end 72 | 73 | 74 | 75 | local done = false; 76 | 77 | 78 | 79 | local filename = ffi.new"char[4096]"; 80 | 81 | --/* Enable standard application logging */ 82 | sdl.SetLogPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 83 | 84 | --/* Load the SDL library */ 85 | if not (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS)) then 86 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 87 | return (1); 88 | end 89 | 90 | sdl.strlcpy(filename, "sample.wav", ffi.sizeof(filename)); 91 | 92 | --/* Load the wave file into memory */ 93 | if (sdl.LoadWAV(filename, wave.spec, wave.sound, wave.soundlen) == nil) then 94 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, sdl.GetError()); 95 | quit(1); 96 | end 97 | 98 | --/* Show the list of available drivers */ 99 | sdl.Log("Available audio drivers:"); 100 | for i = 0,sdl.GetNumAudioDrivers()-1 do 101 | sdl.Log("%i: %s",ffi.new("int", i), sdl.GetAudioDriver(i)); 102 | end 103 | 104 | sdl.Log("Using audio driver: %s\n", sdl.GetCurrentAudioDriver()); 105 | 106 | open_audio(); 107 | 108 | sdl.FlushEvents(sdl.EVENT_AUDIO_DEVICE_ADDED, sdl.EVENT_AUDIO_DEVICE_REMOVED); 109 | 110 | 111 | while (not done) do 112 | local event = ffi.new"SDL_Event" 113 | 114 | while (sdl.PollEvent(event)) do 115 | if (event.type == sdl.EVENT_QUIT) then 116 | done = true; 117 | end 118 | if ((event.type == sdl.EVENT_AUDIO_DEVICE_ADDED and not event.adevice.recording==1) or 119 | (event.type == sdl.EVENT_AUDIO_DEVICE_REMOVED and not event.adevice.recording==1 and event.adevice.which == device)) then 120 | reopen_audio(); 121 | end 122 | end 123 | if (sdl.GetAudioStreamAvailable(stream) < wave.soundlen[0]) then 124 | -- feed more data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */ 125 | sdl.PutAudioStreamData(stream, wave.sound[0], wave.soundlen[0]); 126 | end 127 | sdl.Delay(100); 128 | end 129 | 130 | 131 | --/* Clean up on signal */ 132 | close_audio(); 133 | sdl.FreeWAV(wave.sound); 134 | sdl.Quit(); 135 | return (0); 136 | -------------------------------------------------------------------------------- /test/camera/read-process-draw.lua: -------------------------------------------------------------------------------- 1 | -- /* 2 | -- * This example code reads frames from a camera and draws it to the screen. 3 | -- * 4 | -- * This is a very simple approach that is often Good Enough. You can get 5 | -- * fancier with this: multiple cameras, front/back facing cameras on phones, 6 | -- * color spaces, choosing formats and framerates...this just requests 7 | -- * _anything_ and goes with what it is handed. 8 | -- * 9 | -- * This code is public domain. Feel free to use it for any purpose! 10 | -- */ 11 | 12 | local sdl = require"sdl3_ffi" 13 | local ffi = require"ffi" 14 | --local process_format = sdl.PIXELFORMAT_RGBA64_FLOAT 15 | local process_format = sdl.PIXELFORMAT_RGBA8888 16 | local process_texture 17 | 18 | local function print_format_details(format) 19 | print("format",ffi.string(sdl.GetPixelFormatName(format))) 20 | local format_details = sdl.GetPixelFormatDetails(format); 21 | print(format_details.format) 22 | print(format_details[0].bits_per_pixel, format_details[0].bytes_per_pixel) 23 | print(format_details.Rbits, format_details.Gbits, format_details.Bbits, format_details.Abits) 24 | print(format_details.Rmask, format_details.Gmask, format_details.Bmask, format_details.Amask) 25 | print(format_details.Rshift, format_details.Gshift, format_details.Bshift, format_details.Ashift) 26 | print"-------------------------------" 27 | end 28 | local function BlackAndWhite(surface) 29 | local surface_pixels = ffi.cast("Uint8*", surface.pixels) 30 | for y = 0, surface.h-1 do 31 | local pixels = ffi.cast("Uint32*",surface_pixels + (y * surface.pitch)) 32 | for x = 0,surface.w -1 do 33 | local p = ffi.cast("Uint8*",pixels + x) 34 | local average = (p[1] + p[2] + p[3]) / 3; 35 | p[1] = (average > 125) and 0xFF or 0x00; --/* make everything else either black or white. */ 36 | p[2] = p[1] 37 | p[3] = p[1] 38 | p[0] = 0xFF 39 | end 40 | end 41 | end 42 | 43 | --/* We will use this renderer to draw into this window every frame. */ 44 | local window = ffi.new("SDL_Window*[1]") 45 | local renderer = ffi.new("SDL_Renderer*[1]") 46 | local camera = ffi.new("SDL_Camera*") 47 | local texture = ffi.new("SDL_Texture*") 48 | 49 | 50 | local devices = ffi.new("SDL_CameraID[1]") 51 | local devcount = ffi.new("int[1]",{0}) 52 | 53 | 54 | if (not sdl.Init(sdl.INIT_VIDEO + sdl.INIT_CAMERA)) then 55 | sdl.Log("Couldn't initialize SDL: %s", sdl.GetError()); 56 | assert(false) 57 | end 58 | 59 | if (not sdl.CreateWindowAndRenderer("examples/camera/read-and-draw", 640, 480, 0, window, renderer)) then 60 | SDL_Log("Couldn't create window/renderer: %s", sdl.GetError()); 61 | return 62 | end 63 | 64 | devices = sdl.GetCameras(devcount); 65 | if (devices == nil) then 66 | sdl.Log("Couldn't enumerate camera devices: %s", sdl.GetError()); 67 | return 68 | elseif (devcount[0] == 0) then 69 | sdl.Log("Couldn't find any camera devices! Please connect a camera and try again."); 70 | return 71 | end 72 | 73 | camera = sdl.OpenCamera(devices[0], NULL); --// just take the first thing we see in any format it wants. 74 | sdl.free(devices); 75 | if (camera == nil) then 76 | sdl.Log("Couldn't open camera: %s", sdl.GetError()); 77 | return 78 | end 79 | 80 | local camspec = ffi.new("SDL_CameraSpec[1]") 81 | if sdl.GetCameraFormat(camera,camspec) then 82 | print(camspec[0].format, camspec[0].colorspace, camspec[0].width, camspec[0].height) 83 | print_format_details(camspec[0].format) 84 | print_format_details(sdl.PIXELFORMAT_RGBA64_FLOAT) 85 | print_format_details(sdl.PIXELFORMAT_RGBA128_FLOAT) 86 | print_format_details(sdl.PIXELFORMAT_RGBA8888) 87 | print(ffi.sizeof"float") 88 | print(ffi.sizeof"double") 89 | else 90 | sdl.Log("Couldn't get camera spec: %s", sdl.GetError()); 91 | end 92 | 93 | local done = false; 94 | while (not done) do 95 | local event = ffi.new"SDL_Event" 96 | while (sdl.PollEvent(event)) do 97 | if (event.type == sdl.EVENT_QUIT) then 98 | done = true; 99 | elseif (event.type == sdl.EVENT_CAMERA_DEVICE_APPROVED) then 100 | sdl.Log("Camera use approved by user!") 101 | elseif (event.type == sdl.EVENT_CAMERA_DEVICE_DENIED) then 102 | sdl.Log("Camera use denied by user!"); 103 | end 104 | end 105 | --sdl.Delay(100); 106 | local timestampNS = ffi.new("Uint64[1]",{0}) 107 | local frame = sdl.AcquireCameraFrame(camera, timestampNS); 108 | 109 | if (frame ~= nil) then 110 | 111 | local converted = sdl.ConvertSurface(frame, process_format); 112 | --black and white 113 | BlackAndWhite(converted) 114 | --/* Some platforms (like Emscripten) don't know _what_ the camera offers 115 | -- until the user gives permission, so we build the texture and resize 116 | -- the window when we get a first frame from the camera. */ 117 | if (texture == nil ) then 118 | sdl.SetWindowSize(window[0], frame.w, frame.h); --/* Resize the window to match */ 119 | texture = sdl.CreateTexture(renderer[0], process_format, sdl.TEXTUREACCESS_STREAMING, frame.w, frame.h); 120 | end 121 | 122 | if (texture ~= nil) then 123 | sdl.UpdateTexture(texture, nil, converted.pixels, converted.pitch); 124 | end 125 | 126 | sdl.ReleaseCameraFrame(camera, frame); 127 | sdl.DestroySurface(converted) 128 | end 129 | 130 | sdl.SetRenderDrawColor(renderer[0], 0x99, 0x99, 0x99, 255); 131 | sdl.RenderClear(renderer[0]); 132 | if (texture ~= nil) then --/* draw the latest camera frame, if available. */ 133 | sdl.RenderTexture(renderer[0], texture, NULL, NULL); 134 | end 135 | sdl.RenderPresent(renderer[0]); 136 | end 137 | 138 | sdl.CloseCamera(camera); 139 | sdl.DestroyTexture(texture); 140 | --/* SDL will clean up the window/renderer for us. */ -------------------------------------------------------------------------------- /test/audio/multi_sndfile_player_gui.lua: -------------------------------------------------------------------------------- 1 | --jit.off(true,true) 2 | 3 | local sdl = require 'sdl2_ffi' 4 | local ffi = require 'ffi' 5 | --https://github.com/sonoro1234/LuaJIT-libsndfile 6 | local sndf = require"sndfile_ffi" 7 | 8 | local AudioPlayer = require"sdlAudioPlayer" 9 | --to show pointer values 10 | ffi.cdef[[int snprintf ( char * s, size_t n, const char * format, ... ); 11 | int sprintf ( char * str, const char * format, ... ); 12 | ]] 13 | --------------------------will run in audio thread after playing files 14 | local delaycdef = [[typedef struct delay{double feedback[1];double delay[1];double maxdelay;} delay]] 15 | ffi.cdef(delaycdef) 16 | local fxdata = ffi.new("delay",{ffi.new("double[1]",0.0),ffi.new("double[1]",1),2}) 17 | 18 | local function delayfunc(data,code,typebuffer,nchannels,spec) 19 | local ffi = require"ffi" 20 | ffi.cdef(code) 21 | data = ffi.cast("delay*",data) 22 | local index = 0 23 | local lenb = math.floor(spec.freq*nchannels*data.maxdelay) 24 | local buffer = ffi.new(typebuffer.."[?]",lenb) 25 | 26 | return function(streamf,lenf,streamTime) 27 | local lenbe = math.floor(spec.freq*nchannels*data.delay[0]) 28 | local j 29 | for i=0,(lenf*nchannels)-1 do 30 | j = index + i 31 | if j > lenbe-1 then j = j - lenbe end 32 | streamf[i] = streamf[i] + buffer[j] 33 | buffer[j] = streamf[i] *data.feedback[0] 34 | end 35 | index = index + lenf*nchannels 36 | if index > lenbe-1 then index = index - lenbe end 37 | end 38 | end 39 | 40 | ------------------------------------------------------------------------ 41 | -----------------------main-------------------------------------- 42 | 43 | local ig = require"imgui.sdl" 44 | 45 | local filename = "african_roomS.wav"; 46 | --local filename = "arugh.wav" --"sample.wav"; 47 | 48 | --/* Enable standard application logging */ 49 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 50 | if (sdl.Init(sdl.INIT_VIDEO + sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 51 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 52 | return (1); 53 | end 54 | 55 | --copy specs from file 56 | local info = sndf.get_info(filename) 57 | local audioplayer,err = AudioPlayer({ 58 | --device = device_name, 59 | freq = info.samplerate, 60 | format = sdl.AUDIO_S16SYS, 61 | channels = info.channels, 62 | samples = 1024}, 63 | delayfunc,fxdata,delaycdef) 64 | 65 | if not audioplayer then print(err) end 66 | 67 | print"--------------wanted" 68 | audioplayer.wanted_spec[0]:print() 69 | print("---------------opened device",device) 70 | audioplayer.obtained_spec[0]:print() 71 | 72 | ---------------------------------------------------- 73 | 74 | --insert 3 files 75 | --level 1, timeoffset 0 76 | audioplayer:insert(filename,1,0.0) 77 | audioplayer:insert("arughSt.wav",1,0.01) 78 | --will not load, diferent samplerate and channels 79 | local node2 = audioplayer:insert("arughSt.wav",0,0.75) 80 | --audioplayer:insert("arughSt.wav",0.7,2.75) 81 | --audioplayer:insert("arughSt.wav",0,4.75) 82 | audioplayer:insert(filename,1,2) 83 | 84 | for node in audioplayer:nodes() do 85 | print("node",node.sf) 86 | end 87 | 88 | --audioplayer:erase(node2) 89 | print"after erase" 90 | for node in audioplayer:nodes() do 91 | print("node",node.sf) 92 | end 93 | 94 | --audioplayer:record("recording.wav",sndf.SF_FORMAT_WAV+sndf.SF_FORMAT_FLOAT) 95 | print("audioplayer.recordfile",audioplayer.recordfile) 96 | 97 | print"--------------------------------------" 98 | -------------------------------------------------- 99 | 100 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_FLAGS, sdl.GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 101 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE); 102 | sdl.gL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1); 103 | sdl.gL_SetAttribute(sdl.GL_DEPTH_SIZE, 24); 104 | sdl.gL_SetAttribute(sdl.GL_STENCIL_SIZE, 8); 105 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_MAJOR_VERSION, 3); 106 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_MINOR_VERSION, 2); 107 | 108 | local window = sdl.createWindow("ImGui SDL2+OpenGL3 example", sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, 700, 500, sdl.WINDOW_OPENGL+sdl.WINDOW_RESIZABLE); 109 | 110 | 111 | local gl_context = sdl.gL_CreateContext(window); 112 | sdl.gL_SetSwapInterval(1); -- Enable vsync 113 | 114 | local ig_Impl = ig.Imgui_Impl_SDL_opengl3() 115 | 116 | ig_Impl:Init(window, gl_context) 117 | 118 | local igio = ig.GetIO() 119 | 120 | local done = false; 121 | local streamtime = ffi.new("float[1]") 122 | while (not done) do 123 | 124 | local event = ffi.new"SDL_Event" 125 | while (sdl.pollEvent(event) ~=0) do 126 | ig.lib.ImGui_ImplSDL2_ProcessEvent(event); 127 | if (event.type == sdl.QUIT) then 128 | done = true; 129 | end 130 | if (event.type == sdl.WINDOWEVENT and event.window.event == sdl.WINDOWEVENT_CLOSE and event.window.windowID == sdl.getWindowID(window)) then 131 | done = true; 132 | end 133 | end 134 | 135 | sdl.gL_MakeCurrent(window, gl_context); 136 | 137 | 138 | ig_Impl:NewFrame() 139 | -------audio gui 140 | if ig.Button("start") then 141 | audioplayer:start() 142 | end 143 | if ig.Button("stop") then 144 | audioplayer:stop() 145 | end 146 | 147 | streamtime[0] = audioplayer:get_stream_time() 148 | --print(streamtime[0], audioplayer:get_stream_time()) 149 | if ig.SliderFloat("time",streamtime,0,15) then 150 | audioplayer:set_stream_time(streamtime[0]) 151 | end 152 | 153 | ig.SliderScalar("delay",ig.lib.ImGuiDataType_Double,fxdata.delay,ffi.new("double[1]",0),ffi.new("double[1]",fxdata.maxdelay)) 154 | 155 | ig.SliderScalar("feedback",ig.lib.ImGuiDataType_Double,fxdata.feedback,ffi.new("double[1]",0),ffi.new("double[1]",1)) 156 | 157 | if ig.Button("nodes") then 158 | print"----------nodes---------------" 159 | print(audioplayer.root,tonumber(audioplayer.root),audioplayer.root.next[0]) 160 | for node in audioplayer:nodes() do 161 | print(node,node.next[0],node.level,node.timeoffset) 162 | print(node.sf,node.sf:samplerate(),node.sf:channels(),node.sf:format()) 163 | end 164 | end 165 | 166 | if ig.Button("insert") then 167 | audioplayer:insert(filename,1,streamtime[0]) 168 | end 169 | ig.SameLine() 170 | if ig.Button("insert arugh") then 171 | audioplayer:insert("arughSt.wav",0.5,streamtime[0]) 172 | end 173 | if audioplayer.recordfile~=nil then 174 | ig.SameLine() 175 | if ig.Button("close record") then 176 | audioplayer.recordfile:close() 177 | end 178 | end 179 | 180 | ---[[ 181 | 182 | local format = string.format 183 | local cbuf = ffi.new"char[201]" 184 | local level = ffi.new("float[1]") 185 | ig.PushItemWidth(80) 186 | for node in audioplayer:nodes() do 187 | ig.PushID_Ptr(node) 188 | ffi.C.sprintf(cbuf,"%p",node) 189 | ig.Text(cbuf);ig.SameLine() 190 | level[0] = node.level 191 | if ig.SliderFloat("level",level,0,1) then 192 | node.level = level[0] 193 | end 194 | ig.SameLine();ig.Text(format("time:%4.2f",node.timeoffset)) 195 | ig.SameLine();ig.Text(format("srate:%4.0f",node.sf:samplerate()));ig.SameLine(); 196 | 197 | if ig.SmallButton("delete") then 198 | audioplayer:erase(node) 199 | end 200 | ig.PopID() 201 | end 202 | ig.PopItemWidth() 203 | 204 | --]] 205 | -- end audio gui 206 | ig_Impl:Render() 207 | sdl.gL_SwapWindow(window); 208 | end 209 | 210 | audioplayer:close() 211 | ig_Impl:destroy() 212 | 213 | sdl.gL_DeleteContext(gl_context); 214 | sdl.destroyWindow(window); 215 | sdl.Quit(); 216 | 217 | -------------------------------------------------------------------------------- /generator/generator.lua: -------------------------------------------------------------------------------- 1 | local ffi = require"ffi" 2 | local ffi_cdef = function(code) 3 | local ret,err = pcall(ffi.cdef,code) 4 | if not ret then 5 | local lineN = 1 6 | for line in code:gmatch("([^\n\r]*)\r?\n") do 7 | print(lineN, line) 8 | lineN = lineN + 1 9 | end 10 | print(err) 11 | error"bad cdef" 12 | end 13 | end 14 | 15 | local cp2c = require"cpp2ffi" 16 | ------------------------------------------------------ 17 | local cdefs = {} 18 | 19 | cp2c.save_data("./outheader.h",[[#include ]]) 20 | local pipe,err = io.popen([[gcc -E -dD -I ../SDL/include/ ./outheader.h]],"r") 21 | if not pipe then 22 | error("could not execute gcc "..err) 23 | end 24 | 25 | local defines = {} 26 | for line in cp2c.location(pipe,{[[SDL.-]]},defines) do 27 | --local line = strip(line) 28 | table.insert(cdefs,line) 29 | end 30 | pipe:close() 31 | os.remove"./outheader.h" 32 | 33 | 34 | local txt = table.concat(cdefs,"\n") 35 | --cp2c.save_data("./cpreout.txt",txt) 36 | 37 | local itemsarr,items = cp2c.parseItems(txt) 38 | 39 | print"items" 40 | for k,v in pairs(items) do 41 | print(k,#v) 42 | end 43 | 44 | --make new cdefs 45 | local cdefs = {} 46 | for k,v in ipairs(itemsarr) do 47 | -- if v.item:match"_Static_assert" then 48 | -- for kk,vv in pairs(v) do print(kk,vv) end 49 | -- break 50 | -- end 51 | if v.re_name ~= "functionD_re" then --skip defined funcs 52 | if v.re_name=="function_re" then 53 | --skip CreateThread and _Static_assert 54 | if not v.item:match("CreateThread") and not v.item:match"_Static_assert" then 55 | local item = v.item 56 | if item:match("^%s*extern") then 57 | item = item:gsub("^%s*extern%s*(.+)","\n%1") 58 | end 59 | table.insert(cdefs,item) 60 | end 61 | else 62 | table.insert(cdefs,v.item) 63 | end 64 | end 65 | end 66 | ------------------------------ 67 | local deftab = {"//defines"} 68 | local defstrtab = {} 69 | local ffi = require"ffi" 70 | ffi_cdef(table.concat(cdefs,"")) 71 | local wanted_strings = {"^SDL","^AUDIO_","^KMOD_","^RW_"} 72 | for i,v in ipairs(defines) do 73 | local wanted = false 74 | for _,wan in ipairs(wanted_strings) do 75 | if (v[1]):match(wan) then wanted=true; break end 76 | end 77 | if wanted then 78 | -- clear SDL_UINT64_C 79 | v[2] = v[2]:gsub("SDL_UINT64_C","") 80 | if v[2]:match([[^%b""]]) then 81 | defstrtab[v[1]]=v[2] --is string def 82 | else 83 | local lin = "static const int "..v[1].." = " .. v[2] .. ";" 84 | local ok,msg = pcall(function() return ffi.cdef(lin) end) 85 | if not ok then 86 | print("skipping def",lin) 87 | print(msg) 88 | else 89 | table.insert(deftab,lin) 90 | end 91 | end 92 | end 93 | end 94 | 95 | 96 | local special = [[ 97 | typedef unsigned long (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, 98 | unsigned (__stdcall *func)(void *), void *arg, 99 | unsigned, unsigned *threadID); 100 | typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); 101 | 102 | uintptr_t __cdecl _beginthreadex(void *_Security,unsigned _StackSize,unsigned (__stdcall *_StartAddress) (void *),void *_ArgList,unsigned _InitFlag,unsigned *_ThrdAddr); 103 | void __cdecl _endthreadex(unsigned _Retval); 104 | 105 | static const int SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_MASK; 106 | SDL_Thread * SDL_CreateThreadRuntime(SDL_ThreadFunction fn, const char *name, void *data,pfnSDL_CurrentBeginThread bf,pfnSDL_CurrentEndThread ef); 107 | SDL_Thread * SDL_CreateThreadWithStackSizeRuntime(int ( * fn) (void *),const char *name, const size_t stacksize, void *data,pfnSDL_CurrentBeginThread bf,pfnSDL_CurrentEndThread ef); 108 | ]] 109 | 110 | 111 | 112 | -----------make test 113 | local funcnames = {} 114 | --[[ 115 | for i,v in ipairs(items[function_re]) do 116 | local funcname = v:match("([%w_]+)%s*%(") 117 | if not funcname then print(v) end 118 | table.insert(funcnames,"if not pcall(function() local nn=M.C."..funcname.." end) then print('bad','"..funcname.."') end") 119 | end 120 | --]] 121 | 122 | local strdefT = {} 123 | cp2c.table_do_sorted(defstrtab, function(k,v) table.insert(strdefT,k.."="..v..",") end) 124 | local strdef = "local strdef = {"..table.concat(strdefT,"\n").."}" 125 | --output sdl3_ffi 126 | local sdlstr = strdef..[[ 127 | local ffi = require"ffi" 128 | 129 | --uncomment to debug cdef calls]].. 130 | "\n---[["..[[ 131 | 132 | --local ffi_cdef = ffi.cdef 133 | local ffi_cdef = function(code) 134 | local ret,err = pcall(ffi.cdef,code) 135 | if not ret then 136 | local lineN = 1 137 | for line in code:gmatch("([^\n\r]*)\r?\n") do 138 | print(lineN, line) 139 | lineN = lineN + 1 140 | end 141 | print(err) 142 | error"bad cdef" 143 | end 144 | end 145 | ]].."--]]"..[[ 146 | 147 | ffi_cdef]].."[["..table.concat(cdefs,"").."]]"..[[ 148 | 149 | ffi_cdef]].."[["..table.concat(deftab,"\n").."]]"..[[ 150 | 151 | 152 | ffi_cdef]].."[["..special.."]]"..[[ 153 | 154 | 155 | local lib = ffi.load"SDL3" 156 | 157 | local M = {C=lib,strdef=strdef} 158 | 159 | 160 | 161 | function M.createThread(a,b,c) 162 | return lib.SDL_CreateThreadRuntime(a,b,c,ffi.C._beginthreadex, ffi.C._endthreadex) 163 | end 164 | 165 | function M.createThreadWithStackSizeRuntime(a,b,c,d) 166 | return lib.SDL_CreateThreadWithStackSize(a,b,c,d,ffi.C._beginthreadex, ffi.C._endthreadex) 167 | end 168 | 169 | 170 | 171 | function M.LoadBMP(file) 172 | return M.LoadBMP_IO(M.IOFromFile(file, 'rb'), 1) 173 | end 174 | function M.LoadWAV(file, spec, audio_buf, audio_len) 175 | return M.LoadWAV_IO(M.IOFromFile(file, "rb"), 1, spec, audio_buf, audio_len) 176 | end 177 | function M.SaveBMP(surface, file) 178 | return M.SaveBMP_IO(surface, M.IOFromFile(file, 'wb'), 1) 179 | end 180 | 181 | local AudioSpecs = {} 182 | AudioSpecs.__index = AudioSpecs 183 | function AudioSpecs:print() 184 | print(string.format('spec parameters: \nfreq=%s, \nformat=%s, \nformat bits=%s, \nis float %s,\nendianess=%d, \nis signed %s, \nchannels=%s \nsilence=%s, \nsamples=%s bytes,\nsize=%s bytes', self.freq,self.format, bit.band(self.format, 0xff),tostring(bit.band(0x1000,self.format)>0), bit.band(0x100,self.format) , tostring(bit.band(0xF000,self.format)>0),self.channels, self.silence, self.samples, self.size)) 185 | end 186 | ffi.metatype("SDL_AudioSpec",AudioSpecs) 187 | 188 | --function returning typebuffer,lenfac,nchannels from spec 189 | function M.audio_buffer_type(spec) 190 | local nchannels = spec.channels 191 | local bitsize = bit.band(spec.format,0xff) 192 | local isfloat = bit.band(spec.format,0x100) 193 | local typebuffer 194 | if isfloat>0 then 195 | if bitsize == 32 then typebuffer = "float" 196 | else error("unknown float buffer type bits:"..tostring(bitsize)) end 197 | else 198 | if bitsize == 16 then typebuffer = "short" 199 | elseif bitsize == 32 then typebuffer = "int" 200 | else error("unknown buffer type bits:"..tostring(bitsize)) end 201 | end 202 | local lenfac = 1/(ffi.sizeof(typebuffer)*nchannels) 203 | return typebuffer,lenfac,nchannels 204 | end 205 | 206 | --typedef void ( *SDL_AudioStreamCallback)(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount); 207 | local callback_t 208 | local states_anchor = {} 209 | function M.MakeAudioCallback(func, ...) 210 | if not callback_t then 211 | local CallbackFactory = require "lj-async.callback" 212 | callback_t = CallbackFactory("void(*)(void*,void*,int,int)") --"SDL_AudioStreamCallback" 213 | end 214 | local cb = callback_t(func, ...) 215 | table.insert(states_anchor,cb) 216 | return cb:funcptr(), cb 217 | end 218 | local threadfunc_t 219 | function M.MakeThreadFunc(func, ...) 220 | if not threadfunc_t then 221 | local CallbackFactory = require "lj-async.callback" 222 | threadfunc_t = CallbackFactory("int(*)(void*)") 223 | end 224 | local cb = threadfunc_t(func, ...) 225 | table.insert(states_anchor,cb) 226 | return cb:funcptr(), cb 227 | end 228 | 229 | setmetatable(M,{ 230 | __index = function(t,k) 231 | local ok,ptr = pcall(function(str) return lib["SDL_"..str] end,k) 232 | if not ok then ok,ptr = pcall(function(str) return lib[str] end,k) end --some defines without SDL_ 233 | if not ok then 234 | ptr = strdef[k] 235 | ok = ptr and true or false 236 | end 237 | if not ok then --torch sdl2 calling 238 | local str2 = "SDL_"..string.upper(k:sub(1,1))..k:sub(2) 239 | ok,ptr = pcall(function(str) return lib[str] end,str2) 240 | end 241 | if not ok then error(k.." not found") end 242 | rawset(M, k, ptr) 243 | return ptr 244 | end 245 | }) 246 | 247 | 248 | ]]..table.concat(funcnames,"\n")..[[ 249 | 250 | return M 251 | ]] 252 | 253 | cp2c.save_data("./sdl3_ffi.lua",sdlstr) 254 | cp2c.copyfile("./sdl3_ffi.lua","../sdl3_ffi.lua") 255 | -------------------------------------------------------------------------------- /test/camera/camera_opengl.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl3_ffi" 2 | local ffi = require"ffi" 3 | local gllib = require"gl" 4 | gllib.set_loader(sdl) 5 | local gl, glc, glu, glext = gllib.libraries() 6 | 7 | local window, context 8 | function EQtriang(dummy,wi) 9 | gl.glPushMatrix() 10 | gl.glScaled(wi,wi,1) 11 | gl.glBegin(glc.GL_TRIANGLES) -- Drawing Using Triangles 12 | gl.glTexCoord2f(0.5, 1) gl.glVertex3f( 0.5, math.sqrt(3)*0.5, 0) -- Top 13 | gl.glTexCoord2f(0, 0) gl.glVertex3f(0, 0, 0) -- Bottom Left 14 | gl.glTexCoord2f(1, 0) gl.glVertex3f( 1, 0, 0) -- Bottom Right 15 | gl.glEnd() -- Finished Drawing The Triangle 16 | gl.glPopMatrix() 17 | end 18 | 19 | local heightfac = math.sqrt(3) * 0.5 -- ratio from height to side in equilateral triangle 20 | local function TeselR(fun,wi,lev) 21 | local fu = lev > 0 and TeselR or fun 22 | local w = wi*(2^lev) 23 | 24 | gl.glPushMatrix() 25 | fu(fun,wi,lev-1) 26 | gl.glPopMatrix() 27 | 28 | gl.glPushMatrix() 29 | gl.glTranslatef(1.5*w,heightfac*w,0) 30 | gl.glRotatef(120,0,0,1) 31 | fu(fun,wi,lev-1) 32 | 33 | gl.glPopMatrix() 34 | 35 | gl.glPushMatrix() 36 | --gl.LoadIdentity() -- Reset The Current Modelview Matrix 37 | gl.glTranslatef(1.5*w,heightfac*w,0) 38 | gl.glRotatef(-120,0,0,1) 39 | fu(fun,wi,lev-1) 40 | 41 | gl.glRotatef(180,1,0,0) 42 | fu(fun,wi,lev-1) 43 | 44 | gl.glPopMatrix() 45 | end 46 | 47 | local ww,hw 48 | local mouse_x = 0 49 | 50 | local function resize_cb(width, height) 51 | ww,hw = width, height 52 | sdl.gL_MakeCurrent(window,context); 53 | gl.glViewport(0, 0, width, height) 54 | 55 | gl.glMatrixMode(glc.GL_PROJECTION) -- Select The Projection Matrix 56 | gl.glLoadIdentity() -- Reset The Projection Matrix 57 | 58 | gl.glOrtho(0, width, height,0, 0.0, 500); 59 | gl.glMatrixMode(glc.GL_MODELVIEW) -- Select The Model View Matrix 60 | gl.glLoadIdentity() -- Reset The Model View Matrix 61 | end 62 | 63 | local function openCamera() 64 | local devices = ffi.new("SDL_CameraID[1]") 65 | local devcount = ffi.new("int[1]",{0}) 66 | local camera = ffi.new("SDL_Camera*") 67 | devices = sdl.GetCameras(devcount); 68 | if (devices == nil) then 69 | sdl.Log("Couldn't enumerate camera devices: %s", sdl.GetError()); 70 | return 71 | elseif (devcount[0] == 0) then 72 | sdl.Log("Couldn't find any camera devices! Please connect a camera and try again."); 73 | return 74 | end 75 | 76 | camera = sdl.OpenCamera(devices[0], NULL); --// just take the first thing we see in any format it wants. 77 | sdl.free(devices); 78 | if (camera == nil) then 79 | sdl.Log("Couldn't open camera: %s", sdl.GetError()); 80 | return 81 | end 82 | return camera 83 | end 84 | 85 | local function print_format_details(format) 86 | print("format",ffi.string(sdl.GetPixelFormatName(format))) 87 | local format_details = sdl.GetPixelFormatDetails(format); 88 | print(format_details.format) 89 | print(format_details[0].bits_per_pixel, format_details[0].bytes_per_pixel) 90 | print(format_details.Rbits, format_details.Gbits, format_details.Bbits, format_details.Abits) 91 | print(format_details.Rmask, format_details.Gmask, format_details.Bmask, format_details.Amask) 92 | print(format_details.Rshift, format_details.Gshift, format_details.Bshift, format_details.Ashift) 93 | print"-------------------------------" 94 | end 95 | local function formatToopengl(surface) 96 | local texture_format, converted 97 | -- Check that the image's width is a power of 2 98 | if( (bit.band(surface.w , (surface.w - 1))) ~= 0 ) then 99 | -- print("warning: image.bmp's width is not a power of 2\n"); 100 | end 101 | 102 | -- Also check if the height is a power of 2 103 | if( (bit.band(surface.h , (surface.h - 1))) ~= 0 ) then 104 | -- print("warning: image.bmp's height is not a power of 2\n"); 105 | end 106 | local format_details = sdl.GetPixelFormatDetails(surface.format); 107 | -- get the number of channels in the SDL surface 108 | local nOfColors = format_details.bytes_per_pixel; 109 | if( nOfColors == 4 ) then --// contains an alpha channel 110 | if(format_details.Rmask == 0x000000ff) then 111 | texture_format = glc.GL_RGBA; 112 | else 113 | texture_format = glc.GL_BGRA; 114 | end 115 | elseif( nOfColors == 3 ) then --// no alpha channel 116 | if(format_details.Rmask == 0x000000ff) then 117 | texture_format = glc.GL_RGB; 118 | else 119 | texture_format = glc.GL_BGR; 120 | end 121 | else 122 | --print("warning: the image is not truecolor.. this will probably break\n"); 123 | converted = sdl.ConvertSurface(surface, sdl.PIXELFORMAT_RGBA32); 124 | if converted == nil then 125 | print("conversion error:",sdl.GetError()) 126 | end 127 | texture_format = glc.GL_RGBA; 128 | end 129 | return texture_format, converted 130 | end 131 | 132 | local function updateTexture(texture, surf) 133 | gl.glBindTexture(glc.GL_TEXTURE_2D, texture[0]) 134 | gl.glTexParameteri(glc.GL_TEXTURE_2D,glc.GL_TEXTURE_MIN_FILTER,glc.GL_LINEAR) 135 | gl.glTexParameteri(glc.GL_TEXTURE_2D,glc.GL_TEXTURE_MAG_FILTER,glc.GL_LINEAR) 136 | gl.glPixelStorei(glc.GL_UNPACK_ALIGNMENT, 1) 137 | local surfor, conv = formatToopengl(surf) 138 | if conv ~= nil then 139 | gl.glTexImage2D(glc.GL_TEXTURE_2D,0, glc.GL_RGBA, conv.w, conv.h, 0, surfor, glc.GL_UNSIGNED_BYTE, conv.pixels) 140 | sdl.DestroySurface(conv) 141 | else 142 | gl.glTexImage2D(glc.GL_TEXTURE_2D,0, glc.GL_RGBA, surf.w, surf.h, 0, surfor, glc.GL_UNSIGNED_BYTE, surf.pixels) 143 | end 144 | end 145 | ------------------- 146 | if (not sdl.Init(sdl.INIT_VIDEO + sdl.INIT_CAMERA)) then 147 | sdl.Log("Couldn't initialize SDL: %s", sdl.GetError()); 148 | assert(false) 149 | end 150 | 151 | window = sdl.CreateWindow("examples/camera/read-and-draw", 640, 480, sdl.WINDOW_OPENGL + sdl.WINDOW_RESIZABLE) 152 | if (window == nil ) then 153 | SDL_Log("Couldn't create window/renderer: %s", sdl.GetError()); 154 | return 155 | end 156 | context = sdl.GL_CreateContext(window); 157 | if (context == nil) then 158 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "SDL_GL_CreateContext(): %s\n", sdl.GetError()); 159 | return 160 | end 161 | ------ gl info 162 | sdl.Log("\n"); 163 | sdl.Log("Vendor : %s\n", gl.glGetString(glc.GL_VENDOR)); 164 | sdl.Log("Renderer : %s\n", gl.glGetString(glc.GL_RENDERER)); 165 | sdl.Log("Version : %s\n", gl.glGetString(glc.GL_VERSION)); 166 | --sdl.Log("Extensions : %s\n", gl.glGetString(glc.GL_EXTENSIONS)); 167 | sdl.Log("\n"); 168 | 169 | ---------load image 170 | sdl.gL_MakeCurrent(window,context); 171 | gl.glEnable(glc.GL_TEXTURE_2D) 172 | 173 | local texture = ffi.new("GLuint[?]",1) 174 | gl.glGenTextures(1, texture) -- Create The Texture 175 | 176 | -- local image = sdl.LoadBMP("flower.bmp") 177 | -- assert(image~=nil) 178 | -- print("image",image.w, image.h,image.pixels) 179 | -- print_format_details(image.format) 180 | -- updateTexture(texture, image) 181 | -- sdl.DestroySurface(image) 182 | 183 | camera = openCamera() 184 | ------------------ 185 | local dw,dh = ffi.new("int[1]"), ffi.new("int[1]") 186 | sdl.GetWindowSize(window, dw, dh); 187 | resize_cb(dw[0],dh[0]) 188 | local running = true 189 | local event = ffi.new('SDL_Event') 190 | while running do 191 | while sdl.pollEvent(event) do 192 | if event.type == sdl.EVENT_QUIT then 193 | running = false 194 | end 195 | if event.type == sdl.EVENT_WINDOW_RESIZED then 196 | resize_cb(event.window.data1, event.window.data2) 197 | end 198 | if event.type == sdl.EVENT_MOUSE_MOTION then 199 | mouse_x = event.motion.x 200 | end 201 | end 202 | 203 | local timestampNS = ffi.new("Uint64[1]",{0}) 204 | local frame = sdl.AcquireCameraFrame(camera, timestampNS); 205 | 206 | local w,h = ww,hw 207 | local endwide = h*2/math.sqrt(3) + w 208 | local cellwide = math.max(10,mouse_x) 209 | local iters = math.floor(math.log(endwide/cellwide)/math.log(2)) 210 | 211 | sdl.gL_MakeCurrent(window,context); 212 | 213 | if (frame ~= nil) then 214 | sdl.SetWindowSize(window, frame.w, frame.h); --/* Resize the window to match */ 215 | updateTexture(texture, frame); 216 | sdl.ReleaseCameraFrame(camera, frame); 217 | end 218 | 219 | 220 | gl.glClearColor(0.0, 0.0, 0.0, 1.0); 221 | gl.glClear(glc.GL_COLOR_BUFFER_BIT)-- | GL_DEPTH_BUFFER_BIT); 222 | gl.glBindTexture(glc.GL_TEXTURE_2D, texture[0]) 223 | 224 | gl.glLoadIdentity() -- Reset The Current Modelview Matrix 225 | gl.glTranslatef(-0.5*(endwide -w),0,-40) 226 | 227 | TeselR(EQtriang,cellwide,iters) 228 | 229 | sdl.gL_SwapWindow(window); 230 | end 231 | 232 | sdl.destroyWindow(window) 233 | sdl.quit() 234 | -------------------------------------------------------------------------------- /generator/cpp2ffi.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local function table_do_sorted(t,f) 4 | local sorted = {} 5 | for k,v in pairs(t) do 6 | table.insert(sorted,k) 7 | end 8 | table.sort(sorted) 9 | for ii,k in ipairs(sorted) do 10 | f(k,t[k]) 11 | end 12 | end 13 | M.table_do_sorted = table_do_sorted 14 | 15 | local function str_split(str, pat) 16 | local t = {} 17 | local fpat = "(.-)" .. pat 18 | local last_end = 1 19 | local s, e, cap = str:find(fpat, 1) 20 | while s do 21 | table.insert(t,cap) 22 | last_end = e+1 23 | s, e, cap = str:find(fpat, last_end) 24 | end 25 | if last_end <= #str then 26 | cap = str:sub(last_end) 27 | table.insert(t, cap) 28 | elseif str:sub(-1)==pat then 29 | table.insert(t, "") 30 | end 31 | return t 32 | end 33 | local function split_comment(line) 34 | local comment = line:match("(%s*//.*)") or "" 35 | line = line:gsub("%s*//.*","") 36 | line = line:gsub("%s*$","") 37 | return line,comment 38 | end 39 | M.split_comment = split_comment 40 | local function strip(cad) 41 | return cad:gsub("^%s*(.-)%s*$","%1") --remove initial and final spaces 42 | end 43 | local function strip_end(cad) 44 | return cad:gsub("^(.-)%s*$","%1") --remove initial and final spaces 45 | end 46 | local function clean_spaces(cad) 47 | cad = strip(cad) 48 | cad = cad:gsub("%s+"," ") --not more than one space 49 | cad = cad:gsub("%s*([%(%),=])%s*","%1") --not spaces with ( , ) 50 | return cad 51 | end 52 | function strsplit(str, pat) 53 | local t = {} 54 | local fpat = "(.-)" .. pat 55 | local last_end = 1 56 | local s, e, cap = str:find(fpat, 1) 57 | while s do 58 | table.insert(t,cap) 59 | last_end = e+1 60 | s, e, cap = str:find(fpat, last_end) 61 | end 62 | if last_end <= #str then 63 | cap = str:sub(last_end) 64 | table.insert(t, cap) 65 | elseif str:sub(-1)==pat then 66 | table.insert(t, "") 67 | end 68 | return t 69 | end 70 | -------------------------------------------------------------------------- 71 | local function save_data(filename,...) 72 | local file,err = io.open(filename,"w") 73 | if not file then error(err) end 74 | for i=1, select('#', ...) do 75 | local data = select(i, ...) 76 | file:write(data) 77 | end 78 | file:close() 79 | end 80 | M.save_data = save_data 81 | ---------------------------------------- 82 | local function read_data(filename) 83 | local hfile,err = io.open(filename,"r") 84 | if not hfile then error(err) end 85 | local hstrfile = hfile:read"*a" 86 | hfile:close() 87 | return hstrfile 88 | end 89 | M.read_data = read_data 90 | ------------------------------------------------------------ 91 | local function copyfile(src,dst,blocksize) 92 | blocksize = blocksize or 1024*4 93 | print( "copyfile", src, dst) 94 | local srcf, err = io.open(src,"rb") 95 | if not srcf then error(err) end 96 | local dstf, err = io.open(dst,"wb") 97 | if not dstf then error(err) end 98 | while true do 99 | local data = srcf:read(blocksize) 100 | if not data then break end 101 | dstf:write(data) 102 | end 103 | srcf:close() 104 | dstf:close() 105 | end 106 | M.copyfile = copyfile 107 | 108 | --gives the re table 109 | local function getRE() 110 | local res = { 111 | function_re = "^([^;{}]+%b()[\n%s]*;)%s*", 112 | function_re = "^([^;{}]+%b()[\n%s%w]*;)", --const at the end 113 | struct_re = "^([^;{}]-struct[^;{}]-%b{}[%s%w_%(%)]*;)", 114 | enum_re = "^([^;{}]-enum[^;{}]-%b{}[%s%w_%(%)]*;)", 115 | union_re = "^([^;{}]-union[^;{}]-%b{}[%s%w_%(%)]*;)", 116 | structenum_re = "^([^;{}]-%b{}[%s%w_%(%)]*;)", 117 | namespace_re = "^([^;{}]-namespace[^;{}]-%b{})", 118 | class_re = "^([^;{}]-class[^;{}]-%b{}%s*;)", 119 | typedef_re = "^\n*(typedef[^;]+;)", 120 | functypedef_re = "^\n*%s*(typedef[%w%s%*_]+%(%s*%*%s*[%w_]+%s*%)%s*%b()%s*;)", 121 | functypedef_re = "^\n*%s*(typedef[%w%s%*_]+%([^*]*%*%s*[%w_]+%s*%)%s*%b()%s*;)", 122 | vardef_re = "^\n*([^;{}%(%)]+;)", 123 | functionD_re = "^([^;{}]-%b()[\n%s%w]*%b{})", 124 | --functionD_re = "^([^;{}]-%b()[^{}%(%)]*%b{})", 125 | functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)%s*;" 126 | } 127 | 128 | local resN = {"functypedef_re","functype_re","function_re","functionD_re","struct_re","enum_re","union_re","namespace_re","class_re","typedef_re","vardef_re"} 129 | 130 | return res,resN 131 | end 132 | M.getRE = getRE 133 | --takes preprocesed file in table cdefsor and returns items 134 | local function parseItems(txt,dumpit) 135 | --dumpit = true 136 | local res,resN = getRE() 137 | 138 | local ini = 1 139 | local item 140 | local items = {} 141 | local itemarr = {} 142 | while true do 143 | local found = false 144 | for ire,re_name in ipairs(resN) do 145 | local re = res[re_name] 146 | local i,e = txt:find(re,ini) 147 | if i then 148 | item = txt:sub(i,e) 149 | --if re~=functionD_re then --skip defined functions 150 | item = item:gsub("extern __attribute__%(%(dllexport%)%) ","") 151 | table.insert(itemarr,{re_name=re_name,item=item}) 152 | --end 153 | items[re_name] = items[re_name] or {} 154 | table.insert(items[re_name],item) 155 | found = true 156 | ini = e + 1 157 | if dumpit then 158 | print(item) 159 | print(ire,"------------------------------------------------------") 160 | end 161 | break 162 | end 163 | end 164 | if not found then 165 | if not (ini >= #txt) then 166 | local rest = txt:sub(ini) 167 | local onlyspaces = rest:match("^%s*$") 168 | if not onlyspaces then 169 | print(ini,#txt); 170 | print(txt); 171 | print(item) 172 | error"parseItems error" 173 | end 174 | end 175 | break 176 | end 177 | end 178 | return itemarr,items 179 | end 180 | M.parseItems = parseItems 181 | local function name_overloadsAlgo(v) 182 | local aa = {} 183 | local bb = {} 184 | local done = {} 185 | local maxnum = 0 186 | for i,t in ipairs(v) do 187 | bb[i] = "" 188 | local signature = t.signature:sub(2,-2) -- without parenthesis 189 | aa[i] = {} 190 | local num = 1 191 | --for typec in t.signature:gmatch(".-([^,%(%s]+)[,%)]") do 192 | --for typec in t.signature:gmatch(".-([^,%(]+)[,%)]") do 193 | --for typec in signature:gmatch(".-([^,]+),?") do 194 | for typec in signature:gsub("(%(.-%))", function(x) return x:gsub(",","\0") end):gmatch(".-([^,]+),?") do 195 | --typec = typec:gsub 196 | aa[i][num] = typec:gsub("%z+", ",") 197 | num = num + 1 198 | end 199 | num = num - 1 200 | maxnum = (num > maxnum) and num or maxnum 201 | end 202 | 203 | for l=1,maxnum do 204 | local keys = {} 205 | local diferent = true 206 | local equal = true 207 | for i=1,#v do 208 | aa[i][l] = aa[i][l] or "nil" 209 | keys[aa[i][l]] = 1 + (aa[i][l] and keys[aa[i][l]] or 0) 210 | if not done[i] then 211 | for j=i+1,#v do 212 | if not done[j] then 213 | if aa[i][l] == aa[j][l] then 214 | diferent = false 215 | else 216 | equal = false 217 | end 218 | end 219 | end 220 | end 221 | end 222 | if not equal then -- not all the same 223 | for i=1,#v do 224 | if not done[i] then 225 | bb[i] = bb[i]..(aa[i][l]=="nil" and "" or aa[i][l]) 226 | if keys[aa[i][l]] == 1 then 227 | done[i] = true 228 | end 229 | end 230 | end 231 | end 232 | end 233 | return aa,bb 234 | end 235 | local function typetoStr(typ) 236 | --typ = typ:gsub("[^%(%)]+%(%*?(.+)%).+","%1") -- funcs 237 | typ = typ:gsub("[^%(%)]+%(%*?(.+)%).+","FnPtr") 238 | typ = typ:gsub("[%w_]+%[(%d*)%]","arr%1") 239 | typ = typ:gsub("%*","Ptr") 240 | typ = typ:gsub("void","") 241 | typ = typ:gsub("unsigned%s","u") 242 | typ = typ:gsub("const%s","")--"c") 243 | typ = typ:gsub("%s+","_") 244 | typ = typ:gsub("charPtr","Str") 245 | typ = typ:gsub("int","Int") 246 | typ = typ:gsub("bool","Bool") 247 | typ = typ:gsub("float","Float") 248 | typ = typ:gsub("uInt","Uint") 249 | typ = typ:gsub("ImGui","") 250 | typ = typ:gsub("Im","") 251 | typ = typ:gsub("[<>]","") 252 | return typ 253 | end 254 | local function parseFunction(self,stname,lineorig,namespace) 255 | line = clean_spaces(lineorig) 256 | --move * 257 | line = line:gsub("%s*%*","%*") 258 | line = line:gsub("%*([%w_])","%* %1") 259 | line = line:gsub("(%(%*)%s","%1") 260 | 261 | --print(line) 262 | --clean implemetation 263 | line = line:gsub("%s*%b{}","") 264 | --clean attribute 265 | line = line:gsub("%s*__attribute__%b()","") 266 | --clean static and inline and mutable 267 | line = line:gsub("static","") 268 | line = line:gsub("inline","") 269 | line = line:gsub("mutable","") 270 | --skip operator 271 | if line:match("operator") then return end 272 | --skip template 273 | if line:match("template") then return end 274 | 275 | local ret = line:match("([^%(%)]+[%*%s])%s?~?[_%w]+%b()") 276 | local funcname, args = line:match("(~?[_%w]+)%s*(%b())") 277 | 278 | if not args then 279 | print"not gettint args in" 280 | print(line,lineorig) 281 | print(funcname,"args",args) 282 | end 283 | 284 | local argscsinpars = args:gsub("(=[^,%(%)]*)(%b())","%1") 285 | argscsinpars = argscsinpars:gsub("(=[^,%(%)]*)([,%)])","%2") 286 | -- if argscsinpars:match("&") then 287 | -- for arg in argscsinpars:gmatch("[%(,]*([^,%(%)]+)[%),]") do 288 | -- if arg:match("&") and not arg:match("const") then 289 | -- print(funcname,argscsinpars) 290 | -- end 291 | -- end 292 | -- end 293 | --argscsinpars = argscsinpars:gsub("&","") 294 | 295 | --for _,ttype in ipairs(self.templatedTypes) do 296 | --local template = argscsinpars:match(ttype.."%s*<(.+)>") 297 | local ttype,template = argscsinpars:match("([^%s,%(%)]+)%s*<(.+)>") 298 | local te="" 299 | if template then 300 | te = template:gsub("%s","_") 301 | te = te:gsub("%*","Ptr") 302 | self.templates[ttype] = self.templates[ttype] or {} 303 | self.templates[ttype][template] = te 304 | end 305 | --end 306 | argscsinpars = argscsinpars:gsub("<([%w_%*]+)>","_"..te) --ImVector 307 | 308 | local argsArr = {} 309 | local functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)" 310 | local functype_reex = "^(%s*[%w%s%*]+)%(%*([%w_]+)%)(%([^%(%)]*%))" 311 | local functype_arg_rest = "^(%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)),*(.*)" 312 | local rest = argscsinpars:sub(2,-2) --strip () 313 | 314 | while true do 315 | --local tt = strsplit(rest,",") 316 | --for ii,arg in ipairs(tt) do 317 | --for arg in argscsinpars:gmatch("[%(,]*([^,%(%)]+)[%),]") do 318 | if rest == "void" then break end 319 | local type,name,retf,sigf 320 | local arg,restt = rest:match(functype_arg_rest) 321 | if arg then 322 | local t1,namef,t2 = arg:match(functype_reex) 323 | type=t1.."(*)"..t2;name=namef 324 | retf = t1 325 | sigf = t2 326 | rest = restt 327 | else 328 | arg,restt = rest:match(",*([^,%(%)]+),*(.*)") 329 | if not arg then break end 330 | rest = restt 331 | if arg:match("&") and arg:match("const") then 332 | arg = arg:gsub("&","") 333 | end 334 | if arg:match("%.%.%.") then 335 | type="...";name="..." 336 | else 337 | type,name = arg:match("(.+)%s([^%s]+)") 338 | end 339 | 340 | if not type or not name then 341 | print("failure arg detection",funcname,type,name,argscsinpars,arg) 342 | 343 | else 344 | if name:match"%*" then print("**",funcname) end 345 | --float name[2] to float[2] name 346 | local siz = name:match("(%[%d*%])") 347 | if siz then 348 | type = type..siz 349 | name = name:gsub("(%[%d*%])","") 350 | end 351 | end 352 | end 353 | table.insert(argsArr,{type=type,name=name,ret=retf,signature=sigf}) 354 | if arg:match("&") and not arg:match("const") then 355 | --only post error if not manual 356 | local cname = self.getCname(stname,funcname) --cimguiname 357 | if not self.manuals[cname] then 358 | print("reference to no const arg in",funcname,argscsinpars) 359 | end 360 | end 361 | end 362 | argscsinpars = argscsinpars:gsub("&","") 363 | 364 | local signature = argscsinpars:gsub("([%w%s%*_]+)%s[%w_]+%s*([,%)])","%1%2") 365 | signature = signature:gsub("%s*([,%)])","%1") --space before , and ) 366 | signature = signature:gsub(",%s*",",")--space after , 367 | signature = signature:gsub("([%w_]+)%s[%w_]+(%[%d*%])","%1%2") -- float[2] 368 | signature = signature:gsub("(%(%*)[%w_]+(%)%([^%(%)]*%))","%1%2") --func defs 369 | 370 | local call_args = argscsinpars:gsub("([%w_]+%s[%w_]+)%[%d*%]","%1") --float[2] 371 | call_args = call_args:gsub("%(%*([%w_]+)%)%([^%(%)]*%)"," %1") --func type 372 | call_args = call_args:gsub("[^%(].-([%w_]+)%s*([,%)])","%1%2") 373 | 374 | if not ret and stname then --must be constructors 375 | if not (stname == funcname or "~"..stname==funcname) then --break end 376 | print("false constructor:",line); 377 | print("b2:",ret,stname,funcname,args) 378 | return --are function defs 379 | end 380 | end 381 | 382 | local cimguiname = self.getCname(stname,funcname) 383 | table.insert(self.funcdefs,{stname=stname,funcname=funcname,args=args,argsc=argscsinpars,signature=signature,cimguiname=cimguiname,call_args=call_args,ret =ret,comment=comment}) 384 | local defsT = self.defsT 385 | defsT[cimguiname] = defsT[cimguiname] or {} 386 | table.insert(defsT[cimguiname],{}) 387 | local defT = defsT[cimguiname][#defsT[cimguiname]] 388 | defT.defaults = {} 389 | --for k,def in args:gmatch("([%w%s%*_]+)=([%w_%(%)%s,%*]+)[,%)]") do 390 | --for k,def in args:gmatch("([%w_]+)=([%w_%(%)%s,%*%.%-]+)[,%)]") do 391 | for k,def in args:gmatch('([%w_]+)=([%w_%(%)%s,%*%.%-%+%%"]+)[,%)]') do 392 | defT.defaults[k]=def 393 | end 394 | defT.namespace = namespace 395 | defT.cimguiname = cimguiname 396 | defT.stname = stname 397 | defT.funcname = funcname 398 | defT.argsoriginal = args 399 | defT.args=argscsinpars 400 | defT.signature = signature 401 | defT.call_args = call_args 402 | defT.isvararg = signature:match("%.%.%.%)$") 403 | defT.location = locat 404 | defT.comment = "" --comment 405 | defT.argsT = argsArr 406 | if self.get_manuals(defT) then 407 | defT.manual = true 408 | end 409 | if ret then 410 | defT.ret = clean_spaces(ret:gsub("&","*")) 411 | defT.retref = ret:match("&") 412 | -- if defT.ret=="ImVec2" or defT.ret=="ImVec4" or defT.ret=="ImColor" then 413 | -- defT.ret = defT.ret.."_Simple" 414 | -- end 415 | end 416 | defsT[cimguiname][signature] = defT 417 | end 418 | local function itemsCount(items) 419 | print"------------items" 420 | local res,resN = getRE() 421 | for k,v in pairs(resN) do 422 | local its = items[v] 423 | print(v,its and #its or 0) 424 | end 425 | end 426 | 427 | local function AdjustArguments(FP) 428 | for fun,defs in pairs(FP.defsT) do 429 | --struct function but no constructors 430 | if defs[1].stname~="" and defs[1].ret then 431 | --print("adjusting",fun) 432 | for i,def in ipairs(defs) do 433 | local empty = def.args:match("^%(%)") --no args 434 | --local ptret = def.retref and "&" or "" 435 | def.args = def.args:gsub("^%(","("..def.stname.."* self"..(empty and "" or ",")) 436 | table.insert(def.argsT,1,{type=def.stname.."*",name="self"}) 437 | end 438 | end 439 | end 440 | end 441 | local function ADDnonUDT(FP) 442 | local defsT = FP.defsT 443 | local newcdefs = {} 444 | --for cimguiname,defs in pairs(defsT) do 445 | --for i,defT in ipairs(defs) do 446 | --local t = {cimguiname=cimguiname,signature=defT.signature,ret=defT.ret} 447 | for numcdef,t in ipairs(FP.funcdefs) do 448 | if t.cimguiname then 449 | local cimf = defsT[t.cimguiname] 450 | local defT = cimf[t.signature] 451 | --if UDT return generate nonUDT version 452 | local isUDT = false 453 | for _,udt_ret in ipairs(FP.UDTs) do 454 | if udt_ret == defT.ret then isUDT=true;break end 455 | end 456 | --if defT.ret=="ImVec2" or defT.ret=="ImVec4" or defT.ret=="ImColor" then 457 | if isUDT then 458 | --passing as a pointer arg 459 | local defT2 = {} 460 | --first strings 461 | for k,v in pairs(defT) do 462 | defT2[k] = v 463 | end 464 | --then argsT table 465 | defT2.argsT = {{type=defT.ret.."*",name="pOut"}} 466 | for k,v in ipairs(defT.argsT) do 467 | table.insert(defT2.argsT,{type=v.type,name=v.name}) 468 | end 469 | local comma = (#defT.argsT > 0) and "," or "" 470 | defT2.args = "("..defT.ret.." *pOut"..comma..defT.args:sub(2) 471 | defT2.ret = "void" 472 | defT2.ov_cimguiname = (defT2.ov_cimguiname or defT2.cimguiname).."_nonUDT" 473 | defT2.nonUDT = 1 474 | defT2.retref = nil 475 | defsT[t.cimguiname][#defsT[t.cimguiname] + 1] = defT2 476 | defsT[t.cimguiname][t.signature.."nonUDT"] = defT2 477 | table.insert(newcdefs,{stname=t.stname,funcname=t.funcname,args=args,argsc=argscsinpars,signature=t.signature.."nonUDT",cimguiname=t.cimguiname,call_args=call_args,ret =t.ret,comment=comment}) 478 | --converting to Simple type---------------------------------------------------- 479 | local defT3 = {} 480 | --first strings 481 | for k,v in pairs(defT) do 482 | defT3[k] = v 483 | end 484 | --then argsT table 485 | defT3.argsT = {} 486 | for k,v in ipairs(defT.argsT) do 487 | table.insert(defT3.argsT,{type=v.type,name=v.name}) 488 | end 489 | local comma = (#defT.argsT > 0) and "," or "" 490 | --defT3.args = "("..defT.ret.." *pOut"..comma..defT.args:sub(2) 491 | defT3.ret = defT.ret.."_Simple" 492 | defT3.retorig = defT.ret 493 | defT3.ov_cimguiname = (defT3.ov_cimguiname or defT3.cimguiname).."_nonUDT2" 494 | defT3.nonUDT = 2 495 | defT3.retref = nil 496 | defsT[t.cimguiname][#defsT[t.cimguiname] + 1] = defT3 497 | defsT[t.cimguiname][t.signature.."nonUDT2"] = defT3 498 | table.insert(newcdefs,{stname=t.stname,funcname=t.funcname,args=args,argsc=argscsinpars,signature=t.signature.."nonUDT2",cimguiname=t.cimguiname,call_args=call_args,ret =t.ret,comment=comment}) 499 | end 500 | end 501 | --end 502 | end 503 | for i,v in ipairs(newcdefs) do 504 | table.insert(FP.funcdefs,v) 505 | end 506 | end 507 | 508 | local function ADDdestructors(FP) 509 | local defsT = FP.defsT 510 | local newcdefs = {} 511 | --TODO add constructor = true 512 | for numcdef,t in ipairs(FP.funcdefs) do 513 | newcdefs[#newcdefs+1] = t 514 | if t.cimguiname then 515 | local defT = defsT[t.cimguiname] 516 | --local defT = cimf[t.signature] 517 | --for fname,defT in pairs(FP.defsT) do 518 | if not defT[1].ret and not defT[1].constructor then --if constructor not processed 519 | if defT[1].funcname:match("~") then 520 | defsT[t.cimguiname] = nil --clear destructor 521 | newcdefs[#newcdefs] = nil 522 | else 523 | for j,cons in ipairs(defT) do 524 | cons.constructor = true 525 | end 526 | assert(defT[1].stname==defT[1].funcname) 527 | local def = {} 528 | def.stname = defT[1].stname 529 | def.ret = "void" 530 | def.ov_cimguiname = def.stname.."_destroy" 531 | def.cimguiname = def.ov_cimguiname 532 | def.destructor = true 533 | def.args = "("..def.stname.."* self)" 534 | def.call_args = "(self)" 535 | def.signature = "("..def.stname.."*)" 536 | def.defaults = {} 537 | def.argsT = {{type=def.stname.."*",name="self"}} 538 | defsT[def.ov_cimguiname] = {def} 539 | defsT[def.ov_cimguiname][def.signature] = def 540 | newcdefs[#newcdefs+1]={stname=def.stname,funcname=def.ov_cimguiname,args=def.args,signature=def.signature,cimguiname=def.cimguiname,call_args=def.call_args,ret =def.ret} 541 | end 542 | end 543 | end 544 | end 545 | FP.funcdefs = newcdefs 546 | end 547 | 548 | --only basic ending 549 | local c_types = { 550 | ["char"]=true, 551 | ["int"]=true, 552 | ["float"]=true, 553 | ["double"]=true, 554 | ["short"]=true, 555 | ["long"]=true, 556 | ["signed"]=true, 557 | ["unsigned"]=true, 558 | ["size_t"]=true, 559 | ["ptrdiff_t"]=true, 560 | } 561 | local function check_arg_detection(fdefs,typedefs) 562 | print"-----------------check arg detection---------------------------" 563 | for k,defT in pairs(fdefs) do 564 | for i,def in ipairs(defT) do 565 | for j,arg in ipairs(def.argsT) do 566 | --check name is not type, which happens in declaration without name 567 | if arg.name=="*" or not arg.type or not arg.name or c_types[arg.name] or typedefs[arg.name] then 568 | print("bad argument name",arg.name, "in",def.funcname,def.args) 569 | end 570 | end 571 | end 572 | end 573 | print"-----------------end check arg detection-----------------------" 574 | end 575 | local function printItems(items) 576 | local function printItemsKind(items,kind) 577 | local res,resN = getRE() 578 | print("---------------------------------------",kind) 579 | local someit = items[kind] 580 | if someit then 581 | for i=1,#someit do print(someit[i]) end 582 | end 583 | end 584 | local res,resN = getRE() 585 | for k,v in pairs(resN) do 586 | printItemsKind(items,v) 587 | end 588 | end 589 | ------------- 590 | function M.Parser() 591 | local par = {} 592 | local cdefs ={} 593 | local itemsarr={} 594 | local items = {} 595 | par.itemsarr = itemsarr 596 | par.items = items 597 | par.defsT = {} 598 | par.funcdefs = {} 599 | par.embeded_structs = {} 600 | par.inerstructs = {} 601 | par.templates = {} 602 | par.typedefs_dict = {} 603 | par.cname_overloads = {} 604 | par.manuals = {} 605 | par.UDTs = {} 606 | 607 | function par:insert(line) 608 | table.insert(cdefs,line) 609 | end 610 | function par.getCname(stname,funcname) 611 | if #stname == 0 then return funcname end --top level 612 | local pre = stname.."_" 613 | return pre..funcname 614 | end 615 | function par.getCname_overload(stname,funcname,signature) 616 | local cname = par.getCname(stname,funcname) 617 | local ov_cname = par.cname_overloads[cname] and par.cname_overloads[cname][signature] --or cname 618 | return ov_cname 619 | end 620 | function par.get_manuals(def) 621 | return par.manuals[def.ov_cimguiname] or par.manuals[def.cimguiname] 622 | end 623 | function par:do_parse() 624 | self:parseItems() 625 | self:parseFunctions() 626 | self:compute_overloads() 627 | end 628 | function par:parseItems() 629 | --typedefs dictionary 630 | for i,line in ipairs(cdefs) do 631 | if line:match("typedef") then 632 | line = clean_spaces(line) 633 | local value,key = line:match("typedef%s+(.+)%s+([%w_]+);") 634 | if key and value then 635 | self.typedefs_dict[key] = value 636 | else --try function typedef 637 | local key = line:match("%(%*([%w_]+)%)%([^%(%)]*%)") 638 | if key then 639 | local linet = line 640 | linet = linet:gsub("typedef ","") 641 | linet = linet:gsub("%(%*("..key..")%)","(*)") 642 | self.typedefs_dict[key] = linet 643 | else 644 | print("not found function typedef") 645 | print(key,value,line) 646 | end 647 | end 648 | end 649 | end 650 | local txt = table.concat(cdefs,"\n") 651 | itemsarr,items = parseItems(txt) 652 | self.itemsarr , self.items = itemsarr,items 653 | end 654 | function par:printItems() 655 | printItems(items) 656 | end 657 | par.parseFunction = parseFunction 658 | 659 | function par:parseFunctions() 660 | for i,it in ipairs(itemsarr) do 661 | if it.re_name == "function_re" or it.re_name == "functionD_re" then 662 | self:parseFunction("",it.item) 663 | elseif it.re_name == "namespace_re" then 664 | local nsp = it.item:match("%b{}"):sub(2,-2) 665 | local namespace = it.item:match("namespace%s+(%S+)") 666 | local nspparr,itemsnsp = parseItems(nsp) 667 | for insp,itnsp in ipairs(nspparr) do 668 | if itnsp.re_name == "function_re" or itnsp.re_name == "functionD_re" then 669 | self:parseFunction("",itnsp.item,namespace) 670 | end 671 | end 672 | elseif it.re_name == "struct_re" then 673 | local nsp = it.item:match("%b{}"):sub(2,-2) 674 | local stname = it.item:match("struct%s+(%S+)") 675 | local nspparr,itemsnsp = parseItems(nsp) 676 | for insp,itnsp in ipairs(nspparr) do 677 | if itnsp.re_name == "function_re" or itnsp.re_name == "functionD_re" then 678 | self:parseFunction(stname,itnsp.item) 679 | elseif itnsp.re_name == "struct_re" then 680 | --get embeded_structs 681 | local embededst = itnsp.item:match("struct%s+(%S+)") 682 | self.embeded_structs[embededst] = stname.."::"..embededst 683 | local nsp2 = strip_end(itnsp.item:match("%b{}"):sub(2,-2)) 684 | local itemsemarr,itemsem = parseItems(nsp2) 685 | assert(not itemsem.struct_re,"two level embed struct") 686 | for iemb,itemb in ipairs(itemsemarr) do 687 | if itemb.re_name == "function_re" or itemb.re_name == "functionD_re" then 688 | self:parseFunction(embededst,itemb.item) 689 | end 690 | end 691 | end 692 | end 693 | end 694 | end 695 | --require"anima" 696 | --prtable(self.defsT) 697 | end 698 | function par:clean_struct(stru) 699 | local outtab = {} 700 | local iner = strip_end(stru:match("%b{}"):sub(2,-2)) 701 | local stname = stru:match("struct%s*(%S+)%s*%b{}") 702 | if not stname then 703 | print(stru) 704 | error"could not get stname" 705 | end 706 | --initial 707 | table.insert(outtab,stru:match("(.-)%b{}")) 708 | table.insert(outtab,"{") 709 | local itlist,itemsin = parseItems(iner) 710 | if #itlist == 0 then return "" end --here we avoid empty structs 711 | for j,it in ipairs(itlist) do 712 | if it.re_name == "vardef_re" or it.re_name == "functype_re" or it.re_name == "union_re" then 713 | local it2 = it.item --:gsub("<([%w_]+)>","_%1") --templates 714 | --local ttype,template = it.item:match("([^%s,%(%)]+)%s*<(.+)>") 715 | local ttype,template = it.item:match"([^%s,%(%)]+)%s*<(.+)>" 716 | if template then 717 | local te = template:gsub("%s","_") 718 | te = te:gsub("%*","Ptr") 719 | self.templates[ttype] = self.templates[ttype] or {} 720 | self.templates[ttype][template] = te 721 | it2 = it2:gsub("<([%w_%*%s]+)>","_"..te) 722 | end 723 | --clean mutable 724 | it2 = it2:gsub("mutable","") 725 | --skip static variables 726 | if not (it.re_name == "vardef_re" and it2:match"static") then 727 | table.insert(outtab,it2) 728 | end 729 | elseif it.re_name == "struct_re" then 730 | table.insert(self.inerstructs,it) 731 | end 732 | end 733 | --final 734 | table.insert(outtab,"\n};") 735 | return table.concat(outtab,""),stname,outtab 736 | end 737 | function par:gen_structs_and_enums() 738 | local outtab = {} 739 | local outtabpre = {} 740 | local typedefs_table = {} 741 | 742 | --first typedefs 743 | for i,it in ipairs(itemsarr) do 744 | if it.re_name == "typedef_re" or it.re_name == "functypedef_re" or it.re_name == "vardef_re" then 745 | table.insert(outtabpre,it.item) 746 | -- add typedef after struct name 747 | if it.re_name == "vardef_re" and it.item:match"struct" then 748 | local stname = it.item:match("struct%s*(%S+)%s*;") 749 | table.insert(typedefs_table,"typedef struct "..stname.." "..stname..";\n") 750 | self.typedefs_dict[stname]="struct "..stname 751 | end 752 | end 753 | end 754 | --then structs and enums 755 | for i,it in ipairs(itemsarr) do 756 | if it.re_name == "enum_re" then 757 | table.insert(outtab,it.item) 758 | elseif it.re_name == "struct_re" then 759 | local cleanst,structname = self:clean_struct(it.item) 760 | if structname then 761 | table.insert(outtab,cleanst) 762 | table.insert(typedefs_table,"typedef struct "..structname.." "..structname..";\n") 763 | self.typedefs_dict[structname]="struct "..structname 764 | end 765 | end 766 | end 767 | --inner_structs 768 | for i,it in ipairs(self.inerstructs) do 769 | local cleanst,structname = self:clean_struct(it.item) 770 | if structname then 771 | table.insert(outtab,cleanst) 772 | table.insert(typedefs_table,"typedef struct "..structname.." "..structname..";\n") 773 | self.typedefs_dict[structname]="struct "..structname 774 | end 775 | end 776 | local uniques = {} 777 | for i,l in ipairs(typedefs_table) do 778 | if not uniques[l] then 779 | uniques[l] = true 780 | table.insert(outtabpre,1,l) 781 | end 782 | end 783 | --check arg detection failure if no name in function declaration 784 | check_arg_detection(self.defsT,self.typedefs_dict) 785 | return table.concat(outtabpre,""),table.concat(outtab,"") 786 | end 787 | ----------- 788 | function par:parse_struct_line(line,outtab) 789 | local functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)" 790 | local functype_reex = "^(%s*[%w%s%*]+%(%*)([%w_]+)(%)%([^%(%)]*%))" 791 | line = clean_spaces(line) 792 | if line:match(functype_re) then 793 | local t1,name,t2 = line:match(functype_reex) 794 | table.insert(outtab,{type=t1..t2,name=name}) 795 | else 796 | --split type name1,name2; in several lines 797 | local typen,rest = line:match("%s*([^,]+)%s(%S+[,;])") 798 | --local template_type = typen:match("/%*<(.+)>%*/") 799 | --if template_type then typen = typen:match("(.+)/%*") end 800 | local template_type 801 | for k,v in pairs(self.templates) do 802 | template_type = typen:match(k.."_(.+)") 803 | if template_type then break end 804 | end 805 | if template_type then 806 | template_type = template_type:gsub("_"," ") 807 | template_type = template_type:gsub("Ptr","%*") 808 | end 809 | for name in rest:gmatch("([^%s,;]+)%s?[,;]") do 810 | table.insert(outtab,{type=typen,template_type=template_type,name=name}) 811 | end 812 | end 813 | end 814 | function par:gen_structs_and_enums_table() 815 | local outtab = {enums={},structs={}} 816 | local outtabpre = {} 817 | local typedefs_table = {} 818 | 819 | self.inerstructs = {} 820 | --then structs and enums 821 | for i,it in ipairs(itemsarr) do 822 | if it.re_name == "enum_re" then 823 | local enumname = it.item:match"^%s*enum%s+([^%s;{}]+)" 824 | outtab.enums[enumname] = {} 825 | local inner = strip_end(it.item:match("%b{}"):sub(2,-2)) 826 | local enumarr = str_split(inner,",") 827 | for j,line in ipairs(enumarr) do 828 | local name,value = line:match("%s*([%w_]+)%s*=%s*([^,]+)") 829 | if value then 830 | table.insert(outtab.enums[enumname],{name=name,value=value}) 831 | else --increment by one 832 | local name = line:match("%s*([^,]+)") 833 | local enum_table = outtab.enums[enumname] 834 | local value = enum_table[#enum_table] and (enum_table[#enum_table].value + 1) or 0 835 | table.insert(outtab.enums[enumname],{name=name,value=value}) 836 | end 837 | end 838 | elseif it.re_name == "struct_re" then 839 | local cleanst,structname,strtab = self:clean_struct(it.item) 840 | if structname then --not empty struc 841 | outtab.structs[structname] = {} 842 | for j=3,#strtab-1 do 843 | self:parse_struct_line(strtab[j],outtab.structs[structname]) 844 | end 845 | end 846 | end 847 | end 848 | --inner_structs 849 | 850 | for i,it in ipairs(self.inerstructs) do 851 | local cleanst,structname,strtab = self:clean_struct(it.item) 852 | if structname then --not empty struc 853 | outtab.structs[structname] = {} 854 | for j=3,#strtab-1 do 855 | self:parse_struct_line(strtab[j],outtab.structs[structname]) 856 | end 857 | end 858 | end 859 | --[[ 860 | local uniques = {} 861 | for i,l in ipairs(typedefs_table) do 862 | if not uniques[l] then 863 | uniques[l] = true 864 | table.insert(outtabpre,1,l) 865 | end 866 | end 867 | --]] 868 | --calcule size of name[16+1] [xxx_COUNT] 869 | local allenums = {} 870 | --first calc_value in enums 871 | for enumname,enum in pairs(outtab.enums) do 872 | for i,t in ipairs(enum) do 873 | local val = tonumber(t.value) 874 | if val then 875 | t.calc_value = val 876 | elseif t.value:match"<<" then 877 | local v1,v2 = t.value:match("(%d+)%s*<<%s*(%d+)") 878 | t.calc_value = bit.lshift(v1,v2) 879 | elseif t.value:match"|" then --or several enums 880 | local ens = t.value 881 | ens = strsplit(ens,"|") 882 | for i,v in ipairs(ens) do ens[i] = allenums[clean_spaces(v)] end 883 | t.calc_value = bit.bor(unpack(ens)) 884 | elseif allenums[t.value] then 885 | t.calc_value = allenums[t.value] 886 | else 887 | print("Error unknown value in enums",t.value) 888 | end 889 | assert(t.calc_value) 890 | allenums[t.name] = t.calc_value 891 | end 892 | end 893 | --then calcsize in struct members 894 | for stname,struct in pairs(outtab.structs) do 895 | for i,t in ipairs(struct) do 896 | local val = t.name:match"%[([^%[%]]+)%]" 897 | if val then 898 | if tonumber(val) then 899 | t.size = tonumber(val) 900 | elseif allenums[val] then 901 | t.size = allenums[val] 902 | elseif val:match"%+" then 903 | local s1,s2 = val:match("(%d+)%s*%+%s*(%d+)") 904 | t.size = s1+s2 905 | else 906 | print("Error size is",val) 907 | end 908 | assert(t.size) 909 | end 910 | end 911 | end 912 | return outtab 913 | end 914 | par.alltypes = {} 915 | local function get_types(v) 916 | for i,t in ipairs(v) do 917 | local signature = t.signature:sub(2,-2) -- without parenthesis 918 | for typec in signature:gsub("(%(.-%))", function(x) return x:gsub(",","\0") end):gmatch(".-([^,]+),?") do 919 | local key = typec:gsub("%z+", ",") 920 | par.alltypes[key] = true 921 | end 922 | end 923 | end 924 | function par:dump_alltypes() 925 | for k,v in pairs(self.alltypes) do print(k, typetoStr(k) ) end 926 | end 927 | function par:compute_overloads() 928 | local strt = {} 929 | local numoverloaded = 0 930 | self.alltypes = {} 931 | table.insert(strt,"----------------overloadings---------------------------") 932 | --require"anima.utils" 933 | for k,v in pairs(self.defsT) do 934 | get_types(v) 935 | if #v > 1 then 936 | numoverloaded = numoverloaded + #v 937 | --print(k,#v) 938 | table.insert(strt,string.format("%s\t%d",k,#v)) 939 | local typesc,post = name_overloadsAlgo(v) 940 | for i,t in ipairs(v) do 941 | --take overloaded name from manual table or algorythm 942 | t.ov_cimguiname = self.getCname_overload(t.stname,t.funcname,t.signature) or k..typetoStr(post[i]) 943 | table.insert(strt,string.format("%d\t%s\t%s %s",i,t.ret,t.ov_cimguiname,t.signature)) 944 | --prtable(typesc[i]) 945 | end 946 | --check not two names are equal (produced by bad cimguiname_overload) 947 | for i=1,#v-1 do 948 | for j=i+1,#v-1 do 949 | if v[i].ov_cimguiname == v[j].ov_cimguiname then 950 | local t,tj = v[i],v[j] 951 | print("Error caused by Bad overloading "..t.ov_cimguiname.." of function ",t.funcname,t.signature,"conflicts with ",tj.funcname,tj.signature) 952 | error("Bad overloading:"..t.ov_cimguiname) 953 | end 954 | end 955 | end 956 | else 957 | v[1].ov_cimguiname = v[1].cimguiname 958 | end 959 | end 960 | --print(numoverloaded, "overloaded") 961 | table.insert(strt,string.format("%d overloaded",numoverloaded)) 962 | AdjustArguments(self) 963 | ADDnonUDT(self) 964 | ADDdestructors(self) 965 | self.overloadstxt = table.concat(strt,"\n") 966 | end 967 | return par 968 | end 969 | 970 | ------serializeTable("anyname",table) gives a string that recreates the table with dofile(generated_string) 971 | local function serializeTable(name, value, saved) 972 | 973 | local function basicSerialize (o) 974 | if type(o) == "number" or type(o)=="boolean" then 975 | return tostring(o) 976 | elseif type(o) == "string" then 977 | return string.format("%q", o) 978 | else 979 | return "nil" 980 | end 981 | end 982 | 983 | local string_table = {} 984 | if not saved then 985 | table.insert(string_table, "local "..name.." = ") 986 | else 987 | table.insert(string_table, name.." = ") 988 | end 989 | 990 | saved = saved or {} -- initial value 991 | 992 | if type(value) == "number" or type(value) == "string" or type(value)=="boolean" then 993 | table.insert(string_table,basicSerialize(value).."\n") 994 | elseif type(value) == "table" then 995 | if saved[value] then -- value already saved? 996 | table.insert(string_table,saved[value].."\n") 997 | else 998 | saved[value] = name -- save name for next time 999 | table.insert(string_table, "{}\n") 1000 | ---[[ 1001 | local ordered_keys = {} 1002 | for k,v in pairs(value) do 1003 | table.insert(ordered_keys,k) 1004 | end 1005 | local function sorter(a,b) 1006 | if type(a)==type(b) then 1007 | return a