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