mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
1683 lines
44 KiB
C++
1683 lines
44 KiB
C++
/*
|
|
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 "tunnel_channel.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;
|
|
channels_names["tunnel"] = RED_CHANNEL_TUNNEL;
|
|
|
|
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;
|
|
channels_names["tunnel"] = RED_CHANNEL_TUNNEL;
|
|
|
|
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;
|
|
_peer_con_opt[RED_CHANNEL_TUNNEL] = 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());
|
|
}
|
|
|
|
if (_enabled_channels[RED_CHANNEL_TUNNEL]) {
|
|
_client.register_channel_factory(TunnelChannel::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();
|
|
}
|
|
|