diff --git a/Makefile.common b/Makefile.common
index cde29a815d..37544e28f2 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1391,7 +1391,8 @@ OBJ += gfx/drivers_context/gfx_null_ctx.o
ifeq ($(HAVE_KMS), 1)
HAVE_AND_WILL_USE_DRM = 1
- OBJ += gfx/drivers_context/drm_ctx.o
+ OBJ += gfx/drivers_context/drm_ctx.o \
+ gfx/display_servers/dispserv_kms.o
ifeq ($(HAVE_ODROIDGO2), 1)
OBJ += gfx/drivers_context/drm_go2_ctx.o
diff --git a/gfx/display_servers/dispserv_kms.c b/gfx/display_servers/dispserv_kms.c
new file mode 100644
index 0000000000..e434501847
--- /dev/null
+++ b/gfx/display_servers/dispserv_kms.c
@@ -0,0 +1,233 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2016-2019 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
+
+
+#include "../video_display_server.h"
+#include "../../retroarch.h"
+#include "../video_crt_switch.h" /* Needed to set aspect for low resolution in Linux */
+#include "../common/drm_common.h"
+#include "../../verbosity.h"
+
+typedef struct
+{
+ int crt_name_id;
+ int monitor_index;
+ unsigned opacity;
+ uint8_t flags;
+ char crt_name[16];
+ char new_mode[256];
+ char old_mode[256];
+ char orig_output[256];
+} dispserv_kms_t;
+
+static bool kms_display_server_set_resolution(void *data,
+ unsigned width, unsigned height, int int_hz, float hz,
+ int center, int monitor_index, int xoffset, int padjust)
+{
+ unsigned curr_width = 0;
+ unsigned curr_height = 0;
+ unsigned curr_bpp = 0;
+ float curr_refreshrate = 0;
+ bool retval = false;
+ int reinit_flags = DRIVERS_CMD_ALL;
+ dispserv_kms_t *dispserv = (dispserv_kms_t*)data;
+
+ if (!dispserv)
+ return false;
+
+ if (g_drm_mode)
+ {
+ curr_refreshrate = drm_calc_refresh_rate(g_drm_mode);
+ curr_width = g_drm_mode->hdisplay;
+ curr_height = g_drm_mode->vdisplay;
+ curr_bpp = 32;
+ }
+ RARCH_DBG("[DRM]: Display server set resolution - incoming: %d x %d, %f Hz\n",width, height, hz);
+
+ if (width == 0)
+ width = curr_width;
+ if (height == 0)
+ height = curr_height;
+ if (curr_bpp == 0)
+ curr_bpp = curr_bpp;
+ if (hz == 0)
+ hz = curr_refreshrate;
+
+ /* set core refresh from hz */
+ video_monitor_set_refresh_rate(hz);
+
+ RARCH_DBG("[DRM]: Display server set resolution - actual: %d x %d, %f Hz\n",width, height, hz);
+
+ retval = video_driver_set_video_mode(width, height, true);
+
+ /* Reinitialize drivers. */
+ command_event(CMD_EVENT_REINIT, &reinit_flags);
+
+ return retval;
+}
+
+/* TODO: move to somewhere common as it is reused from dispserv_win32.c */
+/* Display resolution list qsort helper function */
+static int resolution_list_qsort_func(
+ const video_display_config_t *a, const video_display_config_t *b)
+{
+ char str_a[64];
+ char str_b[64];
+
+ if (!a || !b)
+ return 0;
+
+ str_a[0] = str_b[0] = '\0';
+
+ snprintf(str_a, sizeof(str_a), "%04dx%04d (%d Hz)",
+ a->width,
+ a->height,
+ a->refreshrate);
+
+ snprintf(str_b, sizeof(str_b), "%04dx%04d (%d Hz)",
+ b->width,
+ b->height,
+ b->refreshrate);
+
+ return strcasecmp(str_a, str_b);
+}
+
+static void *kms_display_server_get_resolution_list(
+ void *data, unsigned *len)
+{
+ unsigned i = 0;
+ unsigned j = 0;
+ unsigned count = 0;
+ unsigned curr_width = 0;
+ unsigned curr_height = 0;
+ unsigned curr_bpp = 0;
+ float curr_refreshrate = 0;
+ unsigned curr_orientation = 0;
+ struct video_display_config *conf = NULL;
+
+
+ if (g_drm_mode)
+ {
+ curr_refreshrate = drm_calc_refresh_rate(g_drm_mode);
+ curr_width = g_drm_mode->hdisplay;
+ curr_height = g_drm_mode->vdisplay;
+ curr_bpp = 32;
+ }
+
+ *len = g_drm_connector->count_modes;
+ if (!(conf = (struct video_display_config*)
+ calloc(*len, sizeof(struct video_display_config))))
+ return NULL;
+
+ for (i = 0, j = 0; (int)i < g_drm_connector->count_modes; i++)
+ {
+ conf[j].width = g_drm_connector->modes[i].hdisplay;
+ conf[j].height = g_drm_connector->modes[i].vdisplay;
+ conf[j].bpp = 32;
+ conf[j].refreshrate = floor(drm_calc_refresh_rate(&g_drm_connector->modes[i]));
+ conf[j].idx = j;
+ conf[j].current = false;
+
+ if ( (conf[j].width == curr_width)
+ && (conf[j].height == curr_height)
+ && (conf[j].bpp == curr_bpp)
+ && (drm_calc_refresh_rate(&g_drm_connector->modes[i]) == curr_refreshrate)
+ )
+ conf[j].current = true;
+ j++;
+ }
+
+ qsort(
+ conf, count,
+ sizeof(video_display_config_t),
+ (int (*)(const void *, const void *))
+ resolution_list_qsort_func);
+
+ return conf;
+}
+
+/* TODO: screen orientation has support in DRM via planes, although not really exposed via xf86drm */
+#if 0
+static void kms_display_server_set_screen_orientation(void *data,
+ enum rotation rotation)
+{
+}
+
+static enum rotation kms_display_server_get_screen_orientation(void *data)
+{
+ int i, j;
+ enum rotation rotation = ORIENTATION_NORMAL;
+ dispserv_kms_t *dispserv = (dispserv_kms_t*)data;
+ return rotation;
+}
+#endif
+
+static void* kms_display_server_init(void)
+{
+ dispserv_kms_t *dispserv = (dispserv_kms_t*)calloc(1, sizeof(*dispserv));
+
+ if (dispserv)
+ return dispserv;
+ return NULL;
+}
+
+static void kms_display_server_destroy(void *data)
+{
+ dispserv_kms_t *dispserv = (dispserv_kms_t*)data;
+ if (dispserv)
+ free(dispserv);
+}
+
+static bool kms_display_server_set_window_opacity(void *data, unsigned opacity)
+{
+ return true;
+}
+
+static uint32_t kms_display_server_get_flags(void *data)
+{
+ uint32_t flags = 0;
+ BIT32_SET(flags, DISPSERV_CTX_CRT_SWITCHRES);
+
+ return flags;
+}
+
+const video_display_server_t dispserv_kms = {
+ kms_display_server_init,
+ kms_display_server_destroy,
+ kms_display_server_set_window_opacity,
+ NULL, /* set_window_progress */
+ NULL, /* set window decorations */
+ kms_display_server_set_resolution,
+ kms_display_server_get_resolution_list,
+ NULL, /* get output options */
+ NULL, /* kms_display_server_set_screen_orientation */
+ NULL, /* kms_display_server_get_screen_orientation */
+ kms_display_server_get_flags,
+ "kms"
+};
diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c
index 5b59e4c129..c11c5e90c8 100644
--- a/gfx/drivers_context/drm_ctx.c
+++ b/gfx/drivers_context/drm_ctx.c
@@ -634,6 +634,19 @@ static void gfx_ctx_drm_get_video_size(void *data,
*height = drm->fb_height;
}
+static void gfx_ctx_drm_get_video_output_size(void *data,
+ unsigned *width, unsigned *height, char *desc, size_t desc_len)
+{
+ gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
+
+ if (!drm)
+ return;
+
+ *width = drm->fb_width;
+ *height = drm->fb_height;
+
+}
+
static void free_drm_resources(gfx_ctx_drm_data_t *drm)
{
if (!drm)
@@ -783,6 +796,8 @@ nextgpu:
g_drm_fd = fd;
+ video_driver_display_type_set(RARCH_DISPLAY_KMS);
+
return drm;
error:
@@ -831,7 +846,10 @@ static bool gfx_ctx_drm_set_video_mode(void *data,
return true;
}
if ((width == 0 && height == 0) || !fullscreen)
+ {
g_drm_mode = &g_drm_connector->modes[0];
+ RARCH_WARN("[KMS]: Falling back to mode 0 (default)\n");
+ }
else
{
/* check if custom HDMI timings were asked */
@@ -916,6 +934,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data,
error:
gfx_ctx_drm_destroy_resources(drm);
+ RARCH_ERR("[KMS]: Error when switching mode.\n");
if (drm)
free(drm);
@@ -1074,7 +1093,7 @@ const gfx_ctx_driver_t gfx_ctx_drm = {
gfx_ctx_drm_set_video_mode,
gfx_ctx_drm_get_video_size,
drm_get_refresh_rate,
- NULL, /* get_video_output_size */
+ gfx_ctx_drm_get_video_output_size,
NULL, /* get_video_output_prev */
NULL, /* get_video_output_next */
NULL, /* get_metrics */
diff --git a/gfx/video_defines.h b/gfx/video_defines.h
index 6bf67c7ccb..0dcdc694e3 100644
--- a/gfx/video_defines.h
+++ b/gfx/video_defines.h
@@ -101,7 +101,8 @@ enum rarch_display_type
/* video_display => N/A, video_window => HWND */
RARCH_DISPLAY_WIN32,
RARCH_DISPLAY_WAYLAND,
- RARCH_DISPLAY_OSX
+ RARCH_DISPLAY_OSX,
+ RARCH_DISPLAY_KMS
};
enum font_driver_render_api
diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h
index 52392a1736..f29ac527e3 100644
--- a/gfx/video_display_server.h
+++ b/gfx/video_display_server.h
@@ -99,6 +99,7 @@ enum rotation video_display_server_get_screen_orientation(void);
extern const video_display_server_t dispserv_win32;
extern const video_display_server_t dispserv_x11;
+extern const video_display_server_t dispserv_kms;
extern const video_display_server_t dispserv_android;
RETRO_END_DECLS
diff --git a/gfx/video_driver.c b/gfx/video_driver.c
index 160029c4c1..3b03f81fe7 100644
--- a/gfx/video_driver.c
+++ b/gfx/video_driver.c
@@ -686,21 +686,22 @@ void video_monitor_compute_fps_statistics(uint64_t
void video_monitor_set_refresh_rate(float hz)
{
- char msg[128];
+ char msg[256];
+ char rate[8];
settings_t *settings = config_get_ptr();
- /* TODO/FIXME - localize */
- size_t _len = strlcpy(msg, "Setting refresh rate to", sizeof(msg));
- msg[_len ] = ':';
- msg[++_len] = ' ';
- msg[++_len] = '\0';
- _len += snprintf(msg + _len, sizeof(msg) - _len, "%.3f", hz);
- msg[_len ] = ' ';
- msg[_len+1] = 'H';
- msg[_len+2] = 'z';
- msg[_len+3] = '.';
- msg[_len+4] = '\0';
+
+ /* Avoid message spamming if there is no change. */
+ if (settings->floats.video_refresh_rate == hz)
+ return;
+
+ snprintf(rate, sizeof(rate), "%.3f", hz);
+ snprintf(msg, sizeof(msg),
+ msg_hash_to_str(MSG_VIDEO_REFRESH_RATE_CHANGED), rate);
+
+ /* Message is visible for twice the usual duration */
+ /* as modeswitch will cause monitors to go blank for a while */
if (settings->bools.notification_show_refresh_rate)
- runloop_msg_queue_push(msg, 1, 180, false, NULL,
+ runloop_msg_queue_push(msg, 1, 360, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_LOG("[Video]: %s\n", msg);
@@ -1064,6 +1065,11 @@ void* video_display_server_init(enum rarch_display_type type)
case RARCH_DISPLAY_X11:
#if defined(HAVE_X11)
current_display_server = &dispserv_x11;
+#endif
+ break;
+ case RARCH_DISPLAY_KMS:
+#if defined(HAVE_KMS)
+ current_display_server = &dispserv_kms;
#endif
break;
default:
@@ -1140,6 +1146,7 @@ bool video_display_server_set_resolution(unsigned width, unsigned height,
int int_hz, float hz, int center, int monitor_index, int xoffset, int padjust)
{
video_driver_state_t *video_st = &video_driver_st;
+ RARCH_DBG("[Video]: Display server set resolution, hz: %f\n",hz);
if (current_display_server && current_display_server->set_resolution)
return current_display_server->set_resolution(
video_st->current_display_server_data, width, height, int_hz,
@@ -1254,6 +1261,7 @@ void video_switch_refresh_rate_maybe(
bool video_display_server_set_refresh_rate(float hz)
{
video_driver_state_t *video_st = &video_driver_st;
+ RARCH_DBG("[Video]: Display server set refresh rate %f\n", hz);
if (current_display_server && current_display_server->set_resolution)
return current_display_server->set_resolution(
video_st->current_display_server_data, 0, 0, (int)hz,
@@ -1270,7 +1278,7 @@ void video_display_server_restore_refresh_rate(void)
if (!refresh_rate_original || refresh_rate_current == refresh_rate_original)
return;
-
+ RARCH_DBG("[Video]: Display server restore refresh rate %f\n", refresh_rate_original);
video_monitor_set_refresh_rate(refresh_rate_original);
video_display_server_set_refresh_rate(refresh_rate_original);
}
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 3b5e667c72..9e7eb0bf65 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -311,6 +311,7 @@ VIDEO CONTEXT
#if defined(HAVE_KMS)
#include "../gfx/drivers_context/drm_ctx.c"
+#include "../gfx/display_servers/dispserv_kms.c"
#endif
#if defined(HAVE_EGL)
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index d984796d37..359b576236 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -14713,6 +14713,10 @@ MSG_HASH(
MSG_VRR_RUNLOOP_DISABLED,
"Sync to exact content framerate disabled."
)
+MSG_HASH(
+ MSG_VIDEO_REFRESH_RATE_CHANGED,
+ "Video refresh rate changed to %s Hz."
+ )
/* Lakka */
@@ -14797,7 +14801,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_SCREEN_RESOLUTION,
- "Select display mode."
+ "Select display mode (Restart required)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHUTDOWN,
diff --git a/msg_hash.h b/msg_hash.h
index 548e47197b..240f7eb5ac 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -560,6 +560,7 @@ enum msg_hash_enums
MSG_FAILED_TO_ENTER_GAMEMODE_LINUX,
MSG_VRR_RUNLOOP_ENABLED,
MSG_VRR_RUNLOOP_DISABLED,
+ MSG_VIDEO_REFRESH_RATE_CHANGED,
MSG_IOS_TOUCH_MOUSE_ENABLED,
MSG_IOS_TOUCH_MOUSE_DISABLED,
diff --git a/retroarch.c b/retroarch.c
index 125f693280..f029da52c8 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -802,6 +802,7 @@ void drivers_init(
bool windowed_fullscreen = settings->bools.video_fullscreen && settings->bools.video_windowed_fullscreen;
bool all_fullscreen = settings->bools.video_fullscreen || settings->bools.video_windowed_fullscreen;
+ /* Making a switch from PC standard 60 Hz to NTSC 59.94 is excluded by the last condition. */
if ( refresh_rate > 0.0
&& !settings->uints.crt_switch_resolution
&& !settings->bools.vrr_runloop_enable
@@ -7013,6 +7014,7 @@ bool retroarch_main_quit(void)
video_driver_state_t*video_st = video_state_get_ptr();
settings_t *settings = config_get_ptr();
bool config_save_on_exit = settings->bools.config_save_on_exit;
+ struct retro_system_av_info *av_info = &video_st->av_info;
/* Restore video driver before saving */
video_driver_restore_cached(settings);
@@ -7052,9 +7054,15 @@ bool retroarch_main_quit(void)
/* Restore original refresh rate, if it has been changed
* automatically in SET_SYSTEM_AV_INFO */
- if (video_st->video_refresh_rate_original)
- video_display_server_restore_refresh_rate();
+ if (video_st->video_refresh_rate_original)
+ {
+ RARCH_DBG("[Video]: Restoring original refresh rate: %f Hz\n", video_st->video_refresh_rate_original);
+ /* Set the av_info fps also to the original refresh rate */
+ /* to avoid re-initialization problems */
+ av_info->timing.fps = video_st->video_refresh_rate_original;
+ video_display_server_restore_refresh_rate();
+ }
if (!(runloop_st->flags & RUNLOOP_FLAG_SHUTDOWN_INITIATED))
{
/* Save configs before quitting
diff --git a/runloop.c b/runloop.c
index 49124726c1..a396ddc3a6 100644
--- a/runloop.c
+++ b/runloop.c
@@ -3912,8 +3912,13 @@ void runloop_event_deinit_core(void)
/* Restore original refresh rate, if it has been changed
* automatically in SET_SYSTEM_AV_INFO */
if (video_st->video_refresh_rate_original)
+ {
+ /* Set the av_info fps also to the original refresh rate */
+ /* to avoid re-initialization problems */
+ struct retro_system_av_info *av_info = &video_st->av_info;
+ av_info->timing.fps = video_st->video_refresh_rate_original;
video_display_server_restore_refresh_rate();
-
+ }
/* Recalibrate frame delay target */
if (settings->bools.video_frame_delay_auto)
video_st->frame_delay_target = 0;