add display option for multi-monitor systems
This commit is contained in:
parent
7f8b72d864
commit
58b964baba
4 changed files with 175 additions and 32 deletions
|
@ -92,6 +92,7 @@
|
|||
CONFIGDEF_INT (FULLSCREEN_DESKTOP, "fullscreen_desktop_mode", 1) \
|
||||
CONFIGDEF_INT (VID_WIDTH, "vid_width", RESX) \
|
||||
CONFIGDEF_INT (VID_HEIGHT, "vid_height", RESY) \
|
||||
CONFIGDEF_INT (VID_DISPLAY, "vid_display", 0) \
|
||||
CONFIGDEF_INT (VID_RESIZABLE, "vid_resizable", 0) \
|
||||
CONFIGDEF_INT (VID_FRAMESKIP, "vid_frameskip", 1) \
|
||||
CONFIGDEF_INT (VSYNC, "vsync", CONFIG_VSYNC_DEFAULT) \
|
||||
|
|
|
@ -30,6 +30,7 @@ typedef enum BindingType {
|
|||
BT_GamepadKeyBinding,
|
||||
BT_GamepadAxisBinding,
|
||||
BT_GamepadDevice,
|
||||
BT_VideoDisplay,
|
||||
} BindingType;
|
||||
|
||||
typedef struct OptionBinding {
|
||||
|
@ -321,6 +322,24 @@ static int bind_common_onoffplus_set(OptionBinding *b, int v) {
|
|||
#define bind_common_int_get bind_common_onoff_inverted_get
|
||||
#define bind_common_int_set bind_common_onoff_inverted_set
|
||||
|
||||
// BT_VideoDisplay: fullscreen display number
|
||||
static OptionBinding* bind_video_display(int cfgentry) {
|
||||
OptionBinding *bind = bind_new();
|
||||
|
||||
bind->configentry = cfgentry;
|
||||
bind->type = BT_VideoDisplay;
|
||||
|
||||
bind->getter = bind_common_int_get;
|
||||
bind->setter = bind_common_int_set;
|
||||
|
||||
bind->valrange_min = 0;
|
||||
bind->valrange_max = 0; // updated later
|
||||
|
||||
bind->selected = video_current_display();
|
||||
|
||||
return bind;
|
||||
}
|
||||
|
||||
static int bind_common_intplus1_get(OptionBinding *b) {
|
||||
return config_get_int(b->configentry) - 1;
|
||||
}
|
||||
|
@ -374,6 +393,8 @@ typedef struct OptionsMenuContext {
|
|||
} OptionsMenuContext;
|
||||
|
||||
static void destroy_options_menu(MenuData *m) {
|
||||
bool change_vidmode = false;
|
||||
|
||||
for(int i = 0; i < m->ecount; ++i) {
|
||||
OptionBinding *bind = bind_get(m, i);
|
||||
|
||||
|
@ -384,14 +405,9 @@ static void destroy_options_menu(MenuData *m) {
|
|||
if(bind->type == BT_Resolution && video_query_capability(VIDEO_CAP_CHANGE_RESOLUTION) == VIDEO_AVAILABLE) {
|
||||
if(bind->selected != -1) {
|
||||
VideoMode *mode = video.modes + bind->selected;
|
||||
|
||||
video_set_mode(mode->width, mode->height,
|
||||
config_get_int(CONFIG_FULLSCREEN),
|
||||
config_get_int(CONFIG_VID_RESIZABLE)
|
||||
);
|
||||
|
||||
config_set_int(CONFIG_VID_WIDTH, video.intended.width);
|
||||
config_set_int(CONFIG_VID_HEIGHT, video.intended.height);
|
||||
config_set_int(CONFIG_VID_WIDTH, mode->width);
|
||||
config_set_int(CONFIG_VID_HEIGHT, mode->height);
|
||||
change_vidmode = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,6 +415,16 @@ static void destroy_options_menu(MenuData *m) {
|
|||
free(bind);
|
||||
}
|
||||
|
||||
if(change_vidmode) {
|
||||
video_set_mode(
|
||||
config_get_int(CONFIG_VID_DISPLAY),
|
||||
config_get_int(CONFIG_VID_WIDTH),
|
||||
config_get_int(CONFIG_VID_HEIGHT),
|
||||
config_get_int(CONFIG_FULLSCREEN),
|
||||
config_get_int(CONFIG_VID_RESIZABLE)
|
||||
);
|
||||
}
|
||||
|
||||
if(m->context) {
|
||||
OptionsMenuContext *ctx = m->context;
|
||||
free(ctx->data);
|
||||
|
@ -455,11 +481,16 @@ static MenuData* create_options_menu_video(MenuData *parent) {
|
|||
MenuData *m = create_options_menu_base("Video Options");
|
||||
OptionBinding *b;
|
||||
|
||||
|
||||
add_menu_entry(m, "Fullscreen", do_nothing,
|
||||
b = bind_option(CONFIG_FULLSCREEN, bind_common_onoff_get, bind_common_onoff_set)
|
||||
); bind_onoff(b);
|
||||
b->dependence = bind_fullscreen_dependence;
|
||||
|
||||
add_menu_entry(m, "Display", do_nothing,
|
||||
b = bind_video_display(CONFIG_VID_DISPLAY)
|
||||
);
|
||||
|
||||
add_menu_entry(m, "Window size", do_nothing,
|
||||
b = bind_resolution()
|
||||
); b->setter = bind_resolution_set;
|
||||
|
@ -470,12 +501,12 @@ static MenuData* create_options_menu_video(MenuData *parent) {
|
|||
); bind_onoff(b);
|
||||
b->dependence = bind_resizable_dependence;
|
||||
|
||||
add_menu_entry(m, "Pause the game when it's not focused", do_nothing,
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Pause the game when not focused", do_nothing,
|
||||
b = bind_option(CONFIG_FOCUS_LOSS_PAUSE, bind_common_onoff_get, bind_common_onoff_set)
|
||||
); bind_onoff(b);
|
||||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Vertical synchronization", do_nothing,
|
||||
b = bind_option(CONFIG_VSYNC, bind_common_onoffplus_get, bind_common_onoffplus_set)
|
||||
); bind_addvalue(b, "on");
|
||||
|
@ -972,7 +1003,7 @@ static void draw_options_menu(MenuData *menu) {
|
|||
case BT_GamepadDevice: {
|
||||
if(bind_isactive(bind)) {
|
||||
// XXX: I'm not exactly a huge fan of fixing up state in drawing code, but it seems the way to go for now...
|
||||
bind->valrange_max = gamepad_device_count();
|
||||
bind->valrange_max = gamepad_device_count() - 1;
|
||||
|
||||
if(bind->selected < 0 || bind->selected > bind->valrange_max) {
|
||||
bind->selected = gamepad_current_device_num();
|
||||
|
@ -1003,6 +1034,29 @@ static void draw_options_menu(MenuData *menu) {
|
|||
break;
|
||||
}
|
||||
|
||||
case BT_VideoDisplay: {
|
||||
// XXX: see the BT_GamepadDevice case...
|
||||
bind->valrange_max = video_num_displays() - 1;
|
||||
|
||||
if(bind->selected < 0 || bind->selected > bind->valrange_max) {
|
||||
bind->selected = video_current_display();
|
||||
|
||||
if(bind->selected < 0) {
|
||||
bind->selected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "#%i: %s", bind->selected + 1, video_display_name(bind->selected));
|
||||
text_draw(buf, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
.max_width = (SCREEN_W - 220) / 2,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case BT_GamepadKeyBinding:
|
||||
case BT_GamepadAxisBinding: {
|
||||
bool is_axis = (bind->type == BT_GamepadAxisBinding);
|
||||
|
@ -1378,6 +1432,7 @@ static bool options_input_handler(SDL_Event *event, void *arg) {
|
|||
case BT_GamepadDevice:
|
||||
case BT_IntValue:
|
||||
case BT_Resolution:
|
||||
case BT_VideoDisplay:
|
||||
(next ? bind_setnext : bind_setprev)(bind);
|
||||
break;
|
||||
|
||||
|
|
119
src/video.c
119
src/video.c
|
@ -185,7 +185,7 @@ static const char* modeflagsstr(uint32_t flags) {
|
|||
}
|
||||
}
|
||||
|
||||
static void video_new_window_internal(int w, int h, uint32_t flags, bool fallback) {
|
||||
static void video_new_window_internal(uint display, uint w, uint h, uint32_t flags, bool fallback) {
|
||||
if(video.window) {
|
||||
SDL_DestroyWindow(video.window);
|
||||
video.window = NULL;
|
||||
|
@ -193,7 +193,12 @@ static void video_new_window_internal(int w, int h, uint32_t flags, bool fallbac
|
|||
|
||||
char title[sizeof(WINDOW_TITLE) + strlen(TAISEI_VERSION) + 2];
|
||||
snprintf(title, sizeof(title), "%s v%s", WINDOW_TITLE, TAISEI_VERSION);
|
||||
video.window = r_create_window(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flags);
|
||||
video.window = r_create_window(
|
||||
title,
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
w, h, flags
|
||||
);
|
||||
|
||||
if(video.window) {
|
||||
SDL_SetWindowMinimumSize(video.window, SCREEN_W / 4, SCREEN_H / 4);
|
||||
|
@ -206,10 +211,10 @@ static void video_new_window_internal(int w, int h, uint32_t flags, bool fallbac
|
|||
return;
|
||||
}
|
||||
|
||||
video_new_window_internal(RESX, RESY, flags & ~SDL_WINDOW_FULLSCREEN_DESKTOP, true);
|
||||
video_new_window_internal(display, RESX, RESY, flags & ~SDL_WINDOW_FULLSCREEN_DESKTOP, true);
|
||||
}
|
||||
|
||||
static void video_new_window(int w, int h, bool fs, bool resizable) {
|
||||
static void video_new_window(uint display, uint w, uint h, bool fs, bool resizable) {
|
||||
uint32_t flags = 0;
|
||||
|
||||
if(fs) {
|
||||
|
@ -218,21 +223,23 @@ static void video_new_window(int w, int h, bool fs, bool resizable) {
|
|||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
|
||||
video_new_window_internal(w, h, flags, false);
|
||||
video_new_window_internal(display, w, h, flags, false);
|
||||
display = video_current_display();
|
||||
|
||||
log_info("Created a new window: %ix%i (%s)",
|
||||
log_info("Created a new window: %ix%i (%s), on display #%i %s",
|
||||
video.current.width,
|
||||
video.current.height,
|
||||
modeflagsstr(SDL_GetWindowFlags(video.window))
|
||||
modeflagsstr(SDL_GetWindowFlags(video.window)),
|
||||
display,
|
||||
video_display_name(display)
|
||||
);
|
||||
|
||||
events_pause_keyrepeat();
|
||||
SDL_RaiseWindow(video.window);
|
||||
}
|
||||
|
||||
static bool video_set_display_mode(int w, int h) {
|
||||
static bool video_set_display_mode(uint display, uint w, uint h) {
|
||||
SDL_DisplayMode closest, target = { .w = w, .h = h };
|
||||
int display = SDL_GetWindowDisplayIndex(video.window);
|
||||
|
||||
if(!SDL_GetClosestDisplayMode(display, &target, &closest)) {
|
||||
log_error("No available display modes for %ix%i on display %i", w, h, display);
|
||||
|
@ -262,18 +269,32 @@ static void video_set_fullscreen_internal(bool fullscreen) {
|
|||
SDL_RaiseWindow(video.window);
|
||||
}
|
||||
|
||||
void video_set_mode(int w, int h, bool fs, bool resizable) {
|
||||
void video_set_mode(uint display, uint w, uint h, bool fs, bool resizable) {
|
||||
video.intended.width = w;
|
||||
video.intended.height = h;
|
||||
|
||||
if(display >= video_num_displays()) {
|
||||
log_warn("Display index %u is invalid, falling back to 0 (%s)", display, video_display_name(0));
|
||||
display = 0;
|
||||
}
|
||||
|
||||
if(!video.window) {
|
||||
video_new_window(w, h, fs, resizable);
|
||||
video_new_window(display, w, h, fs, resizable);
|
||||
return;
|
||||
}
|
||||
|
||||
if(w != video.current.width || h != video.current.height) {
|
||||
uint old_display = video_current_display();
|
||||
bool display_changed = display != old_display;
|
||||
bool size_changed = w != video.current.width || h != video.current.height;
|
||||
|
||||
if(display_changed) {
|
||||
video_new_window(display, w, h, fs, resizable);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size_changed) {
|
||||
if(fs && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
|
||||
video_set_display_mode(w, h);
|
||||
video_set_display_mode(display, w, h);
|
||||
video_set_fullscreen_internal(fs);
|
||||
video_update_mode_settings();
|
||||
} else if(video.backend == VIDEO_BACKEND_X11) {
|
||||
|
@ -282,11 +303,11 @@ void video_set_mode(int w, int h, bool fs, bool resizable) {
|
|||
// and we'd never know. SDL_GL_GetDrawableSize/SDL_GetWindowSize aren't helping as of SDL 2.0.5.
|
||||
//
|
||||
// There's not much to be done about it. We're at mercy of SDL here and SDL is at mercy of the WM.
|
||||
video_new_window(w, h, fs, resizable);
|
||||
video_new_window(display, w, h, fs, resizable);
|
||||
return;
|
||||
} else if(video.backend == VIDEO_BACKEND_EMSCRIPTEN && !fs) {
|
||||
// Needed to work around various SDL bugs and HTML/DOM quirks...
|
||||
video_new_window(w, h, fs, resizable);
|
||||
video_new_window(display, w, h, fs, resizable);
|
||||
return;
|
||||
} else {
|
||||
SDL_SetWindowSize(video.window, w, h);
|
||||
|
@ -299,7 +320,23 @@ void video_set_mode(int w, int h, bool fs, bool resizable) {
|
|||
}
|
||||
|
||||
void video_set_fullscreen(bool fullscreen) {
|
||||
video_set_mode(video.intended.width, video.intended.height, fullscreen, config_get_int(CONFIG_VID_RESIZABLE));
|
||||
video_set_mode(
|
||||
SDL_GetWindowDisplayIndex(video.window),
|
||||
video.intended.width,
|
||||
video.intended.height,
|
||||
fullscreen,
|
||||
config_get_int(CONFIG_VID_RESIZABLE)
|
||||
);
|
||||
}
|
||||
|
||||
void video_set_display(uint idx) {
|
||||
video_set_mode(
|
||||
idx,
|
||||
video.intended.width,
|
||||
video.intended.height,
|
||||
config_get_int(CONFIG_FULLSCREEN),
|
||||
config_get_int(CONFIG_VID_RESIZABLE)
|
||||
);
|
||||
}
|
||||
|
||||
static void* video_screenshot_task(void *arg) {
|
||||
|
@ -486,7 +523,7 @@ static void video_handle_resize(int w, int h) {
|
|||
log_warn("Bad resize: %ix%i is too small!", w, h);
|
||||
// FIXME: the video_new_window is actually a workaround for Wayland.
|
||||
// I'm not sure if it's necessary for anything else.
|
||||
video_new_window(video.intended.width, video.intended.height, false, video_is_resizable());
|
||||
video_new_window(video_current_display(), video.intended.width, video.intended.height, false, video_is_resizable());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -521,6 +558,10 @@ static bool video_handle_config_event(SDL_Event *evt, void *arg) {
|
|||
video_set_fullscreen(val->i);
|
||||
break;
|
||||
|
||||
case CONFIG_VID_DISPLAY:
|
||||
video_set_display(val->i);
|
||||
break;
|
||||
|
||||
case CONFIG_VID_RESIZABLE:
|
||||
SDL_SetWindowResizable(video.window, val->i);
|
||||
break;
|
||||
|
@ -533,6 +574,46 @@ static bool video_handle_config_event(SDL_Event *evt, void *arg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint video_num_displays(void) {
|
||||
int displays = SDL_GetNumVideoDisplays();
|
||||
|
||||
if(displays < 1) {
|
||||
if(displays == 0) {
|
||||
log_warn("SDL_GetNumVideoDisplays() returned 0, this shouldn't happen");
|
||||
} else {
|
||||
log_sdl_error(LOG_WARN, "SDL_GetNumVideoDisplays");
|
||||
}
|
||||
|
||||
displays = 1;
|
||||
}
|
||||
|
||||
return displays;
|
||||
}
|
||||
|
||||
const char *video_display_name(uint id) {
|
||||
assert(id < video_num_displays());
|
||||
|
||||
const char *name = SDL_GetDisplayName(id);
|
||||
|
||||
if(name == NULL) {
|
||||
log_sdl_error(LOG_WARN, "SDL_GetDisplayName");
|
||||
name = "Unknown";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
uint video_current_display(void) {
|
||||
int display = SDL_GetWindowDisplayIndex(video.window);
|
||||
|
||||
if(display < 0) {
|
||||
log_sdl_error(LOG_WARN, "SDL_GetWindowDisplayIndex");
|
||||
display = 1;
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
void video_init(void) {
|
||||
bool fullscreen_available = false;
|
||||
|
||||
|
@ -566,7 +647,8 @@ void video_init(void) {
|
|||
|
||||
// Register all resolutions that are available in fullscreen
|
||||
|
||||
for(int s = 0; s < SDL_GetNumVideoDisplays(); ++s) {
|
||||
for(int s = 0; s < video_num_displays(); ++s) {
|
||||
log_info("Found display #%i: %s", s, video_display_name(s));
|
||||
for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
|
||||
SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
|
||||
|
||||
|
@ -609,6 +691,7 @@ void video_init(void) {
|
|||
qsort(video.modes, video.mcount, sizeof(VideoMode), video_compare_modes);
|
||||
|
||||
video_set_mode(
|
||||
config_get_int(CONFIG_VID_DISPLAY),
|
||||
config_get_int(CONFIG_VID_WIDTH),
|
||||
config_get_int(CONFIG_VID_HEIGHT),
|
||||
config_get_int(CONFIG_FULLSCREEN),
|
||||
|
|
|
@ -46,7 +46,7 @@ typedef enum VideoBackend {
|
|||
typedef struct {
|
||||
VideoMode *modes;
|
||||
SDL_Window *window;
|
||||
int mcount;
|
||||
uint mcount;
|
||||
VideoMode intended;
|
||||
VideoMode current;
|
||||
VideoBackend backend;
|
||||
|
@ -70,7 +70,7 @@ extern Video video;
|
|||
|
||||
void video_init(void);
|
||||
void video_shutdown(void);
|
||||
void video_set_mode(int w, int h, bool fs, bool resizable);
|
||||
void video_set_mode(uint display, uint w, uint h, bool fs, bool resizable);
|
||||
void video_set_fullscreen(bool fullscreen);
|
||||
void video_get_viewport(FloatRect *vp);
|
||||
void video_get_viewport_size(float *width, float *height);
|
||||
|
@ -79,5 +79,9 @@ bool video_is_resizable(void);
|
|||
extern VideoCapabilityState (*video_query_capability)(VideoCapability cap);
|
||||
void video_take_screenshot(void);
|
||||
void video_swap_buffers(void);
|
||||
uint video_num_displays(void);
|
||||
uint video_current_display(void);
|
||||
void video_set_display(uint idx);
|
||||
const char *video_display_name(uint id) attr_returns_nonnull;
|
||||
|
||||
#endif // IGUARD_video_h
|
||||
|
|
Loading…
Reference in a new issue