/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli co-programmer: */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_EVENT #include "event_handler.h" #include "event_listener.h" #include "event.h" #include "key_mapper.h" #include "key_names.h" #include "compiler.h" #include "debug.h" #include /** * @brief standard constructor */ EventHandler::EventHandler () { this->setClassID(CL_EVENT_HANDLER, "EventHandler"); this->setName("EventHandler"); SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_InitSubSystem(SDL_INIT_EVENTTHREAD); SDL_SetEventFilter(EventHandler::eventFilter); /* now initialize them all to zero */ for (unsigned int i = 0; i < ES_NUMBER; i++) this->bUNICODE[i] = false; this->grabEvents(false); this->state = ES_GAME; this->eventsGrabbed = false; this->keyState = SDL_GetKeyState(NULL); } /** * @brief the singleton reference to this class */ EventHandler* EventHandler::singletonRef = NULL; /** * @brief standard deconstructor */ EventHandler::~EventHandler () { bool forgotToUnsubscribe = false; for(int i = 0; i < ES_NUMBER; ++i) { for(int j = 0; j < EV_NUMBER; ++j) { if(!this->listeners[i][j].empty()) { if (!forgotToUnsubscribe) { forgotToUnsubscribe = true; PRINTF(2)("forgot to unsubscribe an EventListener!\n");// %s!\n", this->listeners[i][j]->getName()); } } } } if (forgotToUnsubscribe) { PRINTF(2)("Listing still subscribed EventListeners\n"); PRINTF(2)("========================================\n"); this->debug(); PRINTF(2)("========================================\n"); } SDL_QuitSubSystem(SDL_INIT_JOYSTICK); EventHandler::singletonRef = NULL; } /** * @brief initializes the event handler * * this has to be called before the use of the event handler */ void EventHandler::init() { this->keyMapper.loadKeyBindings(); } /** * @param state: to which the event handler shall change */ void EventHandler::setState(elState state) { if (state == this->state) return; PRINTF(4)("Switching to State %s\n", EventHandler::ELStateToString(state).c_str()); /// When Changing the State, all the keys will be released. /// This is done by sending each eventListener, that still /// has an Event subscribed, a Release Event. int keyCount; Uint8 * pressedKeys = SDL_GetKeyState(&keyCount); for (unsigned int i = 0; i < SDLK_LAST; i++) { if (pressedKeys[i]) { Event ev; ev.bPressed = false; ev.type = i; if (unlikely(this->bUNICODE[this->state])) ev.x = i; this->dispachEvent(this->state, ev ); } } // switching to the new State. elState oldState = this->state; this->state = state; // in the End the Corresponding handler will be notified. Event stateSwitchEvent; stateSwitchEvent.type = EV_LEAVE_STATE; this->dispachEvent(oldState, stateSwitchEvent); SDL_EnableUNICODE(this->bUNICODE[state]); }; /** * @brief pushes the current State in the State-stack, and selects state * @param state the new State to set */ void EventHandler::pushState(elState state) { if (likely(state != ES_NULL && state != ES_ALL )) { this->stateStack.push(this->state); this->setState(state); } else { PRINTF(2)("unable to push State\n"); } } /** * @brief this removes the topmost stack-entry and select the underlying one * @returns the next stack-entry */ elState EventHandler::popState() { if (stateStack.empty()) return ES_NULL; elState state = (elState)stateStack.top(); this->stateStack.pop(); if (state == ES_NULL) { PRINTF(2)("No more states availiable. (unable to pop state)\n"); return ES_NULL; } else { this->setState(state); return state; } } /** * @brief subscribe to an event * @param el: the event listener that wants to subscribe itself, the listener that will be called when the evetn occures * @param state: for which the listener wants to receive events * @param eventType: the event type that wants to be listened for. This is one of the most important function of the EventHandler. If you would like to subscribe for more than one state, you have to subscribe for each state again. If you want to subscribe for all states, use state = ES_ALL, which will subscribe your listener for all states together. */ void EventHandler::subscribe(EventListener* el, elState state, int eventType) { PRINTF(4)("Subscribing event type: %i\n", eventType); if( state == ES_ALL ) { for(unsigned int i = 0; i < ES_NUMBER; i++) if (!this->findListener( NULL, (elState)i, eventType, el)) this->listeners[i][eventType].push_back(el); else { PRINTF(2)("%s::%s was already subscribed to state %d event %d\n", el->getClassCName(), el->getCName(), i, eventType); } } else { if (!this->findListener( NULL, state, eventType, el)) this->listeners[state][eventType].push_back(el); else { PRINTF(2)("%s::%s was already subscribed to state %d event %d\n", el->getClassCName(), el->getCName(), state, eventType); } } } /** * @brief unsubscribe from the EventHandler * @param state: the stat in which it has been subscribed * @param eventType: the event, that shall be unsubscribed * * if you want to unsubscribe an event listener from all subscribed events, just use the * unsubscribe(EventListener* el, elState state) function */ void EventHandler::unsubscribe(EventListener* el, elState state, int eventType) { PRINTF(4)("Unsubscribing event type nr: %i\n", eventType); if (state == ES_ALL) for (unsigned int i = 0; i < ES_NUMBER; i++) { std::vector::iterator listener; if (this->findListener(&listener, (elState)i, eventType, el)) this->listeners[i][eventType].erase(listener); } else { std::vector::iterator listener; if (this->findListener(&listener, state, eventType, el)) this->listeners[state][eventType].erase(listener); } } /** * @brief unsubscribe all events from a specific listener * @param el: the listener that wants to unsubscribe itself * @param state: the state in which the events shall be unsubscribed */ void EventHandler::unsubscribe(EventListener* el, elState state) { assert( el != NULL && state < ES_NUMBER); if( state == ES_ALL) { for(unsigned int i = 0; i < ES_NUMBER; i++) { for(unsigned int j = 0; j < EV_NUMBER; j++) { std::vector::iterator deller = std::find (this->listeners[i][j].begin(), this->listeners[i][j].end(), el); if( deller != this->listeners[i][j].end()) this->listeners[i][j].erase(deller); } } } else { for(int j = 0; j < EV_NUMBER; j++) { std::vector::iterator deller = std::find (this->listeners[state][j].begin(), this->listeners[state][j].end(), el); if( deller != this->listeners[state][j].end()) this->listeners[state][j].erase(deller); } } } /** * @brief returns true if at state and eventType there is something subscribed. * @param state the state to check in. * @param eventType the eventtype to check. * @returns true if a event is subscibed. */ bool EventHandler::isSubscribed(elState state, int eventType) { return(listeners[state][eventType].empty()) ? false : true; }; /** * @brief flush all registered events * @param state: a specific state */ void EventHandler::flush(elState state) { if( state == ES_ALL) { for(int i = 0; i < ES_NUMBER; ++i) { for(int j = 0; j < EV_NUMBER; ++j) { this->listeners[i][j].clear(); } } } else { for(int j = 0; j < EV_NUMBER; ++j) { this->listeners[state][j].clear(); } } } bool EventHandler::findListener(std::vector::iterator* it, elState state, int eventType, EventListener* listener) { std::vector::iterator findIterator = std::find(this->listeners[state][eventType].begin(), this->listeners[state][eventType].end(), listener); if (it != NULL) *it = findIterator; return ( findIterator != this->listeners[state][eventType].end()); } /** * @brief if the unicode characters should be recorded. * @param state the State in whitch to set the new Value. * @param enableUNICODE: enabled, or disabled. */ void EventHandler::withUNICODE(elState state, bool enableUNICODE) { this->bUNICODE[state] = enableUNICODE; if (this->state == state) SDL_EnableUNICODE(enableUNICODE); } /** * @brief grabs InputEvents. * @param grabEvents if the Events should be grabbed(true) or released(false) */ void EventHandler::grabEvents(bool grabEvents) { this->eventsGrabbed = grabEvents; if(!grabEvents) { SDL_ShowCursor(SDL_ENABLE); SDL_WM_GrabInput(SDL_GRAB_OFF); } else { SDL_WM_GrabInput(SDL_GRAB_ON); SDL_ShowCursor(SDL_DISABLE); } } /** * @brief core function of event handler: receives all events from SDL * * The event from the SDL framework are collected here and distributed to all listeners. */ void EventHandler::process() const { SDL_Event event; Event ev; while( SDL_PollEvent (&event)) { switch( event.type) { case SDL_KEYDOWN: ev.bPressed = true; ev.type = event.key.keysym.sym; if (unlikely(this->bUNICODE[this->state])) ev.x = event.key.keysym.unicode; break; case SDL_KEYUP: ev.bPressed = false; ev.type = event.key.keysym.sym; if (unlikely(this->bUNICODE[this->state])) ev.x = event.key.keysym.unicode; break; case SDL_MOUSEMOTION: ev.bPressed = false; ev.type = EV_MOUSE_MOTION; ev.x = event.motion.x; ev.y = event.motion.y; ev.xRel = event.motion.xrel; ev.yRel = event.motion.yrel; break; case SDL_MOUSEBUTTONUP: ev.bPressed = false; ev.x = event.motion.x; ev.y = event.motion.y; ev.type = event.button.button + SDLK_LAST; break; case SDL_MOUSEBUTTONDOWN: ev.bPressed = true; ev.x = event.motion.x; ev.y = event.motion.y; ev.type = event.button.button + SDLK_LAST; break; case SDL_JOYAXISMOTION: ev.bPressed = false; ev.type = EV_JOY_AXIS_MOTION; break; case SDL_JOYBALLMOTION: ev.bPressed = false; ev.type = EV_JOY_BALL_MOTION; break; case SDL_JOYHATMOTION: ev.bPressed = false; ev.type = EV_JOY_HAT_MOTION; break; case SDL_JOYBUTTONDOWN: ev.bPressed = true; ev.type = EV_JOY_BUTTON; break; case SDL_JOYBUTTONUP: ev.bPressed = true; ev.type = EV_JOY_BUTTON; break; case SDL_ACTIVEEVENT: ev.type = EV_WINDOW_FOCUS; ev.bPressed = (event.active.gain != 0); break; case SDL_VIDEORESIZE: ev.resize = event.resize; ev.type = EV_VIDEO_RESIZE; break; case SDL_QUIT: ev.type = EV_MAIN_QUIT; break; default: ev.type = EV_UNKNOWN; break; } this->dispachEvent(this->state, ev); } } /** * @brief dispaches an Event. * @param event the Event to dispach. */ void EventHandler::dispachEvent(elState state, const Event& event) const { /* small debug routine: shows all events dispatched by the event handler */ PRINT(4)("\n==========================| EventHandler::process () |===\n"); PRINT(4)("= Got Event nr %i, for state %i\n", event.type, state); /// setting a temporary state in case of an EventListener's process changes the state. for (unsigned int i = 0; i < this->listeners[state][event.type].size(); i++) { PRINT(4)("= Event dispatcher msg: This event has been consumed\n"); PRINT(4)("= Got Event nr %i, for state %i (%d registered) to %s::%s(%p)\n", event.type, i, state, this->listeners[state][event.type][i]->getClassCName(), this->listeners[state][event.type][i]->getCName(), this->listeners[state][event.type][i]); PRINT(4)("=======================================================\n"); this->listeners[state][event.type][i]->process(event); } /* else { PRINT(4)("= Event dispatcher msg: This event has NOT been consumed\n"); PRINT(4)("=======================================================\n"); }*/ } /** * @brief An eventFilter. * @param event the Event to be filtered. * @returns 0 on filtered Event. 1 Otherwise. */ int EventHandler::eventFilter(const SDL_Event *event) { if (likely(EventHandler::getInstance()->eventsGrabbed)) { if (event->type == SDL_KEYDOWN && event->key.keysym.sym == SDLK_TAB && SDL_GetKeyState(NULL)[SDLK_LALT]) { PRINTF(3)("Not sending event to the WindowManager\n"); EventHandler::getInstance()->grabEvents(false); return 0; } } else { if (event->type == SDL_MOUSEBUTTONDOWN) { EventHandler::getInstance()->grabEvents( true); return 1; } } return 1; } int EventHandler::releaseMouse(void* p) { SDL_ShowCursor(SDL_ENABLE); SDL_WM_GrabInput(SDL_GRAB_OFF); return 0; } /** * @param state The State to get the Name of. * @returns the Name of the State. */ const std::string& EventHandler::ELStateToString(elState state) { if (state < ES_NUMBER) return EventHandler::stateNames[state]; else return EventHandler::stateNames[5]; } /** * @param stateName the Name of the State to retrieve. * @return the State given by the name */ elState EventHandler::StringToELState(const std::string& stateName) { for (unsigned int i = 0 ; i < ES_NUMBER; i++) if (stateName == EventHandler::stateNames[i]) return (elState)i; return ES_NULL; } const std::string EventHandler::stateNames[] = { "game", "game_menu", "menu", "shell", "all", "unknown", }; /** * @brief outputs some nice information about the EventHandler */ void EventHandler::debug() const { PRINT(0)("===============================\n"); PRINT(0)(" EventHandle Debug Information \n"); PRINT(0)("===============================\n"); for(int i = 0; i < ES_NUMBER; ++i) { for(int j = 0; j < EV_NUMBER; ++j) for (unsigned int evl = 0; evl < this->listeners[i][j].size(); evl++) PRINT(0)("Event %s(%d) of State %s(%d) subscribed to %s (%p)\n", EVToKeyName(j).c_str(), j, ELStateToString((elState)i).c_str(), i, this->listeners[i][j][evl]->getCName(), this->listeners[i][j][evl]); } PRINT(0)("============================EH=\n"); }