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

Arnon Gilboa agilboa at redhat.com
Sun Oct 17 07:13:46 PDT 2010


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         |    3 +
 client/application.cpp     |   74 +++++++++-
 client/application.h       |   26 +++-
 client/foreign_menu.cpp    |  364 ++++++++++++++++++++++++++++++++++++++++++++
 client/foreign_menu.h      |   98 ++++++++++++
 client/foreign_menu_prot.h |  107 +++++++++++++
 client/windows/redc.vcproj |   14 ++-
 client/x11/Makefile.am     |    3 +
 8 files changed, 683 insertions(+), 6 deletions(-)
 create mode 100644 client/foreign_menu.cpp
 create mode 100644 client/foreign_menu.h
 create mode 100644 client/foreign_menu_prot.h

diff --git a/client/Makefile.am b/client/Makefile.am
index 185518a..0b3109b 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -61,6 +61,9 @@ RED_COMMON_SRCS =			\
 	debug.h				\
 	display_channel.cpp		\
 	display_channel.h		\
+	foreign_menu.cpp		\
+	foreign_menu.h			\
+	foreign_menu_prot.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..365e84a
--- /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 "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/foreign_menu_prot.h b/client/foreign_menu_prot.h
new file mode 100644
index 0000000..8c22461
--- /dev/null
+++ b/client/foreign_menu_prot.h
@@ -0,0 +1,107 @@
+/*
+   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_PROT
+#define _H_FOREIGN_MENU_PROT
+
+#define FOREIGN_MENU_MAGIC      (*(uint32_t*)"FRGM")
+#define FOREIGN_MENU_VERSION    1
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#define ATTR_PACKED __declspec(align(1))
+#endif
+
+typedef struct ATTR_PACKED FrgMenuInitHeader {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t size;
+} FrgMenuInitHeader;
+
+typedef struct ATTR_PACKED FrgMenuInit {
+    FrgMenuInitHeader base;
+    uint64_t credentials;
+    uint8_t title[0]; //UTF8
+} FrgMenuInit;
+
+typedef struct ATTR_PACKED FrgMenuMsg {
+    uint32_t id;
+    uint32_t size;
+} FrgMenuMsg;
+
+enum {
+    //extrenal app -> spice client
+    FOREIGN_MENU_SET_TITLE = 1,
+    FOREIGN_MENU_ADD_ITEM,
+    FOREIGN_MENU_MODIFY_ITEM,
+    FOREIGN_MENU_REMOVE_ITEM,
+    FOREIGN_MENU_CLEAR,
+
+    //spice client -> external app
+    FOREIGN_MENU_ITEM_EVENT = 1001,
+    FOREIGN_MENU_APP_ACTIVATED,
+    FOREIGN_MENU_APP_DEACTIVATED,
+};
+
+typedef struct ATTR_PACKED FrgMenuSetTitle {
+    FrgMenuMsg base;
+    uint8_t string[0]; //UTF8
+} FrgMenuSetTitle;
+
+enum {
+    FOREIGN_MENU_ITEM_TYPE_CHECKED      = 1 << 0,
+    FOREIGN_MENU_ITEM_TYPE_DIM          = 1 << 1,
+    FOREIGN_MENU_ITEM_TYPE_SEPARATOR    = 1 << 2
+};
+
+#define FOREIGN_MENU_INVALID_ID 0
+
+typedef struct ATTR_PACKED FrgMenuAddItem {
+    FrgMenuMsg base;
+    uint32_t id;
+    uint32_t type;
+    uint32_t position;
+    uint8_t string[0]; //UTF8
+} FrgMenuAddItem, FrgMenuModItem;
+
+typedef struct ATTR_PACKED FrgMenuRmItem {
+    FrgMenuMsg base;
+    uint32_t id;
+} FrgMenuRmItem;
+
+typedef struct FrgMenuMsg FrgMenuRmItems;
+typedef struct FrgMenuMsg FrgMenuDelete;
+
+enum {
+    FOREIGN_MENU_EVENT_CLICK,
+    FOREIGN_MENU_EVENT_CHECKED,
+    FOREIGN_MENU_EVENT_UNCHECKED
+};
+
+typedef struct ATTR_PACKED FrgMenuEvent {
+    FrgMenuMsg base;
+    uint32_t id;
+    uint32_t action; //FOREIGN_MENU_EVENT_?
+} FrgMenuEvent;
+
+typedef struct FrgMenuMsg FrgMenuActivate;
+typedef struct FrgMenuMsg FrgMenuDeactivate;
+
+#undef ATTR_PACKED
+
+#endif
diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj
index 6b5182c..4ed240f 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,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\foreign_menu.h"
+				>
+			</File>
+			<File
+				RelativePath="..\foreign_menu_prot.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\common\gdi_canvas.h"
 				>
 			</File>
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 02aa8eb..fd3266b 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -61,6 +61,9 @@ 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)/foreign_menu_prot.h		\
 	$(CLIENT_DIR)/glz_decoded_image.h		\
 	$(CLIENT_DIR)/glz_decoder_config.h		\
 	$(CLIENT_DIR)/glz_decoder.cpp			\
-- 
1.5.5.6



More information about the Spice-devel mailing list