diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/application.cpp | 553 | ||||
-rw-r--r-- | client/application.h | 70 | ||||
-rw-r--r-- | client/gui/gui.cpp | 1354 | ||||
-rw-r--r-- | client/gui/gui.h | 120 | ||||
-rw-r--r-- | client/gui/resource_provider.cpp | 128 | ||||
-rw-r--r-- | client/gui/resource_provider.h | 40 | ||||
-rw-r--r-- | client/gui/softrenderer.cpp | 7 | ||||
-rw-r--r-- | client/gui/softrenderer.h | 1 | ||||
-rw-r--r-- | client/platform.h | 1 | ||||
-rw-r--r-- | client/red_channel.cpp | 3 | ||||
-rw-r--r-- | client/red_client.cpp | 20 | ||||
-rw-r--r-- | client/red_client.h | 12 | ||||
-rw-r--r-- | client/screen.h | 2 | ||||
-rw-r--r-- | client/utils.cpp | 11 | ||||
-rw-r--r-- | client/utils.h | 2 | ||||
-rw-r--r-- | client/windows/platform.cpp | 5 | ||||
-rw-r--r-- | client/windows/redc.vcproj | 16 | ||||
-rw-r--r-- | client/x11/Makefile.am | 4 | ||||
-rw-r--r-- | client/x11/platform.cpp | 9 |
19 files changed, 2184 insertions, 174 deletions
diff --git a/client/application.cpp b/client/application.cpp index e96a9c4a..2f24307d 100644 --- a/client/application.cpp +++ b/client/application.cpp @@ -42,6 +42,7 @@ #include "cmd_line_parser.h" #include "tunnel_channel.h" #include "rect.h" +#include "gui/gui.h" #include <log4cpp/BasicConfigurator.hh> #include <log4cpp/FileAppender.hh> @@ -85,54 +86,86 @@ void MonitorsQuery::do_response(AbstractProcessLoop& events_loop) } } -class GUILayer: public ScreenLayer { +//todo: add inactive visual appearance +class GUIBarrier: public ScreenLayer { public: - GUILayer(); + GUIBarrier(int id) + : ScreenLayer(SCREEN_LAYER_GUI_BARIER, true) + , _id (id) + , _cursor (Platform::create_inactive_cursor()) + { + } + + ~GUIBarrier() + { + detach(); + } + + int get_id() { return _id;} + + void attach(RedScreen& in_screen) + { + if (screen()) { + ASSERT(&in_screen == screen()) + return; + } + in_screen.attach_layer(*this); + } + + void detach() + { + if (!screen()) { + return; + } + screen()->detach_layer(*this); + } + + virtual bool pointer_test(int x, int y) { return true;} + virtual void on_pointer_enter(int x, int y, unsigned int buttons_state) + { + AutoRef<LocalCursor> cursor(Platform::create_inactive_cursor()); + screen()->set_cursor(*cursor); + return; + } + +private: + int _id; + AutoRef<LocalCursor> _cursor; +}; + +class InfoLayer: public ScreenLayer { +public: + InfoLayer(); virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc); - void set_splash_mode(); void set_info_mode(); void set_sticky(bool is_on); virtual void on_size_changed(); private: - void draw_splash(const QRegion& dest_region, RedDrawable& dest); void draw_info(const QRegion& dest_region, RedDrawable& dest); + void update_sticky_rect(); private: - ImageFromRes _splash_pixmap; AlphaImageFromRes _info_pixmap; AlphaImageFromRes _sticky_pixmap; - Point _splash_pos; Point _info_pos; Point _sticky_pos; Rect _sticky_rect; - bool _splash_mode; bool _sticky_on; RecurciveMutex _update_lock; }; -GUILayer::GUILayer() - : ScreenLayer(SCREEN_LAYER_GUI, false) - , _splash_pixmap (SPLASH_IMAGE_RES_ID) +InfoLayer::InfoLayer() + : ScreenLayer(SCREEN_LAYER_INFO, false) , _info_pixmap (INFO_IMAGE_RES_ID) , _sticky_pixmap (STICKY_KEY_PIXMAP) - , _splash_mode (false) , _sticky_on (false) { } -void GUILayer::draw_splash(const QRegion& dest_region, RedDrawable& dest) -{ - ASSERT(!_sticky_on); - for (int i = 0; i < (int)dest_region.num_rects; i++) { - Rect* r = &dest_region.rects[i]; - dest.copy_pixels(_splash_pixmap, r->left - _splash_pos.x, r->top - _splash_pos.y, *r); - } -} - -void GUILayer::draw_info(const QRegion& dest_region, RedDrawable& dest) +void InfoLayer::draw_info(const QRegion& dest_region, RedDrawable& dest) { for (int i = 0; i < (int)dest_region.num_rects; i++) { Rect* r = &dest_region.rects[i]; @@ -145,36 +178,18 @@ void GUILayer::draw_info(const QRegion& dest_region, RedDrawable& dest) } } -void GUILayer::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc) +void InfoLayer::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc) { RecurciveLock lock(_update_lock); - if (_splash_mode) { - draw_splash(dest_region, dest_dc); - } else { - draw_info(dest_region, dest_dc); - } + draw_info(dest_region, dest_dc); } -void GUILayer::set_splash_mode() +void InfoLayer::set_info_mode() { RecurciveLock lock(_update_lock); - Point size = _splash_pixmap.get_size(); - Point screen_size = screen()->get_size(); - Rect r; - _splash_pos.y = r.top = (screen_size.y - size.y) / 2; - _splash_pos.x = r.left = (screen_size.x - size.x) / 2; - r.bottom = r.top + size.y; - r.right = r.left + size.x; - _splash_mode = true; - lock.unlock(); - set_rect_area(r); - ASSERT(!_sticky_on); -} + ASSERT(screen()); -void GUILayer::set_info_mode() -{ - RecurciveLock lock(_update_lock); Point size = _info_pixmap.get_size(); Point screen_size = screen()->get_size(); Rect r; @@ -184,45 +199,45 @@ void GUILayer::set_info_mode() _info_pos.x = r.right - size.x; _info_pos.y = r.top = 0; r.bottom = r.top + size.y; - _splash_mode = false; lock.unlock(); set_rect_area(r); - + update_sticky_rect(); set_sticky(_sticky_on); } -void GUILayer::set_sticky(bool is_on) +void InfoLayer::update_sticky_rect() +{ + Point size = _sticky_pixmap.get_size(); + Point screen_size = screen()->get_size(); + + _sticky_pos.x = (screen_size.x - size.x) / 2; + _sticky_pos.y = screen_size.y * 2 / 3; + _sticky_rect.left = _sticky_pos.x; + _sticky_rect.top = _sticky_pos.y; + _sticky_rect.right = _sticky_rect.left + size.x; + _sticky_rect.bottom = _sticky_rect.top + size.y; +} + +void InfoLayer::set_sticky(bool is_on) { RecurciveLock lock(_update_lock); if (!_sticky_on && !is_on) { return; } - Point size = _sticky_pixmap.get_size(); - Point screen_size = screen()->get_size(); - _sticky_on = is_on; + if (_sticky_on) { - _sticky_pos.x = (screen_size.x - size.x) / 2; - _sticky_pos.y = screen_size.y * 2 / 3; - _sticky_rect.left = _sticky_pos.x; - _sticky_rect.top = _sticky_pos.y; - _sticky_rect.right = _sticky_rect.left + size.x; - _sticky_rect.bottom = _sticky_rect.top + size.y; add_rect_area(_sticky_rect); - invalidate(); + invalidate(_sticky_rect); } else { remove_rect_area(_sticky_rect); } } -void GUILayer::on_size_changed() +void InfoLayer::on_size_changed() { - if (_splash_mode) { - set_splash_mode(); - } else { - set_info_mode(); - } + set_info_mode(); } void StickyKeyTimer::response(AbstractProcessLoop& events_loop) @@ -235,10 +250,46 @@ void StickyKeyTimer::response(AbstractProcessLoop& events_loop) ASSERT(sticky_info->key_down); sticky_info->sticky_mode = true; DBG(0, "ON sticky"); - app->_gui_layer->set_sticky(true); + app->_info_layer->set_sticky(true); app->deactivate_interval_timer(this); } +class GUITimer: public Timer { +public: + GUITimer(GUI& gui) + : _gui (gui) + { + } + + virtual void response(AbstractProcessLoop& events_loop) + { + _gui.idle(); + } + +private: + GUI& _gui; +}; + + +#ifdef GUI_DEMO +class TestTimer: public Timer { +public: + TestTimer(Application& app) + : _app (app) + { + } + + virtual void response(AbstractProcessLoop& events_loop) + { + _app.message_box_test(); + } + +private: + Application& _app; +}; +#endif + + static MouseHandler default_mouse_handler; static KeyHandler default_key_handler; @@ -254,6 +305,7 @@ enum AppCommands { APP_CMD_CONNECT, APP_CMD_DISCONNECT, #endif + APP_CMD_SHOW_GUI, }; Application::Application() @@ -267,13 +319,14 @@ Application::Application() , _exit_code (0) , _active_screen (NULL) , _num_keys_pressed (0) - , _gui_layer (new GUILayer()) + , _info_layer (new InfoLayer()) , _key_handler (&default_key_handler) , _mouse_handler (&default_mouse_handler) , _monitors (NULL) , _title (L"SPICEc:%d") - , _splash_mode (true) , _sys_key_intercept_mode (false) + , _gui_mode (GUI_MODE_FULL) + , _state (DISCONNECTED) { DBG(0, ""); Platform::set_process_loop(*this); @@ -281,8 +334,6 @@ Application::Application() memset(_keyboard_state, 0, sizeof(_keyboard_state)); init_menu(); _main_screen = get_screen(0); - _main_screen->attach_layer(*_gui_layer); - _gui_layer->set_splash_mode(); Platform::set_event_listener(this); Platform::set_display_mode_listner(this); @@ -293,6 +344,7 @@ Application::Application() _commands_map["connect"] = APP_CMD_CONNECT; _commands_map["disconnect"] = APP_CMD_DISCONNECT; #endif + _commands_map["show-gui"] = APP_CMD_SHOW_GUI; _canvas_types.resize(1); #ifdef WIN32 @@ -307,6 +359,7 @@ Application::Application() ",connect=shift+f5" ",disconnect=shift+f6" #endif + ",show-gui=shift+f7" , _commands_map)); _hot_keys = parser->get(); @@ -316,11 +369,32 @@ Application::Application() _sticky_info.key_down = false; _sticky_info.key = REDKEY_INVALID; _sticky_info.timer.reset(new StickyKeyTimer()); + + _gui.reset(new GUI(*this, DISCONNECTED)); + _gui_timer.reset(new GUITimer(*_gui.get())); + activate_interval_timer(*_gui_timer, 1000 / 30); +#ifdef GUI_DEMO + _gui_test_timer.reset(new TestTimer(*this)); + activate_interval_timer(*_gui_test_timer, 1000 * 30); +#endif + for (int i = RED_CHANNEL_MAIN; i < RED_CHANNEL_END; i++) { + _peer_con_opt[i] = RedPeer::ConnectionOptions::CON_OP_BOTH; + } } Application::~Application() { - _main_screen->detach_layer(*_gui_layer); + deactivate_interval_timer(*_gui_timer); +#ifdef GUI_DEMO + deactivate_interval_timer(*_gui_test_timer); +#endif + destroyed_gui_barriers(); + _gui->set_screen(NULL); + + if (_info_layer->screen()) { + _main_screen->detach_layer(*_info_layer); + } + _main_screen->unref(); destroy_monitors(); } @@ -405,7 +479,7 @@ void Application::remove_mouse_handler(MouseHandler& handler) void Application::capture_mouse() { - if (!_active_screen) { + if (!_active_screen || _gui->screen()) { return; } _active_screen->capture_mouse(); @@ -441,15 +515,20 @@ private: void Application::connect() { + ASSERT(_state == DISCONNECTED); + set_state(CONNECTING); _client.connect(); } int Application::run() { - _client.connect(); - _exit_code = ProcessLoop::run(); - return _exit_code; + if (_gui_mode != GUI_MODE_FULL) { + connect(); + } + show_gui(); + _exit_code = ProcessLoop::run(); + return _exit_code; } RedScreen* Application::find_screen(int id) @@ -505,6 +584,7 @@ RedScreen* Application::get_screen(int id) size.y = SCREEN_INIT_HEIGHT; } screen = _screens[id] = new RedScreen(*this, id, _title, size.x, size.y); + create_gui_barrier(*screen, id); if (id != 0) { if (_full_screen) { @@ -532,10 +612,65 @@ RedScreen* Application::get_screen(int id) return screen; } +void Application::attach_gui_barriers() +{ + GUIBarriers::iterator iter = _gui_barriers.begin(); + + for (; iter != _gui_barriers.end(); iter++) { + GUIBarrier* barrier = *iter; + ASSERT((int)_screens.size() > barrier->get_id() && _screens[barrier->get_id()]); + barrier->attach(*_screens[barrier->get_id()]); + } +} + +void Application::detach_gui_barriers() +{ + GUIBarriers::iterator iter = _gui_barriers.begin(); + + for (; iter != _gui_barriers.end(); iter++) { + GUIBarrier* barrier = *iter; + barrier->detach(); + } +} + +void Application::create_gui_barrier(RedScreen& screen, int id) +{ + GUIBarrier* barrier = new GUIBarrier(id); + _gui_barriers.push_front(barrier); + if (_gui.get() && _gui->screen()) { + barrier->attach(screen); + } +} + +void Application::destroyed_gui_barriers() +{ + while (_gui_barriers.begin() != _gui_barriers.end()) { + GUIBarrier* barrier = *_gui_barriers.begin(); + _gui_barriers.erase(_gui_barriers.begin()); + delete barrier; + } +} + +void Application::destroyed_gui_barrier(int id) +{ + GUIBarriers::iterator iter = _gui_barriers.begin(); + + for (; iter != _gui_barriers.end(); iter++) { + GUIBarrier* barrier = *iter; + if (barrier->get_id() == id) { + _gui_barriers.erase(iter); + delete barrier; + return; + } + } +} + void Application::on_screen_destroyed(int id, bool was_captured) { bool reposition = false; + destroyed_gui_barrier(id); + if ((int)_screens.size() < id + 1 || !_screens[id]) { THROW("no screen"); } @@ -585,18 +720,32 @@ void Application::unpress_all() } } -void Application::on_connected() +void Application::set_state(State state) { + if (state == _state) { + return; + } + _state = state; + _gui->set_state(_state); + if (_gui->screen() && !_gui->is_visible()) { + hide_gui(); + } + reset_sticky(); +} +void Application::on_connected() +{ + set_state(CONNECTED); } void Application::on_disconnected(int error_code) { -#ifdef RED_DEBUG - show_splash(0); -#else - do_quit(error_code); -#endif + if (_gui_mode != GUI_MODE_FULL) { + ProcessLoop::quit(error_code); + return; + } + set_state(DISCONNECTED); + show_gui(); } void Application::on_visibility_start(int screen_id) @@ -604,8 +753,8 @@ void Application::on_visibility_start(int screen_id) if (screen_id) { return; } - - hide_splash(0); + set_state(VISIBILITY); + hide_gui(); } void Application::on_disconnecting() @@ -621,6 +770,88 @@ Menu* Application::get_app_menu() return (*_app_menu)->ref(); } +void Application::show_info_layer() +{ + if (_info_layer->screen() || _state != VISIBILITY) { + return; + } + + _main_screen->attach_layer(*_info_layer); + _info_layer->set_info_mode(); + reset_sticky(); +} + +void Application::hide_info_layer() +{ + if (!_info_layer->screen()) { + return; + } + + _main_screen->detach_layer(*_info_layer); + reset_sticky(); +} + +#ifdef GUI_DEMO + +class TestResponce: public GUI::BoxResponse { +public: + virtual void response(int response) + { + DBG(0, "%d", response); + } + + virtual void aborted() + { + DBG(0, ""); + } +}; + + +TestResponce response_test; + +void Application::message_box_test() +{ + GUI::ButtonsList list(3); + list[0].id = 101; + list[0].text = "Yes"; + list[1].id = 102; + list[1].text = "No"; + list[2].id = 103; + list[2].text = "Don't Know"; + + + if (!_gui->message_box(GUI::QUESTION, "My question", list, &response_test)) { + DBG(0, "busy"); + } else { + show_gui(); + } +} + +#endif + +void Application::show_gui() +{ + if (_gui->screen() || !_gui->prepare_dialog()) { + return; + } + + hide_info_layer(); + release_capture(); + _gui->set_screen(_main_screen); + attach_gui_barriers(); +} + +void Application::hide_gui() +{ + if (!_gui->screen()) { + return; + } + + _gui->set_screen(NULL); + detach_gui_barriers(); + show_info_layer(); +} + void Application::do_command(int command) { switch (command) { @@ -650,6 +881,9 @@ void Application::do_command(int command) do_disconnect(); break; #endif + case APP_CMD_SHOW_GUI: + show_gui(); + break; } } @@ -810,7 +1044,7 @@ inline bool Application::is_sticky_trace_key(RedKey key) void Application::reset_sticky() { - _sticky_info.trace_is_on = !_splash_mode && _sys_key_intercept_mode; + _sticky_info.trace_is_on = (_state == VISIBILITY) && _sys_key_intercept_mode; _sticky_info.key_first_down = false; deactivate_interval_timer(*_sticky_info.timer); if (_sticky_info.sticky_mode) { @@ -821,12 +1055,11 @@ void Application::reset_sticky() } _sticky_info.sticky_mode = false; DBG(0, "OFF sticky"); - _gui_layer->set_sticky(false); + _info_layer->set_sticky(false); } _sticky_info.key_down = false; _sticky_info.key = REDKEY_INVALID; - } void Application::on_key_down(RedKey key) @@ -1228,27 +1461,6 @@ void Application::on_display_mode_change() _client.on_display_mode_change(); } -void Application::show_splash(int screen_id) -{ - if (screen_id != 0) { - return; - } - _splash_mode = true; - release_capture(); - ASSERT(!_sticky_info.trace_is_on); - (*_gui_layer).set_splash_mode(); -} - -void Application::hide_splash(int screen_id) -{ - if (screen_id != 0) { - return; - } - _splash_mode = false; - (*_gui_layer).set_info_mode(); - reset_sticky(); -} - uint32_t Application::get_mouse_mode() { return _client.get_mouse_mode(); @@ -1346,17 +1558,62 @@ void Application::send_hotkey_key_set(const HotkeySet& key_set) } } -static inline int str_to_port(const char *str) + +//controller interface begin + +bool Application::connect(const std::string& host, int port, int sport, const std::string& password) { - long port; - char *endptr; - port = strtol(str, &endptr, 0); - if (endptr != str + strlen(str) || port < 0 || port > 0xffff) { - return -1; + if (_state != DISCONNECTED) { + return false; } - return port; + _client.set_target(host, port, sport); + _client.set_password(password); + connect(); + return true; +} + +void Application::disconnect() +{ + do_disconnect(); +} + +void Application::quit() +{ + ProcessLoop::quit(SPICEC_ERROR_CODE_SUCCESS); +} + +void Application::hide_me() +{ + hide_gui(); +} + +bool Application::is_disconnect_allowed() +{ + return _gui_mode == GUI_MODE_FULL; +} + +const std::string& Application::get_host() +{ + return _client.get_host(); } +int Application::get_port() +{ + return _client.get_port(); +} + +int Application::get_sport() +{ + return _client.get_sport(); +} + +const std::string& Application::get_password() +{ + return _client.get_password(); +} + +//controller interface end + bool Application::set_channels_security(CmdLineParser& parser, bool on, char *val) { RedPeer::ConnectionOptions::Type option; @@ -1455,6 +1712,33 @@ bool Application::set_enable_channels(CmdLineParser& parser, bool enable, char * return true; } +void Application::register_channels() +{ + if (_enabled_channels[RED_CHANNEL_DISPLAY]) { + _client.register_channel_factory(DisplayChannel::Factory()); + } + + if (_enabled_channels[RED_CHANNEL_CURSOR]) { + _client.register_channel_factory(CursorChannel::Factory()); + } + + if (_enabled_channels[RED_CHANNEL_INPUTS]) { + _client.register_channel_factory(InputsChannel::Factory()); + } + + if (_enabled_channels[RED_CHANNEL_PLAYBACK]) { + _client.register_channel_factory(PlaybackChannel::Factory()); + } + + if (_enabled_channels[RED_CHANNEL_RECORD]) { + _client.register_channel_factory(RecordChannel::Factory()); + } + + if (_enabled_channels[RED_CHANNEL_TUNNEL]) { + _client.register_channel_factory(TunnelChannel::Factory()); + } +} + bool Application::process_cmd_line(int argc, char** argv) { std::string host; @@ -1477,6 +1761,15 @@ bool Application::process_cmd_line(int argc, char** argv) SPICE_OPT_CANVAS_TYPE, }; + if (argc == 1) { + _gui_mode = GUI_MODE_FULL; + register_channels(); + _main_screen->show(true, NULL); + return true; + } + + _gui_mode = GUI_MODE_ACTIVE_SESSION; + CmdLineParser parser("Spice client", false); parser.add(SPICE_OPT_HOST, "host", "spice server address", "host", true, 'h'); @@ -1504,13 +1797,9 @@ bool Application::process_cmd_line(int argc, char** argv) parser.add(SPICE_OPT_CANVAS_TYPE, "canvas-type", "set rendering canvas", "canvas_type", true); parser.set_multi(SPICE_OPT_CANVAS_TYPE, ','); - _peer_con_opt[RED_CHANNEL_MAIN] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_DISPLAY] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_INPUTS] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_CURSOR] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_PLAYBACK] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_RECORD] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _peer_con_opt[RED_CHANNEL_TUNNEL] = RedPeer::ConnectionOptions::CON_OP_INVALID; + for (int i = RED_CHANNEL_MAIN; i < RED_CHANNEL_END; i++) { + _peer_con_opt[i] = RedPeer::ConnectionOptions::CON_OP_INVALID; + } parser.begin(argc, argv); @@ -1626,31 +1915,11 @@ bool Application::process_cmd_line(int argc, char** argv) return false; } - if (_enabled_channels[RED_CHANNEL_DISPLAY]) { - _client.register_channel_factory(DisplayChannel::Factory()); - } - - if (_enabled_channels[RED_CHANNEL_CURSOR]) { - _client.register_channel_factory(CursorChannel::Factory()); - } - - if (_enabled_channels[RED_CHANNEL_INPUTS]) { - _client.register_channel_factory(InputsChannel::Factory()); - } - - if (_enabled_channels[RED_CHANNEL_PLAYBACK]) { - _client.register_channel_factory(PlaybackChannel::Factory()); - } - - if (_enabled_channels[RED_CHANNEL_RECORD]) { - _client.register_channel_factory(RecordChannel::Factory()); - } - - if (_enabled_channels[RED_CHANNEL_TUNNEL]) { - _client.register_channel_factory(TunnelChannel::Factory()); - } + register_channels(); - _client.init(host.c_str(), port, sport, password.c_str(), auto_display_res); + _client.set_target(host, port, sport); + _client.set_password(password); + _client.set_auto_display_res(auto_display_res); if (full_screen) { enter_full_screen(); diff --git a/client/application.h b/client/application.h index c2b81e6c..5c607025 100644 --- a/client/application.h +++ b/client/application.h @@ -30,11 +30,18 @@ class RedScreen; class Application; class ScreenLayer; -class GUILayer; +class InfoLayer; class InputsHandler; class Monitor; class CmdLineParser; class Menu; +class GUI; +class GUITimer; +class GUIBarrier; + +#ifdef GUI_DEMO +class TestTimer; +#endif class ConnectedEvent: public Event { @@ -112,12 +119,28 @@ typedef struct StickyInfo { typedef std::list<KeyHandler*> KeyHandlersStack; +typedef std::list<GUIBarrier*> GUIBarriers; class Application : public ProcessLoop, public Platform::EventListener, public Platform::DisplayModeListner, public CommandTarget { public: + + enum State { + DISCONNECTED, + CONNECTING, + CONNECTED, + VISIBILITY, + DISCONECTING, + }; + + enum GuiMode { + GUI_MODE_FULL, + GUI_MODE_ACTIVE_SESSION, + GUI_MODE_MINIMAL, + }; + Application(); virtual ~Application(); @@ -158,8 +181,6 @@ public: void exit_full_screen(); bool toggle_full_screen(); void minimize(); - void show_splash(int screen_id); - void hide_splash(int screen_id); void set_title(std::wstring& title); void hide(); void show(); @@ -172,12 +193,32 @@ public: Menu* get_app_menu(); virtual void do_command(int command); + + //controller interface begin + bool connect(const std::string& host, int port, int sport, const std::string& password); + void disconnect(); + void quit(); + void hide_me(); + void beep(); + bool is_disconnect_allowed(); + + const std::string& get_host(); + int get_port(); + int get_sport(); + const std::string& get_password(); + //controller interface end + +#ifdef GUI_DEMO + void message_box_test(); +#endif + static int main(int argc, char** argv, const char* version_str); private: bool set_channels_security(CmdLineParser& parser, bool on, char *val); bool set_enable_channels(CmdLineParser& parser, bool enable, char *val); bool set_canvas_option(CmdLineParser& parser, char *val); + void register_channels(); bool process_cmd_line(int argc, char** argv); void abort(); void init_menu(); @@ -185,6 +226,7 @@ private: bool release_capture(); bool do_connect(); bool do_disconnect(); + void set_state(State); void restore_screens_size(); Monitor* find_monitor(int id); @@ -208,6 +250,16 @@ private: void do_on_key_up(RedKey key); void __remove_key_handler(KeyHandler& handler); + void show_info_layer(); + void hide_info_layer(); + void attach_gui_barriers(); + void detach_gui_barriers(); + void show_gui(); + void hide_gui(); + void create_gui_barrier(RedScreen& screen, int id); + void destroyed_gui_barrier(int id); + void destroyed_gui_barriers(); + // returns the press value before operation (i.e., if it was already pressed) bool press_key(RedKey key); bool unpress_key(RedKey key); @@ -238,17 +290,25 @@ private: int _num_keys_pressed; HotKeys _hot_keys; CommandsMap _commands_map; - std::auto_ptr<GUILayer> _gui_layer; + std::auto_ptr<InfoLayer> _info_layer; KeyHandler* _key_handler; KeyHandlersStack _key_handlers; MouseHandler* _mouse_handler; const MonitorsList* _monitors; std::wstring _title; - bool _splash_mode; bool _sys_key_intercept_mode; StickyInfo _sticky_info; std::vector<int> _canvas_types; AutoRef<Menu> _app_menu; + std::auto_ptr<GUI> _gui; + AutoRef<GUITimer> _gui_timer; + GUIBarriers _gui_barriers; + GuiMode _gui_mode; +#ifdef GUI_DEMO + AutoRef<TestTimer> _gui_test_timer; +#endif + + State _state; }; #endif diff --git a/client/gui/gui.cpp b/client/gui/gui.cpp new file mode 100644 index 00000000..1bbf3c42 --- /dev/null +++ b/client/gui/gui.cpp @@ -0,0 +1,1354 @@ +#include "common.h" + +#include <limits.h> +#include <stdlib.h> + +#include "gui.h" +#include "screen.h" +#include "utils.h" +#include "debug.h" +#include "red_pixmap_cairo.h" +#include "resource_provider.h" + +#include "CEGUISystem.h" +#include "CEGUIWindowManager.h" +#include "CEGUIWindow.h" +#include "CEGUIFontManager.h" +#include "CEGUIExceptions.h" +#include "CEGUIScheme.h" +#include "elements/CEGUIPushButton.h" +#include "elements/CEGUIEditbox.h" +#include "elements/CEGUITabControl.h" +#include "elements/CEGUIListbox.h" +#include "elements/CEGUIListboxTextItem.h" + +#define MAIN_GUI_WIDTH 640 +#define MAIN_GUI_HEIGHT 480 +#define BUTTON_WIDTH 90 +#define BUTTON_HEIGHT 22 +#define GUI_SPACE 10 +#define GUI_LABEL_WIDTH 65 +#define GUI_LABEL_HEIGHT 22 +#define GUI_PORT_WIDTH 50 +#define GUI_TEXT_BOX_HEIGHT GUI_LABEL_HEIGHT + +#define LOGIN_DIALOG_WIDTH 400 +#define LOGIN_DIALOG_HEIGHT 300 +#define LOGIN_DIALOG_V_START 150 + +#define CONNECTING_DIALOG_WIDTH 400 +#define CONNECTING_DIALOG_HEIGHT 300 + +#define MESSAGE_BOX_WIDTH 380 +#define MESSAGE_BOX_HEIGHT 150 + +#define CONNECT_OPTION_DIALOG_WIDTH LOGIN_DIALOG_WIDTH +#define CONNECT_OPTION_DIALOG_HEIGHT LOGIN_DIALOG_HEIGHT + +static inline void set_win_pos(CEGUI::Window* win, int x, int y) +{ + win->setPosition(CEGUI::UVector2(cegui_absdim((float)x), cegui_absdim((float)y))); +} + +static inline void set_win_size(CEGUI::Window* win, int width, int height) +{ + win->setSize(CEGUI::UVector2(cegui_absdim((float)width), cegui_absdim((float)height))); +} + +static void add_bottom_button(CEGUI::Window* parent, const char *str, + CEGUI::SubscriberSlot subscriber, + int& position) +{ + CEGUI::Window* button = CEGUI::WindowManager::getSingleton().createWindow("TaharezLook/Button"); + int win_width = (int)parent->getWidth().asAbsolute(1); + int win_height = (int)parent->getHeight().asAbsolute(1); + int y_pos = win_height - BUTTON_HEIGHT - GUI_SPACE; + + position += BUTTON_WIDTH + GUI_SPACE; + set_win_pos(button, win_width - position, y_pos); + set_win_size(button, BUTTON_WIDTH, BUTTON_HEIGHT); + button->setText(str); + button->subscribeEvent(CEGUI::PushButton::EventClicked, subscriber); + button->setInheritsAlpha(false); + //button->setTooltipText("tool tip"); + parent->addChildWindow(button); +} + +#ifdef GUI_DEMO + +class SampleTabFactory: public GUI::TabFactory { +public: + + class MyListItem : public CEGUI::ListboxTextItem { + public: + MyListItem (const CEGUI::String& text) + : CEGUI::ListboxTextItem(text) + { + setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush"); + } + }; + + class SampleTab: public GUI::Tab { + public: + SampleTab(int id, int width, int height) + { + string_printf(_name, "SampleTab-%d", id); + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + _root_window = winMgr.createWindow("TaharezLook/StaticText"); + set_win_pos(_root_window, 0, 0); + set_win_size(_root_window, width, height); + _root_window->setText("Tab of SampleTabFactory"); + + if (id != 1) { + return; + } + + _list = (CEGUI::Listbox*)winMgr.createWindow("TaharezLook/Listbox"); + set_win_pos(_list, 10, 10); + set_win_size(_list, 200, 100); + _list->setMultiselectEnabled(false); + _list->addItem(new MyListItem("Item-1")); + _list->addItem(new MyListItem("Item-2")); + _list->addItem(new MyListItem("Item-3")); + _list->addItem(new MyListItem("Item-4")); + _list->addItem(new MyListItem("Item-5")); + _list->addItem(new MyListItem("Item-6")); + + _list->subscribeEvent(CEGUI::Listbox::EventSelectionChanged, + CEGUI::Event::Subscriber(&SampleTab::handle_list_selection, + this)); + + _root_window->addChildWindow(_list); + + _label = winMgr.createWindow("TaharezLook/StaticText"); + set_win_pos(_label, 220, 10); + set_win_size(_label, 200, 22); + _root_window->addChildWindow(_label); + + _list->setItemSelectState((size_t)0, true); + + } + + virtual ~SampleTab() + { + CEGUI::WindowManager::getSingleton().destroyWindow(_root_window); + } + + virtual CEGUI::Window& get_root_window() + { + return *_root_window; + } + + virtual const std::string& get_name() + { + return _name; + } + + bool handle_list_selection(const CEGUI::EventArgs& e) + { + DBG(0, "changed"); + CEGUI::ListboxItem* selection = _list->getFirstSelectedItem(); + if (selection) { + _label->setText(selection->getText()); + return true; + } + + if (_list->getItemCount()) { + _list->setItemSelectState((size_t)_list->getItemCount() - 1, true); + } + + return true; + } + + private: + std::string _name; + CEGUI::Window* _root_window; + CEGUI::Listbox* _list; + CEGUI::Window* _label; + }; + + SampleTabFactory(int id) + : GUI::TabFactory(id) + , _id (id) + { + } + + GUI::Tab* create_tab(bool connected, int width, int height) + { + if (!connected && _id % 2 == 0) { + return NULL; + } + return new SampleTab(_id, width, height); + } + +private: + int _id; +}; + +#endif + +class GUI::Dialog { +public: + Dialog(GUI& gui, bool close_on_message_click = false) + : _gui (gui) + , _root (NULL) + , _message_box (NULL) + , _box_response (NULL) + , _close_on_message_click (close_on_message_click) + { + } + + virtual ~Dialog() + { + if (gui_system().getGUISheet() == _root) { + gui_system().setGUISheet(NULL); + } + + CEGUI::WindowManager::getSingleton().destroyWindow(_root); + } + + CEGUI::Window& root_window() { return *_root;} + Application& application() { return _gui.get_application();} + CEGUI::System& gui_system() { return _gui.gui_system();} + + void set_dialog(Dialog* dialog) { _gui.set_dialog(dialog);} + TabFactorys& get_factoris() { return _gui._tab_factorys;} + + bool message_box(MessageType type, const char *text, const ButtonsList& buttons, + BoxResponse* response_handler); + void error_box(const char* error_message); + + void pre_destroy(); + +private: + void handle_message_click(int id); + void set_opaque(CEGUI::Window* win); + + void dim(); + void undim(); + +protected: + GUI& _gui; + CEGUI::Window* _root; + +private: + class BottonAction { + public: + BottonAction(Dialog& dialog, int id) + : _dialog (dialog) + , _id (id) + { + } + + bool operator () (const CEGUI::EventArgs& e) + { + _dialog.handle_message_click(_id); + return true; + } + + private: + Dialog& _dialog; + int _id; + }; + + CEGUI::Window* _message_box; + BoxResponse* _box_response; + + class UndimInfo { + public: + UndimInfo(const CEGUI::String& name, float alpha, bool inherits) + : _name (name) + , _alpha (alpha) + , _inherits (inherits) + { + } + + void restore() + { + try { + CEGUI::Window* win = CEGUI::WindowManager::getSingleton().getWindow(_name); + win->setAlpha(_alpha); + win->setInheritsAlpha(_inherits); + } catch (...) { + } + } + + private: + CEGUI::String _name; + float _alpha; + bool _inherits; + + }; + + std::list<UndimInfo*> _undim_info_list; + bool _close_on_message_click; +}; + + +void GUI::Dialog::set_opaque(CEGUI::Window* win) +{ + float alpha = win->getAlpha(); + bool inherits = win->inheritsAlpha(); + + if (alpha != 1 || !inherits ) { + _undim_info_list.push_back(new UndimInfo(win->getName(), alpha, inherits)); + win->setInheritsAlpha(true); + win->setAlpha(1); + } + + size_t child_count = win->getChildCount(); + for (size_t i = 0; i < child_count; ++i) { + CEGUI::Window* child = win->getChildAtIdx(i); + set_opaque(child); + } +} + +void GUI::Dialog::dim() +{ + set_opaque(_root); + _root->setAlpha(0.5); +} + +void GUI::Dialog::undim() +{ + while (!_undim_info_list.empty()) { + UndimInfo* inf = _undim_info_list.front(); + _undim_info_list.pop_front(); + inf->restore(); + delete inf; + } + _root->setAlpha(1); +} + +bool GUI::Dialog::message_box(MessageType type, const char *text, const ButtonsList& buttons, + BoxResponse* response_handler) +{ + if (_message_box) { + return false; + } + + try { + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + + CEGUI::Window* wnd = _message_box = winMgr.createWindow("TaharezLook/StaticText"); + int x_pos = (MAIN_GUI_WIDTH - MESSAGE_BOX_WIDTH) / 2; + int y_pos = (MAIN_GUI_HEIGHT - MESSAGE_BOX_HEIGHT) / 3; + set_win_pos(wnd, x_pos, y_pos); + set_win_size(wnd, MESSAGE_BOX_WIDTH, MESSAGE_BOX_HEIGHT); + wnd->setModalState(true); + wnd->setInheritsAlpha(false); + dim(); + _root->addChildWindow(wnd); + + CEGUI::Window* text_wnd = winMgr.createWindow("TaharezLook/StaticText"); + set_win_pos(text_wnd, GUI_SPACE, GUI_SPACE); + set_win_size(text_wnd, MESSAGE_BOX_WIDTH - 2 * GUI_SPACE, + MESSAGE_BOX_HEIGHT - 3 * GUI_SPACE - BUTTON_HEIGHT); + text_wnd->setProperty("FrameEnabled", "false"); + text_wnd->setProperty("HorzFormatting", "WordWrapLeftAligned"); + text_wnd->setProperty("VertFormatting", "TopAligned"); + text_wnd->setText(text); + //text_wnd->getTextRenderArea(); + wnd->addChildWindow(text_wnd); + + x_pos = 0; + + for (unsigned int i = 0; i < buttons.size(); i++) { + add_bottom_button(wnd, buttons[i].text, + CEGUI::Event::Subscriber(BottonAction(*this, buttons[i].id)), + x_pos); + } + + _box_response = response_handler; + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + throw; + } + + return true; +} + +class BoxResponseEvent: public Event { +public: + BoxResponseEvent(GUI::BoxResponse* box_response, int id) + : _box_response (box_response) + , _id (id) + { + } + + virtual void response(AbstractProcessLoop &events_loop) + { + _box_response->response(_id); + } + +private: + GUI::BoxResponse* _box_response; + int _id; +}; + +class BoxAbortEvent: public Event { +public: + BoxAbortEvent(GUI::BoxResponse* box_response) + : _box_response (box_response) + { + } + + virtual void response(AbstractProcessLoop &events_loop) + { + _box_response->aborted(); + } + +private: + GUI::BoxResponse* _box_response; +}; + +void GUI::Dialog::handle_message_click(int id) +{ + DBG(0, ""); + ASSERT(_message_box); + _message_box->setModalState(false); + _root->removeChildWindow(_message_box); + CEGUI::Window *win = _message_box; + _message_box = NULL; + CEGUI::WindowManager::getSingleton().destroyWindow(win); + undim(); + if (_box_response) { + AutoRef<BoxResponseEvent> event(new BoxResponseEvent(_box_response, id)); + _box_response = NULL; + application().push_event(*event); + } + + if (_close_on_message_click) { + application().hide_me(); + } +} + +void GUI::Dialog::pre_destroy() + { + if (_box_response) { + AutoRef<BoxAbortEvent> event(new BoxAbortEvent(_box_response)); + _box_response = NULL; + application().push_event(*event); + } +} + +void GUI::Dialog::error_box(const char* message) +{ + ASSERT(message && strlen(message) > 0); + DBG(0, "%s", message); + ButtonsList list(1); + list[0].id = 0; + list[0].text = res_get_string(STR_BUTTON_OK); + message_box(INFO, message, list, NULL); +} + +class TabDialog : public GUI::Dialog { +public: + TabDialog(GUI& gui, bool connected); + virtual ~TabDialog(); + +protected: + CEGUI::Window* _main_win; + +private: + typedef std::list<GUI::Tab*> Tabs; + Tabs _tabs; +}; + +class MessageDialog : public GUI::Dialog { +public: + MessageDialog(GUI& gui); + virtual ~MessageDialog() {} +}; + +class LoginDialog : public GUI::Dialog { +public: + LoginDialog(GUI& guip); + + bool handle_connect(const CEGUI::EventArgs& e); + bool handle_quit(const CEGUI::EventArgs& e); + bool handle_options(const CEGUI::EventArgs& e); + +private: + static void set_port_text(CEGUI::Window* win, int port); + +private: + CEGUI::Window* _host_box; + CEGUI::Window* _port_box; + CEGUI::Window* _sport_box; + CEGUI::Window* _pass_box; +}; + +class PreLoginDialog: public TabDialog { +public: + PreLoginDialog(GUI& gui); + + bool handle_back(const CEGUI::EventArgs& e); + bool handle_quit(const CEGUI::EventArgs& e); +}; + +PreLoginDialog::PreLoginDialog(GUI& gui) + : TabDialog(gui, false) +{ + try { + + int position = 0; + add_bottom_button(_main_win, res_get_string(STR_BUTTON_BACK), + CEGUI::Event::Subscriber(&PreLoginDialog::handle_back, this), + position); + add_bottom_button(_main_win, res_get_string(STR_BUTTON_QUIT), + CEGUI::Event::Subscriber(&PreLoginDialog::handle_quit, this), + position); + + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + } catch (...) { + throw; + } +} + +bool PreLoginDialog::handle_back(const CEGUI::EventArgs& e) +{ + set_dialog(new LoginDialog(_gui)); + return true; +} + +bool PreLoginDialog::handle_quit(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().quit(); + return true; +} + +MessageDialog::MessageDialog(GUI& gui) + : GUI::Dialog(gui, true) +{ + try { + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + _root = winMgr.createWindow("DefaultWindow"); + set_win_size(_root, MAIN_GUI_WIDTH, MAIN_GUI_HEIGHT); + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + throw; + } +} + +LoginDialog::LoginDialog(GUI& gui) + : GUI::Dialog(gui) +{ + try { + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + _root = winMgr.createWindow("DefaultWindow"); + + CEGUI::Window* wnd = winMgr.createWindow("TaharezLook/StaticText"); + int x_pos = (MAIN_GUI_WIDTH - LOGIN_DIALOG_WIDTH) / 2; + int y_pos = (MAIN_GUI_HEIGHT - LOGIN_DIALOG_HEIGHT) / 3; + set_win_pos(wnd, x_pos, y_pos); + set_win_size(wnd, LOGIN_DIALOG_WIDTH, LOGIN_DIALOG_HEIGHT); + _root->addChildWindow(wnd); + + CEGUI::Window* host_label = winMgr.createWindow("TaharezLook/StaticText"); + host_label->setText(res_get_string(STR_LABEL_HOST)); + host_label->setProperty("FrameEnabled", "false"); + host_label->setProperty("BackgroundEnabled", "false"); + set_win_pos(host_label, GUI_SPACE, LOGIN_DIALOG_V_START); + set_win_size(host_label, GUI_LABEL_WIDTH, GUI_LABEL_HEIGHT); + + wnd->addChildWindow(host_label); + + _host_box = winMgr.createWindow("TaharezLook/Editbox"); + set_win_pos(_host_box, GUI_LABEL_WIDTH + GUI_SPACE, LOGIN_DIALOG_V_START); + int width = LOGIN_DIALOG_WIDTH - GUI_LABEL_WIDTH - 2 * GUI_SPACE; + set_win_size(_host_box, width, GUI_LABEL_HEIGHT); + _host_box->setText(application().get_host()); + wnd->addChildWindow(_host_box); + + CEGUI::Window* port_label = winMgr.createWindow("TaharezLook/StaticText"); + port_label->setText(res_get_string(STR_LABEL_PORT)); + port_label->setProperty("FrameEnabled", "false"); + port_label->setProperty("BackgroundEnabled", "false"); + y_pos = LOGIN_DIALOG_V_START + GUI_LABEL_HEIGHT + GUI_SPACE; + set_win_pos(port_label, GUI_SPACE, y_pos); + set_win_size(port_label, GUI_LABEL_WIDTH, GUI_LABEL_HEIGHT); + wnd->addChildWindow(port_label); + + _port_box = winMgr.createWindow("TaharezLook/Editbox"); + set_win_pos(_port_box, GUI_SPACE + GUI_LABEL_WIDTH, y_pos); + set_win_size(_port_box, GUI_PORT_WIDTH, GUI_TEXT_BOX_HEIGHT); + set_port_text(_port_box, application().get_port()); + wnd->addChildWindow(_port_box); + + _sport_box = winMgr.createWindow("TaharezLook/Editbox"); + x_pos = LOGIN_DIALOG_WIDTH - GUI_SPACE - GUI_PORT_WIDTH; + set_win_pos(_sport_box, x_pos, y_pos); + set_win_size(_sport_box, GUI_PORT_WIDTH, GUI_TEXT_BOX_HEIGHT); + set_port_text(_sport_box, application().get_sport()); + wnd->addChildWindow(_sport_box); + + CEGUI::Window* sport_label = winMgr.createWindow("TaharezLook/StaticText"); + sport_label->setText(res_get_string(STR_LABEL_SPORT)); + sport_label->setProperty("FrameEnabled", "false"); + sport_label->setProperty("BackgroundEnabled", "false"); + x_pos -= GUI_LABEL_WIDTH; + set_win_pos(sport_label, x_pos, y_pos); + set_win_size(sport_label, GUI_LABEL_WIDTH, GUI_LABEL_HEIGHT); + wnd->addChildWindow(sport_label); + + CEGUI::Window* label = winMgr.createWindow("TaharezLook/StaticText"); + label->setText(res_get_string(STR_LABEL_PASSWORD)); + label->setProperty("FrameEnabled", "false"); + label->setProperty("BackgroundEnabled", "false"); + y_pos += GUI_LABEL_HEIGHT + GUI_SPACE; + set_win_pos(label, GUI_SPACE, y_pos); + set_win_size(label, GUI_LABEL_WIDTH, GUI_LABEL_HEIGHT); + wnd->addChildWindow(label); + + _pass_box = winMgr.createWindow("TaharezLook/Editbox"); + x_pos = GUI_LABEL_WIDTH + GUI_SPACE; + set_win_pos(_pass_box, x_pos, y_pos); + width = LOGIN_DIALOG_WIDTH - GUI_LABEL_WIDTH - 2 * GUI_SPACE; + set_win_size(_pass_box, width, GUI_TEXT_BOX_HEIGHT); + ((CEGUI::Editbox*)_pass_box)->setTextMasked(true); + ((CEGUI::Editbox*)_pass_box)->setMaskCodePoint(/*0x000026AB*/ 0x00002022 ); + _pass_box->setText(application().get_password().c_str()); + wnd->addChildWindow(_pass_box); + + x_pos = 0; + add_bottom_button(wnd, + res_get_string(STR_BUTTON_CONNECT), + CEGUI::Event::Subscriber(&LoginDialog::handle_connect, this), + x_pos); + add_bottom_button(wnd, + res_get_string(STR_BUTTON_QUIT), + CEGUI::Event::Subscriber(&LoginDialog::handle_quit, this), + x_pos); + add_bottom_button(wnd, + res_get_string(STR_BUTTON_OPTIONS), + CEGUI::Event::Subscriber(&LoginDialog::handle_options, this), + x_pos); + + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + throw; + } +} + +bool LoginDialog::handle_connect(const CEGUI::EventArgs& e) +{ + const char* host_name = _host_box->getText().c_str(); + + if (strlen(host_name) == 0) { + error_box(res_get_string(STR_MESG_MISSING_HOST_NAME)); + return true; + } + int port = -1; + int sport = -1; + + const char* port_str = _port_box->getText().c_str(); + if (strlen(port_str) != 0 && (port = str_to_port(port_str)) == -1) { + error_box(res_get_string(STR_MESG_INVALID_PORT)); + return true; + } + + const char* sport_str = _sport_box->getText().c_str(); + if (strlen(sport_str) != 0 && (sport = str_to_port(sport_str)) == -1) { + error_box(res_get_string(STR_MESG_INVALID_SPORT)); + return true; + } + + if (port == sport && port == -1) { + error_box(res_get_string(STR_MESG_MISSING_PORT)); + return true; + } + + DBG(0, "host %s port %d sport %d", host_name, port, sport); + application().connect(host_name, port, sport, _pass_box->getText().c_str()); + return true; +} + +bool LoginDialog::handle_quit(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().quit(); + return true; +} + +bool LoginDialog::handle_options(const CEGUI::EventArgs& e) +{ + set_dialog(new PreLoginDialog(_gui)); + return true; +} + +void LoginDialog::set_port_text(CEGUI::Window* win, int port) +{ + if (port == -1) { + win->setText(""); + return; + } + + char port_string[25]; + sprintf(port_string, "%d", port); + win->setText(port_string); +} + +class ConnectingDialog : public GUI::Dialog { +public: + ConnectingDialog(GUI& gui); + +private: + bool handle_cancel(const CEGUI::EventArgs& e); + bool handle_quit(const CEGUI::EventArgs& e); +}; + +bool ConnectingDialog::handle_cancel(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().disconnect(); + return true; +} + +bool ConnectingDialog::handle_quit(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().quit(); + return true; +} + +ConnectingDialog::ConnectingDialog(GUI& gui) + : GUI::Dialog(gui) +{ + try { + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + _root = winMgr.createWindow("DefaultWindow"); + + CEGUI::Window* wnd = winMgr.createWindow("TaharezLook/StaticText"); + int x_pos = (MAIN_GUI_WIDTH - CONNECTING_DIALOG_WIDTH) / 2; + int y_pos = (MAIN_GUI_HEIGHT - CONNECTING_DIALOG_HEIGHT) / 2; + set_win_pos(wnd, x_pos, y_pos); + set_win_size(wnd, CONNECTING_DIALOG_WIDTH, CONNECTING_DIALOG_HEIGHT); + CEGUI::String text(res_get_string(STR_MESG_CONNECTING)); + wnd->setText(text + " " + application().get_host()); + wnd->setProperty("HorzFormatting", "LeftAligned"); + wnd->setProperty("VertFormatting", "TopAligned"); + _root->addChildWindow(wnd); + + x_pos = 0; + + add_bottom_button(wnd, res_get_string(STR_BUTTON_CANCEL), + CEGUI::Event::Subscriber(&ConnectingDialog::handle_cancel, this), + x_pos); + + if (_gui.is_disconnect_allowed()) { + add_bottom_button(wnd, res_get_string(STR_BUTTON_QUIT), + CEGUI::Event::Subscriber(&ConnectingDialog::handle_quit, this), + x_pos); + } + + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + throw; + } +} + +TabDialog::TabDialog(GUI& gui, bool connected) + : GUI::Dialog(gui) +{ + CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton(); + + try { + _root = winMgr.createWindow("DefaultWindow"); + + CEGUI::Window* wnd = _main_win = winMgr.createWindow("TaharezLook/StaticText"); + set_win_pos(wnd, 0, 0); + set_win_size(wnd, MAIN_GUI_WIDTH, MAIN_GUI_HEIGHT); + wnd->setAlpha(0.5); + _root->addChildWindow(wnd); + + CEGUI::TabControl* tab_ctrl; + tab_ctrl = static_cast<CEGUI::TabControl*>(winMgr.createWindow("TaharezLook/TabControl")); + set_win_pos(tab_ctrl, GUI_SPACE, GUI_SPACE); + int tab_width = MAIN_GUI_WIDTH - GUI_SPACE * 2; + int tab_height = MAIN_GUI_HEIGHT - GUI_SPACE * 3 - BUTTON_HEIGHT; + set_win_size(tab_ctrl, tab_width, tab_height); + tab_ctrl->setInheritsAlpha(false); + tab_ctrl->setTabHeight(cegui_absdim(22)); + tab_ctrl->setTabTextPadding(cegui_absdim(10)); + tab_height = (int)tab_ctrl->getTabHeight().asAbsolute(1); + wnd->addChildWindow(tab_ctrl); + + GUI::TabFactorys& _tab_factorys = get_factoris(); + GUI::TabFactorys::iterator iter = _tab_factorys.begin(); + + int tab_content_width = MAIN_GUI_WIDTH - GUI_SPACE * 4; + int tab_content_height = MAIN_GUI_HEIGHT - GUI_SPACE * 5 - BUTTON_HEIGHT - tab_height; + + for (; iter != _tab_factorys.end(); iter++) { + + GUI::Tab* tab = (*iter)->create_tab(connected, tab_content_width, + tab_content_height); + + if (!tab) { + continue; + } + + _tabs.push_front(tab); + CEGUI::Window* gui_sheet = winMgr.createWindow("DefaultGUISheet"); + gui_sheet->setText(tab->get_name()); + set_win_pos(gui_sheet, GUI_SPACE, GUI_SPACE); + set_win_size(gui_sheet, tab_content_width, tab_content_height); + tab_ctrl->addTab(gui_sheet); + CEGUI::Window& tab_window = tab->get_root_window(); + tab_window.setDestroyedByParent(false); + gui_sheet->addChildWindow(&tab_window); + } + + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + } catch (...) { + throw; + } +} + +TabDialog::~TabDialog() +{ + while (!_tabs.empty()) { + GUI::Tab* tab = *_tabs.begin(); + _tabs.pop_front(); + delete tab; + } +} + +class SettingsDialog : public TabDialog { +public: + SettingsDialog(GUI& gui); + + bool handle_close(const CEGUI::EventArgs& e); + bool handle_quit(const CEGUI::EventArgs& e); + bool handle_disconnect(const CEGUI::EventArgs& e); +}; + +bool SettingsDialog::handle_close(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().hide_me(); + return true; +} + +bool SettingsDialog::handle_quit(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().quit(); + return true; +} + +bool SettingsDialog::handle_disconnect(const CEGUI::EventArgs& e) +{ + DBG(0, ""); + application().disconnect(); + return true; +} + +SettingsDialog::SettingsDialog(GUI& gui) + : TabDialog(gui, true) +{ + try { + + int position = 0; + add_bottom_button(_main_win, res_get_string(STR_BUTTON_CLOSE), + CEGUI::Event::Subscriber(&SettingsDialog::handle_close, this), + position); + add_bottom_button(_main_win, res_get_string(STR_BUTTON_QUIT), + CEGUI::Event::Subscriber(&SettingsDialog::handle_quit, this), + position); + + if (_gui.is_disconnect_allowed()) { + add_bottom_button(_main_win, res_get_string(STR_BUTTON_DISCONNECT), + CEGUI::Event::Subscriber(&SettingsDialog::handle_disconnect, this), + position); + } + + } catch (CEGUI::Exception& e) { + LOG_ERROR("Exception: %s", e.getMessage().c_str()); + } catch (...) { + throw; + } +} + +GUI::GUI(Application& app, Application::State state) + : ScreenLayer (SCREEN_LAYER_GUI, false) + , _app (app) + , _state (state) + , _pixmap (new RedPixmapCairo(MAIN_GUI_WIDTH, MAIN_GUI_HEIGHT, RedPixmap::RGB32, true, NULL, + NULL)) + , _renderer (new CEGUI::SoftRenderer(_pixmap->get_data(), MAIN_GUI_WIDTH, MAIN_GUI_HEIGHT, + _pixmap->get_stride())) + , _gui_system (new CEGUI::System(_renderer, new CEGUIResourceProvider())) + , _dialog (NULL) + , _prev_time (Platform::get_monolithic_time()) + +{ + LOG_INFO(""); + init_cegui(); +#ifdef GUI_DEMO + register_tab_factory(*(new SampleTabFactory(2))); + register_tab_factory(*(new SampleTabFactory(3))); + register_tab_factory(*(new SampleTabFactory(1))); + register_tab_factory(*(new SampleTabFactory(4))); + register_tab_factory(*(new SampleTabFactory(5))); +#endif + create_dialog(); +} + +GUI::~GUI() +{ + delete _dialog; + detach(); + delete _gui_system; + delete _renderer; + delete _pixmap; +} + +void GUI::init_cegui() +{ + CEGUI::SchemeManager::getSingleton().loadScheme("TaharezLook.scheme"); + _gui_system->setDefaultMouseCursor("TaharezLook", "MouseArrow"); + _gui_system->setDefaultTooltip("TaharezLook/Tooltip"); + + CEGUI::String font_name("DejaVuSans-10"); + CEGUI::Font* font; + + if (!CEGUI::FontManager::getSingleton().isFontPresent(font_name)) { + font = CEGUI::FontManager::getSingleton().createFont(font_name + ".font"); + } else { + font = CEGUI::FontManager::getSingleton().getFont(font_name); + } + + //font->setProperty("PointSize", "10"); + CEGUI::System::getSingleton().setDefaultFont(font); +} + +bool comp_factorys(GUI::TabFactory* f1, GUI::TabFactory* f2) +{ + return f1->get_order() < f2->get_order(); +} + +void GUI::register_tab_factory(TabFactory& factory) +{ + TabFactorys::iterator iter = _tab_factorys.begin(); + + for (; iter != _tab_factorys.end(); iter++) { + if ((*iter) == &factory) { + return; + } + } + + _tab_factorys.push_back(&factory); + _tab_factorys.sort(comp_factorys); +} + +void GUI::unregister_tab_factory(TabFactory& factory) +{ + TabFactorys::iterator iter = _tab_factorys.begin(); + + for (; iter != _tab_factorys.end(); iter++) { + if ((*iter) == &factory) { + _tab_factorys.erase(iter); + return; + } + } +} + +void GUI::detach() +{ + if (!screen()) { + return; + } + clear_area(); + screen()->detach_layer(*this); + set_dialog(NULL); +} + +void GUI::conditional_update() +{ + if (_gui_system->isRedrawRequested()) { + invalidate(); + } +} + +void GUI::update_layer_area() +{ + if (!_dialog || !screen()) { + clear_area(); + return; + } + Point screen_size = screen()->get_size(); + + int dx = (screen_size.x - MAIN_GUI_WIDTH) / 2; + int dy = (screen_size.y - MAIN_GUI_HEIGHT) / 2; + + DBG(0, "screen_size.x = %d screen_size.y = %d", screen_size.x, screen_size.y); + + _pixmap->set_origin(-dx, -dy); + CEGUI::Window& root = _dialog->root_window(); + QRegion regin; + region_init(®in); + + for (unsigned int i = 0; i < root.getChildCount(); i++) { + + CEGUI::Window* child = root.getChildAtIdx(i); + if (!child->isVisible()) { + continue; + } + + CEGUI::Rect area = child->getPixelRect(); + Rect r; + r.left = (int)area.d_left + dx; + r.right = (int)area.d_right + dx; + r.top = (int)area.d_top + dy; + r.bottom = (int)area.d_bottom + dy; + region_add(®in, &r); + + } + set_area(regin); + region_destroy(®in); +} + +void GUI::copy_pixels(const QRegion& dest_region, RedDrawable& dest) +{ + if (region_is_empty(&dest_region)) { + return; + } + + for (int i = 0; i < (int)dest_region.num_rects; i++) { + Rect* r = &dest_region.rects[i]; + _pixmap->copy_pixels(dest, r->left, r->top, *r); + } + + _gui_system->renderGUI(); + for (int i = 0; i < (int)dest_region.num_rects; i++) { + Rect* r = &dest_region.rects[i]; + dest.copy_pixels(*_pixmap, r->left, r->top, *r); + } +} + +void GUI::on_size_changed() +{ + DBG(0, ""); + update_layer_area(); +} + +void GUI::set_dialog(Dialog* dialog) +{ + if (_dialog) { + _dialog->pre_destroy(); + delete _dialog; + _dialog = NULL; + } + + if (!dialog) { + return; + } + + _dialog = dialog; + gui_system().setGUISheet(&_dialog->root_window()); + update_layer_area(); +} + +void GUI::create_dialog() +{ + switch (_state) { + case Application::DISCONNECTED: + set_dialog(new LoginDialog(*this)); + break; + case Application::VISIBILITY: + set_dialog(new SettingsDialog(*this)); + break; + case Application::CONNECTING: + set_dialog(new ConnectingDialog(*this)); + break; + case Application::CONNECTED: + break; + case Application::DISCONECTING: + set_dialog(NULL); + break; + } +} + +void GUI::set_state(Application::State state) +{ + if (_state == state) { + return; + } + _state = state; + create_dialog(); +} + +bool GUI::prepare_dialog() +{ + if (!_dialog) { + create_dialog(); + } + + return !!_dialog; +} + +void GUI::set_screen(RedScreen* in_screen) +{ + detach(); + if (!in_screen) { + _app.remove_key_handler(*this); + return; + } + ASSERT(!screen()); + in_screen->attach_layer(*this); + CEGUI::MouseCursor::getSingleton().hide(); + update_layer_area(); + _app.set_key_handler(*this); +} + +void GUI::on_pointer_enter(int x, int y, unsigned int buttons_state) +{ + CEGUI::MouseCursor::getSingleton().show(); + screen()->hide_cursor(); + _app.set_key_handler(*this); + on_pointer_motion(x, y, buttons_state); +} + +void GUI::on_pointer_motion(int x, int y, unsigned int buttons_state) +{ + _gui_system->injectMousePosition(float(x + _pixmap->get_origin().x), + float(y + _pixmap->get_origin().y)); + invalidate(); +} + +void GUI::on_mouse_button_press(int button, int buttons_state) +{ + _app.set_key_handler(*this); + switch (button) { + case REDC_MOUSE_LBUTTON: + _gui_system->injectMouseButtonDown(CEGUI::LeftButton); + break; + case REDC_MOUSE_MBUTTON: + _gui_system->injectMouseButtonDown(CEGUI::MiddleButton); + break; + case REDC_MOUSE_RBUTTON: + _gui_system->injectMouseButtonDown(CEGUI::RightButton); + break; + case REDC_MOUSE_UBUTTON: + _gui_system->injectMouseWheelChange(-1); + break; + case REDC_MOUSE_DBUTTON: + _gui_system->injectMouseWheelChange(1); + break; + default: + THROW("invalid RedButton %d", button); + } + conditional_update(); +} + +void GUI::on_mouse_button_release(int button, int buttons_state) +{ + switch (button) { + case REDC_MOUSE_LBUTTON: + _gui_system->injectMouseButtonUp(CEGUI::LeftButton); + break; + case REDC_MOUSE_MBUTTON: + _gui_system->injectMouseButtonUp(CEGUI::MiddleButton); + break; + case REDC_MOUSE_RBUTTON: + _gui_system->injectMouseButtonUp(CEGUI::RightButton); + break; + case REDC_MOUSE_UBUTTON: + case REDC_MOUSE_DBUTTON: + break; + default: + THROW("invalid RedButton %d", button); + } + conditional_update(); +} + +void GUI::on_pointer_leave() +{ + CEGUI::MouseCursor::getSingleton().hide(); + invalidate(); +} + +#define KEY_CASE(a, b) \ + case a: \ + return CEGUI::Key::b + +static inline CEGUI::Key::Scan red_ket_cegui_scan(RedKey key) +{ + switch (key) { + KEY_CASE(REDKEY_ESCAPE, Escape); + KEY_CASE(REDKEY_1, One); + KEY_CASE(REDKEY_2, Two); + KEY_CASE(REDKEY_3, Three); + KEY_CASE(REDKEY_4, Four); + KEY_CASE(REDKEY_5, Five); + KEY_CASE(REDKEY_6, Six); + KEY_CASE(REDKEY_7, Seven); + KEY_CASE(REDKEY_8, Eight); + KEY_CASE(REDKEY_9, Nine); + KEY_CASE(REDKEY_0, Zero); + KEY_CASE(REDKEY_MINUS, Minus); + KEY_CASE(REDKEY_EQUALS, Equals); + KEY_CASE(REDKEY_BACKSPACE, Backspace); + KEY_CASE(REDKEY_TAB, Tab); + KEY_CASE(REDKEY_Q, Q); + KEY_CASE(REDKEY_W, W); + KEY_CASE(REDKEY_E, E); + KEY_CASE(REDKEY_R, R); + KEY_CASE(REDKEY_T, T); + KEY_CASE(REDKEY_Y, Y); + KEY_CASE(REDKEY_U, U); + KEY_CASE(REDKEY_I, I); + KEY_CASE(REDKEY_O, O); + KEY_CASE(REDKEY_P, P); + KEY_CASE(REDKEY_L_BRACKET, LeftBracket); + KEY_CASE(REDKEY_R_BRACKET, RightBracket); + KEY_CASE(REDKEY_ENTER, Return); + KEY_CASE(REDKEY_L_CTRL, LeftControl); + KEY_CASE(REDKEY_A, A); + KEY_CASE(REDKEY_S, S); + KEY_CASE(REDKEY_D, D); + KEY_CASE(REDKEY_F, F); + KEY_CASE(REDKEY_G, G); + KEY_CASE(REDKEY_H, H); + KEY_CASE(REDKEY_J, J); + KEY_CASE(REDKEY_K, K); + KEY_CASE(REDKEY_L, L); + KEY_CASE(REDKEY_SEMICOLON, Semicolon); + KEY_CASE(REDKEY_QUOTE, Apostrophe); + + KEY_CASE(REDKEY_BACK_QUOTE, Grave); + KEY_CASE(REDKEY_L_SHIFT, LeftShift); + KEY_CASE(REDKEY_BACK_SLASH, Backslash); + KEY_CASE(REDKEY_Z, Z); + KEY_CASE(REDKEY_X, X); + KEY_CASE(REDKEY_C, C); + KEY_CASE(REDKEY_V, V); + KEY_CASE(REDKEY_B, B); + KEY_CASE(REDKEY_N, N); + KEY_CASE(REDKEY_M, M); + KEY_CASE(REDKEY_COMMA, Comma); + KEY_CASE(REDKEY_PERIOD, Period); + KEY_CASE(REDKEY_SLASH, Slash); + KEY_CASE(REDKEY_R_SHIFT, RightShift); + KEY_CASE(REDKEY_PAD_MULTIPLY, Multiply); + KEY_CASE(REDKEY_L_ALT, LeftAlt); + KEY_CASE(REDKEY_SPACE, Space); + KEY_CASE(REDKEY_CAPS_LOCK, Capital); + KEY_CASE(REDKEY_F1, F1); + KEY_CASE(REDKEY_F2, F2); + KEY_CASE(REDKEY_F3, F3); + KEY_CASE(REDKEY_F4, F4); + KEY_CASE(REDKEY_F5, F5); + KEY_CASE(REDKEY_F6, F6); + KEY_CASE(REDKEY_F7, F7); + KEY_CASE(REDKEY_F8, F8); + KEY_CASE(REDKEY_F9, F9); + KEY_CASE(REDKEY_F10, F10); + KEY_CASE(REDKEY_NUM_LOCK, NumLock); + KEY_CASE(REDKEY_SCROLL_LOCK, ScrollLock); + KEY_CASE(REDKEY_PAD_7, Numpad7); + KEY_CASE(REDKEY_PAD_8, Numpad8); + KEY_CASE(REDKEY_PAD_9, Numpad9); + KEY_CASE(REDKEY_PAD_MINUS, Subtract); + KEY_CASE(REDKEY_PAD_4, Numpad4); + KEY_CASE(REDKEY_PAD_5, Numpad5); + KEY_CASE(REDKEY_PAD_6, Numpad6); + KEY_CASE(REDKEY_PAD_PLUS, Add); + KEY_CASE(REDKEY_PAD_1, Numpad1); + KEY_CASE(REDKEY_PAD_2, Numpad2); + KEY_CASE(REDKEY_PAD_3, Numpad3); + KEY_CASE(REDKEY_PAD_0, Numpad0); + KEY_CASE(REDKEY_PAD_POINT, Decimal); + + KEY_CASE(REDKEY_EUROPEAN, OEM_102); + KEY_CASE(REDKEY_F11, F11); + KEY_CASE(REDKEY_F12, F12); + + KEY_CASE(REDKEY_JAPANESE_HIRAGANA_KATAKANA, Kana); + KEY_CASE(REDKEY_JAPANESE_BACKSLASH, ABNT_C1); + KEY_CASE(REDKEY_JAPANESE_HENKAN, Convert); + KEY_CASE(REDKEY_JAPANESE_MUHENKAN, NoConvert); + KEY_CASE(REDKEY_JAPANESE_YEN, Yen); + + //KEY_CASE(REDKEY_KOREAN_HANGUL_HANJA, + //KEY_CASE(REDKEY_KOREAN_HANGUL, + + KEY_CASE(REDKEY_PAD_ENTER, NumpadEnter); + KEY_CASE(REDKEY_R_CTRL, RightControl); + KEY_CASE(REDKEY_FAKE_L_SHIFT, LeftShift); + KEY_CASE(REDKEY_PAD_DIVIDE, Divide); + KEY_CASE(REDKEY_FAKE_R_SHIFT, RightShift); + KEY_CASE(REDKEY_CTRL_PRINT_SCREEN, SysRq); + KEY_CASE(REDKEY_R_ALT, RightAlt); + //KEY_CASE(REDKEY_CTRL_BREAK + KEY_CASE(REDKEY_HOME, Home); + KEY_CASE(REDKEY_UP, ArrowUp); + KEY_CASE(REDKEY_PAGEUP, PageUp); + KEY_CASE(REDKEY_LEFT, ArrowLeft); + KEY_CASE(REDKEY_RIGHT, ArrowRight); + KEY_CASE(REDKEY_END, End); + KEY_CASE(REDKEY_DOWN, ArrowDown); + KEY_CASE(REDKEY_PAGEDOWN, PageDown); + KEY_CASE(REDKEY_INSERT, Insert); + KEY_CASE(REDKEY_DELETE, Delete); + KEY_CASE(REDKEY_LEFT_CMD, LeftWindows); + KEY_CASE(REDKEY_RIGHT_CMD, RightWindows); + KEY_CASE(REDKEY_MENU, AppMenu); + KEY_CASE(REDKEY_PAUSE, Pause); + default: + return (CEGUI::Key::Scan)0; + }; +} + +void GUI::on_key_down(RedKey key) +{ + CEGUI::Key::Scan scan = red_ket_cegui_scan(key); + + if (scan == (CEGUI::Key::Scan)0) { + return; + } + + _gui_system->injectKeyDown(scan); + conditional_update(); +} + +void GUI::on_key_up(RedKey key) +{ + CEGUI::Key::Scan scan = red_ket_cegui_scan(key); + + if (scan == (CEGUI::Key::Scan)0) { + return; + } + + _gui_system->injectKeyUp(scan); + conditional_update(); +} + +void GUI::on_char(uint32_t ch) +{ + _gui_system->injectChar(ch); + conditional_update(); +} + +void GUI::idle() +{ + uint64_t now = Platform::get_monolithic_time(); + _gui_system->injectTimePulse(float(double(now) - double(_prev_time)) / (1000 * 1000 * 1000)); + _prev_time = now; + conditional_update(); +} + +bool GUI::message_box(MessageType type, const char *text, const ButtonsList& buttons, + BoxResponse* _response_handler) +{ + if (!_dialog) { + set_dialog(new MessageDialog(*this)); + } + return _dialog->message_box(type, text, buttons, _response_handler); +} + diff --git a/client/gui/gui.h b/client/gui/gui.h new file mode 100644 index 00000000..2e505b02 --- /dev/null +++ b/client/gui/gui.h @@ -0,0 +1,120 @@ +#ifndef _H_GUI +#define _H_GUI + +#include "softrenderer.h" +#include "screen_layer.h" +#include "inputs_handler.h" +#include "application.h" + +class RedPixmapCairo; + +class GUI : public ScreenLayer, public KeyHandler { +public: + class Dialog; + class Tab; + class TabFactory; + + typedef std::list<TabFactory*> TabFactorys; + + GUI(Application& app, Application::State state); + virtual ~GUI(); + + void set_screen(RedScreen* screen); //show and hide + + Application& get_application() { return _app;} + CEGUI::System& gui_system() { return *_gui_system;} + + void set_state(Application::State state); + bool is_visible() { return !!_dialog;} + bool prepare_dialog(); + bool is_disconnect_allowed() { return _app.is_disconnect_allowed();} + + virtual bool pointer_test(int x, int y) { return contains_point(x, y);} + virtual void on_pointer_enter(int x, int y, unsigned int buttons_state); + virtual void on_pointer_leave(); + virtual void on_pointer_motion(int x, int y, unsigned int buttons_state); + virtual void on_mouse_button_press(int button, int buttons_state); + virtual void on_mouse_button_release(int button, int buttons_state); + + virtual void on_key_down(RedKey key); + virtual void on_key_up(RedKey key); + virtual void on_char(uint32_t ch); + virtual bool permit_focus_loss() { return false;} + + void idle(); + + virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc); + virtual void on_size_changed(); + + void register_tab_factory(TabFactory& factory); + void unregister_tab_factory(TabFactory& factory); + + class BoxResponse { + public: + virtual ~BoxResponse() {} + virtual void response(int response) = 0; + virtual void aborted() = 0; + }; + + enum MessageType { + QUESTION, + INFO, + WARNINNG, + ERROR, + }; + + struct ButtonInfo { + int id; + const char *text; + }; + + typedef std::vector<ButtonInfo> ButtonsList; + bool message_box(MessageType type, const char *text, const ButtonsList& buttons, + BoxResponse* _response_handler); + +private: + TabFactorys& get_factoris() { return _tab_factorys;} + + void create_dialog(); + void detach(); + void update_layer_area(); + void init_cegui(); + void conditional_update(); + void set_dialog(Dialog* dialog); + +private: + Application& _app; + Application::State _state; + RedPixmapCairo* _pixmap; + CEGUI::SoftRenderer* _renderer; + CEGUI::System* _gui_system; + Dialog* _dialog; + uint64_t _prev_time; + TabFactorys _tab_factorys; + + friend class Dialog; +}; + +class GUI::Tab { +public: + virtual ~Tab() {} + + virtual CEGUI::Window& get_root_window() = 0; + virtual const std::string& get_name() = 0; +}; + +class GUI::TabFactory { +public: + TabFactory() : _order (-1) {} + TabFactory(int order) : _order (order) {} + + virtual ~TabFactory() {} + virtual Tab* create_tab(bool connected, int width, int hight) = 0; + int get_order() { return _order;} + +private: + int _order; +}; + +#endif + diff --git a/client/gui/resource_provider.cpp b/client/gui/resource_provider.cpp new file mode 100644 index 00000000..47f86a43 --- /dev/null +++ b/client/gui/resource_provider.cpp @@ -0,0 +1,128 @@ +#include "common.h" + +#include "resource_provider.h" +#include "debug.h" +#include "utils.h" + +#include "CEGUIExceptions.h" + +#include "taharez_look.scheme.c" +#include "taharez_look.imageset.c" +#include "taharez_look.tga.c" +#include "commonwealth-10.font.c" +#include "commonv2c.ttf.c" +#include "taharez_look.looknfeel.c" +#include "dejavu_sans-10.font.c" +#include "dejavu_sans.ttf.c" + +//(echo "const unsigned char <struct name>[] = +//{"; od -txC -v <in file name> | sed -e "s/^[0-9]*//" -e s"/ \([0-9a-f][0-9a-f]\)/0x\1,/g" -e"\$d" +//| sed -e"\$s/,$/};/") > <out file name>.c + +void CEGUIResourceProvider::loadRawDataContainer(const CEGUI::String &filename, + CEGUI::RawDataContainer &output, + const CEGUI::String &resourceGroup) +{ + DBG(0, "%s", filename.c_str()); + if (strcmp(filename.c_str(), "TaharezLook.scheme") == 0) { + DBG(0, "size %d", sizeof(taharez_look_schem)); + output.setData((CEGUI::uint8*)taharez_look_schem); + output.setSize(sizeof(taharez_look_schem)); + return; + } + + if (strcmp(filename.c_str(), "TaharezLook.imageset") == 0) { + DBG(0, "size %d", sizeof(taharez_look_imageset)); + output.setData((CEGUI::uint8*)taharez_look_imageset); + output.setSize(sizeof(taharez_look_imageset)); + return; + } + + if (strcmp(filename.c_str(), "TaharezLook.tga") == 0) { + DBG(0, "size %d", sizeof(taharez_look_tga)); + output.setData((CEGUI::uint8*)taharez_look_tga); + output.setSize(sizeof(taharez_look_tga)); + return; + } + + if (strcmp(filename.c_str(), "Commonwealth-10.font") == 0) { + DBG(0, "size %d", sizeof(commonwealth_10_font)); + output.setData((CEGUI::uint8*)commonwealth_10_font); + output.setSize(sizeof(commonwealth_10_font)); + return; + } + + if (strcmp(filename.c_str(), "Commonv2c.ttf") == 0) { + DBG(0, "size %d", sizeof(commonv2c_ttf)); + output.setData((CEGUI::uint8*)commonv2c_ttf); + output.setSize(sizeof(commonv2c_ttf)); + return; + } + + if (strcmp(filename.c_str(), "TaharezLook.looknfeel") == 0) { + DBG(0, "size %d", sizeof(taharez_look_looknfeel)); + output.setData((CEGUI::uint8*)taharez_look_looknfeel); + output.setSize(sizeof(taharez_look_looknfeel)); + return; + } + + if (strcmp(filename.c_str(), "DejaVuSans-10.font") == 0) { + DBG(0, "size %d", sizeof(dejavu_sans_10_font)); + output.setData((CEGUI::uint8*)dejavu_sans_10_font); + output.setSize(sizeof(dejavu_sans_10_font)); + return; + } + + if (strcmp(filename.c_str(), "DejaVuSans.ttf") == 0) { + DBG(0, "size %d", sizeof(dejavu_sans_ttf)); + output.setData((CEGUI::uint8*)dejavu_sans_ttf); + output.setSize(sizeof(dejavu_sans_ttf)); + return; + } + + throw CEGUI::GenericException("failed"); +} + +void CEGUIResourceProvider::unloadRawDataContainer(CEGUI::RawDataContainer& data) +{ + data.setData(NULL); + data.setSize(0); +} + +struct ResString{ + int id; + const char* str; +} res_strings[] = { + {STR_MESG_MISSING_HOST_NAME, "Missing host name"}, + {STR_MESG_INVALID_PORT, "Invalid port"}, + {STR_MESG_INVALID_SPORT, "Invalid sport"}, + {STR_MESG_MISSING_PORT, "Missing port"}, + {STR_MESG_CONNECTING, "Connecting to"}, + {STR_BUTTON_OK, "OK"}, + {STR_BUTTON_CANCEL, "Cancel"}, + {STR_BUTTON_CONNECT, "Connect"}, + {STR_BUTTON_QUIT, "Quit"}, + {STR_BUTTON_CLOSE, "Close"}, + {STR_BUTTON_DISCONNECT, "Disconnect"}, + {STR_BUTTON_OPTIONS, "Options"}, + {STR_BUTTON_BACK, "Back"}, + {STR_LABEL_HOST, "Host"}, + {STR_LABEL_PORT, "Port"}, + {STR_LABEL_SPORT, "Secore port"}, + {STR_LABEL_PASSWORD, "Password"}, + {0, NULL}, +}; + +const char* res_get_string(int id) +{ + ResString *string; + + for (string = res_strings; string->str; string++) { + if (string->id == id) { + return string->str; + } + } + + return NULL; +} + diff --git a/client/gui/resource_provider.h b/client/gui/resource_provider.h new file mode 100644 index 00000000..1443abe4 --- /dev/null +++ b/client/gui/resource_provider.h @@ -0,0 +1,40 @@ +#ifndef _H_RESOURCE_PROVIDER +#define _H_RESOURCE_PROVIDER + +#include "CEGUIDefaultResourceProvider.h" + +class CEGUIResourceProvider: public CEGUI::ResourceProvider { +public: + virtual void loadRawDataContainer(const CEGUI::String &filename, + CEGUI::RawDataContainer &output, + const CEGUI::String &resourceGroup); + + virtual void unloadRawDataContainer(CEGUI::RawDataContainer& data); +}; + +enum { + STR_INVALID, + STR_MESG_MISSING_HOST_NAME, + STR_MESG_INVALID_PORT, + STR_MESG_INVALID_SPORT, + STR_MESG_MISSING_PORT, + STR_MESG_CONNECTING, + STR_BUTTON_OK, + STR_BUTTON_CANCEL, + STR_BUTTON_CONNECT, + STR_BUTTON_QUIT, + STR_BUTTON_CLOSE, + STR_BUTTON_DISCONNECT, + STR_BUTTON_OPTIONS, + STR_BUTTON_BACK, + STR_LABEL_HOST, + STR_LABEL_PORT, + STR_LABEL_SPORT, + STR_LABEL_PASSWORD, +}; + +//todo: move to x11/res.cpp and make x11/res.cpp cross-platform +const char* res_get_string(int id); + +#endif + diff --git a/client/gui/softrenderer.cpp b/client/gui/softrenderer.cpp index 9e08f7a5..87fffb8b 100644 --- a/client/gui/softrenderer.cpp +++ b/client/gui/softrenderer.cpp @@ -64,19 +64,23 @@ void SoftRenderer::setupImageCodec() #else String _default_codec_name(STRINGIZE(TGAImageCodec/*CEGUI_DEFAULT_IMAGE_CODEC*/)); DynamicModule* module = NULL; + try { DynamicModule* module = new DynamicModule(String("CEGUI") + _default_codec_name); _destroy_image_codec = (void(*)(ImageCodec*))module->getSymbolAddress("destroyImageCodec"); + if (!_destroy_image_codec) { throw GenericException("Missing destroyImageCodec symbol"); } ImageCodec* (*create_f)(void); create_f = (ImageCodec* (*)(void))module->getSymbolAddress("createImageCodec"); + if (!create_f) { throw GenericException("Missing createImageCodec symbol"); } + _image_codec = create_f(); } catch (...) { delete module; @@ -86,14 +90,12 @@ void SoftRenderer::setupImageCodec() #endif } - void SoftRenderer::cleanupImageCodec() { _destroy_image_codec(_image_codec); delete _image_codec_module; } - static inline uint8_t calac_pixel(uint64_t c1, uint64_t c2, uint64_t c3, uint64_t a_mul) { //(c' * c" * a' * a" + c"' * 255 ^ 3 - c"' * a' * a" * 255) / 255^4 @@ -369,4 +371,3 @@ uint SoftRenderer::getVertScreenDPI() const } - diff --git a/client/gui/softrenderer.h b/client/gui/softrenderer.h index 939eb38b..9fc1a29e 100644 --- a/client/gui/softrenderer.h +++ b/client/gui/softrenderer.h @@ -128,4 +128,3 @@ namespace CEGUI #endif - diff --git a/client/platform.h b/client/platform.h index c32b8634..07777c07 100644 --- a/client/platform.h +++ b/client/platform.h @@ -40,6 +40,7 @@ public: static void get_temp_dir(std::string& path); static uint64_t get_process_id(); static uint64_t get_thread_id(); + static void error_beep(); static const MonitorsList& init_monitors(); static void destroy_monitors(); diff --git a/client/red_channel.cpp b/client/red_channel.cpp index cfd47057..c7dce015 100644 --- a/client/red_channel.cpp +++ b/client/red_channel.cpp @@ -401,7 +401,8 @@ void RedChannel::run() _client.get_port(), _client.get_sport()); RedChannelBase::connect(con_options, _client.get_connection_id(), - _client.get_host(), _client.get_password()); + _client.get_host().c_str(), + _client.get_password().c_str()); on_connect(); set_state(CONNECTED_STATE); _loop.add_socket(*this); diff --git a/client/red_client.cpp b/client/red_client.cpp index 8fcb81d6..e040f63f 100644 --- a/client/red_client.cpp +++ b/client/red_client.cpp @@ -274,9 +274,12 @@ public: RedClient::RedClient(Application& application) : RedChannel(*this, RED_CHANNEL_MAIN, 0, new MainChannelLoop(*this)) , _application (application) + , _port (-1) + , _sport (-1) , _connection_id (0) , _mouse_mode (RED_MOUSE_MODE_SERVER) , _notify_disconnect (false) + , _auto_display_res (false) , _aborting (false) , _agent_connected (false) , _agent_mon_config_sent (false) @@ -326,22 +329,7 @@ RedClient::~RedClient() delete _agent_msg; } -void RedClient::init(const char* host, int port, int sport, const char *password, - bool auto_display_res) -{ - _host = host; - _port = port; - _sport = sport; - _auto_display_res = auto_display_res; - - if (password != NULL) { - _password = password; - } else { - _password = ""; - } -} - -void RedClient::set_target(const char* host, uint16_t port, uint16_t sport) +void RedClient::set_target(const std::string& host, int port, int sport) { _port = port; _sport = sport; diff --git a/client/red_client.h b/client/red_client.h index fde958f2..a98d2d2f 100644 --- a/client/red_client.h +++ b/client/red_client.h @@ -131,8 +131,6 @@ public: RedClient(Application& application); ~RedClient(); - void init(const char* host, int port, int sport, const char *password, bool auto_display_res); - void register_channel_factory(ChannelFactory& factory); virtual void connect(); @@ -143,9 +141,11 @@ public: void activate_interval_timer(Timer* timer, unsigned int millisec); void deactivate_interval_timer(Timer* timer); - void set_target(const char* host, uint16_t port, uint16_t sport); - const char* get_password() { return _password.c_str();} - const char* get_host() { return _host.c_str();} + void set_target(const std::string&, int port, int sport); + void set_password(const std::string& password) { _password = password;} + void set_auto_display_res(bool auto_display_res) { _auto_display_res = auto_display_res;} + const std::string& get_password() { return _password;} + const std::string& get_host() { return _host;} int get_port() { return _port;} int get_sport() { return _sport;} virtual uint32_t get_connection_id() { return _connection_id;} @@ -202,10 +202,10 @@ private: private: Application& _application; - std::string _password; std::string _host; int _port; int _sport; + std::string _password; uint32_t _connection_id; uint32_t _mouse_mode; Mutex _notify_lock; diff --git a/client/screen.h b/client/screen.h index 5952fe81..c07d5419 100644 --- a/client/screen.h +++ b/client/screen.h @@ -37,7 +37,9 @@ class RedScreen; enum { SCREEN_LAYER_DISPLAY, SCREEN_LAYER_CURSOR, + SCREEN_LAYER_GUI_BARIER, SCREEN_LAYER_GUI, + SCREEN_LAYER_INFO, }; class UpdateTimer: public Timer { diff --git a/client/utils.cpp b/client/utils.cpp index 29201743..c27aa361 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -37,3 +37,14 @@ void wstring_printf(std::wstring& str, const wchar_t* format, ...) va_end(ap); } +int str_to_port(const char *str) +{ + long port; + char *endptr; + port = strtol(str, &endptr, 0); + if (endptr != str + strlen(str) || port < 0 || port > 0xffff) { + return -1; + } + return port; +} + diff --git a/client/utils.h b/client/utils.h index 4be75281..d4163b87 100644 --- a/client/utils.h +++ b/client/utils.h @@ -98,6 +98,8 @@ static inline void set_bit_be(const void* addr, int bit) ((uint8_t*)addr)[bit >> 3] |= (0x80 >> (bit & 0x07)); } +int str_to_port(const char *str); + void string_vprintf(std::string& str, const char* format, va_list ap); void string_printf(std::string& str, const char *format, ...); void wstring_vprintf(std::wstring& str, const wchar_t* format, va_list ap); diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp index 6d5deba5..11f13266 100644 --- a/client/windows/platform.cpp +++ b/client/windows/platform.cpp @@ -218,6 +218,11 @@ uint64_t Platform::get_thread_id() return GetCurrentThreadId(); } +void Platform::error_beep() +{ + MessageBeep(MB_ICONERROR); +} + class WinMonitor: public Monitor { public: WinMonitor(int id, const wchar_t* name, const wchar_t* string); diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj index 2741ae57..2f97d378 100644 --- a/client/windows/redc.vcproj +++ b/client/windows/redc.vcproj @@ -241,6 +241,10 @@ >
</File>
<File
+ RelativePath="..\gui\gui.cpp"
+ >
+ </File>
+ <File
RelativePath="..\hot_keys.cpp"
>
</File>
@@ -371,6 +375,10 @@ >
</File>
<File
+ RelativePath="..\gui\resource_provider.cpp"
+ >
+ </File>
+ <File
RelativePath="..\rop3.cpp"
>
</File>
@@ -481,6 +489,10 @@ >
</File>
<File
+ RelativePath="..\gui\gui.h"
+ >
+ </File>
+ <File
RelativePath="..\hot_keys.h"
>
</File>
@@ -569,6 +581,10 @@ >
</File>
<File
+ RelativePath="..\gui\resource_provider.h"
+ >
+ </File>
+ <File
RelativePath="..\screen.h"
>
</File>
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am index 072548db..12039f0f 100644 --- a/client/x11/Makefile.am +++ b/client/x11/Makefile.am @@ -107,6 +107,10 @@ RED_COMMON_SRCS = \ $(top_srcdir)/client/gui/softrenderer.cpp \ $(top_srcdir)/client/gui/softtexture.h \ $(top_srcdir)/client/gui/softtexture.cpp \ + $(top_srcdir)/client/gui/resource_provider.h \ + $(top_srcdir)/client/gui/resource_provider.cpp \ + $(top_srcdir)/client/gui/gui.h \ + $(top_srcdir)/client/gui/gui.cpp \ $(NULL) bin_PROGRAMS = spicec diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp index d13c9fe6..1a98e2ed 100644 --- a/client/x11/platform.cpp +++ b/client/x11/platform.cpp @@ -249,6 +249,15 @@ uint64_t Platform::get_thread_id() return uint64_t(syscall(SYS_gettid)); } +void Platform::error_beep() +{ + if (!x_display) { + return; + } + + XBell(x_display, 0); +} + void Platform::msleep(unsigned int millisec) { usleep(millisec * 1000); |