From e9814dea3e32229730b10de53d6586b839462606 Mon Sep 17 00:00:00 2001 From: "theredbluecube2@gmail.com" Date: Thu, 7 Aug 2025 17:48:03 +0300 Subject: [PATCH] add controller support --- .gitignore | 3 +- Makefile.unix => Makefile | 2 +- Makefile.win | 72 --------- src/main.cpp | 316 +++++++++++++++++++++++++++----------- 4 files changed, 230 insertions(+), 163 deletions(-) rename Makefile.unix => Makefile (98%) delete mode 100644 Makefile.win diff --git a/.gitignore b/.gitignore index ab93ccf..3d03f5d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ lib .vscode bin -build -Makefile \ No newline at end of file +build \ No newline at end of file diff --git a/Makefile.unix b/Makefile similarity index 98% rename from Makefile.unix rename to Makefile index c7895da..1751ef4 100644 --- a/Makefile.unix +++ b/Makefile @@ -9,7 +9,7 @@ DBG_CXXFLAGS := $(CXXFLAGS) -g RLS_CXXFLAGS := $(CXXFLAGS) -O2 # linker stuff -LDFLAGS := -lncursesw +LDFLAGS := -lncursesw -lSDL2 DBG_LDFLAGS := $(LDFLAGS) RLS_LDFLAGS := $(LDFLAGS) -s diff --git a/Makefile.win b/Makefile.win deleted file mode 100644 index b0d2965..0000000 --- a/Makefile.win +++ /dev/null @@ -1,72 +0,0 @@ -# SPDX-FileCopyrightText: 2025 thorium1256 -# -# SPDX-License-Identifier: GPL-3.0-or-later - -# compiler stuff -CXX := c++ -CXXFLAGS := -I include -std=c++11 -Wall -Wextra -DBG_CXXFLAGS := $(CXXFLAGS) -g -RLS_CXXFLAGS := $(CXXFLAGS) -O2 - -# linker stuff -LDFLAGS := -L C:\msys64\mingw64\bin -lncursesw6 -DBG_LDFLAGS := $(LDFLAGS) -RLS_STRP_LDFLAGS := $(LDFLAGS) -s - -# directories -BUILD_DIR := build -SRC_DIR := src -BIN_DIR := bin -DBG_DIR := $(BIN_DIR)\debug -RLS_DIR := $(BIN_DIR)\release -LIB_DIR := lib -DBG_EXEC := $(DBG_DIR)\debug.exe -RLS_STRIPPED_EXEC := $(RLS_DIR)\tuimine.exe - -# sources and objects -LIBS := $(wildcard $(LIB_DIR)/*.dll) -LIBS_DBG := $(patsubst $(LIB_DIR)/%.dll, $(DBG_DIR)\\%.dll, $(LIBS)) -LIBS_RLS := $(patsubst $(LIB_DIR)/%.dll, $(RLS_DIR)\\%.dll, $(LIBS)) -SRCS := $(wildcard $(SRC_DIR)/*.cpp) -DBG_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/debug_%.o,$(SRCS)) -RLS_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/release_%.o,$(SRCS)) - -$(shell if not exist $(BIN_DIR) mkdir $(BIN_DIR)) -$(shell if not exist $(DBG_DIR) mkdir $(DBG_DIR)) -$(shell if not exist $(RLS_DIR) mkdir $(RLS_DIR)) -$(shell if not exist $(BUILD_DIR) mkdir $(BUILD_DIR)) - -# phony rules -.PHONY = all debug release clean libraries_debug libraries_release both - -all: release - -debug: $(DBG_EXEC) libraries_debug -release: $(RLS_STRIPPED_EXEC) libraries_release -both: debug release - -libraries_debug: $(LIBS_DBG) -libraries_release: $(LIBS_RLS) - -# linking -$(DBG_EXEC): $(DBG_OBJS) - $(CXX) -o $@ $^ $(DBG_LDFLAGS) - -$(RLS_STRIPPED_EXEC): $(RLS_OBJS) - $(CXX) -o $@ $^ $(RLS_STRP_LDFLAGS) - -# compiling -$(BUILD_DIR)/debug_%.o: $(SRC_DIR)/%.cpp - $(CXX) -c -o $@ $< $(DBG_CXXFLAGS) - -$(BUILD_DIR)/release_%.o: $(SRC_DIR)/%.cpp - $(CXX) -c -o $@ $< $(RLS_CXXFLAGS) - -$(DBG_DIR)\\%.dll: $(LIB_DIR)\\%.dll - xcopy $< $(DBG_DIR)\ >nul - -$(RLS_DIR)\\%.dll: $(LIB_DIR)\\%.dll - xcopy $< $(RLS_DIR)\ >nul - -clean: - rmdir /s /q $(BUILD_DIR) $(DBG_DIR) $(RLS_DIR) diff --git a/src/main.cpp b/src/main.cpp index 1c11e2c..57db112 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,9 +4,10 @@ #include #include -#include +#include #include #include +#include #include "Board.h" #ifndef _WIN32 #include @@ -20,6 +21,8 @@ void handleSIGWINCH(int sig) #define MAX_TIME 60 +SDL_GameController *controller; + void startGame(Board &board) { int minesLeft = board.getMineCount(); @@ -161,99 +164,207 @@ void startGame(Board &board) c = getch(); if (c == ERR) - continue; - - switch (c) { - case KEY_UP: - cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1; - break; - case KEY_DOWN: - cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0; - break; - case KEY_LEFT: - cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1; - break; - case KEY_RIGHT: - cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0; - break; - case 'q': - echo(); - cbreak(); - endwin(); - exit(0); - break; - case 'z': - if (!somethingHasBeenDone) + if (controller != nullptr) { - startTime = time(nullptr); - somethingHasBeenDone = true; + SDL_Event event; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_CONTROLLERBUTTONDOWN) + { + switch (event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_DPAD_UP: + cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1; + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0; + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0; + break; + case SDL_CONTROLLER_BUTTON_START: + if (isPaused) + { + pauseTime = difftime(time(NULL), startPauseTime); + totalpausetime += pauseTime; + } + else + { + startPauseTime = time(NULL); + } + isPaused = !isPaused; + break; + case SDL_CONTROLLER_BUTTON_A: + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed) + { + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + auto neighbors = board.getNeighborsOf(cursorX, cursorY); + int flagCount = 0; + for (const Cell &neighbor : neighbors) + { + if (neighbor.getState() == Cell::State::Flagged) + flagCount++; + } + if (static_cast(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount) + break; + for (const Cell &neighbor : neighbors) + { + if (neighbor.getState() == Cell::State::Flagged) + continue; + board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y); + } + break; + } + board.revealCellAt(cursorX, cursorY); + break; + case SDL_CONTROLLER_BUTTON_B: + if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed) + break; + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged) + { + minesLeft++; + } + else + { + minesLeft--; + } + board.flagCellAt(cursorX, cursorY); + break; + case SDL_CONTROLLER_BUTTON_MISC1: + startTime = time(NULL); + minesLeft = board.getMineCount(); + board.regenerateBoard(); + somethingHasBeenDone = false; + elapsedTime = 0; + break; + case SDL_CONTROLLER_BUTTON_BACK: + echo(); + cbreak(); + endwin(); + exit(0); + break; + } + } + else + { + break; + } + } } - board.revealCellAt(cursorX, cursorY); - break; - case 'x': - if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed) + } + + if (!(c == ERR)) + { + switch (c) + { + case KEY_UP: + cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1; break; - if (!somethingHasBeenDone) - { - startTime = time(nullptr); - somethingHasBeenDone = true; - } - if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged) - { - minesLeft++; - } - else - { - minesLeft--; - } - board.flagCellAt(cursorX, cursorY); - break; - case 'r': - startTime = time(NULL); - minesLeft = board.getMineCount(); - board.regenerateBoard(); - somethingHasBeenDone = false; - elapsedTime = 0; - break; - case 'p': - if (isPaused) - { - pauseTime = difftime(time(NULL), startPauseTime); - totalpausetime += pauseTime; - } - else - { - startPauseTime = time(NULL); - } - isPaused = !isPaused; - break; - case 'c': - if (board.getCellStateAt(cursorX, cursorY) != Cell::State::Revealed) - { + case KEY_DOWN: + cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0; + break; + case KEY_LEFT: + cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1; + break; + case KEY_RIGHT: + cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0; + break; + case 'q': + echo(); + cbreak(); + endwin(); + exit(0); + break; + case 'z': + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + board.revealCellAt(cursorX, cursorY); + break; + case 'x': + if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed) + break; + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged) + { + minesLeft++; + } + else + { + minesLeft--; + } + board.flagCellAt(cursorX, cursorY); + break; + case 'r': + startTime = time(NULL); + minesLeft = board.getMineCount(); + board.regenerateBoard(); + somethingHasBeenDone = false; + elapsedTime = 0; + break; + case 'p': + if (isPaused) + { + pauseTime = difftime(time(NULL), startPauseTime); + totalpausetime += pauseTime; + } + else + { + startPauseTime = time(NULL); + } + isPaused = !isPaused; + break; + case 'c': + if (board.getCellStateAt(cursorX, cursorY) != Cell::State::Revealed) + { + break; + } + if (!somethingHasBeenDone) + { + startTime = time(nullptr); + somethingHasBeenDone = true; + } + auto neighbors = board.getNeighborsOf(cursorX, cursorY); + int flagCount = 0; + for (const Cell &neighbor : neighbors) + { + if (neighbor.getState() == Cell::State::Flagged) + flagCount++; + } + if (static_cast(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount) + break; + for (const Cell &neighbor : neighbors) + { + if (neighbor.getState() == Cell::State::Flagged) + continue; + board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y); + } break; } - if (!somethingHasBeenDone) - { - startTime = time(nullptr); - somethingHasBeenDone = true; - } - auto neighbors = board.getNeighborsOf(cursorX, cursorY); - int flagCount = 0; - for (const Cell &neighbor : neighbors) - { - if (neighbor.getState() == Cell::State::Flagged) - flagCount++; - } - if (static_cast(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount) - break; - for (const Cell &neighbor : neighbors) - { - if (neighbor.getState() == Cell::State::Flagged) - continue; - board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y); - } - break; } if (isPaused) @@ -327,6 +438,19 @@ void startGame(Board &board) } } +SDL_GameController *findController() +{ + for (int i = 0; i < SDL_NumJoysticks(); i++) + { + if (SDL_IsGameController(i)) + { + return SDL_GameControllerOpen(i); + } + } + + return nullptr; +} + int main() { #ifndef _WIN32 @@ -342,6 +466,22 @@ int main() ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); #endif + bool controllerSupport; + + if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) + { + std::cerr << "SDL failed to initialize GameController API! Controller support will not be available!" << std::endl; + controllerSupport = false; + } + else + { + controllerSupport = true; + } + + controller = findController(); + if (controller != nullptr) + controllerSupport = false; // no controller for you + start_color(); init_pair(1, COLOR_BLUE, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK);