shithub: pt2-clone

Download patch

ref: 118366b1d4c94e68bc8a6e4f10bf543bdd321e0d
parent: e537ab6fa5e1a6e121f0ea19da0bf9479f8022f1
author: Olav Sørensen <olav.sorensen@live.no>
date: Thu Dec 19 16:37:25 EST 2019

Pushed v1.01 code

- Hardware mouse ("HWMOUSE" in config) now has the same colors and shape
  as the original PT cursor. This mode is now the new config default.
- MacOS: Pass NDEBUG to clang preprocessor defines, to prevent debug code
  from being compiled in release mode.

diff: cannot open b/pt_pal_editor/release/macos/pt_pal_editor-osx.app/Contents/MacOS//null: file does not exist: '.../release/macos/pt_pal_editor-osx.app/Contents/MacOS//null' diff: cannot open b/pt_pal_editor/release/other//null: file does not exist: 'b/pt_pal_editor/release/other//null' diff: cannot open b/release/macos/pt2-clone-macos.app/Contents/MacOS//null: file does not exist: 'b/release/macos/pt2-clone-macos.app/Contents/MacOS//null'
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@
 vs2019_project/.vs/pt2-clone/v16/.suo
 *.cache
 *.ipch
+.DS_Store
+vs2019_project/pt2-clone/Release/pt2-clone.vcxproj.FileListAbsolute.txt
+vs2019_project/pt2-clone/x64/Debug/pt2-clone.vcxproj.FileListAbsolute.txt
--- a/make-linux.sh
+++ b/make-linux.sh
@@ -3,7 +3,7 @@
 rm release/other/pt2-clone &> /dev/null
 
 echo Compiling, please wait...
