view window/event.cc @ 66:d112357a0ec1

Fix a bug with savegames introduced with changeset c7bcc0ec2267. Warning: savegames created since c7bcc0ec2267 are probably corrupted, you may have to start the game over. If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow', and 'Text Window' by 'TextImpl Window' in your save files.
author Thibaut Girka <thib@sitedethib.com>
date Sat, 11 Dec 2010 18:36:20 +0100
parents 4416cfac86ae
children
line wrap: on
line source

/*
 * Copyright (c) 2004-2006  Kazunori "jagarl" Ueno
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "SDL.h"
#include "event.h"
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <sys/stat.h>

using namespace std;

bool save_req = false, load_req = false, grpdump_req = false; //  scn2k/scn2k_impl.cc: キーボードからセーブ・ロードできるように
bool pressAreq=false,pressFreq=false,pressDreq=false;
namespace Event {
/* Impl: struct Event::Video */

Video::Video(Container& container) : region(0, 0, 0, 0), z(0), parent(container) {
	activated = false;
	parent.Add(this);
}
Video::Video(Container& container, const Rect& init_rect) : region(init_rect), z(0), parent(container) {
	activated = false;
	parent.Add(this);
}
Video::Video(Container& container, const Rect& init_rect, int _z) : region(init_rect), z(_z), parent(container) {
	activated = false;
	parent.Add(this);
}
Video::~Video() {
	parent.Delete(this);
};
void Video::SetRegion(const Rect& new_rect) {
	region = new_rect;
}
void Video::SetZ(int new_z) {
	z = new_z;
}
void Video::activate(void) {
	activated = true;
}
void Video::deactivate(void) {
	activated = false;
}
inline int Video::point_in(int x, int y) {
	if (!activated) return -1;
	if (region.point_in(x,y)) return z;
	else return -1;
}

/* カーソルの動く順序:上、左の順に準位付け */
bool operator <(const Video& pos1, const Video& pos2) {
	if (pos1.region.ty < pos2.region.ty) return true;
	if (pos1.region.ty == pos2.region.ty) return pos1.region.lx < pos2.region.lx;
	if (pos1.region.by >= pos2.region.by) return pos1.region.lx <= pos2.region.lx;
	return false;
}


/* Impl: struct Event::Time */
Time::Time(Container& container) : wakeup_time(container.current_time), parent(container) {
	parent.Add(this);
}
Time::~Time() {
	parent.Delete(this);
}
/* Define: struct Event::ContainerImpl */

struct ContainerImplTime_Item {
	Time* instance;
	bool valid;
	bool operator ==(Time* const& to) const {
		return to == instance;
	}
	ContainerImplTime_Item(Time* _time) :
		instance(_time), valid(true) {
	}
};

class ContainerImplTime : private vector<ContainerImplTime_Item> {
public:
	ContainerImplTime(void);
	bool Exec(unsigned int current_time);
	void Add(Time* new_event);
	void Delete(Time* delete_event);
private:
	static vector<ContainerImplTime_Item> new_item;
	unsigned int prev_execed_time;
	static bool is_invalid(const_reference value) {
		return !value.valid;
	}
};

vector<ContainerImplTime_Item> ContainerImplTime::new_item;

ContainerImplTime::ContainerImplTime(void) {
	prev_execed_time = 0;
}
void ContainerImplTime::Add(Time* event) {
	ContainerImplTime_Item item(event);
	new_item.push_back(item);
}
void ContainerImplTime::Delete(Time* delete_event) {
	iterator it = find(begin(), end(), delete_event);
	if (it != end()) {
		it->valid = false;
		it->instance = NULL;
		return;
	}
	it = find(new_item.begin(), new_item.end(), delete_event);
	if (it != end()) {
		it->valid = false;
		it->instance = NULL;
		return;
	}
}

