Init engine fields, cleanup.
[jack2.git] / common / JackClient.cpp
blob64e8d428571e0e284661a9084332d6111c8b13d5
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);
176 break;
178 case kRemoveClient:
179 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
180 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
181 fClientRegistration(name, 0, fClientRegistrationArg);
183 break;
185 case kBufferSizeCallback:
186 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
187 if (fBufferSize) {
188 res = fBufferSize(value1, fBufferSizeArg);
190 break;
192 case kSampleRateCallback:
193 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
194 if (fSampleRate) {
195 res = fSampleRate(value1, fSampleRateArg);
197 break;
199 case kGraphOrderCallback:
200 jack_log("JackClient::kGraphOrderCallback");
201 if (fGraphOrder) {
202 res = fGraphOrder(fGraphOrderArg);
204 break;
206 case kStartFreewheelCallback:
207 jack_log("JackClient::kStartFreewheel");
208 SetupDriverSync(true);
209 fThread.DropRealTime(); // Always done (JACK server in RT mode or not...)
210 if (fFreewheel) {
211 fFreewheel(1, fFreewheelArg);
213 break;
215 case kStopFreewheelCallback:
216 jack_log("JackClient::kStopFreewheel");
217 SetupDriverSync(false);
218 if (fFreewheel) {
219 fFreewheel(0, fFreewheelArg);
221 if (GetEngineControl()->fRealTime) {
222 fThread.AcquireRealTime();
224 break;
226 case kPortRegistrationOnCallback:
227 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
228 if (fPortRegistration) {
229 fPortRegistration(value1, 1, fPortRegistrationArg);
231 break;
233 case kPortRegistrationOffCallback:
234 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
235 if (fPortRegistration) {
236 fPortRegistration(value1, 0, fPortRegistrationArg);
238 break;
240 case kPortConnectCallback:
241 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
242 if (fPortConnect) {
243 fPortConnect(value1, value2, 1, fPortConnectArg);
245 break;
247 case kPortDisconnectCallback:
248 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
249 if (fPortConnect) {
250 fPortConnect(value1, value2, 0, fPortConnectArg);
252 break;
254 case kPortRenameCallback:
255 jack_log("JackClient::kPortRenameCallback port = %ld", value1);
256 if (fPortRename) {
257 fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
259 break;
261 case kXRunCallback:
262 jack_log("JackClient::kXRunCallback");
263 if (fXrun) {
264 res = fXrun(fXrunArg);
266 break;
268 case kShutDownCallback:
269 jack_log("JackClient::kShutDownCallback");
270 if (fInfoShutdown) {
271 fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
272 fInfoShutdown = NULL;
274 break;
276 case kSessionCallback:
277 jack_log("JackClient::kSessionCallback");
278 if (fSession) {
279 jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) );
280 char uuid_buf[JACK_UUID_SIZE];
281 event->type = (jack_session_event_type_t) value1;
282 event->session_dir = strdup( message );
283 event->command_line = NULL;
284 event->flags = (jack_session_flags_t) 0;
285 snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID );
286 event->client_uuid = strdup( uuid_buf );
287 fImmediateSessionReply = false;
288 fSession(event, fSessionArg);
289 res = (fImmediateSessionReply) ? 1 : 2;
291 break;
295 return res;
299 \brief We need to start thread before activating in the server, otherwise the FW driver
300 connected to the client may not be activated.
302 int JackClient::Activate()
304 jack_log("JackClient::Activate");
305 if (IsActive())
306 return 0;
308 // RT thread is started only when needed...
309 if (IsRealTime()) {
310 if (StartThread() < 0)
311 return -1;
315 Insertion of client in the graph will cause a kGraphOrderCallback notification
316 to be delivered by the server, the client wants to receive it.
318 GetClientControl()->fActive = true;
320 // Transport related callback become "active"
321 GetClientControl()->fTransportSync = true;
322 GetClientControl()->fTransportTimebase = true;
324 int result = -1;
325 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
326 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
327 return result;
331 \brief Need to stop thread after deactivating in the server.
333 int JackClient::Deactivate()
335 jack_log("JackClient::Deactivate");
336 if (!IsActive())
337 return 0;
339 GetClientControl()->fActive = false;
341 // Transport related callback become "unactive"
342 GetClientControl()->fTransportSync = false;
343 GetClientControl()->fTransportTimebase = false;
345 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
346 int result = -1;
347 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
348 jack_log("JackClient::Deactivate res = %ld", result);
350 // RT thread is stopped only when needed...
351 if (IsRealTime())
352 fThread.Kill();
353 return result;
356 //----------------------
357 // RT thread management
358 //----------------------
361 \brief Called once when the thread starts.
363 bool JackClient::Init()
365 if (fInit) {
366 jack_log("JackClient::Init calling client thread init callback");
367 fInit(fInitArg);
369 return true;
372 int JackClient::StartThread()
374 jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
375 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
376 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
377 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
379 // Will do "something" on OSX only...
380 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
382 if (fThread.StartSync() < 0) {
383 jack_error("Start thread error");
384 return -1;
387 if (GetEngineControl()->fRealTime) {
388 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
389 jack_error("AcquireRealTime error");
393 return 0;
397 \brief RT thread.
400 bool JackClient::Execute()
402 if (!jack_tls_set(JackGlobals::fRealTime, this))
403 jack_error("failed to set thread realtime key");
405 if (GetEngineControl()->fRealTime)
406 set_threaded_log_function();
408 // Execute a dummy cycle to be sure thread has the correct properties
409 DummyCycle();
411 if (fThreadFun) {
412 fThreadFun(fThreadFunArg);
413 } else {
414 ExecuteThread();
416 return false;
419 void JackClient::DummyCycle()
421 WaitSync();
422 SignalSync();
425 inline void JackClient::ExecuteThread()
427 while (true) {
428 CycleWaitAux();
429 CycleSignalAux(CallProcessCallback());
433 inline jack_nframes_t JackClient::CycleWaitAux()
435 if (!WaitSync())
436 Error(); // Terminates the thread
437 CallSyncCallbackAux();
438 return GetEngineControl()->fBufferSize;
441 inline void JackClient::CycleSignalAux(int status)
443 if (status == 0)
444 CallTimebaseCallbackAux();
445 SignalSync();
446 if (status != 0)
447 End(); // Terminates the thread
450 jack_nframes_t JackClient::CycleWait()
452 return CycleWaitAux();
455 void JackClient::CycleSignal(int status)
457 CycleSignalAux(status);
460 inline int JackClient::CallProcessCallback()
462 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
465 inline bool JackClient::WaitSync()
467 // Suspend itself: wait on the input synchro
468 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
469 jack_error("SuspendRefNum error");
470 return false;
471 } else {
472 return true;
476 inline void JackClient::SignalSync()
478 // Resume: signal output clients connected to the running client
479 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
480 jack_error("ResumeRefNum error");
484 inline void JackClient::End()
486 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
487 // Hum... not sure about this, the following "close" code is called in the RT thread...
488 int result;
489 fThread.DropSelfRealTime();
490 GetClientControl()->fActive = false;
491 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
492 fThread.Terminate();
495 inline void JackClient::Error()
497 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
498 // Hum... not sure about this, the following "close" code is called in the RT thread...
499 int result;
500 fThread.DropSelfRealTime();
501 GetClientControl()->fActive = false;
502 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
503 ShutDown();
504 fThread.Terminate();
507 //-----------------
508 // Port management
509 //-----------------
511 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
513 // Check if port name is empty
514 string port_name_str = string(port_name);
515 if (port_name_str.size() == 0) {
516 jack_error("port_name is empty");
517 return 0; // Means failure here...
520 // Check port name length
521 string name = string(GetClientControl()->fName) + string(":") + port_name_str;
522 if (name.size() >= JACK_PORT_NAME_SIZE) {
523 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
524 "Please use %lu characters or less",
525 GetClientControl()->fName,
526 port_name,
527 JACK_PORT_NAME_SIZE - 1);
528 return 0; // Means failure here...
531 int result = -1;
532 jack_port_id_t port_index = NO_PORT;
533 fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
535 if (result == 0) {
536 jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
537 fPortList.push_back(port_index);
538 return port_index;
539 } else {
540 return 0;
544 int JackClient::PortUnRegister(jack_port_id_t port_index)
546 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
547 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
549 if (it != fPortList.end()) {
550 fPortList.erase(it);
551 int result = -1;
552 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
553 return result;
554 } else {
555 jack_error("unregistering a port %ld that is not own by the client", port_index);
556 return -1;
560 int JackClient::PortConnect(const char* src, const char* dst)
562 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
563 int result = -1;
564 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
565 return result;
568 int JackClient::PortDisconnect(const char* src, const char* dst)
570 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
571 int result = -1;
572 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
573 return result;
576 int JackClient::PortDisconnect(jack_port_id_t src)
578 jack_log("JackClient::PortDisconnect src = %ld", src);
579 int result = -1;
580 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
581 return result;
584 int JackClient::PortIsMine(jack_port_id_t port_index)
586 JackPort* port = GetGraphManager()->GetPort(port_index);
587 return GetClientControl()->fRefNum == port->GetRefNum();
590 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
592 int result = -1;
593 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
594 return result;
597 //--------------------
598 // Context management
599 //--------------------
601 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
603 int result = -1;
604 fChannel->SetBufferSize(buffer_size, &result);
605 return result;
608 int JackClient::SetFreeWheel(int onoff)
610 int result = -1;
611 fChannel->SetFreewheel(onoff, &result);
612 return result;
616 ShutDown is called:
617 - from the RT thread when Execute method fails
618 - possibly from a "closed" notification channel
619 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
622 void JackClient::ShutDown()
624 jack_log("ShutDown");
625 JackGlobals::fServerRunning = false;
627 if (fInfoShutdown) {
628 fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
629 fInfoShutdown = NULL;
630 } else if (fShutdown) {
631 fShutdown(fShutdownArg);
632 fShutdown = NULL;
636 //----------------------
637 // Transport management
638 //----------------------
640 inline int JackClient::ActivateAux()
642 // If activated without RT thread...
643 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
645 jack_log("ActivateAux");
647 // RT thread is started
648 if (StartThread() < 0)
649 return -1;
651 int result = -1;
652 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
653 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
654 return result;
656 } else {
657 return 0;
661 int JackClient::ReleaseTimebase()
663 int result = -1;
664 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
665 if (result == 0) {
666 GetClientControl()->fTransportTimebase = false;
667 fTimebase = NULL;
668 fTimebaseArg = NULL;
670 return result;
673 /* Call the server if the client is active, otherwise keeps the arguments */
674 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
676 GetClientControl()->fTransportSync = (fSync != NULL);
677 fSyncArg = arg;
678 fSync = sync_callback;
679 return ActivateAux();
682 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
684 int result = -1;
685 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
687 if (result == 0) {
688 GetClientControl()->fTransportTimebase = true;
689 fTimebase = timebase_callback;
690 fTimebaseArg = arg;
691 return ActivateAux();
692 } else {
693 fTimebase = NULL;
694 fTimebaseArg = NULL;
695 return -1;
699 int JackClient::SetSyncTimeout(jack_time_t timeout)
701 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
702 return 0;
705 // Must be RT safe
707 void JackClient::TransportLocate(jack_nframes_t frame)
709 jack_position_t pos;
710 pos.frame = frame;
711 pos.valid = (jack_position_bits_t)0;
712 jack_log("TransportLocate pos = %ld", pos.frame);
713 GetEngineControl()->fTransport.RequestNewPos(&pos);
716 int JackClient::TransportReposition(jack_position_t* pos)
718 jack_position_t tmp = *pos;
719 jack_log("TransportReposition pos = %ld", pos->frame);
720 if (tmp.valid & ~JACK_POSITION_MASK) {
721 return EINVAL;
722 } else {
723 GetEngineControl()->fTransport.RequestNewPos(pos);
724 return 0;
728 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
730 return GetEngineControl()->fTransport.Query(pos);
733 jack_nframes_t JackClient::GetCurrentTransportFrame()
735 return GetEngineControl()->fTransport.GetCurrentFrame();
738 // Must be RT safe: directly write in the transport shared mem
739 void JackClient::TransportStart()
741 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
744 // Must be RT safe: directly write in the transport shared mem
745 void JackClient::TransportStop()
747 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
750 // Never called concurently with the server
751 // TODO check concurrency with SetSyncCallback
753 void JackClient::CallSyncCallback()
755 CallSyncCallbackAux();
758 inline void JackClient::CallSyncCallbackAux()
760 if (GetClientControl()->fTransportSync) {
762 JackTransportEngine& transport = GetEngineControl()->fTransport;
763 jack_position_t* cur_pos = transport.ReadCurrentState();
764 jack_transport_state_t transport_state = transport.GetState();
766 if (fSync != NULL) {
767 if (fSync(transport_state, cur_pos, fSyncArg)) {
768 GetClientControl()->fTransportState = JackTransportRolling;
769 GetClientControl()->fTransportSync = false;
771 } else {
772 GetClientControl()->fTransportState = JackTransportRolling;
773 GetClientControl()->fTransportSync = false;
778 void JackClient::CallTimebaseCallback()
780 CallTimebaseCallbackAux();
783 inline void JackClient::CallTimebaseCallbackAux()
785 JackTransportEngine& transport = GetEngineControl()->fTransport;
786 int master;
787 bool unused;
789 transport.GetTimebaseMaster(master, unused);
791 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
793 jack_transport_state_t transport_state = transport.GetState();
794 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
796 if (GetClientControl()->fTransportTimebase) {
797 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
798 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
799 } else if (transport_state == JackTransportRolling) {
800 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
803 transport.WriteNextStateStop(1);
807 //---------------------
808 // Callback management
809 //---------------------
811 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
813 if (IsActive()) {
814 jack_error("You cannot set callbacks on an active client");
815 } else {
816 fShutdownArg = arg;
817 fShutdown = callback;
821 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
823 if (IsActive()) {
824 jack_error("You cannot set callbacks on an active client");
825 } else {
826 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
827 fInfoShutdownArg = arg;
828 fInfoShutdown = callback;
832 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
834 if (IsActive()) {
835 jack_error("You cannot set callbacks on an active client");
836 return -1;
837 } else if (fThreadFun) {
838 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
839 return -1;
840 } else {
841 fProcessArg = arg;
842 fProcess = callback;
843 return 0;
847 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
849 if (IsActive()) {
850 jack_error("You cannot set callbacks on an active client");
851 return -1;
852 } else {
853 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
854 fXrunArg = arg;
855 fXrun = callback;
856 return 0;
860 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
862 if (IsActive()) {
863 jack_error("You cannot set callbacks on an active client");
864 return -1;
865 } else {
866 fInitArg = arg;
867 fInit = callback;
868 /* make sure that the message buffer thread is initialized too */
869 JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
870 return 0;
874 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
876 jack_log("SetGraphOrderCallback ");
878 if (IsActive()) {
879 jack_error("You cannot set callbacks on an active client");
880 return -1;
881 } else {
882 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
883 fGraphOrder = callback;
884 fGraphOrderArg = arg;
885 return 0;
889 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
891 if (IsActive()) {
892 jack_error("You cannot set callbacks on an active client");
893 return -1;
894 } else {
895 GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
896 fBufferSizeArg = arg;
897 fBufferSize = callback;
898 return 0;
902 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
904 if (IsActive()) {
905 jack_error("You cannot set callbacks on an active client");
906 return -1;
907 } else {
908 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
909 fSampleRateArg = arg;
910 fSampleRate = callback;
911 // Now invoke it
912 if (callback)
913 callback(GetEngineControl()->fSampleRate, arg);
914 return 0;
918 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
920 if (IsActive()) {
921 jack_error("You cannot set callbacks on an active client");
922 return -1;
923 } else {
924 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
925 fClientRegistrationArg = arg;
926 fClientRegistration = callback;
927 return 0;
931 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
933 if (IsActive()) {
934 jack_error("You cannot set callbacks on an active client");
935 return -1;
936 } else {
937 GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
938 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
939 fFreewheelArg = arg;
940 fFreewheel = callback;
941 return 0;
945 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
947 if (IsActive()) {
948 jack_error("You cannot set callbacks on an active client");
949 return -1;
950 } else {
951 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
952 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
953 fPortRegistrationArg = arg;
954 fPortRegistration = callback;
955 return 0;
959 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
961 if (IsActive()) {
962 jack_error("You cannot set callbacks on an active client");
963 return -1;
964 } else {
965 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
966 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
967 fPortConnectArg = arg;
968 fPortConnect = callback;
969 return 0;
973 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
975 if (IsActive()) {
976 jack_error("You cannot set callbacks on an active client");
977 return -1;
978 } else {
979 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
980 fPortRenameArg = arg;
981 fPortRename = callback;
982 return 0;
986 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
988 if (IsActive()) {
989 jack_error("You cannot set callbacks on an active client");
990 return -1;
991 } else if (fProcess) {
992 jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
993 return -1;
994 } else {
995 fThreadFun = fun;
996 fThreadFunArg = arg;
997 return 0;
1001 int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
1003 if (IsActive()) {
1004 jack_error("You cannot set callbacks on an active client");
1005 return -1;
1006 } else {
1007 GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
1008 fSessionArg = arg;
1009 fSession = callback;
1010 return 0;
1014 //------------------
1015 // Internal clients
1016 //------------------
1018 char* JackClient::GetInternalClientName(int ref)
1020 char name_res[JACK_CLIENT_NAME_SIZE + 1];
1021 int result = -1;
1022 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
1023 return (result < 0) ? NULL : strdup(name_res);
1026 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
1028 int int_ref, result = -1;
1029 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
1030 return int_ref;
1033 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
1035 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
1036 jack_error ("\"%s\" is too long for a JACK client name.\n"
1037 "Please use %lu characters or less.",
1038 client_name, JACK_CLIENT_NAME_SIZE);
1039 return 0;
1042 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
1043 jack_error("\"%s\" is too long for a shared object name.\n"
1044 "Please use %lu characters or less.",
1045 va->load_name, PATH_MAX);
1046 int my_status1 = *status | (JackFailure | JackInvalidOption);
1047 *status = (jack_status_t)my_status1;
1048 return 0;
1051 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1052 jack_error ("\"%s\" is too long for internal client init "
1053 "string.\nPlease use %lu characters or less.",
1054 va->load_init, JACK_LOAD_INIT_LIMIT);
1055 int my_status1 = *status | (JackFailure | JackInvalidOption);
1056 *status = (jack_status_t)my_status1;
1057 return 0;
1060 int int_ref, result = -1;
1061 // UUID TO CHECK
1062 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
1063 return int_ref;
1066 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1068 int result = -1;
1069 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1072 //------------------
1073 // Session API
1074 //------------------
1076 jack_session_command_t *JackClient::SessionNotify( const char* target, jack_session_event_type_t type, const char* path )
1078 jack_session_command_t *res;
1079 fChannel->SessionNotify( GetClientControl()->fRefNum, target, type, path, &res );
1080 return res;
1083 int JackClient::SessionReply( jack_session_event_t *ev )
1085 if (ev->command_line) {
1086 strncpy( GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand) );
1087 } else {
1088 GetClientControl()->fSessionCommand[0] = '\0';
1091 GetClientControl()->fSessionFlags = ev->flags;
1093 jack_log( "JackClient::SessionReply... we are here" );
1094 if (fChannel->IsChannelThread()) {
1095 jack_log( "JackClient::SessionReply... in callback reply" );
1096 fImmediateSessionReply = true;
1097 return 0;
1100 jack_log( "JackClient::SessionReply... out of cb" );
1102 int res;
1103 fChannel->SessionReply( GetClientControl()->fRefNum, &res);
1104 return res;
1107 char* JackClient::GetUUIDForClientName(const char* client_name)
1109 char uuid_res[JACK_UUID_SIZE];
1110 int result = -1;
1111 fChannel->GetUUIDForClientName( GetClientControl()->fRefNum, client_name, uuid_res, &result);
1113 if (result)
1114 return NULL;
1116 return strdup(uuid_res);
1119 char* JackClient::GetClientNameForUUID(const char* uuid)
1121 char name_res[JACK_CLIENT_NAME_SIZE + 1];
1122 int result = -1;
1123 fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
1125 if (result)
1126 return NULL;
1128 return strdup(name_res);
1131 int JackClient::ReserveClientName(const char *name, const char* uuid)
1133 int result = -1;
1134 fChannel->ReserveClientName( GetClientControl()->fRefNum, name, uuid, &result);
1135 return result;
1138 } // end of namespace