Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,197 @@
|
||||
module sapp
|
||||
|
||||
pub enum EventType {
|
||||
invalid
|
||||
key_down
|
||||
key_up
|
||||
char
|
||||
mouse_down
|
||||
mouse_up
|
||||
mouse_scroll
|
||||
mouse_move
|
||||
mouse_enter
|
||||
mouse_leave
|
||||
touches_began
|
||||
touches_moved
|
||||
touches_ended
|
||||
touches_cancelled
|
||||
resized
|
||||
iconified
|
||||
restored
|
||||
focused
|
||||
unfocused
|
||||
suspended
|
||||
resumed
|
||||
quit_requested
|
||||
clipboard_pasted
|
||||
files_dropped
|
||||
num
|
||||
}
|
||||
|
||||
pub enum MouseButton {
|
||||
invalid = -1
|
||||
left = 0
|
||||
right = 1
|
||||
middle = 2
|
||||
}
|
||||
|
||||
pub enum MouseCursor {
|
||||
default = C.SAPP_MOUSECURSOR_DEFAULT
|
||||
arrow = C.SAPP_MOUSECURSOR_ARROW
|
||||
ibeam = C.SAPP_MOUSECURSOR_IBEAM
|
||||
crosshair = C.SAPP_MOUSECURSOR_CROSSHAIR
|
||||
pointing_hand = C.SAPP_MOUSECURSOR_POINTING_HAND
|
||||
resize_ew = C.SAPP_MOUSECURSOR_RESIZE_EW
|
||||
resize_ns = C.SAPP_MOUSECURSOR_RESIZE_NS
|
||||
resize_nwse = C.SAPP_MOUSECURSOR_RESIZE_NWSE
|
||||
resize_nesw = C.SAPP_MOUSECURSOR_RESIZE_NESW
|
||||
resize_all = C.SAPP_MOUSECURSOR_RESIZE_ALL
|
||||
not_allowed = C.SAPP_MOUSECURSOR_NOT_ALLOWED
|
||||
}
|
||||
|
||||
pub enum Modifier {
|
||||
shift = 1 //(1<<0)
|
||||
ctrl = 2 //(1<<1)
|
||||
alt = 4 //(1<<2)
|
||||
super = 8 //(1<<3)
|
||||
lmb = 0x100
|
||||
rmb = 0x200
|
||||
mmb = 0x400
|
||||
}
|
||||
|
||||
pub enum KeyCode {
|
||||
invalid = 0
|
||||
space = 32
|
||||
apostrophe = 39 //'
|
||||
comma = 44 //,
|
||||
minus = 45 //-
|
||||
period = 46 //.
|
||||
slash = 47 ///
|
||||
_0 = 48
|
||||
_1 = 49
|
||||
_2 = 50
|
||||
_3 = 51
|
||||
_4 = 52
|
||||
_5 = 53
|
||||
_6 = 54
|
||||
_7 = 55
|
||||
_8 = 56
|
||||
_9 = 57
|
||||
semicolon = 59 //;
|
||||
equal = 61 //=
|
||||
a = 65
|
||||
b = 66
|
||||
c = 67
|
||||
d = 68
|
||||
e = 69
|
||||
f = 70
|
||||
g = 71
|
||||
h = 72
|
||||
i = 73
|
||||
j = 74
|
||||
k = 75
|
||||
l = 76
|
||||
m = 77
|
||||
n = 78
|
||||
o = 79
|
||||
p = 80
|
||||
q = 81
|
||||
r = 82
|
||||
s = 83
|
||||
t = 84
|
||||
u = 85
|
||||
v = 86
|
||||
w = 87
|
||||
x = 88
|
||||
y = 89
|
||||
z = 90
|
||||
left_bracket = 91 //[
|
||||
backslash = 92 //\
|
||||
right_bracket = 93 //]
|
||||
grave_accent = 96 //`
|
||||
world_1 = 161 // non-us #1
|
||||
world_2 = 162 // non-us #2
|
||||
escape = 256
|
||||
enter = 257
|
||||
tab = 258
|
||||
backspace = 259
|
||||
insert = 260
|
||||
delete = 261
|
||||
right = 262
|
||||
left = 263
|
||||
down = 264
|
||||
up = 265
|
||||
page_up = 266
|
||||
page_down = 267
|
||||
home = 268
|
||||
end = 269
|
||||
caps_lock = 280
|
||||
scroll_lock = 281
|
||||
num_lock = 282
|
||||
print_screen = 283
|
||||
pause = 284
|
||||
f1 = 290
|
||||
f2 = 291
|
||||
f3 = 292
|
||||
f4 = 293
|
||||
f5 = 294
|
||||
f6 = 295
|
||||
f7 = 296
|
||||
f8 = 297
|
||||
f9 = 298
|
||||
f10 = 299
|
||||
f11 = 300
|
||||
f12 = 301
|
||||
f13 = 302
|
||||
f14 = 303
|
||||
f15 = 304
|
||||
f16 = 305
|
||||
f17 = 306
|
||||
f18 = 307
|
||||
f19 = 308
|
||||
f20 = 309
|
||||
f21 = 310
|
||||
f22 = 311
|
||||
f23 = 312
|
||||
f24 = 313
|
||||
f25 = 314
|
||||
kp_0 = 320
|
||||
kp_1 = 321
|
||||
kp_2 = 322
|
||||
kp_3 = 323
|
||||
kp_4 = 324
|
||||
kp_5 = 325
|
||||
kp_6 = 326
|
||||
kp_7 = 327
|
||||
kp_8 = 328
|
||||
kp_9 = 329
|
||||
kp_decimal = 330
|
||||
kp_divide = 331
|
||||
kp_multiply = 332
|
||||
kp_subtract = 333
|
||||
kp_add = 334
|
||||
kp_enter = 335
|
||||
kp_equal = 336
|
||||
left_shift = 340
|
||||
left_control = 341
|
||||
left_alt = 342
|
||||
left_super = 343
|
||||
right_shift = 344
|
||||
right_control = 345
|
||||
right_alt = 346
|
||||
right_super = 347
|
||||
menu = 348
|
||||
}
|
||||
|
||||
// TouchToolType is an Android specific 'tool type' enum for touch events.
|
||||
// This lets the application check what type of input device was used for touch events.
|
||||
// NOTE: the values must remain in sync with the corresponding Android SDK type, so don't change those.
|
||||
// See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN
|
||||
pub enum TouchToolType {
|
||||
unknown
|
||||
finger
|
||||
stylus
|
||||
mouse
|
||||
eraser
|
||||
palm
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
@[has_globals]
|
||||
module sapp
|
||||
|
||||
import sokol.gfx
|
||||
import sokol.memory
|
||||
|
||||
// Android needs a global reference to `g_desc`
|
||||
__global g_desc C.sapp_desc
|
||||
|
||||
pub fn create_desc() gfx.Desc {
|
||||
return gfx.Desc{
|
||||
environment: glue_environment()
|
||||
image_pool_size: 1000
|
||||
}
|
||||
}
|
||||
|
||||
// create_default_pass creates a default `gfx.Pass` compatible with `sapp` and `sokol.gfx.begin_pass/1`.
|
||||
pub fn create_default_pass(action gfx.PassAction) gfx.Pass {
|
||||
return gfx.Pass{
|
||||
action: action
|
||||
swapchain: glue_swapchain()
|
||||
}
|
||||
}
|
||||
|
||||
// sapp_to_gfx_pixelformat converts an sapp_pixel_format int to a gfx.PixelFormat.
|
||||
// The sapp and gfx pixel format enums have different integer values, so a direct
|
||||
// cast is incorrect.
|
||||
fn sapp_to_gfx_pixelformat(sapp_fmt int) gfx.PixelFormat {
|
||||
// sapp_pixel_format: _DEFAULT=0, NONE=1, RGBA8=2, SRGB8A8=3, BGRA8=4, SBGRA8=5, DEPTH=6, DEPTH_STENCIL=7
|
||||
return match sapp_fmt {
|
||||
1 { gfx.PixelFormat.none }
|
||||
2 { gfx.PixelFormat.rgba8 }
|
||||
3 { gfx.PixelFormat.srgb8a8 }
|
||||
4 { gfx.PixelFormat.bgra8 }
|
||||
5 { gfx.PixelFormat.bgra8 } // sbgra8 has no gfx equivalent, use bgra8
|
||||
6 { gfx.PixelFormat.depth }
|
||||
7 { gfx.PixelFormat.depth_stencil }
|
||||
else { gfx.PixelFormat.none }
|
||||
}
|
||||
}
|
||||
|
||||
// glue_environment returns a `gfx.Environment` compatible for use with `sapp` specific `gfx.Pass`es.
|
||||
// The retuned `gfx.Environment` can be used when rendering via `sapp`.
|
||||
// See also: documentation at the top of thirdparty/sokol/sokol_gfx.h
|
||||
pub fn glue_environment() gfx.Environment {
|
||||
sapp_env := C.sapp_get_environment()
|
||||
mut env := gfx.Environment{}
|
||||
unsafe { vmemset(&env, 0, int(sizeof(env))) }
|
||||
env.defaults.color_format = sapp_to_gfx_pixelformat(sapp_env.defaults.color_format)
|
||||
env.defaults.depth_format = sapp_to_gfx_pixelformat(sapp_env.defaults.depth_format)
|
||||
env.defaults.sample_count = sapp_env.defaults.sample_count
|
||||
$if macos && !darwin_sokol_glcore33 ? {
|
||||
env.metal.device = sapp_env.metal.device
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// glue_swapchain returns a `gfx.Swapchain` compatible for use with `sapp` specific display/rendering `gfx.Pass`es.
|
||||
// The retuned `gfx.Swapchain` can be used when rendering via `sapp`.
|
||||
// See also: documentation at the top of thirdparty/sokol/sokol_gfx.h
|
||||
pub fn glue_swapchain() gfx.Swapchain {
|
||||
sapp_sc := C.sapp_get_swapchain()
|
||||
mut swapchain := gfx.Swapchain{}
|
||||
unsafe { vmemset(&swapchain, 0, int(sizeof(swapchain))) }
|
||||
swapchain.width = sapp_sc.width
|
||||
swapchain.height = sapp_sc.height
|
||||
swapchain.sample_count = sapp_sc.sample_count
|
||||
swapchain.color_format = sapp_to_gfx_pixelformat(sapp_sc.color_format)
|
||||
swapchain.depth_format = sapp_to_gfx_pixelformat(sapp_sc.depth_format)
|
||||
$if macos && !darwin_sokol_glcore33 ? {
|
||||
swapchain.metal.current_drawable = sapp_sc.metal.current_drawable
|
||||
swapchain.metal.depth_stencil_texture = sapp_sc.metal.depth_stencil_texture
|
||||
swapchain.metal.msaa_color_texture = sapp_sc.metal.msaa_color_texture
|
||||
} $else {
|
||||
swapchain.gl.framebuffer = sapp_sc.gl.framebuffer
|
||||
}
|
||||
return swapchain
|
||||
}
|
||||
|
||||
// returns true after sokol-app has been initialized
|
||||
@[inline]
|
||||
pub fn isvalid() bool {
|
||||
return C.sapp_isvalid()
|
||||
}
|
||||
|
||||
// returns the current framebuffer width in pixels
|
||||
@[inline]
|
||||
pub fn width() int {
|
||||
return C.sapp_width()
|
||||
}
|
||||
|
||||
// returns the current framebuffer height in pixels
|
||||
@[inline]
|
||||
pub fn height() int {
|
||||
return C.sapp_height()
|
||||
}
|
||||
|
||||
// color_format gets default framebuffer color pixel format
|
||||
@[inline]
|
||||
pub fn color_format() int {
|
||||
return C.sapp_color_format()
|
||||
}
|
||||
|
||||
// depth_format gets default framebuffer depth pixel format
|
||||
@[inline]
|
||||
pub fn depth_format() int {
|
||||
return C.sapp_depth_format()
|
||||
}
|
||||
|
||||
// sample_count gets default framebuffer sample count
|
||||
@[inline]
|
||||
pub fn sample_count() int {
|
||||
return C.sapp_sample_count()
|
||||
}
|
||||
|
||||
// returns true when high_dpi was requested and actually running in a high-dpi scenario
|
||||
@[inline]
|
||||
pub fn high_dpi() bool {
|
||||
return C.sapp_high_dpi()
|
||||
}
|
||||
|
||||
// returns the dpi scaling factor (window pixels to framebuffer pixels)
|
||||
@[inline]
|
||||
pub fn dpi_scale() f32 {
|
||||
return C.sapp_dpi_scale()
|
||||
}
|
||||
|
||||
// show or hide the mobile device onscreen keyboard
|
||||
@[inline]
|
||||
pub fn show_keyboard(visible bool) {
|
||||
C.sapp_show_keyboard(visible)
|
||||
}
|
||||
|
||||
// return true if the mobile device onscreen keyboard is currently shown
|
||||
@[inline]
|
||||
pub fn keyboard_shown() bool {
|
||||
return C.sapp_keyboard_shown()
|
||||
}
|
||||
|
||||
// show or hide the mouse cursor
|
||||
@[inline]
|
||||
pub fn show_mouse(visible bool) {
|
||||
C.sapp_show_mouse(visible)
|
||||
}
|
||||
|
||||
// set mouse cursor
|
||||
@[inline]
|
||||
pub fn set_mouse_cursor(cursor MouseCursor) {
|
||||
C.sapp_set_mouse_cursor(cursor)
|
||||
}
|
||||
|
||||
// show or hide the mouse cursor
|
||||
@[inline]
|
||||
pub fn mouse_shown() bool {
|
||||
return C.sapp_mouse_shown()
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn lock_mouse(locked bool) {
|
||||
C.sapp_lock_mouse(locked)
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn mouse_locked() bool {
|
||||
return C.sapp_mouse_locked()
|
||||
}
|
||||
|
||||
// return the userdata pointer optionally provided in sapp_desc
|
||||
@[inline]
|
||||
pub fn userdata() voidptr {
|
||||
return C.sapp_userdata()
|
||||
}
|
||||
|
||||
// return a copy of the sapp_desc structure
|
||||
@[inline]
|
||||
pub fn query_desc() Desc {
|
||||
return C.sapp_query_desc()
|
||||
}
|
||||
|
||||
// initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED)
|
||||
@[inline]
|
||||
pub fn request_quit() {
|
||||
C.sapp_request_quit()
|
||||
}
|
||||
|
||||
// cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received)
|
||||
@[inline]
|
||||
pub fn cancel_quit() {
|
||||
C.sapp_cancel_quit()
|
||||
}
|
||||
|
||||
// initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED)
|
||||
@[inline]
|
||||
pub fn quit() {
|
||||
C.sapp_quit()
|
||||
}
|
||||
|
||||
// call from inside event callback to consume the current event (don't forward to platform)
|
||||
@[inline]
|
||||
pub fn consume_event() {
|
||||
C.sapp_consume_event()
|
||||
}
|
||||
|
||||
// get the current frame counter (for comparison with sapp_event.frame_count)
|
||||
@[inline]
|
||||
pub fn frame_count() u64 {
|
||||
return C.sapp_frame_count()
|
||||
}
|
||||
|
||||
// get an averaged/smoothed frame duration in seconds
|
||||
@[inline]
|
||||
pub fn frame_duration() f64 {
|
||||
return C.sapp_frame_duration()
|
||||
}
|
||||
|
||||
// write string into clipboard
|
||||
@[inline]
|
||||
pub fn set_clipboard_string(str &u8) {
|
||||
C.sapp_set_clipboard_string(&char(str))
|
||||
}
|
||||
|
||||
// read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED)
|
||||
@[inline]
|
||||
pub fn get_clipboard_string() &char {
|
||||
return &char(C.sapp_get_clipboard_string())
|
||||
}
|
||||
|
||||
// special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub)
|
||||
pub fn run(desc &Desc) {
|
||||
if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } {
|
||||
unsafe {
|
||||
desc.allocator.alloc_fn = memory.salloc
|
||||
desc.allocator.free_fn = memory.sfree
|
||||
desc.allocator.user_data = voidptr(0x10005a44)
|
||||
}
|
||||
}
|
||||
if desc.logger.func == unsafe { nil } {
|
||||
unsafe {
|
||||
desc.logger.func = memory.slog
|
||||
}
|
||||
}
|
||||
g_desc = *desc
|
||||
C.sapp_run(desc)
|
||||
}
|
||||
|
||||
// HTML5: enable or disable the hardwired "Leave Site?" dialog box
|
||||
@[inline]
|
||||
pub fn html5_ask_leave_site(ask bool) {
|
||||
C.sapp_html5_ask_leave_site(ask)
|
||||
}
|
||||
|
||||
// macOS: get ARC-bridged pointer to macOS NSWindow
|
||||
@[inline]
|
||||
pub fn macos_get_window() voidptr {
|
||||
return voidptr(C.sapp_macos_get_window())
|
||||
}
|
||||
|
||||
// iOS: get ARC-bridged pointer to iOS UIWindow
|
||||
@[inline]
|
||||
pub fn ios_get_window() voidptr {
|
||||
return voidptr(C.sapp_ios_get_window())
|
||||
}
|
||||
|
||||
// Win32: get the HWND window handle
|
||||
@[inline]
|
||||
pub fn win32_get_hwnd() voidptr {
|
||||
return voidptr(C.sapp_win32_get_hwnd())
|
||||
}
|
||||
|
||||
// GL: get framebuffer object
|
||||
@[inline]
|
||||
pub fn gl_get_framebuffer() u32 {
|
||||
return C.sapp_gl_get_framebuffer()
|
||||
}
|
||||
|
||||
// X11: get display handle
|
||||
@[inline]
|
||||
pub fn x11_get_display() voidptr {
|
||||
return voidptr(C.sapp_x11_get_display())
|
||||
}
|
||||
|
||||
// X11: get window handle
|
||||
@[inline]
|
||||
pub fn x11_get_window() voidptr {
|
||||
return voidptr(C.sapp_x11_get_window())
|
||||
}
|
||||
|
||||
// Android: get native activity handle
|
||||
@[inline]
|
||||
pub fn android_get_native_activity() voidptr {
|
||||
return voidptr(C.sapp_android_get_native_activity())
|
||||
}
|
||||
|
||||
// Toggle full screen
|
||||
@[inline]
|
||||
pub fn toggle_fullscreen() {
|
||||
C.sapp_toggle_fullscreen()
|
||||
}
|
||||
|
||||
// Check if full screen rendering
|
||||
@[inline]
|
||||
pub fn is_fullscreen() bool {
|
||||
return C.sapp_is_fullscreen()
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn get_num_dropped_files() int {
|
||||
return C.sapp_get_num_dropped_files()
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn get_dropped_file_path(index int) string {
|
||||
unsafe {
|
||||
return cstring_to_vstring(C.sapp_get_dropped_file_path(index))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module sapp
|
||||
|
||||
import sokol.memory
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_allocator {
|
||||
pub mut:
|
||||
alloc_fn memory.FnAllocatorAlloc = unsafe { nil }
|
||||
free_fn memory.FnAllocatorFree = unsafe { nil }
|
||||
user_data voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_logger {
|
||||
pub mut:
|
||||
func memory.FnLogCb = unsafe { nil }
|
||||
user_data voidptr
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
module sapp
|
||||
|
||||
// EGL initialization and teardown.
|
||||
// Shared between Wayland and X11 backends.
|
||||
|
||||
fn sapp_egl_choose_config() EGLConfig {
|
||||
samples := if g_sapp_state.desc.sample_count > 1 {
|
||||
EGLint(g_sapp_state.desc.sample_count)
|
||||
} else {
|
||||
EGLint(0)
|
||||
}
|
||||
alpha_size := if g_sapp_state.desc.alpha { EGLint(8) } else { EGLint(0) }
|
||||
sample_buffers := if g_sapp_state.desc.sample_count > 1 { EGLint(1) } else { EGLint(0) }
|
||||
|
||||
config_attrs := [
|
||||
egl_surface_type,
|
||||
egl_window_bit,
|
||||
egl_renderable_type,
|
||||
egl_opengl_bit,
|
||||
egl_red_size,
|
||||
EGLint(8),
|
||||
egl_green_size,
|
||||
EGLint(8),
|
||||
egl_blue_size,
|
||||
EGLint(8),
|
||||
egl_alpha_size,
|
||||
alpha_size,
|
||||
egl_depth_size,
|
||||
EGLint(24),
|
||||
egl_stencil_size,
|
||||
EGLint(8),
|
||||
egl_sample_buffers,
|
||||
sample_buffers,
|
||||
egl_samples,
|
||||
samples,
|
||||
egl_none,
|
||||
]!
|
||||
|
||||
mut egl_configs := unsafe { [32]EGLConfig{} }
|
||||
mut config_count := EGLint(0)
|
||||
if C.eglChooseConfig(g_sapp_state.egl.display, &config_attrs[0], &egl_configs[0], 32, &config_count) == 0
|
||||
|| config_count == 0 {
|
||||
eprintln('sokol_app: EGL: no configs found')
|
||||
return egl_configs[0]
|
||||
}
|
||||
|
||||
mut config := egl_configs[0]
|
||||
for i in 0 .. int(config_count) {
|
||||
c := egl_configs[i]
|
||||
mut r := EGLint(0)
|
||||
mut g := EGLint(0)
|
||||
mut b := EGLint(0)
|
||||
mut a := EGLint(0)
|
||||
mut d := EGLint(0)
|
||||
mut s := EGLint(0)
|
||||
mut n := EGLint(0)
|
||||
if C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_red_size, &r) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_green_size, &g) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_blue_size, &b) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_alpha_size, &a) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_depth_size, &d) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_stencil_size, &s) != 0
|
||||
&& C.eglGetConfigAttrib(g_sapp_state.egl.display, c, egl_samples, &n) != 0 && r == 8
|
||||
&& g == 8 && b == 8 && a == alpha_size && d == 24 && s == 8 && n == samples {
|
||||
config = c
|
||||
break
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
fn sapp_egl_create_context(config EGLConfig) {
|
||||
gl_major := if g_sapp_state.desc.gl.major_version != 0 {
|
||||
g_sapp_state.desc.gl.major_version
|
||||
} else {
|
||||
4
|
||||
}
|
||||
gl_minor := if g_sapp_state.desc.gl.minor_version != 0 {
|
||||
g_sapp_state.desc.gl.minor_version
|
||||
} else {
|
||||
1
|
||||
}
|
||||
|
||||
ctx_attrs := [
|
||||
egl_context_major_version,
|
||||
EGLint(gl_major),
|
||||
egl_context_minor_version,
|
||||
EGLint(gl_minor),
|
||||
egl_context_opengl_profile_mask,
|
||||
egl_context_opengl_core_profile_bit,
|
||||
egl_none,
|
||||
]!
|
||||
|
||||
g_sapp_state.egl.context = C.eglCreateContext(g_sapp_state.egl.display, config, unsafe { nil },
|
||||
&ctx_attrs[0])
|
||||
if g_sapp_state.egl.context == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to create context')
|
||||
}
|
||||
}
|
||||
|
||||
fn sapp_egl_create_surface(native_window voidptr) {
|
||||
config := sapp_egl_choose_config()
|
||||
g_sapp_state.egl.config = config
|
||||
g_sapp_state.egl.surface = C.eglCreateWindowSurface(g_sapp_state.egl.display, config,
|
||||
native_window, unsafe { nil })
|
||||
if g_sapp_state.egl.surface == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to create window surface')
|
||||
}
|
||||
if g_sapp_state.egl.context == unsafe { nil } {
|
||||
sapp_egl_create_context(config)
|
||||
}
|
||||
}
|
||||
|
||||
fn sapp_egl_make_current() {
|
||||
if C.eglMakeCurrent(g_sapp_state.egl.display, g_sapp_state.egl.surface,
|
||||
g_sapp_state.egl.surface, g_sapp_state.egl.context) == 0 {
|
||||
eprintln('sokol_app: EGL: eglMakeCurrent failed')
|
||||
}
|
||||
mut fb := i32(0)
|
||||
C.glGetIntegerv(gl_framebuffer_binding, &fb)
|
||||
g_sapp_state.gl.framebuffer = u32(fb)
|
||||
C.eglSwapInterval(g_sapp_state.egl.display, EGLint(g_sapp_state.swap_interval))
|
||||
}
|
||||
|
||||
$if sokol_wayland ? {
|
||||
fn sapp_egl_init_wayland() {
|
||||
if C.eglBindAPI(egl_opengl_api) == 0 {
|
||||
eprintln('sokol_app: EGL: failed to bind OpenGL API')
|
||||
}
|
||||
|
||||
g_sapp_state.egl.display = C.eglGetDisplay(voidptr(g_sapp_state.wl.display))
|
||||
if g_sapp_state.egl.display == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to get display')
|
||||
}
|
||||
|
||||
mut major := EGLint(0)
|
||||
mut minor := EGLint(0)
|
||||
if C.eglInitialize(g_sapp_state.egl.display, &major, &minor) == 0 {
|
||||
eprintln('sokol_app: EGL: failed to initialize')
|
||||
}
|
||||
g_sapp_state.gl.major = int(major)
|
||||
g_sapp_state.gl.minor = int(minor)
|
||||
}
|
||||
}
|
||||
|
||||
fn sapp_egl_init_x11() {
|
||||
if C.eglBindAPI(egl_opengl_api) == 0 {
|
||||
eprintln('sokol_app: EGL: failed to bind OpenGL API')
|
||||
}
|
||||
|
||||
g_sapp_state.egl.display = C.eglGetDisplay(voidptr(g_sapp_state.x11.display))
|
||||
if g_sapp_state.egl.display == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to get display')
|
||||
}
|
||||
|
||||
mut major := EGLint(0)
|
||||
mut minor := EGLint(0)
|
||||
if C.eglInitialize(g_sapp_state.egl.display, &major, &minor) == 0 {
|
||||
eprintln('sokol_app: EGL: failed to initialize')
|
||||
}
|
||||
g_sapp_state.gl.major = int(major)
|
||||
g_sapp_state.gl.minor = int(minor)
|
||||
|
||||
config := sapp_egl_choose_config()
|
||||
|
||||
mut visual_id := EGLint(0)
|
||||
if C.eglGetConfigAttrib(g_sapp_state.egl.display, config, EGLint(0x302E), &visual_id) == 0 {
|
||||
eprintln('sokol_app: EGL: failed to get native visual ID')
|
||||
}
|
||||
|
||||
mut visual_info_template := C.XVisualInfo{}
|
||||
visual_info_template.visualid = VisualID(visual_id)
|
||||
|
||||
mut num_visuals := 0
|
||||
visual_info := C.XGetVisualInfo(g_sapp_state.x11.display, visual_id_mask,
|
||||
&visual_info_template, &num_visuals)
|
||||
if visual_info == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to get visual info')
|
||||
}
|
||||
|
||||
x11_create_window(visual_info.visual, visual_info.depth)
|
||||
C.XFree(visual_info)
|
||||
|
||||
g_sapp_state.egl.surface = C.eglCreateWindowSurface(g_sapp_state.egl.display, config,
|
||||
EGLNativeWindowType(g_sapp_state.x11.window), unsafe { nil })
|
||||
if g_sapp_state.egl.surface == unsafe { nil } {
|
||||
eprintln('sokol_app: EGL: failed to create window surface')
|
||||
}
|
||||
|
||||
sapp_egl_create_context(config)
|
||||
sapp_egl_make_current()
|
||||
}
|
||||
|
||||
fn sapp_egl_destroy() {
|
||||
if g_sapp_state.egl.display != unsafe { nil } {
|
||||
C.eglMakeCurrent(g_sapp_state.egl.display, unsafe { nil }, unsafe { nil }, unsafe { nil })
|
||||
|
||||
if g_sapp_state.egl.context != unsafe { nil } {
|
||||
C.eglDestroyContext(g_sapp_state.egl.display, g_sapp_state.egl.context)
|
||||
g_sapp_state.egl.context = unsafe { nil }
|
||||
}
|
||||
|
||||
if g_sapp_state.egl.surface != unsafe { nil } {
|
||||
C.eglDestroySurface(g_sapp_state.egl.display, g_sapp_state.egl.surface)
|
||||
g_sapp_state.egl.surface = unsafe { nil }
|
||||
}
|
||||
|
||||
C.eglTerminate(g_sapp_state.egl.display)
|
||||
g_sapp_state.egl.display = unsafe { nil }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
module sapp
|
||||
|
||||
#define SOKOL_VALIDATE_NON_FATAL 1
|
||||
|
||||
// returns true after sokol-app has been initialized
|
||||
fn C.sapp_isvalid() bool
|
||||
|
||||
// returns the current framebuffer width in pixels
|
||||
fn C.sapp_width() i32
|
||||
fn C.sapp_widthf() f32
|
||||
|
||||
// returns the current framebuffer height in pixels
|
||||
fn C.sapp_height() i32
|
||||
fn C.sapp_heightf() f32
|
||||
|
||||
// get default framebuffer color pixel format
|
||||
fn C.sapp_color_format() i32
|
||||
|
||||
// get default framebuffer depth pixel format
|
||||
fn C.sapp_depth_format() i32
|
||||
|
||||
// get default framebuffer sample count
|
||||
fn C.sapp_sample_count() i32
|
||||
|
||||
// returns true when high_dpi was requested and actually running in a high-dpi scenario
|
||||
fn C.sapp_high_dpi() bool
|
||||
|
||||
// returns the dpi scaling factor (window pixels to framebuffer pixels)
|
||||
fn C.sapp_dpi_scale() f32
|
||||
|
||||
// show or hide the mobile device onscreen keyboard
|
||||
fn C.sapp_show_keyboard(visible bool)
|
||||
|
||||
// return true if the mobile device onscreen keyboard is currently shown
|
||||
fn C.sapp_keyboard_shown() bool
|
||||
|
||||
// Check if full screen rendering
|
||||
fn C.sapp_is_fullscreen() bool
|
||||
|
||||
// Toggle full screen
|
||||
fn C.sapp_toggle_fullscreen()
|
||||
|
||||
// show or hide the mouse cursor
|
||||
fn C.sapp_show_mouse(visible bool)
|
||||
|
||||
// return true if the mouse cursor is shown
|
||||
fn C.sapp_mouse_shown() bool
|
||||
|
||||
// set mouse cursor
|
||||
fn C.sapp_set_mouse_cursor(cursor MouseCursor)
|
||||
|
||||
// get current mouse cursor type
|
||||
fn C.sapp_get_mouse_cursor() MouseCursor
|
||||
|
||||
// bind a custom mouse cursor image to a cursor type, returns the cursor type
|
||||
fn C.sapp_bind_mouse_cursor_image(cursor MouseCursor, desc &ImageDesc) MouseCursor
|
||||
|
||||
// unbind a custom mouse cursor image
|
||||
fn C.sapp_unbind_mouse_cursor_image(cursor MouseCursor)
|
||||
|
||||
// lock or unlock the mouse cursor
|
||||
fn C.sapp_lock_mouse(locked bool)
|
||||
|
||||
// return true if the mouse cursor is locked
|
||||
fn C.sapp_mouse_locked() bool
|
||||
|
||||
// return the userdata pointer optionally provided in sapp_desc
|
||||
fn C.sapp_userdata() voidptr
|
||||
|
||||
// return a copy of the sapp_desc structure
|
||||
fn C.sapp_query_desc() Desc
|
||||
|
||||
// initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED)
|
||||
fn C.sapp_request_quit()
|
||||
|
||||
// cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received)
|
||||
fn C.sapp_cancel_quit()
|
||||
|
||||
// intiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED)
|
||||
fn C.sapp_quit()
|
||||
|
||||
// call from inside event callback to consume the current event (don't forward to platform)
|
||||
fn C.sapp_consume_event()
|
||||
|
||||
// get the current frame counter (for comparison with sapp_event.frame_count)
|
||||
fn C.sapp_frame_count() u64
|
||||
|
||||
// get an averaged/smoothed frame duration in seconds
|
||||
fn C.sapp_frame_duration() f64
|
||||
|
||||
// write string into clipboard
|
||||
fn C.sapp_set_clipboard_string(const_str &char)
|
||||
|
||||
// read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED)
|
||||
fn C.sapp_get_clipboard_string() &char
|
||||
|
||||
// set the window title (only on desktop platforms)
|
||||
fn C.sapp_set_window_title(&char)
|
||||
|
||||
// set the window icon (only on Windows and Linux)
|
||||
fn C.sapp_set_icon(const_icon_desc &IconDesc)
|
||||
|
||||
// Get number of dropped files
|
||||
fn C.sapp_get_num_dropped_files() i32
|
||||
|
||||
// Get the file path of the dropped file
|
||||
fn C.sapp_get_dropped_file_path(i32) &char
|
||||
|
||||
// special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub)
|
||||
fn C.sapp_run(const_desc &Desc) i32
|
||||
|
||||
// get runtime environment information
|
||||
fn C.sapp_get_environment() Environment
|
||||
|
||||
// get current frame's swapchain information (call once per frame!)
|
||||
fn C.sapp_get_swapchain() Swapchain
|
||||
|
||||
// EGL: get EGLDisplay object
|
||||
fn C.sapp_egl_get_display() voidptr
|
||||
|
||||
// EGL: get EGLContext object
|
||||
fn C.sapp_egl_get_context() voidptr
|
||||
|
||||
// HTML5: enable or disable the hardwired "Leave Site?" dialog box
|
||||
fn C.sapp_html5_ask_leave_site(ask bool)
|
||||
|
||||
// HTML5: get byte size of a dropped file
|
||||
fn C.sapp_html5_get_dropped_file_size(index i32) u32
|
||||
|
||||
// macOS: get ARC-bridged pointer to macOS NSWindow
|
||||
fn C.sapp_macos_get_window() voidptr
|
||||
|
||||
// iOS: get ARC-bridged pointer to iOS UIWindow
|
||||
fn C.sapp_ios_get_window() voidptr
|
||||
|
||||
// D3D11: get pointer to IDXGISwapChain object
|
||||
fn C.sapp_d3d11_get_swap_chain() voidptr
|
||||
|
||||
// Win32: get the HWND window handle
|
||||
fn C.sapp_win32_get_hwnd() voidptr
|
||||
|
||||
// GL: get major version
|
||||
fn C.sapp_gl_get_major_version() i32
|
||||
|
||||
// GL: get minor version
|
||||
fn C.sapp_gl_get_minor_version() i32
|
||||
|
||||
// GL: return true if the context is GLES
|
||||
fn C.sapp_gl_is_gles() bool
|
||||
|
||||
// GL: get framebuffer object
|
||||
fn C.sapp_gl_get_framebuffer() u32
|
||||
|
||||
// X11: get Window
|
||||
fn C.sapp_x11_get_window() voidptr
|
||||
|
||||
// X11: get Display
|
||||
fn C.sapp_x11_get_display() voidptr
|
||||
|
||||
// Android: get native activity handle
|
||||
fn C.sapp_android_get_native_activity() voidptr
|
||||
|
||||
// V-specific: read RGBA pixels from the OpenGL framebuffer
|
||||
fn C.v_sapp_gl_read_rgba_pixels(x i32, y i32, width i32, height i32, pixels charptr)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,62 @@
|
||||
module sapp
|
||||
|
||||
// Shared state structs for the V sokol_app backends.
|
||||
// Platform-specific state (SappState, g_sapp_state, SappWayland, SappWin32, etc.)
|
||||
// lives in sapp_state_linux.v / sapp_state_windows.v respectively.
|
||||
|
||||
const ring_num_slots = 256
|
||||
const max_title_length = 128
|
||||
const mousecursor_num = 27
|
||||
|
||||
// Timing state for frame duration averaging
|
||||
struct SappTiming {
|
||||
mut:
|
||||
last f64
|
||||
accum f64
|
||||
avg f64
|
||||
spike_count int
|
||||
num int
|
||||
ring_head int
|
||||
ring_tail int
|
||||
ring_buf [ring_num_slots]f64
|
||||
}
|
||||
|
||||
// Mouse state
|
||||
struct SappMouse {
|
||||
mut:
|
||||
x f32
|
||||
y f32
|
||||
dx f32
|
||||
dy f32
|
||||
shown bool
|
||||
locked bool
|
||||
pos_valid bool
|
||||
current_cursor MouseCursor
|
||||
}
|
||||
|
||||
// Clipboard state
|
||||
struct SappClipboard {
|
||||
mut:
|
||||
enabled bool
|
||||
buf_size int
|
||||
buffer &char = unsafe { nil }
|
||||
}
|
||||
|
||||
// Drag and drop state
|
||||
struct SappDrop {
|
||||
mut:
|
||||
enabled bool
|
||||
max_files int
|
||||
max_path_length int
|
||||
num_files int
|
||||
buf_size int
|
||||
buffer &char = unsafe { nil }
|
||||
}
|
||||
|
||||
// GL state
|
||||
struct SappGl {
|
||||
mut:
|
||||
framebuffer u32
|
||||
major int
|
||||
minor int
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
@[has_globals]
|
||||
module sapp
|
||||
|
||||
// Linux-specific state structs for the V sokol_app backend.
|
||||
// Shared state (SappTiming, SappMouse, etc.) is in sapp_state.v.
|
||||
|
||||
// X11 and XKB share keypad/navigation keysyms. Keep the stateful keypad mapping in one place so
|
||||
// Num Lock can flip keypad arrows between navigation keys and keypad digits consistently.
|
||||
fn linux_translate_navigation_or_keypad_keysym(keysym u32) KeyCode {
|
||||
return match keysym {
|
||||
0xff63, 0xff9e { .insert }
|
||||
0xffff, 0xff9f { .delete }
|
||||
0xff50, 0xff95 { .home }
|
||||
0xff57, 0xff9c { .end }
|
||||
0xff55, 0xff9a { .page_up }
|
||||
0xff56, 0xff9b { .page_down }
|
||||
0xff51, 0xff96 { .left }
|
||||
0xff53, 0xff98 { .right }
|
||||
0xff52, 0xff97 { .up }
|
||||
0xff54, 0xff99 { .down }
|
||||
0xffb0 { .kp_0 }
|
||||
0xffb1 { .kp_1 }
|
||||
0xffb2 { .kp_2 }
|
||||
0xffb3 { .kp_3 }
|
||||
0xff9d, 0xffb5 { .kp_5 }
|
||||
0xffb4 { .kp_4 }
|
||||
0xffb6 { .kp_6 }
|
||||
0xffb7 { .kp_7 }
|
||||
0xffb8 { .kp_8 }
|
||||
0xffb9 { .kp_9 }
|
||||
0xffac, 0xffae { .kp_decimal }
|
||||
0xffaf { .kp_divide }
|
||||
0xffaa { .kp_multiply }
|
||||
0xffad { .kp_subtract }
|
||||
0xffab { .kp_add }
|
||||
0xff8d { .kp_enter }
|
||||
0xffbd { .kp_equal }
|
||||
else { .invalid }
|
||||
}
|
||||
}
|
||||
|
||||
// Wayland-specific state (only compiled when -d sokol_wayland is used)
|
||||
$if sokol_wayland ? {
|
||||
struct SappWayland {
|
||||
mut:
|
||||
// Core Wayland objects
|
||||
display &C.wl_display = unsafe { nil }
|
||||
registry &C.wl_registry = unsafe { nil }
|
||||
compositor &C.wl_compositor = unsafe { nil }
|
||||
surface &C.wl_surface = unsafe { nil }
|
||||
egl_window &C.wl_egl_window = unsafe { nil }
|
||||
shm &C.wl_shm = unsafe { nil }
|
||||
// XDG shell
|
||||
xdg_wm_base &C.xdg_wm_base = unsafe { nil }
|
||||
xdg_surface &C.xdg_surface = unsafe { nil }
|
||||
xdg_toplevel &C.xdg_toplevel = unsafe { nil }
|
||||
// Input
|
||||
seat &C.wl_seat = unsafe { nil }
|
||||
pointer &C.wl_pointer = unsafe { nil }
|
||||
keyboard &C.wl_keyboard = unsafe { nil }
|
||||
touch &C.wl_touch = unsafe { nil }
|
||||
seat_version u32
|
||||
// Pointer constraints
|
||||
pointer_constraints &C.zwp_pointer_constraints_v1 = unsafe { nil }
|
||||
locked_pointer &C.zwp_locked_pointer_v1 = unsafe { nil }
|
||||
relative_pointer_mgr &C.zwp_relative_pointer_manager_v1 = unsafe { nil }
|
||||
relative_pointer &C.zwp_relative_pointer_v1 = unsafe { nil }
|
||||
// XKB keyboard handling
|
||||
xkb_context &C.xkb_context = unsafe { nil }
|
||||
xkb_keymap &C.xkb_keymap = unsafe { nil }
|
||||
xkb_state &C.xkb_state = unsafe { nil }
|
||||
xkb_compose_table &C.xkb_compose_table = unsafe { nil }
|
||||
xkb_compose_state &C.xkb_compose_state = unsafe { nil }
|
||||
// Cursor
|
||||
cursor_shape_manager &C.wp_cursor_shape_manager_v1 = unsafe { nil }
|
||||
cursor_shape_device &C.wp_cursor_shape_device_v1 = unsafe { nil }
|
||||
cursor_theme &C.wl_cursor_theme = unsafe { nil }
|
||||
cursor_default &C.wl_cursor = unsafe { nil }
|
||||
cursor_surface &C.wl_surface = unsafe { nil }
|
||||
cursor_size int
|
||||
// Fractional scale
|
||||
fractional_scale_mgr &C.wp_fractional_scale_manager_v1 = unsafe { nil }
|
||||
fractional_scale &C.wp_fractional_scale_v1 = unsafe { nil }
|
||||
scale_numerator u32
|
||||
// Viewporter
|
||||
viewporter &C.wp_viewporter = unsafe { nil }
|
||||
viewport &C.wp_viewport = unsafe { nil }
|
||||
// Decorations
|
||||
decoration_manager &C.zxdg_decoration_manager_v1 = unsafe { nil }
|
||||
toplevel_decoration &C.zxdg_toplevel_decoration_v1 = unsafe { nil }
|
||||
// Clipboard / data device
|
||||
data_device_manager &C.wl_data_device_manager = unsafe { nil }
|
||||
data_device &C.wl_data_device = unsafe { nil }
|
||||
data_source &C.wl_data_source = unsafe { nil }
|
||||
data_offer &C.wl_data_offer = unsafe { nil }
|
||||
clipboard_string &char = unsafe { nil }
|
||||
clipboard_size int
|
||||
// Window state
|
||||
width int
|
||||
height int
|
||||
fb_width int
|
||||
fb_height int
|
||||
scale f32 = 1.0
|
||||
configured bool
|
||||
closed bool
|
||||
fullscreen bool
|
||||
maximized bool
|
||||
focused bool
|
||||
// Pointer state
|
||||
pointer_x f32
|
||||
pointer_y f32
|
||||
pointer_enter_serial u32
|
||||
// Key repeat
|
||||
key_repeat_rate int = 25
|
||||
key_repeat_delay int = 600
|
||||
key_repeat_timer_fd int = -1
|
||||
key_repeat_keycode u32
|
||||
}
|
||||
} $else {
|
||||
// Dummy struct for non-Wayland builds
|
||||
struct SappWayland {}
|
||||
}
|
||||
|
||||
// X11 XInput extension state
|
||||
struct SappXi {
|
||||
mut:
|
||||
available bool
|
||||
major_opcode int
|
||||
event_base int
|
||||
error_base int
|
||||
major int
|
||||
minor int
|
||||
}
|
||||
|
||||
// X11 XDnD state
|
||||
struct SappXdnd {
|
||||
mut:
|
||||
version i64
|
||||
source Window
|
||||
format Atom
|
||||
xdnd_aware Atom
|
||||
xdnd_enter Atom
|
||||
xdnd_position Atom
|
||||
xdnd_status Atom
|
||||
xdnd_action_copy Atom
|
||||
xdnd_drop Atom
|
||||
xdnd_finished Atom
|
||||
xdnd_selection Atom
|
||||
xdnd_type_list Atom
|
||||
text_uri_list Atom
|
||||
}
|
||||
|
||||
// X11-specific state
|
||||
struct SappX11 {
|
||||
mut:
|
||||
mouse_buttons u8
|
||||
display &C.Display = unsafe { nil }
|
||||
screen int
|
||||
root Window
|
||||
colormap Colormap
|
||||
window Window
|
||||
hidden_cursor Cursor
|
||||
standard_cursors [mousecursor_num]Cursor
|
||||
custom_cursors [mousecursor_num]Cursor
|
||||
window_state int
|
||||
dpi f32
|
||||
error_code u8
|
||||
// X atoms
|
||||
utf8_string Atom
|
||||
clipboard_atom Atom
|
||||
targets Atom
|
||||
wm_protocols Atom
|
||||
wm_delete_window Atom
|
||||
wm_state Atom
|
||||
net_wm_name Atom
|
||||
net_wm_icon_name Atom
|
||||
net_wm_icon Atom
|
||||
net_wm_state Atom
|
||||
net_wm_state_fullscreen Atom
|
||||
// Extensions
|
||||
xi SappXi
|
||||
xdnd SappXdnd
|
||||
// Key repeat tracking (keycodes 0..255)
|
||||
key_repeat [256]bool
|
||||
}
|
||||
|
||||
// EGL state
|
||||
struct SappEgl {
|
||||
mut:
|
||||
display EGLDisplay
|
||||
context EGLContext
|
||||
surface EGLSurface
|
||||
config EGLConfig
|
||||
}
|
||||
|
||||
// Main application state - Linux version with Wayland/X11/EGL backends
|
||||
struct SappState {
|
||||
mut:
|
||||
desc Desc
|
||||
valid bool
|
||||
fullscreen bool
|
||||
first_frame bool
|
||||
init_called bool
|
||||
cleanup_called bool
|
||||
quit_requested bool
|
||||
quit_ordered bool
|
||||
event_consumed bool
|
||||
html5_ask_leave_site bool
|
||||
onscreen_keyboard_shown bool
|
||||
window_width int
|
||||
window_height int
|
||||
framebuffer_width int
|
||||
framebuffer_height int
|
||||
sample_count int
|
||||
swap_interval int
|
||||
dpi_scale f32 = 1.0
|
||||
frame_count u64
|
||||
timing SappTiming
|
||||
event Event
|
||||
mouse SappMouse
|
||||
clipboard SappClipboard
|
||||
drop SappDrop
|
||||
// Platform-specific state
|
||||
wl SappWayland
|
||||
x11 SappX11
|
||||
egl SappEgl
|
||||
gl SappGl
|
||||
// Keycode translation table
|
||||
keycodes [max_keycodes]KeyCode
|
||||
// Window title
|
||||
window_title [max_title_length]u8
|
||||
// V patch
|
||||
v_native_render bool
|
||||
// Custom cursors
|
||||
custom_cursor_bound [mousecursor_num]bool
|
||||
}
|
||||
|
||||
// The global state instance
|
||||
__global g_sapp_state = SappState{}
|
||||
|
||||
// === Shared helper functions ===
|
||||
|
||||
// init_sapp_event initializes an event struct with common fields.
|
||||
fn init_sapp_event(event_type EventType) {
|
||||
unsafe { C.memset(&g_sapp_state.event, 0, int(sizeof(g_sapp_state.event))) }
|
||||
g_sapp_state.event.@type = event_type
|
||||
g_sapp_state.event.frame_count = g_sapp_state.frame_count
|
||||
g_sapp_state.event.mouse_button = .invalid
|
||||
g_sapp_state.event.window_width = g_sapp_state.window_width
|
||||
g_sapp_state.event.window_height = g_sapp_state.window_height
|
||||
g_sapp_state.event.framebuffer_width = g_sapp_state.framebuffer_width
|
||||
g_sapp_state.event.framebuffer_height = g_sapp_state.framebuffer_height
|
||||
g_sapp_state.event.mouse_x = g_sapp_state.mouse.x
|
||||
g_sapp_state.event.mouse_y = g_sapp_state.mouse.y
|
||||
g_sapp_state.event.mouse_dx = g_sapp_state.mouse.dx
|
||||
g_sapp_state.event.mouse_dy = g_sapp_state.mouse.dy
|
||||
}
|
||||
|
||||
// call_sapp_event dispatches the event to the user's callback.
|
||||
fn call_sapp_event(e &Event) bool {
|
||||
if !g_sapp_state.cleanup_called {
|
||||
if g_sapp_state.desc.event_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.event_cb(e)
|
||||
} else if g_sapp_state.desc.event_userdata_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.event_userdata_cb(e, g_sapp_state.desc.user_data)
|
||||
}
|
||||
}
|
||||
if g_sapp_state.event_consumed {
|
||||
g_sapp_state.event_consumed = false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sapp_call_init calls the user's init callback.
|
||||
fn sapp_call_init() {
|
||||
if g_sapp_state.desc.init_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.init_cb()
|
||||
} else if g_sapp_state.desc.init_userdata_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.init_userdata_cb(g_sapp_state.desc.user_data)
|
||||
}
|
||||
g_sapp_state.init_called = true
|
||||
}
|
||||
|
||||
// sapp_call_frame calls the user's frame callback.
|
||||
fn sapp_call_frame() {
|
||||
if g_sapp_state.desc.frame_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.frame_cb()
|
||||
} else if g_sapp_state.desc.frame_userdata_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.frame_userdata_cb(g_sapp_state.desc.user_data)
|
||||
}
|
||||
}
|
||||
|
||||
// sapp_call_cleanup calls the user's cleanup callback.
|
||||
fn sapp_call_cleanup() {
|
||||
if !g_sapp_state.cleanup_called {
|
||||
if g_sapp_state.desc.cleanup_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.cleanup_cb()
|
||||
} else if g_sapp_state.desc.cleanup_userdata_cb != unsafe { nil } {
|
||||
g_sapp_state.desc.cleanup_userdata_cb(g_sapp_state.desc.user_data)
|
||||
}
|
||||
g_sapp_state.cleanup_called = true
|
||||
}
|
||||
}
|
||||
|
||||
// sapp_do_frame handles the per-frame logic.
|
||||
fn sapp_do_frame() {
|
||||
if g_sapp_state.first_frame {
|
||||
g_sapp_state.first_frame = false
|
||||
sapp_call_init()
|
||||
}
|
||||
sapp_call_frame()
|
||||
g_sapp_state.frame_count++
|
||||
}
|
||||
|
||||
// === Timing helpers ===
|
||||
|
||||
fn sapp_timing_reset(t &SappTiming) {
|
||||
unsafe {
|
||||
mut mt := t
|
||||
mt.last = 0.0
|
||||
mt.accum = 0.0
|
||||
mt.avg = 0.0
|
||||
mt.spike_count = 0
|
||||
mt.num = 0
|
||||
mt.ring_head = 0
|
||||
mt.ring_tail = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn sapp_timing_get_avg(t &SappTiming) f64 {
|
||||
return t.avg
|
||||
}
|
||||
|
||||
// === Drop file helpers ===
|
||||
|
||||
fn sapp_dropped_file_path_ptr(index int) &char {
|
||||
assert g_sapp_state.drop.buffer != unsafe { nil }
|
||||
assert index >= 0 && index <= g_sapp_state.drop.max_files
|
||||
offset := index * g_sapp_state.drop.max_path_length
|
||||
assert offset < g_sapp_state.drop.buf_size
|
||||
return unsafe { &char(g_sapp_state.drop.buffer + offset) }
|
||||
}
|
||||
|
||||
// === State initialization ===
|
||||
|
||||
fn sapp_desc_defaults(desc &Desc) Desc {
|
||||
mut res := *desc
|
||||
if res.sample_count == 0 {
|
||||
res.sample_count = 1
|
||||
}
|
||||
if res.swap_interval == 0 {
|
||||
res.swap_interval = 1
|
||||
}
|
||||
if res.gl.major_version == 0 {
|
||||
res.gl.major_version = 4
|
||||
res.gl.minor_version = 1
|
||||
}
|
||||
if res.clipboard_size == 0 {
|
||||
res.clipboard_size = 8192
|
||||
}
|
||||
if res.max_dropped_files == 0 {
|
||||
res.max_dropped_files = 1
|
||||
}
|
||||
if res.max_dropped_file_path_length == 0 {
|
||||
res.max_dropped_file_path_length = 2048
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn sapp_init_state(desc &Desc) {
|
||||
unsafe { C.memset(&g_sapp_state, 0, int(sizeof(g_sapp_state))) }
|
||||
g_sapp_state.desc = sapp_desc_defaults(desc)
|
||||
g_sapp_state.first_frame = true
|
||||
g_sapp_state.window_width = g_sapp_state.desc.width
|
||||
g_sapp_state.window_height = g_sapp_state.desc.height
|
||||
g_sapp_state.framebuffer_width = g_sapp_state.window_width
|
||||
g_sapp_state.framebuffer_height = g_sapp_state.window_height
|
||||
g_sapp_state.sample_count = g_sapp_state.desc.sample_count
|
||||
g_sapp_state.swap_interval = g_sapp_state.desc.swap_interval
|
||||
g_sapp_state.clipboard.enabled = g_sapp_state.desc.enable_clipboard
|
||||
if g_sapp_state.clipboard.enabled {
|
||||
g_sapp_state.clipboard.buf_size = g_sapp_state.desc.clipboard_size
|
||||
g_sapp_state.clipboard.buffer = unsafe {
|
||||
&char(C.calloc(1, usize(g_sapp_state.clipboard.buf_size)))
|
||||
}
|
||||
}
|
||||
g_sapp_state.drop.enabled = g_sapp_state.desc.enable_dragndrop
|
||||
if g_sapp_state.drop.enabled {
|
||||
g_sapp_state.drop.max_files = g_sapp_state.desc.max_dropped_files
|
||||
g_sapp_state.drop.max_path_length = g_sapp_state.desc.max_dropped_file_path_length
|
||||
g_sapp_state.drop.buf_size = g_sapp_state.drop.max_files * g_sapp_state.drop.max_path_length
|
||||
g_sapp_state.drop.buffer = unsafe { &char(C.calloc(1, usize(g_sapp_state.drop.buf_size))) }
|
||||
}
|
||||
// Copy window title
|
||||
if g_sapp_state.desc.window_title != unsafe { nil }
|
||||
&& unsafe { C.strlen(g_sapp_state.desc.window_title) } > 0 {
|
||||
title := g_sapp_state.desc.window_title
|
||||
mut i := 0
|
||||
for i < max_title_length - 1 {
|
||||
ch := unsafe { title[i] }
|
||||
if ch == 0 {
|
||||
break
|
||||
}
|
||||
g_sapp_state.window_title[i] = u8(ch)
|
||||
i++
|
||||
}
|
||||
g_sapp_state.window_title[i] = 0
|
||||
} else {
|
||||
// Default title
|
||||
g_sapp_state.window_title[0] = `s`
|
||||
g_sapp_state.window_title[1] = `o`
|
||||
g_sapp_state.window_title[2] = `k`
|
||||
g_sapp_state.window_title[3] = `o`
|
||||
g_sapp_state.window_title[4] = `l`
|
||||
g_sapp_state.window_title[5] = 0
|
||||
}
|
||||
g_sapp_state.dpi_scale = 1.0
|
||||
g_sapp_state.fullscreen = g_sapp_state.desc.fullscreen
|
||||
g_sapp_state.mouse.shown = true
|
||||
}
|
||||
|
||||
fn sapp_discard_state() {
|
||||
if g_sapp_state.clipboard.enabled && g_sapp_state.clipboard.buffer != unsafe { nil } {
|
||||
unsafe { C.free(g_sapp_state.clipboard.buffer) }
|
||||
}
|
||||
if g_sapp_state.drop.enabled && g_sapp_state.drop.buffer != unsafe { nil } {
|
||||
unsafe { C.free(g_sapp_state.drop.buffer) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
module sapp
|
||||
|
||||
import sokol.memory
|
||||
|
||||
const max_touchpoints = 8
|
||||
const max_mousebuttons = 3
|
||||
const max_keycodes = 512
|
||||
const max_iconimages = 8
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_allocator {
|
||||
pub mut:
|
||||
alloc_fn memory.FnAllocatorAlloc = unsafe { nil }
|
||||
free_fn memory.FnAllocatorFree = unsafe { nil }
|
||||
user_data voidptr
|
||||
}
|
||||
|
||||
pub type Allocator = C.sapp_allocator
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_logger {
|
||||
pub mut:
|
||||
func memory.FnLogCb = unsafe { nil }
|
||||
user_data voidptr
|
||||
}
|
||||
|
||||
pub type Logger = C.sapp_logger
|
||||
|
||||
// sapp_range is a general pointer/size-pair struct for passing binary blobs into sokol_app.h
|
||||
@[typedef]
|
||||
pub struct C.sapp_range {
|
||||
pub:
|
||||
ptr voidptr
|
||||
size usize
|
||||
}
|
||||
|
||||
pub type Range = C.sapp_range
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_image_desc {
|
||||
pub:
|
||||
width int
|
||||
height int
|
||||
cursor_hotspot_x int
|
||||
cursor_hotspot_y int
|
||||
pixels Range
|
||||
}
|
||||
|
||||
pub type ImageDesc = C.sapp_image_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_icon_desc {
|
||||
pub:
|
||||
sokol_default bool
|
||||
images [max_iconimages]ImageDesc
|
||||
}
|
||||
|
||||
pub type IconDesc = C.sapp_icon_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_gl_desc {
|
||||
pub mut:
|
||||
major_version int // override GL/GLES major version
|
||||
minor_version int // override GL/GLES minor version
|
||||
}
|
||||
|
||||
pub type GlDesc = C.sapp_gl_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_win32_desc {
|
||||
pub:
|
||||
console_utf8 bool // if true, set the output console codepage to UTF-8
|
||||
console_create bool // if true, attach stdout/stderr to a new console window
|
||||
console_attach bool // if true, attach stdout/stderr to parent process
|
||||
}
|
||||
|
||||
pub type Win32Desc = C.sapp_win32_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_html5_desc {
|
||||
pub:
|
||||
canvas_selector &char = c'#canvas' // css selector of the HTML5 canvas element, default is "#canvas"
|
||||
canvas_resize bool // if true, the HTML5 canvas size is set to sapp_desc.width/height
|
||||
preserve_drawing_buffer bool // HTML5 only: whether to preserve default framebuffer content between frames
|
||||
premultiplied_alpha bool // HTML5 only: whether the rendered pixels use premultiplied alpha convention
|
||||
ask_leave_site bool // initial state of the internal html5_ask_leave_site flag
|
||||
update_document_title bool // if true, update the HTML document.title with sapp_desc.window_title
|
||||
bubble_mouse_events bool // if true, mouse events will bubble up to the web page
|
||||
bubble_touch_events bool // same for touch events
|
||||
bubble_wheel_events bool // same for wheel events
|
||||
bubble_key_events bool // if true, bubble up *all* key events to browser
|
||||
bubble_char_events bool // if true, bubble up character events to browser
|
||||
use_emsc_set_main_loop bool // if true, use emscripten_set_main_loop() instead of emscripten_request_animation_frame_loop()
|
||||
emsc_set_main_loop_simulate_infinite_loop bool // this will be passed as the simulate_infinite_loop arg to emscripten_set_main_loop()
|
||||
}
|
||||
|
||||
pub type Html5Desc = C.sapp_html5_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_ios_desc {
|
||||
pub:
|
||||
keyboard_resizes_canvas bool // if true, showing the iOS keyboard shrinks the canvas
|
||||
}
|
||||
|
||||
pub type IosDesc = C.sapp_ios_desc
|
||||
|
||||
// --- environment structs (returned by sapp_get_environment) ---
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_environment_defaults {
|
||||
pub:
|
||||
color_format int
|
||||
depth_format int
|
||||
sample_count int
|
||||
}
|
||||
|
||||
pub type EnvironmentDefaults = C.sapp_environment_defaults
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_metal_environment {
|
||||
pub:
|
||||
device voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_d3d11_environment {
|
||||
pub:
|
||||
device voidptr
|
||||
device_context voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_wgpu_environment {
|
||||
pub:
|
||||
device voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_vulkan_environment {
|
||||
pub:
|
||||
instance voidptr
|
||||
physical_device voidptr
|
||||
device voidptr
|
||||
queue voidptr
|
||||
queue_family_index u32
|
||||
}
|
||||
|
||||
pub type MetalEnvironment = C.sapp_metal_environment
|
||||
pub type D3d11Environment = C.sapp_d3d11_environment
|
||||
pub type WgpuEnvironment = C.sapp_wgpu_environment
|
||||
pub type VulkanEnvironment = C.sapp_vulkan_environment
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_environment {
|
||||
pub:
|
||||
defaults EnvironmentDefaults
|
||||
metal MetalEnvironment
|
||||
d3d11 D3d11Environment
|
||||
wgpu WgpuEnvironment
|
||||
vulkan VulkanEnvironment
|
||||
}
|
||||
|
||||
pub type Environment = C.sapp_environment
|
||||
|
||||
// --- swapchain structs (returned by sapp_get_swapchain) ---
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_metal_swapchain {
|
||||
pub:
|
||||
current_drawable voidptr
|
||||
depth_stencil_texture voidptr
|
||||
msaa_color_texture voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_d3d11_swapchain {
|
||||
pub:
|
||||
render_view voidptr
|
||||
resolve_view voidptr
|
||||
depth_stencil_view voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_wgpu_swapchain {
|
||||
pub:
|
||||
render_view voidptr
|
||||
resolve_view voidptr
|
||||
depth_stencil_view voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_vulkan_swapchain {
|
||||
pub:
|
||||
render_image voidptr
|
||||
render_view voidptr
|
||||
resolve_image voidptr
|
||||
resolve_view voidptr
|
||||
depth_stencil_image voidptr
|
||||
depth_stencil_view voidptr
|
||||
render_finished_semaphore voidptr
|
||||
present_complete_semaphore voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_gl_swapchain {
|
||||
pub:
|
||||
framebuffer u32
|
||||
}
|
||||
|
||||
pub type MetalSwapchain = C.sapp_metal_swapchain
|
||||
pub type D3d11Swapchain = C.sapp_d3d11_swapchain
|
||||
pub type WgpuSwapchain = C.sapp_wgpu_swapchain
|
||||
pub type VulkanSwapchain = C.sapp_vulkan_swapchain
|
||||
pub type GlSwapchain = C.sapp_gl_swapchain
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_swapchain {
|
||||
pub:
|
||||
width int
|
||||
height int
|
||||
sample_count int
|
||||
color_format int
|
||||
depth_format int
|
||||
metal MetalSwapchain
|
||||
d3d11 D3d11Swapchain
|
||||
wgpu WgpuSwapchain
|
||||
vulkan VulkanSwapchain
|
||||
gl GlSwapchain
|
||||
}
|
||||
|
||||
pub type Swapchain = C.sapp_swapchain
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_desc {
|
||||
pub mut:
|
||||
// these are the user-provided callbacks without user data
|
||||
init_cb fn () = unsafe { nil }
|
||||
frame_cb fn () = unsafe { nil }
|
||||
cleanup_cb fn () = unsafe { nil }
|
||||
event_cb fn (&Event) = unsafe { nil } // &sapp_event
|
||||
|
||||
user_data voidptr // these are the user-provided callbacks with user data
|
||||
init_userdata_cb fn (voidptr) = unsafe { nil }
|
||||
frame_userdata_cb fn (voidptr) = unsafe { nil }
|
||||
cleanup_userdata_cb fn (voidptr) = unsafe { nil }
|
||||
event_userdata_cb fn (&Event, voidptr) = unsafe { nil }
|
||||
|
||||
width int // the preferred width of the window / canvas
|
||||
height int // the preferred height of the window / canvas
|
||||
sample_count int // MSAA sample count
|
||||
swap_interval int // the preferred swap interval on Windows, macOS, Linux, iOS, and HTML5; Android support is not implemented yet
|
||||
high_dpi bool // whether the rendering canvas is full-resolution on HighDPI displays
|
||||
fullscreen bool // whether the window should be created in fullscreen mode
|
||||
alpha bool // whether the framebuffer should have an alpha channel (ignored on some platforms)
|
||||
window_title &char // the window title as UTF-8 encoded string
|
||||
enable_clipboard bool // enable clipboard access, default is false
|
||||
clipboard_size int // max size of clipboard content in bytes
|
||||
enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
|
||||
max_dropped_files int // max number of dropped files to process (default: 1)
|
||||
max_dropped_file_path_length int // max length in bytes of a dropped UTF-8 file path (default: 2048)
|
||||
icon IconDesc // the initial window icon to set
|
||||
allocator Allocator // optional memory allocation overrides (default: malloc/free)
|
||||
logger Logger // logging callback override (default: NO LOGGING!)
|
||||
// backend-specific options
|
||||
gl GlDesc // OpenGL specific options
|
||||
win32 Win32Desc // Win32 specific options
|
||||
html5 Html5Desc // HTML5 specific options
|
||||
ios IosDesc // iOS specific options
|
||||
// V patches
|
||||
__v_native_render bool // V patch to allow for native rendering
|
||||
min_width int // V patch to allow for min window width
|
||||
min_height int // V patch to allow for min window height
|
||||
borderless_window bool // V patch to create a window without native decorations when supported
|
||||
}
|
||||
|
||||
pub type Desc = C.sapp_desc
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_event {
|
||||
pub mut:
|
||||
frame_count u64 // current frame counter, always valid, useful for checking if two events were issued in the same frame
|
||||
type EventType // the event type, always valid
|
||||
key_code KeyCode // the virtual key code, only valid in KEY_UP, KEY_DOWN
|
||||
char_code u32 // the UTF-32 character code, only valid in CHAR events
|
||||
key_repeat bool // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR
|
||||
modifiers u32 // current modifier keys, valid in all key-, char- and mouse-events
|
||||
mouse_button MouseButton // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP
|
||||
mouse_x f32 // current horizontal mouse position in pixels, always valid except during mouse lock
|
||||
mouse_y f32 // current vertical mouse position in pixels, always valid except during mouse lock
|
||||
mouse_dx f32 // relative horizontal mouse movement since last frame, always valid
|
||||
mouse_dy f32 // relative vertical mouse movement since last frame, always valid
|
||||
scroll_x f32 // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events
|
||||
scroll_y f32 // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events
|
||||
num_touches int // number of valid items in the touches[] array
|
||||
touches [max_touchpoints]TouchPoint // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED
|
||||
window_width int // current window- and framebuffer width in pixels, always valid
|
||||
window_height int // current window- and framebuffer height in pixels, always valid
|
||||
framebuffer_width int // = window_width * dpi_scale
|
||||
framebuffer_height int // = window_height * dpi_scale
|
||||
}
|
||||
|
||||
pub type Event = C.sapp_event
|
||||
|
||||
pub fn (e &Event) str() string {
|
||||
return 'evt: frame_count=${e.frame_count}, type=${EventType(e.type)}'
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.sapp_touchpoint {
|
||||
pub:
|
||||
identifier u64
|
||||
pos_x f32
|
||||
pos_y f32
|
||||
android_tooltype TouchToolType
|
||||
changed bool
|
||||
}
|
||||
|
||||
pub type TouchPoint = C.sapp_touchpoint
|
||||
@@ -0,0 +1,69 @@
|
||||
module sapp
|
||||
|
||||
import os
|
||||
import stbi
|
||||
|
||||
#define SOKOL_VALIDATE_NON_FATAL 1
|
||||
|
||||
// v_sapp_gl_read_rgba_pixels reads pixles from the OpenGL buffer into `pixels`.
|
||||
fn C.v_sapp_gl_read_rgba_pixels(x int, y int, width int, height int, pixels charptr)
|
||||
|
||||
// screenshot takes a screenshot of the current window and
|
||||
// saves it to `path`. The format is inferred from the extension
|
||||
// of the file name in `path`.
|
||||
//
|
||||
// Supported formats are: `.png`, `.ppm`.
|
||||
pub fn screenshot(path string) ! {
|
||||
match os.file_ext(path) {
|
||||
'.png' {
|
||||
return screenshot_png(path)
|
||||
}
|
||||
'.ppm' {
|
||||
return screenshot_ppm(path)
|
||||
}
|
||||
else {
|
||||
return error(@MOD + '.' + @FN + ' currently only supports .png and .ppm files.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// screenshot_ppm takes a screenshot of the current window and
|
||||
// saves it to `path` as a .ppm file.
|
||||
@[manualfree]
|
||||
pub fn screenshot_ppm(path string) ! {
|
||||
ss := screenshot_window()
|
||||
write_rgba_to_ppm(path, ss.width, ss.height, 4, ss.pixels)!
|
||||
unsafe { ss.destroy() }
|
||||
}
|
||||
|
||||
// screenshot_png takes a screenshot of the current window and
|
||||
// saves it to `path` as a .png file.
|
||||
@[manualfree]
|
||||
pub fn screenshot_png(path string) ! {
|
||||
ss := screenshot_window()
|
||||
stbi.set_flip_vertically_on_write(true)
|
||||
stbi.stbi_write_png(path, ss.width, ss.height, 4, ss.pixels, ss.width * 4)!
|
||||
unsafe { ss.destroy() }
|
||||
}
|
||||
|
||||
// write_rgba_to_ppm writes `pixels` data in RGBA format to PPM3 format.
|
||||
fn write_rgba_to_ppm(path string, w int, h int, components int, pixels &u8) ! {
|
||||
mut f_out := os.create(path)!
|
||||
defer {
|
||||
f_out.close()
|
||||
}
|
||||
f_out.writeln('P3')!
|
||||
f_out.writeln('${w} ${h}')!
|
||||
f_out.writeln('255')!
|
||||
for i := h - 1; i >= 0; i-- {
|
||||
for j := 0; j < w; j++ {
|
||||
idx := i * w * components + j * components
|
||||
unsafe {
|
||||
r := int(pixels[idx])
|
||||
g := int(pixels[idx + 1])
|
||||
b := int(pixels[idx + 2])
|
||||
f_out.write_string('${r} ${g} ${b} ')!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,42 @@
|
||||
module sapp
|
||||
|
||||
@[heap]
|
||||
pub struct Screenshot {
|
||||
width int
|
||||
height int
|
||||
size int
|
||||
mut:
|
||||
pixels &u8 = unsafe { nil }
|
||||
}
|
||||
|
||||
@[manualfree]
|
||||
pub fn screenshot_window() &Screenshot {
|
||||
img_width := width()
|
||||
img_height := height()
|
||||
img_size := img_width * img_height * 4
|
||||
img_pixels := unsafe { &u8(malloc(img_size)) }
|
||||
C.v_sapp_gl_read_rgba_pixels(0, 0, img_width, img_height, img_pixels)
|
||||
return &Screenshot{
|
||||
width: img_width
|
||||
height: img_height
|
||||
size: img_size
|
||||
pixels: img_pixels
|
||||
}
|
||||
}
|
||||
|
||||
// free - free *only* the Screenshot pixels.
|
||||
@[unsafe]
|
||||
pub fn (mut ss Screenshot) free() {
|
||||
unsafe {
|
||||
free(ss.pixels)
|
||||
ss.pixels = &u8(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// destroy - free the Screenshot pixels,
|
||||
// then free the screenshot data structure itself.
|
||||
@[unsafe]
|
||||
pub fn (mut ss Screenshot) destroy() {
|
||||
unsafe { ss.free() }
|
||||
unsafe { free(ss) }
|
||||
}
|
||||
Reference in new issue
Block a user