bool ContainerImplTime::Exec(unsigned int current_time) {
	if (current_time == Time::NEVER_WAKE) return true;
	// 呼び出しまでに作製されたitemを追加 
	insert(end(), new_item.begin(), new_item.end());
	new_item.clear();
	if (empty()) return true;
	if (current_time == Time::FRAME_UPDATE) { // ビデオフレームの更新時
		for (iterator it = begin(); it != end(); it++) {
			if (! it->valid) continue;
			
			unsigned tm = it->instance->Wakeup();
			if (tm == Time::FRAME_UPDATE) {
				it->instance->Elapsed(prev_execed_time);
			}
		}
	} else { // 時間変化時
		if (current_time < prev_execed_time) prev_execed_time = 0; /* 時間が一回りして0に戻ったとき */
		for (iterator it = begin(); it != end(); it++) {
			if (! it->valid) continue;
			unsigned tm = it->instance->Wakeup();
			if (tm >= prev_execed_time && tm < current_time) {
				it->instance->Elapsed(current_time);
			}
		}
		prev_execed_time = current_time;
	}
	// 処理中に削除された item を実際に削除
	erase(remove_if(begin(), end(), is_invalid), end());
	return true;
}


class ContainerImplVideo : private vector<Video*> {
	public:
		bool Exec(void);

		ContainerImplVideo(void);
		~ContainerImplVideo();
		void Add(Video* item);
		void Delete(Video* item);
		void RegisterGlobalMotionFunc(Container::motionfunc, void* pointer);
		void DeleteGlobalMotionFunc(Container::motionfunc, void* pointer);
		void RegisterGlobalPressFunc(Container::motionfunc, void* pointer);
		void DeleteGlobalPressFunc(Container::motionfunc, void* pointer);
	private:
		struct Motionfunc {
			Container::motionfunc func;
			void* pointer;
			bool operator ==(const Motionfunc& m) const { return func == m.func && pointer == m.pointer;}
		};
		list<Motionfunc> motion_vec;
		list<Motionfunc> press_vec;
		typedef list<Motionfunc>::iterator MotionIterator;
		bool is_sorted;
	public:
		int button_pressed;
		int button_released;
		int mouse_x, mouse_y;
		int new_mouse_x, new_mouse_y;
	private:
		void SetChanged(void);
		static bool SortLess(const Video* pos1, const Video* pos2) {
			return pos1 < pos2;
		}
		void Sort(void);
		void Motion(int x, int y); // mouse motion
		void Press(void);
		void TakeScreenshot(void);
		iterator cur_pos;
		Video* cur_item; // 現在のフォーカス位置
		int cur_pressed_x, cur_pressed_y;
};

void ContainerImplVideo::SetChanged(void) {
	if (is_sorted) {
		if (cur_item != NULL) {
			cur_pos = find(begin(), end(), cur_item);
			if (cur_pos == end()) cur_item = NULL;
		}
		is_sorted = false;
	}
}

void ContainerImplVideo::Sort(void) {
	sort(begin(), end(), SortLess);
	if (cur_item != NULL) {
		cur_pos = lower_bound(begin(), end(), cur_item, SortLess);
	} else {
		cur_pos = end();
	}
	is_sorted = true;
}

ContainerImplVideo::ContainerImplVideo(void) {
	is_sorted = false;
	button_pressed = 0;
	button_released = 0;
	cur_item = NULL;
	mouse_x = 0; mouse_y = 0;
	new_mouse_x = 0; new_mouse_y = 0;
}

ContainerImplVideo::~ContainerImplVideo(void) {
};

void ContainerImplVideo::Add(Video* event) {
	push_back(event);
	SetChanged();
}

