2012-07-28 22:53:53 +02:00
|
|
|
/*
|
|
|
|
* This software is licensed under the terms of the MIT-License
|
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2018-01-04 18:14:31 +01:00
|
|
|
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
2012-07-28 22:53:53 +02:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2017-03-06 01:25:59 +01:00
|
|
|
#include <png.h>
|
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
#include "global.h"
|
|
|
|
#include "video.h"
|
2018-04-12 16:08:48 +02:00
|
|
|
#include "renderer/api.h"
|
2018-05-15 02:27:25 +02:00
|
|
|
#include "util/pngcruft.h"
|
2018-05-25 08:01:07 +02:00
|
|
|
#include "taskmanager.h"
|
2017-02-24 01:54:28 +01:00
|
|
|
|
2017-04-01 02:08:35 +02:00
|
|
|
Video video;
|
2012-07-28 22:53:53 +02:00
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
typedef struct ScreenshotTaskData {
|
2018-05-22 07:53:18 +02:00
|
|
|
char *dest_path;
|
2018-05-25 08:01:07 +02:00
|
|
|
uint8_t *pixels;
|
2018-05-22 07:53:18 +02:00
|
|
|
uint width;
|
|
|
|
uint height;
|
2018-05-25 08:01:07 +02:00
|
|
|
} ScreenshotTaskData;
|
2012-07-28 22:53:53 +02:00
|
|
|
|
|
|
|
static void video_add_mode(int width, int height) {
|
|
|
|
if(video.modes) {
|
|
|
|
int i; for(i = 0; i < video.mcount; ++i) {
|
|
|
|
VideoMode *m = &(video.modes[i]);
|
|
|
|
if(m->width == width && m->height == height)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
video.modes = (VideoMode*)realloc(video.modes, (++video.mcount) * sizeof(VideoMode));
|
|
|
|
video.modes[video.mcount-1].width = width;
|
|
|
|
video.modes[video.mcount-1].height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_compare_modes(const void *a, const void *b) {
|
|
|
|
VideoMode *va = (VideoMode*)a;
|
|
|
|
VideoMode *vb = (VideoMode*)b;
|
|
|
|
return va->width * va->height - vb->width * vb->height;
|
|
|
|
}
|
|
|
|
|
2017-04-19 09:56:18 +02:00
|
|
|
void video_get_viewport_size(int *width, int *height) {
|
2017-02-04 04:58:55 +01:00
|
|
|
float w = video.current.width;
|
|
|
|
float h = video.current.height;
|
|
|
|
float r = w / h;
|
|
|
|
|
2017-04-08 20:48:40 +02:00
|
|
|
if(r > VIDEO_ASPECT_RATIO) {
|
|
|
|
w = h * VIDEO_ASPECT_RATIO;
|
|
|
|
} else if(r < VIDEO_ASPECT_RATIO) {
|
|
|
|
h = w / VIDEO_ASPECT_RATIO;
|
2017-02-04 04:58:55 +01:00
|
|
|
}
|
2018-04-12 16:08:48 +02:00
|
|
|
|
2017-04-19 09:56:18 +02:00
|
|
|
*width = w;
|
|
|
|
*height = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_set_viewport(void) {
|
|
|
|
int w, h;
|
|
|
|
video_get_viewport_size(&w,&h);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_viewport((video.current.width - w) / 2, (video.current.height - h) / 2, w, h);
|
2017-02-04 04:58:55 +01:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:11:42 +02:00
|
|
|
static void video_update_vsync(void) {
|
2017-09-26 23:55:28 +02:00
|
|
|
if(global.frameskip || config_get_int(CONFIG_VSYNC) == 0) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_vsync(VSYNC_NONE);
|
2017-02-10 02:11:45 +01:00
|
|
|
} else {
|
2018-04-12 16:08:48 +02:00
|
|
|
switch(config_get_int(CONFIG_VSYNC)) {
|
|
|
|
case 1: r_vsync(VSYNC_NORMAL); break;
|
|
|
|
default: r_vsync(VSYNC_ADAPTIVE); break;
|
2017-02-10 02:11:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-05 03:22:41 +01:00
|
|
|
|
2017-04-08 20:48:40 +02:00
|
|
|
static void video_update_quality(void) {
|
2017-04-19 09:56:18 +02:00
|
|
|
int vw, vh;
|
2018-02-06 07:19:25 +01:00
|
|
|
video_get_viewport_size(&vw, &vh);
|
2017-04-19 09:56:18 +02:00
|
|
|
float q = (float)vh / SCREEN_H;
|
2018-02-06 07:19:25 +01:00
|
|
|
video.quality_factor = q;
|
2017-04-08 20:48:40 +02:00
|
|
|
|
|
|
|
float fg = q * config_get_float(CONFIG_FG_QUALITY);
|
|
|
|
float bg = q * config_get_float(CONFIG_BG_QUALITY);
|
2017-04-16 00:04:21 +02:00
|
|
|
float text = q * config_get_float(CONFIG_TEXT_QUALITY);
|
2017-04-08 20:48:40 +02:00
|
|
|
|
2017-04-16 00:04:21 +02:00
|
|
|
log_debug("q:%f, fg:%f, bg:%f, text:%f", q, fg, bg, text);
|
2017-04-08 20:48:40 +02:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
init_fbo_pair(&resources.fbo_pairs.bg, bg, TEX_TYPE_RGB);
|
|
|
|
init_fbo_pair(&resources.fbo_pairs.fg, fg, TEX_TYPE_RGB);
|
|
|
|
init_fbo_pair(&resources.fbo_pairs.rgba, fg, TEX_TYPE_RGBA);
|
2017-04-15 23:58:42 +02:00
|
|
|
|
2017-04-16 00:04:21 +02:00
|
|
|
reload_fonts(text);
|
2017-04-08 20:48:40 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
static uint32_t get_fullscreen_flag(void) {
|
|
|
|
if(config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
|
|
|
|
return SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
} else {
|
|
|
|
return SDL_WINDOW_FULLSCREEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void video_check_fullscreen_sanity(void) {
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
|
|
|
|
if(SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(video.window), &mode)) {
|
|
|
|
log_warn("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(video.current.width != mode.w || video.current.height != mode.h) {
|
|
|
|
log_warn("BUG: window is not actually fullscreen after modesetting. Video mode: %ix%i, window size: %ix%i",
|
|
|
|
mode.w, mode.h, video.current.width, video.current.height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void video_update_mode_settings(void) {
|
|
|
|
SDL_ShowCursor(false);
|
|
|
|
video_update_vsync();
|
2018-04-12 16:08:48 +02:00
|
|
|
SDL_GetWindowSize(video.window, &video.current.width, &video.current.height);
|
2017-09-19 14:13:04 +02:00
|
|
|
video.real.width = video.current.width;
|
|
|
|
video.real.height = video.current.height;
|
|
|
|
video_set_viewport();
|
|
|
|
video_update_quality();
|
2017-09-29 21:03:49 +02:00
|
|
|
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
|
2017-09-19 14:13:04 +02:00
|
|
|
|
2017-09-19 16:11:42 +02:00
|
|
|
if(video_is_fullscreen() && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
|
2017-09-19 14:13:04 +02:00
|
|
|
video_check_fullscreen_sanity();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* modeflagsstr(uint32_t flags) {
|
|
|
|
if(WINFLAGS_IS_FAKE_FULLSCREEN(flags)) {
|
|
|
|
return "fake fullscreen";
|
|
|
|
} else if(WINFLAGS_IS_REAL_FULLSCREEN(flags)) {
|
|
|
|
return "true fullscreen";
|
|
|
|
} else if(flags & SDL_WINDOW_RESIZABLE) {
|
|
|
|
return "windowed, resizable";
|
|
|
|
} else {
|
|
|
|
return "windowed";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void video_new_window_internal(int w, int h, uint32_t flags, bool fallback) {
|
2017-02-04 03:56:40 +01:00
|
|
|
if(video.window) {
|
|
|
|
SDL_DestroyWindow(video.window);
|
|
|
|
video.window = NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-26 18:39:17 +01:00
|
|
|
char title[sizeof(WINDOW_TITLE) + strlen(TAISEI_VERSION) + 2];
|
2017-12-28 08:29:17 +01:00
|
|
|
snprintf(title, sizeof(title), "%s v%s", WINDOW_TITLE, TAISEI_VERSION);
|
2018-04-12 16:08:48 +02:00
|
|
|
video.window = r_create_window(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flags);
|
2017-02-04 03:56:40 +01:00
|
|
|
|
|
|
|
if(video.window) {
|
2017-09-19 14:13:04 +02:00
|
|
|
video_update_mode_settings();
|
2017-02-04 03:56:40 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fallback) {
|
2017-09-19 14:13:04 +02:00
|
|
|
log_fatal("Failed to create window with mode %ix%i (%s): %s", w, h, modeflagsstr(flags), SDL_GetError());
|
2017-02-04 03:56:40 +01:00
|
|
|
return;
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
video_new_window_internal(RESX, RESY, flags & ~SDL_WINDOW_FULLSCREEN_DESKTOP, true);
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
static void video_new_window(int w, int h, bool fs, bool resizable) {
|
2018-04-12 16:08:48 +02:00
|
|
|
uint32_t flags = 0;
|
2017-02-04 05:08:34 +01:00
|
|
|
|
2017-03-01 01:25:52 +01:00
|
|
|
if(fs) {
|
2017-09-19 14:13:04 +02:00
|
|
|
flags |= get_fullscreen_flag();
|
2017-05-03 16:14:38 +02:00
|
|
|
} else if(resizable) {
|
2017-03-01 01:25:52 +01:00
|
|
|
flags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
}
|
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
video_new_window_internal(w, h, flags, false);
|
2017-03-20 07:08:09 +01:00
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
log_info("Created a new window: %ix%i (%s)",
|
2017-03-20 07:08:09 +01:00
|
|
|
video.current.width,
|
|
|
|
video.current.height,
|
2017-09-19 14:13:04 +02:00
|
|
|
modeflagsstr(SDL_GetWindowFlags(video.window))
|
2017-03-20 07:08:09 +01:00
|
|
|
);
|
2017-11-11 19:47:15 +01:00
|
|
|
|
|
|
|
events_pause_keyrepeat();
|
|
|
|
SDL_RaiseWindow(video.window);
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
static bool video_set_display_mode(int w, int h) {
|
|
|
|
SDL_DisplayMode closest, target = { .w = w, .h = h };
|
|
|
|
int display = SDL_GetWindowDisplayIndex(video.window);
|
|
|
|
|
|
|
|
if(!SDL_GetClosestDisplayMode(display, &target, &closest)) {
|
|
|
|
log_warn("No available display modes for %ix%i on display %i", w, h, display);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(closest.w != w || closest.h != h) {
|
|
|
|
log_warn("Can't use %ix%i, closest available is %ix%i", w, h, closest.w, closest.h);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_SetWindowDisplayMode(video.window, &closest)) {
|
|
|
|
log_warn("Failed to set display mode for %ix%i on display %i: %s", closest.w, closest.h, display, SDL_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-19 16:11:42 +02:00
|
|
|
static void video_set_fullscreen(bool fullscreen) {
|
|
|
|
uint32_t flags = fullscreen ? get_fullscreen_flag() : 0;
|
2017-11-11 19:47:15 +01:00
|
|
|
events_pause_keyrepeat();
|
2017-09-19 16:11:42 +02:00
|
|
|
|
2017-11-11 19:47:15 +01:00
|
|
|
if(SDL_SetWindowFullscreen(video.window, flags) < 0) {
|
2017-09-19 16:11:42 +02:00
|
|
|
log_warn("Failed to switch to %s mode: %s", modeflagsstr(flags), SDL_GetError());
|
|
|
|
}
|
2017-11-11 19:47:15 +01:00
|
|
|
|
|
|
|
SDL_RaiseWindow(video.window);
|
2017-09-19 16:11:42 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 14:13:04 +02:00
|
|
|
void video_set_mode(int w, int h, bool fs, bool resizable) {
|
|
|
|
video.intended.width = w;
|
|
|
|
video.intended.height = h;
|
|
|
|
|
|
|
|
if(!video.window) {
|
|
|
|
video_new_window(w, h, fs, resizable);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(w != video.current.width || h != video.current.height) {
|
|
|
|
if(fs && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
|
|
|
|
video_set_display_mode(w, h);
|
|
|
|
video_set_fullscreen(fs);
|
|
|
|
video_update_mode_settings();
|
2017-11-11 19:47:15 +01:00
|
|
|
} else {
|
2017-09-19 14:13:04 +02:00
|
|
|
// XXX: I would like to use SDL_SetWindowSize for size changes, but apparently it's impossible to reliably detect
|
2018-01-12 19:26:07 +01:00
|
|
|
// when it fails to actually resize the window. For example, a tiling WM (awesome) may be getting in its way
|
|
|
|
// and we'd never know. SDL_GL_GetDrawableSize/SDL_GetWindowSize aren't helping as of SDL 2.0.5.
|
2017-09-19 14:13:04 +02:00
|
|
|
//
|
2018-01-12 19:26:07 +01:00
|
|
|
// There's not much to be done about it. We're at mercy of SDL here and SDL is at mercy of the WM.
|
2017-09-19 14:13:04 +02:00
|
|
|
video_new_window(w, h, fs, resizable);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
video_set_fullscreen(fs);
|
|
|
|
SDL_SetWindowResizable(video.window, resizable);
|
|
|
|
}
|
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
static void* video_screenshot_task(void *arg) {
|
|
|
|
ScreenshotTaskData *tdata = arg;
|
|
|
|
log_debug("%u %u %s", tdata->width, tdata->height, tdata->dest_path);
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
uint width = tdata->width;
|
|
|
|
uint height = tdata->height;
|
|
|
|
uint8_t *pixels = tdata->pixels;
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
SDL_RWops *output = vfs_open(tdata->dest_path, VFS_MODE_WRITE);
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-05-22 07:53:18 +02:00
|
|
|
if(!output) {
|
2017-04-18 21:48:18 +02:00
|
|
|
log_warn("VFS error: %s", vfs_get_error());
|
2018-05-25 08:01:07 +02:00
|
|
|
return NULL;
|
2017-03-06 01:25:59 +01:00
|
|
|
}
|
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
char *syspath = vfs_repr(tdata->dest_path, true);
|
2018-05-22 07:53:18 +02:00
|
|
|
log_info("Saving screenshot as %s", syspath);
|
|
|
|
free(syspath);
|
|
|
|
|
2017-03-06 01:25:59 +01:00
|
|
|
png_structp png_ptr;
|
2018-01-12 19:26:07 +01:00
|
|
|
png_infop info_ptr;
|
2017-03-06 01:25:59 +01:00
|
|
|
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
|
|
png_setup_error_handlers(png_ptr);
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
png_set_IHDR(
|
2018-04-12 16:08:48 +02:00
|
|
|
png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
|
2018-01-12 19:26:07 +01:00
|
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
|
|
|
|
);
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
png_byte *row_pointers[height];
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
for(int y = 0; y < height; y++) {
|
|
|
|
row_pointers[y] = png_malloc(png_ptr, width * 3);
|
|
|
|
memcpy(row_pointers[y], pixels + width * 3 * (height - 1 - y), width * 3);
|
2017-03-06 01:25:59 +01:00
|
|
|
}
|
|
|
|
|
2018-05-22 07:53:18 +02:00
|
|
|
png_init_rwops_write(png_ptr, output);
|
2017-03-06 01:25:59 +01:00
|
|
|
png_set_rows(png_ptr, info_ptr, row_pointers);
|
|
|
|
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
for(int y = 0; y < height; y++) {
|
2017-03-06 01:25:59 +01:00
|
|
|
png_free(png_ptr, row_pointers[y]);
|
2018-04-12 16:08:48 +02:00
|
|
|
}
|
2017-03-06 01:25:59 +01:00
|
|
|
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
2018-05-22 07:53:18 +02:00
|
|
|
SDL_RWclose(output);
|
2017-03-06 01:25:59 +01:00
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
return NULL;
|
2018-05-22 07:53:18 +02:00
|
|
|
}
|
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
static void video_screenshot_free_task_data(void *arg) {
|
|
|
|
ScreenshotTaskData *tdata = arg;
|
|
|
|
free(tdata->pixels);
|
|
|
|
free(tdata->dest_path);
|
|
|
|
free(tdata);
|
2018-05-22 07:53:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_take_screenshot(void) {
|
2018-05-25 08:01:07 +02:00
|
|
|
ScreenshotTaskData tdata;
|
|
|
|
memset(&tdata, 0, sizeof(tdata));
|
|
|
|
tdata.pixels = r_screenshot(&tdata.width, &tdata.height);
|
2018-05-22 07:53:18 +02:00
|
|
|
|
|
|
|
log_debug("Screenshot requested");
|
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
if(!tdata.pixels) {
|
2018-05-22 07:53:18 +02:00
|
|
|
log_warn("Failed to take a screenshot");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char outfile[128];
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = localtime(&rawtime);
|
|
|
|
strftime(outfile, 128, "taisei_%Y%m%d_%H-%M-%S%z.png", timeinfo);
|
2018-05-25 08:01:07 +02:00
|
|
|
tdata.dest_path = strjoin("storage/screenshots/", outfile, NULL);;
|
2018-05-22 07:53:18 +02:00
|
|
|
|
2018-05-25 08:01:07 +02:00
|
|
|
task_detach(taskmgr_global_submit((TaskParams) {
|
|
|
|
.callback = video_screenshot_task,
|
|
|
|
.userdata = memdup(&tdata, sizeof(tdata)),
|
|
|
|
.userdata_free_callback = video_screenshot_free_task_data,
|
|
|
|
}));
|
2017-03-06 01:25:59 +01:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:11:42 +02:00
|
|
|
bool video_is_resizable(void) {
|
2017-03-01 01:25:52 +01:00
|
|
|
return SDL_GetWindowFlags(video.window) & SDL_WINDOW_RESIZABLE;
|
|
|
|
}
|
|
|
|
|
2017-09-19 16:11:42 +02:00
|
|
|
bool video_is_fullscreen(void) {
|
2017-09-19 14:13:04 +02:00
|
|
|
return WINFLAGS_IS_FULLSCREEN(SDL_GetWindowFlags(video.window));
|
2017-09-18 13:19:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool video_can_change_resolution(void) {
|
2017-09-19 16:11:42 +02:00
|
|
|
return !video_is_fullscreen() || !config_get_int(CONFIG_FULLSCREEN_DESKTOP);
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
|
|
|
|
2017-02-17 19:23:22 +01:00
|
|
|
static void video_cfg_fullscreen_callback(ConfigIndex idx, ConfigValue v) {
|
2017-09-19 16:05:53 +02:00
|
|
|
video_set_mode(
|
|
|
|
config_get_int(CONFIG_VID_WIDTH),
|
|
|
|
config_get_int(CONFIG_VID_HEIGHT),
|
|
|
|
config_set_int(idx, v.i),
|
|
|
|
config_get_int(CONFIG_VID_RESIZABLE)
|
|
|
|
);
|
2017-02-17 19:23:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void video_cfg_vsync_callback(ConfigIndex idx, ConfigValue v) {
|
|
|
|
config_set_int(idx, v.i);
|
|
|
|
video_update_vsync();
|
|
|
|
}
|
|
|
|
|
2017-03-01 01:25:52 +01:00
|
|
|
static void video_cfg_resizable_callback(ConfigIndex idx, ConfigValue v) {
|
|
|
|
SDL_SetWindowResizable(video.window, config_set_int(idx, v.i));
|
|
|
|
}
|
|
|
|
|
2017-04-08 20:48:40 +02:00
|
|
|
static void video_quality_callback(ConfigIndex idx, ConfigValue v) {
|
|
|
|
config_set_float(idx, v.f);
|
|
|
|
video_update_quality();
|
|
|
|
}
|
|
|
|
|
2017-05-05 03:36:27 +02:00
|
|
|
static void video_init_sdl(void) {
|
2018-04-12 16:08:48 +02:00
|
|
|
// XXX: workaround for an SDL bug: https://bugzilla.libsdl.org/show_bug.cgi?id=4127
|
|
|
|
SDL_SetHintWithPriority(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0", SDL_HINT_OVERRIDE);
|
|
|
|
|
|
|
|
uint num_drivers = SDL_GetNumVideoDrivers();
|
|
|
|
void *buf;
|
|
|
|
SDL_RWops *out = SDL_RWAutoBuffer(&buf, 256);
|
|
|
|
|
|
|
|
SDL_RWprintf(out, "Available video drivers:");
|
|
|
|
|
|
|
|
for(uint i = 0; i < num_drivers; ++i) {
|
|
|
|
SDL_RWprintf(out, " %s", SDL_GetVideoDriver(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_WriteU8(out, 0);
|
|
|
|
log_info("%s", (char*)buf);
|
|
|
|
SDL_RWclose(out);
|
|
|
|
|
2018-04-18 00:34:41 +02:00
|
|
|
const char *prefer_drivers = env_get("TAISEI_PREFER_SDL_VIDEODRIVERS", "");
|
|
|
|
const char *force_driver = env_get("TAISEI_VIDEO_DRIVER", "");
|
2017-05-05 03:36:27 +02:00
|
|
|
|
2018-04-18 00:34:41 +02:00
|
|
|
if(*force_driver) {
|
2017-11-11 20:37:24 +01:00
|
|
|
log_warn("TAISEI_VIDEO_DRIVER is deprecated and will be removed, use TAISEI_PREFER_SDL_VIDEODRIVERS or SDL_VIDEODRIVER instead");
|
|
|
|
} else {
|
2018-04-18 00:34:41 +02:00
|
|
|
force_driver = env_get("SDL_VIDEODRIVER", "");
|
2017-11-11 20:37:24 +01:00
|
|
|
}
|
2017-05-05 03:36:27 +02:00
|
|
|
|
2017-11-11 20:37:24 +01:00
|
|
|
if(!(prefer_drivers && *prefer_drivers)) {
|
|
|
|
// https://bugzilla.libsdl.org/show_bug.cgi?id=3948
|
|
|
|
// A suboptimal X11 server may be available on top of those systems,
|
|
|
|
// so we push X11 down in the priority list.
|
|
|
|
prefer_drivers = "wayland,mir,cocoa,windows,x11";
|
2017-05-05 03:36:27 +02:00
|
|
|
}
|
|
|
|
|
2018-04-18 00:34:41 +02:00
|
|
|
if(prefer_drivers && *prefer_drivers && !*force_driver) {
|
2017-11-11 20:37:24 +01:00
|
|
|
char buf[strlen(prefer_drivers) + 1];
|
|
|
|
char *driver, *bufptr = buf;
|
|
|
|
int drivernum = 0;
|
|
|
|
strcpy(buf, prefer_drivers);
|
2017-05-05 03:36:27 +02:00
|
|
|
|
2017-11-11 20:37:24 +01:00
|
|
|
while((driver = strtok_r(NULL, " :;,", &bufptr))) {
|
|
|
|
++drivernum;
|
|
|
|
log_info("Trying preferred driver #%i: %s", drivernum, driver);
|
2017-05-05 03:36:27 +02:00
|
|
|
|
2017-11-11 20:37:24 +01:00
|
|
|
if(SDL_VideoInit(driver)) {
|
|
|
|
log_info("Driver '%s' failed: %s", driver, SDL_GetError());
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-05 03:36:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-11 20:37:24 +01:00
|
|
|
if(drivernum) {
|
|
|
|
log_info("All preferred drivers failed, falling back to SDL default");
|
2017-05-05 03:36:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-11 20:37:24 +01:00
|
|
|
if(SDL_VideoInit(force_driver)) {
|
|
|
|
log_fatal("SDL_VideoInit() failed: %s", SDL_GetError());
|
2017-05-05 03:36:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
static void video_handle_resize(int w, int h) {
|
|
|
|
video.current.width = w;
|
|
|
|
video.current.height = h;
|
|
|
|
video_set_viewport();
|
|
|
|
video_update_quality();
|
|
|
|
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool video_handle_window_event(SDL_Event *event, void *arg) {
|
|
|
|
switch(event->window.event) {
|
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
|
|
video_handle_resize(event->window.data1, event->window.data2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
2017-11-24 15:39:07 +01:00
|
|
|
if(config_get_int(CONFIG_FOCUS_LOSS_PAUSE)) {
|
|
|
|
events_emit(TE_GAME_PAUSE, 0, NULL, NULL);
|
|
|
|
}
|
2017-09-29 21:03:49 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
void video_init(void) {
|
2017-02-11 04:52:08 +01:00
|
|
|
bool fullscreen_available = false;
|
2017-02-04 03:56:40 +01:00
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
memset(&video, 0, sizeof(video));
|
2018-01-03 15:38:42 +01:00
|
|
|
memset(&resources.fbo_pairs, 0, sizeof(resources.fbo_pairs));
|
2017-02-04 03:56:40 +01:00
|
|
|
|
2017-05-05 03:36:27 +02:00
|
|
|
video_init_sdl();
|
|
|
|
log_info("Using driver '%s'", SDL_GetCurrentVideoDriver());
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_init();
|
|
|
|
|
2017-05-05 03:36:27 +02:00
|
|
|
// Register all resolutions that are available in fullscreen
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-05-05 03:36:27 +02:00
|
|
|
for(int s = 0; s < SDL_GetNumVideoDisplays(); ++s) {
|
|
|
|
for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
|
2017-02-04 03:56:40 +01:00
|
|
|
SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-02-04 03:56:40 +01:00
|
|
|
if(SDL_GetDisplayMode(s, i, &mode) != 0) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("SDL_GetDisplayMode() failed: %s", SDL_GetError());
|
2017-02-04 03:56:40 +01:00
|
|
|
} else {
|
|
|
|
video_add_mode(mode.w, mode.h);
|
2017-02-11 04:52:08 +01:00
|
|
|
fullscreen_available = true;
|
2017-02-04 03:56:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!fullscreen_available) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("No available fullscreen modes");
|
2017-02-17 17:03:49 +01:00
|
|
|
config_set_int(CONFIG_FULLSCREEN, false);
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
2017-02-04 03:56:40 +01:00
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
// Then, add some common 4:3 modes for the windowed mode if they are not there yet.
|
|
|
|
// This is required for some multihead setups.
|
2018-05-22 07:53:18 +02:00
|
|
|
VideoMode common_modes[] = {
|
|
|
|
{RESX, RESY},
|
|
|
|
|
|
|
|
{640, 480},
|
|
|
|
{800, 600},
|
|
|
|
{1024, 768},
|
|
|
|
{1280, 960},
|
|
|
|
{1152, 864},
|
|
|
|
{1400, 1050},
|
|
|
|
{1440, 1080},
|
|
|
|
|
|
|
|
{0, 0},
|
|
|
|
};
|
|
|
|
|
2017-05-05 03:36:27 +02:00
|
|
|
for(int i = 0; common_modes[i].width; ++i) {
|
2012-07-28 22:53:53 +02:00
|
|
|
video_add_mode(common_modes[i].width, common_modes[i].height);
|
2017-05-05 03:36:27 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-28 22:53:53 +02:00
|
|
|
// sort it, mainly for the options menu
|
|
|
|
qsort(video.modes, video.mcount, sizeof(VideoMode), video_compare_modes);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-05-05 03:49:00 +02:00
|
|
|
video_set_mode(
|
2017-03-01 01:25:52 +01:00
|
|
|
config_get_int(CONFIG_VID_WIDTH),
|
|
|
|
config_get_int(CONFIG_VID_HEIGHT),
|
|
|
|
config_get_int(CONFIG_FULLSCREEN),
|
|
|
|
config_get_int(CONFIG_VID_RESIZABLE)
|
|
|
|
);
|
2017-02-17 19:23:22 +01:00
|
|
|
|
|
|
|
config_set_callback(CONFIG_FULLSCREEN, video_cfg_fullscreen_callback);
|
|
|
|
config_set_callback(CONFIG_VSYNC, video_cfg_vsync_callback);
|
2017-03-01 01:25:52 +01:00
|
|
|
config_set_callback(CONFIG_VID_RESIZABLE, video_cfg_resizable_callback);
|
2017-04-08 20:48:40 +02:00
|
|
|
config_set_callback(CONFIG_FG_QUALITY, video_quality_callback);
|
|
|
|
config_set_callback(CONFIG_BG_QUALITY, video_quality_callback);
|
2017-04-15 23:58:42 +02:00
|
|
|
config_set_callback(CONFIG_TEXT_QUALITY, video_quality_callback);
|
2017-03-20 07:08:09 +01:00
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
EventHandler h = {
|
|
|
|
.proc = video_handle_window_event,
|
|
|
|
.priority = EPRIO_SYSTEM,
|
|
|
|
.event_type = SDL_WINDOWEVENT,
|
|
|
|
};
|
|
|
|
|
|
|
|
events_register_handler(&h);
|
2017-03-20 07:08:09 +01:00
|
|
|
log_info("Video subsystem initialized");
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_shutdown(void) {
|
2018-05-22 07:53:18 +02:00
|
|
|
events_unregister_handler(video_handle_window_event);
|
2017-02-04 03:56:40 +01:00
|
|
|
SDL_DestroyWindow(video.window);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_shutdown();
|
2012-07-28 22:53:53 +02:00
|
|
|
free(video.modes);
|
2017-05-05 03:36:27 +02:00
|
|
|
SDL_VideoQuit();
|
2012-07-28 22:53:53 +02:00
|
|
|
}
|
2017-12-26 09:56:21 +01:00
|
|
|
|
|
|
|
void video_swap_buffers(void) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_target(NULL);
|
|
|
|
r_swap(video.window);
|
|
|
|
r_clear(CLEAR_COLOR);
|
2017-12-26 09:56:21 +01:00
|
|
|
}
|