/*
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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include "common.h"
#include "foreign_menu.h"
#include
#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__) || __SIZEOF_LONG__ == 4
#define PIPE_NAME "/tmp/SpiceForeignMenu-%llu.uds"
#else
#define PIPE_NAME "/tmp/SpiceForeignMenu-%lu.uds"
#endif
ForeignMenu::ForeignMenu(ForeignMenuInterface *handler, bool active)
: _handler (handler)
, _active (active)
, _refs (1)
{
char pipe_name[PIPE_NAME_MAX_LEN];
ASSERT(_handler != NULL);
#ifndef WIN32
const char *p_socket = getenv("SPICE_FOREIGN_MENU_SOCKET");
if (p_socket) {
LOG_INFO("Creating a foreign menu connection %s", p_socket);
_foreign_menu = NamedPipe::create(p_socket, *this);
} else
#endif
{
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::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::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::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::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