Skip to content

Commit bbb174e

Browse files
committed
gg, examples: add optional update_fn: fn (dt f32, ctx &gg.Context) to gg.Context and gg.Config . Pass consistently the current GG context to *all* callback functions, instead of nil.
Previously, when the user_data field: was not set explicitly in the gg.new_context() call, it was still passed to *some* of the callbacks, which was harder to understand, and hindered simple GG apps. Now, when the user_data field is not set explicitly, all gg callbacks will receive a pointer to the current gg context, instead of nil.
1 parent a3f283f commit bbb174e

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

examples/gg/moving_square.v

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import gg
2+
3+
struct App {
4+
mut:
5+
x f64 = 100.0
6+
y f64 = 100.0
7+
}
8+
9+
mut app := &App{}
10+
gg.start(
11+
window_title: 'Moving Square'
12+
width: 640
13+
height: 480
14+
update_fn: fn [mut app] (dt f32, ctx &gg.Context) {
15+
println(' frame: ${ctx.frame:6} | dt: ${dt:9.6f}s')
16+
if ctx.pressed_keys[gg.KeyCode.right] {
17+
app.x = app.x + 200 * dt
18+
}
19+
if ctx.pressed_keys[gg.KeyCode.left] {
20+
app.x = app.x - 200 * dt
21+
}
22+
if ctx.pressed_keys[gg.KeyCode.down] {
23+
app.y = app.y + 200 * dt
24+
}
25+
if ctx.pressed_keys[gg.KeyCode.up] {
26+
app.y = app.y - 200 * dt
27+
}
28+
}
29+
frame_fn: fn [mut app] (ctx &gg.Context) {
30+
ctx.begin()
31+
ctx.draw_rect_filled(int(app.x), int(app.y), 50, 50, gg.red)
32+
ctx.end()
33+
}
34+
)

vlib/gg/gg.c.v

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ pub:
101101
cleanup_fn FNCb = unsafe { nil } // Called once, after Sokol determines that the application is finished/closed. Put your app specific cleanup/free actions here.
102102
fail_fn FNFail = unsafe { nil } // Called once per Sokol error/log message. TODO: currently it does nothing with latest Sokol, reimplement using Sokol's new sapp_logger APIs.
103103

104+
update_fn FNUpdate = unsafe { nil } // Called once at the start of each frame, so usually ~60 times a second. The first argument is the delta `dt` time passed, since the *previous* update call (in seconds).
105+
104106
event_fn FNEvent = unsafe { nil } // Called once per each user initiated event, received by Sokol/GG.
105107
on_event FNEvent2 = unsafe { nil } // Called once per each user initiated event, received by Sokol/GG. Same as event_fn, just the parameter order is different. TODO: deprecate this, in favor of event_fn
106108
quit_fn FNEvent = unsafe { nil } // Called when the user closes the app window.
@@ -202,7 +204,10 @@ pub mut:
202204
font_inited bool
203205
ui_mode bool // do not redraw everything 60 times/second, but only when the user requests
204206
frame u64 // the current frame counted from the start of the application; always increasing
205-
timer time.StopWatch
207+
//
208+
timer time.StopWatch // starts right after new_context, and can be controlled/stopped/restarted in whatever way the user wants.
209+
update_timer time.StopWatch // measures how much time has passed since the start of the frame.
210+
// Note: when there is an update_fn, this timer is reset by GG itself, at the start of each frame.
206211