void ContainerImplVideo::Delete(Video* delete_event) {
	iterator it = find(begin(), end(), delete_event);
	if (it != end()) {
		erase(it);
		SetChanged();
	} else {
		fprintf(stderr,"\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
		fprintf(stderr,"X  ContainerImplVideo: Cannot delete node %p\n",delete_event);
		fprintf(stderr,"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n");
		fprintf(stderr,"vector from:\n");
		for(it=begin(); it!=end(); it++) {
			fprintf(stderr,"%p, ",*it);
		}
		fprintf(stderr,"\n");
	}
	if (delete_event == cur_item) {
		cur_pos = end();
		cur_item = NULL;
		Motion(mouse_x, mouse_y);
	}
}

void ContainerImplVideo::RegisterGlobalMotionFunc(Container::motionfunc func, void* pointer) {
	Motionfunc f;
	f.func = func;
	f.pointer = pointer;
	if (find(motion_vec.begin(), motion_vec.end(), f) == motion_vec.end()) {
		motion_vec.push_back(f);
	}
}

void ContainerImplVideo::DeleteGlobalMotionFunc(Container::motionfunc func, void* pointer) {
	Motionfunc f;
	f.func = func;
	f.pointer = pointer;
	list<Motionfunc>::iterator it = find(motion_vec.begin(), motion_vec.end(), f);
	if (it != motion_vec.end())
		motion_vec.erase(it);
}

void ContainerImplVideo::RegisterGlobalPressFunc(Container::motionfunc func, void* pointer) {
	Motionfunc f;
	f.func = func;
	f.pointer = pointer;
	if (find(press_vec.begin(), press_vec.end(), f) == press_vec.end()) {
		press_vec.push_back(f);
	}
}

void ContainerImplVideo::DeleteGlobalPressFunc(Container::motionfunc func, void* pointer) {
	Motionfunc f;
	f.func = func;
	f.pointer = pointer;
	list<Motionfunc>::iterator it = find(press_vec.begin(), press_vec.end(), f);
	if (it != press_vec.end())
		press_vec.erase(it);
}

void ContainerImplVideo::Motion(int x, int y) {
	mouse_x = x; mouse_y = y;
	MotionIterator mit;
	for (mit=motion_vec.begin(); mit != motion_vec.end();) {
		MotionIterator mit_next = mit;
		mit_next++;
		if (!(*mit->func)(x, y, mit->pointer)) motion_vec.erase(mit);
		mit = mit_next;
	
	}
	
	/* @@@ ドラッグ処理とマウスを押す処理のバッティングで「二回ボタンを押さないと云々」関連のバグの可能性あり */
	if (button_pressed & (1<<MOUSE_LEFT)) {
		if (cur_item) cur_item->Drag(cur_pressed_x, cur_pressed_y, x, y);
		return;
	}
	if (cur_item != NULL) cur_item->Motion(x,y);
	int z = -1; iterator z_it;
	iterator it;
	for (it = begin(); it != end(); it++) {
		int new_z = (*it)->point_in(x, y);
		if (z < new_z) {
			z = new_z;
			z_it = it;
		}
	}
	if (z != -1) {
		if (cur_item == *z_it) return;
		if (cur_item) cur_item->Out();
		cur_pos = z_it;
		cur_item = *z_it;
		cur_item->In();
		return;
	} else {
		if (cur_item) cur_item->Out();
		cur_pos = end();
		cur_item = NULL;
	}
	return;
}

void ContainerImplVideo::Press(void) {
	if (cur_item) {
		cur_pressed_x = mouse_x;
		cur_pressed_y = mouse_y;
		cur_item->Press();
		return;
	}
	MotionIterator mit;
	for (mit=press_vec.begin(); mit != press_vec.end(); ) {
		MotionIterator mit_next = mit;
		mit_next++;
		if (!(*mit->func)(mouse_x, mouse_y, mit->pointer)) {
			press_vec.erase(mit);
		}
		mit = mit_next;
	}
}

void ContainerImplVideo::TakeScreenshot(void) {
	int n=0;
	char filename[1024];
	struct stat buffer;
	for(n=0; n<9999; n++) {
		// XXX: put screenshots in a seperate dir?
		sprintf(filename, "xclannad_%04i.bmp", n);
		if(stat(filename, &buffer) == -1) break;
	}
	SDL_SaveBMP(SDL_GetVideoSurface(), filename);
}

bool ContainerImplVideo::Exec(void) {
	bool is_mouse_motion = false;
	int motion_x = 0, motion_y = 0;
	SDL_Event event;
	SDL_PumpEvents();
	while(SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS) == 1) {
		switch(event.type) {
			case SDL_QUIT: return false; // @@@ なにかやらないと
			case SDL_ACTIVEEVENT: // なにもしない
				// cout<<"active : gain "<<int(event.active.gain)<<", state "<<int(event.active.state)<<endl;
				break;
			case SDL_KEYDOWN:
				if (!is_sorted) Sort();
				switch(event.key.keysym.sym) {
					case SDLK_F12:
					case SDLK_PRINT:
					case SDLK_p:  // for Zaurus
						TakeScreenshot();
						break;
					// Some window managers (eg enlightenment) use Alt-Enter for
					// themselves, F11 is a good alternative
					case SDLK_F11:
						SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
						break;
					case SDLK_RETURN:
						if (SDL_GetModState() & KMOD_ALT) {
							SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
							break;
						}
					case SDLK_SPACE:
						Press();
						break;
					case SDLK_TAB: // move to next widget
						if (cur_pos != end())  cur_pos++;
						if (cur_pos == end()) cur_pos = begin();
						if (cur_pos != end())  {
							cur_item = *cur_pos;
							cur_item->In();
						} else {
							cur_item = NULL;
						}
						break;
					case SDLK_LEFT: if (cur_pos != end()) (*cur_pos)->KeyLeft(); break;
					case SDLK_RIGHT:if (cur_pos != end()) (*cur_pos)->KeyRight(); break;
					case SDLK_LSHIFT: case SDLK_RSHIFT: button_pressed |= (1<<KEY_SHIFT); break;
					case SDLK_ESCAPE: button_pressed |= (1<<MOUSE_RIGHT); break; /* for Zaurus */
					case SDLK_s: save_req = true; break;
					case SDLK_l: load_req = true; break;
					case SDLK_g: grpdump_req = true; break;
					case SDLK_a: pressAreq = true; break;
					case SDLK_d: pressDreq = true; break;
					case SDLK_f: pressFreq = true; break;
				}
				break;
			case SDL_KEYUP:
				// cout << "keyup which "<<int(event.key.which)<<", sym "<<int(event.key.keysym.sym)<<endl;
				switch(event.key.keysym.sym) {
					case SDLK_RETURN: case SDLK_SPACE:
						if (cur_item) cur_item->Release();
					case SDLK_LSHIFT: case SDLK_RSHIFT: button_pressed &= ~(1<<KEY_SHIFT); button_released |= 1<<KEY_SHIFT; break;
					case SDLK_ESCAPE: button_pressed &= ~(1<<MOUSE_RIGHT); button_released |= 1<<MOUSE_RIGHT; break; /* for Zaurus */
				}
				break;
			case  SDL_MOUSEMOTION:
				motion_x = event.motion.x;
				motion_y = event.motion.y;
				is_mouse_motion = true;
				// Motion(event.motion.x, event.motion.y);
				// cout<< "motion which "<<int(event.motion.which)<<
				//	"x "<<event.motion.x << "y "<<event.motion.y<<endl;
				break;
			case SDL_MOUSEBUTTONUP:
				if (event.button.button == 1) {
					Motion(event.button.x, event.button.y);
					is_mouse_motion = false;
					if (cur_item) cur_item->Release();
				}
				switch(event.button.button) {
					case 1:
						button_pressed &= ~(1<<MOUSE_LEFT);
						button_released |= 1<<MOUSE_LEFT;
						break;
					case 2:
						button_pressed &= ~(1<<MOUSE_MIDDLE);
						button_released |= 1<<MOUSE_MIDDLE;
						break;
					case 3:
						button_pressed &= ~(1<<MOUSE_RIGHT);
						button_released |= 1<<MOUSE_RIGHT;
						break;
					case 4:
						button_pressed &= ~(1<<MOUSE_UP);
						button_released |= 1<<MOUSE_UP;
						break;
					case 5:
						button_pressed &= ~(1<<MOUSE_DOWN);
						button_released |= 1<<MOUSE_DOWN;
						break;
				}
				break;
			case SDL_MOUSEBUTTONDOWN:
				if (event.button.button == 1) {
					Motion(event.button.x, event.button.y);
					is_mouse_motion = false;
					Press();
				}
				switch(event.button.button) {
					case 1: button_pressed |= (1<<MOUSE_LEFT); break;
					case 2: button_pressed |= (1<<MOUSE_MIDDLE); break;
					case 3: button_pressed |= (1<<MOUSE_RIGHT); break;
					case 4: button_pressed |= (1<<MOUSE_UP); break;
					case 5: button_pressed |= (1<<MOUSE_DOWN); break;
				}
				// cout << "mouse which "<<int(event.button.which)<<"button "<<int(event.button.button)<<
				//	"state "<<int(event.button.state)<<"x "<<event.button.x << "y "<<event.button.y<<endl;
				break;
			case SDL_VIDEOEXPOSE: // redraw の必要がある?
				// cout<<"expose."<<endl;
				break;
		}
	}
	// Motion 呼び出しは一回だけ
	if (is_mouse_motion)
		Motion(motion_x, motion_y);
	return true;
}

