Add a string parameter to server ==> client notification, add a new InfoShutdown...
[jack2.git] / common / JackClient.cpp
blob436a7194f052191c5fe5da8a0286ef34a3074e4a
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "JackClient.h"
22 #include "JackGraphManager.h"
23 #include "JackClientControl.h"
24 #include "JackEngineControl.h"
25 #include "JackGlobals.h"
26 #include "JackChannel.h"
27 #include "JackTransportEngine.h"
28 #include "driver_interface.h"
29 #include "JackLibGlobals.h"
31 #include <math.h>
32 #include <string>
33 #include <algorithm>
35 using namespace std;
37 namespace Jack
40 #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
42 JackClient::JackClient():fThread(this)
45 JackClient::JackClient(JackSynchro* table):fThread(this)
47 fSynchroTable = table;
48 fProcess = NULL;
49 fGraphOrder = NULL;
50 fXrun = NULL;
51 fShutdown = NULL;
52 fInfoShutdown = NULL;
53 fInit = NULL;
54 fBufferSize = NULL;
55 fClientRegistration = NULL;
56 fFreewheel = NULL;
57 fPortRegistration = NULL;
58 fPortConnect = NULL;
59 fPortRename = NULL;
60 fTimebase = NULL;
61 fSync = NULL;
62 fThreadFun = NULL;
63 fProcessArg = NULL;
64 fGraphOrderArg = NULL;
65 fXrunArg = NULL;
66 fShutdownArg = NULL;
67 fInfoShutdownArg = NULL;
68 fInitArg = NULL;
69 fBufferSizeArg = NULL;
70 fFreewheelArg = NULL;
71 fClientRegistrationArg = NULL;
72 fPortRegistrationArg = NULL;
73 fPortConnectArg = NULL;
74 fPortRenameArg = NULL;
75 fSyncArg = NULL;
76 fTimebaseArg = NULL;
77 fThreadFunArg = NULL;
80 JackClient::~JackClient()
83 int JackClient::Close()
85 jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
86 int result = 0;
88 Deactivate();
89 fChannel->Stop(); // Channels is stopped first to avoid receiving notifications while closing
91 // Request close only if server is still running
92 if (JackGlobals::fServerRunning) {
93 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
94 } else {
95 jack_log("JackClient::Close server is shutdown");
98 fChannel->Close();
99 fSynchroTable[GetClientControl()->fRefNum].Disconnect();
100 JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
101 return result;
104 bool JackClient::IsActive()
106 return (GetClientControl()) ? GetClientControl()->fActive : false;
109 pthread_t JackClient::GetThreadID()
111 return fThread.GetThreadID();
115 In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
116 The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
117 Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
119 void JackClient::SetupDriverSync(bool freewheel)
121 if (!freewheel && !GetEngineControl()->fSyncMode) {
122 jack_log("JackClient::SetupDriverSync driver sem in flush mode");
123 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
124 fSynchroTable[i].SetFlush(true);
126 } else {
127 jack_log("JackClient::SetupDriverSync driver sem in normal mode");
128 for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
129 fSynchroTable[i].SetFlush(false);
134 \brief Notification received from the server.
137 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
139 return 0;
142 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
144 int res = 0;
146 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
147 switch (notify) {
149 case kAddClient:
150 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
151 break;
153 case kRemoveClient:
154 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
155 break;
157 case kActivateClient:
158 jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
159 Init();
160 break;
164 The current semantic is that notifications can only be received when the client has been activated,
165 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
167 if (IsActive()) {
169 switch (notify) {
171 case kAddClient:
172 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
173 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
174 fClientRegistration(name, 1, fClientRegistrationArg);
175 break;
177 case kRemoveClient:
178 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
179 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
180 fClientRegistration(name, 0, fClientRegistrationArg);
181 break;
183 case kBufferSizeCallback:
184 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
185 if (fBufferSize)
186 res = fBufferSize(value1, fBufferSizeArg);
187 break;
189 case kSampleRateCallback:
190 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
191 if (fSampleRate)
192 res = fSampleRate(value1, fSampleRateArg);
193 break;
195 case kGraphOrderCallback:
196 jack_log("JackClient::kGraphOrderCallback");
197 if (fGraphOrder)
198 res = fGraphOrder(fGraphOrderArg);
199 break;
201 case kStartFreewheelCallback:
202 jack_log("JackClient::kStartFreewheel");
203 SetupDriverSync(true);
204 fThread.DropRealTime();
205 if (fFreewheel)
206 fFreewheel(1, fFreewheelArg);
207 break;
209 case kStopFreewheelCallback:
210 jack_log("JackClient::kStopFreewheel");
211 SetupDriverSync(false);
212 if (fFreewheel)
213 fFreewheel(0, fFreewheelArg);
214 fThread.AcquireRealTime();
215 break;
217 case kPortRegistrationOnCallback:
218 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
219 if (fPortRegistration)
220 fPortRegistration(value1, 1, fPortRegistrationArg);
221 break;
223 case kPortRegistrationOffCallback:
224 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
225 if (fPortRegistration)
226 fPortRegistration(value1, 0, fPortRegistrationArg);
227 break;
229 case kPortConnectCallback:
230 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
231 if (fPortConnect)
232 fPortConnect(value1, value2, 1, fPortConnectArg);
233 break;
235 case kPortDisconnectCallback:
236 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
237 if (fPortConnect)
238 fPortConnect(value1, value2, 0, fPortConnectArg);
239 break;
241 case kPortRenameCallback:
242 jack_log("JackClient::kPortRenameCallback port = %ld");
243 if (fPortRename)
244 fPortRename(value1, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
245 break;
247 case kXRunCallback:
248 jack_log("JackClient::kXRunCallback");
249 if (fXrun)
250 res = fXrun(fXrunArg);
251 break;
253 case kShutDownCallback:
254 jack_log("JackClient::kShutDownCallback");
255 if (fInfoShutdown)
256 fInfoShutdown(message, fInfoShutdownArg);
257 break;
261 return res;
265 \brief We need to start thread before activating in the server, otherwise the FW driver
266 connected to the client may not be activated.
268 int JackClient::Activate()
270 jack_log("JackClient::Activate");
271 if (IsActive())
272 return 0;
274 // RT thread is started only when needed...
275 if (IsRealTime()) {
276 if (StartThread() < 0)
277 return -1;
281 Insertion of client in the graph will cause a kGraphOrderCallback notification
282 to be delivered by the server, the client wants to receive it.
284 GetClientControl()->fActive = true;
286 // Transport related callback become "active"
287 GetClientControl()->fTransportSync = true;
288 GetClientControl()->fTransportTimebase = true;
290 int result = -1;
291 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
292 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
293 return result;
297 \brief Need to stop thread after deactivating in the server.
299 int JackClient::Deactivate()
301 jack_log("JackClient::Deactivate");
302 if (!IsActive())
303 return 0;
305 GetClientControl()->fActive = false;
307 // Transport related callback become "unactive"
308 GetClientControl()->fTransportSync = false;
309 GetClientControl()->fTransportTimebase = false;
311 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
312 int result = -1;
313 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
314 jack_log("JackClient::Deactivate res = %ld", result);
316 // RT thread is stopped only when needed...
317 if (IsRealTime())
318 fThread.Kill();
319 return result;
322 //----------------------
323 // RT thread management
324 //----------------------
327 \brief Called once when the thread starts.
329 bool JackClient::Init()
331 if (fInit) {
332 jack_log("JackClient::Init calling client thread init callback");
333 fInit(fInitArg);
335 return true;
338 int JackClient::StartThread()
340 jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
341 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
342 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
343 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
345 // Will do "something" on OSX only...
346 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
348 if (fThread.StartSync() < 0) {
349 jack_error("Start thread error");
350 return -1;
353 if (GetEngineControl()->fRealTime) {
354 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
355 jack_error("AcquireRealTime error");
359 return 0;
363 \brief RT thread.
366 bool JackClient::Execute()
368 if (!jack_tls_set(JackGlobals::fRealTime, this))
369 jack_error("failed to set thread realtime key");
371 if (GetEngineControl()->fRealTime)
372 set_threaded_log_function();
374 // Execute a dummy cycle to be sure thread has the correct properties
375 DummyCycle();
377 if (fThreadFun) {
378 fThreadFun(fThreadFunArg);
379 } else {
380 ExecuteThread();
382 return false;
385 void JackClient::DummyCycle()
387 WaitSync();
388 SignalSync();
391 inline void JackClient::ExecuteThread()
393 while (true) {
394 CycleWaitAux();
395 CycleSignalAux(CallProcessCallback());
399 inline jack_nframes_t JackClient::CycleWaitAux()
401 if (!WaitSync())
402 Error(); // Terminates the thread
403 CallSyncCallbackAux();
404 return GetEngineControl()->fBufferSize;
407 inline void JackClient::CycleSignalAux(int status)
409 if (status == 0)
410 CallTimebaseCallbackAux();
411 SignalSync();
412 if (status != 0)
413 End(); // Terminates the thread
416 jack_nframes_t JackClient::CycleWait()
418 return CycleWaitAux();
421 void JackClient::CycleSignal(int status)
423 CycleSignalAux(status);
426 inline int JackClient::CallProcessCallback()
428 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
431 inline bool JackClient::WaitSync()
433 // Suspend itself: wait on the input synchro
434 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
435 jack_error("SuspendRefNum error");
436 return false;
437 } else {
438 return true;
442 inline void JackClient::SignalSync()
444 // Resume: signal output clients connected to the running client
445 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
446 jack_error("ResumeRefNum error");
450 inline void JackClient::End()
452 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
453 // Hum... not sure about this, the following "close" code is called in the RT thread...
454 int result;
455 fThread.DropRealTime();
456 GetClientControl()->fActive = false;
457 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
458 fThread.Terminate();
461 inline void JackClient::Error()
463 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
464 // Hum... not sure about this, the following "close" code is called in the RT thread...
465 int result;
466 fThread.DropRealTime();
467 GetClientControl()->fActive = false;
468 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
469 ShutDown();
470 fThread.Terminate();
473 //-----------------
474 // Port management
475 //-----------------
477 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
479 // Check if port name is empty
480 string port_name_str = string(port_name);
481 if (port_name_str.size() == 0) {
482 jack_error("port_name is empty");
483 return 0; // Means failure here...
486 // Check port name length
487 string name = string(GetClientControl()->fName) + string(":") + port_name_str;
488 if (name.size() >= JACK_PORT_NAME_SIZE) {
489 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
490 "Please use %lu characters or less",
491 GetClientControl()->fName,
492 port_name,
493 JACK_PORT_NAME_SIZE - 1);
494 return 0; // Means failure here...
497 int result = -1;
498 jack_port_id_t port_index = NO_PORT;
499 fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
501 if (result == 0) {
502 jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
503 fPortList.push_back(port_index);
504 return port_index;
505 } else {
506 return 0;
510 int JackClient::PortUnRegister(jack_port_id_t port_index)
512 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
513 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
515 if (it != fPortList.end()) {
516 fPortList.erase(it);
517 int result = -1;
518 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
519 return result;
520 } else {
521 jack_error("unregistering a port %ld that is not own by the client", port_index);
522 return -1;
526 int JackClient::PortConnect(const char* src, const char* dst)
528 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
529 int result = -1;
530 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
531 return result;
534 int JackClient::PortDisconnect(const char* src, const char* dst)
536 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
537 int result = -1;
538 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
539 return result;
542 int JackClient::PortDisconnect(jack_port_id_t src)
544 jack_log("JackClient::PortDisconnect src = %ld", src);
545 int result = -1;
546 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
547 return result;
550 int JackClient::PortIsMine(jack_port_id_t port_index)
552 JackPort* port = GetGraphManager()->GetPort(port_index);
553 return GetClientControl()->fRefNum == port->GetRefNum();
556 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
558 int result = -1;
559 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
560 return result;
563 //--------------------
564 // Context management
565 //--------------------
567 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
569 int result = -1;
570 fChannel->SetBufferSize(buffer_size, &result);
571 return result;
574 int JackClient::SetFreeWheel(int onoff)
576 int result = -1;
577 fChannel->SetFreewheel(onoff, &result);
578 return result;
582 ShutDown is called:
583 - from the RT thread when Execute method fails
584 - possibly from a "closed" notification channel
585 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
588 void JackClient::ShutDown()
590 jack_log("ShutDown");
591 JackGlobals::fServerRunning = false;
593 if (fInfoShutdown) {
594 fInfoShutdown("JACK server has been closed", fInfoShutdownArg);
595 fInfoShutdown = NULL;
596 } else if (fShutdown) {
597 fShutdown(fShutdownArg);
598 fShutdown = NULL;
602 //----------------------
603 // Transport management
604 //----------------------
606 inline int JackClient::ActivateAux()
608 // If activated without RT thread...
609 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
611 jack_log("ActivateAux");
613 // RT thread is started
614 if (StartThread() < 0)
615 return -1;
617 int result = -1;
618 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
619 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
620 return result;
622 } else {
623 return 0;
627 int JackClient::ReleaseTimebase()
629 int result = -1;
630 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
631 if (result == 0) {
632 GetClientControl()->fTransportTimebase = false;
633 fTimebase = NULL;
634 fTimebaseArg = NULL;
636 return result;
639 /* Call the server if the client is active, otherwise keeps the arguments */
640 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
642 GetClientControl()->fTransportSync = (fSync != NULL);
643 fSyncArg = arg;
644 fSync = sync_callback;
645 return ActivateAux();
648 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
650 int result = -1;
651 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
653 if (result == 0) {
654 GetClientControl()->fTransportTimebase = true;
655 fTimebase = timebase_callback;
656 fTimebaseArg = arg;
657 return ActivateAux();
658 } else {
659 fTimebase = NULL;
660 fTimebaseArg = NULL;
661 return -1;
665 int JackClient::SetSyncTimeout(jack_time_t timeout)
667 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
668 return 0;
671 // Must be RT safe
673 void JackClient::TransportLocate(jack_nframes_t frame)
675 jack_position_t pos;
676 pos.frame = frame;
677 pos.valid = (jack_position_bits_t)0;
678 jack_log("TransportLocate pos = %ld", pos.frame);
679 GetEngineControl()->fTransport.RequestNewPos(&pos);
682 int JackClient::TransportReposition(jack_position_t* pos)
684 jack_position_t tmp = *pos;
685 jack_log("TransportReposition pos = %ld", pos->frame);
686 if (tmp.valid & ~JACK_POSITION_MASK) {
687 return EINVAL;
688 } else {
689 GetEngineControl()->fTransport.RequestNewPos(pos);
690 return 0;
694 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
696 return GetEngineControl()->fTransport.Query(pos);
699 jack_nframes_t JackClient::GetCurrentTransportFrame()
701 return GetEngineControl()->fTransport.GetCurrentFrame();
704 // Must be RT safe: directly write in the transport shared mem
705 void JackClient::TransportStart()
707 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
710 // Must be RT safe: directly write in the transport shared mem
711 void JackClient::TransportStop()
713 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
716 // Never called concurently with the server
717 // TODO check concurrency with SetSyncCallback
719 void JackClient::CallSyncCallback()
721 CallSyncCallbackAux();
724 inline void JackClient::CallSyncCallbackAux()
726 if (GetClientControl()->fTransportSync) {
728 JackTransportEngine& transport = GetEngineControl()->fTransport;
729 jack_position_t* cur_pos = transport.ReadCurrentState();
730 jack_transport_state_t transport_state = transport.GetState();
732 if (fSync != NULL) {
733 if (fSync(transport_state, cur_pos, fSyncArg)) {
734 GetClientControl()->fTransportState = JackTransportRolling;
735 GetClientControl()->fTransportSync = false;
737 } else {
738 GetClientControl()->fTransportState = JackTransportRolling;
739 GetClientControl()->fTransportSync = false;
744 void JackClient::CallTimebaseCallback()
746 CallTimebaseCallbackAux();
749 inline void JackClient::CallTimebaseCallbackAux()
751 JackTransportEngine& transport = GetEngineControl()->fTransport;
752 int master;
753 bool unused;
755 transport.GetTimebaseMaster(master, unused);
757 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
759 jack_transport_state_t transport_state = transport.GetState();
760 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
762 if (GetClientControl()->fTransportTimebase) {
763 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
764 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
765 } else if (transport_state == JackTransportRolling) {
766 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
769 transport.WriteNextStateStop(1);
773 //---------------------
774 // Callback management
775 //---------------------
777 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
779 if (IsActive()) {
780 jack_error("You cannot set callbacks on an active client");
781 } else {
782 fShutdownArg = arg;
783 fShutdown = callback;
787 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
789 if (IsActive()) {
790 jack_error("You cannot set callbacks on an active client");
791 } else {
792 fInfoShutdownArg = arg;
793 fInfoShutdown = callback;
797 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
799 if (IsActive()) {
800 jack_error("You cannot set callbacks on an active client");
801 return -1;
802 } else if (fThreadFun) {
803 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
804 return -1;
805 } else {
806 fProcessArg = arg;
807 fProcess = callback;
808 return 0;
812 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
814 if (IsActive()) {
815 jack_error("You cannot set callbacks on an active client");
816 return -1;
817 } else {
818 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
819 fXrunArg = arg;
820 fXrun = callback;
821 return 0;
825 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
827 if (IsActive()) {
828 jack_error("You cannot set callbacks on an active client");
829 return -1;
830 } else {
831 fInitArg = arg;
832 fInit = callback;
833 return 0;
837 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
839 jack_log("SetGraphOrderCallback ");
841 if (IsActive()) {
842 jack_error("You cannot set callbacks on an active client");
843 return -1;
844 } else {
845 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
846 fGraphOrder = callback;
847 fGraphOrderArg = arg;
848 return 0;
852 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
854 if (IsActive()) {
855 jack_error("You cannot set callbacks on an active client");
856 return -1;
857 } else {
858 GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
859 fBufferSizeArg = arg;
860 fBufferSize = callback;
861 return 0;
865 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
867 if (IsActive()) {
868 jack_error("You cannot set callbacks on an active client");
869 return -1;
870 } else {
871 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
872 fSampleRateArg = arg;
873 fSampleRate = callback;
874 return 0;
878 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
880 if (IsActive()) {
881 jack_error("You cannot set callbacks on an active client");
882 return -1;
883 } else {
884 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
885 fClientRegistrationArg = arg;
886 fClientRegistration = callback;
887 return 0;
891 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
893 if (IsActive()) {
894 jack_error("You cannot set callbacks on an active client");
895 return -1;
896 } else {
897 GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
898 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
899 fFreewheelArg = arg;
900 fFreewheel = callback;
901 return 0;
905 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
907 if (IsActive()) {
908 jack_error("You cannot set callbacks on an active client");
909 return -1;
910 } else {
911 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
912 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
913 fPortRegistrationArg = arg;
914 fPortRegistration = callback;
915 return 0;
919 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
921 if (IsActive()) {
922 jack_error("You cannot set callbacks on an active client");
923 return -1;
924 } else {
925 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
926 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
927 fPortConnectArg = arg;
928 fPortConnect = callback;
929 return 0;
933 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
935 if (IsActive()) {
936 jack_error("You cannot set callbacks on an active client");
937 return -1;
938 } else {
939 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
940 fPortRenameArg = arg;
941 fPortRename = callback;
942 return 0;
946 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
948 if (IsActive()) {
949 jack_error("You cannot set callbacks on an active client");
950 return -1;
951 } else if (fProcess) {
952 jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
953 return -1;
954 } else {
955 fThreadFun = fun;
956 fThreadFunArg = arg;
957 return 0;
961 //------------------
962 // Internal clients
963 //------------------
965 char* JackClient::GetInternalClientName(int ref)
967 char name_res[JACK_CLIENT_NAME_SIZE + 1];
968 int result = -1;
969 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
971 if (result < 0) {
972 return NULL;
973 } else {
974 char* name = (char*)malloc(strlen(name_res));
975 strcpy(name, name_res);
976 return name;
980 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
982 int int_ref, result = -1;
983 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
984 return int_ref;
987 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
989 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
990 jack_error ("\"%s\" is too long for a JACK client name.\n"
991 "Please use %lu characters or less.",
992 client_name, JACK_CLIENT_NAME_SIZE);
993 return 0;
996 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
997 jack_error("\"%s\" is too long for a shared object name.\n"
998 "Please use %lu characters or less.",
999 va->load_name, PATH_MAX);
1000 int my_status1 = *status | (JackFailure | JackInvalidOption);
1001 *status = (jack_status_t)my_status1;
1002 return 0;
1005 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1006 jack_error ("\"%s\" is too long for internal client init "
1007 "string.\nPlease use %lu characters or less.",
1008 va->load_init, JACK_LOAD_INIT_LIMIT);
1009 int my_status1 = *status | (JackFailure | JackInvalidOption);
1010 *status = (jack_status_t)my_status1;
1011 return 0;
1014 int int_ref, result = -1;
1015 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, &result);
1016 return int_ref;
1019 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1021 int result = -1;
1022 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1026 } // end of namespace