207212
mbtn_mask u8
208213
mouse_buttons MouseButtons // typed version of mbtn_mask; easier to use for user programs
@@ -287,6 +292,7 @@ fn gg_init_sokol_window(user_data voidptr) {
287292
ctx.pipeline.init_pipeline()
288293

289294
ctx.timer = time.new_stopwatch()
295+
ctx.update_timer = time.new_stopwatch()
290296
if ctx.config.init_fn != unsafe { nil } {
291297
$if android {
292298
// NOTE on Android sokol can emit resize events *before* the init function is
@@ -348,6 +354,11 @@ fn gg_frame_fn(mut ctx Context) {
348354
return
349355
}
350356
}
357+
if ctx.config.update_fn != unsafe { nil } {
358+
dt := ctx.update_timer.elapsed().seconds()
359+
ctx.update_timer.restart()
360+
ctx.config.update_fn(f32(dt), ctx.user_data)
361+
}
351362
ctx.config.frame_fn(ctx.user_data)
352363
ctx.needs_refresh = false
353364
}
@@ -402,66 +413,66 @@ fn gg_event_fn(ce voidptr, user_data voidptr) {
402413
ctx.pressed_keys_edge[key_idx] = prev != next
403414
}
404415
if ctx.config.event_fn != unsafe { nil } {
405-
ctx.config.event_fn(e, ctx.config.user_data)
416+
ctx.config.event_fn(e, ctx.user_data)
406417
} else if ctx.config.on_event != unsafe { nil } {
407-
ctx.config.on_event(ctx.config.user_data, e)
418+
ctx.config.on_event(ctx.user_data, e)
408419
}
409420
match e.typ {
410421
.mouse_move {
411422
if ctx.config.move_fn != unsafe { nil } {
412-
ctx.config.move_fn(e.mouse_x, e.mouse_y, ctx.config.user_data)
423+
ctx.config.move_fn(e.mouse_x, e.mouse_y, ctx.user_data)
413424
}
414425
}
415426
.mouse_down {
416427
if ctx.config.click_fn != unsafe { nil } {
417-
ctx.config.click_fn(e.mouse_x, e.mouse_y, e.mouse_button, ctx.config.user_data)
428+
ctx.config.click_fn(e.mouse_x, e.mouse_y, e.mouse_button, ctx.user_data)
418429
}
419430
}
420431
.mouse_up {
421432
if ctx.config.unclick_fn != unsafe { nil } {
422-
ctx.config.unclick_fn(e.mouse_x, e.mouse_y, e.mouse_button, ctx.config.user_data)
433+
ctx.config.unclick_fn(e.mouse_x, e.mouse_y, e.mouse_button, ctx.user_data)
423434
}
424435
}
425436
.mouse_leave {
426437
if ctx.config.leave_fn != unsafe { nil } {
427-
ctx.config.leave_fn(e, ctx.config.user_data)
438+
ctx.config.leave_fn(e, ctx.user_data)
428439
}
429440
}
430441
.mouse_enter {
431442
if ctx.config.enter_fn != unsafe { nil } {
432-
ctx.config.enter_fn(e, ctx.config.user_data)
443+
ctx.config.enter_fn(e, ctx.user_data)
433444
}
434445
}
435446
.mouse_scroll {
436447
if ctx.config.scroll_fn != unsafe { nil } {
437-
ctx.config.scroll_fn(e, ctx.config.user_data)
448+
ctx.config.scroll_fn(e, ctx.user_data)
438449
}
439450
}
440451
.key_down {
441452
if ctx.config.keydown_fn != unsafe { nil } {
442-
ctx.config.keydown_fn(e.key_code, unsafe { Modifier(e.modifiers) }, ctx.config.user_data)
453+
ctx.config.keydown_fn(e.key_code, unsafe { Modifier(e.modifiers) }, ctx.user_data)
443454
}
444455
}
445456
.key_up {
446457
if ctx.config.keyup_fn != unsafe { nil } {
447-
ctx.config.keyup_fn(e.key_code, unsafe { Modifier(e.modifiers) }, ctx.config.user_data)
458+
ctx.config.keyup_fn(e.key_code, unsafe { Modifier(e.modifiers) }, ctx.user_data)
448459
}
449460
}
450461
.char {
451462
if ctx.config.char_fn != unsafe { nil } {
452-
ctx.config.char_fn(e.char_code, ctx.config.user_data)
463+
ctx.config.char_fn(e.char_code, ctx.user_data)
453464
}
454465
}
455466
.resized {
456467
ctx.scale = dpi_scale()
457468
ctx.ft.scale = ctx.scale
458469
if ctx.config.resized_fn != unsafe { nil } {
459-
ctx.config.resized_fn(e, ctx.config.user_data)
470+
ctx.config.resized_fn(e, ctx.user_data)
460471
}
461472
}
462473
.quit_requested {
463474
if ctx.config.quit_fn != unsafe { nil } {
464-
ctx.config.quit_fn(e, ctx.config.user_data)
475+
ctx.config.quit_fn(e, ctx.user_data)
465476
}
466477
}
467478
else {
@@ -481,12 +492,12 @@ fn gg_event_fn(ce voidptr, user_data voidptr) {
481492
e.key_code = .invalid
482493
e.typ = .char
483494
if ctx.config.event_fn != unsafe { nil } {
484-
ctx.config.event_fn(e, ctx.config.user_data)
495+
ctx.config.event_fn(e, ctx.user_data)
485496
} else if ctx.config.on_event != unsafe { nil } {
486-
ctx.config.on_event(ctx.config.user_data, e)
497+
ctx.config.on_event(ctx.user_data, e)
487498
}
488499
if ctx.config.char_fn != unsafe { nil } {
489-
ctx.config.char_fn(e.char_code, ctx.config.user_data)
500+
ctx.config.char_fn(e.char_code, ctx.user_data)
490501
}
491502
}
492503
}
@@ -495,7 +506,7 @@ fn gg_event_fn(ce voidptr, user_data voidptr) {
495506
fn gg_cleanup_fn(user_data voidptr) {
496507
mut ctx := unsafe { &Context(user_data) }
497508
if ctx.config.cleanup_fn != unsafe { nil } {
498-
ctx.config.cleanup_fn(ctx.config.user_data)
509+
ctx.config.cleanup_fn(ctx.user_data)
499510
}
500511
gfx.shutdown()
501512
}
@@ -504,7 +515,7 @@ fn gg_fail_fn(msg &char, user_data voidptr) {
504515
mut ctx := unsafe { &Context(user_data) }
505516
vmsg := unsafe { tos3(msg) }
506517
if ctx.config.fail_fn != unsafe { nil } {
507-
ctx.config.fail_fn(vmsg, ctx.config.user_data)
518+
ctx.config.fail_fn(vmsg, ctx.user_data)
508519
} else {
509520
eprintln('gg error: ${vmsg}')
510521
}

vlib/gg/gg.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ pub type FNUnClick = fn (x f32, y f32, button MouseButton, data voidptr)
3232
// FNChar defines the type of a function that will be called once per character
3333
pub type FNChar = fn (c u32, data voidptr)
3434

35+
// FNUpdate defines the type of a function, that will be called at the start of each frame
36+
// with an argument `dt`, that has the passed time in seconds, since the previous update.
37+
pub type FNUpdate = fn (dt f32, data voidptr)
38+
3539
pub struct PenConfig {
3640
pub:
3741
color Color

0 commit comments

Comments
 (0)