diff --git a/vlib/builtin/cfns.v b/vlib/builtin/cfns.v
index e2203a4db1..9597520fde 100644
--- a/vlib/builtin/cfns.v
+++ b/vlib/builtin/cfns.v
@@ -110,7 +110,7 @@ fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, p
fn C.RegCloseKey()
fn C.RegQueryValueEx() voidptr
fn C.RemoveDirectory() int
-fn C.GetStdHandle() int
+fn C.GetStdHandle() voidptr
fn C.SetConsoleMode()
fn C.GetConsoleMode() int
fn C._putws()
@@ -131,7 +131,7 @@ fn C._putenv() int
fn C._waccess() int
fn C._wremove()
fn C.ReadConsole()
-fn C.fgetws() int
+fn C.fgetws() voidptr
fn C.GetModuleFileName() int
fn C._wchdir()
fn C._wgetcwd() int
diff --git a/vlib/os/os.v b/vlib/os/os.v
index 280923f054..c1abee636e 100644
--- a/vlib/os/os.v
+++ b/vlib/os/os.v
@@ -838,7 +838,7 @@ pub fn is_dir(path string) bool {
$if windows {
_path := path.replace('/', '\\')
attr := C.GetFileAttributesW(_path.to_wide())
- if int(attr) == C.INVALID_FILE_ATTRIBUTES {
+ if attr == u32(C.INVALID_FILE_ATTRIBUTES) {
return false
}
if int(attr) & C.FILE_ATTRIBUTE_DIRECTORY != 0 {
diff --git a/vlib/sdl/LICENSE b/vlib/sdl/LICENSE
new file mode 100644
index 0000000000..6ece64cce4
--- /dev/null
+++ b/vlib/sdl/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Nicolas Sauzede
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vlib/sdl/README.md b/vlib/sdl/README.md
new file mode 100644
index 0000000000..3f50fed253
--- /dev/null
+++ b/vlib/sdl/README.md
@@ -0,0 +1,54 @@
+# sdl
+SDL2 V module -- libSDL2 wrapper
+
+Current APIs available/tested in examples :
+- basic graphics (2D drawing)
+- [Image](image/README.md)
+- TTF font (text rendering)
+- input handling (keyboard/joystick events)
+- sounds (WAV mixing)
+- music (MOD mixing)
+- more to come.. (networking ?)
+
+# Support
+sdl is supported on :
+- linux (major distros)
+- MacOS (brew)
+- windows (msys2/mingw64 only for now)
+
+# Examples
+
+[tVintris](examples/tvintris)
+
+![tVintris screenshot](examples/tvintris/images/tvintris.png)
+
+You can run the tVintris example from the V root folder like this :
+```
+v run vlib/sdl/examples/tvintris/tvintris.v
+```
+
+# Dependencies
+
+## Linux
+Fedora :
+`$ sudo dnf install SDL2-devel SDL2_ttf-devel SDL2_mixer-devel SDL2_image-devel`
+
+Ubuntu :
+`$ sudo apt install libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev`
+
+ClearLinux :
+`$ sudo swupd bundle-add devpkg-SDL2_ttf devpkg-SDL2_mixer devpkg-SDL2_image`
+
+## MacOS
+Brew :
+`$ brew install sdl2 sdl2_gfx sdl2_ttf sdl2_mixer sdl2_image sdl2_net`
+
+## Windows
+Windows/MSYS2 :
+`$ pacman -S mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_image`
+
+# Contributions
+
+nsauzede
+spytheman
+adlesh
diff --git a/vlib/sdl/examples/tvintris/README.md b/vlib/sdl/examples/tvintris/README.md
new file mode 100644
index 0000000000..4f23877858
--- /dev/null
+++ b/vlib/sdl/examples/tvintris/README.md
@@ -0,0 +1,17 @@
+# tVintris
+
+tvintris.v is a dual-player (local) version based on original source from tetris example by Alex M.
+It is largely inspired by ancient game Twintris.
+-uses vlib sdl module
+
+![tVintris screenshot](images/tvintris.png)
+
+# how to run tVintris
+
+`$ v run .`
+
+# Credits
+
+Colors, Music and Sounds inspired/ripped from amiga title Twintris (1990 nostalgia !)
+- Graphician : Svein Berge
+- Musician : Tor Bernhard Gausen (Walkman/Cryptoburners)
diff --git a/vlib/sdl/examples/tvintris/fonts/RobotoMono-Regular.ttf b/vlib/sdl/examples/tvintris/fonts/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000..b158a334eb
Binary files /dev/null and b/vlib/sdl/examples/tvintris/fonts/RobotoMono-Regular.ttf differ
diff --git a/vlib/sdl/examples/tvintris/images/tvintris.png b/vlib/sdl/examples/tvintris/images/tvintris.png
new file mode 100644
index 0000000000..31bc7f58dc
Binary files /dev/null and b/vlib/sdl/examples/tvintris/images/tvintris.png differ
diff --git a/vlib/sdl/examples/tvintris/images/v-logo_30_30.png b/vlib/sdl/examples/tvintris/images/v-logo_30_30.png
new file mode 100644
index 0000000000..ae8c476c93
Binary files /dev/null and b/vlib/sdl/examples/tvintris/images/v-logo_30_30.png differ
diff --git a/vlib/sdl/examples/tvintris/sounds/TwintrisThosenine.mod b/vlib/sdl/examples/tvintris/sounds/TwintrisThosenine.mod
new file mode 100644
index 0000000000..4fbceed7b8
Binary files /dev/null and b/vlib/sdl/examples/tvintris/sounds/TwintrisThosenine.mod differ
diff --git a/vlib/sdl/examples/tvintris/sounds/block.wav b/vlib/sdl/examples/tvintris/sounds/block.wav
new file mode 100644
index 0000000000..e66eb0689d
Binary files /dev/null and b/vlib/sdl/examples/tvintris/sounds/block.wav differ
diff --git a/vlib/sdl/examples/tvintris/sounds/single.wav b/vlib/sdl/examples/tvintris/sounds/single.wav
new file mode 100644
index 0000000000..bc2fd5c078
Binary files /dev/null and b/vlib/sdl/examples/tvintris/sounds/single.wav differ
diff --git a/vlib/sdl/examples/tvintris/sounds/triple.wav b/vlib/sdl/examples/tvintris/sounds/triple.wav
new file mode 100644
index 0000000000..d501c4218a
Binary files /dev/null and b/vlib/sdl/examples/tvintris/sounds/triple.wav differ
diff --git a/vlib/sdl/examples/tvintris/tvintris.v b/vlib/sdl/examples/tvintris/tvintris.v
new file mode 100644
index 0000000000..5f6b7cfbca
--- /dev/null
+++ b/vlib/sdl/examples/tvintris/tvintris.v
@@ -0,0 +1,843 @@
+// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+
+// SDL2 port+wrapper, Twintris-like dual-game logic,
+// and more, by Nicolas Sauzede 2019.
+
+module main
+
+import rand
+import time
+import os
+import math
+import sdl
+import sdl.image as img
+[inline] fn sdl_fill_rect(s &vsdl2.Surface,r &vsdl2.Rect,c &vsdl2.Color){vsdl2.fill_rect(s,r,c)}
+
+const (
+ Title = 'tVintris'
+ BASE = os.dir( os.realpath( os.executable() ) )
+ FontName = BASE + '/fonts/RobotoMono-Regular.ttf'
+ MusicName = BASE + '/sounds/TwintrisThosenine.mod'
+ SndBlockName = BASE + '/sounds/block.wav'
+ SndLineName = BASE + '/sounds/single.wav'
+ SndDoubleName = BASE + '/sounds/triple.wav'
+ VLogo = BASE + '/images/v-logo_30_30.png'
+ BlockSize = 20 // pixels
+ FieldHeight = 20 // # of blocks
+ FieldWidth = 10
+ TetroSize = 4
+ WinWidth = BlockSize * FieldWidth * 3
+ WinHeight = BlockSize * FieldHeight
+ TimerPeriod = 250 // ms
+ TextSize = 16
+ AudioBufSize = 1024
+
+ P2FIRE = C.SDLK_l
+ P2UP = C.SDLK_UP
+ P2DOWN = C.SDLK_DOWN
+ P2LEFT = C.SDLK_LEFT
+ P2RIGHT = C.SDLK_RIGHT
+
+ P1FIRE = C.SDLK_s
+ P1UP = C.SDLK_w
+ P1DOWN = C.SDLK_x
+ P1LEFT = C.SDLK_a
+ P1RIGHT = C.SDLK_d
+
+ NJOYMAX = 2
+ // joystick name => enter your own device name
+ JOYP1NAME = 'Generic X-Box pad'
+ // following are joystick button number
+ JBP1FIRE = 1
+ // following are joystick hat value
+ JHP1UP = 1
+ JHP1DOWN = 4
+ JHP1LEFT = 8
+ JHP1RIGHT = 3
+
+ // joystick name => enter your own device name
+ JOYP2NAME = 'RedOctane Guitar Hero X-plorer'
+ // following are joystick button number
+ JBP2FIRE = 0
+ // following are joystick hat value
+ JHP2UP = 4
+ JHP2DOWN = 1
+ JHP2LEFT = 8
+ JHP2RIGHT = 2
+)
+
+const (
+ // Tetros' 4 possible states are encoded in binaries
+ BTetros = [
+ // 0000 0
+ // 0000 0
+ // 0110 6
+ // 0110 6
+ [66, 66, 66, 66],
+ // 0000 0
+ // 0000 0
+ // 0010 2
+ // 0111 7
+ [27, 131, 72, 232],
+ // 0000 0
+ // 0000 0
+ // 0011 3
+ // 0110 6
+ [36, 231, 36, 231],
+ // 0000 0
+ // 0000 0
+ // 0110 6
+ // 0011 3
+ [63, 132, 63, 132],
+ // 0000 0
+ // 0011 3
+ // 0001 1
+ // 0001 1
+ [311, 17, 223, 74],
+ // 0000 0
+ // 0011 3
+ // 0010 2
+ // 0010 2
+ [322, 71, 113, 47],
+ // Special case since 15 can't be used
+ // 1111
+ [1111, 9, 1111, 9],
+ ]
+ // Each tetro has its unique color
+ Colors = [
+ vsdl2.Color{byte(0), byte(0), byte(0), byte(0)}, // unused ?
+ vsdl2.Color{byte(0), byte(0x62), byte(0xc0), byte(0)}, // quad : darkblue 0062c0
+ vsdl2.Color{byte(0xca), byte(0x7d), byte(0x5f), byte(0)}, // tricorn : lightbrown ca7d5f
+ vsdl2.Color{byte(0), byte(0xc1), byte(0xbf), byte(0)}, // short topright : lightblue 00c1bf
+ vsdl2.Color{byte(0), byte(0xc1), byte(0), byte(0)}, // short topleft : lightgreen 00c100
+ vsdl2.Color{byte(0xbf), byte(0xbe), byte(0), byte(0)}, // long topleft : yellowish bfbe00
+ vsdl2.Color{byte(0xd1), byte(0), byte(0xbf), byte(0)}, // long topright : pink d100bf
+ vsdl2.Color{byte(0xd1), byte(0), byte(0), byte(0)}, // longest : lightred d10000
+ vsdl2.Color{byte(0), byte(170), byte(170), byte(0)}, // unused ?
+ ]
+ // Background color
+ BackgroundColor = vsdl2.Color{byte(0), byte(0), byte(0), byte(0)}
+// BackgroundColor = vsdl2.Color{byte(255), byte(255), byte(255), byte(0)}
+ // Foreground color
+ ForegroundColor = vsdl2.Color{byte(0), byte(170), byte(170), byte(0)}
+// ForegroundColor = vsdl2.Color{byte(0), byte(0), byte(0), byte(0)}
+ // Text color
+ TextColor = vsdl2.Color{byte(0xca), byte(0x7d), byte(0x5f), byte(0)}
+// TextColor = vsdl2.Color{byte(0), byte(0), byte(0), byte(0)}
+)
+
+// TODO: type Tetro [TetroSize]struct{ x, y int }
+struct Block {
+ mut:
+ x int
+ y int
+}
+
+enum GameState {
+ paused running gameover
+}
+
+struct AudioContext {
+mut:
+ music voidptr
+ volume int
+ waves [3]voidptr
+}
+
+struct SdlContext {
+pub:
+mut:
+// VIDEO
+ w int
+ h int
+ window voidptr
+ renderer voidptr
+ screen &vsdl2.Surface
+ texture voidptr
+// AUDIO
+ actx AudioContext
+// JOYSTICKS
+ jnames [2]string
+ jids [2]int
+// V logo
+ v_logo &vsdl2.Surface
+ tv_logo voidptr
+}
+
+struct Game {
+mut:
+ // Score of the current game
+ score int
+ // Count consecutive lines for scoring
+ lines int
+ // State of the current game
+ state GameState
+ // X offset of the game display
+ ofs_x int
+ // keys
+ k_fire int
+ k_up int
+ k_down int
+ k_left int
+ k_right int
+ // joystick ID
+ joy_id int
+ // joystick buttons
+ jb_fire int
+ // joystick hat values
+ jh_up int
+ jh_down int
+ jh_left int
+ jh_right int
+ // game rand seed
+ seed int
+ seed_ini int
+ // Position of the current tetro
+ pos_x int
+ pos_y int
+ // field[y][x] contains the color of the block with (x,y) coordinates
+ // "-1" border is to avoid bounds checking.
+ // -1 -1 -1 -1
+ // -1 0 0 -1
+ // -1 0 0 -1
+ // -1 -1 -1 -1
+ field [][]int
+ // TODO: tetro Tetro
+ tetro []Block
+ // TODO: tetros_cache []Tetro
+ tetros_cache []Block
+ // Index of the current tetro. Refers to its color.
+ tetro_idx int
+ // Index of the next tetro. Refers to its color.
+ tetro_next int
+ // tetro stats : buckets of drawn tetros
+ tetro_stats []int
+ // total number of drawn tetros
+ tetro_total int
+ // Index of the rotation (0-3)
+ rotation_idx int
+ // SDL2 context for drawing
+ sdl SdlContext
+ // TTF context for font drawing
+ font voidptr
+}
+
+fn (sdl mut SdlContext) set_sdl_context(w int, h int, title string) {
+ C.SDL_Init(C.SDL_INIT_VIDEO | C.SDL_INIT_AUDIO | C.SDL_INIT_JOYSTICK)
+ C.atexit(C.SDL_Quit)
+ C.TTF_Init()
+ C.atexit(C.TTF_Quit)
+ bpp := 32
+ vsdl2.create_window_and_renderer(w, h, 0, &sdl.window, &sdl.renderer)
+// C.SDL_CreateWindowAndRenderer(w, h, 0, voidptr(&sdl.window), voidptr(&sdl.renderer))
+ C.SDL_SetWindowTitle(sdl.window, title.str)
+ sdl.w = w
+ sdl.h = h
+ sdl.screen = vsdl2.create_rgb_surface(0, w, h, bpp, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000)
+ sdl.texture = C.SDL_CreateTexture(sdl.renderer, C.SDL_PIXELFORMAT_ARGB8888, C.SDL_TEXTUREACCESS_STREAMING, w, h)
+
+ C.Mix_Init(0)
+ C.atexit(C.Mix_Quit)
+ if C.Mix_OpenAudio(48000,C.MIX_DEFAULT_FORMAT,2,AudioBufSize) < 0 {
+ println('couldn\'t open audio')
+ }
+ println('opening music $MusicName')
+ sdl.actx.music = C.Mix_LoadMUS(MusicName.str)
+ sdl.actx.waves[0] = C.Mix_LoadWAV(SndBlockName.str)
+ sdl.actx.waves[1] = C.Mix_LoadWAV(SndLineName.str)
+ sdl.actx.waves[2] = C.Mix_LoadWAV(SndDoubleName.str)
+ sdl.actx.volume = C.SDL_MIX_MAXVOLUME
+ if C.Mix_PlayMusic(sdl.actx.music, 1) != -1 {
+ C.Mix_VolumeMusic(sdl.actx.volume)
+ }
+ njoy := C.SDL_NumJoysticks()
+ for i := 0; i < njoy; i++ {
+ C.SDL_JoystickOpen(i)
+ jn := tos_clone(vsdl2.joystick_name_for_index(i))
+ println('JOY NAME $jn')
+ for j := 0; j < NJOYMAX; j++ {
+ if sdl.jnames[j] == jn {
+ println('FOUND JOYSTICK $j $jn ID=$i')
+ sdl.jids[j] = i
+ }
+ }
+ }
+ flags := C.IMG_INIT_PNG
+ imgres := img.img_init(flags)
+ if ((imgres & flags) != flags) {
+ println('error initializing image library.')
+ }
+ println('opening logo $VLogo')
+ sdl.v_logo = img.load(VLogo)
+ if !isnil(sdl.v_logo) {
+// println('got v_logo=$sdl.v_logo')
+ sdl.tv_logo = vsdl2.create_texture_from_surface(sdl.renderer, sdl.v_logo)
+// println('got tv_logo=$sdl.tv_logo')
+ }
+ C.SDL_JoystickEventState(C.SDL_ENABLE)
+}
+
+fn main() {
+ println('tVintris -- tribute to venerable Twintris')
+ mut game := &Game{ font: 0 }
+ game.sdl.jnames[0] = JOYP1NAME
+ game.sdl.jnames[1] = JOYP2NAME
+ game.sdl.jids[0] = -1
+ game.sdl.jids[1] = -1
+ game.sdl.set_sdl_context(WinWidth, WinHeight, Title)
+ game.font = C.TTF_OpenFont(FontName.str, TextSize)
+ seed := time.now().uni
+ mut game2 := &Game{ font: 0 }
+ game2.sdl = game.sdl
+ game2.font = game.font
+
+ game.joy_id = game.sdl.jids[0]
+// println('JOY1 id=${game.joy_id}')
+ game2.joy_id = game.sdl.jids[1]
+// println('JOY2 id=${game2.joy_id}')
+
+ // delay uses milliseconds so 1000 ms / 30 frames (30fps) roughly = 33.3333 ms/frame
+ time_per_frame := 1000.0 / 30.0
+
+ game.k_fire = P1FIRE
+ game.k_up = P1UP
+ game.k_down = P1DOWN
+ game.k_left = P1LEFT
+ game.k_right = P1RIGHT
+ game.jb_fire = JBP1FIRE
+ game.jh_up = JHP1UP
+ game.jh_down = JHP1DOWN
+ game.jh_left = JHP1LEFT
+ game.jh_right = JHP1RIGHT
+ game.ofs_x = 0
+ game.seed_ini = seed
+ game.init_game()
+ game.state = .running
+ go game.run() // Run the game loop in a new thread
+
+ game2.k_fire = P2FIRE
+ game2.k_up = P2UP
+ game2.k_down = P2DOWN
+ game2.k_left = P2LEFT
+ game2.k_right = P2RIGHT
+ game2.jb_fire = JBP2FIRE
+ game2.jh_up = JHP2UP
+ game2.jh_down = JHP2DOWN
+ game2.jh_left = JHP2LEFT
+ game2.jh_right = JHP2RIGHT
+ game2.ofs_x = WinWidth * 2 / 3
+ game2.seed_ini = seed
+ game2.init_game()
+ game2.state = .running
+ go game2.run() // Run the game loop in a new thread
+
+ mut g := Game{ font: 0 }
+ mut should_close := false
+ mut total_frame_ticks := u64(0)
+ mut total_frames := u32(0)
+
+ for {
+ total_frames += 1
+ start_ticks := vsdl2.get_perf_counter()
+
+ g1 := game
+ g2 := game2
+ // here we determine which game contains most recent state
+ if g1.tetro_total > g.tetro_total {
+ g = *g1
+ }
+ if g2.tetro_total > g.tetro_total {
+ g = *g2
+ }
+ g.draw_begin()
+
+ g1.draw_tetro()
+ g1.draw_field()
+
+ g2.draw_tetro()
+ g2.draw_field()
+
+ g.draw_middle()
+
+ g1.draw_score()
+ g2.draw_score()
+
+ g.draw_stats()
+
+ g.draw_v_logo()
+ g.draw_end()
+
+// game.handle_events() // CRASHES if done in function ???
+ ev := vsdl2.Event{}
+ for 0 < vsdl2.poll_event(&ev) {
+ match int(ev._type) {
+ C.SDL_QUIT { should_close = true }
+ C.SDL_KEYDOWN {
+ key := int(ev.key.keysym.sym)
+ if key == C.SDLK_ESCAPE {
+ should_close = true
+ break
+ }
+ game.handle_key(key)
+ game2.handle_key(key)
+ }
+ C.SDL_JOYBUTTONDOWN {
+ jb := int(ev.jbutton.button)
+ joyid := int(ev.jbutton.which)
+// println('JOY BUTTON $jb $joyid')
+ game.handle_jbutton(jb, joyid)
+ game2.handle_jbutton(jb, joyid)
+ }
+ C.SDL_JOYHATMOTION {
+ jh := int(ev.jhat.hat)
+ jv := int(ev.jhat.value)
+ joyid := int(ev.jhat.which)
+// println('JOY HAT $jh $jv $joyid')
+ game.handle_jhat(jh, jv, joyid)
+ game2.handle_jhat(jh, jv, joyid)
+ }
+ }
+ }
+ if should_close {
+ break
+ }
+ end_ticks := vsdl2.get_perf_counter()
+
+ total_frame_ticks += end_ticks-start_ticks
+ elapsed_time := f64(end_ticks - start_ticks) / f64(vsdl2.get_perf_frequency())
+ // current_fps := 1.0 / elapsed_time
+
+ // should limit system to (1 / time_per_frame) fps
+ vsdl2.delay(u32(math.floor(time_per_frame - elapsed_time)))
+ }
+ if game.font != voidptr(0) {
+ C.TTF_CloseFont(game.font)
+ }
+ if game.sdl.actx.music != voidptr(0) {
+ C.Mix_FreeMusic(game.sdl.actx.music)
+ }
+ C.Mix_CloseAudio()
+ if game.sdl.actx.waves[0] != voidptr(0) {
+ C.Mix_FreeChunk(game.sdl.actx.waves[0])
+ }
+ if game.sdl.actx.waves[1] != voidptr(0) {
+ C.Mix_FreeChunk(game.sdl.actx.waves[1])
+ }
+ if game.sdl.actx.waves[2] != voidptr(0) {
+ C.Mix_FreeChunk(game.sdl.actx.waves[2])
+ }
+ if !isnil(game.sdl.tv_logo) {
+ vsdl2.destroy_texture(game.sdl.tv_logo)
+ }
+ if !isnil(game.sdl.v_logo) {
+ vsdl2.free_surface(game.sdl.v_logo)
+ }
+}
+
+enum Action {
+ idle space fire
+}
+fn (game mut Game) handle_key(key int) {
+ // global keys
+ mut action := Action(.idle)
+ match key {
+ C.SDLK_SPACE { action = .space }
+ game.k_fire { action = .fire }
+ }
+
+ if action == .space {
+ match game.state {
+ .running {
+ C.Mix_PauseMusic()
+ game.state = .paused
+ }
+ .paused {
+ C.Mix_ResumeMusic()
+ game.state = .running
+ }
+ }
+ }
+
+ if action == .fire {
+ match game.state {
+ .gameover {
+ game.init_game()
+ game.state = .running
+ }
+ }
+ }
+ if game.state != .running { return }
+ // keys while game is running
+ match key {
+ game.k_up { game.rotate_tetro() }
+ game.k_left { game.move_right(-1) }
+ game.k_right { game.move_right(1) }
+ game.k_down { game.move_tetro() } // drop faster when the player presses
+ }
+}
+
+fn (game mut Game) handle_jbutton(jb int, joyid int) {
+ if joyid != game.joy_id {
+ return
+ }
+ // global buttons
+ mut action := Action(.idle)
+ match jb {
+ game.jb_fire { action = .fire }
+ }
+
+ if action == .fire {
+ match game.state {
+ .gameover {
+ game.init_game()
+ game.state = .running
+ }
+ }
+ }
+}
+
+fn (game mut Game) handle_jhat(jh int, jv int, joyid int) {
+ if joyid != game.joy_id {
+ return
+ }
+ if game.state != .running { return }
+// println('testing hat values.. joyid=$joyid jh=$jh jv=$jv')
+ // hat values while game is running
+ match jv {
+ game.jh_up { game.rotate_tetro() }
+ game.jh_left { game.move_right(-1) }
+ game.jh_right { game.move_right(1) }
+ game.jh_down { game.move_tetro() } // drop faster when the player presses
+ }
+}
+
+fn (g mut Game) init_game() {
+ g.score = 0
+ g.tetro_total = 0
+ g.tetro_stats = [0, 0, 0, 0, 0, 0, 0]
+ g.parse_tetros()
+ g.seed = g.seed_ini
+ g.generate_tetro()
+ g.field = []
+ // Generate the field, fill it with 0's, add -1's on each edge
+ for i := 0; i < FieldHeight + 2; i++ {
+ mut row := [0].repeat(FieldWidth + 2)
+ row[0] = - 1
+ row[FieldWidth + 1] = - 1
+ g.field << row
+ }
+ mut first_row := g.field[0]
+ mut last_row := g.field[FieldHeight + 1]
+ for j := 0; j < FieldWidth + 2; j++ {
+ first_row[j] = - 1
+ last_row[j] = - 1
+ }
+}
+
+fn (g mut Game) parse_tetros() {
+ for b_tetros in BTetros {
+ for b_tetro in b_tetros {
+ for t in parse_binary_tetro(b_tetro) {
+ g.tetros_cache << t
+ }
+ }
+ }
+}
+
+fn (g mut Game) run() {
+ for {
+ if g.state == .running {
+ g.move_tetro()
+ n := g.delete_completed_lines()
+ if n > 0 {
+ g.lines += n
+ } else {
+ if g.lines > 0 {
+ if g.lines > 1 {
+ C.Mix_PlayChannel(0, g.sdl.actx.waves[2], 0)
+ } else if g.lines == 1 {
+ C.Mix_PlayChannel(0, g.sdl.actx.waves[1], 0)
+ }
+ g.score += 10 * g.lines * g.lines
+ g.lines = 0
+ }
+ }
+ }
+ time.sleep_ms(TimerPeriod) // medium delay between game step
+ }
+}
+
+fn (game mut Game) rotate_tetro() {
+ // Rotate the tetro
+ old_rotation_idx := game.rotation_idx
+ game.rotation_idx++
+ if game.rotation_idx == TetroSize {
+ game.rotation_idx = 0
+ }
+ game.get_tetro()
+ if !game.move_right(0) {
+ game.rotation_idx = old_rotation_idx
+ game.get_tetro()
+ }
+ if game.pos_x < 0 {
+ game.pos_x = 1
+ }
+}
+
+fn (g mut Game) move_tetro() {
+ // Check each block in current tetro
+ for block in g.tetro {
+ y := block.y + g.pos_y + 1
+ x := block.x + g.pos_x
+ // Reached the bottom of the screen or another block?
+ // TODO: if g.field[y][x] != 0
+ //if g.field[y][x] != 0 {
+ row := g.field[y]
+ if row[x] != 0 {
+ // The new tetro has no space to drop => end of the game
+ if g.pos_y < 2 {
+ g.state = .gameover
+ g.tetro_total = 0
+ return
+ }
+ // Drop it and generate a new one
+ g.drop_tetro()
+ g.generate_tetro()
+ C.Mix_PlayChannel(0, g.sdl.actx.waves[0], 0)
+ return
+ }
+ }
+ g.pos_y++
+}
+
+fn (g mut Game) move_right(dx int) bool {
+ // Reached left/right edge or another tetro?
+ for i := 0; i < TetroSize; i++ {
+ tetro := g.tetro[i]
+ y := tetro.y + g.pos_y
+ x := tetro.x + g.pos_x + dx
+ row := g.field[y]
+ if row[x] != 0 {
+ // Do not move
+ return false
+ }
+ }
+ g.pos_x += dx
+ return true
+}
+
+fn (g &Game) delete_completed_lines() int {
+ mut n := 0
+ for y := FieldHeight; y >= 1; y-- {
+ n += g.delete_completed_line(y)
+ }
+ return n
+}
+
+fn (g &Game) delete_completed_line(y int) int {
+ for x := 1; x <= FieldWidth; x++ {
+ f := g.field[y]
+ if f[x] == 0 {
+ return 0
+ }
+ }
+ // Move everything down by 1 position
+ for yy := y - 1; yy >= 1; yy-- {
+ for x := 1; x <= FieldWidth; x++ {
+ mut a := g.field[yy + 1]
+ b := g.field[yy]
+ a[x] = b[x]
+ }
+ }
+ return 1
+}
+
+// Draw a rand tetro index
+fn (g mut Game) rand_tetro() int {
+ cur := g.tetro_next
+ g.tetro_next = rand.rand_r(&g.seed)
+ g.tetro_next = g.tetro_next % BTetros.len
+ return cur
+}
+
+// Place a new tetro on top
+fn (g mut Game) generate_tetro() {
+ g.pos_y = 0
+ g.pos_x = FieldWidth / 2 - TetroSize / 2
+ g.tetro_idx = g.rand_tetro()
+// println('idx=${g.tetro_idx}')
+ g.tetro_stats[g.tetro_idx] += 1
+ g.tetro_total++
+ g.rotation_idx = 0
+ g.get_tetro()
+}
+
+// Get the right tetro from cache
+fn (g mut Game) get_tetro() {
+ idx := g.tetro_idx * TetroSize * TetroSize + g.rotation_idx * TetroSize
+ g.tetro = g.tetros_cache[idx .. idx + TetroSize]
+}
+
+fn (g &Game) drop_tetro() {
+ for i := 0; i < TetroSize; i++ {
+ tetro := g.tetro[i]
+ x := tetro.x + g.pos_x
+ y := tetro.y + g.pos_y
+ // Remember the color of each block
+ // TODO: g.field[y][x] = g.tetro_idx + 1
+ mut row := g.field[y]
+ row[x] = g.tetro_idx + 1
+ }
+}
+
+fn (g &Game) draw_tetro() {
+ for i := 0; i < TetroSize; i++ {
+ tetro := g.tetro[i]
+ g.draw_block(g.pos_y + tetro.y, g.pos_x + tetro.x, g.tetro_idx + 1)
+ }
+}
+
+fn (g &Game) draw_block(i, j, color_idx int) {
+ rect := vsdl2.Rect {g.ofs_x + (j - 1) * BlockSize, (i - 1) * BlockSize,
+ BlockSize - 1, BlockSize - 1}
+ col := Colors[color_idx]
+ sdl_fill_rect(g.sdl.screen, &rect, &col)
+}
+
+fn (g &Game) draw_field() {
+ for i := 1; i < FieldHeight + 1; i++ {
+ for j := 1; j < FieldWidth + 1; j++ {
+ f := g.field[i]
+ if f[j] > 0 {
+ g.draw_block(i, j, f[j])
+ }
+ }
+ }
+}
+
+fn (g &Game) draw_v_logo() {
+ if isnil(g.sdl.tv_logo) {
+ return
+ }
+ texw := 0
+ texh := 0
+ C.SDL_QueryTexture(g.sdl.tv_logo, 0, 0, &texw, &texh)
+ dstrect := vsdl2.Rect { (WinWidth / 2) - (texw / 2), 20, texw, texh }
+ // Currently we can't seem to use vsdl2.render_copy when we need to pass a nil pointer (eg: srcrect to be NULL)
+// vsdl2.render_copy(g.sdl.renderer, tv_logo, 0, &dstrect)
+ C.SDL_RenderCopy(g.sdl.renderer, g.sdl.tv_logo, voidptr(0), voidptr(&dstrect))
+}
+
+fn (g &Game) draw_text(x int, y int, text string, tcol vsdl2.Color) {
+ _tcol := C.SDL_Color{tcol.r, tcol.g, tcol.b, tcol.a}
+ tsurf := C.TTF_RenderText_Solid(g.font, text.str, _tcol)
+ ttext := C.SDL_CreateTextureFromSurface(g.sdl.renderer, tsurf)
+ texw := 0
+ texh := 0
+ C.SDL_QueryTexture(ttext, 0, 0, &texw, &texh)
+ dstrect := vsdl2.Rect { x, y, texw, texh }
+// vsdl2.render_copy(g.sdl.renderer, ttext, 0, &dstrect)
+ C.SDL_RenderCopy(g.sdl.renderer, ttext, voidptr(0), voidptr(&dstrect))
+ C.SDL_DestroyTexture(ttext)
+ vsdl2.free_surface(tsurf)
+}
+
+[inline] fn (g &Game) draw_ptext(x int, y int, text string, tcol vsdl2.Color) {
+ g.draw_text(g.ofs_x + x, y, text, tcol)
+}
+
+[live]
+fn (g &Game) draw_begin() {
+// println('about to clear')
+ C.SDL_RenderClear(g.sdl.renderer)
+ mut rect := vsdl2.Rect {0,0,g.sdl.w,g.sdl.h}
+ col := vsdl2.Color{byte(00), byte(00), byte(0), byte(0)}
+// sdl_fill_rect(g.sdl.screen, &rect, BackgroundColor)
+ sdl_fill_rect(g.sdl.screen, &rect, col)
+
+ rect = vsdl2.Rect {BlockSize * FieldWidth + 2,0,2,g.sdl.h}
+ sdl_fill_rect(g.sdl.screen, &rect, ForegroundColor)
+ rect = vsdl2.Rect {WinWidth - BlockSize * FieldWidth - 4,0,2,g.sdl.h}
+ sdl_fill_rect(g.sdl.screen, &rect, ForegroundColor)
+
+ mut idx := 0
+ for st in g.tetro_stats {
+ mut s := 10
+ if g.tetro_total > 0 {
+ s += 90 * st / g.tetro_total
+ }
+ w := BlockSize
+ h := s * 4 * w / 100
+ rect = vsdl2.Rect {(WinWidth - 7 * (w + 1)) / 2 + idx * (w + 1), WinHeight * 3 / 4 - h, w, h}
+ sdl_fill_rect(g.sdl.screen, &rect, Colors[idx + 1])
+ idx++
+ }
+}
+
+fn (g &Game) draw_middle() {
+ C.SDL_UpdateTexture(g.sdl.texture, 0, g.sdl.screen.pixels, g.sdl.screen.pitch)
+// vsdl2.render_copy(g.sdl.renderer, g.sdl.texture, voidptr(0), voidptr(0))
+ C.SDL_RenderCopy(g.sdl.renderer, g.sdl.texture, voidptr(0), voidptr(0))
+}
+
+fn (g &Game) draw_score() {
+ if g.font != voidptr(0) {
+ g.draw_ptext(1, 2, 'score: ' + g.score.str() + ' nxt=' + g.tetro_next.str(), TextColor)
+ if g.state == .gameover {
+ g.draw_ptext(1, WinHeight / 2 + 0 * TextSize, 'Game Over', TextColor)
+ g.draw_ptext(1, WinHeight / 2 + 2 * TextSize, 'FIRE to restart', TextColor)
+ } else if g.state == .paused {
+ g.draw_ptext(1, WinHeight / 2 + 0 * TextSize, 'Game Paused', TextColor)
+ g.draw_ptext(1, WinHeight / 2 + 2 * TextSize, 'SPACE to resume', TextColor)
+ }
+ }
+}
+
+fn (g &Game) draw_stats() {
+ if g.font != voidptr(0) {
+ g.draw_text(WinWidth / 3 + 10, WinHeight * 3 / 4 + 0 * TextSize, 'stats: ' + g.tetro_total.str() + ' tetros', TextColor)
+ mut stats := ''
+ for st in g.tetro_stats {
+ mut s := 0
+ if g.tetro_total > 0 {
+ s = 100 * st / g.tetro_total
+ }
+ stats += ' '
+ stats += s.str()
+ }
+ g.draw_text(WinWidth / 3 - 8, WinHeight * 3 / 4 + 2 * TextSize, stats, TextColor)
+ }
+}
+
+fn (g &Game) draw_end() {
+ C.SDL_RenderPresent(g.sdl.renderer)
+}
+
+fn parse_binary_tetro(t_ int) []Block {
+ mut t := t_
+ res := [Block{}].repeat(4)
+ mut cnt := 0
+ horizontal := t == 9// special case for the horizontal line
+ for i := 0; i <= 3; i++ {
+ // Get ith digit of t
+ p := int(math.pow(10, 3 - i))
+ mut digit := int(t / p)
+ t %= p
+ // Convert the digit to binary
+ for j := 3; j >= 0; j-- {
+ bin := digit % 2
+ digit /= 2
+ if bin == 1 || (horizontal && i == TetroSize - 1) {
+ // TODO: res[cnt].x = j
+ // res[cnt].y = i
+ mut point := &res[cnt]
+ point.x = j
+ point.y = i
+ cnt++
+ }
+ }
+ }
+ return res
+}
diff --git a/vlib/sdl/image/README.md b/vlib/sdl/image/README.md
new file mode 100644
index 0000000000..39b00103a4
--- /dev/null
+++ b/vlib/sdl/image/README.md
@@ -0,0 +1,4 @@
+# SDL2 Image Library
+Assuming you've installed the dependencies for sdl (already include `SDL2_image` dependency)
+
+See the Tvintris example for usage
diff --git a/vlib/sdl/image/image.v b/vlib/sdl/image/image.v
new file mode 100644
index 0000000000..4e32e195ae
--- /dev/null
+++ b/vlib/sdl/image/image.v
@@ -0,0 +1,29 @@
+module image
+
+#flag linux -lSDL2_image
+#include
+
+// following kludge until `sdl2-config ...` is supported also on windows
+#flag windows -I/msys64/mingw64/include/SDL2
+#flag windows -L/mingw64/lib -lSDL2_image
+
+//////////////////////////////////////////////////////////
+// SDL_Image.h
+//////////////////////////////////////////////////////////
+//fn C.IMG_Load_RW(logo &vsdl2.RwOps, free_src int) &vsdl2.Surface
+fn C.IMG_Init(flags int) int
+fn C.IMG_Quit()
+fn C.IMG_Load(file byteptr) voidptr
+
+pub fn img_init(flags int) int {
+ return C.IMG_Init(flags)
+}
+
+pub fn quit() {
+ C.IMG_Quit()
+}
+
+pub fn load(file string) &vsdl2.Surface {
+ res := C.IMG_Load(file.str)
+ return res
+}
diff --git a/vlib/sdl/sdl.v b/vlib/sdl/sdl.v
new file mode 100644
index 0000000000..5770872778
--- /dev/null
+++ b/vlib/sdl/sdl.v
@@ -0,0 +1,308 @@
+// Copyright(C) 2019 Nicolas Sauzede. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+
+module sdl
+
+#flag linux `sdl2-config --cflags --libs` -lSDL2_ttf -lSDL2_mixer -lSDL2_image
+#flag darwin `sdl2-config --cflags --libs` -lSDL2_ttf -lSDL2_mixer -lSDL2_image
+
+//#flag windows `sdl2-config --cflags`
+//#flag windows `sdl2-config --libs` -lSDL2_ttf -lSDL2_mixer -lSDL2_image
+//#flag `sdl2-config --cflags --libs` -lSDL2_ttf -lSDL2_mixer -lSDL2_image
+
+#flag -DSDL_DISABLE_IMMINTRIN_H
+
+// following kludge until `sdl2-config ...` is supported also on windows
+#flag windows -I/msys64/mingw64/include/SDL2
+#flag windows -Dmain=SDL_main
+#flag windows -L/mingw64/lib -lmingw32 -lSDL2main -lSDL2 -lSDL2_ttf -lSDL2_mixer -lSDL2_image
+
+#include
+#include
+#include
+
+
+//struct C.SDL_Color{
+pub struct Color{
+pub:
+ r byte /**< Red value 0-255 */
+ g byte /**< Green value 0-255 */
+ b byte /**< Blue value 0-255 */
+ a byte /**< Alpha value 0-255 */
+}
+//type Color C.SDL_Color
+
+pub struct C.SDL_Color{
+pub:
+ r byte
+ g byte
+ b byte
+ a byte
+}
+
+//struct C.SDL_Rect {
+pub struct Rect {
+pub:
+ x int /**< number of pixels from left side of screen */
+ y int /**< num of pixels from top of screen */
+ w int /**< width of rectangle */
+ h int /**< height of rectangle */
+}
+//type Rect C.SDL_Rect
+
+//pub struct C.SDL_Surface {
+pub struct Surface {
+pub:
+ flags u32
+ format voidptr
+ w int
+ h int
+ pitch int
+ pixels voidptr
+ userdata voidptr
+ locked int
+ lock_data voidptr
+ clip_rect Rect
+ map voidptr
+ refcount int
+}
+//type Surface C.SDL_Surface
+//type Surface Surface
+
+/////////////////////////////////////////////////////////
+
+struct QuitEvent {
+ _type u32 /**< SDL_QUIT */
+ timestamp u32
+}
+struct Keysym {
+pub:
+ scancode int /**< hardware specific scancode */
+ sym int /**< SDL virtual keysym */
+ mod u16 /**< current key modifiers */
+ unused u32 /**< translated character */
+}
+struct KeyboardEvent {
+pub:
+ _type u32 /**< SDL_KEYDOWN or SDL_KEYUP */
+ timestamp u32
+ windowid u32
+ state byte /**< SDL_PRESSED or SDL_RELEASED */
+ repeat byte
+ padding2 byte
+ padding3 byte
+ keysym Keysym
+}
+struct JoyButtonEvent {
+pub:
+ _type u32 /**< SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP */
+ timestamp u32
+ which int /**< The joystick device index */
+ button byte /**< The joystick button index */
+ state byte /**< SDL_PRESSED or SDL_RELEASED */
+}
+struct JoyHatEvent {
+pub:
+ _type u32 /**< SDL_JOYHATMOTION */
+ timestamp u32
+ which int /**< The joystick device index */
+ hat byte /**< The joystick hat index */
+ value byte /**< The hat position value:
+ * SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP
+ * SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT
+ * SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN
+ * Note that zero means the POV is centered.
+ */
+}
+
+//pub union EventU {
+pub union Event {
+pub:
+ _type u32
+ quit QuitEvent
+ key KeyboardEvent
+ jbutton JoyButtonEvent
+ jhat JoyHatEvent
+ _pad56 [56]byte
+}
+//type Event EventU
+
+
+//struct C.SDL_AudioSpec {
+pub struct AudioSpec {
+pub:
+mut:
+ freq int /**< DSP frequency -- samples per second */
+ format u16 /**< Audio data format */
+ channels byte /**< Number of channels: 1 mono, 2 stereo */
+ silence byte /**< Audio buffer silence value (calculated) */
+ samples u16 /**< Audio buffer size in samples (power of 2) */
+ size u32 /**< Necessary for some compile environments */
+ callback voidptr
+ userdata voidptr
+}
+
+// pub struct RwOps {
+// pub:
+// mut:
+// seek voidptr
+// read voidptr
+// write voidptr
+// close voidptr
+// type_ u32
+// hidden voidptr
+// }
+//type AudioSpec C.voidptrioSpec
+
+type atexit_func_t fn ()
+fn C.atexit(atexit_func_t)
+
+///////////////////////////////////////////////////
+fn C.SDL_MapRGB(fmt voidptr byte, g byte, b byte) u32
+fn C.SDL_CreateRGBSurface(flags u32, width int, height int, depth int, Rmask u32, Gmask u32, Bmask u32, Amask u32) voidptr
+fn C.SDL_PollEvent(&Event) int
+fn C.SDL_NumJoysticks() int
+fn C.SDL_JoystickNameForIndex(device_index int) voidptr
+fn C.SDL_RenderCopy(renderer voidptr, texture voidptr, srcrect voidptr, dstrect voidptr) int
+fn C.SDL_CreateWindow(title byteptr, x int, y int, w int, h int, flags u32) voidptr
+fn C.SDL_CreateWindowAndRenderer(width int, height int, window_flags u32, window &voidptr, renderer &voidptr) int
+fn C.SDL_DestroyWindow(window voidptr)
+fn C.SDL_GetWindowSize(window voidptr, w voidptr, h voidptr)
+fn C.SDL_SetHint(name byteptr, value byteptr) C.SDL_bool
+//fn C.SDL_RWFromFile(byteptr, byteptr) &RwOps
+//fn C.SDL_CreateTextureFromSurface(renderer &C.SDL_Renderer, surface &C.SDL_Surface) &C.SDL_Texture
+fn C.SDL_CreateTextureFromSurface(renderer voidptr, surface voidptr) voidptr
+fn C.SDL_CreateTexture(renderer voidptr, format u32, access int, w int, h int) voidptr
+fn C.SDL_FillRect(dst voidptr, dstrect voidptr, color u32) int
+fn C.SDL_RenderPresent(renderer voidptr)
+fn C.SDL_RenderClear(renderer voidptr) int
+fn C.SDL_UpdateTexture(texture voidptr, rect voidptr, pixels voidptr, pitch int) int
+fn C.SDL_QueryTexture(texture voidptr, format voidptr, access voidptr, w voidptr, h voidptr) int
+fn C.SDL_DestroyTexture(texture voidptr)
+fn C.SDL_FreeSurface(surface voidptr)
+fn C.SDL_Init(flags u32) int
+fn C.SDL_Quit()
+fn C.SDL_SetWindowTitle(window voidptr, title byteptr)
+// following is wrong : SDL_Zero is a macro accepting an argument
+fn C.SDL_zero()
+fn C.SDL_LoadWAV(file byteptr, spec voidptr, audio_buf voidptr, audio_len voidptr) voidptr
+fn C.SDL_FreeWAV(audio_buf voidptr)
+fn C.SDL_OpenAudio(desired voidptr, obtained voidptr) int
+fn C.SDL_CloseAudio()
+fn C.SDL_PauseAudio(pause_on int)
+fn C.SDL_JoystickOpen(device_index int) int
+fn C.SDL_JoystickEventState(state int) int
+
+//////////////////////////////////////////////////////////
+// SDL_Timer.h
+//////////////////////////////////////////////////////////
+fn C.SDL_GetTicks() u32
+fn C.SDL_TICKS_PASSED(a,b u32) bool
+fn C.SDL_GetPerformanceCounter() u64
+fn C.SDL_GetPerformanceFrequency() u64
+fn C.SDL_Delay(ms u32)
+
+//////////////////////////////////////////////////////////
+// TTF
+//////////////////////////////////////////////////////////
+fn C.TTF_Init() int
+fn C.TTF_Quit()
+fn C.TTF_OpenFont(file byteptr, ptsize int) voidptr
+fn C.TTF_CloseFont(font voidptr)
+//fn C.TTF_RenderText_Solid(voidptr, voidptr, SdlColor) voidptr
+fn C.TTF_RenderText_Solid(voidptr, voidptr, C.SDL_Color) voidptr
+//////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////
+// MIX
+//////////////////////////////////////////////////////////
+fn C.Mix_Init(flags int) int
+fn C.Mix_OpenAudio(frequency int, format u16, channels int, chunksize int) int
+fn C.Mix_LoadMUS(file byteptr) voidptr
+fn C.Mix_LoadWAV(file byteptr) voidptr
+fn C.Mix_PlayMusic(music voidptr, loops int) int
+fn C.Mix_VolumeMusic(volume int) int
+fn C.Mix_FreeMusic(music voidptr)
+fn C.Mix_CloseAudio()
+fn C.Mix_FreeChunk(chunk voidptr)
+fn C.Mix_PauseMusic()
+fn C.Mix_ResumeMusic()
+fn C.Mix_PlayChannel(channel int, chunk voidptr, loops int) int
+
+//////////////////////////////////////////////////////////
+// GL
+//////////////////////////////////////////////////////////
+fn C.SDL_GL_SetAttribute(attr int, value int) int
+fn C.SDL_GL_CreateContext(window voidptr) voidptr
+fn C.SDL_GL_MakeCurrent(window voidptr, context voidptr) int
+fn C.SDL_GL_SetSwapInterval(interval int) int
+fn C.SDL_GL_SwapWindow(window voidptr)
+fn C.SDL_GL_DeleteContext(context voidptr)
+
+pub fn create_texture_from_surface(renderer voidptr, surface &Surface) voidptr {
+ return C.SDL_CreateTextureFromSurface(renderer, voidptr(surface))
+}
+
+pub fn create_window_and_renderer(width int, height int, window_flags u32, window voidptr, renderer voidptr) int {
+ return C.SDL_CreateWindowAndRenderer(width, height, window_flags, window, renderer)
+}
+
+pub fn joystick_name_for_index(device_index int) byteptr {
+ return byteptr(C.SDL_JoystickNameForIndex(device_index))
+}
+
+pub fn fill_rect(screen &Surface, rect &Rect, _col &Color) {
+ col := C.SDL_MapRGB(screen.format, _col.r, _col.g, _col.b)
+ _screen := voidptr(screen)
+ _rect := voidptr(rect)
+ C.SDL_FillRect(_screen, _rect, col)
+}
+
+pub fn create_rgb_surface(flags u32, width int, height int, depth int, rmask u32, gmask u32, bmask u32, amask u32) &Surface {
+ res := C.SDL_CreateRGBSurface(flags, width, height, depth, rmask, gmask, bmask, amask)
+ return res
+}
+
+pub fn render_copy(renderer voidptr, texture voidptr, srcrect &Rect, dstrect &Rect) int {
+ _srcrect := voidptr(srcrect)
+ _dstrect := voidptr(dstrect)
+ return C.SDL_RenderCopy(renderer, texture, _srcrect, _dstrect)
+}
+
+pub fn poll_event(event &Event) int {
+ return C.SDL_PollEvent(voidptr(event))
+}
+
+pub fn destroy_texture(text voidptr) {
+ C.SDL_DestroyTexture(text)
+}
+
+pub fn free_surface(surf &Surface) {
+ _surf := voidptr(surf)
+ C.SDL_FreeSurface(_surf)
+}
+
+pub fn get_ticks() u32 {
+ return C.SDL_GetTicks()
+}
+
+pub fn ticks_passed(a, b u32) bool {
+ return C.SDL_TICKS_PASSED(a,b)
+}
+
+pub fn get_perf_counter() u64 {
+ return C.SDL_GetPerformanceCounter()
+}
+
+pub fn get_perf_frequency() u64 {
+ return C.SDL_GetPerformanceFrequency()
+}
+
+pub fn delay(ms u32) {
+ C.SDL_Delay(ms)
+}
+
+pub const (
+ version = '0.2' // hack to avoid unused module warning in the main program
+)