summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/Makefile.am2
-rw-r--r--client/application.cpp145
-rw-r--r--client/application.h15
-rw-r--r--client/controller.cpp443
-rw-r--r--client/controller.h116
-rw-r--r--client/windows/redc.vcproj8
-rw-r--r--client/x11/Makefile.am2
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 \