Vesmir

Vesmir is an artificial universe simulation. It generates a galaxy and about thousand stars in it. Some stars have planets to form solar systems, where some planets has moons as their companions. All the stars and planet images are modeled in realtime based on their chemical composition, so the outlook of those objects are quite close how they would look in reality.

Vesmir demonstrates drawing operations, menues, some of the widgets how they interact with window, and how to bind own C functions to Ano script. Vesmir is not by any means a real space simulator, it is just an example, it should not be taken too serious.

User interface

Move the mouse over the galaxy and press <enter> on some star. View changes to that star, and if star has planets you can see some squares rounding around a cross. Squares indicates planets and cross is the star at the center of the solarsystem. Move mouse over one of the planets, press <enter> again and view changes to planet view. Backspace key navigates back to previous view. Galaxy and solar systems can be zoomed with mouse wheel (or buttons 4 and 5 to be exact), and they can be moved around by pressing mouse button 1 (that's usually the left mouse button) while moving the mouse.

In galaxy there is black holes (hard to find, but one sits exactly at the center of the galaxy), neutron stars, ordinary stars like the Sun, terrestrial planets with atmosphere like Mars, and gas giants like Saturn. Try to find another Earth! Stars also simulates nuclear fusion, so after considerably amount of time you may see a message saying some star has collapsed to neutron star or black hole. Roll the Time warp to southeast and wait.

Star and planet details can be viewed by pressing Print object button. Details are printed to standard output. Star and planet images can be saved to disk by pressing Save object button. Render object button opens star or planet image in separate window.

View reset button resets the view to its default position and sets zooming to zero. Time reset button resets the time warp setting, making clock to run at normal speed.

Compilation

Easiest way to get Vesmir running is to go to examples directory in package root, and run:

$ ./build.sh gui_vesmir

build.sh script compiles Ano script, widget definitions and menu tables to C, copies source files in place and pops up instructions what to do next. Follow them. Check also examples/README for more info.

Screenshots

Galaxy view and thousands of stars
Class K star with info printed
Ordinary neutron star
Terrestrial planet
Planet with cloudy atmosphere
Gas planet with some info printed

Preview

;
; @ANO_SCRIPT_NAME		gui_vesmir
; @ANO_SCRIPT_VERSION		0.0.4
; @ANO_SCRIPT_DESCRIPTION	Artificial universe simulation
;
; List of files to unveil if unveil() support is in use:
;
; @ANO_UNVEIL_FILES		"examples/datafiles/vesmir_appicon.tga = r", \
;				"share/detroit/fonts/NotoSans-Italic.ttf = r", \
;				"share/detroit/fonts/NotoSans-Regular.ttf = r", \
;				"/tmp = rw"
;
; @ANO_FLAGS_VAR_NAME_SUBS	[x]
; @ANO_FLAGS_VAR_WARN_UNUSED	[ ]
;
; Copyright (c) 2016-2024, Jani Salonen <salojan@goto10.co>
; All rights reserved.
;

main {
	; Global uninitialized variables
	var	[handle] hnd_win_main
	var	[handle] hnd_win_view
	var	[handle] hnd_win_preview
	var	[handle] hnd_fnt_title
	var	[handle] hnd_fnt_view
	var	[handle] hnd_fnt_selected
	var	[handle] hnd_fnt_selection

	; Global variables
	mov	img_dir ("/tmp")

	mov	hlp_txt ("LMB + mouse to move view, wheel to zoom, enter to select, backspace to previous view")
	mov	hlp_clr (# 0xff, 0xff, 0xff, 0x00)

	mov	fnt ("share/detroit/fonts/NotoSans-Regular.ttf")
	mov	fnt_clr (# 0xff, 0xd7, 0x69, 0x00)

	mov	fnt_sz_big ([uint] 14)
	mov	fnt_sz_med ([uint] 10)
	mov	fnt_sz_sml ([uint] 8)

	mov	view (0)

	mov	xpos (0)
	mov	ypos (0)
	mov	xpre (0)
	mov	ypre (0)

	mov	zoom_min (0)
	mov	zoom_max (100)
	mov	zoom_slc (10)
	mov	zoom (zoom_min)

	mov	galaxy ([ulong] 0)
	mov	star ([ulong] 0)
	mov	planet ([ulong] 0)
	mov	moon ([ulong] 0)

	mov	mess ([uint] 1)

	; Initialize windowing system
	window_init

	; Open main window
	window_open ("Vesmir", NULL, \
		[handle] @0, [uint] 1, [uint] 0, [uint] 0, \
		[int] -1, [int] -1, [uint] 800, [uint] 600, \
		NULL, NULL, NULL, NULL, NULL, NULL, \
		NULL, NULL, "cb_destroy_main", \
		NULL, NULL, NULL, "cb_open_main")
}

;
; Main window callbacks
;

callback cb_open_main (_hnd) {
	mov	hnd_win_main (_hnd)

	; Set window icon
	window_icon (hnd_win_main, \
		"examples/datafiles/vesmir_appicon.tga")

	; Open drawing window
	window_open (NULL, NULL, \
		hnd_win_main, [uint] 0, [uint] 0, [uint] 1, \
		[int] 20, [int] 180, [uint] 760, [uint] 400, \
		"cb_mainloop_draw", NULL, \
		"cb_keypress_draw", "cb_keyrelease_draw", \
		"cb_buttonpress_draw", "cb_buttonrelease_draw", \
		NULL, NULL, NULL, \
		"cb_motion_draw", NULL, NULL, "cb_open_draw")

	; Open fonts
	font_open (fnt, fnt_sz_big, [uint] 0)
	cmp	rc (INVALID)
	je	"cb_open_main_end"
	mov	hnd_fnt_title (rc)

	font_open (fnt, fnt_sz_big, [uint] 0)
	cmp	rc (INVALID)
	je	"cb_open_main_end"
	mov	hnd_fnt_view (rc)

	font_open (fnt, fnt_sz_med, [uint] 0)
	cmp	rc (INVALID)
	je	"cb_open_main_end"
	mov	hnd_fnt_selected (rc)

	font_open (fnt, fnt_sz_med, [uint] 0)
	cmp	rc (INVALID)
	je	"cb_open_main_end"
	mov	hnd_fnt_selection (rc)

	font_open (fnt, fnt_sz_sml, [uint] 0)
	cmp	rc (INVALID)
	je	"cb_open_main_end"
	mov	hnd_fnt_date (rc)

	; Pre-render some static strings to be displayed later
	font_render (hnd_fnt_selected, "UTF-8", "Selected:", \
		[uint] 0, fnt_clr)
	font_render (hnd_fnt_view, "UTF-8", "View:", \
		[uint] 0, fnt_clr)

	; Map main window
	window_map (hnd_win_main)

cb_open_main_end:
}

callback cb_destroy_main {
	window_close (hnd_win_main)

	font_close (hnd_fnt_title)
	font_close (hnd_fnt_view)
	font_close (hnd_fnt_selected)
	font_close (hnd_fnt_selection)
	font_close (hnd_fnt_date)

	exit
}

;
; Drawing window callbacks
;

callback cb_mainloop_draw {
	draw_wipe (hnd_win_view)

	cmp	view (0)
	je	"cb_mainloop_draw_galaxy"
	cmp	view (1)
	je	"cb_mainloop_draw_star"
	cmp	view (2)
	je	"cb_mainloop_draw_planet"
	end

cb_mainloop_draw_galaxy:
	; Main loop for galaxy view
	mov	galaxy_x (xpos)
	mov	galaxy_y (ypos)
	mov	galaxy_z (zoom)

	galaxy_live (hnd_win_view, galaxy, \
		galaxy_x, galaxy_y, galaxy_z)
	galaxy_get_name (galaxy)
	mov	_name (rc)

	call	"cb_mainloop_draw_texts" (_name)
	jmp	"cb_mainloop_draw_done"

cb_mainloop_draw_star:
	; Main loop for star view
	mov	star_x (xpos)
	mov	star_y (ypos)
	mov	star_z (zoom)

	star_live (hnd_win_view, galaxy, star, \
		star_x, star_y, star_z)
	star_get_name (galaxy, star)
	mov	_name (rc)

	call	"cb_mainloop_draw_texts" (_name)
	jmp	"cb_mainloop_draw_done"

cb_mainloop_draw_planet:
	; Main loop for planet view
	mov	planet_x (xpos)
	mov	planet_y (ypos)
	mov	planet_z (zoom)

	planet_live (hnd_win_view, galaxy, star, planet, \
		planet_x, planet_y, planet_z)
	planet_get_name (galaxy, star, planet)
	mov	_name (rc)

	call	"cb_mainloop_draw_texts" (_name)
	jmp	"cb_mainloop_draw_done"

cb_mainloop_draw_done:
	; Zoom view slowly back to its base position
	cmp	zoom (zoom_min)
	jbe	"cb_mainloop_draw_end"
	sub	zoom (0.0005)

cb_mainloop_draw_end:
}

function cb_mainloop_draw_texts (_object) {
	universe_chk_message
	cmp	mess (rc)
	je	"cb_mainloop_draw_texts_ok"
	mov	mess (rc)

	universe_get_message
	mov	_mess (rc)

	widget_update_label (hnd_win_main, "help", \
		_mess, NULL, fnt, 0, 0, 0, hlp_clr)
	widget_refresh (hnd_win_main)

cb_mainloop_draw_texts_ok:
	universe_get_date
	mov	_date (rc)

	font_render (hnd_fnt_date, "UTF-8", _date, [uint] 0, fnt_clr)
	font_render (hnd_fnt_title, "UTF-8", _object, [uint] 0, fnt_clr)

	font_get_width (hnd_fnt_date)
	mov	_w (rc)
	font_get_height (hnd_fnt_date)
	mov	_h (rc)

	draw_text (hnd_win_view, hnd_fnt_title, \
		[int] 80, [int] 80)
	draw_text (hnd_win_view, hnd_fnt_view, \
		[int] 20, [int] 80)
	draw_text (hnd_win_view, hnd_fnt_selected, \
		[int] 20, [int] 120)
	draw_text (hnd_win_view, hnd_fnt_selection, \
		[int] 80, [int] 120)
	draw_text (hnd_win_view, hnd_fnt_date, \
		[int] 760 - _w, [int] 400 - _h)
}

callback cb_keypress_draw (x, y, _x_root, _y_root, _state, _keycode, _key) {
	cmp	_keycode (6)
	je	"cb_keypress_draw_enter"
	cmp	_keycode (2)
	je	"cb_keypress_draw_backspace"
	end

cb_keypress_draw_enter:
	cmp	view (0)
	je	"cb_keypress_draw_enter_galaxy"
	cmp	view (1)
	je	"cb_keypress_draw_enter_star"
	end

cb_keypress_draw_enter_galaxy:
	; Switch from galaxy to star view
	call	"cb_keypress_draw_enter_switch" (1)
	end

cb_keypress_draw_enter_star:
	; Switch from star to planet view
	call	"cb_keypress_draw_enter_switch" (2)
	end

cb_keypress_draw_backspace:
	cmp	view (1)
	je	"cb_keypress_draw_backspace_star"
	cmp	view (2)
	je	"cb_keypress_draw_backspace_planet"
	end

cb_keypress_draw_backspace_star:
	; Switch from star to galaxy view
	call	"cb_keypress_draw_backspace_switch" \
		(galaxy_x, galaxy_y, galaxy_z, 0)
	end

cb_keypress_draw_backspace_planet:
	; Switch from planet to star view
	call	"cb_keypress_draw_backspace_switch" \
		(star_x, star_y, star_z, 1)
}

function cb_keypress_draw_enter_switch (_switch) {
	mov	xpos (0)
	mov	ypos (0)
	mov	xpre (0)
	mov	ypre (0)
	mov	zoom (zoom_min)
	mov	view (_switch)

	widget_update_label (hnd_win_main, "help", \
		hlp_txt, NULL, fnt, 0, 0, 0, hlp_clr)
	widget_refresh (hnd_win_main)
}

function cb_keypress_draw_backspace_switch (_switch_x, _switch_y, _switch_z, _switch) {
	mov	xpos (_switch_x)
	mov	ypos (_switch_y)
	mov	zoom (_switch_z)
	mov	view (_switch)

	widget_update_label (hnd_win_main, "help", \
		hlp_txt, NULL, fnt, 0, 0, 0, hlp_clr)
	widget_refresh (hnd_win_main)
}

callback cb_keyrelease_draw (x, y, _x_root, _y_root, _state, _keycode, _key) {
}

callback cb_buttonpress_draw (x, y, _x_root, _y_root, _state, _button) {
	cmp	_button (1)
	je	"cb_buttonpress_draw_zoom_in"
	cmp	_button (5)
	je	"cb_buttonpress_draw_zoom_in"
	cmp	_button (3)
	je	"cb_buttonpress_draw_zoom_out"
	cmp	_button (4)
	je	"cb_buttonpress_draw_zoom_out"

	; Clear previous pointer position for motion event
	mov	xpre (0)
	mov	ypre (0)
	end

cb_buttonpress_draw_zoom_in:
	; Zoom view inwards
	cmp	zoom (zoom_max)
	jae	"cb_buttonpress_draw_zoom_in_end"
	mov	_i (zoom_max)
	sub	_i (zoom)
	div	_i (zoom_slc)
	add	zoom (_i)

cb_buttonpress_draw_zoom_in_end:
	end

cb_buttonpress_draw_zoom_out:
	; Zoom view outwards
	cmp	zoom (zoom_min)
	jbe	"cb_buttonpress_draw_zoom_out_end"
	mov	_o (zoom_max)
	sub	_o (zoom)
	div	_o (zoom_slc)
	sub	zoom (_o)

cb_buttonpress_draw_zoom_out_end:
}

callback cb_buttonrelease_draw (x, y, _x_root, _y_root, _state, _button) {
}

callback cb_motion_draw (x, y, _x_root, _y_root, _state, _detail) {
	; 256 = Button1Mask, 1 << 8
	cmp	_state (256)
	jne	"cb_motion_draw_view"

	; Button 1 pressed with mouse motion, move view
	cmp	xpre (0)
	jne	"cb_motion_draw_a"
	mov	xpre (x)

cb_motion_draw_a:
	cmp	ypre (0)
	jne	"cb_motion_draw_b"
	mov	ypre (y)

cb_motion_draw_b:
	mov	_x (x)
	mov	_y (y)
	sub	_x (xpre)
	sub	_y (ypre)
	mul	_x (1.5)
	mul	_y (1.5)
	add	xpos (_x)
	add	ypos (_y)
	mov	xpre (x)
	mov	ypre (y)
	end

cb_motion_draw_view:
	; Select object closest to mouse pointer
	cmp	view (0)
	je	"cb_motion_draw_view_galaxy"
	cmp	view (1)
	je	"cb_motion_draw_view_star"
	cmp	view (2)
	je	"cb_motion_draw_view_planet"
	end

cb_motion_draw_view_galaxy:
	; Get the closest star
	galaxy_live_closest (galaxy, x, y)
	cmp	rc (0)
	je	"cb_motion_draw_view_galaxy_end"
	mov	star (rc)
	dec	star

	; Get the closest star name and render it
	star_get_name (galaxy, star)
	mov	_name (rc)

	font_render (hnd_fnt_selection, "UTF-8", _name, [uint] 0, fnt_clr)

cb_motion_draw_view_galaxy_end:
	end

cb_motion_draw_view_star:
	; Get the closest planet
	star_live_closest (galaxy, star, x, y)
	cmp	rc (0)
	je	"cb_motion_draw_view_star_end"
	mov	planet (rc)
	dec	planet

	; Get the closest planet name and render it
	planet_get_name (galaxy, star, planet)
	mov	_name (rc)

	font_render (hnd_fnt_selection, "UTF-8", _name, [uint] 0, fnt_clr)

cb_motion_draw_view_star_end:
	end

cb_motion_draw_view_planet:
	; Get the closest moon
	planet_live_closest (galaxy, star, planet, x, y)
	cmp	rc (0)
	je	"cb_motion_draw_view_planet_end"
	mov	moon (rc)
	dec	moon

	; Get the closest moon name and render it
	satellite_get_name (galaxy, star, planet, moon)
	mov	_name (rc)

	font_render (hnd_fnt_selection, "UTF-8", _name, [uint] 0, fnt_clr)

cb_motion_draw_view_planet_end:
}

callback cb_open_draw (_hnd) {
	mov	hnd_win_view (_hnd)

	; Map drawing window
	window_map (hnd_win_view)
}

;
; Preview window callbacks
;

callback cb_open_preview (_hnd) {
	mov	hnd_win_preview (_hnd)

	cmp	view (1)
	jne	"cb_open_preview_planet"
	star_render (hnd_win_preview, galaxy, star)
	jmp	"cb_open_preview_end"

cb_open_preview_planet:
	cmp	view (2)
	jne	"cb_open_preview_end"
	planet_render (hnd_win_preview, galaxy, star, planet)
	jmp	"cb_open_preview_end"

cb_open_preview_end:
	; Map preview window
	window_map (hnd_win_preview)
}

callback cb_destroy_preview {
	window_close (hnd_win_preview)

	mov	hnd_win_preview (INVALID)
}

;
; Widget callbacks
;

callback cb_push_renderobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
	cmp	view (0)
	je	"cb_push_renderobject_galaxy"
	cmp	view (1)
	je	"cb_push_renderobject_star"
	cmp	view (2)
	je	"cb_push_renderobject_planet"
	end

cb_push_renderobject_galaxy:
	universe_set_message \
		("Rendering galaxy is not supported")
	end

cb_push_renderobject_star:
	; Open preview window for star
	call	"cb_push_renderobject_open" \
		("Vesmir star preview")
	end

cb_push_renderobject_planet:
	; Open preview window for planet
	call	"cb_push_renderobject_open" \
		("Vesmir planet preview")
}

function cb_push_renderobject_open (_title) {
	cmp	hnd_win_preview (INVALID)
	je	"cb_push_renderobject_ok"
	window_close (hnd_win_preview)

cb_push_renderobject_ok:
	window_open (_title, NULL, \
		[handle] @0, [uint] 2, [uint] 0, [uint] 0, \
		[int] -1, [int] -1, [uint] 256, [uint] 256, \
		NULL, NULL, NULL, NULL, NULL, NULL, \
		NULL, NULL, "cb_destroy_preview", \
		NULL, NULL, NULL, "cb_open_preview")
}

callback cb_push_printobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
	cmp	view (0)
	je	"cb_push_printobject_galaxy"
	cmp	view (1)
	je	"cb_push_printobject_star"
	cmp	view (2)
	je	"cb_push_printobject_planet"
	end

cb_push_printobject_galaxy:
	; Print galaxy details
	galaxy_print (galaxy)
	end

cb_push_printobject_star:
	; Print star details
	star_print (galaxy, star)
	end

cb_push_printobject_planet:
	; Print planet details
	planet_print (galaxy, star, planet)
}

callback cb_push_saveobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
	cmp	view (0)
	je	"cb_push_saveobject_galaxy"
	cmp	view (1)
	je	"cb_push_saveobject_star"
	cmp	view (2)
	je	"cb_push_saveobject_planet"
	end

cb_push_saveobject_galaxy:
	universe_set_message ("Saving galaxy is not supported")
	end

cb_push_saveobject_star:
	; Save star image
	star_store (galaxy, star, img_dir)
	end

cb_push_saveobject_planet:
	; Save planet image
	planet_store (galaxy, star, planet, img_dir)
}

