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