commit 9c189354cbaa98dd7eb951cc64f1f52d055fe733 Author: Maschell Date: Sun Aug 30 00:41:54 2020 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95bd17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +build/ +cmake-build-debug/ +cmake/ +*.elf +*.rpx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..24ad3c2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cmake/sdl2"] + path = cmake/sdl2 + url = https://github.com/aminosbh/sdl2-cmake-modules diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1ab03aa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.17) +project(SDL2_Playground) + +set(CMAKE_CXX_STANDARD 20) + +add_executable(${PROJECT_NAME} + src/main.cpp + src/gui/GuiElement.h + src/gui/GuiFrame.cpp + src/gui/GuiFrame.h + src/gui/GuiImage.cpp + src/gui/GuiImage.h + src/gui/sigslot.h + src/CVideo.cpp + src/CVideo.h + src/gui/GuiElement.cpp + src/gui/GuiText.cpp + src/gui/GuiText.h + src/gui/GuiSound.cpp + src/gui/GuiSound.h + src/gui/GuiTrigger.cpp + src/gui/GuiTrigger.h + src/gui/GuiController.h + src/gui/GuiButton.cpp + src/gui/GuiButton.h + + + src/gui/SDLController.h src/MainWindow.cpp src/MainWindow.h src/gui/SDLControllerJoystick.h src/gui/SDLControllerMouse.h + src/gui/SDLControllerWiiUGamepad.h + src/gui/SDLControllerWiiUProContoller.h + ) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2) + +find_package(SDL2 REQUIRED) +find_package(SDL2_image REQUIRED) +find_package(SDL2_ttf REQUIRED) +find_package(SDL2_mixer REQUIRED) + +target_link_libraries(${PROJECT_NAME} SDL2::Main SDL2::Image SDL2::TTF SDL2::Mixer) diff --git a/Makefile.wiiu b/Makefile.wiiu new file mode 100644 index 0000000..40d65da --- /dev/null +++ b/Makefile.wiiu @@ -0,0 +1,144 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wut/share/wut_rules + +WUMS_ROOT := $(DEVKITPRO)/wums +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := SDL2_Playground +BUILD := build +SOURCES := src \ + src/gui +DATA := data +INCLUDES := source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=c++14 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) + +LIBS := -lSDL2_mixer -lSDL2 -lSDL2_image -ljpeg -lvorbisidec -logg -lmodplug -lmpg123 -lSDL2_ttf -lfreetype -lpng -lz -lbz2 -lwut + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) + + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.wiiu + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- + +all : $(OUTPUT).rpx + + +$(OUTPUT).rpx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o: %.s + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..b35d395 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Building + +## Windows with devkitPros version of msys2 +Setup mingw: +1. Add the mingw64 repository to `/etc/pacman.conf`. +``` +[mingw64] +Server = https://repo.msys2.org/mingw/x86_64 +``` +2. Install packages (List taken from [here](https://gist.github.com/thales17/fb2e4cff60890a51d9dddd4c6e832ad2)) +``` +pacman -Syu +pacman -S git mingw-w64-x86_64-toolchain mingw64/mingw-w64-x86_64-SDL2 mingw64/mingw-w64-x86_64-SDL2_mixer mingw64/mingw-w64-x86_64-SDL2_image mingw64/mingw-w64-x86_64-SDL2_ttf mingw64/mingw-w64-x86_64-SDL2_net mingw64/mingw-w64-x86_64-cmake mingw-w64-x86_64-glm make +``` + +``` +mkdir build && cd build +C:\devkitPro\msys2\mingw64\bin\cmake.exe -DSDL2_PATH=C:/devkitPro/msys2/mingw64 -Wno-dev -G "Unix Makefiles" -DCMAKE_CXX_COMPILER=C:/devkitPro/msys2/mingw64/bin/g++.exe DCMAKE_C_COMPILER=C:/devkitPro/msys2/mingw64/bin/gcc.exe ../ +make +``` \ No newline at end of file diff --git a/cmake/sdl2 b/cmake/sdl2 new file mode 160000 index 0000000..ad006a3 --- /dev/null +++ b/cmake/sdl2 @@ -0,0 +1 @@ +Subproject commit ad006a3daae65a612ed87415037e32188b81071e diff --git a/src/CVideo.cpp b/src/CVideo.cpp new file mode 100644 index 0000000..f2647d1 --- /dev/null +++ b/src/CVideo.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "CVideo.h" +#include "logger.h" +#include +#include + +CVideo::CVideo() { + SDL_Init(SDL_INIT_EVERYTHING); + + auto SDLFlags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC; + + //Setup window + window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, 0); + if (!window) { return; } + renderer = SDL_CreateRenderer(window, -1, SDLFlags); + if (!renderer) { return; } + SDL_SetRenderTarget(renderer, NULL); + + if (SDL_Init(SDL_INIT_AUDIO) != 0) { + DEBUG_FUNCTION_LINE("SDL init error: %s\n", SDL_GetError()); + return; + } + + int flags = 0; + int result = 0; + if (flags != (result = Mix_Init(flags))) { + DEBUG_FUNCTION_LINE("Could not initialize mixer (result: %d).\n", result); + DEBUG_FUNCTION_LINE("Mix_Init: %s\n", Mix_GetError()); + } + + auto dev = Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 640); + SDL_PauseAudioDevice(dev, 0); +} + +CVideo::~CVideo() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +SDL_Renderer *CVideo::getRenderer() { + return renderer; +} + +float CVideo::getHeight() { + int h = 0; + SDL_GetWindowSize(window, NULL, &h); + return h; +} + +float CVideo::getWidth() { + int w = 0; + SDL_GetWindowSize(window, &w, NULL); + return w; +} + +unsigned int CVideo::getPixelFormat() { + return SDL_GetWindowPixelFormat(window); +} diff --git a/src/CVideo.h b/src/CVideo.h new file mode 100644 index 0000000..0dbf068 --- /dev/null +++ b/src/CVideo.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once +#include + +class CVideo { +public: + CVideo(); + + virtual ~CVideo(); + + SDL_Renderer *getRenderer(); + + float getHeight(); + float getWidth(); + + unsigned int getPixelFormat(); + +private: + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; +}; \ No newline at end of file diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp new file mode 100644 index 0000000..aa3b321 --- /dev/null +++ b/src/MainWindow.cpp @@ -0,0 +1,75 @@ +#include "MainWindow.h" + +MainWindow::~MainWindow() { + delete label;; + delete touchTrigger;; + delete sound;; + delete image;; + delete image2;; + delete image3;; + delete image4;; + delete image5;; + delete image;; + delete label;; + delete button;; + delete bgMusic;; +} + +MainWindow::MainWindow(int32_t w, int32_t h) : GuiFrame(w, h) { +#if defined _WIN32 + auto picture_path = "test.png"; + auto font_path = "FreeSans.ttf"; + auto bgMusic_path = "bgMusic.ogg"; + auto music_click = "button_click.mp3"; +#else + auto picture_path = "fs:/vol/external01/test.png"; + auto font_path = "fs:/vol/external01/FreeSans.ttf"; + auto bgMusic_path = "fs:/vol/external01/bgMusic.ogg"; + auto music_click = "fs:/vol/external01/button_click.mp3"; +#endif + + TTF_Init(); + TTF_Font *font; + font = TTF_OpenFont(font_path, 35); + + label = new GuiText("This is a test AVAVAVVAVA", 25, {255, 255, 0, 255}, font); + + bgMusic = new GuiSound(bgMusic_path); + bgMusic->SetLoop(true); + bgMusic->Play(); + + image = new GuiImage(picture_path); + image2 = new GuiImage(picture_path); + image3 = new GuiImage(picture_path); + image4 = new GuiImage(picture_path); + image5 = new GuiImage(picture_path); + + button = new GuiButton(image5->getWidth(), image5->getHeight()); + + this->setAlignment(ALIGN_TOP_LEFT); + this->append(button); + this->append(image); + this->append(image2); + this->append(image3); + this->append(image4); + + image->setAlignment(ALIGN_TOP_LEFT); + image2->setAlignment(ALIGN_TOP_RIGHT); + image3->setAlignment(ALIGN_BOTTOM | ALIGN_LEFT); + image4->setAlignment(ALIGN_BOTTOM | ALIGN_RIGHT); + + button->setAlignment(ALIGN_CENTERED); + button->setImage(image5); + + sound = new GuiSound(music_click); + + touchTrigger = new GuiTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::TOUCHED); + touchTrigger = new GuiTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::TOUCHED); + button->setTrigger(touchTrigger); + button->setEffectGrow(); + label->setAlignment(ALIGN_CENTERED); + button->setLabel(label); + button->setSoundClick(sound); + + button->clicked.connect(this, &MainWindow::test); +} \ No newline at end of file diff --git a/src/MainWindow.h b/src/MainWindow.h new file mode 100644 index 0000000..3cd20ee --- /dev/null +++ b/src/MainWindow.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include "gui/GuiFrame.h" +#include "gui/GuiButton.h" +#include "logger.h" + +class MainWindow : public GuiFrame, public sigslot::has_slots<> { +public: + void test(GuiButton *, const GuiController *, GuiTrigger *) { + DEBUG_FUNCTION_LINE("Hello, you have clicked the button"); + } + + ~MainWindow(); + + MainWindow(int32_t w, int32_t h); + +private: + GuiText *label = nullptr; + GuiTrigger *touchTrigger = nullptr; + GuiSound *sound = nullptr; + GuiImage *image = nullptr; + GuiImage *image2 = nullptr; + GuiImage *image3 = nullptr; + GuiImage *image4 = nullptr; + GuiImage *image5 = nullptr; + GuiButton *button = nullptr; + GuiSound *bgMusic = nullptr; +}; diff --git a/src/gui/GuiButton.cpp b/src/gui/GuiButton.cpp new file mode 100644 index 0000000..ca64430 --- /dev/null +++ b/src/gui/GuiButton.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "GuiButton.h" +#include "GuiController.h" + +/** + * Constructor for the GuiButton class. + */ + +GuiButton::GuiButton(float w, float h) { + width = w; + height = h; + image = NULL; + imageOver = NULL; + imageHold = NULL; + imageClick = NULL; + icon = NULL; + iconOver = NULL; + + for (int32_t i = 0; i < 4; i++) { + label[i] = NULL; + labelOver[i] = NULL; + labelHold[i] = NULL; + labelClick[i] = NULL; + } + for (int32_t i = 0; i < iMaxGuiTriggers; i++) { + trigger[i] = NULL; + } + + soundOver = NULL; + soundHold = NULL; + soundClick = NULL; + clickedTrigger = NULL; + heldTrigger = NULL; + selectable = true; + holdable = false; + clickable = true; +} + +/** + * Destructor for the GuiButton class. + */ +GuiButton::~GuiButton() { +} + +void GuiButton::setImage(GuiImage *img) { + image = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setImageOver(GuiImage *img) { + imageOver = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setImageHold(GuiImage *img) { + imageHold = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setImageClick(GuiImage *img) { + imageClick = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setIcon(GuiImage *img) { + icon = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setIconOver(GuiImage *img) { + iconOver = img; + if (img) { img->setParent(this); } +} + +void GuiButton::setLabel(GuiText *txt, int32_t n) { + label[n] = txt; + if (txt) { txt->setParent(this); } +} + +void GuiButton::setLabelOver(GuiText *txt, int32_t n) { + labelOver[n] = txt; + if (txt) { txt->setParent(this); } +} + +void GuiButton::setLabelHold(GuiText *txt, int32_t n) { + labelHold[n] = txt; + if (txt) { txt->setParent(this); } +} + +void GuiButton::setLabelClick(GuiText *txt, int32_t n) { + labelClick[n] = txt; + if (txt) { txt->setParent(this); } +} + +void GuiButton::setSoundOver(GuiSound *snd) { + soundOver = snd; +} + +void GuiButton::setSoundHold(GuiSound *snd) { + soundHold = snd; +} + +void GuiButton::setSoundClick(GuiSound *snd) { + soundClick = snd; +} + +void GuiButton::setTrigger(GuiTrigger *t, int32_t idx) { + if (idx >= 0 && idx < iMaxGuiTriggers) { + trigger[idx] = t; + } else { + for (int32_t i = 0; i < iMaxGuiTriggers; i++) { + if (!trigger[i]) { + trigger[i] = t; + break; + } + } + } +} + +void GuiButton::resetState(void) { + clickedTrigger = NULL; + heldTrigger = NULL; + GuiElement::resetState(); +} + +/** + * Draw the button on screen + */ +void GuiButton::draw(CVideo *v) { + if (!this->isVisible()) { + return; + } + + // draw image + if ((isDrawOverOnlyWhenSelected() && (isStateSet(STATE_SELECTED) && imageOver)) || + (!isDrawOverOnlyWhenSelected() && (isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && imageOver))) { + imageOver->draw(v); + } else if (image) { + image->draw(v); + } + + if ((isDrawOverOnlyWhenSelected() && (isStateSet(STATE_SELECTED) && iconOver)) || + (!isDrawOverOnlyWhenSelected() && (isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && iconOver))) { + iconOver->draw(v); + } else if (icon) { + icon->draw(v); + } + + // draw text + for (int32_t i = 0; i < 4; i++) { + if (isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && labelOver[i]) { + labelOver[i]->draw(v); + } else if (label[i]) { + label[i]->draw(v); + } + } +} + +void GuiButton::update(GuiController *c) { + if (!c || isStateSet(STATE_DISABLED | STATE_HIDDEN | STATE_DISABLE_INPUT, c->chanIdx)) { + return; + } else if (parentElement && (parentElement->isStateSet(STATE_DISABLED | STATE_HIDDEN | STATE_DISABLE_INPUT, c->chanIdx))) { + return; + } + + if (selectable) { + if (c->data.validPointer && this->isInside(c->data.x, c->data.y)) { + if (!isStateSet(STATE_OVER, c->chanIdx)) { + setState(STATE_OVER, c->chanIdx); + + //if(this->isRumbleActive()) + // this->rumble(t->chan); + + if (soundOver) { + soundOver->Play(); + } + + if (effectsOver && !effects) { + // initiate effects + effects = effectsOver; + effectAmount = effectAmountOver; + effectTarget = effectTargetOver; + } + + pointedOn(this, c); + } + } else if (isStateSet(STATE_OVER, c->chanIdx)) { + this->clearState(STATE_OVER, c->chanIdx); + pointedOff(this, c); + + if (effectTarget == effectTargetOver && effectAmount == effectAmountOver) { + // initiate effects (in reverse) + effects = effectsOver; + effectAmount = -effectAmountOver; + effectTarget = 100; + } + } + } + + for (int32_t i = 0; i < iMaxGuiTriggers; i++) { + if (!trigger[i]) { + continue; + } + + // button triggers + if (clickable) { + int32_t isClicked = trigger[i]->clicked(c); + if (!clickedTrigger && (isClicked != GuiTrigger::CLICKED_NONE) + && (trigger[i]->isClickEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { + if (soundClick) { + soundClick->Play(); + } + + clickedTrigger = trigger[i]; + + if (!isStateSet(STATE_CLICKED, c->chanIdx)) { + if (isClicked == GuiTrigger::CLICKED_TOUCH) { + setState(STATE_CLICKED_TOUCH, c->chanIdx); + } else { + setState(STATE_CLICKED, c->chanIdx); + } + } + + clicked(this, c, trigger[i]); + } else if ((isStateSet(STATE_CLICKED, c->chanIdx) || isStateSet(STATE_CLICKED_TOUCH, c->chanIdx)) && (clickedTrigger == trigger[i]) && !isStateSet(STATE_HELD, c->chanIdx) && !trigger[i]->held(c) && + ((isClicked == GuiTrigger::CLICKED_NONE) || trigger[i]->released(c))) { + if ((isStateSet(STATE_CLICKED_TOUCH, c->chanIdx) && this->isInside(c->data.x, c->data.y)) || (isStateSet(STATE_CLICKED, c->chanIdx))) { + clickedTrigger = NULL; + clearState(STATE_CLICKED, c->chanIdx); + released(this, c, trigger[i]); + } + } + } + + if (holdable) { + bool isHeld = trigger[i]->held(c); + + if ((!heldTrigger || heldTrigger == trigger[i]) && isHeld + && (trigger[i]->isHoldEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { + heldTrigger = trigger[i]; + + if (!isStateSet(STATE_HELD, c->chanIdx)) { + setState(STATE_HELD, c->chanIdx); + } + + held(this, c, trigger[i]); + } else if (isStateSet(STATE_HELD, c->chanIdx) && (heldTrigger == trigger[i]) && (!isHeld || trigger[i]->released(c))) { + //! click is removed at this point and converted to held + if (clickedTrigger == trigger[i]) { + clickedTrigger = NULL; + clearState(STATE_CLICKED, c->chanIdx); + } + heldTrigger = NULL; + clearState(STATE_HELD, c->chanIdx); + released(this, c, trigger[i]); + } + } + } +} diff --git a/src/gui/GuiButton.h b/src/gui/GuiButton.h new file mode 100644 index 0000000..b5e0c96 --- /dev/null +++ b/src/gui/GuiButton.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once +#include "GuiElement.h" +#include "GuiImage.h" +#include "GuiText.h" +#include "GuiSound.h" +#include "GuiTrigger.h" +#include "../CVideo.h" + +//!Display, manage, and manipulate buttons in the GUI. Buttons can have images, icons, text, and sound set (all of which are optional) +class GuiButton : public GuiElement { +public: + //!Constructor + //!\param w Width + //!\param h Height + GuiButton(float w, float h); + + //!Destructor + virtual ~GuiButton(); + + //!Sets the button's image + //!\param i Pointer to GuiImage object + void setImage(GuiImage *i); + + //!Sets the button's image on over + //!\param i Pointer to GuiImage object + void setImageOver(GuiImage *i); + + void setIcon(GuiImage *i); + + void setIconOver(GuiImage *i); + + //!Sets the button's image on hold + //!\param i Pointer to GuiImage object + void setImageHold(GuiImage *i); + + //!Sets the button's image on click + //!\param i Pointer to GuiImage object + void setImageClick(GuiImage *i); + + //!Sets the button's label + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabel(GuiText *t, int32_t n = 0); + + //!Sets the button's label on over (eg: different colored text) + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelOver(GuiText *t, int32_t n = 0); + + //!Sets the button's label on hold + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelHold(GuiText *t, int32_t n = 0); + + //!Sets the button's label on click + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelClick(GuiText *t, int32_t n = 0); + + //!Sets the sound to play on over + //!\param s Pointer to GuiSound object + void setSoundOver(GuiSound *s); + + //!Sets the sound to play on hold + //!\param s Pointer to GuiSound object + void setSoundHold(GuiSound *s); + + //!Sets the sound to play on click + //!\param s Pointer to GuiSound object + void setSoundClick(GuiSound *s); + + //!Set a new GuiTrigger for the element + //!\param i Index of trigger array to set + //!\param t Pointer to GuiTrigger + void setTrigger(GuiTrigger *t, int32_t idx = -1); + + //! + void resetState(void); + + //!Constantly called to draw the GuiButton + void draw(CVideo *video); + + //!Constantly called to allow the GuiButton to respond to updated input data + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void update(GuiController *c); + + sigslot::signal2 selected; + sigslot::signal2 deSelected; + sigslot::signal2 pointedOn; + sigslot::signal2 pointedOff; + sigslot::signal3 clicked; + sigslot::signal3 held; + sigslot::signal3 released; +protected: + static const int32_t iMaxGuiTriggers = 10; + + GuiImage *image; //!< Button image (default) + GuiImage *imageOver; //!< Button image for STATE_SELECTED + GuiImage *imageHold; //!< Button image for STATE_HELD + GuiImage *imageClick; //!< Button image for STATE_CLICKED + GuiImage *icon; + GuiImage *iconOver; + GuiText *label[4]; //!< Label(s) to display (default) + GuiText *labelOver[4]; //!< Label(s) to display for STATE_SELECTED + GuiText *labelHold[4]; //!< Label(s) to display for STATE_HELD + GuiText *labelClick[4]; //!< Label(s) to display for STATE_CLICKED + GuiSound *soundOver; //!< Sound to play for STATE_SELECTED + GuiSound *soundHold; //!< Sound to play for STATE_HELD + GuiSound *soundClick; //!< Sound to play for STATE_CLICKED + GuiTrigger *trigger[iMaxGuiTriggers]; //!< GuiTriggers (input actions) that this element responds to + GuiTrigger *clickedTrigger; + GuiTrigger *heldTrigger; +}; diff --git a/src/gui/GuiController.h b/src/gui/GuiController.h new file mode 100644 index 0000000..8866593 --- /dev/null +++ b/src/gui/GuiController.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include +#include "GuiTrigger.h" + +class GuiController { +public: + //!Constructor + GuiController(int32_t channel) + : chan(channel) { + memset(&lastData, 0, sizeof(lastData)); + memset(&data, 0, sizeof(data)); + + switch (chan) { + default: + case GuiTrigger::CHANNEL_1: + chanIdx = 0; + break; + case GuiTrigger::CHANNEL_2: + chanIdx = 1; + break; + case GuiTrigger::CHANNEL_3: + chanIdx = 2; + break; + case GuiTrigger::CHANNEL_4: + chanIdx = 3; + break; + case GuiTrigger::CHANNEL_5: + chanIdx = 4; + break; + } + } + + //!Destructor + virtual ~GuiController() {} + + typedef struct { + uint32_t buttons_h; + uint32_t buttons_d; + uint32_t buttons_r; + bool validPointer; + bool touched; + float pointerAngle; + int32_t x; + int32_t y; + } PadData; + + int32_t chan; + int32_t chanIdx; + PadData data; + PadData lastData; +}; diff --git a/src/gui/GuiElement.cpp b/src/gui/GuiElement.cpp new file mode 100644 index 0000000..1a5d516 --- /dev/null +++ b/src/gui/GuiElement.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "GuiElement.h" + +//! TODO remove this! +static int32_t screenwidth = 1280; +static int32_t screenheight = 720; + +/** + * Constructor for the Object class. + */ +GuiElement::GuiElement() { + xoffset = 0.0f; + yoffset = 0.0f; + zoffset = 0.0f; + width = 0.0f; + height = 0.0f; + alpha = 1.0f; + scaleX = 1.0f; + scaleY = 1.0f; + scaleZ = 1.0f; + for (int32_t i = 0; i < 5; i++) { + state[i] = STATE_DEFAULT; + } + stateChan = -1; + parentElement = NULL; + rumble = true; + selectable = false; + clickable = false; + holdable = false; + drawOverOnlyWhenSelected = false; + visible = true; + yoffsetDyn = 0; + xoffsetDyn = 0; + alphaDyn = -1; + scaleDyn = 1; + effects = EFFECT_NONE; + effectAmount = 0; + effectTarget = 0; + effectsOver = EFFECT_NONE; + effectAmountOver = 0; + effectTargetOver = 0; + angle = 0.0f; + + // default alignment - align to top left + alignment = (ALIGN_TOP_LEFT); +} + +/** + * Get the left position of the GuiElement. + * @see SetLeft() + * @return Left position in pixel. + */ +float GuiElement::getLeft() { + float pWidth = 0; + float pLeft = 0; + float pScaleX = 1.0f; + + if (parentElement) { + pWidth = parentElement->getWidth(); + pLeft = parentElement->getLeft(); + pScaleX = parentElement->getScaleX(); + } + + pLeft += xoffsetDyn; + + float x = pLeft; + + //! TODO: the conversion from int to float and back to int is bad for performance, change that + if (alignment & ALIGN_CENTER) { + x = pLeft + pWidth * 0.5f * pScaleX - width * 0.5f * getScaleX(); + } else if (alignment & ALIGN_RIGHT) { + x = pLeft + pWidth * pScaleX - width * getScaleX(); + } + + return x + xoffset; +} + +/** + * Get the top position of the GuiElement. + * @see SetTop() + * @return Top position in pixel. + */ +float GuiElement::getTop() { + float pHeight = 0; + float pTop = 0; + float pScaleY = 1.0f; + + if (parentElement) { + pHeight = parentElement->getHeight(); + pTop = parentElement->getTop(); + pScaleY = parentElement->getScaleY(); + } + + pTop += yoffsetDyn; + + float y = pTop; + + //! TODO: the conversion from int to float and back to int is bad for performance, change that + if (alignment & ALIGN_MIDDLE) { + y = pTop + pHeight * 0.5f * pScaleY - getHeight() * 0.5f * getScaleY(); + } else if (alignment & ALIGN_BOTTOM) { + y = pTop + pHeight * pScaleY - getHeight() * getScaleY(); + } + + return y + yoffset; +} + +void GuiElement::setEffect(int32_t eff, int32_t amount, int32_t target) { + if (eff & EFFECT_SLIDE_IN) { + // these calculations overcompensate a little + if (eff & EFFECT_SLIDE_TOP) { + if (eff & EFFECT_SLIDE_FROM) { + yoffsetDyn = (int32_t) -getHeight() * scaleY; + } else { + yoffsetDyn = -screenheight; + } + } else if (eff & EFFECT_SLIDE_LEFT) { + if (eff & EFFECT_SLIDE_FROM) { + xoffsetDyn = (int32_t) -getWidth() * scaleX; + } else { + xoffsetDyn = -screenwidth; + } + } else if (eff & EFFECT_SLIDE_BOTTOM) { + if (eff & EFFECT_SLIDE_FROM) { + yoffsetDyn = (int32_t) getHeight() * scaleY; + } else { + yoffsetDyn = screenheight; + } + } else if (eff & EFFECT_SLIDE_RIGHT) { + if (eff & EFFECT_SLIDE_FROM) { + xoffsetDyn = (int32_t) getWidth() * scaleX; + } else { + xoffsetDyn = screenwidth; + } + } + } + if ((eff & EFFECT_FADE) && amount > 0) { + alphaDyn = 0; + } else if ((eff & EFFECT_FADE) && amount < 0) { + alphaDyn = alpha; + } + effects |= eff; + effectAmount = amount; + effectTarget = target; +} + +//!Sets an effect to be enabled on wiimote cursor over +//!\param e Effect to enable +//!\param a Amount of the effect (usage varies on effect) +//!\param t Target amount of the effect (usage varies on effect) +void GuiElement::setEffectOnOver(int32_t e, int32_t a, int32_t t) { + effectsOver |= e; + effectAmountOver = a; + effectTargetOver = t; +} + +void GuiElement::resetEffects() { + yoffsetDyn = 0; + xoffsetDyn = 0; + alphaDyn = -1; + scaleDyn = 1; + effects = EFFECT_NONE; + effectAmount = 0; + effectTarget = 0; + effectsOver = EFFECT_NONE; + effectAmountOver = 0; + effectTargetOver = 0; +} + +void GuiElement::updateEffects() { + if (!this->isVisible() && parentElement) { + return; + } + + if (effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_SLIDE_FROM)) { + if (effects & EFFECT_SLIDE_IN) { + if (effects & EFFECT_SLIDE_LEFT) { + xoffsetDyn += effectAmount; + + if (xoffsetDyn >= 0) { + xoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_RIGHT) { + xoffsetDyn -= effectAmount; + + if (xoffsetDyn <= 0) { + xoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_TOP) { + yoffsetDyn += effectAmount; + + if (yoffsetDyn >= 0) { + yoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_BOTTOM) { + yoffsetDyn -= effectAmount; + + if (yoffsetDyn <= 0) { + yoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } + } else { + if (effects & EFFECT_SLIDE_LEFT) { + xoffsetDyn -= effectAmount; + + if (xoffsetDyn <= -screenwidth) { + effects = 0; // shut off effect + effectFinished(this); + } else if ((effects & EFFECT_SLIDE_FROM) && xoffsetDyn <= -getWidth()) { + effects = 0; // shut off effect + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_RIGHT) { + xoffsetDyn += effectAmount; + + if (xoffsetDyn >= screenwidth) { + effects = 0; // shut off effect + effectFinished(this); + } else if ((effects & EFFECT_SLIDE_FROM) && xoffsetDyn >= getWidth() * scaleX) { + effects = 0; // shut off effect + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_TOP) { + yoffsetDyn -= effectAmount; + + if (yoffsetDyn <= -screenheight) { + effects = 0; // shut off effect + effectFinished(this); + } else if ((effects & EFFECT_SLIDE_FROM) && yoffsetDyn <= -getHeight()) { + effects = 0; // shut off effect + effectFinished(this); + } + } else if (effects & EFFECT_SLIDE_BOTTOM) { + yoffsetDyn += effectAmount; + + if (yoffsetDyn >= screenheight) { + effects = 0; // shut off effect + effectFinished(this); + } else if ((effects & EFFECT_SLIDE_FROM) && yoffsetDyn >= getHeight()) { + effects = 0; // shut off effect + effectFinished(this); + } + } + } + } else if (effects & EFFECT_FADE) { + alphaDyn += effectAmount * (1.0f / 255.0f); + + if (effectAmount < 0 && alphaDyn <= 0) { + alphaDyn = 0; + effects = 0; // shut off effect + effectFinished(this); + } else if (effectAmount > 0 && alphaDyn >= alpha) { + alphaDyn = alpha; + effects = 0; // shut off effect + effectFinished(this); + } + } else if (effects & EFFECT_SCALE) { + scaleDyn += effectAmount * 0.01f; + + if ((effectAmount < 0 && scaleDyn <= (effectTarget * 0.01f)) + || (effectAmount > 0 && scaleDyn >= (effectTarget * 0.01f))) { + scaleDyn = effectTarget * 0.01f; + effects = 0; // shut off effect + effectFinished(this); + } + } +} diff --git a/src/gui/GuiElement.h b/src/gui/GuiElement.h new file mode 100644 index 0000000..8d95e97 --- /dev/null +++ b/src/gui/GuiElement.h @@ -0,0 +1,604 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sigslot.h" + +enum { + EFFECT_NONE = 0x00, + EFFECT_SLIDE_TOP = 0x01, + EFFECT_SLIDE_BOTTOM = 0x02, + EFFECT_SLIDE_RIGHT = 0x04, + EFFECT_SLIDE_LEFT = 0x08, + EFFECT_SLIDE_IN = 0x10, + EFFECT_SLIDE_OUT = 0x20, + EFFECT_SLIDE_FROM = 0x40, + EFFECT_FADE = 0x80, + EFFECT_SCALE = 0x100, + EFFECT_COLOR_TRANSITION = 0x200 +}; + +enum { + ALIGN_LEFT = 0x01, + ALIGN_CENTER = 0x02, + ALIGN_RIGHT = 0x04, + ALIGN_TOP = 0x10, + ALIGN_MIDDLE = 0x20, + ALIGN_BOTTOM = 0x40, + ALIGN_TOP_LEFT = ALIGN_LEFT | ALIGN_TOP, + ALIGN_TOP_CENTER = ALIGN_CENTER | ALIGN_TOP, + ALIGN_TOP_RIGHT = ALIGN_RIGHT | ALIGN_TOP, + ALIGN_CENTERED = ALIGN_CENTER | ALIGN_MIDDLE, +}; + +//!Forward declaration +class GuiController; + +class CVideo; + +//!Primary GUI class. Most other classes inherit from this class. +class GuiElement { +public: + //!Constructor + GuiElement(); + + //!Destructor + virtual ~GuiElement() {} + + //!Set the element's parent + //!\param e Pointer to parent element + virtual void setParent(GuiElement *e) { + parentElement = e; + } + + //!Gets the element's parent + //!\return Pointer to parent element + virtual GuiElement *getParent() { + return parentElement; + } + + //!Gets the current leftmost coordinate of the element + //!Considers horizontal alignment, x offset, width, and parent element's GetLeft() / GetWidth() values + //!\return left coordinate + virtual float getLeft(); + + //!Gets the current topmost coordinate of the element + //!Considers vertical alignment, y offset, height, and parent element's GetTop() / GetHeight() values + //!\return top coordinate + virtual float getTop(); + + //!Gets the current Z coordinate of the element + //!\return Z coordinate + virtual float getDepth() { + float zParent = 0.0f; + + if (parentElement) { + zParent = parentElement->getDepth(); + } + + return zParent + zoffset; + } + + virtual float getCenterX(void) { + float pCenterX = 0.0f; + + if (parentElement) { + pCenterX = parentElement->getCenterX(); + } + + pCenterX += xoffset + xoffsetDyn; + + if (alignment & ALIGN_LEFT) { + float pWidth = 0.0f; + float pScale = 0.0f; + + if (parentElement) { + pWidth = parentElement->getWidth(); + pScale = parentElement->getScaleX(); + } + + pCenterX -= pWidth * 0.5f * pScale - width * 0.5f * getScaleX(); + } else if (alignment & ALIGN_RIGHT) { + float pWidth = 0.0f; + float pScale = 0.0f; + + if (parentElement) { + pWidth = parentElement->getWidth(); + pScale = parentElement->getScaleX(); + } + + pCenterX += pWidth * 0.5f * pScale - width * 0.5f * getScaleX(); + } + return pCenterX; + } + + virtual float getCenterY(void) { + float pCenterY = 0.0f; + + if (parentElement) { + pCenterY = parentElement->getCenterY(); + } + + pCenterY += yoffset + yoffsetDyn; + + if (alignment & ALIGN_TOP) { + float pHeight = 0.0f; + float pScale = 0.0f; + + if (parentElement) { + pHeight = parentElement->getHeight(); + pScale = parentElement->getScaleY(); + } + + pCenterY += pHeight * 0.5f * pScale + getHeight() * 0.5f * getScaleY(); + } else if (alignment & ALIGN_BOTTOM) { + float pHeight = 0.0f; + float pScale = 0.0f; + + if (parentElement) { + pHeight = parentElement->getHeight(); + pScale = parentElement->getScaleY(); + } + + pCenterY -= pHeight * 0.5f * pScale - getHeight() * 0.5f * getScaleY(); + } + return pCenterY; + } + + //!Gets elements xoffset + virtual float getOffsetX() { + return xoffset; + } + + //!Gets elements yoffset + virtual float getOffsetY() { + return yoffset; + } + + //!Gets the current width of the element. Does not currently consider the scale + //!\return width + virtual float getWidth() { + return width; + }; + + //!Gets the height of the element. Does not currently consider the scale + //!\return height + virtual float getHeight() { + return height; + } + + //!Sets the size (width/height) of the element + //!\param w Width of element + //!\param h Height of element + virtual void setSize(float w, float h) { + width = w; + height = h; + } + + //!Sets the element's visibility + //!\param v Visibility (true = visible) + virtual void setVisible(bool v) { + visible = v; + visibleChanged(this, v); + } + + //!Checks whether or not the element is visible + //!\return true if visible, false otherwise + virtual bool isVisible() const { + return !isStateSet(STATE_HIDDEN) && visible; + }; + + //!Checks whether or not the element is selectable + //!\return true if selectable, false otherwise + virtual bool isSelectable() { + return !isStateSet(STATE_DISABLED) && selectable; + } + + virtual bool isDrawOverOnlyWhenSelected() { + return drawOverOnlyWhenSelected; + } + + virtual void setdrawOverOnlyWhenSelected(bool s) { + drawOverOnlyWhenSelected = s; + } + + //!Checks whether or not the element is clickable + //!\return true if clickable, false otherwise + virtual bool isClickable() { + return !isStateSet(STATE_DISABLED) && clickable; + } + + //!Checks whether or not the element is holdable + //!\return true if holdable, false otherwise + virtual bool isHoldable() { + return !isStateSet(STATE_DISABLED) && holdable; + } + + //!Sets whether or not the element is selectable + //!\param s Selectable + virtual void setSelectable(bool s) { + selectable = s; + } + + //!Sets whether or not the element is clickable + //!\param c Clickable + virtual void setClickable(bool c) { + clickable = c; + } + + //!Sets whether or not the element is holdable + //!\param c Holdable + virtual void setHoldable(bool d) { + holdable = d; + } + + //!Sets the element's state + //!\param s State (STATE_DEFAULT, STATE_SELECTED, STATE_CLICKED, STATE_DISABLED) + //!\param c Controller channel (0-3, -1 = none) + virtual void setState(int32_t s, int32_t c = -1) { + if (c >= 0 && c < 5) { + state[c] |= s; + } else { + for (int32_t i = 0; i < 5; i++) { + state[i] |= s; + } + } + stateChan = c; + stateChanged(this, s, c); + } + + virtual void clearState(int32_t s, int32_t c = -1) { + if (c >= 0 && c < 5) { + state[c] &= ~s; + } else { + for (int32_t i = 0; i < 5; i++) { + state[i] &= ~s; + } + } + stateChan = c; + stateChanged(this, s, c); + } + + virtual bool isStateSet(int32_t s, int32_t c = -1) const { + if (c >= 0 && c < 5) { + return (state[c] & s) != 0; + } else { + for (int32_t i = 0; i < 5; i++) { + if ((state[i] & s) != 0) { + return true; + } + } + + return false; + } + } + + //!Gets the element's current state + //!\return state + virtual int32_t getState(int32_t c = 0) { + return state[c]; + }; + + //!Gets the controller channel that last changed the element's state + //!\return Channel number (0-3, -1 = no channel) + virtual int32_t getStateChan() { + return stateChan; + }; + + //!Resets the element's state to STATE_DEFAULT + virtual void resetState() { + for (int32_t i = 0; i < 5; i++) { + state[i] = STATE_DEFAULT; + } + stateChan = -1; + } + + //!Sets the element's alpha value + //!\param a alpha value + virtual void setAlpha(float a) { + alpha = a; + } + + //!Gets the element's alpha value + //!Considers alpha, alphaDyn, and the parent element's getAlpha() value + //!\return alpha + virtual float getAlpha() { + float a; + + if (alphaDyn >= 0) { + a = alphaDyn; + } else { + a = alpha; + } + + if (parentElement) { + a = (a * parentElement->getAlpha()); + } + + return a; + } + + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScale(float s) { + scaleX = s; + scaleY = s; + scaleZ = s; + } + + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleX(float s) { + scaleX = s; + } + + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleY(float s) { + scaleY = s; + } + + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleZ(float s) { + scaleZ = s; + } + + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScale() { + float s = 0.5f * (scaleX + scaleY) * scaleDyn; + + if (parentElement) { + s *= parentElement->getScale(); + } + + return s; + } + + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleX() { + float s = scaleX * scaleDyn; + + if (parentElement) { + s *= parentElement->getScaleX(); + } + + return s; + } + + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleY() { + float s = scaleY * scaleDyn; + + if (parentElement) { + s *= parentElement->getScaleY(); + } + + return s; + } + + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleZ() { + float s = scaleZ; + + if (parentElement) { + s *= parentElement->getScaleZ(); + } + + return s; + } + + //!Checks whether rumble was requested by the element + //!\return true is rumble was requested, false otherwise + virtual bool isRumbleActive() { + return rumble; + } + + //!Sets whether or not the element is requesting a rumble event + //!\param r true if requesting rumble, false if not + virtual void setRumble(bool r) { + rumble = r; + } + + //!Set an effect for the element + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + virtual void setEffect(int32_t e, int32_t a, int32_t t = 0); + + //!Sets an effect to be enabled on wiimote cursor over + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + virtual void setEffectOnOver(int32_t e, int32_t a, int32_t t = 0); + + //!Shortcut to SetEffectOnOver(EFFECT_SCALE, 4, 110) + virtual void setEffectGrow() { + setEffectOnOver(EFFECT_SCALE, 4, 110); + } + + //!Reset all applied effects + virtual void resetEffects(); + + //!Gets the current element effects + //!\return element effects + virtual int32_t getEffect() const { + return effects; + } + + //!\return true if element animation is on going + virtual bool isAnimated() const { + return (parentElement != 0) && (getEffect() > 0); + } + + //!Checks whether the specified coordinates are within the element's boundaries + //!\param x X coordinate + //!\param y Y coordinate + //!\return true if contained within, false otherwise + virtual bool isInside(float x, float y) { + return (x > (this->getCenterX() - getScaleX() * getWidth() * 0.5f) + && x < (this->getCenterX() + getScaleX() * getWidth() * 0.5f) + && y > (this->getCenterY() - getScaleY() * getHeight() * 0.5f) + && y < (this->getCenterY() + getScaleY() * getHeight() * 0.5f)); + } + + //!Sets the element's position + //!\param x X coordinate + //!\param y Y coordinate + virtual void setPosition(float x, float y) { + xoffset = x; + yoffset = y; + } + + //!Sets the element's position + //!\param x X coordinate + //!\param y Y coordinate + //!\param z Z coordinate + virtual void setPosition(float x, float y, float z) { + xoffset = x; + yoffset = y; + zoffset = z; + } + + //!Gets whether or not the element is in STATE_SELECTED + //!\return true if selected, false otherwise + virtual int32_t getSelected() { + return -1; + } + + //!Sets the element's alignment respective to its parent element + //!Bitwise ALIGN_LEFT | ALIGN_RIGHT | ALIGN_CENTRE, ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE) + //!\param align Alignment + virtual void setAlignment(int32_t a) { + alignment = a; + } + + //!Gets the element's alignment + virtual int32_t getAlignment() const { + return alignment; + } + + //!Angle of the object + virtual void setAngle(float a) { + angle = a; + } + + //!Angle of the object + virtual float getAngle() const { + float r_angle = angle; + if (parentElement) { r_angle += parentElement->getAngle(); } + return r_angle; + } + + //!Called constantly to allow the element to respond to the current input data + //!\param t Pointer to a GuiController, containing the current input data from PAD/WPAD/VPAD + virtual void update(GuiController *t) {} + + //!Called constantly to redraw the element + virtual void draw(CVideo *v) {} + + //!Called constantly to process stuff in the element + virtual void process() {} + + //!Updates the element's effects (dynamic values) + //!Called by Draw(), used for animation purposes + virtual void updateEffects(); + + typedef struct _POINT { + int32_t x; + int32_t y; + } POINT; + + enum { + STATE_DEFAULT = 0, + STATE_SELECTED = 0x01, + STATE_CLICKED = 0x02, + STATE_HELD = 0x04, + STATE_OVER = 0x08, + STATE_HIDDEN = 0x10, + STATE_DISABLE_INPUT = 0x20, + STATE_CLICKED_TOUCH = 0x40, + STATE_DISABLED = 0x80 + }; + + //! Switch pointer from control to screen position + POINT PtrToScreen(POINT p) { + //! TODO for 3D + //POINT r = { p.x + getLeft(), p.y + getTop() }; + return p; + } + + //! Switch pointer screen to control position + POINT PtrToControl(POINT p) { + //! TODO for 3D + //POINT r = { p.x - getLeft(), p.y - getTop() }; + return p; + } + + //! Signals + sigslot::signal2 visibleChanged; + sigslot::signal3 stateChanged; + sigslot::signal1 effectFinished; +protected: + bool rumble; //!< Wiimote rumble (on/off) - set to on when this element requests a rumble event + bool visible; //!< Visibility of the element. If false, Draw() is skipped + bool selectable; //!< Whether or not this element selectable (can change to SELECTED state) + bool clickable; //!< Whether or not this element is clickable (can change to CLICKED state) + bool holdable; //!< Whether or not this element is holdable (can change to HELD state) + bool drawOverOnlyWhenSelected; //!< Whether or not this element is holdable (can change to HELD state) + float width; //!< Element width + float height; //!< Element height + float xoffset; //!< Element X offset + float yoffset; //!< Element Y offset + float zoffset; //!< Element Z offset + float alpha; //!< Element alpha value (0-255) + float angle; //!< Angle of the object (0-360) + float scaleX; //!< Element scale (1 = 100%) + float scaleY; //!< Element scale (1 = 100%) + float scaleZ; //!< Element scale (1 = 100%) + int32_t alignment; //!< Horizontal element alignment, respective to parent element + int32_t state[5]; //!< Element state (DEFAULT, SELECTED, CLICKED, DISABLED) + int32_t stateChan; //!< Which controller channel is responsible for the last change in state + GuiElement *parentElement; //!< Parent element + + //! TODO: Move me to some Animator class + int32_t xoffsetDyn; //!< Element X offset, dynamic (added to xoffset value for animation effects) + int32_t yoffsetDyn; //!< Element Y offset, dynamic (added to yoffset value for animation effects) + float alphaDyn; //!< Element alpha, dynamic (multiplied by alpha value for blending/fading effects) + float scaleDyn; //!< Element scale, dynamic (multiplied by alpha value for blending/fading effects) + int32_t effects; //!< Currently enabled effect(s). 0 when no effects are enabled + int32_t effectAmount; //!< Effect amount. Used by different effects for different purposes + int32_t effectTarget; //!< Effect target amount. Used by different effects for different purposes + int32_t effectsOver; //!< Effects to enable when wiimote cursor is over this element. Copied to effects variable on over event + int32_t effectAmountOver; //!< EffectAmount to set when wiimote cursor is over this element + int32_t effectTargetOver; //!< EffectTarget to set when wiimote cursor is over this element +}; diff --git a/src/gui/GuiFrame.cpp b/src/gui/GuiFrame.cpp new file mode 100644 index 0000000..eeaf77c --- /dev/null +++ b/src/gui/GuiFrame.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "GuiFrame.h" + +GuiFrame::GuiFrame(GuiFrame *p) { + parent = p; + width = 0; + height = 0; + dim = false; + + if (parent) { + parent->append(this); + } +} + +GuiFrame::GuiFrame(float w, float h, GuiFrame *p) { + parent = p; + width = w; + height = h; + dim = false; + + if (parent) { + parent->append(this); + } +} + +GuiFrame::~GuiFrame() { + closing(this); + + if (parent) { + parent->remove(this); + } +} + +void GuiFrame::append(GuiElement *e) { + if (e == NULL) { + return; + } + + remove(e); + mutex.lock(); + elements.push_back(e); + e->setParent(this); + mutex.unlock(); +} + +void GuiFrame::insert(GuiElement *e, uint32_t index) { + if (e == NULL || (index >= elements.size())) { + return; + } + + remove(e); + mutex.lock(); + elements.insert(elements.begin() + index, e); + e->setParent(this); + mutex.unlock(); +} + +void GuiFrame::remove(GuiElement *e) { + if (e == NULL) { + return; + } + + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + if (e == elements[i]) { + elements.erase(elements.begin() + i); + break; + } + } + mutex.unlock(); +} + +void GuiFrame::removeAll() { + mutex.lock(); + elements.clear(); + mutex.unlock(); +} + +void GuiFrame::close() { + //Application::instance()->pushForDelete(this); +} + +void GuiFrame::dimBackground(bool d) { + dim = d; +} + +GuiElement *GuiFrame::getGuiElementAt(uint32_t index) const { + if (index >= elements.size()) { + return NULL; + } + + return elements[index]; +} + +uint32_t GuiFrame::getSize() { + return elements.size(); +} + +void GuiFrame::resetState() { + GuiElement::resetState(); + + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + elements[i]->resetState(); + } + mutex.unlock(); +} + +void GuiFrame::setState(int32_t s, int32_t c) { + GuiElement::setState(s, c); + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + elements[i]->setState(s, c); + } + mutex.unlock(); +} + +void GuiFrame::clearState(int32_t s, int32_t c) { + GuiElement::clearState(s, c); + + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + elements[i]->clearState(s, c); + } + mutex.unlock(); +} + +void GuiFrame::setVisible(bool v) { + visible = v; + + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + elements[i]->setVisible(v); + } + mutex.unlock(); +} + +int32_t GuiFrame::getSelected() { + // find selected element + int32_t found = -1; + mutex.lock(); + for (uint32_t i = 0; i < elements.size(); ++i) { + if (elements[i]->isStateSet(STATE_SELECTED | STATE_OVER)) { + found = i; + break; + } + } + mutex.unlock(); + return found; +} + +void GuiFrame::draw(CVideo *v) { + if (!this->isVisible() && parentElement) { + return; + } + + if (parentElement && dim == true) { + //GXColor dimColor = (GXColor){0, 0, 0, 0x70}; + //Menu_DrawRectangle(0, 0, GetZPosition(), screenwidth,screenheight, &dimColor, false, true); + } + + mutex.lock(); + //! render appended items next frame but allow stop of render if size is reached + uint32_t size = elements.size(); + + for (uint32_t i = 0; i < size && i < elements.size(); ++i) { + elements[i]->draw(v); + } + mutex.unlock(); +} + +void GuiFrame::updateEffects() { + if (!this->isVisible() && parentElement) { + return; + } + + GuiElement::updateEffects(); + + mutex.lock(); + //! render appended items next frame but allow stop of render if size is reached + uint32_t size = elements.size(); + + for (uint32_t i = 0; i < size && i < elements.size(); ++i) { + elements[i]->updateEffects(); + } + mutex.unlock(); +} + +void GuiFrame::process() { + if (!this->isVisible() && parentElement) { + return; + } + + GuiElement::process(); + + mutex.lock(); + //! render appended items next frame but allow stop of render if size is reached + uint32_t size = elements.size(); + + for (uint32_t i = 0; i < size && i < elements.size(); ++i) { + elements[i]->process(); + } + mutex.unlock(); +} + +void GuiFrame::update(GuiController *c) { + if (isStateSet(STATE_DISABLED) && parentElement) { + return; + } + + mutex.lock(); + //! update appended items next frame + uint32_t size = elements.size(); + + for (uint32_t i = 0; i < size && i < elements.size(); ++i) { + elements[i]->update(c); + } + mutex.unlock(); +} diff --git a/src/gui/GuiFrame.h b/src/gui/GuiFrame.h new file mode 100644 index 0000000..9f907d8 --- /dev/null +++ b/src/gui/GuiFrame.h @@ -0,0 +1,123 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include +#include +#include "GuiElement.h" +#include "sigslot.h" + +//!Allows GuiElements to be grouped together into a "window" +class GuiFrame : public GuiElement { +public: + //!Constructor + GuiFrame(GuiFrame *parent = 0); + + //!\overload + //!\param w Width of window + //!\param h Height of window + GuiFrame(float w, float h, GuiFrame *parent = 0); + + //!Destructor + virtual ~GuiFrame(); + + //!Appends a GuiElement to the GuiFrame + //!\param e The GuiElement to append. If it is already in the GuiFrame, it is removed first + void append(GuiElement *e); + + //!Inserts a GuiElement into the GuiFrame at the specified index + //!\param e The GuiElement to insert. If it is already in the GuiFrame, it is removed first + //!\param i Index in which to insert the element + void insert(GuiElement *e, uint32_t i); + + //!Removes the specified GuiElement from the GuiFrame + //!\param e GuiElement to be removed + void remove(GuiElement *e); + + //!Removes all GuiElements + void removeAll(); + + //!Bring element to front of the window + void bringToFront(GuiElement *e) { + remove(e); + append(e); + } + + //!Returns the GuiElement at the specified index + //!\param index The index of the element + //!\return A pointer to the element at the index, NULL on error (eg: out of bounds) + GuiElement *getGuiElementAt(uint32_t index) const; + + //!Returns the size of the list of elements + //!\return The size of the current element list + uint32_t getSize(); + + //!Sets the visibility of the window + //!\param v visibility (true = visible) + void setVisible(bool v); + + //!Resets the window's state to STATE_DEFAULT + void resetState(); + + //!Sets the window's state + //!\param s State + void setState(int32_t s, int32_t c = -1); + + void clearState(int32_t s, int32_t c = -1); + + //!Gets the index of the GuiElement inside the window that is currently selected + //!\return index of selected GuiElement + int32_t getSelected(); + + //!Dim the Window's background + void dimBackground(bool d); + + //!Draws all the elements in this GuiFrame + void draw(CVideo *v); + + //!Updates the window and all elements contains within + //!Allows the GuiFrame and all elements to respond to the input data specified + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void update(GuiController *t); + + //!virtual Close Window - this will put the object on the delete queue in MainWindow + virtual void close(); + + //!virtual show window function + virtual void show() {} + + //!virtual hide window function + virtual void hide() {} + + //!virtual enter main loop function (blocking) + virtual void exec() {} + + //!virtual updateEffects which is called by the main loop + virtual void updateEffects(); + + //!virtual process which is called by the main loop + virtual void process(); + + //! Signals + //! On Closing + sigslot::signal1 closing; +protected: + bool dim; //! Enable/disable dim of a window only + GuiFrame *parent; //!< Parent Window + std::vector elements; //!< Contains all elements within the GuiFrame + std::recursive_mutex mutex; +}; diff --git a/src/gui/GuiImage.cpp b/src/gui/GuiImage.cpp new file mode 100644 index 0000000..122ce07 --- /dev/null +++ b/src/gui/GuiImage.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include +#include "GuiImage.h" +#include "../CVideo.h" +#include "../logger.h" + +GuiImage::GuiImage(const std::string& path) { + imgSurface = IMG_Load( path.c_str() ); + + this->width = imgSurface->w; + this->height = imgSurface->h; +} + +/** + * Destructor for the GuiImage class. + */ +GuiImage::~GuiImage() { + if(imgSurface){ + SDL_FreeSurface(imgSurface); + imgSurface = NULL; + } + if(texture){ + SDL_DestroyTexture(texture); + texture = NULL; + } +} + +void GuiImage::process(){ + auto res = this->getAngle() + 1; + if(res > 360){ + res =0; + } + + setAngle(res); + +} + +void GuiImage::draw(CVideo *pVideo) { + if (!this->isVisible()) { + return; + } + + if(texture == NULL){ + SDL_Surface * optimizedSurface = SDL_ConvertSurfaceFormat( imgSurface, pVideo->getPixelFormat(), 0 ); + if(optimizedSurface != NULL){ + SDL_FreeSurface(imgSurface); + imgSurface = optimizedSurface; + DEBUG_FUNCTION_LINE("Optimized surface"); + } + texture = SDL_CreateTextureFromSurface(pVideo->getRenderer(), imgSurface); + } + + float currScaleX = getScaleX(); + float currScaleY = getScaleY(); + + SDL_Rect rect; + rect.x = getLeft(); + rect.y = getTop(); + rect.w = currScaleX * getWidth(); + rect.h = currScaleY * getHeight(); + + + // copy the texture to the rendering context + if(getAngle() == 0){ + SDL_RenderCopy(pVideo->getRenderer(), texture, NULL, &rect); + }else{ + SDL_RenderCopyEx(pVideo->getRenderer(), texture, NULL, &rect, getAngle(), NULL, SDL_FLIP_NONE); + } + + +} diff --git a/src/gui/GuiImage.h b/src/gui/GuiImage.h new file mode 100644 index 0000000..142729d --- /dev/null +++ b/src/gui/GuiImage.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include +#include "GuiElement.h" +#include "glm/glm.hpp" + +//!Display, manage, and manipulate images in the GUI +class GuiImage : public GuiElement { +public: + //!\overload + //!\param img Pointer to GuiImageData element + GuiImage(const std::string &path); + + //!Destructor + virtual ~GuiImage(); + + //!Constantly called to draw the image + void draw(CVideo *pVideo); + + +protected: + SDL_Surface *imgSurface = nullptr; + SDL_Texture *texture = nullptr; + + virtual void process(); +}; diff --git a/src/gui/GuiSound.cpp b/src/gui/GuiSound.cpp new file mode 100644 index 0000000..db3323b --- /dev/null +++ b/src/gui/GuiSound.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "GuiSound.h" +#include "../logger.h" + +GuiSound::GuiSound(const char *filepath) { + Load(filepath); +} + +GuiSound::~GuiSound() { + if(music){ + Mix_FreeChunk(music); + } +} + + +bool GuiSound::Load(const char *filepath) { + music = Mix_LoadWAV(filepath); + DEBUG_FUNCTION_LINE("load %s %d", filepath, music); + return music != nullptr; +} + + + +void GuiSound::Play() { + playedOn = Mix_PlayChannel(-1,music, loops); +} + +void GuiSound::Stop() { + Pause(); +} + +void GuiSound::Pause() { + Mix_HaltChannel(playedOn); +} + +void GuiSound::Resume() { + Play(); +} + +bool GuiSound::IsPlaying() { + if(playedOn == -1){ + return false; + } + return Mix_Playing(playedOn); +} + +void GuiSound::SetVolume(uint32_t vol) { + if(music != nullptr){ + Mix_VolumeChunk(music, vol); + } +} + +void GuiSound::SetLoop(bool l) { + if(l){ + loops = -1; + }else{ + loops = 1; + } +} + +void GuiSound::Rewind() { + Stop(); +} diff --git a/src/gui/GuiSound.h b/src/gui/GuiSound.h new file mode 100644 index 0000000..6234d26 --- /dev/null +++ b/src/gui/GuiSound.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include "GuiElement.h" +#include + +//!Sound conversion and playback. A wrapper for other sound libraries - ASND, libmad, ltremor, etc +class GuiSound : public GuiElement { +public: + //!Constructor + //!\param sound Pointer to the sound data + //!\param filesize Length of sound data + GuiSound(const char *filepath); + + //!Destructor + virtual ~GuiSound(); + + //!Load a file and replace the old one + bool Load(const char *filepath); + + //!Start sound playback + void Play(); + + //!Stop sound playback + void Stop(); + + //!Pause sound playback + void Pause(); + + //!Resume sound playback + void Resume(); + + //!Checks if the sound is currently playing + //!\return true if sound is playing, false otherwise + bool IsPlaying(); + + //!Rewind the music + void Rewind(); + + //!Set sound volume + //!\param v Sound volume (0-100) + void SetVolume(uint32_t v); + + //!\param l Loop (true to loop) + void SetLoop(bool l); + + Mix_Chunk *music; + int32_t loops = 0; + int32_t playedOn = -1; +}; \ No newline at end of file diff --git a/src/gui/GuiText.cpp b/src/gui/GuiText.cpp new file mode 100644 index 0000000..14de7d2 --- /dev/null +++ b/src/gui/GuiText.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include +#include "GuiText.h" +#include "../CVideo.h" +#include "../logger.h" + + +/** + * Constructor for the GuiText class. + */ + +GuiText::GuiText(const std::string& text, int32_t s, SDL_Color c, TTF_Font* gFont) { + //Render text surface + textSurface = TTF_RenderText_Solid( gFont, text.c_str(), c ); + if( textSurface == NULL ) { + DEBUG_FUNCTION_LINE( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() ); + } + +} + +GuiText::~GuiText(){ + if(textSurface){ + SDL_FreeSurface( textSurface ); + textSurface = nullptr; + + } + + if(textTexture){ + SDL_DestroyTexture( textTexture ); + textTexture = nullptr; + } +} + +/** + * Draw the text on screen + */ +void GuiText::draw(CVideo *pVideo) { + if (!this->isVisible()) { + return; + } + + if(textTexture == NULL){ + //Create texture from surface pixels + textTexture = SDL_CreateTextureFromSurface( pVideo->getRenderer(), textSurface ); + if( textTexture == NULL ) { + DEBUG_FUNCTION_LINE( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() ); + }else{ + width = textSurface->w; + height = textSurface->h; + } + + if(textSurface){ + SDL_FreeSurface( textSurface ); + textSurface = nullptr; + } + } + + if(!textTexture){ + return; + } + + SDL_Rect rect; + rect.x = getLeft(); + rect.y = getTop(); + rect.w = getScaleX() * getWidth(); + rect.h = getScaleY() * getHeight(); + + // copy the texture to the rendering context + if(getAngle() == 0){ + SDL_RenderCopy(pVideo->getRenderer(), textTexture, NULL, &rect); + }else{ + SDL_RenderCopyEx(pVideo->getRenderer(), textTexture, NULL, &rect, getAngle(), NULL, SDL_FLIP_NONE); + } +} \ No newline at end of file diff --git a/src/gui/GuiText.h b/src/gui/GuiText.h new file mode 100644 index 0000000..d726c59 --- /dev/null +++ b/src/gui/GuiText.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include "GuiElement.h" +#include +#include +#include + + +//!Display, manage, and manipulate text in the GUI +class GuiText : public GuiElement { +public: + + //!\param t Text + //!\param s Font size + //!\param c Font color + GuiText(const std::string &t, int32_t s, SDL_Color c, TTF_Font *gFont); + virtual ~GuiText(); + + virtual void draw(CVideo *pVideo); + +protected: + SDL_Surface *textSurface = nullptr; + SDL_Texture *textTexture = nullptr; +}; diff --git a/src/gui/GuiTrigger.cpp b/src/gui/GuiTrigger.cpp new file mode 100644 index 0000000..3b6a9a8 --- /dev/null +++ b/src/gui/GuiTrigger.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "GuiElement.h" +#include "GuiController.h" +#include "GuiTrigger.h" + +/** + * Constructor for the GuiTrigger class. + */ +GuiTrigger::GuiTrigger() + : chan(CHANNEL_ALL), btns(BUTTON_NONE), bClickEverywhere(false), bHoldEverywhere(false), bSelectionClickEverywhere(false), bLastTouched(false) { +} + +GuiTrigger::GuiTrigger(uint32_t ch, uint32_t btn, bool clickEverywhere, bool holdEverywhere, bool selectionClickEverywhere) + : chan(ch), btns(btn), bClickEverywhere(clickEverywhere), bHoldEverywhere(holdEverywhere), bSelectionClickEverywhere(selectionClickEverywhere), bLastTouched(false) { +} + +/** + * Destructor for the GuiTrigger class. + */ +GuiTrigger::~GuiTrigger() { +} + +/** + * Sets a simple trigger. Requires: + * - Element is selected + * - Trigger button is pressed + */ +void GuiTrigger::setTrigger(uint32_t ch, uint32_t btn) { + chan = ch; + btns = btn; +} + +bool GuiTrigger::left(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + if ((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_LEFT | STICK_L_LEFT)) { + return true; + } + return false; +} + +bool GuiTrigger::right(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + if ((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_RIGHT | STICK_L_RIGHT)) { + return true; + } + return false; +} + +bool GuiTrigger::up(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + if ((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_UP | STICK_L_UP)) { + return true; + } + return false; +} + +bool GuiTrigger::down(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + if ((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_DOWN | STICK_L_DOWN)) { + return true; + } + return false; +} + +int32_t GuiTrigger::clicked(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return CLICKED_NONE; + } + + int32_t bResult = CLICKED_NONE; + + if (controller->data.touched && controller->data.validPointer && (btns & TOUCHED) && !controller->lastData.touched) { + bResult = CLICKED_TOUCH; + } + + if (controller->data.buttons_d & btns) { + bResult = CLICKED_BUTTON; + } + return bResult; +} + +bool GuiTrigger::held(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + + bool bResult = false; + + if (controller->data.touched && (btns & TOUCHED) && controller->data.validPointer && controller->lastData.touched && controller->lastData.validPointer) { + bResult = true; + } + + if (controller->data.buttons_h & btns) { + bResult = true; + } + + return bResult; +} + +bool GuiTrigger::released(const GuiController *controller) const { + if ((controller->chan & chan) == 0) { + return false; + } + + if (clicked(controller) || held(controller)) { + return false; + } + + bool bResult = false; + + if (!controller->data.touched && (btns & TOUCHED) && controller->lastData.touched && controller->lastData.validPointer) { + bResult = true; + } + + if (controller->data.buttons_r & btns) { + bResult = true; + } + + return bResult; +} + diff --git a/src/gui/GuiTrigger.h b/src/gui/GuiTrigger.h new file mode 100644 index 0000000..a09adb4 --- /dev/null +++ b/src/gui/GuiTrigger.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include + +class GuiController; + +//!Menu input trigger management. Determine if action is neccessary based on input data by comparing controller input data to a specific trigger element. +class GuiTrigger { +public: + enum eClicked { + CLICKED_NONE = 0x00, + CLICKED_TOUCH = 0x01, + CLICKED_BUTTON = 0x02, + }; + enum eChannels { + CHANNEL_1 = 0x01, + CHANNEL_2 = 0x02, + CHANNEL_3 = 0x04, + CHANNEL_4 = 0x08, + CHANNEL_5 = 0x10, + CHANNEL_ALL = 0xFF + }; + enum eButtons { + BUTTON_NONE = 0x0000, + TOUCHED = 0x80000000, + VPAD_TOUCH = TOUCHED, + BUTTON_Z = 0x20000, + BUTTON_C = 0x10000, + BUTTON_A = 0x8000, + BUTTON_B = 0x4000, + BUTTON_X = 0x2000, + BUTTON_Y = 0x1000, + BUTTON_1 = BUTTON_Y, + BUTTON_2 = BUTTON_X, + BUTTON_LEFT = 0x0800, + BUTTON_RIGHT = 0x0400, + BUTTON_UP = 0x0200, + BUTTON_DOWN = 0x0100, + BUTTON_ZL = 0x0080, + BUTTON_ZR = 0x0040, + BUTTON_L = 0x0020, + BUTTON_R = 0x0010, + BUTTON_PLUS = 0x0008, + BUTTON_MINUS = 0x0004, + BUTTON_HOME = 0x0002, + BUTTON_SYNC = 0x0001, + STICK_R_LEFT = 0x04000000, + STICK_R_RIGHT = 0x02000000, + STICK_R_UP = 0x01000000, + STICK_R_DOWN = 0x00800000, + STICK_L_LEFT = 0x40000000, + STICK_L_RIGHT = 0x20000000, + STICK_L_UP = 0x10000000, + STICK_L_DOWN = 0x08000000, + BUTTON_STICK_L = BUTTON_Z, + BUTTON_STICK_R = BUTTON_C, + }; + + //!Constructor + GuiTrigger(); + + //!Constructor + GuiTrigger(uint32_t ch, uint32_t btns, bool clickEverywhere = false, bool holdEverywhere = false, bool selectionClickEverywhere = false); + + //!Destructor + virtual ~GuiTrigger(); + + //!Sets a simple trigger. Requires: element is selected, and trigger button is pressed + void setTrigger(uint32_t ch, uint32_t btns); + + void setClickEverywhere(bool b) { + bClickEverywhere = b; + } + + void setHoldOnly(bool b) { + bHoldEverywhere = b; + } + + void setSelectionClickEverywhere(bool b) { + bSelectionClickEverywhere = b; + } + + bool isClickEverywhere() const { + return bClickEverywhere; + } + + bool isHoldEverywhere() const { + return bHoldEverywhere; + } + + bool isSelectionClickEverywhere() const { + return bSelectionClickEverywhere; + } + + bool left(const GuiController *controller) const; + + bool right(const GuiController *controller) const; + + bool up(const GuiController *controller) const; + + bool down(const GuiController *controller) const; + + int32_t clicked(const GuiController *controller) const; + + bool held(const GuiController *controller) const; + + bool released(const GuiController *controller) const; + +private: + uint32_t chan; + uint32_t btns; + bool bClickEverywhere; + bool bHoldEverywhere; + bool bSelectionClickEverywhere; + bool bLastTouched; +}; diff --git a/src/gui/SDLController.h b/src/gui/SDLController.h new file mode 100644 index 0000000..8c13150 --- /dev/null +++ b/src/gui/SDLController.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include "GuiController.h" +#include "../logger.h" + +#define printButton(chan, x) if(data.buttons_d & x) DEBUG_FUNCTION_LINE("Controller #%d %s", chan, #x) + +class SDLController : public GuiController { +public: + SDLController(int32_t channel) : GuiController(channel) { + + } + + virtual bool update(SDL_Event *e) = 0; + + + virtual void before() { + lastData = data; + + data.buttons_d = 0; + data.buttons_r = 0; + } + + virtual void after() { + data.buttons_d |= (data.buttons_h & (~(lastData.buttons_h))); + data.buttons_r |= ((lastData.buttons_h) & (~data.buttons_h)); + if (data.buttons_h != 0 || data.buttons_d != 0 || data.buttons_r != 0) { + // DEBUG_FUNCTION_LINE("Controller #%d: h %08X d %08X r %08X", chanIdx, data.buttons_h, data.buttons_d, data.buttons_r); + + printButton(chanIdx, GuiTrigger::BUTTON_A); + printButton(chanIdx, GuiTrigger::BUTTON_B); + printButton(chanIdx, GuiTrigger::BUTTON_X); + printButton(chanIdx, GuiTrigger::BUTTON_Y); + printButton(chanIdx, GuiTrigger::BUTTON_STICK_L); + printButton(chanIdx, GuiTrigger::BUTTON_STICK_R); + printButton(chanIdx, GuiTrigger::BUTTON_L); + printButton(chanIdx, GuiTrigger::BUTTON_R); + printButton(chanIdx, GuiTrigger::BUTTON_ZL); + printButton(chanIdx, GuiTrigger::BUTTON_ZR); + printButton(chanIdx, GuiTrigger::BUTTON_PLUS); + printButton(chanIdx, GuiTrigger::BUTTON_MINUS); + printButton(chanIdx, GuiTrigger::BUTTON_LEFT); + printButton(chanIdx, GuiTrigger::BUTTON_UP); + printButton(chanIdx, GuiTrigger::BUTTON_RIGHT); + printButton(chanIdx, GuiTrigger::BUTTON_DOWN); + printButton(chanIdx, GuiTrigger::STICK_L_LEFT); + printButton(chanIdx, GuiTrigger::STICK_L_UP); + printButton(chanIdx, GuiTrigger::STICK_L_RIGHT); + printButton(chanIdx, GuiTrigger::STICK_L_DOWN); + printButton(chanIdx, GuiTrigger::STICK_R_LEFT); + printButton(chanIdx, GuiTrigger::STICK_R_UP); + printButton(chanIdx, GuiTrigger::STICK_R_RIGHT); + printButton(chanIdx, GuiTrigger::STICK_R_DOWN); + printButton(chanIdx, GuiTrigger::TOUCHED); + } + } +}; diff --git a/src/gui/SDLControllerJoystick.h b/src/gui/SDLControllerJoystick.h new file mode 100644 index 0000000..db28bc9 --- /dev/null +++ b/src/gui/SDLControllerJoystick.h @@ -0,0 +1,58 @@ +#pragma once + +class SDLControllerJoystick : public SDLController { +public: + SDLControllerJoystick(int32_t channel, SDL_JoystickID joystickId) : SDLController(channel) { + + } + + virtual bool update(SDL_Event *e) override { + if (e->type == SDL_JOYBUTTONDOWN) { + data.buttons_h |= (1 << e->jbutton.button); + } else if (e->type == SDL_JOYBUTTONUP) { + data.buttons_h &= ~(1 << e->jbutton.button); + } else if (e->type == SDL_JOYHATMOTION) { + auto val = e->jhat.value; + + auto hatMask = (GuiTrigger::BUTTON_LEFT | GuiTrigger::BUTTON_UP | GuiTrigger::BUTTON_DOWN | GuiTrigger::BUTTON_RIGHT); + + // Remove hat values so we can add the new value. + data.buttons_h &= ~hatMask; + + switch (val) { + case SDL_HAT_LEFTUP: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_LEFT: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + break; + case SDL_HAT_LEFTDOWN: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + case SDL_HAT_UP: + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_DOWN: + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + case SDL_HAT_RIGHTUP: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_RIGHT: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + break; + case SDL_HAT_RIGHTDOWN: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + } + } else if (e->type == SDL_JOYAXISMOTION) { + // + } + return true; + } +}; + diff --git a/src/gui/SDLControllerMouse.h b/src/gui/SDLControllerMouse.h new file mode 100644 index 0000000..896b4e8 --- /dev/null +++ b/src/gui/SDLControllerMouse.h @@ -0,0 +1,26 @@ + +#pragma once + +class SDLControllerMouse: public SDLController { +public: + SDLControllerMouse(int32_t channel) : SDLController(channel) { + + } + + virtual bool update(SDL_Event *e) override { + if (e->type == SDL_MOUSEMOTION) { + data.y = e->motion.y; + data.x = e->motion.x; + data.validPointer = true; + } else if (e->type == SDL_MOUSEBUTTONDOWN && e->button.button == SDL_BUTTON_LEFT) { + data.buttons_h |= GuiTrigger::TOUCHED; + } else if (e->type == SDL_MOUSEBUTTONUP && e->button.button == SDL_BUTTON_LEFT) { + data.buttons_h &= ~GuiTrigger::TOUCHED; + }else{ + DEBUG_FUNCTION_LINE("Unknown event"); + return false; + } + return true; + } +}; + diff --git a/src/gui/SDLControllerWiiUGamepad.h b/src/gui/SDLControllerWiiUGamepad.h new file mode 100644 index 0000000..57529c8 --- /dev/null +++ b/src/gui/SDLControllerWiiUGamepad.h @@ -0,0 +1,51 @@ +#pragma once + + +#pragma once + +#include "SDLController.h" + +static GuiTrigger::eButtons vpad_button_map[] = { + GuiTrigger::BUTTON_A, + GuiTrigger::BUTTON_B, + GuiTrigger::BUTTON_X, + GuiTrigger::BUTTON_Y, + GuiTrigger::BUTTON_STICK_L, GuiTrigger::BUTTON_STICK_R, + GuiTrigger::BUTTON_L, GuiTrigger::BUTTON_R, + GuiTrigger::BUTTON_ZL, GuiTrigger::BUTTON_ZR, + GuiTrigger::BUTTON_PLUS, GuiTrigger::BUTTON_MINUS, + GuiTrigger::BUTTON_LEFT, GuiTrigger::BUTTON_UP, GuiTrigger::BUTTON_RIGHT, GuiTrigger::BUTTON_DOWN, + GuiTrigger::STICK_L_LEFT, GuiTrigger::STICK_L_UP, GuiTrigger::STICK_L_RIGHT, GuiTrigger::STICK_L_DOWN, + GuiTrigger::STICK_R_LEFT, GuiTrigger::STICK_R_UP, GuiTrigger::STICK_R_RIGHT, GuiTrigger::STICK_R_DOWN, +}; + +class SDLControllerWiiUGamepad : public SDLController { +public: + SDLControllerWiiUGamepad(int32_t channel) : SDLController(channel) { + + } + + virtual bool update(SDL_Event *e) override { + if (e->type == SDL_FINGERMOTION) { + data.y = e->tfinger.y * 720; + data.x = e->tfinger.x * 1280;; + data.validPointer = true; + } else if (e->type == SDL_FINGERUP) { + data.validPointer = false; + data.buttons_h &= ~GuiTrigger::TOUCHED; + } else if (e->type == SDL_FINGERDOWN) { + data.validPointer = true; + data.buttons_h |= GuiTrigger::TOUCHED; + } + if (e->type == SDL_JOYBUTTONDOWN) { + data.buttons_h |= vpad_button_map[e->jbutton.button]; + } else if (e->type == SDL_JOYBUTTONUP) { + data.buttons_h &= ~vpad_button_map[e->jbutton.button]; + } else { + DEBUG_FUNCTION_LINE("Unknown event"); + return false; + } + return true; + } +}; + diff --git a/src/gui/SDLControllerWiiUProContoller.h b/src/gui/SDLControllerWiiUProContoller.h new file mode 100644 index 0000000..aedbb5d --- /dev/null +++ b/src/gui/SDLControllerWiiUProContoller.h @@ -0,0 +1,11 @@ +#pragma once + +#include "SDLControllerWiiUGamepad.h" + +class SDLControllerWiiUProContoller : public SDLControllerWiiUGamepad { +public: + SDLControllerWiiUProContoller(int32_t channel) : SDLControllerWiiUGamepad(channel){ + + } +}; + diff --git a/src/gui/SDLControllerXboxOne.h b/src/gui/SDLControllerXboxOne.h new file mode 100644 index 0000000..b89011b --- /dev/null +++ b/src/gui/SDLControllerXboxOne.h @@ -0,0 +1,106 @@ +#pragma once + +#include "SDLController.h" + +static GuiTrigger::eButtons xbox_button_map[] = + { + GuiTrigger::BUTTON_A, + GuiTrigger::BUTTON_B, + GuiTrigger::BUTTON_X, + GuiTrigger::BUTTON_Y, + GuiTrigger::BUTTON_L, + GuiTrigger::BUTTON_R, + GuiTrigger::BUTTON_MINUS, + GuiTrigger::BUTTON_PLUS, + GuiTrigger::BUTTON_STICK_L, + GuiTrigger::BUTTON_STICK_R, + }; + +#define getDigitalAxis(axis, targetAxis, value, hold, first, second) \ +if(axis == targetAxis){ \ + if (value < 0x4000 && value > -0x4000){ \ + hold &= ~first; \ + hold &= ~second; \ + }else if(value < -0x4000){ \ + hold |= first; \ + hold &= ~second; \ + }else if(value > 0x4000){ \ + hold |= second; \ + hold &= ~first; \ + } \ +} \ + +#define getDigitalTrigger(axis, targetAxis, value, hold, first) \ +if(axis == targetAxis){ \ + if(value > 0){ \ + hold |= first; \ + }else{ \ + hold &= ~first; \ + } \ +} \ + + +class SDLControllerXboxOne : public SDLController { +public: + SDLControllerXboxOne(int32_t channel) : SDLController(channel) { + + } + + virtual bool update(SDL_Event *e) override { + if (e->type == SDL_JOYBUTTONDOWN) { + data.buttons_h |= xbox_button_map[e->jbutton.button]; + } else if (e->type == SDL_JOYBUTTONUP) { + data.buttons_h &= ~xbox_button_map[e->jbutton.button]; + } else if (e->type == SDL_JOYHATMOTION) { + auto val = e->jhat.value; + + auto hatMask = (GuiTrigger::BUTTON_LEFT | GuiTrigger::BUTTON_UP | GuiTrigger::BUTTON_DOWN | GuiTrigger::BUTTON_RIGHT); + + // Remove hat values so we can add the new values. + data.buttons_h &= ~hatMask; + + switch (val) { + case SDL_HAT_LEFTUP: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_LEFT: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + break; + case SDL_HAT_LEFTDOWN: + data.buttons_h |= GuiTrigger::BUTTON_LEFT; + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + case SDL_HAT_UP: + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_DOWN: + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + case SDL_HAT_RIGHTUP: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + data.buttons_h |= GuiTrigger::BUTTON_UP; + break; + case SDL_HAT_RIGHT: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + break; + case SDL_HAT_RIGHTDOWN: + data.buttons_h |= GuiTrigger::BUTTON_RIGHT; + data.buttons_h |= GuiTrigger::BUTTON_DOWN; + break; + } + } else if (e->type == SDL_JOYAXISMOTION) { + getDigitalTrigger(e->jaxis.axis, 2, e->jaxis.value, data.buttons_h, GuiTrigger::BUTTON_ZL); + getDigitalTrigger(e->jaxis.axis, 5, e->jaxis.value, data.buttons_h, GuiTrigger::BUTTON_ZR); + getDigitalAxis(e->jaxis.axis, 0, e->jaxis.value, data.buttons_h, GuiTrigger::STICK_L_LEFT, GuiTrigger::STICK_L_RIGHT); + getDigitalAxis(e->jaxis.axis, 1, e->jaxis.value, data.buttons_h, GuiTrigger::STICK_L_UP, GuiTrigger::STICK_L_DOWN); + getDigitalAxis(e->jaxis.axis, 3, e->jaxis.value, data.buttons_h, GuiTrigger::STICK_R_LEFT, GuiTrigger::STICK_R_RIGHT); + getDigitalAxis(e->jaxis.axis, 4, e->jaxis.value, data.buttons_h, GuiTrigger::STICK_R_UP, GuiTrigger::STICK_R_DOWN); + } else { + DEBUG_FUNCTION_LINE("Unknown event"); + return false; + } + return true; + } +}; + diff --git a/src/gui/sigslot.h b/src/gui/sigslot.h new file mode 100644 index 0000000..f578c47 --- /dev/null +++ b/src/gui/sigslot.h @@ -0,0 +1,2433 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// +#ifndef SIGSLOT_H__ +#define SIGSLOT_H__ + +#include +#include + +#define _SIGSLOT_SINGLE_THREADED + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + + +namespace sigslot { + + class single_threaded { + public: + single_threaded() { + ; + } + + virtual ~single_threaded() { + ; + } + + virtual void lock() { + ; + } + + virtual void unlock() { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global { + public: + multi_threaded_global() { + static bool isinitialised = false; + + if(!isinitialised) { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) { + ; + } + + virtual ~multi_threaded_global() { + ; + } + + virtual void lock() { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local { + public: + multi_threaded_local() { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global { + public: + multi_threaded_global() { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) { + ; + } + + virtual ~multi_threaded_global() { + ; + } + + virtual void lock() { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local { + public: + multi_threaded_local() { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +#ifdef _SIGSLOT_HAS_LWP_THREADS + + class multi_threaded_global { + public: + multi_threaded_global() { + ; + } + + multi_threaded_global(const multi_threaded_global&) { + ; + } + + virtual ~multi_threaded_global() { + ; + } + + virtual void lock() { + ; + } + + virtual void unlock() { + ; + } + }; + + class multi_threaded_local { + public: + multi_threaded_local() { + ; + } + + multi_threaded_local(const multi_threaded_local&) { + ; + } + + virtual ~multi_threaded_local() { + } + + virtual void lock() { + ; + } + + virtual void unlock() { + ; + } + }; + +#endif // _SIGSLOT_HAS_LWP_THREADS + + template + class lock_block { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) { + m_mutex->lock(); + } + + ~lock_block() { + m_mutex->unlock(); + } + }; + + template + class has_slots; + + template + class _connection_base0 { + public: + virtual ~_connection_base0() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit() = 0; + + virtual _connection_base0 *clone() = 0; + + virtual _connection_base0 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base1 { + public: + virtual ~_connection_base1() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type) = 0; + + virtual _connection_base1 *clone() = 0; + + virtual _connection_base1 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base2 { + public: + virtual ~_connection_base2() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type) = 0; + + virtual _connection_base2 *clone() = 0; + + virtual _connection_base2 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base3 { + public: + virtual ~_connection_base3() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + + virtual _connection_base3 *clone() = 0; + + virtual _connection_base3 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base4 { + public: + virtual ~_connection_base4() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + + virtual _connection_base4 *clone() = 0; + + virtual _connection_base4 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base5 { + public: + virtual ~_connection_base5() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + + virtual _connection_base5 *clone() = 0; + + virtual _connection_base5 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base6 { + public: + virtual ~_connection_base6() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + + virtual _connection_base6 *clone() = 0; + + virtual _connection_base6 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base7 { + public: + virtual ~_connection_base7() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + + virtual _connection_base7 *clone() = 0; + + virtual _connection_base7 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _connection_base8 { + public: + virtual ~_connection_base8() { + ; + } + + virtual has_slots *getdest() const = 0; + + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + + virtual _connection_base8 *clone() = 0; + + virtual _connection_base8 *duplicate(has_slots *pnewdest) = 0; + }; + + template + class _signal_base : public mt_policy { + public: + virtual void slot_disconnect(has_slots *pslot) = 0; + + virtual void slot_duplicate(const has_slots *poldslot, has_slots *pnewslot) = 0; + }; + + template + class has_slots : public mt_policy { + private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() { + ; + } + + has_slots(const has_slots &hs) + : mt_policy(hs) { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while (it != itEnd) { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base *sender) { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base *sender) { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while (it != itEnd) { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template + class _signal_base0 : public _signal_base { + public: + typedef typename std::list<_connection_base0 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base0() { + ; + } + + _signal_base0(const _signal_base0 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base1 : public _signal_base { + public: + typedef typename std::list<_connection_base1 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base1() { + ; + } + + _signal_base1(const _signal_base1 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base2 : public _signal_base { + public: + typedef typename std::list<_connection_base2 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base2() { + ; + } + + _signal_base2(const _signal_base2 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base3 : public _signal_base { + public: + typedef std::list<_connection_base3 *> + connections_list; + + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base3() { + ; + } + + _signal_base3(const _signal_base3 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base4 : public _signal_base { + public: + typedef std::list<_connection_base4 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base4() { + ; + } + + _signal_base4(const _signal_base4 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + this->m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base5 : public _signal_base { + public: + typedef std::list<_connection_base5 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base5() { + ; + } + + _signal_base5(const _signal_base5 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base6 : public _signal_base { + public: + typedef std::list<_connection_base6 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base6() { + ; + } + + _signal_base6(const _signal_base6 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base7 : public _signal_base { + public: + typedef std::list<_connection_base7 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base7() { + ; + } + + _signal_base7(const _signal_base7 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base8 : public _signal_base { + public: + typedef std::list<_connection_base8 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base8() { + ; + } + + _signal_base8(const _signal_base8 &s) + : _signal_base(s) { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots *oldtarget, has_slots *newtarget) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == oldtarget) { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() { + disconnect_all(); + } + + void disconnect_all() { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots *pclass) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if ((*it)->getdest() == pclass) { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots *pslot) { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + iterator itNext = it; + ++itNext; + + if ((*it)->getdest() == pslot) { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template + class _connection0 : public _connection_base0 { + public: + _connection0() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection0(dest_type *pobject, void (dest_type::*pmemfun)()) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection0() { + ; + } + + virtual _connection_base0 *clone() { + return new _connection0(*this); + } + + virtual _connection_base0 *duplicate(has_slots *pnewdest) { + return new _connection0((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit() { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(); + }; + + template + class _connection1 : public _connection_base1 { + public: + _connection1() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection1(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() { + ; + } + + virtual _connection_base1 *clone() { + return new _connection1(*this); + } + + virtual _connection_base1 *duplicate(has_slots *pnewdest) { + return new _connection1((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template + class _connection2 : public _connection_base2 { + public: + _connection2() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection2(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() { + ; + } + + + virtual _connection_base2 *clone() { + return new _connection2(*this); + } + + virtual _connection_base2 *duplicate(has_slots *pnewdest) { + return new _connection2((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template + class _connection3 : public _connection_base3 { + public: + _connection3() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection3(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() { + ; + } + + + virtual _connection_base3 *clone() { + return new _connection3(*this); + } + + virtual _connection_base3 *duplicate(has_slots *pnewdest) { + return new _connection3((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template + class _connection4 : public _connection_base4 { + public: + _connection4() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection4(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() { + ; + } + + virtual _connection_base4 *clone() { + return new _connection4(*this); + } + + virtual _connection_base4 *duplicate(has_slots *pnewdest) { + return new _connection4((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template + class _connection5 : public _connection_base5 { + public: + _connection5() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection5(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() { + ; + } + + virtual _connection_base5 *clone() { + return new _connection5(*this); + } + + virtual _connection_base5 *duplicate(has_slots *pnewdest) { + return new _connection5((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template + class _connection6 : public _connection_base6 { + public: + _connection6() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection6(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() { + ; + } + + virtual _connection_base6 *clone() { + return new _connection6(*this); + } + + virtual _connection_base6 *duplicate(has_slots *pnewdest) { + return new _connection6((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template + class _connection7 : public _connection_base7 { + public: + _connection7() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection7(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() { + ; + } + + virtual _connection_base7 *clone() { + return new _connection7(*this); + } + + virtual _connection_base7 *duplicate(has_slots *pnewdest) { + return new _connection7((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template + class _connection8 : public _connection_base8 { + public: + _connection8() { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection8(dest_type *pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() { + ; + } + + virtual _connection_base8 *clone() { + return new _connection8(*this); + } + + virtual _connection_base8 *duplicate(has_slots *pnewdest) { + return new _connection8((dest_type *) pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots *getdest() const { + return m_pobject; + } + + private: + dest_type *m_pobject; + + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template + class signal0 : public _signal_base0 { + public: + typedef typename _signal_base0::connections_list::const_iterator const_iterator; + + signal0() { + ; + } + + signal0(const signal0 &s) + : _signal_base0(s) { + ; + } + + virtual ~signal0() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)()) { + lock_block lock(this); + _connection0 *conn = + new _connection0(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template + class signal1 : public _signal_base1 { + public: + typedef typename _signal_base1::connections_list::const_iterator const_iterator; + + signal1() { + ; + } + + signal1(const signal1 &s) + : _signal_base1(s) { + ; + } + + virtual ~signal1() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type)) { + lock_block lock(this); + _connection1 *conn = + new _connection1(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template + class signal2 : public _signal_base2 { + public: + typedef typename _signal_base2::connections_list::const_iterator const_iterator; + + signal2() { + ; + } + + signal2(const signal2 &s) + : _signal_base2(s) { + ; + } + + virtual ~signal2() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) { + lock_block lock(this); + _connection2 *conn = new + _connection2(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template + class signal3 : public _signal_base3 { + public: + typedef typename _signal_base3::connections_list::const_iterator const_iterator; + + signal3() { + ; + } + + signal3(const signal3 &s) + : _signal_base3(s) { + ; + } + + virtual ~signal3() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) { + lock_block lock(this); + _connection3 *conn = + new _connection3(pclass, + pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template + class signal4 : public _signal_base4 { + public: + typedef typename _signal_base4::connections_list::const_iterator const_iterator; + + signal4() { + ; + } + + signal4(const signal4 &s) + : _signal_base4(s) { + ; + } + + virtual ~signal4() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) { + lock_block lock(this); + _connection4 * + conn = new _connection4(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template + class signal5 : public _signal_base5 { + public: + typedef typename _signal_base5::connections_list::const_iterator const_iterator; + + signal5() { + ; + } + + signal5(const signal5 &s) + : _signal_base5(s) { + ; + } + + virtual ~signal5() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) { + lock_block lock(this); + _connection5 *conn = new _connection5(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template + class signal6 : public _signal_base6 { + public: + typedef typename _signal_base6::connections_list::const_iterator const_iterator; + + signal6() { + ; + } + + signal6(const signal6 &s) + : _signal_base6(s) { + ; + } + + virtual ~signal6() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) { + lock_block lock(this); + _connection6 *conn = + new _connection6(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template + class signal7 : public _signal_base7 { + public: + typedef typename _signal_base7::connections_list::const_iterator const_iterator; + + signal7() { + ; + } + + signal7(const signal7 &s) + : _signal_base7(s) { + ; + } + + virtual ~signal7() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) { + lock_block lock(this); + _connection7 *conn = + new _connection7(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template + class signal8 : public _signal_base8 { + public: + typedef typename _signal_base8::connections_list::const_iterator const_iterator; + + signal8() { + ; + } + + signal8(const signal8 &s) + : _signal_base8(s) { + ; + } + + virtual ~signal8() { + ; + } + + template + void connect(desttype *pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) { + lock_block lock(this); + _connection8 *conn = + new _connection8(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while (it != itEnd) { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // SIGSLOT_H__ diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..3dce1e5 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __WIIU__ +#include +#include +#include + +#include + +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#ifdef __WIIU__ + #define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) +#else + #define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + printf("[%23s]%30s@L%04d: " FMT "\n",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) +#endif +#ifdef __cplusplus +} +#endif + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e64e57a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,237 @@ +#include +#include "CVideo.h" +#include "gui/GuiFrame.h" +#include "gui/GuiImage.h" +#include "gui/GuiButton.h" +#include "gui/GuiController.h" +#include "gui/SDLController.h" +#include "MainWindow.h" +#include "logger.h" +#include "gui/SDLControllerJoystick.h" +#include "gui/SDLControllerMouse.h" +#include "gui/SDLControllerWiiUGamepad.h" +#include "gui/SDLControllerXboxOne.h" +#include "gui/SDLControllerWiiUProContoller.h" + +#include +#include + +#include + +#if defined _WIN32 +#include +#endif + +#ifdef __WIIU__ +#include +#include +#include +#include +bool CheckRunning(){ + switch(ProcUIProcessMessages(true)) + { + case PROCUI_STATUS_EXITING: + { + return false; + } + case PROCUI_STATUS_RELEASE_FOREGROUND: + { + ProcUIDrawDoneRelease(); + break; + } + case PROCUI_STATUS_IN_FOREGROUND: + { + break; + } + case PROCUI_STATUS_IN_BACKGROUND: + default: + break; + } + return true; +} +#endif + +void proccessEvents(); + +bool addJoystick(int deviceId, std::map &controllerList, std::map& map); + +GuiTrigger::eChannels increaseChannel(GuiTrigger::eChannels channel); + +void removeJoystick(int32_t which, std::map &controllerList, std::map& joystickToChannel); + +int main(int argc, char *args[]) { + CVideo *video = new CVideo(); + +#if defined _WIN32 + // Create the Console + AllocConsole(); + + // Create Console Output Handle + HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE); + int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT); + FILE *hf_out = _fdopen(hCrt, "w"); + setvbuf(hf_out, NULL, _IONBF, 1); + *stdout = *hf_out; + + // Create Console Input Handle + HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); + hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT); + FILE *hf_in = _fdopen(hCrt, "r"); + setvbuf(hf_in, NULL, _IONBF, 128); + + *stdin = *hf_in; +#elif __WIIU__ + WHBLogUdpInit(); +#endif + + GuiFrame *frame = new MainWindow(video->getWidth(), video->getHeight()); + + std::map controllerList; + std::map joystickToChannel; + +#if defined _WIN32 + controllerList[GuiTrigger::CHANNEL_1] = new SDLControllerMouse(GuiTrigger::CHANNEL_1); + DEBUG_FUNCTION_LINE("Add mouse"); +#endif + + while (true) { +#ifdef __WIIU__ + if(!CheckRunning()){ + exit(0); + break; + } +#endif + + //! Read out inputs + for( auto const& [channel, controller] : controllerList ){ + controller->before(); + } + + bool quit = false; + SDL_Event e; + while (SDL_PollEvent(&e)) { + SDL_JoystickID jId = -1; + if(e.type == SDL_JOYDEVICEADDED) { + addJoystick(e.jdevice.which, controllerList, joystickToChannel); + }else if(e.type == SDL_JOYDEVICEREMOVED) { + auto j = SDL_JoystickFromInstanceID(e.jdevice.which); + if (j) { + removeJoystick(e.jdevice.which, controllerList, joystickToChannel); + SDL_JoystickClose(j); + } + }else if (e.type == SDL_FINGERDOWN || e.type == SDL_FINGERUP || e.type == SDL_FINGERMOTION){ + controllerList[GuiTrigger::CHANNEL_1]->update(&e); + } else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP || e.type == SDL_MOUSEMOTION){ + controllerList[GuiTrigger::CHANNEL_1]->update(&e); + } else if (e.type == SDL_JOYAXISMOTION) { + jId = e.jaxis.which; + } else if (e.type == SDL_JOYHATMOTION) { + jId = e.jhat.which; + }else if (e.type == SDL_JOYBUTTONDOWN || e.type == SDL_JOYBUTTONUP) { + jId = e.jbutton.which; + } else if (e.type == SDL_QUIT || (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)) { + quit = true; + break; + } + + if(jId != -1){ + if(joystickToChannel.find(jId) != joystickToChannel.end()){ + controllerList[joystickToChannel[jId]]->update(&e); + } + } + } + if(quit){ + break; + } + + for( auto const& [joypad, controller] : controllerList ){ + controller->after(); + + frame->update(controller); + } + + frame->process(); + + // clear the screen + SDL_RenderClear(video->getRenderer()); + + frame->draw(video); + + frame->updateEffects(); + + // flip the backbuffer + // this means that everything that we prepared behind the screens is actually shown + SDL_RenderPresent(video->getRenderer()); + + } + + delete frame; + + return 0; +} + +void removeJoystick(int32_t instanceId, std::map &controllerList, std::map& joystickToChannel) { + auto channel = joystickToChannel[instanceId]; + delete controllerList[channel]; + controllerList.erase(channel); + joystickToChannel.erase(instanceId); + DEBUG_FUNCTION_LINE("Removed joystick: %d", instanceId); +} + +bool addJoystick(int deviceId, std::map &controllerList, std::map& joystickToChannel) { + auto joystick = SDL_JoystickOpen(deviceId); + if (joystick == NULL){ + DEBUG_FUNCTION_LINE("SDL_JoystickOpen failed: %s\n", SDL_GetError()); + return false; + } + auto instanceId = SDL_JoystickInstanceID(joystick); + if(std::string("WiiU Gamepad").compare(SDL_JoystickName(joystick)) == 0){ + controllerList[GuiTrigger::CHANNEL_1] = new SDLControllerWiiUGamepad(GuiTrigger::CHANNEL_1); + joystickToChannel[instanceId] = GuiTrigger::CHANNEL_1; + }else { + bool successfully_added = false; + auto channel = GuiTrigger::CHANNEL_2; + while(channel != GuiTrigger::CHANNEL_ALL){ + if(controllerList.find(channel) == controllerList.end()) { + if (std::string(SDL_JoystickName(joystick)).find("Xbox") != std::string::npos){ + controllerList[channel] = new SDLControllerXboxOne(channel); + }else if(std::string(SDL_JoystickName(joystick)).find("WiiU Pro Controller") != std::string::npos) { + controllerList[channel] = new SDLControllerWiiUProContoller(channel); + }else{ + controllerList[channel] = new SDLControllerJoystick(channel, instanceId); + } + joystickToChannel[instanceId] = channel; + successfully_added = true; + break; + } + channel = increaseChannel(channel); + } + if(!successfully_added){ + DEBUG_FUNCTION_LINE("Failed to add joystick. Closing it now"); + SDL_JoystickClose(joystick); + return false; + } + } + DEBUG_FUNCTION_LINE("Added joystick %s", SDL_JoystickName(joystick)); + return true; +} + +GuiTrigger::eChannels increaseChannel(GuiTrigger::eChannels channel) { + switch(channel){ + case GuiTrigger::CHANNEL_1: + return GuiTrigger::CHANNEL_2; + case GuiTrigger::CHANNEL_2: + return GuiTrigger::CHANNEL_3; + case GuiTrigger::CHANNEL_3: + return GuiTrigger::CHANNEL_4; + case GuiTrigger::CHANNEL_4: + return GuiTrigger::CHANNEL_5; + } + return GuiTrigger::CHANNEL_ALL; +} + +void proccessEvents() { + + int res = 0; + +}