summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/Makefile.am2
-rw-r--r--client/application.cpp74
-rw-r--r--client/application.h26
-rw-r--r--client/foreign_menu.cpp364
-rw-r--r--client/foreign_menu.h98
-rw-r--r--client/windows/redc.vcproj18
-rw-r--r--client/x11/Makefile.am2
7 files changed, 574 insertions, 10 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index 185518a9..09f11d5f 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -61,6 +61,8 @@ RED_COMMON_SRCS = \
debug.h \
display_channel.cpp \
display_channel.h \
+ foreign_menu.cpp \
+ foreign_menu.h \
glz_decoded_image.h \
glz_decoder_config.h \
glz_decoder.cpp \
diff --git a/client/application.cpp b/client/application.cpp
index c5d34ff0..9a9a3f68 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -335,6 +335,8 @@ enum AppCommands {
#ifdef USE_GUI
APP_CMD_SHOW_GUI,
#endif // USE_GUI
+ APP_CMD_EXTERNAL_BEGIN = 0x400,
+ APP_CMD_EXTERNAL_END = 0x800,
};
Application::Application()
@@ -586,6 +588,13 @@ void Application::switch_host(const std::string& host, int port, int sport,
int Application::run()
{
+ _exit_code = ProcessLoop::run();
+ return _exit_code;
+}
+
+void Application::on_start_running()
+{
+ _foreign_menu.reset(new ForeignMenu(this));
#ifdef USE_GUI
if (_gui_mode != GUI_MODE_FULL) {
connect();
@@ -595,8 +604,6 @@ int Application::run()
#else
connect();
#endif // HAVE_GUI
- _exit_code = ProcessLoop::run();
- return _exit_code;
}
RedScreen* Application::find_screen(int id)
@@ -974,6 +981,14 @@ void Application::do_command(int command)
show_gui();
break;
#endif // USE_GUI
+ default:
+ AppMenuItemMap::iterator iter = _app_menu_items.find(command);
+ ASSERT(iter != _app_menu_items.end());
+ AppMenuItem* item = &(*iter).second;
+ if (item->type == APP_MENU_ITEM_TYPE_FOREIGN) {
+ ASSERT(*_foreign_menu);
+ (*_foreign_menu)->on_command(item->conn_ref, item->ext_id);
+ }
}
}
@@ -1318,12 +1333,18 @@ void Application::on_app_activated()
{
_active = true;
_key_handler->on_focus_in();
+ if (*_foreign_menu) {
+ (*_foreign_menu)->on_activate();
+ }
}
void Application::on_app_deactivated()
{
_active = false;
_key_handler->on_focus_out();
+ if (*_foreign_menu) {
+ (*_foreign_menu)->on_deactivate();
+ }
#ifdef WIN32
if (!_changing_screens) {
exit_full_screen();
@@ -1573,7 +1594,7 @@ uint32_t Application::get_mouse_mode()
return _client.get_mouse_mode();
}
-void Application::set_title(std::wstring& title)
+void Application::set_title(const std::wstring& title)
{
_title = title;
@@ -1665,6 +1686,53 @@ void Application::send_hotkey_key_set(const HotkeySet& key_set)
}
}
+int Application::get_menu_item_id(AppMenuItemType type, int32_t conn_ref, uint32_t ext_id)
+{
+ int free_id = APP_CMD_EXTERNAL_BEGIN;
+ AppMenuItem item = {type, conn_ref, ext_id};
+ AppMenuItemMap::iterator iter = _app_menu_items.begin();
+ for (; iter != _app_menu_items.end(); iter++) {
+ if (!memcmp(&(*iter).second, &item, sizeof(item))) {
+ return (*iter).first;
+ } else if (free_id == (*iter).first && ++free_id > APP_CMD_EXTERNAL_END) {
+ return APP_CMD_INVALID;
+ }
+ }
+ _app_menu_items[free_id] = item;
+ return free_id;
+}
+
+void Application::clear_menu_items(int32_t opaque_conn_ref)
+{
+ AppMenuItemMap::iterator iter = _app_menu_items.begin();
+ AppMenuItemMap::iterator curr;
+
+ while (iter != _app_menu_items.end()) {
+ curr = iter++;
+ if (((*curr).second).conn_ref == opaque_conn_ref) {
+ _app_menu_items.erase(curr);
+ }
+ }
+}
+
+void Application::remove_menu_item(int item_id)
+{
+ _app_menu_items.erase(item_id);
+}
+
+int Application::get_foreign_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id)
+{
+ return get_menu_item_id(APP_MENU_ITEM_TYPE_FOREIGN, opaque_conn_ref, msg_id);
+}
+
+void Application::update_menu()
+{
+ for (size_t i = 0; i < _screens.size(); ++i) {
+ if (_screens[i]) {
+ _screens[i]->update_menu();
+ }
+ }
+}
//controller interface begin
diff --git a/client/application.h b/client/application.h
index 36ae86e8..d6355ca6 100644
--- a/client/application.h
+++ b/client/application.h
@@ -26,6 +26,7 @@
#include "menu.h"
#include "hot_keys.h"
#include "process_loop.h"
+#include "foreign_menu.h"
class RedScreen;
class Application;
@@ -138,10 +139,23 @@ typedef std::list<KeyHandler*> KeyHandlersStack;
typedef std::list<GUIBarrier*> GUIBarriers;
#endif // USE_GUI
+enum AppMenuItemType {
+ APP_MENU_ITEM_TYPE_INVALID,
+ APP_MENU_ITEM_TYPE_FOREIGN,
+};
+
+typedef struct AppMenuItem {
+ AppMenuItemType type;
+ int32_t conn_ref;
+ uint32_t ext_id;
+} AppMenuItem;
+
+typedef std::map<int, AppMenuItem> AppMenuItemMap;
+
class Application : public ProcessLoop,
public Platform::EventListener,
public Platform::DisplayModeListener,
- public CommandTarget {
+ public ForeignMenuInterface {
public:
enum State {
@@ -186,6 +200,7 @@ public:
void on_activate_screen(RedScreen* screen);
void on_start_screen_key_interception(RedScreen* screen);
void on_stop_screen_key_interception(RedScreen* screen);
+ virtual void on_start_running();
virtual void on_app_activated();
virtual void on_app_deactivated();
virtual void on_monitors_change();
@@ -200,7 +215,7 @@ public:
void exit_full_screen();
bool toggle_full_screen();
void minimize();
- void set_title(std::wstring& title);
+ void set_title(const std::wstring& title);
void hide();
void show();
void external_show();
@@ -216,6 +231,10 @@ public:
Menu* get_app_menu();
virtual void do_command(int command);
+ int get_foreign_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id);
+ void clear_menu_items(int32_t opaque_conn_ref);
+ void remove_menu_item(int item_id);
+ void update_menu();
//controller interface begin
bool connect(const std::string& host, int port, int sport, const std::string& password);
@@ -276,6 +295,7 @@ private:
void send_command_hotkey(int command);
void send_hotkey_key_set(const HotkeySet& key_set);
void menu_item_callback(unsigned int item_id);
+ int get_menu_item_id(AppMenuItemType type, int32_t conn_ref, uint32_t ext_id);
int get_hotkeys_commnad();
bool is_key_set_pressed(const HotkeySet& key_set);
void do_on_key_up(RedKey key);
@@ -341,6 +361,8 @@ private:
StickyInfo _sticky_info;
std::vector<int> _canvas_types;
AutoRef<Menu> _app_menu;
+ AutoRef<ForeignMenu> _foreign_menu;
+ AppMenuItemMap _app_menu_items;
#ifdef USE_GUI
std::auto_ptr<GUI> _gui;
AutoRef<GUITimer> _gui_timer;
diff --git a/client/foreign_menu.cpp b/client/foreign_menu.cpp
new file mode 100644
index 00000000..e5d7459d
--- /dev/null
+++ b/client/foreign_menu.cpp
@@ -0,0 +1,364 @@
+/*
+ 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 "foreign_menu.h"
+#include <spice/foreign_menu_prot.h>
+#include "menu.h"
+#include "utils.h"
+#include "debug.h"
+#include "platform.h"
+
+#define PIPE_NAME_MAX_LEN 50
+
+#ifdef WIN32
+#define PIPE_NAME "SpiceForeignMenu-%lu"
+#elif defined(__i386__)
+#define PIPE_NAME "/tmp/SpiceForeignMenu-%llu.uds"
+#else
+#define PIPE_NAME "/tmp/SpiceForeignMenu-%lu.uds"
+#endif
+
+ForeignMenu::ForeignMenu(ForeignMenuInterface *handler)
+ : _handler (handler)
+ , _active (false)
+ , _refs (1)
+{
+ char pipe_name[PIPE_NAME_MAX_LEN];
+
+ ASSERT(_handler != NULL);
+ snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id());
+ LOG_INFO("Creating a foreign menu connection %s", pipe_name);
+ _foreign_menu = NamedPipe::create(pipe_name, *this);
+ if (!_foreign_menu) {
+ LOG_ERROR("Failed to create a foreign menu connection");
+ }
+}
+
+ForeignMenu::~ForeignMenu()
+{
+ std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
+ for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
+ conn->second->reset_handler();
+ delete conn->second;
+ }
+ if (_foreign_menu) {
+ NamedPipe::destroy(_foreign_menu);
+ }
+}
+
+NamedPipe::ConnectionInterface& ForeignMenu::create()
+{
+ ForeignMenuConnection *conn = new ForeignMenuConnection(_handler, *this);
+
+ if (conn == NULL) {
+ throw Exception("Error allocating a new foreign menu connection");
+ }
+ return *conn;
+}
+
+void ForeignMenu::add_connection(NamedPipe::ConnectionRef conn_ref, ForeignMenuConnection *conn)
+{
+ _connections[conn_ref] = conn;
+ if (_active) {
+ send_active_state(conn, FOREIGN_MENU_APP_ACTIVATED);
+ }
+ conn->on_data();
+}
+
+void ForeignMenu::remove_connection(NamedPipe::ConnectionRef conn_ref)
+{
+ ForeignMenuConnection *conn = _connections[conn_ref];
+ _connections.erase(conn_ref);
+ delete conn;
+}
+
+void ForeignMenu::add_sub_menus()
+{
+ std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
+ for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
+ conn->second->add_sub_menu();
+ }
+}
+
+void ForeignMenu::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id)
+{
+ ForeignMenuConnection *conn = _connections[conn_ref];
+ FrgMenuEvent msg;
+
+ ASSERT(conn);
+ msg.base.id = FOREIGN_MENU_ITEM_EVENT;
+ msg.base.size = sizeof(FrgMenuEvent);
+ msg.id = id;
+ msg.action = FOREIGN_MENU_EVENT_CLICK;
+ conn->write_msg(&msg.base, msg.base.size);
+}
+
+void ForeignMenu::on_activate()
+{
+ std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
+ _active = true;
+ for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
+ send_active_state(conn->second, FOREIGN_MENU_APP_ACTIVATED);
+ }
+}
+
+void ForeignMenu::on_deactivate()
+{
+ std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
+ _active = false;
+ for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
+ send_active_state(conn->second, FOREIGN_MENU_APP_DEACTIVATED);
+ }
+}
+
+void ForeignMenu::send_active_state(ForeignMenuConnection *conn, int32_t cmd)
+{
+ FrgMenuMsg msg;
+
+ ASSERT(conn != NULL);
+ msg.id = cmd;
+ msg.size = sizeof(FrgMenuMsg);
+ conn->write_msg(&msg, msg.size);
+}
+
+ForeignMenuConnection::ForeignMenuConnection(ForeignMenuInterface *handler, ForeignMenu& parent)
+ : _handler (handler)
+ , _parent (parent)
+ , _sub_menu (NULL)
+ , _initialized (false)
+ , _write_pending (0)
+ , _write_pos (_write_buf)
+ , _read_pos (_read_buf)
+{
+}
+
+ForeignMenuConnection::~ForeignMenuConnection()
+{
+ if (_opaque != NamedPipe::INVALID_CONNECTION) {
+ NamedPipe::destroy_connection(_opaque);
+ }
+ if (_handler) {
+ AutoRef<Menu> app_menu(_handler->get_app_menu());
+ (*app_menu)->remove_sub(_sub_menu);
+ _handler->update_menu();
+ _handler->clear_menu_items(_opaque);
+ }
+ if (_sub_menu) {
+ _sub_menu->unref();
+ }
+}
+
+void ForeignMenuConnection::bind(NamedPipe::ConnectionRef conn_ref)
+{
+ _opaque = conn_ref;
+ _parent.add_connection(conn_ref, this);
+}
+
+void ForeignMenuConnection::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 ForeignMenuConnection::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(FrgMenuInitHeader)) {
+ FrgMenuInitHeader *init = (FrgMenuInitHeader *)pos;
+ if (init->magic != FOREIGN_MENU_MAGIC || init->version != FOREIGN_MENU_VERSION) {
+ LOG_ERROR("Bad foreign menu init, magic=0x%x version=%u", init->magic,
+ init->version);
+ _parent.remove_connection(_opaque);
+ return false;
+ }
+ if (nread < init->size) {
+ break;
+ }
+ if (!handle_init((FrgMenuInit*)init)) {
+ _parent.remove_connection(_opaque);
+ return false;
+ }
+ nread -= init->size;
+ pos += init->size;
+ _initialized = true;
+ }
+ if (!_initialized || nread < sizeof(FrgMenuMsg)) {
+ break;
+ }
+ FrgMenuMsg *hdr = (FrgMenuMsg *)pos;
+ if (hdr->size < sizeof(FrgMenuMsg)) {
+ LOG_ERROR("Bad foreign menu 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 ForeignMenuConnection::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 ForeignMenuConnection::handle_init(FrgMenuInit *init)
+{
+ std::string title = "Untitled";
+
+ ASSERT(_handler);
+ if (_sub_menu) {
+ LOG_ERROR("Foreign menu already initialized");
+ return false;
+ }
+ if (init->credentials != 0) {
+ LOG_ERROR("Foreign menu has wrong credentials 0x%x", init->credentials);
+ return false;
+ }
+ if (init->base.size > offsetof(FrgMenuInit, title)) {
+ ((char*)init)[init->base.size - 1] = '\0';
+ title = (char*)init->title;
+ }
+ _sub_menu = new Menu((CommandTarget&)*_handler, title);
+ add_sub_menu();
+ _handler->update_menu();
+ return true;
+}
+
+void ForeignMenuConnection::add_sub_menu()
+{
+ if (_sub_menu) {
+ AutoRef<Menu> app_menu(_handler->get_app_menu());
+ (*app_menu)->add_sub(_sub_menu);
+ }
+}
+
+bool ForeignMenuConnection::handle_message(FrgMenuMsg *hdr)
+{
+ ASSERT(_sub_menu);
+ ASSERT(_handler);
+ switch (hdr->id) {
+ case FOREIGN_MENU_SET_TITLE:
+ ((char*)hdr)[hdr->size - 1] = '\0';
+ _sub_menu->set_name((char*)((FrgMenuSetTitle*)hdr)->string);
+ break;
+ case FOREIGN_MENU_ADD_ITEM: {
+ FrgMenuAddItem *msg = (FrgMenuAddItem*)hdr;
+ ((char*)hdr)[hdr->size - 1] = '\0';
+ int id = _handler->get_foreign_menu_item_id(_opaque, msg->id);
+ _sub_menu->add_command((char*)msg->string, id, get_item_state(msg->type));
+ break;
+ }
+ case FOREIGN_MENU_REMOVE_ITEM: {
+ int id = _handler->get_foreign_menu_item_id(_opaque, ((FrgMenuRmItem*)hdr)->id);
+ _sub_menu->remove_command(id);
+ _handler->remove_menu_item(id);
+ break;
+ }
+ case FOREIGN_MENU_CLEAR:
+ _sub_menu->clear();
+ _handler->clear_menu_items(_opaque);
+ break;
+ case FOREIGN_MENU_MODIFY_ITEM:
+ default:
+ LOG_ERROR("Ignoring an unknown foreign menu identifier %u", hdr->id);
+ return false;
+ }
+ _handler->update_menu();
+ return true;
+}
+
+int ForeignMenuConnection::get_item_state(int item_type)
+{
+ int state = 0;
+
+ if (item_type & FOREIGN_MENU_ITEM_TYPE_CHECKED) {
+ state |= Menu::MENU_ITEM_STATE_CHECKED;
+ }
+ if (item_type & FOREIGN_MENU_ITEM_TYPE_DIM) {
+ state |= Menu::MENU_ITEM_STATE_DIM;
+ }
+ return state;
+}
diff --git a/client/foreign_menu.h b/client/foreign_menu.h
new file mode 100644
index 00000000..2fc4e535
--- /dev/null
+++ b/client/foreign_menu.h
@@ -0,0 +1,98 @@
+/*
+ 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_FOREIGN_MENU
+#define _H_FOREIGN_MENU
+
+#include "named_pipe.h"
+#include "menu.h"
+
+class ForeignMenuConnection;
+struct FrgMenuInit;
+struct FrgMenuMsg;
+
+class ForeignMenuInterface : public CommandTarget {
+public:
+ virtual ~ForeignMenuInterface() {}
+
+ virtual int get_foreign_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id) = 0;
+ virtual void clear_menu_items(int32_t opaque_conn_ref) = 0;
+ virtual void remove_menu_item(int item_id) = 0;
+ virtual Menu* get_app_menu() = 0;
+ virtual void update_menu() = 0;
+};
+
+class ForeignMenu : public NamedPipe::ListenerInterface {
+public:
+ ForeignMenu(ForeignMenuInterface *handler);
+ virtual ~ForeignMenu();
+
+ ForeignMenu* ref() { _refs++; return this;}
+ void unref() { if (!--_refs) delete this;}
+
+ virtual NamedPipe::ConnectionInterface &create();
+ void add_connection(NamedPipe::ConnectionRef conn_ref, ForeignMenuConnection *conn);
+ void remove_connection(NamedPipe::ConnectionRef conn_ref);
+ void add_sub_menus();
+ void on_command(NamedPipe::ConnectionRef conn_ref, int32_t id);
+ void on_activate();
+ void on_deactivate();
+
+private:
+ void send_active_state(ForeignMenuConnection *conn, int32_t cmd);
+
+private:
+ ForeignMenuInterface *_handler;
+ std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*> _connections;
+ NamedPipe::ListenerRef _foreign_menu;
+ bool _active;
+ int _refs;
+};
+
+#define FOREIGN_MENU_BUF_SIZE 4096
+
+class ForeignMenuConnection : public NamedPipe::ConnectionInterface {
+public:
+ ForeignMenuConnection(ForeignMenuInterface *handler, ForeignMenu& parent);
+ virtual ~ForeignMenuConnection();
+
+ virtual void bind(NamedPipe::ConnectionRef conn_ref);
+ virtual void on_data();
+ bool write_msg(const void *buf, int len);
+ void reset_handler() { _handler = NULL;}
+ void add_sub_menu();
+
+private:
+ bool read_msgs();
+ bool handle_init(FrgMenuInit *init);
+ bool handle_message(FrgMenuMsg *hdr);
+ int get_item_state(int item_type);
+
+private:
+ ForeignMenuInterface *_handler;
+ ForeignMenu& _parent;
+ Menu* _sub_menu;
+ bool _initialized;
+ int _write_pending;
+ uint8_t *_write_pos;
+ uint8_t *_read_pos;
+ uint8_t _write_buf[FOREIGN_MENU_BUF_SIZE];
+ uint8_t _read_buf[FOREIGN_MENU_BUF_SIZE];
+ RecurciveMutex _write_lock;
+};
+
+#endif
diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj
index 6b5182c8..538d2cb5 100644
--- a/client/windows/redc.vcproj
+++ b/client/windows/redc.vcproj
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="windows-1255"?>
<VisualStudioProject
ProjectType="Visual C++"
- Version="9,00"
+ Version="9.00"
Name="redc"
ProjectGUID="{4F03BAF9-DFBC-4CA7-B860-8929555981AE}"
RootNamespace="redc"
@@ -228,6 +228,10 @@
>
</File>
<File
+ RelativePath="..\foreign_menu.cpp"
+ >
+ </File>
+ <File
RelativePath="..\gdi_canvas.cpp"
>
</File>
@@ -508,6 +512,10 @@
>
</File>
<File
+ RelativePath="..\foreign_menu.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\gdi_canvas.h"
>
</File>
@@ -711,7 +719,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Generating demarshaller"
- CommandLine="generate.bat"
+ CommandLine="generate.bat&#x0D;&#x0A;"
AdditionalDependencies=""
Outputs="$(ProjectDir)/../generated_demarshallers.cpp;$(ProjectDir)/../generated_marshallers.cpp"
/>
@@ -722,7 +730,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Generating demarshaller"
- CommandLine="generate.bat"
+ CommandLine="generate.bat&#x0D;&#x0A;"
Outputs="$(ProjectDir)/../generated_demarshallers.cpp;$(ProjectDir)/../generated_marshallers.cpp"
/>
</FileConfiguration>
@@ -736,7 +744,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Generating old demarshaller"
- CommandLine="generate1.bat"
+ CommandLine="generate1.bat&#x0D;&#x0A;"
AdditionalDependencies=""
Outputs="$(ProjectDir)/../generated_demarshallers1.cpp;$(ProjectDir)/../generated_marshallers1.cpp"
/>
@@ -747,7 +755,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Generating old demarshaller"
- CommandLine="generate1.bat"
+ CommandLine="generate1.bat&#x0D;&#x0A;"
Outputs="$(ProjectDir)/../generated_demarshallers1.cpp;$(ProjectDir)/../generated_marshallers1.cpp"
/>
</FileConfiguration>
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 02aa8eb2..f6e9fda7 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -61,6 +61,8 @@ RED_COMMON_SRCS = \
$(CLIENT_DIR)/debug.h \
$(CLIENT_DIR)/display_channel.cpp \
$(CLIENT_DIR)/display_channel.h \
+ $(CLIENT_DIR)/foreign_menu.cpp \
+ $(CLIENT_DIR)/foreign_menu.h \
$(CLIENT_DIR)/glz_decoded_image.h \
$(CLIENT_DIR)/glz_decoder_config.h \
$(CLIENT_DIR)/glz_decoder.cpp \