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