[Spice-devel] [PATCH spice 8/9] spicec: add foreign menu

Hans de Goede hdegoede at redhat.com
Mon Oct 18 01:40:51 PDT 2010


Ack.

On 10/18/2010 10:20 AM, Arnon Gilboa wrote:
> Spice foreign menu enables external control of the client menu.
>
> The foreignmenu protocol enables an external application to:
> add a submenu, set its title, clear it, add/modify/remove an item etc.
>
> Foreign menu is based on the cross-platform named pipe.
> ---
>   client/Makefile.am         |    2 +
>   client/application.cpp     |   74 +++++++++-
>   client/application.h       |   26 +++-
>   client/foreign_menu.cpp    |  364 ++++++++++++++++++++++++++++++++++++++++++++
>   client/foreign_menu.h      |   98 ++++++++++++
>   client/windows/redc.vcproj |   18 ++-
>   client/x11/Makefile.am     |    2 +
>   7 files changed, 574 insertions(+), 10 deletions(-)
>   create mode 100644 client/foreign_menu.cpp
>   create mode 100644 client/foreign_menu.h
>
> diff --git a/client/Makefile.am b/client/Makefile.am
> index 185518a..09f11d5 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 c5d34ff..9a9a3f6 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 36ae86e..d6355ca 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 0000000..e5d7459
> --- /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 0000000..2fc4e53
> --- /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 6b5182c..538d2cb 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 02aa8eb..f6e9fda 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			\


More information about the Spice-devel mailing list