-gcc src/gfx/*.c src/*.c -lSDL2 -lm -Wall -Wno-unused-result -Wc++-compat -Wshadow -Winit-self -Wextra -Wunused -Wunreachable-code -Wredundant-decls -Wswitch-default -march=native -mtune=native -O3 -o release/other/pt2-clone
+gcc -DNDEBUG src/gfx/*.c src/*.c -lSDL2 -lm -Wall -Wno-unused-result -Wc++-compat -Wshadow -Winit-self -Wextra -Wunused -Wunreachable-code -Wredundant-decls -Wswitch-default -march=native -mtune=native -O3 -o release/other/pt2-clone
 rm src/gfx/*.o src/*.o &> /dev/null
 
 echo Done! The executable is in the folder named \'release/other\'.
\ No newline at end of file
--- a/make-macos.sh
+++ b/make-macos.sh
@@ -8,7 +8,7 @@
     
     rm release/macos/pt2-clone-macos.app/Contents/MacOS/pt2-clone &> /dev/null
     
-    clang -mmacosx-version-min=10.6 -arch x86_64 -mmmx -mfpmath=sse -msse2 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks src/gfx/*.c src/*.c -O3 -lm -Wall -Winit-self -Wextra -Wunused -Wredundant-decls -Wswitch-default -framework SDL2 -framework Cocoa -lm -o release/macos/pt2-clone-macos.app/Contents/MacOS/pt2-clone-macos
+    clang -mmacosx-version-min=10.6 -arch x86_64 -mmmx -mfpmath=sse -msse2 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks -g0 -DNDEBUG src/gfx/*.c src/*.c -O3 -lm -Wall -Winit-self -Wextra -Wunused -Wredundant-decls -Wswitch-default -framework SDL2 -framework Cocoa -lm -o release/macos/pt2-clone-macos.app/Contents/MacOS/pt2-clone-macos
     strip release/macos/pt2-clone-macos.app/Contents/MacOS/pt2-clone-macos
     install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 release/macos/pt2-clone-macos.app/Contents/MacOS/pt2-clone-macos
     
--- /dev/null
+++ b/pt_pal_editor/release/macos/pt_pal_editor-osx.app/Contents/MacOS/.gitignore
@@ -1,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
--- /dev/null
+++ b/pt_pal_editor/release/other/.gitignore
@@ -1,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
--- a/release/macos/protracker.ini
+++ b/release/macos/protracker.ini
@@ -38,13 +38,11 @@
 
 ; Use hardware mouse
 ;        Syntax: TRUE or FALSE
-; Default value: FALSE
+; Default value: TRUE
 ;       Comment: Disables software mouse and uses the mouse in your OS.
 ;         This can feel more comfortable for some users because of less latency.
-;         However, the OS mouse cursor can be too small depending on VIDEOSCALE,
-;         and it will not be colored (which tells the current tracker state).
 ;
-HWMOUSE=FALSE
+HWMOUSE=TRUE
 
 ; Make fullscreen mode stretch out the image (will often look bad)
 ;        Syntax: TRUE or FALSE
--- /dev/null
+++ b/release/macos/pt2-clone-macos.app/Contents/MacOS/.gitignore
@@ -1,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
--- a/release/other/protracker.ini
+++ b/release/other/protracker.ini
@@ -38,13 +38,11 @@
 
 ; Use hardware mouse
 ;        Syntax: TRUE or FALSE
-; Default value: FALSE
+; Default value: TRUE
 ;       Comment: Disables software mouse and uses the mouse in your OS.
 ;         This can feel more comfortable for some users because of less latency.
-;         However, the OS mouse cursor can be too small depending on VIDEOSCALE,
-;         and it will not be colored (which tells the current tracker state).
 ;
-HWMOUSE=FALSE
+HWMOUSE=TRUE
 
 ; Make fullscreen mode stretch out the image (will often look bad)
 ;        Syntax: TRUE or FALSE
--- a/release/win32/protracker.ini
+++ b/release/win32/protracker.ini
@@ -38,13 +38,11 @@
 
 ; Use hardware mouse
 ;        Syntax: TRUE or FALSE
-; Default value: FALSE
+; Default value: TRUE
 ;       Comment: Disables software mouse and uses the mouse in your OS.
 ;         This can feel more comfortable for some users because of less latency.
-;         However, the OS mouse cursor can be too small depending on VIDEOSCALE,
-;         and it will not be colored (which tells the current tracker state).
 ;
-HWMOUSE=FALSE
+HWMOUSE=TRUE
 
 ; Make fullscreen mode stretch out the image (will often look bad)
 ;        Syntax: TRUE or FALSE
--- a/release/win64/protracker.ini
+++ b/release/win64/protracker.ini
@@ -38,13 +38,11 @@
 
 ; Use hardware mouse
 ;        Syntax: TRUE or FALSE
-; Default value: FALSE
+; Default value: TRUE
 ;       Comment: Disables software mouse and uses the mouse in your OS.
 ;         This can feel more comfortable for some users because of less latency.
-;         However, the OS mouse cursor can be too small depending on VIDEOSCALE,
-;         and it will not be colored (which tells the current tracker state).
 ;
-HWMOUSE=FALSE
+HWMOUSE=TRUE
 
 ; Make fullscreen mode stretch out the image (will often look bad)
 ;        Syntax: TRUE or FALSE
--- a/src/gfx/pt2_gfx_pointer.c
+++ b/src/gfx/pt2_gfx_pointer.c
@@ -1,9 +1,9 @@
 #include <stdint.h>
 #include "../pt2_palette.h"
 
-// This graphical table has to be in the palette kind of data
-// because it changes color according to status. Easier this
-// way.
+/* This graphical table has to be in the palette kind of data
+ * because it changes color according to status. Easier this
+ * way. */
 
 const uint8_t mousePointerBMP[256] =
 {
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -1070,7 +1070,7 @@
 
 	editor.blockMarkFlag = false;
 
-	pointerSetMode(POINTER_MODE_READ_DIR, NO_CARRY);
+	pointerSetMode(POINTER_MODE_MSG2, NO_CARRY);
 	setStatusMessage("RENDERING MOD...", NO_CARRY);
 
 	editor.ui.disableVisualizer = true;
--- a/src/pt2_diskop.c
+++ b/src/pt2_diskop.c
@@ -333,7 +333,7 @@
 	editor.errorMsgBlock = true;
 	editor.errorMsgCounter = 0;
 
-	pointerErrorMode();
+	setErrPointer();
 }
 
 bool diskOpEntryIsEmpty(int32_t fileIndex)
@@ -644,9 +644,6 @@
 
 	freeDiskOpEntryMem();
 
