You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

437 lines
12 KiB

/***************************************************************************
kssocketio.cpp
-------------------
begin : Tue Jun 13 2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@friko.onet.pl
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include<assert.h>
#include<stdio.h> // P_tmpdir
#include<unistd.h> // unlink
#include<fcntl.h>
#include<sys/un.h>
#include<sys/types.h>
#include<sys/socket.h>
//#include<kapp.h>
#include<qapplication.h>
#include<qsocketnotifier.h>
#include"kssocketio.h"
#include"ksmatrix.h"
#include"kmatplotshell.h"
#include"kscommands.h"
#include"widgets/qsconsole.h"
#include"widgets/qsaxes2d.h"
#include"widgets/qsaxes3d.h"
#include"widgets/qscurve.h"
#include"widgets/qsimage.h"
#include"widgets/qscontour.h"
#include"widgets/qssurface.h"
#include"widgets/qsfigure.h"
//--------------------------------------------------------------//
KSSocketIO::KSSocketIO(QObject *parent, const char *name )
: QObject(parent,name), app_number( 0 ), socket_fd( -1 ), socket_notifier( NULL )
{
connection.socket_notifier = NULL;
connection.socket_fd = -1;
msg.data = NULL;
m_shell = NULL;
new_message();
available_axes_id = 0;
}
//--------------------------------------------------------------//
KSSocketIO::~KSSocketIO()
{
cleanup();
}
//--------------------------------------------------------------//
void KSSocketIO::setShell( KMatplotShell *shell )
{
if ( m_shell ) {
QObject::disconnect( m_shell->workbook(), SIGNAL(sigObjectAdded(QSCObject*)), this, SLOT(object_added(QSCObject*)) );
QObject::disconnect( m_shell->workbook(), SIGNAL(sigObjectRemoved(QSCObject*)), this, SLOT(object_removed(QSCObject*)) );
}
m_shell = shell;
m_workbook = m_shell->workbook();
if ( m_shell ) {
QObject::connect( m_shell->workbook(), SIGNAL(sigObjectAdded(QSCObject*)), this, SLOT(object_added(QSCObject*)) );
QObject::connect( m_shell->workbook(), SIGNAL(sigObjectRemoved(QSCObject*)), this, SLOT(object_removed(QSCObject*)) );
}
if ( socket_fd == -1 ) open_socket();
}
//--------------------------------------------------------------//
void KSSocketIO::setFileDescriptor( int fd )
{
assert( socket_fd == -1 );
struct sockaddr_un addr; unsigned int len = sizeof(addr);
memset( (char *)&addr, 0, sizeof(addr) );
if ( getsockname( fd, (struct sockaddr *)&addr, &len ) == 0 ) {
socket_name = QCString( addr.sun_path, sizeof(addr.sun_path) );
QCString number = socket_name;
number.remove( 0, name_prefix().length() );
app_number = number.toUInt();
// listen already called by a parent process
setup_socket( fd );
} else {
QSConsole::write( tr("File descriptor %1 does not point on a valid unix socket.").arg(fd) );
perror("");
}
}
//--------------------------------------------------------------//
int KSSocketIO::axesId( QSAxes *axes ) const
{
QMap<int,QSAxes*>::ConstIterator it;
for( it = m_axes.begin(); it != m_axes.end(); ++it )
if ( it.data() == axes ) return it.key();
return -1;
}
//--------------------------------------------------------------//
int KSSocketIO::registerAxes( QSAxes *axes, int id )
{
assert( axes );
assert( id >= 0 );
//cout << " Registered axes " << axes << " id " << id << endl;
m_axes[id] = axes;
return id;
}
//--------------------------------------------------------------//
void KSSocketIO::unregisterAxes( int id )
{
assert( id >= 0 );
m_axes.remove( id );
}
//--------------------------------------------------------------//
void KSSocketIO::open_socket()
{
assert( socket_fd == -1 );
int socket_fd = socket( AF_UNIX, SOCK_STREAM, 0 );
assert( socket_fd >= 0 );
struct sockaddr_un addr;
do {
app_number ++;
QCString number;
socket_name = name_prefix() + number.setNum(app_number);
memset( (char *)&addr, 0, sizeof(addr) );
addr.sun_family = AF_UNIX;
strcpy( addr.sun_path, (const char *)socket_name );
}
while( bind(socket_fd,(struct sockaddr *)&addr,strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 );
listen( socket_fd, 1 );
setup_socket( socket_fd );
}
//--------------------------------------------------------------//
void KSSocketIO::setup_socket( int fd )
{
socket_fd = fd;
fcntl( fd, F_SETFL, O_NONBLOCK );
socket_notifier = new QSocketNotifier( fd, QSocketNotifier::Read, this );
connect( socket_notifier, SIGNAL(activated(int)), this, SLOT(connection_requested(int)) );
connect( qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanup()) );
}
//--------------------------------------------------------------//
void KSSocketIO::close_socket()
{
if ( socket_fd >= 0 ) {
close( socket_fd );
delete socket_notifier;
socket_notifier = NULL;
socket_fd = -1;
unlink( (const char *)socket_name );
app_number = 0;
socket_name = QCString();
}
}
//--------------------------------------------------------------//
void KSSocketIO::cleanup()
{
disconnect();
close_socket();
}
//--------------------------------------------------------------//
void KSSocketIO::connection_requested(int)
{
accept_connection();
}
//--------------------------------------------------------------//
void KSSocketIO::connection_lost()
{
disconnect();
}
//--------------------------------------------------------------//
void KSSocketIO::accept_connection()
{
socket_notifier->setEnabled(false);
assert( socket_fd >= 0 );
assert( connection.socket_fd == -1 );
struct sockaddr_un addr; unsigned int len = sizeof(addr);
connection.socket_fd = accept( socket_fd, (struct sockaddr *)&addr, &len );
fcntl( connection.socket_fd, F_SETFL, O_NONBLOCK );
if ( connection.socket_fd >= 0 ) {
connection.socket_notifier = new QSocketNotifier( connection.socket_fd, QSocketNotifier::Read, this );
connect( connection.socket_notifier, SIGNAL(activated(int)), this, SLOT(read_data(int)) );
}
}
//--------------------------------------------------------------//
void KSSocketIO::disconnect()
{
if ( connection.socket_fd >= 0 ) {
delete connection.socket_notifier;
connection.socket_notifier = NULL;
close( connection.socket_fd );
connection.socket_fd = -1;
}
new_message();
if ( socket_notifier )
socket_notifier->setEnabled(true);
}
//--------------------------------------------------------------//
void KSSocketIO::read_data( int )
{
int nread = 0;
int hsize = sizeof(msg.header);
//
// read a message header
//
if ( msg.nread < hsize ) {
nread = read( connection.socket_fd, (char *)&msg.header + msg.nread, hsize - msg.nread );
}
//
// read a message body
//
else
if ( msg.nread < msg.dlen ) {
nread = read( connection.socket_fd, msg.data + msg.nread - hsize, msg.dlen - msg.nread );
}
if ( nread <= 0 ) { perror(""); connection_lost(); return; } else msg.nread += nread;
//
// header is read
//
if ( msg.nread == hsize ) { msg.dlen = sizeof(msg.header) + msg.header.h.dlen; msg.data = new char[msg.header.h.dlen]; }
//
// message is read.
//
if ( msg.nread >= hsize && msg.nread == msg.dlen ) {
int reply_code = message_ready();
new_message();
reply( reply_code );
}
}
//--------------------------------------------------------------//
int KSSocketIO::message_ready()
{
int reply_code = -1;
//
// Add plot the app is run as a standalone one.
//if ( !parts.contains(msg.header.h.plot) ) return reply_code;
//KMatplotShell *shell = m_shell;
if ( !m_shell ) return reply_code;
QSAxes *axes = m_axes[msg.header.h.plot];
if ( msg.header.h.type == MsgAddAxes ) {
if ( msg.header.a.axes ) {
QSAxes *new_axes = new QSAxes3D();
if ( m_workbook->page(0) ) m_workbook->execute( new KSCmdAddCObject(new_axes->shadowObject(),m_workbook->page(0)->objects()) );
} else {
QSAxes *new_axes = new QSAxes2D();
if ( m_workbook->page(0) ) m_workbook->execute( new KSCmdAddCObject(new_axes->shadowObject(),m_workbook->page(0)->objects()) );
}
reply_code = available_axes_id;
}
else
if ( msg.header.h.type == MsgRemoveAxes ) {
if ( !axes ) return reply_code;
bool ok = m_shell->workbook()->execute( new KSCmdRemoveCObject(axes->shadowObject()) );
if ( ok ) reply_code = 0;
}
else
if ( msg.header.h.type == MsgChannel ) {
if ( !axes ) return reply_code;
KSMatrix *m = KSMatrix::create( (EType )msg.header.c.etype );
assert( m );
m->setRawData( msg.data,
msg.header.c.rows,
msg.header.c.cols,
true,
msg.header.c.lineo,
msg.header.c.pixelo );
if ( axes->plot(msg.header.c.dnum) ) {
axes->plot(msg.header.c.dnum)->setMatrix( msg.header.c.chan, m );
reply_code = 0;
} else delete m;
// don't delete it.
msg.data = NULL;
}
else
if ( msg.header.h.type == MsgProperty ) {
if ( !axes ) return reply_code;
}
else
if ( msg.header.h.type == MsgAddDataset ) {
if ( !axes ) return reply_code;
QSPlot *p = NULL;
// int plots_number = axes->plotsCount();
// Ooopss !
switch ( msg.header.t.ptype ) {
case PlotCurve: p = new QSCurve( axes ); break;
case PlotImage: p = new QSImage( axes ); break;
case PlotContour: p = new QSGriddedContour( axes ); break;
case PlotSurface: p = new QSSurface( axes ); break;
case PlotFigure: p = new QSFigure( axes ); break;
}
if ( p ) {
axes->plotAdd( p );
reply_code = axes->plotCount()-1;
} else {
reply_code = -1;
}
}
else
if ( msg.header.h.type == MsgRemoveDataset ) {
if ( !axes ) return reply_code;
if ( axes->plot(msg.header.t.dnum) ) {
QSPlot *p = axes->plot(msg.header.t.dnum);
axes->plotRemove( p );
delete p;
reply_code = 0;
}
}
else
if ( msg.header.h.type == MsgRemoveAllDatasets ) {
if ( !axes ) return reply_code;
while( axes->plot(0) ) {
QSPlot *p = axes->plot(0);
axes->plotRemove( p );
delete p;
}
reply_code = 0;
}
return reply_code;
}
//--------------------------------------------------------------//
void KSSocketIO::new_message()
{
delete msg.data;
msg.dlen = 0;
msg.nread = 0;
msg.data = NULL;
}
//--------------------------------------------------------------//
void KSSocketIO::reply( int code )
{
_write_data( connection.socket_fd, (const char *)&code, sizeof(code) );
}
//--------------------------------------------------------------//
QCString KSSocketIO::name_prefix()
{
return QCString(P_tmpdir) + "/" + ".kmatplot." + getenv("USER") + ".";
}
//--------------------------------------------------------------//
int KSSocketIO::_write_data( int fd, const char *data, int len )
{
int nleft;
int bytes;
const char *ptr;
ptr = data;
nleft = len;
while( nleft > 0 ) {
bytes = write( fd, ptr, nleft );
if ( bytes < 0 ) break; /* error */
nleft -= bytes;
ptr += bytes;
}
return(len - nleft);
}
//--------------------------------------------------------------//
void KSSocketIO::object_added( QSCObject *object )
{
if ( object->isAxesShadow() ) {
QSAxes *axes = object->parentAxes();
++available_axes_id;
registerAxes( axes, available_axes_id );
}
}
//--------------------------------------------------------------//
void KSSocketIO::object_removed( QSCObject *object )
{
if ( object->isAxesShadow() ) {
QSAxes *axes= object->parentAxes();
int axes_id = axesId(axes);
if ( axes_id >= 0 ) unregisterAxes( axes_id );
}
}