summaryrefslogtreecommitdiffstats
path: root/client/application.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/application.cpp')
-rw-r--r--client/application.cpp1674
1 files changed, 1674 insertions, 0 deletions
diff --git a/client/application.cpp b/client/application.cpp
new file mode 100644
index 00000000..b30baa84
--- /dev/null
+++ b/client/application.cpp
@@ -0,0 +1,1674 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef WIN32
+#include "config.h"
+#endif
+
+#include "common.h"
+#ifdef WIN32
+#include <io.h>
+#endif
+
+#include "application.h"
+#include "screen.h"
+#include "utils.h"
+#include "debug.h"
+#include "screen_layer.h"
+#include "monitor.h"
+#include "resource.h"
+#ifdef WIN32
+#include "red_gdi_canvas.h"
+#endif
+#include "platform.h"
+#include "cairo_canvas.h"
+#include "gl_canvas.h"
+#include "quic.h"
+#include "mutex.h"
+#include "cmd_line_parser.h"
+
+#include <log4cpp/BasicConfigurator.hh>
+#include <log4cpp/FileAppender.hh>
+#include <log4cpp/RollingFileAppender.hh>
+
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+mutex_t cairo_surface_user_data_mutex;
+#endif
+
+SyncEvent::SyncEvent()
+ : _err (false)
+ , _ready (false)
+{
+}
+
+SyncEvent::~SyncEvent()
+{
+}
+
+void SyncEvent::responce(Application& application)
+{
+ try {
+ do_responce(application);
+ } catch (Exception& e) {
+ LOG_WARN("unhandle exception: %s", e.what());
+ _err = true;
+ } catch (...) {
+ _err = true;
+ }
+ Lock lock(_mutex);
+ _ready = true;
+ _condition.notify_one();
+}
+
+void SyncEvent::wait()
+{
+ //todo: process event if on main thread
+ Lock lock(_mutex);
+ while (!_ready) {
+ _condition.wait(lock);
+ }
+}
+
+void ConnectedEvent::responce(Application& application)
+{
+ application.on_connected();
+}
+
+void DisconnectedEvent::responce(Application& application)
+{
+ application.show_splash(0);
+#ifndef RED_DEBUG
+ application.quit(SPICEC_ERROR_CODE_SUCCESS);
+#endif
+}
+
+void CoonnectionError::responce(Application& application)
+{
+ application.show_splash(0);
+#ifndef RED_DEBUG
+ application.quit(_error_code);
+#endif
+}
+
+void ErrorEvent::responce(Application& application)
+{
+ application.quit(SPICEC_ERROR_CODE_ERROR);
+}
+
+void MonitorsQuery::do_responce(Application& application)
+{
+ Monitor* mon;
+ int i = 0;
+
+ while ((mon = application.find_monitor(i++))) {
+ MonitorInfo info;
+ info.size = mon->get_size();
+ info.depth = 32;
+ info.position = mon->get_position();
+ _monitors.push_back(info);
+ }
+}
+
+class GUILayer: public ScreenLayer {
+public:
+ GUILayer();
+
+ virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc);
+
+ void set_splash_mode();
+ void set_info_mode();
+ virtual void on_size_changed();
+
+private:
+ void draw_splash(const QRegion& dest_region, RedDrawable& dest);
+ void draw_info(const QRegion& dest_region, RedDrawable& dest);
+
+private:
+ ImageFromRes _splash_pixmap;
+ AlphaImageFromRes _info_pixmap;
+ Point _splash_pos;
+ Point _info_pos;
+ bool _splash_mode;
+ Mutex _update_lock;
+};
+
+GUILayer::GUILayer()
+ : ScreenLayer(SCREEN_LAYER_GUI, false)
+ , _splash_pixmap (SPLASH_IMAGE_RES_ID)
+ , _info_pixmap (INFO_IMAGE_RES_ID)
+ , _splash_mode (false)
+{
+}
+
+void GUILayer::draw_splash(const QRegion& dest_region, RedDrawable& dest)
+{
+ 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)
+{
+ for (int i = 0; i < (int)dest_region.num_rects; i++) {
+ Rect* r = &dest_region.rects[i];
+ dest.blend_pixels(_info_pixmap, r->left - _info_pos.x, r->top - _info_pos.y, *r);
+ }
+}
+
+void GUILayer::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
+{
+ Lock lock(_update_lock);
+ if (_splash_mode) {
+ draw_splash(dest_region, dest_dc);
+ } else {
+ draw_info(dest_region, dest_dc);
+ }
+}
+
+void GUILayer::set_splash_mode()
+{
+ Lock 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);
+}
+
+void GUILayer::set_info_mode()
+{
+ Lock lock(_update_lock);
+ Point size = _info_pixmap.get_size();
+ Point screen_size = screen()->get_size();
+ Rect r;
+
+ r.left = (screen_size.x - size.x) / 2;
+ r.right = r.left + size.x;
+ _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);
+}
+
+void GUILayer::on_size_changed()
+{
+ set_info_mode();
+}
+
+static InputsHandler default_inputs_handler;
+
+enum AppCommands {
+ APP_CMD_INVALID,
+ APP_CMD_SEND_CTL_ALT_DEL,
+ APP_CMD_TOGGLE_FULL_SCREEN,
+ APP_CMD_RELEASE_CAPTURE,
+ APP_CMD_SEND_TOGGLE_KEYS,
+ APP_CMD_SEND_RELEASE_KEYS,
+ APP_CMD_SEND_CTL_ALT_END,
+#ifdef RED_DEBUG
+ APP_CMD_CONNECT,
+ APP_CMD_DISCONNECT,
+#endif
+};
+
+Application::Application()
+ : _client (*this)
+ , _enabled_channels(RED_CHANNEL_END, true)
+ , _main_screen (NULL)
+ , _quitting (false)
+ , _active (false)
+ , _full_screen (false)
+ , _changing_screens (false)
+ , _exit_code (0)
+ , _events_gen (0)
+ , _active_screen (NULL)
+ , _gui_layer (new GUILayer())
+ , _inputs_handler (&default_inputs_handler)
+ , _monitors (NULL)
+ , _title (L"SPICEc:%d")
+{
+ DBG(0, "");
+
+ init_monitors();
+ init_key_table();
+ 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);
+
+ _commands_map["toggle-fullscreen"] = APP_CMD_TOGGLE_FULL_SCREEN;
+ _commands_map["release-cursor"] = APP_CMD_RELEASE_CAPTURE;
+#ifdef RED_DEBUG
+ _commands_map["connect"] = APP_CMD_CONNECT;
+ _commands_map["disconnect"] = APP_CMD_DISCONNECT;
+#endif
+
+ _canvas_types.resize(1);
+#ifdef WIN32
+ _canvas_types[0] = CANVAS_OPTION_GDI;
+#else
+ _canvas_types[0] = CANVAS_OPTION_CAIRO;
+#endif
+
+ std::auto_ptr<HotKeysParser> parser(new HotKeysParser("toggle-fullscreen=shift+f11"
+ ",release-cursor=shift+f12"
+#ifdef RED_DEBUG
+ ",connect=shift+f5"
+ ",disconnect=shift+f6"
+#endif
+ , _commands_map));
+ _hot_keys = parser->get();
+}
+
+Application::~Application()
+{
+ _main_screen->detach_layer(*_gui_layer);
+ _main_screen->unref();
+ destroy_monitors();
+}
+
+void Application::init_menu()
+{
+ //fixme: menu items name need to be dynamically updated by hot keys configuration.
+ AutoRef<Menu> root_menu(new Menu(*this, ""));
+ (*root_menu)->add_command("Send Ctrl+Alt+Del\tCtrl+Alt+End", APP_CMD_SEND_CTL_ALT_DEL);
+ (*root_menu)->add_command("Toggle full screen\tShift+F11", APP_CMD_TOGGLE_FULL_SCREEN);
+ AutoRef<Menu> key_menu(new Menu(*this, "Special keys"));
+ (*key_menu)->add_command("Send Shift+F11", APP_CMD_SEND_TOGGLE_KEYS);
+ (*key_menu)->add_command("Send Shift+F12", APP_CMD_SEND_RELEASE_KEYS);
+ (*key_menu)->add_command("Send Ctrl+Alt+End", APP_CMD_SEND_CTL_ALT_END);
+ (*root_menu)->add_sub(key_menu.release());
+ _app_menu.reset(root_menu.release());
+}
+
+void Application::set_inputs_handler(InputsHandler& handler)
+{
+ unpress_all();
+ _inputs_handler = &handler;
+ if (_active) {
+ handler.on_focus_in();
+ }
+}
+
+void Application::remove_inputs_handler(InputsHandler& handler)
+{
+ if (_inputs_handler != &handler) {
+ return;
+ }
+ _inputs_handler = &default_inputs_handler;
+}
+
+void Application::process_events()
+{
+ _events_gen++;
+ for (;;) {
+ Event* event;
+ Lock lock(_events_lock);
+ if (_events.empty()) {
+ return;
+ }
+ event = _events.front();
+ if (event->_generation == _events_gen) {
+ Platform::wakeup();
+ return;
+ }
+ _events.pop_front();
+ lock.unlock();
+ event->responce(*this);
+ event->unref();
+ }
+}
+
+void Application::push_event(Event* event)
+{
+ Lock lock(_events_lock);
+ bool notify = _events.empty();
+ _events.push_back(event);
+ event->_generation = _events_gen;
+ event->ref();
+ if (notify) {
+ Platform::wakeup();
+ }
+}
+
+int Application::message_loop()
+{
+ for (;;) {
+ Platform::wait_events();
+ if (Platform::process_events()) {
+ _quitting = true;
+ break;
+ }
+ process_events();
+ }
+ return _exit_code;
+}
+
+void Application::abort()
+{
+ Platform::set_event_listener(NULL);
+ Platform::set_display_mode_listner(NULL);
+ unpress_all();
+ while (!_client.abort()) {
+ process_events();
+ Platform::msleep(100);
+ }
+}
+
+class AutoAbort {
+public:
+ AutoAbort(Application& app) : _app(app) {}
+ ~AutoAbort() { _app.abort();}
+
+private:
+ Application& _app;
+};
+
+void Application::connect()
+{
+ _client.connect();
+}
+
+int Application::run()
+{
+ try {
+ _client.connect();
+ _exit_code = message_loop();
+ } catch (...) {
+ throw;
+ }
+ return _exit_code;
+}
+
+void Application::quit(int exit_code)
+{
+ if (!_quitting) {
+ _quitting = true;
+ _exit_code = exit_code;
+ Platform::send_quit_request();
+ }
+}
+
+RedScreen* Application::find_screen(int id)
+{
+ if ((int)_screens.size() < id + 1) {
+ return NULL;
+ }
+ return _screens[id];
+}
+
+bool Application::release_capture()
+{
+ unpress_all();
+ if (!_active_screen || !_active_screen->is_captured()) {
+ return false;
+ }
+ _active_screen->relase_inputs();
+ return true;
+}
+
+bool Application::do_connect()
+{
+ _client.connect();
+ return true;
+}
+
+bool Application::do_disconnect()
+{
+ on_disconnecting();
+ _client.disconnect();
+ return true;
+}
+
+#define SCREEN_INIT_WIDTH 800
+#define SCREEN_INIT_HEIGHT 600
+
+RedScreen* Application::get_screen(int id)
+{
+ RedScreen *screen;
+
+ if ((int)_screens.size() < id + 1) {
+ _screens.resize(id + 1);
+ }
+
+ if (!(screen = _screens[id])) {
+ Monitor* mon;
+
+ if (_client.is_auto_display_res() && (mon = find_monitor(id))) {
+ Point size = mon->get_size();
+ screen = _screens[id] = new RedScreen(*this, id, _title, size.x, size.y);
+ } else {
+ screen = _screens[id] = new RedScreen(*this, id, _title, SCREEN_INIT_WIDTH,
+ SCREEN_INIT_HEIGHT);
+ }
+ if (_full_screen) {
+ bool capture;
+
+ mon = get_monitor(id);
+ capture = release_capture();
+ screen->set_monitor(mon);
+ position_screens();
+ screen->show_full_screen();
+ prepare_monitors();
+
+ if (capture) {
+ _main_screen->activate();
+ _main_screen->capture_inputs();
+ }
+ } else if (id != 0) {
+ screen->show(false, _main_screen);
+ }
+ } else {
+ screen = screen->ref();
+ }
+
+ return screen;
+}
+
+void Application::on_screen_destroyed(int id, bool was_captured)
+{
+ bool reposition = false;
+
+ if ((int)_screens.size() < id + 1 || !_screens[id]) {
+ THROW("no screen");
+ }
+ if (_active_screen == _screens[id]) {
+ _active_screen = NULL;
+ }
+
+ if (_full_screen && _screens[id]->has_monitor()) {
+ _screens[id]->get_monitor()->restore();
+ reposition = true;
+ }
+ _screens[id] = NULL;
+ if (reposition) {
+ bool capture = was_captured || release_capture();
+ prepare_monitors();
+ position_screens();
+ if (capture) {
+ _main_screen->activate();
+ _main_screen->capture_inputs();
+ }
+ }
+}
+
+void Application::on_mouse_motion(int dx, int dy, int buttons_state)
+{
+ _inputs_handler->on_mouse_motion(dx, dy, buttons_state);
+}
+
+void Application::on_mouse_position(int x, int y, int buttons_state, int display_id)
+{
+ _inputs_handler->on_mouse_position(x, y, buttons_state, display_id);
+}
+
+void Application::on_mouse_down(int button, int buttons_state)
+{
+ _inputs_handler->on_mouse_down(button, buttons_state);
+}
+
+void Application::on_mouse_up(int button, int buttons_state)
+{
+ _inputs_handler->on_mouse_up(button, buttons_state);
+}
+
+void Application::init_scan_code(int index)
+{
+ ASSERT((index & 0x80) == 0);
+ _key_table[index]._make = index;
+ _key_table[index]._break = index | 0x80;
+}
+
+void Application::init_korean_scan_code(int index)
+{
+ _key_table[index]._make = index;
+ _key_table[index]._break = index;
+}
+
+void Application::init_escape_scan_code(int index)
+{
+ ASSERT(((index - REDKEY_ESCAPE_BASE) & 0x80) == 0);
+ _key_table[index]._make = 0xe0 | ((index - REDKEY_ESCAPE_BASE) << 8);
+ _key_table[index]._break = _key_table[index]._make | 0x8000;
+}
+
+void Application::init_pause_scan_code()
+{
+ _key_table[REDKEY_PAUSE]._make = 0x451de1;
+ _key_table[REDKEY_PAUSE]._break = 0xc59de1;
+}
+
+void Application::init_key_table()
+{
+ memset(_key_table, 0, sizeof(_key_table));
+ init_scan_code(REDKEY_ESCAPE);
+ init_scan_code(REDKEY_1);
+ init_scan_code(REDKEY_2);
+ init_scan_code(REDKEY_3);
+ init_scan_code(REDKEY_4);
+ init_scan_code(REDKEY_5);
+ init_scan_code(REDKEY_6);
+ init_scan_code(REDKEY_7);
+ init_scan_code(REDKEY_8);
+ init_scan_code(REDKEY_9);
+ init_scan_code(REDKEY_0);
+ init_scan_code(REDKEY_MINUS);
+ init_scan_code(REDKEY_EQUALS);
+ init_scan_code(REDKEY_BACKSPACE);
+ init_scan_code(REDKEY_TAB);
+ init_scan_code(REDKEY_Q);
+ init_scan_code(REDKEY_W);
+ init_scan_code(REDKEY_E);
+ init_scan_code(REDKEY_R);
+ init_scan_code(REDKEY_T);
+ init_scan_code(REDKEY_Y);
+ init_scan_code(REDKEY_U);
+ init_scan_code(REDKEY_I);
+ init_scan_code(REDKEY_O);
+ init_scan_code(REDKEY_P);
+ init_scan_code(REDKEY_L_BRACKET);
+ init_scan_code(REDKEY_R_BRACKET);
+ init_scan_code(REDKEY_ENTER);
+ init_scan_code(REDKEY_L_CTRL);
+ init_scan_code(REDKEY_A);
+ init_scan_code(REDKEY_S);
+ init_scan_code(REDKEY_D);
+ init_scan_code(REDKEY_F);
+ init_scan_code(REDKEY_G);
+ init_scan_code(REDKEY_H);
+ init_scan_code(REDKEY_J);
+ init_scan_code(REDKEY_K);
+ init_scan_code(REDKEY_L);
+ init_scan_code(REDKEY_SEMICOLON);
+ init_scan_code(REDKEY_QUOTE);
+ init_scan_code(REDKEY_BACK_QUOTE);
+ init_scan_code(REDKEY_L_SHIFT);
+ init_scan_code(REDKEY_BACK_SLASH);
+ init_scan_code(REDKEY_Z);
+ init_scan_code(REDKEY_X);
+ init_scan_code(REDKEY_C);
+ init_scan_code(REDKEY_V);
+ init_scan_code(REDKEY_B);
+ init_scan_code(REDKEY_N);
+ init_scan_code(REDKEY_M);
+ init_scan_code(REDKEY_COMMA);
+ init_scan_code(REDKEY_PERIOD);
+ init_scan_code(REDKEY_SLASH);
+ init_scan_code(REDKEY_R_SHIFT);
+ init_scan_code(REDKEY_PAD_MULTIPLY);
+ init_scan_code(REDKEY_L_ALT);
+ init_scan_code(REDKEY_SPACE);
+ init_scan_code(REDKEY_CAPS_LOCK);
+ init_scan_code(REDKEY_F1);
+ init_scan_code(REDKEY_F2);
+ init_scan_code(REDKEY_F3);
+ init_scan_code(REDKEY_F4);
+ init_scan_code(REDKEY_F5);
+ init_scan_code(REDKEY_F6);
+ init_scan_code(REDKEY_F7);
+ init_scan_code(REDKEY_F8);
+ init_scan_code(REDKEY_F9);
+ init_scan_code(REDKEY_F10);
+ init_scan_code(REDKEY_NUM_LOCK);
+ init_scan_code(REDKEY_SCROLL_LOCK);
+ init_scan_code(REDKEY_PAD_7);
+ init_scan_code(REDKEY_PAD_8);
+ init_scan_code(REDKEY_PAD_9);
+ init_scan_code(REDKEY_PAD_MINUS);
+ init_scan_code(REDKEY_PAD_4);
+ init_scan_code(REDKEY_PAD_5);
+ init_scan_code(REDKEY_PAD_6);
+ init_scan_code(REDKEY_PAD_PLUS);
+ init_scan_code(REDKEY_PAD_1);
+ init_scan_code(REDKEY_PAD_2);
+ init_scan_code(REDKEY_PAD_3);
+ init_scan_code(REDKEY_PAD_0);
+ init_scan_code(REDKEY_PAD_POINT);
+
+ init_scan_code(REDKEY_EUROPEAN);
+ init_scan_code(REDKEY_F11);
+ init_scan_code(REDKEY_F12);
+
+ init_scan_code(REDKEY_JAPANESE_HIRAGANA_KATAKANA);
+ init_scan_code(REDKEY_JAPANESE_BACKSLASH);
+ init_scan_code(REDKEY_JAPANESE_HENKAN);
+ init_scan_code(REDKEY_JAPANESE_MUHENKAN);
+ init_scan_code(REDKEY_JAPANESE_YEN);
+
+ init_korean_scan_code(REDKEY_KOREAN_HANGUL);
+ init_korean_scan_code(REDKEY_KOREAN_HANGUL_HANJA);
+
+ init_escape_scan_code(REDKEY_ESCAPE_BASE);
+ init_escape_scan_code(REDKEY_PAD_ENTER);
+ init_escape_scan_code(REDKEY_R_CTRL);
+ init_escape_scan_code(REDKEY_FAKE_L_SHIFT);
+ init_escape_scan_code(REDKEY_PAD_DIVIDE);
+ init_escape_scan_code(REDKEY_FAKE_R_SHIFT);
+ init_escape_scan_code(REDKEY_CTRL_PRINT_SCREEN);
+ init_escape_scan_code(REDKEY_R_ALT);
+ init_escape_scan_code(REDKEY_CTRL_BREAK);
+ init_escape_scan_code(REDKEY_HOME);
+ init_escape_scan_code(REDKEY_UP);
+ init_escape_scan_code(REDKEY_PAGEUP);
+ init_escape_scan_code(REDKEY_LEFT);
+ init_escape_scan_code(REDKEY_RIGHT);
+ init_escape_scan_code(REDKEY_END);
+ init_escape_scan_code(REDKEY_DOWN);
+ init_escape_scan_code(REDKEY_PAGEDOWN);
+ init_escape_scan_code(REDKEY_INSERT);
+ init_escape_scan_code(REDKEY_DELETE);
+ init_escape_scan_code(REDKEY_LEFT_CMD);
+ init_escape_scan_code(REDKEY_RIGHT_CMD);
+ init_escape_scan_code(REDKEY_MENU);
+
+ init_pause_scan_code();
+}
+
+inline uint32_t Application::get_make_scan_code(RedKey key)
+{
+ return _key_table[key]._make;
+}
+
+inline uint32_t Application::get_break_scan_code(RedKey key)
+{
+ return _key_table[key]._break;
+}
+
+void Application::unpress_all()
+{
+ for (int i = 0; i < REDKEY_NUM_KEYS; i++) {
+ if (_key_table[i].press) {
+ uint32_t scan_code = get_break_scan_code((RedKey)i);
+ ASSERT(scan_code);
+ _inputs_handler->on_key_up(scan_code);
+ _key_table[i].press = false;
+ }
+ }
+}
+
+void Application::on_connected()
+{
+}
+
+void Application::on_disconnecting()
+{
+ release_capture();
+}
+
+Menu* Application::get_app_menu()
+{
+ if (!*_app_menu) {
+ return NULL;
+ }
+ return (*_app_menu)->ref();
+}
+
+void Application::do_command(int command)
+{
+ switch (command) {
+ case APP_CMD_SEND_CTL_ALT_DEL:
+ send_alt_ctl_del();
+ break;
+ case APP_CMD_TOGGLE_FULL_SCREEN:
+ toggle_full_screen();
+ break;
+ case APP_CMD_SEND_TOGGLE_KEYS:
+ send_command_hotkey(APP_CMD_SEND_TOGGLE_KEYS);
+ break;
+ case APP_CMD_SEND_RELEASE_KEYS:
+ send_command_hotkey(APP_CMD_SEND_RELEASE_KEYS);
+ break;
+ case APP_CMD_SEND_CTL_ALT_END:
+ send_ctrl_alt_end();
+ break;
+ case APP_CMD_RELEASE_CAPTURE:
+ release_capture();
+ break;
+#ifdef RED_DEBUG
+ case APP_CMD_CONNECT:
+ do_connect();
+ break;
+ case APP_CMD_DISCONNECT:
+ do_disconnect();
+ break;
+#endif
+ }
+}
+
+#ifdef REDKEY_DEBUG
+
+static void show_red_key(RedKey key)
+{
+
+#define KEYCASE(red_key) \
+ case red_key: \
+ DBG(0, #red_key); \
+ return;
+
+ switch (key) {
+ KEYCASE(REDKEY_INVALID);
+ KEYCASE(REDKEY_ESCAPE);
+ KEYCASE(REDKEY_1);
+ KEYCASE(REDKEY_2);
+ KEYCASE(REDKEY_3);
+ KEYCASE(REDKEY_4);
+ KEYCASE(REDKEY_5);
+ KEYCASE(REDKEY_6);
+ KEYCASE(REDKEY_7);
+ KEYCASE(REDKEY_8);
+ KEYCASE(REDKEY_9);
+ KEYCASE(REDKEY_0);
+ KEYCASE(REDKEY_MINUS);
+ KEYCASE(REDKEY_EQUALS);
+ KEYCASE(REDKEY_BACKSPACE);
+ KEYCASE(REDKEY_TAB);
+ KEYCASE(REDKEY_Q);
+ KEYCASE(REDKEY_W);
+ KEYCASE(REDKEY_E);
+ KEYCASE(REDKEY_R);
+ KEYCASE(REDKEY_T);
+ KEYCASE(REDKEY_Y);
+ KEYCASE(REDKEY_U);
+ KEYCASE(REDKEY_I);
+ KEYCASE(REDKEY_O);
+ KEYCASE(REDKEY_P);
+ KEYCASE(REDKEY_ENTER);
+ KEYCASE(REDKEY_L_BRACKET);
+ KEYCASE(REDKEY_R_BRACKET);
+ KEYCASE(REDKEY_L_CTRL);
+ KEYCASE(REDKEY_A);
+ KEYCASE(REDKEY_S);
+ KEYCASE(REDKEY_D);
+ KEYCASE(REDKEY_F);
+ KEYCASE(REDKEY_G);
+ KEYCASE(REDKEY_H);
+ KEYCASE(REDKEY_J);
+ KEYCASE(REDKEY_K);
+ KEYCASE(REDKEY_L);
+ KEYCASE(REDKEY_SEMICOLON);
+ KEYCASE(REDKEY_QUOTE);
+ KEYCASE(REDKEY_BACK_QUOTE);
+ KEYCASE(REDKEY_L_SHIFT);
+ KEYCASE(REDKEY_BACK_SLASH);
+ KEYCASE(REDKEY_Z);
+ KEYCASE(REDKEY_X);
+ KEYCASE(REDKEY_C);
+ KEYCASE(REDKEY_V);
+ KEYCASE(REDKEY_B);
+ KEYCASE(REDKEY_N);
+ KEYCASE(REDKEY_M);
+ KEYCASE(REDKEY_COMMA);
+ KEYCASE(REDKEY_PERIOD);
+ KEYCASE(REDKEY_SLASH);
+ KEYCASE(REDKEY_R_SHIFT);
+ KEYCASE(REDKEY_PAD_MULTIPLY);
+ KEYCASE(REDKEY_L_ALT);
+ KEYCASE(REDKEY_SPACE);
+ KEYCASE(REDKEY_CAPS_LOCK);
+ KEYCASE(REDKEY_F1);
+ KEYCASE(REDKEY_F2);
+ KEYCASE(REDKEY_F3);
+ KEYCASE(REDKEY_F4);
+ KEYCASE(REDKEY_F5);
+ KEYCASE(REDKEY_F6);
+ KEYCASE(REDKEY_F7);
+ KEYCASE(REDKEY_F8);
+ KEYCASE(REDKEY_F9);
+ KEYCASE(REDKEY_F10);
+ KEYCASE(REDKEY_NUM_LOCK);
+ KEYCASE(REDKEY_SCROLL_LOCK);
+ KEYCASE(REDKEY_PAD_7);
+ KEYCASE(REDKEY_PAD_8);
+ KEYCASE(REDKEY_PAD_9);
+ KEYCASE(REDKEY_PAD_MINUS);
+ KEYCASE(REDKEY_PAD_4);
+ KEYCASE(REDKEY_PAD_5);
+ KEYCASE(REDKEY_PAD_6);
+ KEYCASE(REDKEY_PAD_PLUS);
+ KEYCASE(REDKEY_PAD_1);
+ KEYCASE(REDKEY_PAD_2);
+ KEYCASE(REDKEY_PAD_3);
+ KEYCASE(REDKEY_PAD_0);
+ KEYCASE(REDKEY_PAD_POINT);
+ KEYCASE(REDKEY_F11);
+ KEYCASE(REDKEY_F12);
+ KEYCASE(REDKEY_PAD_ENTER);
+ KEYCASE(REDKEY_R_CTRL);
+ KEYCASE(REDKEY_FAKE_L_SHIFT);
+ KEYCASE(REDKEY_PAD_DIVIDE);
+ KEYCASE(REDKEY_FAKE_R_SHIFT);
+ KEYCASE(REDKEY_CTRL_PRINT_SCREEN);
+ KEYCASE(REDKEY_R_ALT);
+ KEYCASE(REDKEY_CTRL_BREAK);
+ KEYCASE(REDKEY_HOME);
+ KEYCASE(REDKEY_UP);
+ KEYCASE(REDKEY_PAGEUP);
+ KEYCASE(REDKEY_LEFT);
+ KEYCASE(REDKEY_RIGHT);
+ KEYCASE(REDKEY_END);
+ KEYCASE(REDKEY_DOWN);
+ KEYCASE(REDKEY_PAGEDOWN);
+ KEYCASE(REDKEY_INSERT);
+ KEYCASE(REDKEY_DELETE);
+ KEYCASE(REDKEY_LEFT_CMD);
+ KEYCASE(REDKEY_RIGHT_CMD);
+ KEYCASE(REDKEY_MENU);
+ default:
+ DBG(0, "???");
+ }
+}
+
+#endif
+
+void Application::on_key_down(RedKey key)
+{
+ if (key <= 0 || key >= REDKEY_NUM_KEYS) {
+ return;
+ }
+
+ uint32_t scan_code = get_make_scan_code(key);
+ if (!scan_code) {
+ LOG_WARN("no make code for %d", key);
+ return;
+ }
+ _key_table[key].press = true;
+
+ int command = get_hotkeys_commnad();
+ if (command != APP_CMD_INVALID) {
+ do_command(command);
+ return;
+ }
+
+#ifdef WIN32
+ if (!_active_screen->intercepts_sys_key() &&
+ (key == REDKEY_LEFT_CMD || key == REDKEY_RIGHT_CMD ||
+ key == REDKEY_MENU || _key_table[REDKEY_L_ALT].press)) {
+ _key_table[key].press = false;
+ return;
+ }
+ if ((_key_table[REDKEY_L_CTRL].press || _key_table[REDKEY_R_CTRL].press) &&
+ (_key_table[REDKEY_L_ALT].press || _key_table[REDKEY_R_ALT].press)) {
+ if (key == REDKEY_END || key == REDKEY_PAD_1) {
+ _key_table[key].press = false;
+ _inputs_handler->on_key_down(get_make_scan_code(REDKEY_DELETE));
+ _inputs_handler->on_key_up(get_break_scan_code(REDKEY_DELETE));
+ } else if (key == REDKEY_DELETE || key == REDKEY_PAD_POINT) {
+ _key_table[key].press = false;
+ return;
+ }
+ }
+#endif
+
+ _inputs_handler->on_key_down(scan_code);
+}
+
+void Application::on_key_up(RedKey key)
+{
+ if (key < 0 || key >= REDKEY_NUM_KEYS || !_key_table[key].press) {
+ return;
+ }
+ _key_table[key].press = false;
+ uint32_t scan_code = get_break_scan_code(key);
+ if (!scan_code) {
+ LOG_WARN("no break code for %d", key);
+ return;
+ }
+ _inputs_handler->on_key_up(scan_code);
+}
+
+void Application::on_deactivate_screen(RedScreen* screen)
+{
+ if (_active_screen == screen) {
+ release_capture();
+ _active_screen = NULL;
+ }
+}
+
+void Application::on_activate_screen(RedScreen* screen)
+{
+ _active_screen = screen;
+}
+
+void Application::on_app_activated()
+{
+ _active = true;
+ _inputs_handler->on_focus_in();
+}
+
+void Application::on_app_deactivated()
+{
+ _active = false;
+ _inputs_handler->on_focus_out();
+#ifdef WIN32
+ if (!_changing_screens) {
+ exit_full_screen();
+ }
+#endif
+}
+
+bool Application::rearrange_monitors(RedScreen& screen)
+{
+ if (!_full_screen) {
+ return false;
+ }
+ bool capture = release_capture();
+ prepare_monitors();
+ position_screens();
+ if (capture && _main_screen != &screen) {
+ capture = false;
+ _main_screen->activate();
+ _main_screen->capture_inputs();
+ }
+ return capture;
+}
+
+Monitor* Application::find_monitor(int id)
+{
+ ASSERT(_monitors);
+ std::list<Monitor*>::const_iterator iter = _monitors->begin();
+ for (; iter != _monitors->end(); iter++) {
+ Monitor *mon = *iter;
+ if (mon->get_id() == id) {
+ return mon;
+ }
+ }
+ return NULL;
+}
+
+Monitor* Application::get_monitor(int id)
+{
+ Monitor *mon = find_monitor(id);
+ if ((mon = find_monitor(id))) {
+ mon->set_used();
+ }
+ return mon;
+}
+
+void Application::assign_monitors()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ ASSERT(!_screens[i]->has_monitor());
+ _screens[i]->set_monitor(get_monitor(i));
+ }
+ }
+}
+
+void Application::prepare_monitors()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ Monitor* mon;
+ if (_screens[i] && (mon = _screens[i]->get_monitor())) {
+ Point size = _screens[i]->get_size();
+ mon->set_mode(size.x, size.y);
+ }
+ }
+ //todo: test match of monitors size/position against real world size/position
+}
+
+void Application::restore_monitors()
+{
+ //todo: renew monitors (destroy + init)
+ std::list<Monitor*>::const_iterator iter = _monitors->begin();
+ for (; iter != _monitors->end(); iter++) {
+ (*iter)->restore();
+ }
+}
+
+void Application::position_screens()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ Monitor* mon;
+ if (_screens[i] && (mon = _screens[i]->get_monitor())) {
+ _screens[i]->position_full_screen(mon->get_position());
+ }
+ }
+}
+
+void Application::hide()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ _screens[i]->hide();
+ }
+ }
+}
+
+void Application::show()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ _screens[i]->show();
+ }
+ }
+}
+
+void Application::external_show()
+{
+ DBG(0, "Entry, _screens.size()=%lu", _screens.size());
+ for (size_t i = 0; i < _screens.size(); ++i) {
+ DBG(0, "%lu", i);
+ if (_screens[i]) {
+ _screens[i]->external_show();
+ }
+ }
+}
+
+void Application::show_full_screen()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ _screens[i]->show_full_screen();
+ }
+ }
+}
+
+void Application::enter_full_screen()
+{
+ _changing_screens = true;
+ release_capture();
+ assign_monitors();
+ hide();
+ prepare_monitors();
+ position_screens();
+ show_full_screen();
+ _main_screen->activate();
+ _main_screen->capture_inputs();
+ _changing_screens = false;
+ _full_screen = true;
+}
+
+void Application::exit_full_screen()
+{
+ if (!_full_screen) {
+ return;
+ }
+ release_capture();
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ Monitor* mon;
+ _screens[i]->exit_full_screen();
+ if ((mon = _screens[i]->get_monitor())) {
+ _screens[i]->set_monitor(NULL);
+ mon->set_free();
+ }
+ }
+ }
+ restore_monitors();
+ _full_screen = false;
+ show();
+ _main_screen->activate();
+}
+
+bool Application::toggle_full_screen()
+{
+ if (_full_screen) {
+ exit_full_screen();
+ } else {
+ enter_full_screen();
+ }
+ return _full_screen;
+}
+
+void Application::minimize()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ _screens[i]->minimize();
+ }
+ }
+}
+
+void Application::destroy_monitors()
+{
+ for (int i = 0; i < (int)_screens.size(); i++) {
+ if (_screens[i]) {
+ _screens[i]->set_monitor(NULL);
+ }
+ }
+ Platform::destroy_monitors();
+ _monitors = NULL;
+}
+
+void Application::init_monitors()
+{
+ exit_full_screen();
+ destroy_monitors();
+ _monitors = &Platform::init_monitors();
+}
+
+void Application::on_monitors_change()
+{
+ if (Monitor::is_self_change()) {
+ return;
+ }
+ exit_full_screen();
+ init_monitors();
+}
+
+void Application::on_display_mode_change()
+{
+ _client.on_display_mode_change();
+}
+
+void Application::show_splash(int screen_id)
+{
+ if (screen_id != 0) {
+ return;
+ }
+ release_capture();
+ (*_gui_layer).set_splash_mode();
+}
+
+void Application::hide_splash(int screen_id)
+{
+ if (screen_id != 0) {
+ return;
+ }
+ (*_gui_layer).set_info_mode();
+}
+
+uint32_t Application::get_mouse_mode()
+{
+ return _client.get_mouse_mode();
+}
+
+void Application::set_title(std::wstring& title)
+{
+ _title = title;
+
+ for (size_t i = 0; i < _screens.size(); ++i) {
+ if (_screens[i]) {
+ _screens[i]->set_name(_title);
+ }
+ }
+}
+
+bool Application::is_key_set_pressed(const HotkeySet& key_set)
+{
+ HotkeySet::const_iterator iter = key_set.begin();
+
+ while (iter != key_set.end()) {
+ if (!(_key_table[iter->main].press || _key_table[iter->alter].press)) {
+ break;
+ }
+ ++iter;
+ }
+
+ return iter == key_set.end();
+}
+
+int Application::get_hotkeys_commnad()
+{
+ HotKeys::const_iterator iter = _hot_keys.begin();
+
+ while (iter != _hot_keys.end()) {
+ if (is_key_set_pressed(iter->second)) {
+ break;
+ }
+ ++iter;
+ }
+
+ return (iter != _hot_keys.end()) ? iter->first : APP_CMD_INVALID;
+}
+
+bool Application::is_cad_pressed()
+{
+ return ((_key_table[REDKEY_L_CTRL].press || _key_table[REDKEY_R_CTRL].press) &&
+ (_key_table[REDKEY_L_ALT].press || _key_table[REDKEY_R_ALT].press) &&
+ (_key_table[REDKEY_DELETE].press || _key_table[REDKEY_PAD_POINT].press));
+}
+
+void Application::send_key_down(RedKey key)
+{
+ _inputs_handler->on_key_down(get_make_scan_code(key));
+}
+
+void Application::send_key_up(RedKey key)
+{
+ _inputs_handler->on_key_up(get_break_scan_code(key));
+}
+
+void Application::send_alt_ctl_del()
+{
+ send_key_down(REDKEY_L_CTRL);
+ send_key_down(REDKEY_L_ALT);
+ send_key_down(REDKEY_DELETE);
+
+ send_key_up(REDKEY_DELETE);
+ send_key_up(REDKEY_L_ALT);
+ send_key_up(REDKEY_L_CTRL);
+}
+
+void Application::send_ctrl_alt_end()
+{
+ send_key_down(REDKEY_L_CTRL);
+ send_key_down(REDKEY_L_ALT);
+ send_key_down(REDKEY_END);
+
+ send_key_up(REDKEY_L_CTRL);
+ send_key_up(REDKEY_L_ALT);
+ send_key_up(REDKEY_END);
+}
+
+void Application::send_command_hotkey(int action)
+{
+ HotKeys::const_iterator iter = _hot_keys.find(action);
+ if (iter != _hot_keys.end()) {
+ send_hotkey_key_set(iter->second);
+ }
+}
+
+void Application::send_hotkey_key_set(const HotkeySet& key_set)
+{
+ HotkeySet::const_iterator iter;
+
+ for (iter = key_set.begin(); iter != key_set.end(); ++iter) {
+ send_key_down(iter->main);
+ }
+
+ for (iter = key_set.begin(); iter != key_set.end(); ++iter) {
+ send_key_up(iter->main);
+ }
+}
+
+static inline 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;
+}
+
+bool Application::set_channels_security(CmdLineParser& parser, bool on, char *val)
+{
+ RedPeer::ConnectionOptions::Type option;
+ option = (on) ? RedPeer::ConnectionOptions::CON_OP_SECURE :
+ RedPeer::ConnectionOptions::CON_OP_UNSECURE;
+
+ typedef std::map< std::string, int> ChannelsNamesMap;
+ ChannelsNamesMap channels_names;
+ channels_names["main"] = RED_CHANNEL_MAIN;
+ channels_names["display"] = RED_CHANNEL_DISPLAY;
+ channels_names["inputs"] = RED_CHANNEL_INPUTS;
+ channels_names["cursor"] = RED_CHANNEL_CURSOR;
+ channels_names["playback"] = RED_CHANNEL_PLAYBACK;
+ channels_names["record"] = RED_CHANNEL_RECORD;
+
+ if (!strcmp(val, "all")) {
+ if ((val = parser.next_argument())) {
+ std::cout << "\"all\" is exclusive in secure-channels\n";
+ return false;
+ }
+ PeerConnectionOptMap::iterator iter = _peer_con_opt.begin();
+ for (; iter != _peer_con_opt.end(); iter++) {
+ (*iter).second = option;
+ }
+ return true;
+ }
+
+ do {
+ ChannelsNamesMap::iterator iter = channels_names.find(val);
+ if (iter == channels_names.end()) {
+ std::cout << "bad channel name \"" << val << "\" in secure-channels\n";
+ return false;
+ }
+ _peer_con_opt[(*iter).second] = option;
+ } while ((val = parser.next_argument()));
+ return true;
+}
+
+bool Application::set_canvas_option(CmdLineParser& parser, char *val)
+{
+ typedef std::map< std::string, CanvasOption> CanvasNamesMap;
+ CanvasNamesMap canvas_types;
+
+ canvas_types["cairo"] = CANVAS_OPTION_CAIRO;
+#ifdef WIN32
+ canvas_types["gdi"] = CANVAS_OPTION_GDI;
+#endif
+#ifdef USE_OGL
+ canvas_types["gl_fbo"] = CANVAS_OPTION_OGL_FBO;
+ canvas_types["gl_pbuff"] = CANVAS_OPTION_OGL_PBUFF;
+#endif
+ _canvas_types.clear();
+ do {
+ CanvasNamesMap::iterator iter = canvas_types.find(val);
+ if (iter == canvas_types.end()) {
+ std::cout << "bad canvas type \"" << val << "\"\n";
+ return false;
+ }
+ _canvas_types.resize(_canvas_types.size() + 1);
+ _canvas_types[_canvas_types.size() - 1] = (*iter).second;
+ } while ((val = parser.next_argument()));
+ return true;
+}
+
+bool Application::set_enable_channels(CmdLineParser& parser, bool enable, char *val)
+{
+ typedef std::map< std::string, int> ChannelsNamesMap;
+ ChannelsNamesMap channels_names;
+ channels_names["display"] = RED_CHANNEL_DISPLAY;
+ channels_names["inputs"] = RED_CHANNEL_INPUTS;
+ channels_names["cursor"] = RED_CHANNEL_CURSOR;
+ channels_names["playback"] = RED_CHANNEL_PLAYBACK;
+ channels_names["record"] = RED_CHANNEL_RECORD;
+
+ if (!strcmp(val, "all")) {
+ if ((val = parser.next_argument())) {
+ std::cout << "\"all\" is exclusive\n";
+ return false;
+ }
+ for (unsigned int i = 0; i < _enabled_channels.size(); i++) {
+ _enabled_channels[i] = enable;
+ }
+ return true;
+ }
+
+ do {
+ ChannelsNamesMap::iterator iter = channels_names.find(val);
+ if (iter == channels_names.end()) {
+ std::cout << "bad channel name \"" << val << "\"\n";
+ return false;
+ }
+ _enabled_channels[(*iter).second] = enable;
+ } while ((val = parser.next_argument()));
+ return true;
+}
+
+bool Application::process_cmd_line(int argc, char** argv)
+{
+ std::string host;
+ int sport = -1;
+ int port = -1;
+ bool auto_display_res = false;
+ bool full_screen = false;
+ std::string password;
+
+ enum {
+ SPICE_OPT_HOST = CmdLineParser::OPTION_FIRST_AVILABLE,
+ SPICE_OPT_PORT,
+ SPICE_OPT_SPORT,
+ SPICE_OPT_PASSWORD,
+ SPICE_OPT_FULL_SCREEN,
+ SPICE_OPT_SECURE_CHANNELS,
+ SPICE_OPT_UNSECURE_CHANNELS,
+ SPICE_OPT_ENABLE_CHANNELS,
+ SPICE_OPT_DISABLE_CHANNELS,
+ SPICE_OPT_CANVAS_TYPE,
+ };
+
+ CmdLineParser parser("Spice client", false);
+
+ parser.add(SPICE_OPT_HOST, "host", "spice server address", "host", true, 'h');
+ parser.set_reqired(SPICE_OPT_HOST);
+ parser.add(SPICE_OPT_PORT, "port", "spice server port", "port", true, 'p');
+ parser.add(SPICE_OPT_SPORT, "secure-port", "spice server secure port", "port", true, 's');
+ parser.add(SPICE_OPT_SECURE_CHANNELS, "secure-channels",
+ "force secure connection on the specified channels", "channel",
+ true);
+ parser.set_multi(SPICE_OPT_SECURE_CHANNELS, ',');
+ parser.add(SPICE_OPT_UNSECURE_CHANNELS, "unsecure-channels",
+ "force unsecure connection on the specified channels", "channel",
+ true);
+ parser.set_multi(SPICE_OPT_UNSECURE_CHANNELS, ',');
+ parser.add(SPICE_OPT_PASSWORD, "password", "server password", "password", true, 'w');
+ parser.add(SPICE_OPT_FULL_SCREEN, "full-screen", "open in full screen mode", "auto-conf",
+ false, 'f');
+
+ parser.add(SPICE_OPT_ENABLE_CHANNELS, "enable-channels", "enable channels", "channel", true);
+ parser.set_multi(SPICE_OPT_ENABLE_CHANNELS, ',');
+
+ parser.add(SPICE_OPT_DISABLE_CHANNELS, "disable-channels", "disable channels", "channel", true);
+ parser.set_multi(SPICE_OPT_DISABLE_CHANNELS, ',');
+
+ 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;
+
+ parser.begin(argc, argv);
+
+ char* val;
+ int op;
+ while ((op = parser.get_option(&val)) != CmdLineParser::OPTION_DONE) {
+ switch (op) {
+ case SPICE_OPT_HOST:
+ host = val;
+ break;
+ case SPICE_OPT_PORT: {
+ if ((port = str_to_port(val)) == -1) {
+ std::cout << "invalid port " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ break;
+ }
+ case SPICE_OPT_SPORT: {
+ if ((port = str_to_port(val)) == -1) {
+ std::cout << "invalid secure port " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ sport = port;
+ break;
+ }
+ case SPICE_OPT_FULL_SCREEN:
+ if (val) {
+ if (strcmp(val, "auto-conf")) {
+ std::cout << "invalid full screen mode " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ auto_display_res = true;
+ }
+ full_screen = true;
+ break;
+ case SPICE_OPT_PASSWORD:
+ password = val;
+ break;
+ case SPICE_OPT_SECURE_CHANNELS:
+ if (!set_channels_security(parser, true, val)) {
+ return false;
+ }
+ break;
+ case SPICE_OPT_UNSECURE_CHANNELS:
+ if (!set_channels_security(parser, false, val)) {
+ return false;
+ }
+ break;
+ case SPICE_OPT_ENABLE_CHANNELS:
+ if (!set_enable_channels(parser, true, val)) {
+ std::cout << "invalid channels " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ break;
+ case SPICE_OPT_DISABLE_CHANNELS:
+ if (!set_enable_channels(parser, false, val)) {
+ std::cout << "invalid channels " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ break;
+ case SPICE_OPT_CANVAS_TYPE:
+ if (!set_canvas_option(parser, val)) {
+ std::cout << "invalid canvas option " << val << "\n";
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+ break;
+ case CmdLineParser::OPTION_HELP:
+ parser.show_help();
+ return false;
+ case CmdLineParser::OPTION_ERROR:
+ return false;
+ default:
+ throw Exception("cmd line error");
+ }
+ }
+
+ if (parser.is_set(SPICE_OPT_SECURE_CHANNELS) && !parser.is_set(SPICE_OPT_SPORT)) {
+ std::cout << "missing --secure-port\n";
+ return false;
+ }
+
+ PeerConnectionOptMap::iterator iter = _peer_con_opt.begin();
+ for (; iter != _peer_con_opt.end(); iter++) {
+ if ((*iter).second == RedPeer::ConnectionOptions::CON_OP_SECURE) {
+ continue;
+ }
+
+ if ((*iter).second == RedPeer::ConnectionOptions::CON_OP_UNSECURE) {
+ continue;
+ }
+
+ if (parser.is_set(SPICE_OPT_PORT) && parser.is_set(SPICE_OPT_SPORT)) {
+ (*iter).second = RedPeer::ConnectionOptions::CON_OP_BOTH;
+ continue;
+ }
+
+ if (parser.is_set(SPICE_OPT_PORT)) {
+ (*iter).second = RedPeer::ConnectionOptions::CON_OP_UNSECURE;
+ continue;
+ }
+
+ if (parser.is_set(SPICE_OPT_SPORT)) {
+ (*iter).second = RedPeer::ConnectionOptions::CON_OP_SECURE;
+ continue;
+ }
+ std::cout << "missing --port or --sport\n";
+ 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());
+ }
+
+ _client.init(host.c_str(), port, sport, password.c_str(), auto_display_res);
+ if (auto_display_res) {
+ Monitor* mon = find_monitor(0);
+ ASSERT(mon);
+ Point size = mon->get_size();
+ _main_screen->set_mode(size.x, size.y, 32);
+ }
+
+ if (full_screen) {
+ enter_full_screen();
+ } else {
+ _main_screen->show(true, NULL);
+ }
+ return true;
+}
+
+void Application::init_logger()
+{
+ std::string temp_dir_name;
+ Platform::get_temp_dir(temp_dir_name);
+ std::string log_file_name = temp_dir_name + "spicec.log";
+
+ int fd = ::open(log_file_name.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0644);
+ if (fd == -1) {
+ log4cpp::BasicConfigurator::configure();
+ return;
+ }
+ log4cpp::Category& root = log4cpp::Category::getRoot();
+#ifdef RED_DEBUG
+ root.setPriority(log4cpp::Priority::DEBUG);
+ root.removeAllAppenders();
+ ::close(fd);
+ root.addAppender(new log4cpp::RollingFileAppender("_", log_file_name));
+#else
+ root.setPriority(log4cpp::Priority::INFO);
+ root.removeAllAppenders();
+ root.addAppender(new log4cpp::FileAppender("_", fd));
+#endif
+}
+
+void Application::init_globals()
+{
+ init_logger();
+ srand((unsigned)time(NULL));
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ cairo_canvas_init();
+#ifdef USE_OGL
+ gl_canvas_init();
+#endif
+ quic_init();
+#ifdef WIN32
+ gdi_canvas_init();
+#endif
+
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+ MUTEX_INIT(cairo_surface_user_data_mutex);
+#endif
+
+ Platform::init();
+ RedWindow::init();
+}
+
+int Application::main(int argc, char** argv, const char* version_str)
+{
+ init_globals();
+ LOG_INFO("starting %s", version_str);
+ std::auto_ptr<Application> app(new Application());
+ AutoAbort auto_abort(*app.get());
+ if (!app->process_cmd_line(argc, argv)) {
+ return app->_exit_code;
+ }
+ return app->run();
+}
+