-	if (!editor.errorMsgActive)
-		pointerSetMode(POINTER_MODE_READ_DIR, NO_CARRY);
-
 	// fill disk op. buffer (type, size, path, file name, date changed)
 
 	// read first file
@@ -703,12 +700,6 @@
 			statusOutOfMemory();
 	}
 
-	if (!editor.errorMsgActive)
-	{
-		pointerSetPreviousMode();
-		diskOpShowSelectText();
-	}
-
 	return true;
 }
 
@@ -949,7 +940,7 @@
 					editor.errorMsgCounter = 0;
 
 					// status/error message is set in the mod loader
-					pointerErrorMode();
+					setErrPointer();
 				}
 			}
 			else if (editor.diskop.mode == DISKOP_MODE_SMP)
--- a/src/pt2_edit.c
+++ b/src/pt2_edit.c
@@ -153,13 +153,10 @@
 				modEntry->head.moduleTitle[i] = (char)tolower(modEntry->head.moduleTitle[i]);
 		}
 
-		if (editor.ui.editObject != PTB_DO_DATAPATH) // special case for disk op. right mouse button
-		{
-			pointerSetPreviousMode();
+		pointerSetPreviousMode();
 
-			if (!editor.mixFlag)
-				updateWindowTitle(MOD_IS_MODIFIED);
-		}
+		if (!editor.mixFlag)
+			updateWindowTitle(MOD_IS_MODIFIED);
 	}
 	else
 	{
--- a/src/pt2_header.h
+++ b/src/pt2_header.h
@@ -13,7 +13,7 @@
 #include <stdint.h>
 #include "pt2_unicode.h"
 
-#define PROG_VER_STR "1.00"
+#define PROG_VER_STR "1.01"
 
 #ifdef _WIN32
 #define DIR_DELIMITER '\\'
@@ -399,7 +399,7 @@
 		uint8_t numLen, numBits;
 
 		// render/update flags
-		bool refreshMousePointer, updateStatusText, updatePatternData;
+		bool updateStatusText, updatePatternData;
 		bool updateSongName, updateMod2WavDialog, mod2WavFinished;
 
 		// edit op. #2
@@ -438,7 +438,8 @@
 		uint16_t *numPtr16, tmpDisp16, *dstOffset, dstPos, textLength, editTextPos;
 		uint16_t dstOffsetEnd, lastSampleOffset;
 		int32_t askTempData, renderX, renderY, renderW, renderH, displayW, displayH;
-		uint32_t xScaleMul, yScaleMul;
+		uint32_t xScale, yScale;
+		double dMouseXMul, dMouseYMul;
 		SDL_PixelFormat *pixelFormat;
 #ifdef _WIN32
 		HWND hWnd;
--- a/src/pt2_keyboard.c
+++ b/src/pt2_keyboard.c
@@ -3670,7 +3670,7 @@
 
 				handleAskNo();
 
-				pointerSetMode(POINTER_MODE_READ_DIR, NO_CARRY);
+				pointerSetMode(POINTER_MODE_MSG2, NO_CARRY);
 				setStatusMessage("RENDERING MOD...", NO_CARRY);
 			}
 		}
@@ -3921,7 +3921,7 @@
 				editor.errorMsgBlock = true;
 				editor.errorMsgCounter = 0;
 
-				pointerErrorMode();
+				setErrPointer();
 			}
 			break;
 
--- a/src/pt2_modloader.c
+++ b/src/pt2_modloader.c
@@ -16,7 +16,7 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include "pt2_palette.h"
+#include "pt2_mouse.h"
 #include "pt2_header.h"
 #include "pt2_sampler.h"
 #include "pt2_textout.h"
@@ -26,7 +26,7 @@
 #include "pt2_unicode.h"
 #include "pt2_modloader.h"
 #include "pt2_sampleloader.h"
-
+#
 typedef struct mem_t
 {
 	bool _eof;
@@ -1161,7 +1161,7 @@
 		editor.errorMsgCounter = 0;
 
 		// status/error message is set in the mod loader
-		pointerErrorMode();
+		setErrPointer();
 	}
 
 	free(filenameU);
