mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-29 23:11:41 +00:00
440 lines
10 KiB
C++
440 lines
10 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003-5 by David White <davidnwhite@verizon.net>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "global.hpp"
|
|
|
|
#include "clipboard.hpp"
|
|
#include "cursor.hpp"
|
|
#include "events.hpp"
|
|
#include "gp2x.hpp"
|
|
#include "preferences_display.hpp"
|
|
#include "sound.hpp"
|
|
#include "video.hpp"
|
|
#include "wassert.hpp"
|
|
|
|
#include "SDL.h"
|
|
|
|
#include <algorithm>
|
|
#include <deque>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace events
|
|
{
|
|
|
|
namespace {
|
|
int disallow_resize = 0;
|
|
}
|
|
|
|
resize_lock::resize_lock()
|
|
{
|
|
++disallow_resize;
|
|
}
|
|
|
|
resize_lock::~resize_lock()
|
|
{
|
|
--disallow_resize;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct context
|
|
{
|
|
context() : focused_handler(-1) {}
|
|
void add_handler(handler* ptr);
|
|
bool remove_handler(handler* ptr);
|
|
int cycle_focus();
|
|
void set_focus(const handler* ptr);
|
|
|
|
std::vector<handler*> handlers;
|
|
int focused_handler;
|
|
|
|
private:
|
|
void delete_handler_index(size_t handler);
|
|
};
|
|
|
|
void context::add_handler(handler* ptr)
|
|
{
|
|
handlers.push_back(ptr);
|
|
}
|
|
|
|
void context::delete_handler_index(size_t handler)
|
|
{
|
|
if(focused_handler == int(handler)) {
|
|
focused_handler = -1;
|
|
} else if(focused_handler > int(handler)) {
|
|
--focused_handler;
|
|
}
|
|
|
|
handlers.erase(handlers.begin()+handler);
|
|
}
|
|
|
|
bool context::remove_handler(handler* ptr)
|
|
{
|
|
if(handlers.empty()) {
|
|
return false;
|
|
}
|
|
|
|
static int depth = 0;
|
|
++depth;
|
|
|
|
//the handler is most likely on the back of the events array,
|
|
//so look there first, otherwise do a complete search.
|
|
if(handlers.back() == ptr) {
|
|
delete_handler_index(handlers.size()-1);
|
|
} else {
|
|
const std::vector<handler*>::iterator i = std::find(handlers.begin(),handlers.end(),ptr);
|
|
if(i != handlers.end()) {
|
|
delete_handler_index(i - handlers.begin());
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
--depth;
|
|
|
|
if(depth == 0) {
|
|
cycle_focus();
|
|
} else {
|
|
focused_handler = -1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int context::cycle_focus()
|
|
{
|
|
int index = focused_handler+1;
|
|
for(size_t i = 0; i != handlers.size(); ++i) {
|
|
if(size_t(index) == handlers.size()) {
|
|
index = 0;
|
|
}
|
|
|
|
if(handlers[size_t(index)]->requires_event_focus()) {
|
|
focused_handler = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return focused_handler;
|
|
}
|
|
|
|
void context::set_focus(const handler* ptr)
|
|
{
|
|
const std::vector<handler*>::const_iterator i = std::find(handlers.begin(),handlers.end(),ptr);
|
|
if(i != handlers.end() && (**i).requires_event_focus()) {
|
|
focused_handler = int(i - handlers.begin());
|
|
}
|
|
}
|
|
|
|
//this object stores all the event handlers. It is a stack of event 'contexts'.
|
|
//a new event context is created when e.g. a modal dialog is opened, and then
|
|
//closed when that dialog is closed. Each context contains a list of the handlers
|
|
//in that context. The current context is the one on the top of the stack
|
|
std::deque<context> event_contexts;
|
|
|
|
} //end anon namespace
|
|
|
|
event_context::event_context()
|
|
{
|
|
event_contexts.push_back(context());
|
|
}
|
|
|
|
event_context::~event_context()
|
|
{
|
|
wassert(event_contexts.empty() == false);
|
|
event_contexts.pop_back();
|
|
}
|
|
|
|
handler::handler(const bool auto_join) : unicode_(SDL_EnableUNICODE(1)), has_joined_(false)
|
|
{
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
|
|
if(auto_join) {
|
|
event_contexts.back().add_handler(this);
|
|
has_joined_ = true;
|
|
}
|
|
}
|
|
|
|
handler::~handler()
|
|
{
|
|
leave();
|
|
SDL_EnableUNICODE(unicode_);
|
|
}
|
|
|
|
void handler::join()
|
|
{
|
|
if(has_joined_) {
|
|
leave(); // should not be in multiple event contexts
|
|
}
|
|
//join self
|
|
event_contexts.back().add_handler(this);
|
|
has_joined_ = true;
|
|
|
|
//instruct members to join
|
|
handler_vector members = handler_members();
|
|
if(!members.empty()) {
|
|
for(handler_vector::iterator i = members.begin(); i != members.end(); i++) {
|
|
(*i)->join();
|
|
}
|
|
}
|
|
}
|
|
|
|
void handler::leave()
|
|
{
|
|
handler_vector members = handler_members();
|
|
if(!members.empty()) {
|
|
for(handler_vector::iterator i = members.begin(); i != members.end(); i++) {
|
|
(*i)->leave();
|
|
}
|
|
} else {
|
|
wassert(event_contexts.empty() == false);
|
|
}
|
|
for(std::deque<context>::reverse_iterator i = event_contexts.rbegin(); i != event_contexts.rend(); ++i) {
|
|
if(i->remove_handler(this)) {
|
|
break;
|
|
}
|
|
}
|
|
has_joined_ = false;
|
|
}
|
|
|
|
void focus_handler(const handler* ptr)
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
event_contexts.back().set_focus(ptr);
|
|
}
|
|
}
|
|
|
|
void cycle_focus()
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
event_contexts.back().cycle_focus();
|
|
}
|
|
}
|
|
|
|
bool has_focus(const handler* ptr)
|
|
{
|
|
if(event_contexts.empty()) {
|
|
return true;
|
|
}
|
|
|
|
if(ptr->requires_event_focus() == false) {
|
|
return true;
|
|
}
|
|
|
|
const int index = event_contexts.back().focused_handler;
|
|
|
|
//if no-one has focus at the moment, this handler obviously wants
|
|
//focus, so give it to it.
|
|
if(index == -1) {
|
|
focus_handler(ptr);
|
|
return true;
|
|
} else {
|
|
return event_contexts.back().handlers[index] == ptr;
|
|
}
|
|
}
|
|
|
|
void pump()
|
|
{
|
|
SDL_PumpEvents();
|
|
|
|
static std::pair<int,int> resize_dimensions(0,0);
|
|
|
|
//used to keep track of double click events
|
|
static int last_mouse_down = -1;
|
|
static int last_click_x = -1, last_click_y = -1;
|
|
|
|
#ifdef GP2X
|
|
gp2x::makeup_events();
|
|
#endif
|
|
|
|
SDL_Event event;
|
|
while(SDL_PollEvent(&event)) {
|
|
|
|
switch(event.type) {
|
|
|
|
case SDL_APPMOUSEFOCUS: {
|
|
SDL_ActiveEvent& ae = reinterpret_cast<SDL_ActiveEvent&>(event);
|
|
if(ae.state == SDL_APPMOUSEFOCUS || ae.state == SDL_APPINPUTFOCUS) {
|
|
cursor::set_focus(ae.gain == 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//if the window must be redrawn, update the entire screen
|
|
case SDL_VIDEOEXPOSE: {
|
|
update_whole_screen();
|
|
break;
|
|
}
|
|
|
|
case SDL_VIDEORESIZE: {
|
|
const SDL_ResizeEvent* const resize = reinterpret_cast<SDL_ResizeEvent*>(&event);
|
|
|
|
if(resize->w < min_allowed_width || resize->h < min_allowed_height) {
|
|
resize_dimensions.first = 0;
|
|
resize_dimensions.second = 0;
|
|
} else {
|
|
resize_dimensions.first = resize->w;
|
|
resize_dimensions.second = resize->h;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#ifdef GP2X
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
gp2x::handle_joystick(reinterpret_cast<SDL_JoyButtonEvent *>(&event));
|
|
break;
|
|
#endif
|
|
|
|
case SDL_MOUSEMOTION: {
|
|
//always make sure a cursor is displayed if the
|
|
//mouse moves or if the user clicks
|
|
cursor::set_focus(true);
|
|
raise_help_string_event(event.motion.x,event.motion.y);
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEBUTTONDOWN: {
|
|
//always make sure a cursor is displayed if the
|
|
//mouse moves or if the user clicks
|
|
cursor::set_focus(true);
|
|
if(event.button.button == SDL_BUTTON_LEFT) {
|
|
static const int DoubleClickTime = 500;
|
|
|
|
static const int DoubleClickMaxMove = 3;
|
|
const int current_ticks = ::SDL_GetTicks();
|
|
if(last_mouse_down >= 0 && current_ticks - last_mouse_down < DoubleClickTime &&
|
|
abs(event.button.x - last_click_x) < DoubleClickMaxMove &&
|
|
abs(event.button.y - last_click_y) < DoubleClickMaxMove) {
|
|
SDL_UserEvent user_event;
|
|
user_event.type = DOUBLE_CLICK_EVENT;
|
|
user_event.code = 0;
|
|
user_event.data1 = reinterpret_cast<void*>(event.button.x);
|
|
user_event.data2 = reinterpret_cast<void*>(event.button.y);
|
|
::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
|
|
}
|
|
|
|
last_mouse_down = current_ticks;
|
|
last_click_x = event.button.x;
|
|
last_click_y = event.button.y;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#if defined(_X11) && !defined(__APPLE__)
|
|
case SDL_SYSWMEVENT: {
|
|
//clipboard support for X11
|
|
handle_system_event(event);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case SDL_QUIT: {
|
|
throw CVideo::quit();
|
|
}
|
|
}
|
|
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
//events may cause more event handlers to be added and/or removed,
|
|
//so we must use indexes instead of iterators here.
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->handle_event(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(resize_dimensions.first > 0 && disallow_resize == 0) {
|
|
preferences::set_resolution(resize_dimensions);
|
|
resize_dimensions.first = 0;
|
|
resize_dimensions.second = 0;
|
|
}
|
|
|
|
if (preferences::music_on())
|
|
sound::think_about_music();
|
|
}
|
|
|
|
void raise_process_event()
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
//events may cause more event handlers to be added and/or removed,
|
|
//so we must use indexes instead of iterators here.
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->process_event();
|
|
}
|
|
}
|
|
}
|
|
|
|
void raise_draw_event()
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
//events may cause more event handlers to be added and/or removed,
|
|
//so we must use indexes instead of iterators here.
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->draw();
|
|
}
|
|
}
|
|
}
|
|
|
|
void raise_volatile_draw_event()
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
//events may cause more event handlers to be added and/or removed,
|
|
//so we must use indexes instead of iterators here.
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->volatile_draw();
|
|
}
|
|
}
|
|
}
|
|
|
|
void raise_volatile_undraw_event()
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
//events may cause more event handlers to be added and/or removed,
|
|
//so we must use indexes instead of iterators here.
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->volatile_undraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
void raise_help_string_event(int mousex, int mousey)
|
|
{
|
|
if(event_contexts.empty() == false) {
|
|
|
|
const std::vector<handler*>& event_handlers = event_contexts.back().handlers;
|
|
|
|
for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2 && i1 < event_handlers.size(); ++i1) {
|
|
event_handlers[i1]->process_help_string(mousex,mousey);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|