Revert previous commit, was incorrect
[amarok.git] / src / socketserver.cpp
blob39f8f23e33be100b86bb96318336ea1387cd4bb8
1 /***************************************************************************
2 * Copyright (C) 2004,5 Max Howell <max.howell@methylblue.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 ***************************************************************************/
11 #define DEBUG_PREFIX "SocketServer"
13 #include "socketserver.h"
15 #include "app.h"
16 #include "amarok.h"
17 #include "debug.h"
18 #include "enginebase.h" //to get the scope
19 #include "enginecontroller.h" //to get the engine
20 #include "Process.h"
21 #include "ContextStatusBar.h"
23 #include <KDialog>
24 #include <KLocale>
25 #include <KMenu> //Vis::Selector
26 #include <KStandardDirs> //locateLocal()
27 #include <KWindowSystem> //Vis::Selector
29 #include <Q3PopupMenu>
30 #include <QByteArray>
31 #include <QPaintEvent>
32 #include <QToolTip> //Vis::Selector ctor
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37 #include <unistd.h>
38 #include <vector>
41 //TODO allow stop/start and pause signals to be sent to registered visualizations
42 //TODO allow transmission of visual data back to us here and allow that to be embedded in stuff
43 //TODO decide whether to use 16 bit integers or 32 bit floats as data sent to analyzers
44 //TODO allow visualizations to determine their own data sizes
46 /// @class Amarok::SocketServer
48 Amarok::SocketServer::SocketServer( const QString &socketName, QObject *parent )
49 : Q3ServerSocket( parent )
51 m_sockfd = ::socket( AF_UNIX, SOCK_STREAM, 0 );
53 if( m_sockfd == -1 ) {
54 warning() << "socket() error\n";
55 return;
58 m_path = KStandardDirs::locateLocal( "socket", socketName ).toLocal8Bit();
60 union {
61 sockaddr_un un;
62 sockaddr sa;
63 } local;
64 local.un.sun_family = AF_UNIX;
65 qstrcpy( &local.un.sun_path[0], m_path );
66 ::unlink( m_path ); //FIXME why do we delete it?
68 if( ::bind( m_sockfd, &local.sa, sizeof(local.un) ) == -1 ) {
69 warning() << "bind() error\n";
70 ::close( m_sockfd );
71 m_sockfd = -1;
72 return;
75 if( ::listen( m_sockfd, 1 ) == -1 ) {
76 warning() << "listen() error\n";
77 ::close( m_sockfd );
78 m_sockfd = -1;
79 return;
82 this->setSocket( m_sockfd );
85 Amarok::SocketServer::~SocketServer()
87 if( m_sockfd != -1 )
88 ::close( m_sockfd );
93 /// @class Vis::SocketServer
95 Vis::SocketServer::SocketServer( QObject *parent )
96 : Amarok::SocketServer( "amarok.visualization_socket", parent )
99 void
100 Vis::SocketServer::newConnection( int sockfd )
102 debug() << "Connection requested: " << sockfd;
103 new SocketNotifier( sockfd ); //handles its own memory
108 /// @class Vis::SocketNotifier
110 Vis::SocketNotifier::SocketNotifier( int sockfd )
111 : QSocketNotifier( sockfd, QSocketNotifier::Read, this )
113 connect( this, SIGNAL(activated( int )), SLOT(request( int )) );
116 void
117 Vis::SocketNotifier::request( int sockfd ) //slot
119 char buf[16]; //TODO docs should state request commands can only be 16 bytes
120 int nbytes = recv( sockfd, buf, 16, 0 );
122 if( nbytes > 0 )
124 QByteArray result( buf );
126 if( result == "REG" )
128 pid_t *pid = reinterpret_cast<pid_t*>(buf + 4);
130 debug() << "Registration pid: " << *pid;
132 Vis::Selector::instance()->mapPID( *pid, sockfd );
134 else if( result == "PCM" )
136 const Engine::Scope &scope = EngineController::engine()->scope();
138 ::send( sockfd, (const char *)&scope[0], scope.size()*sizeof(int16_t), 0 );
141 else {
142 debug() << "recv() error, closing socket: " << sockfd;
143 ::close( sockfd );
144 delete this;
150 /// @class Vis::Selector
152 Vis::Selector*
153 Vis::Selector::instance()
155 QWidget *parent = reinterpret_cast<QWidget*>( pApp->mainWindow() );
156 QObject *o = parent->findChild<QObject *>( "Vis::Selector::instance" );
158 debug() << bool(o == 0);
160 return o ? static_cast<Selector*>( o ) : new Selector( parent );
163 Vis::Selector::Selector( QWidget *parent )
164 : Q3ListView( parent, "Vis::Selector::instance", Qt::WType_Dialog )
165 , m_server( new SocketServer( this ) )
167 Amarok::OverrideCursor waitcursor;
169 setCaption( KDialog::makeStandardCaption( i18n( "Visualizations" ) ) );
171 // Gives the window a small title bar, and skips a taskbar entry
172 #ifdef Q_WS_X11
173 KWindowSystem::setType( winId(), NET::Utility );
174 KWindowSystem::setState( winId(), NET::SkipTaskbar );
175 #endif
177 setSorting( 0 );
178 setColumnWidthMode( 0, Q3ListView::Maximum );
179 viewport()->setToolTip( i18n( "Right-click on item for context menu" ) );
180 addColumn( QString() );
181 addColumn( QString() );
182 reinterpret_cast<QWidget*>(header())->hide();
185 connect( this, SIGNAL(contextMenuRequested( Q3ListViewItem*, const QPoint&, int )),
186 this, SLOT(rightButton( Q3ListViewItem*, const QPoint&, int )) );
188 // Can I get a pointer to the data section of a QCString?
189 char str[4096];
190 FILE* vis = popen( "amarok_libvisual --list", "r" );
191 str[ fread( static_cast<void*>( str ), sizeof(char), 4096, vis ) ] = '\0';
192 pclose( vis );
194 const QStringList entries = QStringList::split( '\n', QString::fromLocal8Bit( str ) );
196 for( QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it )
197 new Item( this, "amarok_libvisual", *it, "libvisual" );
199 resize( sizeHint() + QSize(20,0) );
200 // Center the widget on screen
201 move( parentWidget()->width()/2 - width()/2, parentWidget()->height()/2 - height()/2 );
204 void
205 Vis::Selector::processExited( Process *proc )
207 for( Item *item = static_cast<Item*>( firstChild() ); item; item = static_cast<Item*>( item->nextSibling() ) )
208 if( item->m_proc == proc )
209 item->setOn( false ); //will delete m_proc via stateChange( bool )
212 // Shouldn't be necessary, but it's part of a fix to make libvisual work again when running with amarok binary
213 void
214 Vis::Selector::receivedStdout( Process *proc )
216 debug() << QString::fromLatin1( proc->readAllStandardOutput() );
219 void
220 Vis::Selector::mapPID( int pid, int sockfd )
222 //TODO if we don't find the PID, request process plugin so we can assign the correct checkitem
224 for( Item *item = static_cast<Item*>( firstChild() ); item; item = static_cast<Item*>( item->nextSibling() ) )
225 if( item->m_proc && item->m_proc->pid() == pid )
227 item->m_sockfd = sockfd;
228 return;
231 debug() << "No matching pid in the Vis::Selector!\n";
234 void
235 Vis::Selector::rightButton( Q3ListViewItem* qitem, const QPoint& pos, int )
237 //TODO if the vis is not running it cannot be configured and you shouldn't show the popupmenu!
239 if( !qitem )
240 return;
242 Item *item = static_cast<Item*>( qitem );
244 Q3PopupMenu menu( this );
245 menu.insertItem( i18n( "Fullscreen" ), 0 );
247 if( !item->m_proc || item->m_proc->state() == Process::Running )
248 menu.setItemEnabled( 0, false );
250 switch( menu.exec( pos ) ) {
251 case 0: ::send( item->m_sockfd, "fullscreen", 11, 0 ); break;
252 default: break;
256 #include <QPainter>
257 #include <q3simplerichtext.h>
258 void
259 Vis::Selector::viewportPaintEvent( QPaintEvent *e )
261 if( childCount() == 0 ) {
263 //TODO the right message if amarok_libvisual is present but libvisual isn't
264 hide();
265 Amarok::ContextStatusBar::instance()->longMessage( i18n(
266 "<div align=center>"
267 "<h3>No Visualizations Found</h3>"
268 "Possible reasons:"
269 "<ul>"
270 "<li>libvisual is not installed</li>"
271 "<li>No libvisual plugins are installed</li>"
272 "</ul>"
273 "Please check these possibilities and restart Amarok."
274 "</div>" ), KDE::StatusBar::Sorry );
276 else { Q3ListView::viewportPaintEvent( e ); }
281 /// @class Vis::Selector::Item
283 Vis::Selector::Item::~Item()
285 delete m_proc; //kills the process too
288 void
289 Vis::Selector::Item::stateChange( bool ) //SLOT
291 switch( state() ) {
292 case On:
293 m_proc = new Process();
294 m_proc->setOutputChannelMode( Process::MergedChannels );
295 *m_proc << KStandardDirs::findExe( m_command )
296 << Selector::instance()->m_server->path()
297 << text( 0 );
299 connect( m_proc, SIGNAL(processExited( Process* )), listView(), SLOT(processExited( Process* )) );
300 // Shouldn't be necessary, but make visualizations work again when running with amarok binary
301 connect( m_proc, SIGNAL(readyReceiveStandardOutput( ) ), listView(), SLOT(receivedStdout ( Process* ) ) );
302 debug() << "Starting visualization..\n";
303 m_proc->start();
304 if( m_proc->error() == Process::UnknownError ) // success
305 break;
307 //ELSE FALL_THROUGH
309 warning() << "Could not start " << text( 0 );
311 case Off:
312 debug() << "Stopping visualization\n";
314 delete m_proc;
315 m_proc = 0;
317 break;
319 default:
324 #include "socketserver.moc"