@@ -1205,8 +1205,6 @@
 
 	// don't allow drag n' drop if the tracker is busy
 	if (editor.ui.pointerMode == POINTER_MODE_MSG1 ||
-		editor.ui.pointerMode == POINTER_MODE_LOAD ||
-		editor.ui.pointerMode == POINTER_MODE_READ_DIR ||
 		editor.diskop.isFilling || editor.isWAVRendering ||
 		editor.ui.samplerFiltersBoxShown || editor.ui.samplerVolBoxShown)
 	{
@@ -1328,7 +1326,7 @@
 			editor.errorMsgActive = true;
 			editor.errorMsgBlock = true;
 			editor.errorMsgCounter = 0;
-			pointerErrorMode(); // status/error message is set in the mod loader
+			setErrPointer(); // status/error message is set in the mod loader
 		}
 	}
 	else
--- a/src/pt2_mouse.c
+++ b/src/pt2_mouse.c
@@ -25,8 +25,10 @@
 #include "pt2_keyboard.h"
 
 /* TODO: Move irrelevant routines outta here! Disgusting design!
-** Keep in mind that this was programmed in my early programming days... */
+ * Keep in mind that this was programmed in my early programming days... */
 
+SDL_Cursor *cursors[NUM_CURSORS];
+
 extern SDL_Renderer *renderer;
 extern SDL_Window *window;
 
@@ -74,17 +76,166 @@
 static void handleRepeatedGUIButtons(void);
 static void handleRepeatedSamplerFilterButtons(void);
 
-void updateMouseScaling(void)
+static void pointerSetColor(uint8_t cursorColorIndex)
 {
-	double dScaleX, dScaleY;
+	assert(cursorColorIndex <= 5);
 
-	dScaleX = editor.ui.renderW / (double)SCREEN_W;
-	dScaleY = editor.ui.renderH / (double)SCREEN_H;
+	palette[PAL_MOUSE_1] = cursorColors[cursorColorIndex][0];
+	palette[PAL_MOUSE_2] = cursorColors[cursorColorIndex][1];
+	palette[PAL_MOUSE_3] = cursorColors[cursorColorIndex][2];
 
-	editor.ui.xScaleMul = (dScaleX == 0.0) ? 65536 : (uint32_t)round(65536.0 / dScaleX);
-	editor.ui.yScaleMul = (dScaleY == 0.0) ? 65536 : (uint32_t)round(65536.0 / dScaleY);
+	if (ptConfig.hwMouse)
+		setSystemCursor(cursors[cursorColorIndex]);
 }
 