callback cb_push_viewreset (_widget_name, _widget_id, _min, _max, _steps, _step) {
	; Reset the view
	mov	xpos (0)
	mov	ypos (0)
	mov	zoom (zoom_min)
}

callback cb_push_timereset (_widget_name, _widget_id, _min, _max, _steps, _step) {
	; Reset the clock
	clock_set_multiplier (1)
	widget_step_set (hnd_win_main, "time_warp", [uint] 1)
}

callback cb_turn_timewarp (_widget_name, _widget_id, _min, _max, _steps, _step) {
	; Speed up the universe
	mov	_mul (((_step * _step) * _step) * _step)
	clock_set_multiplier (_mul)
}

;
; Menu item callbacks
;

callback cb_file_exit (_item, _position, _tag, _flag) {
	jmp	"cb_destroy_main"
}

callback cb_object_render (_item, _position, _tag, _flag) {
	mov	_msg ("Not implemented, use widgets instead")
	dump	_msg
}

callback cb_object_print (_item, _position, _tag, _flag) {
	mov	_msg ("Not implemented, use widgets instead")
	dump	_msg
}

callback cb_object_save (_item, _position, _tag, _flag) {
	mov	_msg ("Not implemented, use widgets instead")
	dump	_msg
}

callback cb_time_reset (_item, _position, _tag, _flag) {
	mov	_msg ("Not implemented, use widgets instead")
	dump	_msg
}

