diff options
-rw-r--r-- | client/Makefile.am | 2 | ||||
-rw-r--r-- | client/application.cpp | 145 | ||||
-rw-r--r-- | client/application.h | 15 | ||||
-rw-r--r-- | client/controller.cpp | 443 | ||||
-rw-r--r-- | client/controller.h | 116 | ||||
-rw-r--r-- | client/windows/redc.vcproj | 8 | ||||
-rw-r--r-- | client/x11/Makefile.am | 2 |
7 files changed, 700 insertions, 31 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 09f11d5f..5a14f5fd 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -54,6 +54,8 @@ RED_COMMON_SRCS = \ marshaller.cpp \ generated_marshallers.cpp \ generated_marshallers1.cpp \ + controller.cpp \ + controller.h \ cursor_channel.cpp \ cursor_channel.h \ cursor.cpp \ diff --git a/client/application.cpp b/client/application.cpp index 9a9a3f68..3aaa3e3a 100644 --- a/client/application.cpp +++ b/client/application.cpp @@ -357,6 +357,7 @@ Application::Application() , _monitors (NULL) , _title (L"SPICEc:%d") , _sys_key_intercept_mode (false) + , _enable_controller (false) #ifdef USE_GUI , _gui_mode (GUI_MODE_FULL) #endif // USE_GUI @@ -595,6 +596,11 @@ int Application::run() void Application::on_start_running() { _foreign_menu.reset(new ForeignMenu(this)); + if (_enable_controller) { + _controller.reset(new Controller(this)); + return; + } + //FIXME: _client.connect() or use the following instead? #ifdef USE_GUI if (_gui_mode != GUI_MODE_FULL) { connect(); @@ -988,6 +994,9 @@ void Application::do_command(int command) if (item->type == APP_MENU_ITEM_TYPE_FOREIGN) { ASSERT(*_foreign_menu); (*_foreign_menu)->on_command(item->conn_ref, item->ext_id); + } else if (item->type == APP_MENU_ITEM_TYPE_CONTROLLER) { + ASSERT(*_controller); + (*_controller)->on_command(item->conn_ref, item->ext_id); } } } @@ -1736,6 +1745,11 @@ void Application::update_menu() //controller interface begin +void Application::set_auto_display_res(bool auto_display_res) +{ + _client.set_auto_display_res(auto_display_res); +} + bool Application::connect(const std::string& host, int port, int sport, const std::string& password) { if (_state != DISCONNECTED) { @@ -1743,6 +1757,10 @@ bool Application::connect(const std::string& host, int port, int sport, const st } _client.set_target(host, port, sport); _client.set_password(password); + if (!set_channels_security(port, sport)) { + return false; + } + register_channels(); connect(); return true; } @@ -1757,9 +1775,52 @@ void Application::quit() ProcessLoop::quit(SPICEC_ERROR_CODE_SUCCESS); } +void Application::show_me(bool full_screen) +{ + if (full_screen) { + enter_full_screen(); + } else { + _main_screen->show(true, NULL); + } +} + void Application::hide_me() { - hide_gui(); +// hide_gui(); +// FIXME: this instead? + if (_full_screen) { + exit_full_screen(); + } + hide(); +} + +void Application::set_hotkeys(const std::string& hotkeys) +{ + std::auto_ptr<HotKeysParser> parser(new HotKeysParser(hotkeys, _commands_map)); + _hot_keys = parser->get(); +} + +int Application::get_controller_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id) +{ + return get_menu_item_id(APP_MENU_ITEM_TYPE_CONTROLLER, opaque_conn_ref, msg_id); +} + +void Application::set_menu(Menu* menu) +{ + if (menu) { + _app_menu.reset(menu->ref()); + } else { + init_menu(); + } + if (*_foreign_menu) { + (*_foreign_menu)->add_sub_menus(); + } + update_menu(); +} + +void Application::delete_menu() +{ + set_menu(NULL); } #ifdef USE_GUI @@ -1833,6 +1894,40 @@ bool Application::set_channels_security(CmdLineParser& parser, bool on, char *va return true; } +bool Application::set_channels_security(int port, int sport) +{ + 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 (port != -1 && sport != -1) { + (*iter).second = RedPeer::ConnectionOptions::CON_OP_BOTH; + continue; + } + + if (port != -1) { + (*iter).second = RedPeer::ConnectionOptions::CON_OP_UNSECURE; + continue; + } + + if (sport != -1) { + (*iter).second = RedPeer::ConnectionOptions::CON_OP_SECURE; + continue; + } + + _exit_code = SPICEC_ERROR_CODE_CMD_LINE_ERROR; + return false; + } + return true; +} + bool Application::set_connection_ciphers(const char* ciphers, const char* arg0) { _con_ciphers = ciphers; @@ -2026,7 +2121,7 @@ void Application::register_channels() bool Application::process_cmd_line(int argc, char** argv) { - std::string host; + std::string host = ""; int sport = -1; int port = -1; bool auto_display_res = false; @@ -2050,6 +2145,7 @@ bool Application::process_cmd_line(int argc, char** argv) SPICE_OPT_CANVAS_TYPE, SPICE_OPT_DISPLAY_COLOR_DEPTH, SPICE_OPT_DISABLE_DISPLAY_EFFECTS, + SPICE_OPT_CONTROLLER, }; #ifdef USE_GUI @@ -2068,7 +2164,6 @@ bool Application::process_cmd_line(int argc, char** argv) 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", @@ -2107,6 +2202,8 @@ bool Application::process_cmd_line(int argc, char** argv) "disable guest display effects", "wallpaper/font-smooth/animation/all", true); parser.set_multi(SPICE_OPT_DISABLE_DISPLAY_EFFECTS, ','); + parser.add(SPICE_OPT_CONTROLLER, "controller", "enable external controller"); + for (int i = SPICE_CHANNEL_MAIN; i < SPICE_END_CHANNEL; i++) { _peer_con_opt[i] = RedPeer::ConnectionOptions::CON_OP_INVALID; } @@ -2203,6 +2300,15 @@ bool Application::process_cmd_line(int argc, char** argv) return false; } break; + case SPICE_OPT_CONTROLLER: + if (argc > 2) { + Platform::term_printf("%s: controller cannot be combined with other options\n", + argv[0]); + _exit_code = SPICEC_ERROR_CODE_INVALID_ARG; + return false; + } + _enable_controller = true; + return true; case CmdLineParser::OPTION_HELP: parser.show_help(); return false; @@ -2214,42 +2320,21 @@ bool Application::process_cmd_line(int argc, char** argv) } } + if (host.empty()) { + Platform::term_printf("%s: missing --host\n", argv[0]); + return false; + } + if (parser.is_set(SPICE_OPT_SECURE_CHANNELS) && !parser.is_set(SPICE_OPT_SPORT)) { Platform::term_printf("%s: missing --secure-port\n", argv[0]); _exit_code = SPICEC_ERROR_CODE_CMD_LINE_ERROR; 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; - } - + if (!set_channels_security(port, sport)) { Platform::term_printf("%s: missing --port or --sport\n", argv[0]); - _exit_code = SPICEC_ERROR_CODE_CMD_LINE_ERROR; return false; } - register_channels(); _client.set_target(host, port, sport); diff --git a/client/application.h b/client/application.h index d6355ca6..2cff2d59 100644 --- a/client/application.h +++ b/client/application.h @@ -27,6 +27,7 @@ #include "hot_keys.h" #include "process_loop.h" #include "foreign_menu.h" +#include "controller.h" class RedScreen; class Application; @@ -142,6 +143,7 @@ typedef std::list<GUIBarrier*> GUIBarriers; enum AppMenuItemType { APP_MENU_ITEM_TYPE_INVALID, APP_MENU_ITEM_TYPE_FOREIGN, + APP_MENU_ITEM_TYPE_CONTROLLER, }; typedef struct AppMenuItem { @@ -155,7 +157,8 @@ typedef std::map<int, AppMenuItem> AppMenuItemMap; class Application : public ProcessLoop, public Platform::EventListener, public Platform::DisplayModeListener, - public ForeignMenuInterface { + public ForeignMenuInterface, + public ControllerInterface { public: enum State { @@ -237,11 +240,18 @@ public: void update_menu(); //controller interface begin + void set_auto_display_res(bool auto_display_res); bool connect(const std::string& host, int port, int sport, const std::string& password); void disconnect(); void quit(); + void show_me(bool full_screen); void hide_me(); + void set_hotkeys(const std::string& hotkeys); + int get_controller_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id); + void set_menu(Menu* menu); + void delete_menu(); void beep(); + #ifdef USE_GUI bool is_disconnect_allowed(); #endif @@ -260,6 +270,7 @@ public: private: bool set_channels_security(CmdLineParser& parser, bool on, char *val, const char* arg0); + bool set_channels_security(int port, int sport); bool set_connection_ciphers(const char* ciphers, const char* arg0); bool set_ca_file(const char* ca_file, const char* arg0); bool set_host_cert_subject(const char* subject, const char* arg0); @@ -362,6 +373,8 @@ private: std::vector<int> _canvas_types; AutoRef<Menu> _app_menu; AutoRef<ForeignMenu> _foreign_menu; + bool _enable_controller; + AutoRef<Controller> _controller; AppMenuItemMap _app_menu_items; #ifdef USE_GUI std::auto_ptr<GUI> _gui; diff --git a/client/controller.cpp b/client/controller.cpp new file mode 100644 index 00000000..b2937710 --- /dev/null +++ b/client/controller.cpp @@ -0,0 +1,443 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "controller.h" +#include <spice/controller_prot.h> +#include "cmd_line_parser.h" +#include "menu.h" +#include "utils.h" +#include "debug.h" +#include "platform.h" + +#define PIPE_NAME_MAX_LEN 50 + +#ifdef WIN32 +#define PIPE_NAME "SpiceController-%lu" +#elif defined(__i386__) +#define PIPE_NAME "/tmp/SpiceController-%llu.uds" +#else +#define PIPE_NAME "/tmp/SpiceController-%lu.uds" +#endif + +Controller::Controller(ControllerInterface *handler) + : _handler (handler) + , _exclusive (false) + , _refs (1) +{ + char pipe_name[PIPE_NAME_MAX_LEN]; + + ASSERT(_handler); + snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id()); + LOG_INFO("Creating a controller connection %s", pipe_name); + _pipe = NamedPipe::create(pipe_name, *this); + if (!_pipe) { + LOG_ERROR("Failed to create a controller connection"); + } +} + +Controller::~Controller() +{ + std::map<NamedPipe::ConnectionRef, ControllerConnection*>::const_iterator conn; + for (conn = _connections.begin(); conn != _connections.end(); ++conn) { + conn->second->reset_handler(); + delete conn->second; + } + if (_pipe) { + NamedPipe::destroy(_pipe); + } +} + +NamedPipe::ConnectionInterface& Controller::create() +{ + ControllerConnection *conn = new ControllerConnection(_handler, *this); + + if (conn == NULL) { + throw Exception("Error allocating a new controller connection"); + } + return *conn; +} + +bool Controller::set_exclusive(bool exclusive) +{ + if (_exclusive) { + LOG_ERROR("Cannot init controller, an exclusive controller already connected"); + return false; + } + if (exclusive && _connections.size() > 1) { + LOG_ERROR("Cannot init exclusive controller, other controllers already connected"); + return false; + } + _exclusive = exclusive; + return true; +} + +void Controller::add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn) +{ + _connections[conn_ref] = conn; + conn->on_data(); +} + +void Controller::remove_connection(NamedPipe::ConnectionRef conn_ref) +{ + ControllerConnection *conn = _connections[conn_ref]; + _connections.erase(conn_ref); + _exclusive = false; + delete conn; +} + +void Controller::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id) +{ + ControllerConnection *conn = _connections[conn_ref]; + ControllerValue msg; + + ASSERT(conn); + msg.base.id = CONTROLLER_MENU_ITEM_CLICK; + msg.base.size = sizeof(msg); + msg.value = id; + conn->write_msg(&msg.base, msg.base.size); +} + +ControllerConnection::ControllerConnection(ControllerInterface *handler, Controller& parent) + : _handler (handler) + , _parent (parent) + , _initialized (false) + , _write_pending (0) + , _write_pos (_write_buf) + , _read_pos (_read_buf) + , _port (-1) + , _sport (-1) + , _full_screen (false) + , _auto_display_res (false) +{ +} + +ControllerConnection::~ControllerConnection() +{ + if (_opaque != NamedPipe::INVALID_CONNECTION) { + NamedPipe::destroy_connection(_opaque); + } + if (_handler) { + _handler->clear_menu_items(_opaque); + _handler->delete_menu(); + } +} + +void ControllerConnection::bind(NamedPipe::ConnectionRef conn_ref) +{ + _opaque = conn_ref; + _parent.add_connection(conn_ref, this); +} + +void ControllerConnection::on_data() +{ + if (_write_pending) { + LOG_INFO("Resume pending write %d", _write_pending); + if (!write_msg(_write_pos, _write_pending)) { + return; + } + } + while (read_msgs()); +} + +bool ControllerConnection::read_msgs() +{ + uint8_t *pos = _read_buf; + size_t nread = _read_pos - _read_buf; + int32_t size; + + ASSERT(_handler); + ASSERT(_opaque != NamedPipe::INVALID_CONNECTION); + size = NamedPipe::read(_opaque, (uint8_t*)_read_pos, sizeof(_read_buf) - nread); + if (size == 0) { + return false; + } else if (size < 0) { + LOG_ERROR("Error reading from named pipe %d", size); + _parent.remove_connection(_opaque); + return false; + } + nread += size; + while (nread > 0) { + if (!_initialized && nread >= sizeof(ControllerInitHeader)) { + ControllerInitHeader *init = (ControllerInitHeader *)pos; + if (init->magic != CONTROLLER_MAGIC || init->version != CONTROLLER_VERSION) { + LOG_ERROR("Bad controller init, magic=0x%x version=%u", init->magic, + init->version); + _parent.remove_connection(_opaque); + return false; + } + if (nread < init->size) { + break; + } + if (!handle_init((ControllerInit*)init)) { + _parent.remove_connection(_opaque); + return false; + } + nread -= init->size; + pos += init->size; + _initialized = true; + } + if (!_initialized || nread < sizeof(ControllerMsg)) { + break; + } + ControllerMsg *hdr = (ControllerMsg *)pos; + if (hdr->size < sizeof(ControllerMsg)) { + LOG_ERROR("Bad controller message, size=%u", hdr->size); + _parent.remove_connection(_opaque); + return false; + } + if (nread < hdr->size) { + break; + } + handle_message(hdr); + nread -= hdr->size; + pos += hdr->size; + } + if (nread > 0 && pos != _read_buf) { + memcpy(_read_buf, pos, nread); + } + _read_pos = _read_buf + nread; + return true; +} + +bool ControllerConnection::write_msg(const void *buf, int len) +{ + RecurciveLock lock(_write_lock); + uint8_t *pos; + int32_t written = 0; + + ASSERT(_opaque != NamedPipe::INVALID_CONNECTION); + if (_write_pending && buf != _write_pos) { + if ((_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) && + !write_msg(_write_pos, _write_pending)) { + return false; + } + if (_write_pending) { + if (_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) { + DBG(0, "Dropping message, due to insufficient space in write buffer"); + return true; + } + memcpy(_write_pos + _write_pending, buf, len); + _write_pending += len; + } + } + pos = (uint8_t*)buf; + while (len && (written = NamedPipe::write(_opaque, pos, len)) > 0) { + pos += written; + len -= written; + } + if (len && written == 0) { + if (_write_pending) { + _write_pos = pos; + } else { + _write_pos = _write_buf; + memcpy(_write_buf, pos, len); + } + _write_pending = len; + } else if (written < 0) { + LOG_ERROR("Error writing to named pipe %d", written); + _parent.remove_connection(_opaque); + return false; + } else { + _write_pending = 0; + } + return true; +} + +bool ControllerConnection::handle_init(ControllerInit *init) +{ + ASSERT(_handler); + if (init->credentials != 0) { + LOG_ERROR("Controller menu has wrong credentials 0x%x", init->credentials); + return false; + } + if (!_parent.set_exclusive(init->flags & CONTROLLER_FLAG_EXCLUSIVE)) { + return false; + } + return true; +} + +bool ControllerConnection::handle_message(ControllerMsg *hdr) +{ + uint32_t value = ((ControllerValue*)hdr)->value; + char *data = (char*)((ControllerData*)hdr)->data; + + ASSERT(_handler); + switch (hdr->id) { + case CONTROLLER_HOST: + _host.assign(data); + break; + case CONTROLLER_PORT: + _port = value; + break; + case CONTROLLER_SPORT: + _sport = value; + break; + case CONTROLLER_PASSWORD: + _password.assign(data); + break; + case CONTROLLER_SECURE_CHANNELS: + case CONTROLLER_DISABLE_CHANNELS: + return set_multi_val(hdr->id, data); + case CONTROLLER_TLS_CIPHERS: + return _handler->set_connection_ciphers(data, "Controller"); + case CONTROLLER_CA_FILE: + return _handler->set_ca_file(data, "Controller"); + case CONTROLLER_HOST_SUBJECT: + return _handler->set_host_cert_subject(data, "Controller"); + case CONTROLLER_FULL_SCREEN: + _full_screen = !!(value & CONTROLLER_SET_FULL_SCREEN); + _handler->set_auto_display_res(!!(value & CONTROLLER_AUTO_DISPLAY_RES)); + break; + case CONTROLLER_SET_TITLE: { + std::wstring str; +#ifdef WIN32 + wstring_printf(str, L"%s", data); +#else + wstring_printf(str, L"%S", data); +#endif + _handler->set_title(str); + break; + } + case CONTROLLER_HOTKEYS: + _handler->set_hotkeys(data); + break; + case CONTROLLER_CONNECT: + _handler->connect(_host, _port, _sport, _password); + break; + case CONTROLLER_SHOW: + _handler->show_me(_full_screen); + break; + case CONTROLLER_HIDE: + _handler->hide_me(); + break; + case CONTROLLER_CREATE_MENU: + return create_menu((wchar_t*)data); + case CONTROLLER_DELETE_MENU: + _handler->delete_menu(); + break; + case CONTROLLER_SEND_CAD: + default: + LOG_ERROR("Ignoring an unknown controller message %u", hdr->id); + return false; + } + return true; +} + +#ifdef WIN32 +#define next_tok(str, delim, state) wcstok(str, delim) +#else +#define next_tok(str, delim, state) wcstok(str, delim, state) +#endif + +bool ControllerConnection::create_menu(wchar_t* resource) +{ + bool ret = true; + wchar_t* item_state = 0; + wchar_t* item_dup; + wchar_t* param; + std::string text; + int parent_id; + int flags; + int state; + int id; + + ASSERT(_handler); + AutoRef<Menu> app_menu(_handler->get_app_menu()); + AutoRef<Menu> menu(new Menu((*app_menu)->get_target(), "")); + wchar_t* item = next_tok(resource, CONTROLLER_MENU_ITEM_DELIMITER, &item_state); + while (item != NULL) { + item_dup = wcsdup(item); + ret = ret && (param = next_tok(item_dup, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) && + swscanf(param, L"%d", &parent_id); + ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) && + swscanf(param, L"%d", &id); + ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)); + if (ret) { + string_printf(text, "%S", param); + } + ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) && + swscanf(param, L"%d", &flags); + free(item_dup); + + if (!ret) { + DBG(0, "item parsing failed %S", item); + break; + } + DBG(0, "parent_id=%d, id=%d, text=%s, flags=%d", parent_id, id, text.c_str(), flags); + + AutoRef<Menu> sub_menu((*menu)->find_sub(parent_id)); + if (!(ret = !!*sub_menu)) { + DBG(0, "submenu not found %S", item); + break; + } + + if (flags & CONTROLLER_MENU_FLAGS_SEPARATOR) { + (*sub_menu)->add_separator(); + } else if (flags & CONTROLLER_MENU_FLAGS_POPUP) { + AutoRef<Menu> sub(new Menu((*app_menu)->get_target(), text, id)); + (*sub_menu)->add_sub(sub.release()); + } else { + state = 0; + if (flags & (CONTROLLER_MENU_FLAGS_DISABLED | CONTROLLER_MENU_FLAGS_GRAYED)) { + state |= Menu::MENU_ITEM_STATE_DIM; + } + if (flags & CONTROLLER_MENU_FLAGS_CHECKED) { + state |= Menu::MENU_ITEM_STATE_CHECKED; + } + if (id >= SPICE_MENU_INTERNAL_ID_BASE) { + id = ((id - SPICE_MENU_INTERNAL_ID_BASE) >> SPICE_MENU_INTERNAL_ID_SHIFT) + 1; + } else { + id = _handler->get_controller_menu_item_id(_opaque, id); + } + (*sub_menu)->add_command(text, id, state); + } + item = next_tok(item + wcslen(item) + 1, CONTROLLER_MENU_ITEM_DELIMITER, &item_state); + } + if (ret) { + _handler->set_menu(*menu); + } + return ret; +} + +bool ControllerConnection::set_multi_val(uint32_t op, char* multi_val) +{ + CmdLineParser parser("", false); + int id = CmdLineParser::OPTION_FIRST_AVILABLE; + char* argv[] = {NULL, (char*)"--set", multi_val}; + char* val; + + ASSERT(_handler); + parser.add(id, "set", "none", "none", true); + parser.set_multi(id, ','); + parser.begin(3, argv); + if (parser.get_option(&val) != id) { + return false; + } + switch (op) { + case CONTROLLER_SECURE_CHANNELS: + _handler->set_channels_security(parser, true, val, "Controller"); + break; + case CONTROLLER_DISABLE_CHANNELS: + _handler->set_enable_channels(parser, false, val, "Controller"); + break; + default: + DBG(0, "unsupported op %u", op); + return false; + } + return true; +} diff --git a/client/controller.h b/client/controller.h new file mode 100644 index 00000000..89b2c234 --- /dev/null +++ b/client/controller.h @@ -0,0 +1,116 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_CONTROLLER_MENU +#define _H_CONTROLLER_MENU + +#include "named_pipe.h" +#include "threads.h" + +class ControllerConnection; +struct ControllerInit; +struct ControllerMsg; +class CmdLineParser; +class Menu; + +class ControllerInterface { +public: + virtual ~ControllerInterface() {} + + virtual bool connect(const std::string& host, int port, int sport, + const std::string& password) = 0; + virtual void set_title(const std::wstring& title) = 0; + virtual void set_auto_display_res(bool auto_display_res) = 0; + virtual void show_me(bool full_screen) = 0; + virtual void hide_me() = 0; + virtual bool set_channels_security(CmdLineParser& parser, bool on, char *val, + const char* arg0) = 0; + virtual bool set_enable_channels(CmdLineParser& parser, bool enable, char *val, + const char* arg0) = 0; + virtual bool set_connection_ciphers(const char* ciphers, const char* arg0) = 0; + virtual bool set_ca_file(const char* ca_file, const char* arg0) = 0; + virtual bool set_host_cert_subject(const char* subject, const char* arg0) = 0; + virtual void set_hotkeys(const std::string& hotkeys) = 0; + virtual int get_controller_menu_item_id(int32_t opaque_conn_ref, uint32_t id) = 0; + virtual void clear_menu_items(int32_t opaque_conn_ref) = 0; + virtual Menu* get_app_menu() = 0; + virtual void set_menu(Menu* menu) = 0; + virtual void delete_menu() = 0; +}; + +class Controller : public NamedPipe::ListenerInterface { +public: + Controller(ControllerInterface *handler); + virtual ~Controller(); + + Controller* ref() { _refs++; return this;} + void unref() { if (!--_refs) delete this;} + + virtual NamedPipe::ConnectionInterface &create(); + bool set_exclusive(bool exclusive); + void add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn); + void remove_connection(NamedPipe::ConnectionRef conn_ref); + void on_command(NamedPipe::ConnectionRef conn_ref, int32_t id); + +private: + ControllerInterface *_handler; + std::map<NamedPipe::ConnectionRef, ControllerConnection*> _connections; + NamedPipe::ListenerRef _pipe; + bool _exclusive; + int _refs; +}; + +#define CONTROLLER_BUF_SIZE 4096 + +class ControllerConnection : public NamedPipe::ConnectionInterface { +public: + ControllerConnection(ControllerInterface *handler, Controller& parent); + virtual ~ControllerConnection(); + + virtual void bind(NamedPipe::ConnectionRef conn_ref); + virtual void on_data(); + bool write_msg(const void *buf, int len); + void reset_handler() { _handler = NULL;} + +private: + bool read_msgs(); + bool handle_init(ControllerInit *init); + bool handle_message(ControllerMsg *hdr); + bool create_menu(wchar_t* resource); + bool set_multi_val(uint32_t op, char* multi_val); + +private: + ControllerInterface *_handler; + Controller& _parent; + bool _initialized; + + int _write_pending; + uint8_t *_write_pos; + uint8_t *_read_pos; + uint8_t _write_buf[CONTROLLER_BUF_SIZE]; + uint8_t _read_buf[CONTROLLER_BUF_SIZE]; + RecurciveMutex _write_lock; + + std::string _host; + std::string _password; + int _port; + int _sport; + bool _full_screen; + bool _auto_display_res; +}; + +#endif diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj index 538d2cb5..4276b907 100644 --- a/client/windows/redc.vcproj +++ b/client/windows/redc.vcproj @@ -212,6 +212,10 @@ >
</File>
<File
+ RelativePath="..\controller.cpp"
+ >
+ </File>
+ <File
RelativePath="..\cursor.cpp"
>
</File>
@@ -484,6 +488,10 @@ >
</File>
<File
+ RelativePath="..\controller.h"
+ >
+ </File>
+ <File
RelativePath="..\cursor.h"
>
</File>
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am index f6e9fda7..45ff7fcb 100644 --- a/client/x11/Makefile.am +++ b/client/x11/Makefile.am @@ -54,6 +54,8 @@ RED_COMMON_SRCS = \ $(CLIENT_DIR)/client_net_socket.cpp \ $(CLIENT_DIR)/client_net_socket.h \ $(CLIENT_DIR)/common.h \ + $(CLIENT_DIR)/controller.cpp \ + $(CLIENT_DIR)/controller.h \ $(CLIENT_DIR)/cursor_channel.cpp \ $(CLIENT_DIR)/cursor_channel.h \ $(CLIENT_DIR)/cursor.cpp \ |