/* Impl: struct Event::Container */
Container::Container(void) {
	pimpl_video = new ContainerImplVideo;
	try {
		pimpl_time = new ContainerImplTime;
	} catch(...) {
		delete pimpl_video;
		throw;
	}
	button_pressed = 0;
	current_time = 0;
	int i;
	for (i=0; i<BUTTON_MAX; i++) button_presscount[i] = 0;
}

Container::~Container(void) {
	delete pimpl_video;
	delete pimpl_time;
}

void Container::Add(Video* item) {
	pimpl_video->Add(item);
}

void Container::Delete(Video* item) {
	pimpl_video->Delete(item);
}

void Container::Add(Time* item) {
	pimpl_time->Add(item);
}

void Container::Delete(Time* item) {
	pimpl_time->Delete(item);
}

void Container::RegisterGlobalMotionFunc(Container::motionfunc f, void* pointer) {
	pimpl_video->RegisterGlobalMotionFunc(f, pointer);
}

void Container::DeleteGlobalMotionFunc(Container::motionfunc f, void* pointer) {
	pimpl_video->DeleteGlobalMotionFunc(f, pointer);
}

void Container::RegisterGlobalPressFunc(Container::motionfunc f, void* pointer) {
	pimpl_video->RegisterGlobalPressFunc(f, pointer);
}

