Mercurial > otakunoraifu
diff window/event.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | 01aa5ddf7dc8 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/window/event.cc @@ -0,0 +1,570 @@ +/* + * 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; + +extern bool save_req = false, load_req = false, grpdump_req = false; // scn2k/scn2k_impl.cc: キーボードからセーブ・ロードできるように +extern 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 = 0; + return; + } + it = find(new_item.begin(), new_item.end(), delete_event); + if (it != end()) { + it->valid = false; + it->instance = 0; + return; + } + 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) { + cur_pos = find(begin(), end(), cur_item); + if (cur_pos == end()) cur_item = 0; + } + is_sorted = false; + } +} + +void ContainerImplVideo::Sort(void) { + sort(begin(), end(), SortLess); + if (cur_item) { + 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 = 0; + 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 %x\n",delete_event); + fprintf(stderr,"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n"); + fprintf(stderr,"vector from:\n"); + for(it=begin(); it!=end(); it++) { + fprintf(stderr,"%x, ",*it); + } + fprintf(stderr,"\n"); + } + if (delete_event == cur_item) { + cur_pos = end(); + cur_item = 0; + Motion(mouse_x, mouse_y); + } + return; +} +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); + return; +} +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); + return; +} +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) 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 = 0; + } + 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 = 0; + } + 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; + return; +} +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; + int new_button_pressed = pimpl_video->button_pressed; + 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