7198810bb9c1324cc1a37f5af6c557067cfd33fe
[vg.git] / vg_settings_menu.h
1 #ifndef VG_SETTINGS_MENU_H
2 #define VG_SETTINGS_MENU_H
3
4 #include "vg.h"
5 #include "vg_imgui.h"
6
7 #ifdef VG_GAME_SETTINGS
8 static void vg_game_settings_gui( ui_rect panel ) ;
9 static void vg_game_settings_init(void);
10 #endif
11
12 struct ui_enum_opt vg_settings_vsync_enum[] = {
13 { 0, "None" },
14 { 1, "On" },
15 {-1, "Adaptive" },
16 };
17
18 struct ui_enum_opt vg_settings_quality_enum[] = {
19 { 0, "High Quality" },
20 { 1, "Faster" },
21 { 2, "Absolute Minimum" },
22 };
23
24 struct ui_enum_opt vg_settings_screen_mode_enum[] = {
25 { 0, "Fullscreen (desktop)" },
26 { 1, "Fullscreen (native)" },
27 { 2, "Floating Window" }
28 };
29
30 struct ui_enum_opt vg_settings_dsp_enum[] = {
31 { 1, "Enabled" },
32 { 0, "Disabled" },
33 };
34
35 struct {
36 struct vg_setting_ranged_i32{
37 i32 new_value, *actual_value, min, max;
38 char buf[10];
39 const char *label;
40 }
41 fps_limit;
42
43 struct vg_setting_enum{
44 i32 new_value, *actual_value;
45
46 struct ui_enum_opt *options;
47 u32 option_count;
48 const char *label;
49 }
50 vsync, quality, screenmode, audio_devices, dsp;
51 i32 temp_audio_choice;
52 }
53 static vg_settings = {
54 .fps_limit = { .label = "Fps Limit",
55 .min=24, .max=300, .actual_value = &vg.fps_limit },
56 .vsync = { .label = "Vsync",
57 .actual_value = &vg.vsync,
58 .options = vg_settings_vsync_enum, .option_count = 3 },
59 .quality = { .label = "Graphic Quality",
60 .actual_value = &vg.quality_profile,
61 .options = vg_settings_quality_enum, .option_count = 3 },
62 .screenmode = { .label = "Type",
63 .actual_value = &vg.screen_mode,
64 .options = vg_settings_screen_mode_enum, .option_count=3 },
65 .audio_devices = { .label = "Audio Device",
66 .actual_value = &vg_settings.temp_audio_choice,
67 .options = NULL, .option_count = 0 },
68 .dsp = { .label = "Audio effects (reverb etc.)",
69 .actual_value = &vg_audio.dsp_enabled,
70 .options = vg_settings_dsp_enum, .option_count=2 },
71 };
72
73 static void vg_settings_ui_draw_diff( ui_rect orig ){
74 ui_rect l,r;
75 ui_split( orig, k_ui_axis_v, -32, 0, l, r );
76 ui_text( r, "*", 1, k_ui_align_middle_center, ui_colour(k_ui_blue) );
77 }
78
79 /* i32 settings
80 * ------------------------------------------------------------------------- */
81
82 static void vg_settings_ui_int( char *buf, u32 len ){
83 for( u32 i=0, j=0; i<len; i ++ ){
84 if( ((buf[i] >= '0') && (buf[i] <= '9')) || (buf[i] == '\0') )
85 buf[j ++] = buf[i];
86 }
87 }
88
89 struct ui_textbox_callbacks static vg_settings_ui_int_callbacks = {
90 .change = vg_settings_ui_int
91 };
92
93 static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32 *prop ){
94 if( prop->new_value < prop->min ) return 0;
95 if( prop->new_value > prop->max ) return 0;
96 return 1;
97 }
98
99 static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32 *prop ){
100 if( prop->new_value != *prop->actual_value ) return 1;
101 else return 0;
102 }
103
104 static bool vg_settings_ui_ranged_i32( struct vg_setting_ranged_i32 *prop,
105 ui_rect rect ){
106 ui_rect orig;
107 rect_copy( rect, orig );
108
109 ui_textbox( rect, prop->label, prop->buf, sizeof(prop->buf),
110 1, 0, &vg_settings_ui_int_callbacks );
111 prop->new_value = atoi( prop->buf );
112
113 if( vg_settings_ranged_i32_diff( prop ) )
114 vg_settings_ui_draw_diff( orig );
115
116 bool valid = vg_settings_ranged_i32_valid( prop );
117 if( !valid ){
118 ui_rect _null, line;
119 ui_split( orig, k_ui_axis_h, -1, 0, _null, line );
120 line[1] += 3;
121
122 ui_fill( line, ui_colour( k_ui_red ) );
123 }
124
125 return valid;
126 }
127
128 static void ui_settings_ranged_i32_init( struct vg_setting_ranged_i32 *prop ){
129 vg_str tmp;
130 vg_strnull( &tmp, prop->buf, sizeof(prop->buf) );
131 vg_strcati32( &tmp, *prop->actual_value );
132 prop->new_value = *prop->actual_value;
133 }
134
135 /* enum settings
136 * ------------------------------------------------------------------------- */
137
138 static bool vg_settings_enum_diff( struct vg_setting_enum *prop ){
139 if( prop->new_value != *prop->actual_value ) return 1;
140 else return 0;
141 }
142
143 static bool vg_settings_enum( struct vg_setting_enum *prop, ui_rect rect ){
144 ui_rect orig;
145 rect_copy( rect, orig );
146
147 ui_enum( rect, prop->label,
148 prop->options, prop->option_count, &prop->new_value );
149
150 if( vg_settings_enum_diff( prop ) )
151 vg_settings_ui_draw_diff( orig );
152
153 return 1;
154 }
155
156 static void ui_settings_enum_init( struct vg_setting_enum *prop ){
157 prop->new_value = *prop->actual_value;
158 }
159
160 /* .. */
161
162 static void vg_settings_ui_header( ui_rect inout_panel, const char *name ){
163 ui_rect rect;
164 ui_standard_widget( inout_panel, rect, 2 );
165 ui_text( rect, name, 1, k_ui_align_middle_center, ui_colour(k_ui_fg+3) );
166 }
167
168
169 static bool vg_settings_apply_button( ui_rect inout_panel, bool validated ){
170 ui_rect last_row;
171 ui_px height = (vg_ui.font->glyph_height + 18) * k_ui_scale;
172 ui_split( inout_panel, k_ui_axis_h, -height, k_ui_padding,
173 inout_panel, last_row );
174
175 const char *string = "Apply";
176 if( validated ){
177 if( ui_button( last_row, string ) == 1 )
178 return 1;
179 }
180 else{
181 ui_rect rect;
182 ui_standard_widget( last_row, rect, 1 );
183 ui_fill( rect, ui_colour( k_ui_bg+1 ) );
184 ui_outline( rect, -1, ui_colour( k_ui_red ), 0 );
185
186 ui_rect t = { 0,0, ui_text_line_width( string ), 14 };
187 ui_rect_center( rect, t );
188 ui_text( t, string, 1, k_ui_align_left, ui_colour(k_ui_fg+3) );
189 }
190
191 return 0;
192 }
193
194 static void vg_settings_video_apply(void){
195 if( vg_settings_enum_diff( &vg_settings.screenmode ) ){
196 vg.screen_mode = vg_settings.screenmode.new_value;
197
198 if( (vg.screen_mode == 0) || (vg.screen_mode == 1) ){
199 SDL_DisplayMode video_mode;
200 if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ){
201 vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError());
202 }
203 else {
204 //vg.display_refresh_rate = video_mode.refresh_rate;
205 vg.window_x = video_mode.w;
206 vg.window_y = video_mode.h;
207 }
208 SDL_SetWindowSize( vg.window, vg.window_x, vg.window_y );
209 }
210
211 if( vg.screen_mode == 0 )
212 SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN_DESKTOP );
213 if( vg.screen_mode == 1 )
214 SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN );
215 if( vg.screen_mode == 2 ){
216 SDL_SetWindowFullscreen( vg.window, 0 );
217 SDL_SetWindowSize( vg.window, 1280, 720 );
218 SDL_SetWindowPosition( vg.window, 16, 16 );
219 SDL_SetWindowMinimumSize( vg.window, 1280, 720 );
220 SDL_SetWindowMaximumSize( vg.window, 4096, 4096 );
221 }
222 }
223
224 vg.fps_limit = vg_settings.fps_limit.new_value;
225 vg.quality_profile = vg_settings.quality.new_value;
226 vg.vsync = vg_settings.vsync.new_value;
227 }
228
229 static void aaaaaaaaaaaaaaaaa( ui_rect r );
230 static void vg_settings_video_gui( ui_rect panel ){
231 bool validated = 1;
232 ui_rect rq;
233 ui_standard_widget( panel, rq, 1 );
234 vg_settings_enum( &vg_settings.quality, rq );
235
236 /* FIXME */
237 #if 0
238 if( vg.vsync_feature == k_vsync_feature_error ){
239 ui_info( panel, "There was an error activating vsync feature." );
240 }
241 #endif
242
243 /* frame timing */
244 vg_settings_ui_header( panel, "Frame Timing" );
245 ui_rect duo, d0,d1;
246 ui_standard_widget( panel, duo, 1 );
247 ui_split_ratio( duo, k_ui_axis_v, 0.5f, 16, d0, d1 );
248
249 vg_settings_enum( &vg_settings.vsync, d0 );
250 validated &= vg_settings_ui_ranged_i32( &vg_settings.fps_limit, d1 );
251
252 ui_standard_widget( panel, duo, 10 );
253 aaaaaaaaaaaaaaaaa( duo );
254
255 /* window spec */
256 vg_settings_ui_header( panel, "Window Specification" );
257
258 ui_standard_widget( panel, duo, 1 );
259 vg_settings_enum( &vg_settings.screenmode, duo );
260
261 if( vg_settings_apply_button( panel, validated ) )
262 vg_settings_video_apply();
263 }
264
265 static void vg_settings_audio_apply(void){
266 if( vg_settings_enum_diff( &vg_settings.audio_devices ) ){
267 if( vg_audio.sdl_output_device ){
268 vg_info( "Closing audio device %d\n", vg_audio.sdl_output_device );
269 SDL_CloseAudioDevice( vg_audio.sdl_output_device );
270 }
271
272 vg_strfree( &vg_audio.device_choice );
273
274 if( vg_settings.audio_devices.new_value == -1 ){ }
275 else if( vg_settings.audio_devices.new_value == -2 ){
276 vg_fatal_error( "Programming error\n" );
277 }
278 else {
279 struct ui_enum_opt *selected = NULL, *oi;
280
281 for( int i=0; i<vg_settings.audio_devices.option_count; i ++ ){
282 oi = &vg_settings.audio_devices.options[i];
283
284 if( oi->value == vg_settings.audio_devices.new_value ){
285 selected = oi;
286 break;
287 }
288 }
289
290 vg_strnull( &vg_audio.device_choice, NULL, -1 );
291 vg_strcat( &vg_audio.device_choice, oi->alias );
292 }
293
294 vg_audio_device_init();
295 *vg_settings.audio_devices.actual_value =
296 vg_settings.audio_devices.new_value;
297 }
298
299 audio_lock();
300 if( vg_settings_enum_diff( &vg_settings.dsp ) ){
301 *vg_settings.dsp.actual_value =
302 vg_settings.dsp.new_value;
303 }
304
305 audio_unlock();
306 }
307
308 static void vg_settings_audio_gui( ui_rect panel ){
309 ui_rect rq;
310 ui_standard_widget( panel, rq, 1 );
311 vg_settings_enum( &vg_settings.audio_devices, rq );
312
313 ui_standard_widget( panel, rq, 1 );
314 vg_settings_enum( &vg_settings.dsp, rq );
315
316 if( vg_settings_apply_button( panel, 1 ) )
317 vg_settings_audio_apply();
318 }
319
320 static void vg_settings_open(void){
321 vg.settings_open = 1;
322
323 ui_settings_ranged_i32_init( &vg_settings.fps_limit );
324 ui_settings_enum_init( &vg_settings.vsync );
325 ui_settings_enum_init( &vg_settings.quality );
326 ui_settings_enum_init( &vg_settings.screenmode );
327
328 /* Create audio options */
329 int count = SDL_GetNumAudioDevices( 0 );
330
331 struct ui_enum_opt *options = malloc( sizeof(struct ui_enum_opt)*(count+1) );
332 vg_settings.audio_devices.options = options;
333 vg_settings.audio_devices.option_count = count+1;
334
335 struct ui_enum_opt *o0 = &options[0];
336 o0->alias = "OS Default";
337 o0->value = -1;
338
339 for( int i=0; i<count; i ++ ){
340 struct ui_enum_opt *oi = &options[i+1];
341
342 const char *device_name = SDL_GetAudioDeviceName( i, 0 );
343 int len = strlen(device_name);
344
345 oi->alias = malloc( len+1 );
346 memcpy( (void *)oi->alias, device_name, len+1 );
347 oi->value = i;
348 }
349
350 if( vg_audio.device_choice.buffer ){
351 vg_settings.temp_audio_choice = -2;
352
353 for( int i=0; i<count; i ++ ){
354 struct ui_enum_opt *oi = &options[i+1];
355 if( !strcmp( oi->alias, vg_audio.device_choice.buffer ) ){
356 vg_settings.temp_audio_choice = oi->value;
357 break;
358 }
359 }
360 }
361 else {
362 vg_settings.temp_audio_choice = -1;
363 }
364
365 ui_settings_enum_init( &vg_settings.audio_devices );
366 ui_settings_enum_init( &vg_settings.dsp );
367
368 #ifdef VG_GAME_SETTINGS
369 vg_game_settings_init();
370 #endif
371 }
372
373 static void vg_settings_close(void){
374 vg.settings_open = 0;
375
376 struct ui_enum_opt *options = vg_settings.audio_devices.options;
377 for( int i=1; i < vg_settings.audio_devices.option_count; i ++ )
378 free( (void *)options[i].alias );
379 free( vg_settings.audio_devices.options );
380 }
381
382 static void vg_settings_gui(void){
383 ui_rect null;
384 ui_rect screen = { 0, 0, vg.window_x, vg.window_y };
385 ui_rect window = { 0, 0, 1000, 700 };
386 ui_rect_center( screen, window );
387 vg_ui.wants_mouse = 1;
388
389 ui_fill( window, ui_colour( k_ui_bg+1 ) );
390 ui_outline( window, 1, ui_colour( k_ui_bg+7 ), 0 );
391
392 ui_rect title, panel;
393 ui_split( window, k_ui_axis_h, 28, 0, title, panel );
394 ui_fill( title, ui_colour( k_ui_bg+7 ) );
395 ui_text( title, "Settings", 1, k_ui_align_middle_center,
396 ui_colourcont(k_ui_bg+7) );
397
398 ui_rect quit_button;
399 ui_split( title, k_ui_axis_v, title[2]-title[3], 2, title, quit_button );
400
401 if( ui_button_text( quit_button, "X", 1 ) == 1 ){
402 vg_settings_close();
403 return;
404 }
405
406 ui_rect_pad( panel, (ui_px[2]){ 8, 8 } );
407
408 const char *opts[] = { "video", "audio",
409 #ifdef VG_GAME_SETTINGS
410 "game"
411 #endif
412 };
413
414 static i32 page = 0;
415 ui_tabs( panel, panel, opts, vg_list_size(opts), &page );
416
417 if( page == 0 ){
418 vg_settings_video_gui( panel );
419 }
420 else if( page == 1 )
421 vg_settings_audio_gui( panel );
422
423 #ifdef VG_GAME_SETTINGS
424 else if( page == 2 )
425 vg_game_settings_gui( panel );
426 #endif
427 }
428
429 static int cmd_vg_settings_toggle( int argc, const char *argv[] ){
430 vg_settings_open();
431 return 0;
432 }
433
434 #endif /* VG_SETTINGS_MENU_H */