+void pointerSetMode(uint8_t pointerMode, bool carry)
+{
+	assert(pointerMode <= 5);
+
+	editor.ui.pointerMode = pointerMode;
+	if (carry)
+		editor.ui.previousPointerMode = editor.ui.pointerMode;
+
+	switch (pointerMode)
+	{
+		case POINTER_MODE_IDLE:   pointerSetColor(POINTER_GRAY);   break;
+		case POINTER_MODE_PLAY:   pointerSetColor(POINTER_YELLOW); break;
+		case POINTER_MODE_EDIT:   pointerSetColor(POINTER_BLUE);   break;
+		case POINTER_MODE_RECORD: pointerSetColor(POINTER_BLUE);   break;
+		case POINTER_MODE_MSG1:   pointerSetColor(POINTER_PURPLE); break;
+		case POINTER_MODE_MSG2:   pointerSetColor(POINTER_GREEN);  break;
+		default: break;
+	}
+}
+
+void pointerSetPreviousMode(void)
+{
+	if (editor.ui.editTextFlag || editor.ui.askScreenShown || editor.ui.clearScreenShown)
+		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
+	else
+		pointerSetMode(editor.ui.previousPointerMode, NO_CARRY);
+}
+
+void setMsgPointer(void)
+{
+	pointerSetMode(POINTER_MODE_MSG2, false);
+}
+
+void setErrPointer(void)
+{
+	pointerSetColor(POINTER_RED);
+}
+
+bool setSystemCursor(SDL_Cursor *cursor)
+{
+	if (cursor == NULL)
+	{
+		SDL_SetCursor(SDL_GetDefaultCursor());
+		return false;
+	}
+
+	SDL_SetCursor(cursor);
+	return true;
+}
+
+void freeMouseCursors(void)
+{
+	SDL_SetCursor(SDL_GetDefaultCursor());
+	for (uint32_t i = 0; i < NUM_CURSORS; i++)
+	{
+		if (cursors[i] != NULL)
+		{
+			SDL_FreeCursor(cursors[i]);
+			cursors[i] = NULL;
+		}
+	}
+}
+
+bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse pointer shape
+{
+	freeMouseCursors();
+
+	uint8_t scaling = editor.ui.yScale;
+	for (uint32_t i = 0; i < NUM_CURSORS; i++)
+	{
+		SDL_Surface *surface = SDL_CreateRGBSurface(0, POINTER_W*scaling, POINTER_H*scaling, 32, 0, 0, 0, 0);
+		if (surface == NULL)
+		{
+			freeMouseCursors();
+			ptConfig.hwMouse = false; // enable software mouse
+			return false;
+		}
+
+		uint32_t color1 = cursorColors[i][0];
+		uint32_t color2 = cursorColors[i][1];
+		uint32_t color3 = cursorColors[i][2];
+		uint32_t colorkey = 0x12345678;
+
+		color1   = SDL_MapRGB(surface->format, R24(color1),   G24(color1),   B24(color1));
+		color2   = SDL_MapRGB(surface->format, R24(color2),   G24(color2),   B24(color2));
+		color3   = SDL_MapRGB(surface->format, R24(color3),   G24(color3),   B24(color3));
+		colorkey = SDL_MapRGB(surface->format, R24(colorkey), G24(colorkey), B24(colorkey));
+
+		SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
+		SDL_SetColorKey(surface, SDL_TRUE, colorkey);
+		SDL_SetSurfaceRLE(surface, SDL_TRUE);
+
+		const uint8_t *srcPixels8 = mousePointerBMP;
+		SDL_LockSurface(surface);
+
+		uint32_t *dstPixels32 = (uint32_t *)surface->pixels;
+		for (int32_t k = 0; k < surface->w*surface->h; k++) // fill surface with colorkey pixels
+			dstPixels32[k] = colorkey;
+
+		// blit upscaled cursor to surface
+		for (uint32_t y = 0; y < POINTER_H; y++)
+		{
+			uint32_t *outX = &dstPixels32[(y * scaling) * surface->w];
+			for (uint32_t yScale = 0; yScale < scaling; yScale++)
+			{
+				for (uint32_t x = 0; x < POINTER_W; x++)
+				{
+					uint8_t srcPix = srcPixels8[(y * POINTER_W) + x];
+					if (srcPix != PAL_COLORKEY)
+					{
+						uint32_t pixel = colorkey; // make compiler happy
+						     if (srcPix == PAL_MOUSE_1) pixel = color1;
+						else if (srcPix == PAL_MOUSE_2) pixel = color2;
+						else if (srcPix == PAL_MOUSE_3) pixel = color3;
+
+						for (uint32_t xScale = 0; xScale < scaling; xScale++)
+							outX[xScale] = pixel;
+					}
+
+					outX += scaling;
+				}
+			}
+		}
+
+		SDL_UnlockSurface(surface);
+
+		cursors[i] = SDL_CreateColorCursor(surface, 0, 0);
+		if (cursors[i] == NULL)
+		{
+			SDL_FreeSurface(surface);
+			freeMouseCursors();
+			ptConfig.hwMouse = false; // enable software mouse
+			return false;
+		}
+
+		SDL_FreeSurface(surface);
+	}
+
+	pointerSetPreviousMode(); // this sets the appropriate the hardware cursor
+	return true;
+}
+
+void updateMouseScaling(void)
+{
+	if (editor.ui.renderW > 0) editor.ui.dMouseXMul = (double)SCREEN_W / editor.ui.renderW;
+	if (editor.ui.renderH > 0) editor.ui.dMouseYMul = (double)SCREEN_H / editor.ui.renderH;
+}
+
 void readMouseXY(void)
 {
 	int32_t mx, my;
@@ -103,7 +254,7 @@
 	SDL_GetMouseState(&mx, &my);
 
 	/* in centered fullscreen mode, trap the mouse inside the framed image
-	** and subtract the coords to match the OS mouse position (fixes touch from touchscreens) */
+	 * and subtract the coords to match the OS mouse position (fixes touch from touchscreens) */
 	if (editor.fullscreen && !ptConfig.fullScreenStretch)
 	{
 		if (mx < editor.ui.renderX)
@@ -135,9 +286,9 @@
 	if (mx < 0) mx = 0;
 	if (my < 0) my = 0;
 
-	// multiply coords by video scaling factors
-	mx = (((uint32_t)mx * editor.ui.xScaleMul) + (1 << 15)) >> 16;
-	my = (((uint32_t)my * editor.ui.yScaleMul) + (1 << 15)) >> 16;
+	// multiply coords by video scaling factors (do not round)
+	mx = (uint32_t)(mx * editor.ui.dMouseXMul);
+	my = (uint32_t)(my * editor.ui.dMouseYMul);
 
 	if (mx >= SCREEN_W) mx = SCREEN_W - 1;
 	if (my >= SCREEN_H) my = SCREEN_H - 1;
@@ -147,12 +298,12 @@
 
 	if (ptConfig.hwMouse)
 	{
-		// hardware mouse (OS)
+		// hardware mouse
 		hideSprite(SPRITE_MOUSE_POINTER);
 	}
 	else
 	{
-		// software mouse (PT mouse)
+		// software mouse
 		setSpritePos(SPRITE_MOUSE_POINTER, input.mouse.x, input.mouse.y);
 	}
 }
@@ -1980,7 +2131,7 @@
 		editor.errorMsgBlock = true;
 		editor.errorMsgCounter = 0;
 
-		pointerErrorMode();
+		setErrPointer();
 		removeClearScreen();
 		return true;
 	}
