1211 lines · 29.4 KB · C
RawDownload
// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdio.h>#include <stdint.h>#include <stdbool.h>#include <math.h>#ifdef _WIN32#define WIN32_MEAN_AND_LEAN#include <windows.h>#include <SDL2/SDL_syswm.h>#else#include <unistd.h> // usleep()#endif#include "ft2_header.h"#include "ft2_config.h"#include "ft2_gui.h"#include "ft2_video.h"#include "ft2_events.h"#include "ft2_mouse.h"#include "scopes/ft2_scopes.h"#include "ft2_pattern_ed.h"#include "ft2_pattern_draw.h"#include "ft2_sample_ed.h"#include "ft2_nibbles.h"#include "ft2_inst_ed.h"#include "ft2_diskop.h"#include "ft2_about.h"#include "ft2_trim.h"#include "ft2_sampling.h"#include "ft2_module_loader.h"#include "ft2_midi.h"#include "ft2_bmp.h"#include "ft2_structs.h"#include "ft2_cpu.h" static const uint8_t textCursorData[12] ={	PAL_FORGRND, PAL_FORGRND, PAL_FORGRND,	PAL_FORGRND, PAL_FORGRND, PAL_FORGRND,	PAL_FORGRND, PAL_FORGRND, PAL_FORGRND,	PAL_FORGRND, PAL_FORGRND, PAL_FORGRND}; video_t video; // globalized static bool songIsModified;static char wndTitle[256];static sprite_t sprites[SPRITE_NUM]; // for FPS counter#define FPS_LINES 15#define FPS_SCAN_FRAMES 60#define FPS_RENDER_W 285#define FPS_RENDER_H (((FONT1_CHAR_H + 1) * FPS_LINES) + 1)#define FPS_RENDER_X 2#define FPS_RENDER_Y 2 static char fpsTextBuf[1024];static uint64_t frameStartTime;static double dRunningFrameDuration, dAvgFPS;// ------------------ static void drawReplayerData(void); void resetFPSCounter(void){	editor.framesPassed = 0;	fpsTextBuf[0] = '\0';	dRunningFrameDuration = 1000.0 / VBLANK_HZ;} void beginFPSCounter(void){	if (video.showFPSCounter)		frameStartTime = SDL_GetPerformanceCounter();} static void drawFPSCounter(void){	SDL_version SDLVer; 	SDL_GetVersion(&SDLVer); 	if (editor.framesPassed >= FPS_SCAN_FRAMES && (editor.framesPassed % FPS_SCAN_FRAMES) == 0)	{		dAvgFPS = 1000.0 / (dRunningFrameDuration / FPS_SCAN_FRAMES);		if (dAvgFPS < 0.0 || dAvgFPS > 99999999.9999)			dAvgFPS = 99999999.9999; // prevent number from overflowing text box 		dRunningFrameDuration = 0.0;	} 	clearRect(FPS_RENDER_X+2, FPS_RENDER_Y+2, FPS_RENDER_W, FPS_RENDER_H);	vLineDouble(FPS_RENDER_X, FPS_RENDER_Y+1, FPS_RENDER_H+2, PAL_FORGRND);	vLineDouble(FPS_RENDER_X+FPS_RENDER_W, FPS_RENDER_Y+1, FPS_RENDER_H+2, PAL_FORGRND);	hLineDouble(FPS_RENDER_X+1, FPS_RENDER_Y, FPS_RENDER_W, PAL_FORGRND);	hLineDouble(FPS_RENDER_X+1, FPS_RENDER_Y+FPS_RENDER_H+2, FPS_RENDER_W, PAL_FORGRND); 	// if enough frame data isn't collected yet, show a message	if (editor.framesPassed < FPS_SCAN_FRAMES)	{		const char *text = "Gathering frame information...";		const uint16_t textW = textWidth(text);		textOut(FPS_RENDER_X+((FPS_RENDER_W/2)-(textW/2)), FPS_RENDER_Y+((FPS_RENDER_H/2)-(FONT1_CHAR_H/2)), PAL_FORGRND, text);		return;	} 	double dRefreshRate = video.dMonitorRefreshRate;	if (dRefreshRate < 0.0 || dRefreshRate > 9999.9)		dRefreshRate = 9999.9; // prevent number from overflowing text box 	double dAudLatency = audio.dAudioLatencyMs;	if (dAudLatency < 0.0 || dAudLatency > 999999999.9999)		dAudLatency = 999999999.9999; // prevent number from overflowing text box 	sprintf(fpsTextBuf,	             "SDL version: %u.%u.%u\n" \	             "Frames per second: %.3f\n" \	             "Monitor refresh rate: %.1fHz (+/-)\n" \	             "59..61Hz GPU VSync used: %s\n" \	             "HPC frequency (timer): %.4fMHz\n" \	             "Audio frequency: %.1fkHz (expected %.1fkHz)\n" \	             "Audio buffer samples: %d (expected %d)\n" \	             "Audio latency: %.1fms (expected %.1fms)\n" \	             "Render size: %dx%d (offset %d,%d)\n" \	             "Disp. size: %dx%d (window: %dx%d)\n" \	             "Render scaling: x=%.2f, y=%.2f\n" \	             "Mouse to render space muls: x=%.2f, y=%.2f\n" \	             "Relative mouse coords: %d,%d\n" \	             "Absolute mouse coords: %d,%d\n" \	             "Press CTRL+SHIFT+F to close this box.\n",	             SDLVer.major, SDLVer.minor, SDLVer.patch,	             dAvgFPS,	             dRefreshRate,	             video.vsync60HzPresent ? "yes" : "no",	             hpcFreq.freq64 / (1000.0 * 1000.0),	             audio.haveFreq / 1000.0, audio.wantFreq / 1000.0,	             audio.haveSamples, audio.wantSamples,	             dAudLatency, ((audio.wantSamples * 1000.0) / audio.wantFreq),	             video.renderW, video.renderH, video.renderX, video.renderY,	             video.displayW, video.displayH, video.windowW, video.windowH,	             video.renderW / (float)SCREEN_W, video.renderH / (float)SCREEN_H,	             video.fMouseXMul, video.fMouseYMul,	             mouse.x, mouse.y,	             mouse.absX, mouse.absY); 	// draw text 	uint16_t xPos = FPS_RENDER_X+3;	uint16_t yPos = FPS_RENDER_Y+3; 	char *textPtr = fpsTextBuf;	while (*textPtr != '\0')	{		const char ch = *textPtr++;		if (ch == '\n')		{			yPos += FONT1_CHAR_H+1;			xPos = FPS_RENDER_X+3;			continue;		} 		charOut(xPos, yPos, PAL_FORGRND, ch);		xPos += charWidth(ch);	} 	// draw framerate tester symbol 	const uint16_t symbolEnd = 115; 	// ping-pong movement	uint16_t x = editor.framesPassed % (symbolEnd * 2);	if (x >= symbolEnd)		x = (symbolEnd * 2) - x; 	charOut(164 + x, 16, PAL_FORGRND, '*');} void endFPSCounter(void){	if (video.showFPSCounter && frameStartTime > 0)	{		uint64_t frameTimeDiff64 = SDL_GetPerformanceCounter() - frameStartTime;		if (frameTimeDiff64 > INT32_MAX)			frameTimeDiff64 = INT32_MAX; 		dRunningFrameDuration += (int32_t)frameTimeDiff64 * hpcFreq.dFreqMulMs;	}} void flipFrame(void){	const uint32_t windowFlags = SDL_GetWindowFlags(video.window);	bool minimized = (windowFlags & SDL_WINDOW_MINIMIZED) ? true : false; 	renderSprites(); 	if (video.showFPSCounter)		drawFPSCounter(); 	SDL_UpdateTexture(video.texture, NULL, video.frameBuffer, SCREEN_W * sizeof (int32_t)); 	// SDL 2.0.14 bug on Windows (?): This function consumes ever-increasing memory if the program is minimized	if (!minimized)		SDL_RenderClear(video.renderer); 	SDL_RenderCopy(video.renderer, video.texture, NULL, NULL);	SDL_RenderPresent(video.renderer); 	eraseSprites(); 	if (!video.vsync60HzPresent)	{		// we have no VSync, do crude thread sleeping to sync to ~60Hz		hpc_Wait(&video.vblankHpc);	}	else	{		/* We have VSync, but it can unexpectedly get inactive in certain scenarios.		** We have to force thread sleeping (to ~60Hz) if so.		*/#ifdef __APPLE__		// macOS: VSync gets disabled if the window is 100% covered by another window. Let's add a (crude) fix:		if (minimized || !(windowFlags & SDL_WINDOW_INPUT_FOCUS))			hpc_Wait(&video.vblankHpc);#elif __unix__		/* *NIX: VSync can get disabled in fullscreen mode in some distros/systems. Let's add a fix.		**		** TODO/XXX: This is probably a BAD hack and can cause a poor fullscreen experience if VSync did		**           in fact work in fullscreen mode...		*/		if (minimized || video.fullscreen)			hpc_Wait(&video.vblankHpc);#else		if (minimized)			hpc_Wait(&video.vblankHpc);#endif	} 	editor.framesPassed++; 	/* Reset audio/video sync timestamp every half an hour to prevent	** possible sync drifting after hours of playing a song without	** a single song stop (resets timestamp) in-between.	*/	if (editor.framesPassed >= VBLANK_HZ*60*30)		audio.resetSyncTickTimeFlag = true;} void showErrorMsgBox(const char *fmt, ...){	char strBuf[256];	va_list args; 	// format the text string	va_start(args, fmt);	vsnprintf(strBuf, sizeof (strBuf), fmt, args);	va_end(args); 	// window can be NULL here, no problem...	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", strBuf, video.window);} static void updateRenderSizeVars(void){	float fXScale, fYScale; 	if (video.useDesktopMouseCoords)	{		SDL_DisplayMode dm;		int32_t di = SDL_GetWindowDisplayIndex(video.window);		if (di < 0)			di = 0; // return display index 0 (default) on error 		SDL_GetDesktopDisplayMode(di, &dm);		video.displayW = dm.w;		video.displayH = dm.h;	}	else	{		SDL_GetWindowSize(video.window, &video.displayW, &video.displayH);	} 	SDL_GetWindowSize(video.window, &video.windowW, &video.windowH); 	if (video.fullscreen)	{		if (config.specialFlags2 & STRETCH_IMAGE)		{			video.renderW = video.displayW;			video.renderH = video.displayH;			video.renderX = 0;			video.renderY = 0;		}		else		{			SDL_RenderGetScale(video.renderer, &fXScale, &fYScale); 			video.renderW = (int32_t)(SCREEN_W * fXScale);			video.renderH = (int32_t)(SCREEN_H * fYScale); 			// high-DPI hackery:			//  On high-DPI systems, the display w/h are given in logical pixels,			//  but the renderer size is given in physical pixels. Since our internal			//  render{X,Y,W,H} variables need to be in logical coordinates, as that's			//  what mouse input uses, scale them by the screen's DPI scale factor,			//  which is the physical (renderer) size / the logical (window) size.			//  On non high-DPI systems, this is effectively a no-op.			int32_t actualScreenW, actualScreenH;			SDL_GetRendererOutputSize(video.renderer, &actualScreenW, &actualScreenH); 			const double dXUpscale = (const double)actualScreenW / video.displayW;			const double dYUpscale = (const double)actualScreenH / video.displayH; 			// downscale back to correct sizes			if (dXUpscale != 0.0) video.renderW = (int32_t)(video.renderW / dXUpscale);			if (dYUpscale != 0.0) video.renderH = (int32_t)(video.renderH / dYUpscale); 			video.renderX = (video.displayW - video.renderW) / 2;			video.renderY = (video.displayH - video.renderH) / 2;		}	}	else	{		SDL_GetWindowSize(video.window, &video.renderW, &video.renderH); 		video.renderX = 0;		video.renderY = 0;	} 	// for mouse cursor creation	video.xScale = (uint32_t)round(video.renderW / (double)SCREEN_W);	video.yScale = (uint32_t)round(video.renderH / (double)SCREEN_H); 	createMouseCursors();} void enterFullscreen(void){	SDL_SetWindowFullscreen(video.window, SDL_WINDOW_FULLSCREEN_DESKTOP); 	if (config.specialFlags2 & STRETCH_IMAGE)	{		SDL_DisplayMode dm; 		int32_t di = SDL_GetWindowDisplayIndex(video.window);		if (di < 0)			di = 0; // return display index 0 (default) on error 		SDL_GetDesktopDisplayMode(di, &dm); 		SDL_RenderSetLogicalSize(video.renderer, dm.w, dm.h);	}	else	{		SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H);	} 	SDL_SetWindowSize(video.window, SCREEN_W, SCREEN_H); #ifndef __unix__ // can be severely buggy on Linux... (at least when used like this)	SDL_SetWindowGrab(video.window, SDL_TRUE);#endif 	updateRenderSizeVars();	updateMouseScaling();	setMousePosToCenter();} void leaveFullscreen(void){	SDL_SetWindowFullscreen(video.window, 0);	SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H); 	setWindowSizeFromConfig(false); 	SDL_SetWindowSize(video.window, SCREEN_W * video.upscaleFactor, SCREEN_H * video.upscaleFactor); #ifndef __unix__	SDL_SetWindowGrab(video.window, SDL_FALSE);#endif 	updateRenderSizeVars();	updateMouseScaling();	setMousePosToCenter(); #ifdef __unix__ // can be required on Linux... (or else the window keeps moving down every time you leave fullscreen)	SDL_SetWindowPosition(video.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);#endif} void toggleFullscreen(void){	video.fullscreen ^= 1; 	if (video.fullscreen)		enterFullscreen();	else		leaveFullscreen();} bool setupSprites(void){	sprite_t *s; 	memset(sprites, 0, sizeof (sprites)); 	s = &sprites[SPRITE_MOUSE_POINTER];	s->data = bmp.mouseCursors;	s->w = MOUSE_CURSOR_W;	s->h = MOUSE_CURSOR_H; 	s = &sprites[SPRITE_LEFT_LOOP_PIN];	s->data = &bmp.loopPins[0*(154*16)];	s->w = 16;	s->h = SAMPLE_AREA_HEIGHT; 	s = &sprites[SPRITE_RIGHT_LOOP_PIN];	s->data = &bmp.loopPins[2*(154*16)];	s->w = 16;	s->h = SAMPLE_AREA_HEIGHT; 	s = &sprites[SPRITE_TEXT_CURSOR];	s->data = textCursorData;	s->w = 1;	s->h = 12; 	hideSprite(SPRITE_MOUSE_POINTER);	hideSprite(SPRITE_LEFT_LOOP_PIN);	hideSprite(SPRITE_RIGHT_LOOP_PIN);	hideSprite(SPRITE_TEXT_CURSOR); 	// setup refresh buffer (used to clear sprites after each frame)	s = sprites;	for (uint32_t i = 0; i < SPRITE_NUM; i++, s++)	{		s->refreshBuffer = (uint32_t *)malloc(s->w * s->h * sizeof (int32_t));		if (s->refreshBuffer == NULL)			return false;	} 	return true;} void changeSpriteData(int32_t sprite, const uint8_t *data){	sprites[sprite].data = data;	memset(sprites[sprite].refreshBuffer, 0, sprites[sprite].w * sprites[sprite].h * sizeof (int32_t));} void freeSprites(void){	sprite_t *s = sprites;	for (int32_t i = 0; i < SPRITE_NUM; i++, s++)	{		if (s->refreshBuffer != NULL)		{			free(s->refreshBuffer);			s->refreshBuffer = NULL;		}	}} void setLeftLoopPinState(bool clicked){	changeSpriteData(SPRITE_LEFT_LOOP_PIN, clicked ? &bmp.loopPins[1*(154*16)] : &bmp.loopPins[0*(154*16)]);} void setRightLoopPinState(bool clicked){	changeSpriteData(SPRITE_RIGHT_LOOP_PIN, clicked ? &bmp.loopPins[3*(154*16)] : &bmp.loopPins[2*(154*16)]);} int32_t getSpritePosX(int32_t sprite){	return sprites[sprite].x;} void setSpritePos(int32_t sprite, int32_t x, int32_t y){	sprites[sprite].newX = (int16_t)x;	sprites[sprite].newY = (int16_t)y;} void hideSprite(int32_t sprite){	sprites[sprite].newX = SCREEN_W;} void eraseSprites(void){	sprite_t *s = &sprites[SPRITE_NUM-1];	for (int32_t i = SPRITE_NUM-1; i >= 0; i--, s--) // erasing must be done in reverse order	{		if (s->x >= SCREEN_W || s->y >= SCREEN_H) // sprite is hidden, don't draw nor fill clear buffer			continue; 

Lines 1–500 of 1211.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t