diff options
Diffstat (limited to 'client/application.cpp')
-rw-r--r-- | client/application.cpp | 1674 |
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(); +} + |