mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 21:32:45 +00:00
f1a37f7c75
* Remove switchres before bump * Squashed 'deps/switchres/' content from commit 725e4d484a git-subtree-dir: deps/switchres git-subtree-split: 725e4d484a33632618dd44cdc2a61948dd833282
467 lines
14 KiB
C++
467 lines
14 KiB
C++
/**************************************************************
|
|
|
|
switchres_main.cpp - Swichres standalone launcher
|
|
|
|
---------------------------------------------------------
|
|
|
|
Switchres Modeline generation engine for emulation
|
|
|
|
License GPL-2.0+
|
|
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
|
Alexandre Wodarczyk, Gil Delescluse
|
|
|
|
**************************************************************/
|
|
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <getopt.h>
|
|
#include "switchres.h"
|
|
#include "switchres_defines.h"
|
|
#include "log.h"
|
|
|
|
using namespace std;
|
|
|
|
int show_version();
|
|
int show_usage();
|
|
|
|
enum
|
|
{
|
|
OPT_CRT_RANGE0 = 128,
|
|
OPT_CRT_RANGE1,
|
|
OPT_CRT_RANGE2,
|
|
OPT_CRT_RANGE3,
|
|
OPT_CRT_RANGE4,
|
|
OPT_CRT_RANGE5,
|
|
OPT_CRT_RANGE6,
|
|
OPT_CRT_RANGE7,
|
|
OPT_CRT_RANGE8,
|
|
OPT_CRT_RANGE9,
|
|
OPT_LCD_RANGE,
|
|
OPT_MODELINE,
|
|
OPT_USER_MODE,
|
|
OPT_API,
|
|
OPT_LOCK_UNSUPPORTED_MODES,
|
|
OPT_LOCK_SYSTEM_MODES,
|
|
OPT_REFRESH_DONT_CARE,
|
|
OPT_KEEP_CHANGES,
|
|
OPT_MODELINE_GENERATION,
|
|
OPT_INTERLACE,
|
|
OPT_DOUBLESCAN,
|
|
OPT_DOTCLOCK_MIN,
|
|
OPT_SYNC_REFRESH_TOLERANCE,
|
|
OPT_SUPER_WIDTH,
|
|
OPT_V_SHIFT_CORRECT,
|
|
OPT_H_SIZE,
|
|
OPT_H_SHIFT,
|
|
OPT_V_SHIFT,
|
|
OPT_PIXEL_PRECISION,
|
|
OPT_INTERLACE_FORCE_EVEN,
|
|
OPT_SCALE_PROPORTIONAL,
|
|
OPT_SCREEN_COMPOSITING,
|
|
OPT_SCREEN_REORDERING,
|
|
OPT_ALLOW_HARDWARE_REFRESH,
|
|
OPT_CUSTOM_TIMING,
|
|
OPT_VERBOSITY
|
|
};
|
|
|
|
//============================================================
|
|
// main
|
|
//============================================================
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
|
|
switchres_manager switchres;
|
|
display_manager* df = switchres.display_factory();
|
|
|
|
switchres.parse_config("switchres.ini");
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
float refresh = 0.0;
|
|
modeline user_mode = {};
|
|
int index = 0;
|
|
|
|
int version_flag = false;
|
|
bool help_flag = false;
|
|
bool resolution_flag = false;
|
|
bool calculate_flag = false;
|
|
bool edid_flag = false;
|
|
bool switch_flag = false;
|
|
bool launch_flag = false;
|
|
bool force_flag = false;
|
|
bool interlaced_flag = false;
|
|
bool rotated_flag = false;
|
|
bool user_ini_flag = false;
|
|
bool keep_changes_flag = false;
|
|
bool geometry_flag = false;
|
|
int status_code = 0;
|
|
|
|
string ini_file;
|
|
string launch_command;
|
|
|
|
while (1)
|
|
{
|
|
static struct option long_options[] =
|
|
{
|
|
// Options unique to standalone Switchres
|
|
{"version", no_argument, &version_flag, '1'},
|
|
{"help", no_argument, 0, 'h'},
|
|
{"calc", no_argument, 0, 'c'},
|
|
{"switch", no_argument, 0, 's'},
|
|
{"launch", required_argument, 0, 'l'},
|
|
{"edid", no_argument, 0, 'e'},
|
|
{"rotated", no_argument, 0, 'r'},
|
|
{"force", required_argument, 0, 'f'}, // equ. --user_mode
|
|
{"ini", required_argument, 0, 'i'},
|
|
{"keep", no_argument, 0, 'k'}, // equ. --keep_changes
|
|
{"geometry", required_argument, 0, 'g'},
|
|
// Options available in short and long forms
|
|
{SR_OPT_VERBOSE, no_argument, 0, 'v'},
|
|
{SR_OPT_DISPLAY, required_argument, 0, 'd'},
|
|
{SR_OPT_MONITOR, required_argument, 0, 'm'},
|
|
{SR_OPT_ASPECT, required_argument, 0, 'a'},
|
|
// Long options, from switchres.ini
|
|
{SR_OPT_CRT_RANGE0, required_argument, 0, OPT_CRT_RANGE0},
|
|
{SR_OPT_CRT_RANGE1, required_argument, 0, OPT_CRT_RANGE1},
|
|
{SR_OPT_CRT_RANGE2, required_argument, 0, OPT_CRT_RANGE2},
|
|
{SR_OPT_CRT_RANGE3, required_argument, 0, OPT_CRT_RANGE3},
|
|
{SR_OPT_CRT_RANGE4, required_argument, 0, OPT_CRT_RANGE4},
|
|
{SR_OPT_CRT_RANGE5, required_argument, 0, OPT_CRT_RANGE5},
|
|
{SR_OPT_CRT_RANGE6, required_argument, 0, OPT_CRT_RANGE6},
|
|
{SR_OPT_CRT_RANGE7, required_argument, 0, OPT_CRT_RANGE7},
|
|
{SR_OPT_CRT_RANGE8, required_argument, 0, OPT_CRT_RANGE8},
|
|
{SR_OPT_CRT_RANGE9, required_argument, 0, OPT_CRT_RANGE9},
|
|
{SR_OPT_LCD_RANGE, required_argument, 0, OPT_LCD_RANGE},
|
|
{SR_OPT_MODELINE, required_argument, 0, OPT_MODELINE},
|
|
{SR_OPT_USER_MODE, required_argument, 0, OPT_USER_MODE},
|
|
{SR_OPT_API, required_argument, 0, OPT_API},
|
|
{SR_OPT_LOCK_UNSUPPORTED_MODES, required_argument, 0, OPT_LOCK_UNSUPPORTED_MODES},
|
|
{SR_OPT_LOCK_SYSTEM_MODES, required_argument, 0, OPT_LOCK_SYSTEM_MODES},
|
|
{SR_OPT_REFRESH_DONT_CARE, required_argument, 0, OPT_REFRESH_DONT_CARE},
|
|
{SR_OPT_KEEP_CHANGES, required_argument, 0, OPT_KEEP_CHANGES},
|
|
{SR_OPT_MODELINE_GENERATION, required_argument, 0, OPT_MODELINE_GENERATION},
|
|
{SR_OPT_INTERLACE, required_argument, 0, OPT_INTERLACE},
|
|
{SR_OPT_DOUBLESCAN, required_argument, 0, OPT_DOUBLESCAN},
|
|
{SR_OPT_DOTCLOCK_MIN, required_argument, 0, OPT_DOTCLOCK_MIN},
|
|
{SR_OPT_SYNC_REFRESH_TOLERANCE, required_argument, 0, OPT_SYNC_REFRESH_TOLERANCE},
|
|
{SR_OPT_SUPER_WIDTH, required_argument, 0, OPT_SUPER_WIDTH},
|
|
{SR_OPT_V_SHIFT_CORRECT, required_argument, 0, OPT_V_SHIFT_CORRECT},
|
|
{SR_OPT_H_SIZE, required_argument, 0, OPT_H_SIZE},
|
|
{SR_OPT_H_SHIFT, required_argument, 0, OPT_H_SHIFT},
|
|
{SR_OPT_V_SHIFT, required_argument, 0, OPT_V_SHIFT},
|
|
{SR_OPT_PIXEL_PRECISION, required_argument, 0, OPT_PIXEL_PRECISION},
|
|
{SR_OPT_INTERLACE_FORCE_EVEN, required_argument, 0, OPT_INTERLACE_FORCE_EVEN},
|
|
{SR_OPT_SCALE_PROPORTIONAL, required_argument, 0, OPT_SCALE_PROPORTIONAL},
|
|
{SR_OPT_SCREEN_COMPOSITING, required_argument, 0, OPT_SCREEN_COMPOSITING},
|
|
{SR_OPT_SCREEN_REORDERING, required_argument, 0, OPT_SCREEN_REORDERING},
|
|
{SR_OPT_ALLOW_HARDWARE_REFRESH, required_argument, 0, OPT_ALLOW_HARDWARE_REFRESH},
|
|
{SR_OPT_CUSTOM_TIMING, required_argument, 0, OPT_CUSTOM_TIMING},
|
|
{SR_OPT_VERBOSITY, required_argument, 0, OPT_VERBOSITY},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int option_index = 0;
|
|
int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:kg:", long_options, &option_index);
|
|
|
|
if (c == -1)
|
|
break;
|
|
|
|
if (version_flag)
|
|
{
|
|
show_version();
|
|
return 0;
|
|
}
|
|
|
|
switch (c)
|
|
{
|
|
case 'v':
|
|
switchres.set_log_level(3);
|
|
switchres.set_log_error_fn((void*)printf);
|
|
switchres.set_log_info_fn((void*)printf);
|
|
switchres.set_log_verbose_fn((void*)printf);
|
|
break;
|
|
|
|
case 'h':
|
|
help_flag = true;
|
|
break;
|
|
|
|
case 'c':
|
|
calculate_flag = true;
|
|
break;
|
|
|
|
case 's':
|
|
switch_flag = true;
|
|
break;
|
|
|
|
case 'l':
|
|
launch_flag = true;
|
|
launch_command = optarg;
|
|
break;
|
|
|
|
case 'm':
|
|
df->set_monitor(optarg);
|
|
break;
|
|
|
|
case 'r':
|
|
rotated_flag = true;
|
|
break;
|
|
|
|
case 'd':
|
|
// Add new display in multi-monitor case
|
|
if (index > 0) switchres.add_display();
|
|
index ++;
|
|
switchres.set_current_display(-1);
|
|
switchres.set_option(SR_OPT_DISPLAY, optarg);
|
|
break;
|
|
|
|
case 'a':
|
|
df->set_monitor_aspect(optarg);
|
|
break;
|
|
|
|
case 'e':
|
|
edid_flag = true;
|
|
break;
|
|
|
|
case 'f':
|
|
case OPT_USER_MODE:
|
|
force_flag = true;
|
|
if (sscanf(optarg, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
|
|
log_error("Error: use format <w>x<h>@<r>\n");
|
|
break;
|
|
|
|
case 'i':
|
|
user_ini_flag = true;
|
|
ini_file = optarg;
|
|
break;
|
|
|
|
case 'b':
|
|
df->set_api(optarg);
|
|
break;
|
|
|
|
case 'k':
|
|
keep_changes_flag = true;
|
|
df->set_keep_changes(true);
|
|
break;
|
|
|
|
case 'g':
|
|
double h_size; int h_shift, v_shift;
|
|
if (sscanf(optarg, "%lf:%d:%d", &h_size, &h_shift, &v_shift) < 3)
|
|
log_error("Error: use format --geometry <h_size>:<h_shift>:<v_shift>\n");
|
|
geometry_flag = true;
|
|
df->set_h_size(h_size);
|
|
df->set_h_shift(h_shift);
|
|
df->set_v_shift(v_shift);
|
|
break;
|
|
|
|
// Long options
|
|
case OPT_CRT_RANGE0:
|
|
case OPT_CRT_RANGE1:
|
|
case OPT_CRT_RANGE2:
|
|
case OPT_CRT_RANGE3:
|
|
case OPT_CRT_RANGE4:
|
|
case OPT_CRT_RANGE5:
|
|
case OPT_CRT_RANGE6:
|
|
case OPT_CRT_RANGE7:
|
|
case OPT_CRT_RANGE8:
|
|
case OPT_CRT_RANGE9:
|
|
case OPT_LCD_RANGE:
|
|
case OPT_MODELINE:
|
|
case OPT_API:
|
|
case OPT_LOCK_UNSUPPORTED_MODES:
|
|
case OPT_LOCK_SYSTEM_MODES:
|
|
case OPT_REFRESH_DONT_CARE:
|
|
case OPT_KEEP_CHANGES:
|
|
case OPT_MODELINE_GENERATION:
|
|
case OPT_INTERLACE:
|
|
case OPT_DOUBLESCAN:
|
|
case OPT_DOTCLOCK_MIN:
|
|
case OPT_SYNC_REFRESH_TOLERANCE:
|
|
case OPT_SUPER_WIDTH:
|
|
case OPT_V_SHIFT_CORRECT:
|
|
case OPT_H_SIZE:
|
|
case OPT_H_SHIFT:
|
|
case OPT_V_SHIFT:
|
|
case OPT_PIXEL_PRECISION:
|
|
case OPT_INTERLACE_FORCE_EVEN:
|
|
case OPT_SCALE_PROPORTIONAL:
|
|
case OPT_SCREEN_COMPOSITING:
|
|
case OPT_SCREEN_REORDERING:
|
|
case OPT_ALLOW_HARDWARE_REFRESH:
|
|
case OPT_CUSTOM_TIMING:
|
|
case OPT_VERBOSITY:
|
|
switchres.set_option(long_options[option_index].name, optarg);
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (help_flag)
|
|
goto usage;
|
|
|
|
// Get user video mode information from command line
|
|
if ((argc - optind) < 3)
|
|
{
|
|
log_error("Error: missing argument\n");
|
|
goto usage;
|
|
}
|
|
else if ((argc - optind) > 3)
|
|
{
|
|
log_error("Error: too many arguments\n");
|
|
goto usage;
|
|
}
|
|
else
|
|
{
|
|
resolution_flag = true;
|
|
width = atoi(argv[optind]);
|
|
height = atoi(argv[optind + 1]);
|
|
refresh = atof(argv[optind + 2]);
|
|
|
|
if (width <= 0 || height <= 0 || refresh <= 0.0f)
|
|
{
|
|
log_error("Error: wrong video mode request: %sx%s@%s\n", argv[optind], argv[optind + 1], argv[optind + 2]);
|
|
goto usage;
|
|
}
|
|
|
|
char scan_mode = argv[optind + 2][strlen(argv[optind + 2]) -1];
|
|
if (scan_mode == 'i')
|
|
interlaced_flag = true;
|
|
}
|
|
|
|
if (user_ini_flag)
|
|
switchres.parse_config(ini_file.c_str());
|
|
|
|
if (calculate_flag)
|
|
switchres.display()->set_screen("dummy");
|
|
|
|
switchres.add_display();
|
|
|
|
if (force_flag)
|
|
switchres.display()->set_user_mode(&user_mode);
|
|
|
|
if (!calculate_flag && !edid_flag)
|
|
{
|
|
for (auto &display : switchres.displays)
|
|
display->init();
|
|
}
|
|
|
|
if (resolution_flag)
|
|
{
|
|
for (auto &display : switchres.displays)
|
|
{
|
|
int flags = (interlaced_flag? SR_MODE_INTERLACED : 0) | (rotated_flag? SR_MODE_ROTATED : 0);
|
|
modeline *mode = display->get_mode(width, height, refresh, flags);
|
|
if (mode) display->flush_modes();
|
|
|
|
if (mode && geometry_flag)
|
|
{
|
|
monitor_range range = {};
|
|
modeline_to_monitor_range(&range, mode);
|
|
log_info("Adjusted geometry (%.3f:%d:%d) H: %.3f, %.3f, %.3f V: %.3f, %.3f, %.3f\n",
|
|
display->h_size(), display->h_shift(), display->v_shift(),
|
|
range.hfront_porch, range.hsync_pulse, range.hback_porch,
|
|
range.vfront_porch * 1000, range.vsync_pulse * 1000, range.vback_porch * 1000);
|
|
}
|
|
}
|
|
|
|
if (edid_flag)
|
|
{
|
|
edid_block edid = {};
|
|
modeline *mode = switchres.display()->selected_mode();
|
|
if (mode)
|
|
{
|
|
monitor_range *range = &switchres.display()->range[mode->range];
|
|
edid_from_modeline(mode, range, switchres.display()->monitor(), &edid);
|
|
|
|
char file_name[strlen(switchres.display()->monitor()) + 5];
|
|
sprintf(file_name, "%s.bin", switchres.display()->monitor());
|
|
|
|
FILE *file = fopen(file_name, "wb");
|
|
if (file)
|
|
{
|
|
fwrite(&edid, sizeof(edid), 1, file);
|
|
fclose (file);
|
|
log_info("EDID saved as %s\n", file_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (switch_flag) for (auto &display : switchres.displays) display->set_mode(display->selected_mode());
|
|
|
|
if (switch_flag && !launch_flag && !keep_changes_flag)
|
|
{
|
|
log_info("Press ENTER to exit...\n");
|
|
cin.get();
|
|
}
|
|
|
|
if (launch_flag)
|
|
{
|
|
status_code = system(launch_command.c_str());
|
|
#ifdef __linux__
|
|
status_code = WEXITSTATUS(status_code);
|
|
#endif
|
|
log_info("Process exited with value %d\n", status_code);
|
|
}
|
|
}
|
|
|
|
return (status_code);
|
|
|
|
usage:
|
|
show_usage();
|
|
return 0;
|
|
}
|
|
|
|
//============================================================
|
|
// show_version
|
|
//============================================================
|
|
|
|
int show_version()
|
|
{
|
|
char version[]
|
|
{
|
|
"Switchres " SWITCHRES_VERSION "\n"
|
|
"Modeline generation engine for emulation\n"
|
|
"Copyright (C) 2010-2024 - Chris Kennedy, Antonio Giner, Alexandre Wodarczyk, Gil Delescluse\n"
|
|
"License GPL-2.0+\n"
|
|
"This is free software: you are free to change and redistribute it.\n"
|
|
"There is NO WARRANTY, to the extent permitted by law.\n"
|
|
};
|
|
|
|
log_info("%s", version);
|
|
return 0;
|
|
}
|
|
|
|
//============================================================
|
|
// show_usage
|
|
//============================================================
|
|
|
|
int show_usage()
|
|
{
|
|
char usage[] =
|
|
{
|
|
"Usage: switchres <width> <height> <refresh> [options]\n"
|
|
"Options:\n"
|
|
" -c, --calc Calculate video mode and exit\n"
|
|
" -s, --switch Switch to video mode\n"
|
|
" -l, --launch <command> Launch <command>\n"
|
|
" -m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)\n"
|
|
" -a, --aspect <num:den> Monitor aspect ratio\n"
|
|
" -r, --rotated Rotate axes, preserving aspect ratio.\n"
|
|
" -d, --display <display_index> Use target display (index = 0, 1, 2...)\n"
|
|
" -f, --force <w>x<h>@<r> Force a specific video mode from display mode list\n"
|
|
" -i, --ini <file.ini> Specify an ini file\n"
|
|
" -e, --edid Create an EDID binary with calculated video modes\n"
|
|
" -k, --keep Keep changes on exit (warning: this disables cleanup)\n"
|
|
" -g, --geometry <adjustment> Adjust geometry of generated modeline\n"
|
|
" adjustment = <h_size>:<h_shift>:<v_shift>\n"
|
|
" e.g. switchres 640 480 60 -c -g 1.1:-1:2\n\n"
|
|
"For more options, refer to switchres.ini. All options in switchres.ini can be applied in\n"
|
|
"command line as long options, e.g.: switchres 256 224 57.55 -c --dotclock_min 8.0\n\n"
|
|
};
|
|
|
|
log_info("%s", usage);
|
|
return 0;
|
|
}
|