callback cb_time_set (_item, _position, _tag, _flag) {
	mov	_msg ("Not implemented, use widgets instead")
	dump	_msg
}
;
; Copyright (c) 2016-2024, Jani Salonen <salojan at goto10 piste co>
; All rights reserved.
;
; @WIDGET_USE_BOUNDING_BOXES no
;

window "1" {
	set "0" {
		widget "background" {
			name		"background"
			state		enable

			image		"examples/datafiles/background.tga"
		}

		widget "label" {
			name		"help"
			state		enable

			position	x = 20, y = 580

			label		"LMB + mouse to move view, wheel to zoom, enter to select, backspace to previous view", \
					charset = UTF-8, size = 8, \
					color = rgb:#ffffff, \
					justification = left
		}

		widget "pushbutton_2"
			name		"render_object"
			state		enable
			type		3

			position	x = 40, y = 100

			title		"Render object", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -10

			sticky		no

			action		push = cb_push_renderobject
		}

		widget "pushbutton_2" {
			name		"print_object"
			state		enable
			type		3

			position	x = 160, y = 100

			title		"Print object", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -10

			sticky		no

			action		push = cb_push_printobject
		}

		widget "pushbutton_2" {
			name		"save_object"
			state		enable
			type		3

			position	x = 280, y = 100

			title		"Save object", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -10

			sticky		no

			action		push = cb_push_saveobject
		}

		widget "pushbutton_2" {
			name		"view_reset"
			state		enable
			type		1

			position	x = 440, y = 100

			title		"View reset", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -10

			sticky		no

			action		push = cb_push_viewreset
		}

		widget "pushbutton_2" {
			name		"time_reset"
			state		enable
			type		1

			position	x = 560, y = 100

			title		"Time reset", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -10

			sticky		no

			action		push = cb_push_timereset
		}

		widget "turnswitch" {
			name		"time_warp"
			state		enable
			type		1

			position	x = 670, y = 60

			angle		min = 5, max = 355
			steps		start = 1, total = 1000

			title		"Time warp", \
					charset = UTF-8, size = 10, \
					color = rgb:#ffffff, \
					justification = center, \
					position = above, x = 0, y = -5

			trigger		constant

			action		turn = cb_turn_timewarp
		}
	}
}
;
; Copyright (c) 2016-2024, Jani Salonen <salojan at goto10 piste co>
; All rights reserved.
;