@@ -2119,7 +2270,7 @@
 				editor.ui.answerYes = false;
 				handleAskNo();
 
-				pointerSetMode(POINTER_MODE_READ_DIR, NO_CARRY);
+				pointerSetMode(POINTER_MODE_MSG2, NO_CARRY);
 				setStatusMessage("RENDERING MOD...", NO_CARRY);
 			}
 		}
@@ -4314,7 +4465,7 @@
 			editor.errorMsgActive = true;
 			editor.errorMsgBlock = true;
 			editor.errorMsgCounter = 0;
-			pointerErrorMode();
+			setErrPointer();
 		}
 		break;
 
--- a/src/pt2_mouse.h
+++ b/src/pt2_mouse.h
@@ -1,6 +1,8 @@
 #pragma once
 
 #include <stdint.h>
+#include <stdbool.h>
+#include <SDL2/SDL.h>
 
 // taken from the ptplay project and modified
 enum ptbuttons
@@ -231,6 +233,38 @@
 {
 	int32_t x1, y1, x2, y2, b;
 } guiButton_t;
+
+enum
+{
+	POINTER_W = 16,
+	POINTER_H = 16,
+
+	POINTER_MODE_IDLE = 0,
+	POINTER_MODE_EDIT = 1,
+	POINTER_MODE_PLAY = 2,
+	POINTER_MODE_MSG1 = 3,
+	POINTER_MODE_RECORD = 4,
+	POINTER_MODE_MSG2 = 5,
+
+	POINTER_GRAY = 0,
+	POINTER_YELLOW = 1,
+	POINTER_BLUE = 2,
+	POINTER_PURPLE = 3,
+	POINTER_GREEN = 4,
+	POINTER_RED = 5
+};
+
+#define NUM_CURSORS 6
+
+extern SDL_Cursor *cursors[NUM_CURSORS];
+
+void setMsgPointer(void);
+void setErrPointer(void);
+void pointerSetMode(uint8_t pointerMode, bool carry);
+void pointerSetPreviousMode(void);
+bool setSystemCursor(SDL_Cursor *cursor);
+void freeMouseCursors(void);
+bool createMouseCursors(void);
 
 void readMouseXY(void);
 void updateMouseScaling(void);
--- a/src/pt2_palette.c
+++ b/src/pt2_palette.c
@@ -2,6 +2,7 @@
 #include "pt2_palette.h"
 #include "pt2_header.h"
 #include "pt2_helpers.h"
+#include "pt2_tables.h"
 
 uint32_t palette[PALETTE_NUM] =
 {
@@ -25,125 +26,3 @@
 	0xC0FFEE  // 14- PAL_COLORKEY
 	// -----------------------------
 };
