fb6a11616c3d688f642fdc16f18819fe1f4f9389
[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 vg_strfree( &vg_audio.device_choice );
265
266 if( vg_settings.audio_devices.new_value == -1 ){ }
267 else if( vg_settings.audio_devices.new_value == -2 ){
268 vg_fatal_error( "Programming error\n" );
269 }
270 else {
271 struct ui_enum_opt *selected = NULL, *oi;
272
273 for( int i=0; i<vg_settings.audio_devices.option_count; i ++ ){
274 oi = &vg_settings.audio_devices.options[i];
275
276 if( oi->value == vg_settings.audio_devices.new_value ){
277 selected = oi;
278 break;
279 }
280 }
281
282 vg_strnull( &vg_audio.device_choice, NULL, -1 );
283 vg_strcat( &vg_audio.device_choice, oi->alias );
284 }
285
286 vg_audio_device_init();
287 *vg_settings.audio_devices.actual_value =
288 vg_settings.audio_devices.new_value;
289 }
290 }
291
292 static void vg_settings_audio_gui( ui_rect panel ){
293 ui_rect rq;
294 ui_standard_widget( panel, rq, 1 );
295 vg_settings_enum( &vg_settings.audio_devices, rq );
296
297 const char *string = "Apply";
298
299 ui_rect last_row;
300 ui_px height = (vg_ui.font->glyph_height + 18) * k_ui_scale;
301 ui_split( panel, k_ui_axis_h, -height, k_ui_padding,
302 panel, last_row );
303
304 if( ui_button( last_row, string ) == 1 )
305 vg_settings_audio_apply();
306 }
307
308 static void vg_settings_open(void){
309 vg.settings_open = 1;
310
311 ui_settings_ranged_i32_init( &vg_settings.fps_limit );
312 ui_settings_enum_init( &vg_settings.vsync );
313 ui_settings_enum_init( &vg_settings.quality );
314 ui_settings_enum_init( &vg_settings.screenmode );
315
316 /* Create audio options */
317 int count = SDL_GetNumAudioDevices( 0 );
318
319 struct ui_enum_opt *options = malloc( sizeof(struct ui_enum_opt)*(count+1) );
320 vg_settings.audio_devices.options = options;
321 vg_settings.audio_devices.option_count = count+1;
322
323 struct ui_enum_opt *o0 = &options[0];
324 o0->alias = "OS Default";
325 o0->value = -1;
326
327 for( int i=0; i<count; i ++ ){
328 struct ui_enum_opt *oi = &options[i+1];
329
330 const char *device_name = SDL_GetAudioDeviceName( i, 0 );
331 int len = strlen(device_name);
332
333 oi->alias = malloc( len+1 );
334 memcpy( (void *)oi->alias, device_name, len+1 );
335 oi->value = i;
336 }
337
338 if( vg_audio.device_choice.buffer ){
339 vg_settings.temp_audio_choice = -2;
340
341 for( int i=0; i<count; i ++ ){
342 struct ui_enum_opt *oi = &options[i+1];
343 if( !strcmp( oi->alias, vg_audio.device_choice.buffer ) ){
344 vg_settings.temp_audio_choice = oi->value;
345 break;
346 }
347 }
348 }
349 else {
350 vg_settings.temp_audio_choice = -1;
351 }
352
353 ui_settings_enum_init( &vg_settings.audio_devices );
354 }
355
356 static void vg_settings_close(void){
357 vg.settings_open = 0;
358
359 struct ui_enum_opt *options = vg_settings.audio_devices.options;
360 for( int i=1; i < vg_settings.audio_devices.option_count; i ++ )
361 free( (void *)options[i].alias );
362 free( vg_settings.audio_devices.options );
363 }
364
365 static void vg_settings_gui(void){
366 ui_rect null;
367 ui_rect screen = { 0, 0, vg.window_x, vg.window_y };
368 ui_rect window = { 0, 0, 1000, 700 };
369 ui_rect_center( screen, window );
370 vg_ui.wants_mouse = 1;
371
372 ui_fill( window, ui_colour( k_ui_bg+1 ) );
373 ui_outline( window, 1, ui_colour( k_ui_bg+7 ), 0 );
374
375 ui_rect title, panel;
376 ui_split( window, k_ui_axis_h, 28, 0, title, panel );
377 ui_fill( title, ui_colour( k_ui_bg+7 ) );
378 ui_text( title, "Settings", 1, k_ui_align_middle_center,
379 ui_colourcont(k_ui_bg+7) );
380
381 ui_rect quit_button;
382 ui_split( title, k_ui_axis_v, title[2]-title[3], 2, title, quit_button );
383
384 if( ui_button_text( quit_button, "X", 1 ) == 1 ){
385 vg_settings_close();
386 return;
387 }
388
389 ui_rect_pad( panel, (ui_px[2]){ 8, 8 } );
390
391 static i32 page = 0;
392 ui_tabs( panel, panel, (const char *[]){ "video", "audio", "game" },
393 3, &page );
394
395 if( page == 0 ){
396 vg_settings_video_gui( panel );
397 }
398 else if( page == 1 )
399 vg_settings_audio_gui( panel );
400 }
401
402 static int cmd_vg_settings_toggle( int argc, const char *argv[] ){
403 vg_settings_open();
404 return 0;
405 }
406
407 #endif /* VG_SETTINGS_MENU_H */