;
; container_type "name" {
;
window "1" {
	;
	; menu "name" {									optional_icon
	;
	menu "File" {									examples/datafiles/vesmir_menu_1.tga
		;
		; type "name",		id,	callback_func,		flags,		optional_icon
		;
		item "Exit",		1,	cb_file_exit,		none,		none
	}

	menu "Object" {
		item "Render",		10,	cb_object_render,	none,		none
		item "Print",		11,	cb_object_print,	none,		none
		item "Save",		12,	cb_object_save,		none,		none
	}

	menu "Time" {
		item "Reset",		20,	cb_time_reset,		none,		none

		delim

		radio "Increase 10%",	30,	cb_time_set,		none,		none
		radio "Increase 20%",	30,	cb_time_set,		none,		none
		radio "Increase 30%",	30,	cb_time_set,		none,		none
		radio "Increase 40%",	30,	cb_time_set,		none,		none
		radio "Increase 50%",	30,	cb_time_set,		none,		none
		radio "Increase 60%",	30,	cb_time_set,		none,		none
		radio "Increase 70%",	30,	cb_time_set,		none,		none
		radio "Increase 80%",	30,	cb_time_set,		none,		none
		radio "Increase 90%",	30,	cb_time_set,		none,		none
		radio "Increase 100%",	30,	cb_time_set,		none,		none
	}
}

Copyright © 2024, Jani Salonen <salojan at goto10 piste co>. Piste is finnish word and means dot. All rights reserved.