-
-void pointerErrorMode(void)
-{
-	palette[PAL_MOUSE_1] = 0x770000;
-	palette[PAL_MOUSE_2] = 0x990000;
-	palette[PAL_MOUSE_3] = 0xCC0000;
-
-	editor.ui.refreshMousePointer = true;
-}
-
-void setMsgPointer(void)
-{
-	editor.ui.pointerMode = POINTER_MODE_READ_DIR;
-
-	palette[PAL_MOUSE_1] = 0x004400;
-	palette[PAL_MOUSE_2] = 0x007700;
-	palette[PAL_MOUSE_3] = 0x00AA00;
-
-	editor.ui.refreshMousePointer = true;
-}
-
-void pointerSetMode(int8_t pointerMode, bool carry)
-{
-	editor.ui.refreshMousePointer = true;
-
-	switch (pointerMode)
-	{
-		case POINTER_MODE_IDLE:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x444444;
-			palette[PAL_MOUSE_2] = 0x777777;
-			palette[PAL_MOUSE_3] = 0xAAAAAA;
-		}
-		break;
-
-		case POINTER_MODE_PLAY:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x444400;
-			palette[PAL_MOUSE_2] = 0x777700;
-			palette[PAL_MOUSE_3] = 0xAAAA00;
-		}
-		break;
-
-		case POINTER_MODE_EDIT:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x000066;
-			palette[PAL_MOUSE_2] = 0x004499;
-			palette[PAL_MOUSE_3] = 0x0055BB;
-		}
-		break;
-
-		case POINTER_MODE_RECORD:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x000066;
-			palette[PAL_MOUSE_2] = 0x004499;
-			palette[PAL_MOUSE_3] = 0x0055BB;
-		}
-		break;
-
-		case POINTER_MODE_MSG1:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x440044;
-			palette[PAL_MOUSE_2] = 0x770077;
-			palette[PAL_MOUSE_3] = 0xAA00AA;
-		}
-		break;
-
-		case POINTER_MODE_READ_DIR:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x004400;
-			palette[PAL_MOUSE_2] = 0x007700;
-			palette[PAL_MOUSE_3] = 0x00AA00;
-		}
-		break;
-
-		case POINTER_MODE_LOAD:
-		{
-			editor.ui.pointerMode = pointerMode;
-			if (carry)
-				editor.ui.previousPointerMode = editor.ui.pointerMode;
-
-			palette[PAL_MOUSE_1] = 0x0000AA;
-			palette[PAL_MOUSE_2] = 0x000077;
-			palette[PAL_MOUSE_3] = 0x000044;
-		}
-		break;
-
-		default: break;
-	}
-}
-
-void pointerSetPreviousMode(void)
-{
-	if (editor.ui.editTextFlag || editor.ui.askScreenShown || editor.ui.clearScreenShown)
-		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
-	else
-		pointerSetMode(editor.ui.previousPointerMode, NO_CARRY);
-}
--- a/src/pt2_palette.h
+++ b/src/pt2_palette.h
@@ -24,19 +24,5 @@
 	// -----------------------------
 	PAL_COLORKEY = 14,
 	// -----------------------------
-	PALETTE_NUM,
-	// -----------------------------
-
-	POINTER_MODE_IDLE = 0,
-	POINTER_MODE_EDIT = 1,
-	POINTER_MODE_PLAY = 2,
-	POINTER_MODE_MSG1 = 3,
-	POINTER_MODE_LOAD = 4,
-	POINTER_MODE_RECORD = 5,
-	POINTER_MODE_READ_DIR = 6
+	PALETTE_NUM
 };
-
-void setMsgPointer(void);
-void pointerErrorMode(void);
-void pointerSetMode(int8_t pointerMode, bool carry);
-void pointerSetPreviousMode(void);
--- a/src/pt2_sampleloader.c
+++ b/src/pt2_sampleloader.c
@@ -19,7 +19,7 @@
 #include <sys/stat.h>
 #include "pt2_header.h"
 #include "pt2_textout.h"
-#include "pt2_palette.h"
+#include "pt2_mouse.h"
 #include "pt2_sampler.h"
 #include "pt2_audio.h"
 #include "pt2_sampleloader.h"
