1 /* CRT SwitchRes Core
2 * Copyright (C) 2018 Alphanu / Ben Templeman.
3 *
4 * RetroArch - A frontend for libretro.
5 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
6 * Copyright (C) 2011-2017 - Daniel De Matteis
7 *
8 * RetroArch is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Found-
10 * ation, either version 3 of the License, or (at your option) any later version.
11 *
12 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with RetroArch.
17 * If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <stddef.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <libretro.h>
24 #include <math.h>
25
26 #include "../retroarch.h"
27 #include <retro_common_api.h>
28 #include "video_crt_switch.h"
29 #include "video_display_server.h"
30 #include "../core_info.h"
31 #include "../verbosity.h"
32 #include "../file_path_special.h"
33 #include "../paths.h"
34 #include "gfx_display.h"
35
36 #if !defined(HAVE_VIDEOCORE)
37 #include "../deps/switchres/switchres_wrapper.h"
38 static sr_mode srm;
39 #endif
40
41 #ifdef HAVE_CONFIG_H
42 #include "../config.h"
43 #endif
44
45 #if defined(HAVE_VIDEOCORE)
46 #include "include/userland/interface/vmcs_host/vc_vchi_gencmd.h"
47 static void crt_rpi_switch(videocrt_switch_t *p_switch,int width, int height, float hz, int xoffset, int native_width);
48 #endif
49
50 static void crt_check_hh_core(videocrt_switch_t *p_switch);
51 static void crt_adjust_sr_ini(videocrt_switch_t *p_switch);
52 static bool ini_overrides_loaded = false;
53 static char core_name[256]; /* same size as library_name on retroarch_data.h */
54 static char content_dir[PATH_MAX_LENGTH];
55
crt_check_for_changes(videocrt_switch_t * p_switch)56 static bool crt_check_for_changes(videocrt_switch_t *p_switch)
57 {
58 if ((p_switch->ra_tmp_height != p_switch->ra_core_height) ||
59 (p_switch->ra_core_width != p_switch->ra_tmp_width) ||
60 (p_switch->center_adjust != p_switch->tmp_center_adjust||
61 p_switch->porch_adjust != p_switch->tmp_porch_adjust ) ||
62 (p_switch->ra_core_hz != p_switch->ra_tmp_core_hz))
63 return true;
64
65
66 return false;
67 }
68
crt_store_temp_changes(videocrt_switch_t * p_switch)69 static void crt_store_temp_changes(videocrt_switch_t *p_switch)
70 {
71 p_switch->ra_tmp_height = p_switch->ra_core_height;
72 p_switch->ra_tmp_width = p_switch->ra_core_width;
73 p_switch->tmp_center_adjust = p_switch->center_adjust;
74 p_switch->tmp_porch_adjust = p_switch->porch_adjust;
75 p_switch->ra_tmp_core_hz = p_switch->ra_core_hz;
76
77 }
78
switch_crt_hz(videocrt_switch_t * p_switch)79 static void switch_crt_hz(videocrt_switch_t *p_switch)
80 {
81 video_monitor_set_refresh_rate(p_switch->sr_core_hz);
82
83 }
84
85
crt_aspect_ratio_switch(videocrt_switch_t * p_switch,unsigned width,unsigned height,unsigned srm_width,unsigned srm_height)86 static void crt_aspect_ratio_switch(
87 videocrt_switch_t *p_switch,
88 unsigned width, unsigned height, unsigned srm_width, unsigned srm_height)
89 {
90 /* send aspect float to video_driver */
91 p_switch->fly_aspect = (float)width / (float)height;
92 video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
93 RARCH_LOG("[CRT]: Setting Aspect Ratio: %f \n", (float)p_switch->fly_aspect);
94
95 RARCH_LOG("[CRT]: Setting Video Screen Size to: %dx%d \n", width, height);
96 video_driver_set_size(width , height);
97 video_driver_set_viewport(width , height,1,1);
98
99 video_driver_apply_state_changes();
100
101 }
102
set_aspect(videocrt_switch_t * p_switch,unsigned int width,unsigned int height,unsigned int srm_width,unsigned srm_height,unsigned int srm_xscale,unsigned srm_yscale,bool srm_isstretched)103 static void set_aspect(videocrt_switch_t *p_switch, unsigned int width,
104 unsigned int height, unsigned int srm_width, unsigned srm_height,
105 unsigned int srm_xscale, unsigned srm_yscale, bool srm_isstretched )
106 {
107 unsigned int patched_width = 0;
108 unsigned int patched_height = 0;
109 int scaled_width = 0;
110 int scaled_height = 0;
111
112 /* used to fix aspect should SR not find a resolution */
113 if (srm_width == 0)
114 {
115 video_driver_get_size(&patched_width, &patched_height);
116 srm_xscale = 1;
117 srm_yscale = 1;
118 }else{
119 /* use native values as we will be multiplying by srm scale later. */
120 patched_width = width;
121 patched_height = height;
122 }
123 if (srm_isstretched && srm_width > 0)
124 {
125 srm_xscale = srm_width/width;
126 srm_yscale = srm_height/height;
127 }
128
129 scaled_width = roundf(patched_width*srm_xscale);
130 scaled_height = roundf(patched_height*srm_yscale);
131
132 crt_aspect_ratio_switch(p_switch, scaled_width, scaled_height, srm_width, srm_height);
133 }
134 #if !defined(HAVE_VIDEOCORE)
crt_sr2_init(videocrt_switch_t * p_switch,int monitor_index,unsigned int crt_mode,unsigned int super_width)135 static bool crt_sr2_init(videocrt_switch_t *p_switch, int monitor_index, unsigned int crt_mode, unsigned int super_width)
136 {
137 const char* err_msg;
138 char* mode;
139 char index[10];
140
141
142 if (monitor_index+1 >= 0 && monitor_index+1 < 10)
143 sprintf(index, "%d", monitor_index);
144 else
145 sprintf(index, "%s", "0");
146
147 if (!p_switch->sr2_active)
148 {
149
150 RARCH_LOG("[CRT]: SR init \n");
151
152 sr_init();
153 #if (__STDC_VERSION__ >= 199409L) /* no logs for C98 or less */
154 sr_set_log_callback_info(RARCH_LOG);
155 sr_set_log_callback_debug(RARCH_DBG);
156 sr_set_log_callback_error(RARCH_ERR);
157 #endif
158
159 if (crt_mode == 1)
160 {
161 sr_set_monitor("arcade_15");
162 RARCH_LOG("[CRT]: CRT Mode: %d - arcade_15 \n", crt_mode) ;
163 }else if (crt_mode == 2)
164 {
165 sr_set_monitor("arcade_31");
166 RARCH_LOG("[CRT]: CRT Mode: %d - arcade_31 \n", crt_mode) ;
167 }else if (crt_mode == 3)
168 {
169 sr_set_monitor("pc_31_120");
170 RARCH_LOG("[CRT]: CRT Mode: %d - pc_31_120 \n", crt_mode) ;
171 }else if (crt_mode == 4)
172 {
173 RARCH_LOG("[CRT]: CRT Mode: %d - Selected from ini \n", crt_mode) ;
174 }
175
176 if (super_width >2 )
177 sr_set_user_mode(super_width, 0, 0);
178
179 RARCH_LOG("[CRT]: SR init_disp \n");
180 if (monitor_index+1 > 0)
181 {
182 RARCH_LOG("[CRT]: RA Monitor Index Manual: %s\n", &index[0]);
183 p_switch->rtn = sr_init_disp(index);
184 RARCH_LOG("[CRT]: SR Disp Monitor Index Manual: %s \n", &index[0]);
185 }
186
187 if (monitor_index == -1)
188 {
189 RARCH_LOG("[CRT]: RA Monitor Index Auto: %s\n","auto");
190 p_switch->rtn = sr_init_disp("auto");
191 RARCH_LOG("[CRT]: SR Disp Monitor Index Auto: Auto \n");
192 }
193
194 RARCH_LOG("[CRT]: SR rtn %d \n", p_switch->rtn);
195
196 if(p_switch->rtn == 1)
197 {
198 core_name[0] = '\0';
199 content_dir[0] = '\0';
200 }
201 }
202
203 if (p_switch->rtn == 1)
204 {
205 p_switch->sr2_active = true;
206 return true;
207 }else{
208 RARCH_LOG("[CRT]: SR failed to init \n");
209 sr_deinit();
210 p_switch->sr2_active = false;
211 }
212
213 return false;
214 }
215
216
switch_res_crt(videocrt_switch_t * p_switch,unsigned width,unsigned height,unsigned crt_mode,unsigned native_width,int monitor_index,int super_width)217 static void switch_res_crt(
218 videocrt_switch_t *p_switch,
219 unsigned width, unsigned height, unsigned crt_mode, unsigned native_width, int monitor_index, int super_width)
220 {
221 char current_core_name[sizeof(core_name)];
222 char current_content_dir[sizeof(content_dir)];
223
224 unsigned char interlace = 0, ret;
225 const char* err_msg;
226 int w = native_width, h = height;
227 double rr = p_switch->ra_core_hz;
228
229 if (crt_sr2_init(p_switch, monitor_index, crt_mode, super_width)) /* Checked SR2 is loded if not Load it */
230 {
231 /* Check for core and content changes in case we need to make any adjustments */
232 if(crt_switch_core_name())
233 strlcpy(current_core_name, crt_switch_core_name(), sizeof(current_core_name));
234 else
235 current_core_name[0] = '\0';
236 fill_pathname_parent_dir_name(current_content_dir, path_get(RARCH_PATH_CONTENT), sizeof(current_content_dir));
237 if (!string_is_equal(core_name, current_core_name) || !string_is_equal(content_dir, current_content_dir))
238 {
239 /* A core or content change was detected, we update the current values and make adjustments */
240 strlcpy(core_name, current_core_name, sizeof(core_name));
241 strlcpy(content_dir, current_content_dir, sizeof(content_dir));
242 RARCH_LOG("[CRT]: Current running core %s \n", core_name);
243 crt_adjust_sr_ini(p_switch);
244 crt_check_hh_core(p_switch);
245 }
246 ret = sr_switch_to_mode(w, h, rr, interlace, &srm);
247 if(!ret)
248 {
249 RARCH_LOG("[CRT]: SR failed to switch mode");
250 /*sr_deinit();*/
251
252 }
253 p_switch->sr_core_hz = srm.refresh;
254
255 set_aspect(p_switch, w , h, srm.width, srm.height, srm.x_scale, srm.y_scale, srm.is_stretched);
256
257 RARCH_LOG("[CRT]: SR scaled X:%d Y:%d \n",srm.x_scale, srm.y_scale);
258
259 }else {
260 set_aspect(p_switch, width , height, width, height ,1,1, false);
261 video_driver_set_size(width , height);
262 video_driver_apply_state_changes();
263
264 }
265 }
266 #endif
crt_destroy_modes(videocrt_switch_t * p_switch)267 void crt_destroy_modes(videocrt_switch_t *p_switch)
268 {
269
270 if (p_switch->sr2_active == true)
271 {
272 p_switch->sr2_active = false;
273 sr_deinit();
274 /*RARCH_LOG("[CRT]: SR Destroyed \n"); */
275
276 }
277
278 }
279
crt_check_hh_core(videocrt_switch_t * p_switch)280 static void crt_check_hh_core(videocrt_switch_t *p_switch)
281 {
282 /*
283 char* handheld[8] = {"mGBA","Gambatte","gpSP","Gearboy","VBA Next","VBA-M","SameBoy","TGB Dual"};
284 int i = 0;
285 for(i = 0; i < 7; i++)
286 {
287 if (strcmp(handheld[i],p_switch->core_name) == 0)
288 {
289 RARCH_LOG("[CRT]: Handheld core detected %s adjusting resolutions.\n", p_switch->core_name);
290 p_switch->hh_core = true;
291 break;
292 }
293 else
294 {
295 p_switch->hh_core = false;
296 }
297
298
299 }
300
301 */
302 p_switch->hh_core = false;
303
304 }
305 #if !defined(HAVE_VIDEOCORE)
crt_fix_hh_res(videocrt_switch_t * p_switch,int native_width,int width,int height,int crt_mode,int monitor_index,int super_width)306 static void crt_fix_hh_res(videocrt_switch_t *p_switch, int native_width, int width,
307 int height, int crt_mode, int monitor_index, int super_width)
308 {
309 int corrected_width = 320;
310 int corrected_height = 240;
311
312 switch_res_crt(p_switch, corrected_width, corrected_height , crt_mode, corrected_width, monitor_index-1, super_width);
313 set_aspect(p_switch, native_width , height, native_width, height ,1,1, false);
314 video_driver_set_size(native_width , height);
315
316
317 }
318 #endif
319 /*
320 static void crt_menu_restore(videocrt_switch_t *p_switch)
321 {
322
323 video_driver_get_size(&p_switch->fb_width, &p_switch->fb_height);
324 RARCH_LOG("[CRT]: Menu Only Restoring Aspect: %dx%d \n", p_switch->fb_width, p_switch->fb_height);
325 crt_aspect_ratio_switch(p_switch, p_switch->fb_width, p_switch->fb_height, p_switch->fb_width, p_switch->fb_height);
326
327 }
328
329 static bool crt_get_desktop_res(videocrt_switch_t *p_switch, unsigned width, unsigned height, float hz)
330 {
331 if (p_switch->menu_active == false)
332 {
333 if (p_switch->fb_width == 0)
334 video_driver_get_size(&p_switch->fb_width, &p_switch->fb_height);
335
336 p_switch->fb_ra_core_hz = 60.0;
337 RARCH_LOG("[CRT]: Storing Desktop Resolution: %dx%d@%f \n", p_switch->fb_width, p_switch->fb_height, p_switch->fb_ra_core_hz);
338 crt_menu_restore(p_switch);
339 p_switch->menu_active = true;
340 return true;
341 }
342 return false;
343 }
344 */
345
crt_switch_res_core(videocrt_switch_t * p_switch,unsigned native_width,unsigned width,unsigned height,float hz,unsigned crt_mode,int crt_switch_center_adjust,int crt_switch_porch_adjust,int monitor_index,bool dynamic,int super_width,bool hires_menu)346 void crt_switch_res_core(
347 videocrt_switch_t *p_switch,
348 unsigned native_width, unsigned width, unsigned height,
349 float hz, unsigned crt_mode,
350 int crt_switch_center_adjust,
351 int crt_switch_porch_adjust,
352 int monitor_index, bool dynamic,
353 int super_width, bool hires_menu)
354 {
355 if (height <= 4)
356 {
357 if (hires_menu == true)
358 {
359 native_width = 640;
360 width = 640;
361 height = 480;
362 hz = 60;
363 }else{
364 native_width = 320;
365 width = 320;
366 height = 240;
367 hz = 60;
368 }
369 }
370
371
372 if (height != 4 )
373 {
374
375 p_switch->menu_active = false;
376 p_switch->porch_adjust = crt_switch_porch_adjust;
377 p_switch->ra_core_height = height;
378 p_switch->ra_core_hz = hz;
379
380 p_switch->ra_core_width = width;
381
382 p_switch->center_adjust = crt_switch_center_adjust;
383 p_switch->index = monitor_index;
384
385 /* Detect resolution change and switch */
386 if (crt_check_for_changes(p_switch))
387 {
388 RARCH_LOG("[CRT]: Requested Reolution: %dx%d@%f \n", native_width, height, hz);
389 #if defined(HAVE_VIDEOCORE)
390 crt_rpi_switch(p_switch, width, height, hz, 0, native_width);
391 #else
392
393 if (p_switch->hh_core == false)
394 switch_res_crt(p_switch, p_switch->ra_core_width, p_switch->ra_core_height , crt_mode, native_width, monitor_index-1, super_width);
395 else
396 crt_fix_hh_res(p_switch, native_width, width, height, crt_mode, monitor_index, super_width);
397
398 #endif
399 switch_crt_hz(p_switch);
400 crt_store_temp_changes(p_switch);
401 }
402
403 if (video_driver_get_aspect_ratio() != p_switch->fly_aspect)
404 {
405 RARCH_LOG("[CRT]: Restoring Aspect Ratio: %f \n", (float)p_switch->fly_aspect);
406 video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
407 video_driver_apply_state_changes();
408 }
409
410 }
411
412 }
413
crt_adjust_sr_ini(videocrt_switch_t * p_switch)414 void crt_adjust_sr_ini(videocrt_switch_t *p_switch)
415 {
416 char config_directory[PATH_MAX_LENGTH];
417 char switchres_ini_override_file[PATH_MAX_LENGTH];
418
419 if(p_switch->sr2_active)
420 {
421 /* First we reload the base switchres.ini file to undo any overrides that might have been loaded for another core */
422 if(ini_overrides_loaded)
423 {
424 RARCH_LOG("[CRT]: Loading default switchres.ini \n");
425 sr_load_ini((char *)"switchres.ini");
426 ini_overrides_loaded = false;
427 }
428 if(strlen(core_name) > 0) {
429 /* Then we look for config/Core Name/Core Name.switchres.ini and load it, overriding any variables it specifies */
430 config_directory[0] = '\0';
431 fill_pathname_application_special(config_directory,
432 sizeof(config_directory), APPLICATION_SPECIAL_DIRECTORY_CONFIG);
433 fill_pathname_join_special_ext(switchres_ini_override_file,
434 config_directory, core_name, core_name, ".switchres.ini", sizeof(switchres_ini_override_file));
435 if(path_is_valid(switchres_ini_override_file))
436 {
437 RARCH_LOG("[CRT]: Loading switchres.ini core override file from %s \n", switchres_ini_override_file);
438 sr_load_ini(switchres_ini_override_file);
439 ini_overrides_loaded = true;
440 }
441 /* Next up we load directory overrides, if any */
442 fill_pathname_join_special_ext(switchres_ini_override_file,
443 config_directory, core_name, content_dir, ".switchres.ini", sizeof(switchres_ini_override_file));
444 if(path_is_valid(switchres_ini_override_file))
445 {
446 RARCH_LOG("[CRT]: Loading switchres.ini content directory override file from %s \n", switchres_ini_override_file);
447 sr_load_ini(switchres_ini_override_file);
448 ini_overrides_loaded = true;
449 }
450 }
451 }
452 }
453
454 /* only used for RPi3 */
455 #if defined(HAVE_VIDEOCORE)
crt_rpi_switch(videocrt_switch_t * p_switch,int width,int height,float hz,int xoffset,int native_width)456 static void crt_rpi_switch(videocrt_switch_t *p_switch, int width, int height, float hz, int xoffset, int native_width)
457 {
458 char buffer[1024];
459 VCHI_INSTANCE_T vchi_instance;
460 VCHI_CONNECTION_T *vchi_connection = NULL;
461 static char output[250] = {0};
462 static char output1[250] = {0};
463 static char output2[250] = {0};
464 static char set_hdmi[250] = {0};
465 static char set_hdmi_timing[250] = {0};
466 int i = 0;
467 int hfp = 0;
468 int hsp = 0;
469 int hbp = 0;
470 int vfp = 0;
471 int vsp = 0;
472 int vbp = 0;
473 int hmax = 0;
474 int vmax = 0;
475 int pdefault = 8;
476 int pwidth = 0;
477 int ip_flag = 0;
478 float roundw = 0.0f;
479 float roundh = 0.0f;
480 float pixel_clock = 0.0f;
481 int xscale = 1;
482 int yscale = 1;
483
484 if (height > 300)
485 height = height/2;
486
487 /* set core refresh from hz */
488 video_monitor_set_refresh_rate(hz);
489
490 set_aspect(p_switch, width,
491 height, width, height,
492 1, 1, false);
493 int w = width;
494 while (w < 1920)
495 {
496 w = w+width;
497 }
498
499 if (w > 2000)
500 w =w- width;
501
502 width = w;
503
504 crt_aspect_ratio_switch(p_switch, width,height,width,height);
505
506 /* following code is the mode line generator */
507 hfp = ((width * 0.044) + (width / 112));
508 hbp = ((width * 0.172) + (width /64));
509
510
511 hsp = (width * 0.117);
512
513 if (height < 241)
514 vmax = 261;
515 if (height < 241 && hz > 56 && hz < 58)
516 vmax = 280;
517 if (height < 241 && hz < 55)
518 vmax = 313;
519 if (height > 250 && height < 260 && hz > 54)
520 vmax = 296;
521 if (height > 250 && height < 260 && hz > 52 && hz < 54)
522 vmax = 285;
523 if (height > 250 && height < 260 && hz < 52)
524 vmax = 313;
525 if (height > 260 && height < 300)
526 vmax = 318;
527
528 if (height > 400 && hz > 56)
529 vmax = 533;
530 if (height > 520 && hz < 57)
531 vmax = 580;
532
533 if (height > 300 && hz < 56)
534 vmax = 615;
535 if (height > 500 && hz < 56)
536 vmax = 624;
537 if (height > 300)
538 pdefault = pdefault * 2;
539
540 vfp = (height + ((vmax - height) / 2) - pdefault) - height;
541
542 if (height < 300)
543 vsp = vfp + 3; /* needs to be 3 for progressive */
544 if (height > 300)
545 vsp = vfp + 6; /* needs to be 6 for interlaced */
546
547 vsp = 3;
548 vbp = (vmax-height)-vsp-vfp;
549 hmax = width+hfp+hsp+hbp;
550
551 if (height < 300)
552 {
553 pixel_clock = (hmax * vmax * hz) ;
554 ip_flag = 0;
555 }
556
557 if (height > 300)
558 {
559 pixel_clock = (hmax * vmax * (hz/2)) /2 ;
560 ip_flag = 1;
561 }
562 /* above code is the modeline generator */
563
564 snprintf(set_hdmi_timing, sizeof(set_hdmi_timing),
565 "hdmi_timings %d 1 %d %d %d %d 1 %d %d %d 0 0 0 %f %d %f 1 ",
566 width, hfp, hsp, hbp, height, vfp,vsp, vbp,
567 hz, ip_flag, pixel_clock);
568
569 vcos_init();
570
571 vchi_initialise(&vchi_instance);
572
573 vchi_connect(NULL, 0, vchi_instance);
574
575 vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
576
577 vc_gencmd(buffer, sizeof(buffer), set_hdmi_timing);
578
579 vc_gencmd_stop();
580
581 vchi_disconnect(vchi_instance);
582
583 snprintf(output1, sizeof(output1),
584 "tvservice -e \"DMT 87\" > /dev/null");
585 system(output1);
586 snprintf(output2, sizeof(output1),
587 "fbset -g %d %d %d %d 24 > /dev/null",
588 width, height, width, height);
589 system(output2);
590
591 crt_switch_driver_refresh();
592 }
593 #endif
594