void Container::DeleteGlobalPressFunc(Container::motionfunc f, void* pointer) {
	pimpl_video->DeleteGlobalPressFunc(f, pointer);
}

bool Container::Exec(unsigned int time) {
	current_time = time;
	bool ret = true;
	ret = ret && pimpl_video->Exec();
	ret = ret && pimpl_time->Exec(time);
	int i; int mask = 1;

	for (i=0; i<BUTTON_MAX; i++) {
		if (pimpl_video->button_released&mask) {
			button_presscount[i]++;
		}
		mask <<= 1;
	}
	pimpl_video->button_released = 0;
	button_pressed = pimpl_video->button_pressed;
	return ret;
}

void Container::MousePos(int& x, int& y) {
	x = pimpl_video->mouse_x;
	y = pimpl_video->mouse_y;
}

bool Container::pressed(int mask) {
	if (mask < 0 || mask >= BUTTON_MAX) return 0;
	return (button_pressed & (1<<mask)) != 0;
}

bool Container::presscount(int mask) {
	if (mask < 0 || mask >= BUTTON_MAX) return 0;
	int count = button_presscount[mask];
	button_presscount[mask] = 0;
	return count;
}

} /* end of namespace Container */

// 問題:
// z 軸と xy 軸の相互干渉;高速化
// 移動するウィジット描画の高速化
// キャッシュ
// 文字列の一部のみ更新の高速化
// 「階層 z で x なる領域無効化、y なる領域生成」で良い?>Expose