--- a/src/pt2_scopes.c
+++ b/src/pt2_scopes.c
@@ -141,11 +141,11 @@
 		tmpScope = *sc;
 		didSwapData = se->didSwapData;
 
-		samplesToScan = tmpScope.delta >> 16; // XXX: Is this correct? It appears to do what it should, visually.
+		samplesToScan = tmpScope.delta >> 16;
 		if (samplesToScan <= 0)
 			continue;
 
-		if (samplesToScan > 512) // if delta (period) was overflown, don't waste cycles on reading a ton of samples
+		if (samplesToScan > 512) // don't waste cycles on reading a ton of samples
 			samplesToScan = 512;
 
 		volume = modEntry->channels[i].n_volume;
--- a/src/pt2_tables.c
+++ b/src/pt2_tables.c
@@ -11,6 +11,16 @@
 uint32_t *samplerScreenBMP = NULL, *pat2SmpDialogBMP   = NULL, *trackerFrameBMP    = NULL;
 uint32_t *yesNoDialogBMP   = NULL;
 
+const uint32_t cursorColors[6][3] =
+{
+	{ 0x444444, 0x777777, 0xAAAAAA }, // gray
+	{ 0x444400, 0x777700, 0xAAAA00 }, // yellow
+	{ 0x000066, 0x004499, 0x0055BB }, // blue
+	{ 0x440044, 0x770077, 0xAA00AA }, // purple
+	{ 0x004400, 0x007700, 0x00AA00 }, // green
+	{ 0x770000, 0x990000, 0xCC0000 }  // red
+};
+
 int8_t pNoteTable[32] = // for drumpad
 {
 	24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
--- a/src/pt2_tables.h
+++ b/src/pt2_tables.h
@@ -5,6 +5,7 @@
 #include "pt2_mouse.h"
 
 // TABLES
+extern const uint32_t cursorColors[6][3];
 extern const char noteNames1[36][4];
 extern const char noteNames2[36][4];
 extern const uint8_t vibratoTable[32];
--- a/src/pt2_textout.c
+++ b/src/pt2_textout.c
@@ -639,5 +639,5 @@
 	if (*msg != '\0')
 		setStatusMessage(msg, NO_CARRY);
 
-	pointerErrorMode();
+	setErrPointer();
 }
--- a/src/pt2_visuals.c
+++ b/src/pt2_visuals.c
@@ -1709,7 +1709,7 @@
 			editor.errorMsgActive = true;
 			editor.errorMsgBlock = true;
 			editor.errorMsgCounter = 0;
-			pointerErrorMode();
+			setErrPointer();
 		}
 		break;
 	}
@@ -1771,7 +1771,7 @@
 			modEntry->currRow = modEntry->row;
 
 			editor.blockMarkFlag = false;
-			pointerSetMode(POINTER_MODE_READ_DIR, NO_CARRY);
+			pointerSetMode(POINTER_MODE_MSG2, NO_CARRY);
 			setStatusMessage("RENDERING...", NO_CARRY);
 			modSetTempo(modEntry->currBPM);
 			editor.pat2SmpPos = 0;
@@ -2754,6 +2754,11 @@
 		editor.ui.renderX = 0;
 		editor.ui.renderY = 0;
 	}
+
+	// for mouse cursor creation
+	editor.ui.xScale = (uint32_t)round(editor.ui.renderW / (double)SCREEN_W);
+	editor.ui.yScale = (uint32_t)round(editor.ui.renderH / (double)SCREEN_H);
+	createMouseCursors();
 }
 
 void toggleFullScreen(void)
--- a/vs2019_project/pt2-clone/protracker.ini
+++ b/vs2019_project/pt2-clone/protracker.ini
@@ -38,13 +38,11 @@
 
 ; Use hardware mouse
 ;        Syntax: TRUE or FALSE
-; Default value: FALSE
+; Default value: TRUE
 ;       Comment: Disables software mouse and uses the mouse in your OS.
 ;         This can feel more comfortable for some users because of less latency.
-;         However, the OS mouse cursor can be too small depending on VIDEOSCALE,
-;         and it will not be colored (which tells the current tracker state).
 ;
-HWMOUSE=FALSE
+HWMOUSE=TRUE
 
 ; Make fullscreen mode stretch out the image (will often look bad)
 ;        Syntax: TRUE or FALSE