2 Copyright (C) 2004-2008 Grame
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.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "JackSystemDeps.h"
27 #include "JackLockedEngine.h"
28 #include "JackExternalClient.h"
29 #include "JackInternalClient.h"
30 #include "JackEngineControl.h"
31 #include "JackClientControl.h"
32 #include "JackServerGlobals.h"
33 #include "JackGlobals.h"
34 #include "JackChannel.h"
35 #include "JackError.h"
40 JackEngine::JackEngine(JackGraphManager
* manager
,
42 JackEngineControl
* control
,
43 char self_connect_mode
)
44 : JackLockAble(control
->fServerName
),
45 fSignal(control
->fServerName
)
47 fGraphManager
= manager
;
48 fSynchroTable
= table
;
49 fEngineControl
= control
;
50 fSelfConnectMode
= self_connect_mode
;
51 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
52 fClientTable
[i
] = NULL
;
56 fSessionPendingReplies
= 0;
57 fSessionTransaction
= NULL
;
58 fSessionResult
= NULL
;
61 JackEngine::~JackEngine()
64 int JackEngine::Open()
66 jack_log("JackEngine::Open");
68 // Open audio thread => request thread communication channel
69 if (fChannel
.Open(fEngineControl
->fServerName
) < 0) {
70 jack_error("Cannot connect to server");
77 int JackEngine::Close()
79 jack_log("JackEngine::Close");
82 // Close remaining clients (RT is stopped)
83 for (int i
= fEngineControl
->fDriverNum
; i
< CLIENT_NUM
; i
++) {
84 if (JackLoadableInternalClient
* loadable_client
= dynamic_cast<JackLoadableInternalClient
*>(fClientTable
[i
])) {
85 jack_log("JackEngine::Close loadable client = %s", loadable_client
->GetClientControl()->fName
);
86 loadable_client
->Close();
87 fClientTable
[i
] = NULL
;
88 delete loadable_client
;
89 } else if (JackExternalClient
* external_client
= dynamic_cast<JackExternalClient
*>(fClientTable
[i
])) {
90 jack_log("JackEngine::Close external client = %s", external_client
->GetClientControl()->fName
);
91 external_client
->Close();
92 fClientTable
[i
] = NULL
;
93 delete external_client
;
100 void JackEngine::NotifyQuit()
102 fChannel
.NotifyQuit();
105 //-----------------------------
106 // Client ressource management
107 //-----------------------------
109 int JackEngine::AllocateRefnum()
111 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
112 if (!fClientTable
[i
]) {
113 jack_log("JackEngine::AllocateRefNum ref = %ld", i
);
120 void JackEngine::ReleaseRefnum(int refnum
)
122 fClientTable
[refnum
] = NULL
;
124 if (fEngineControl
->fTemporary
) {
126 for (i
= fEngineControl
->fDriverNum
; i
< CLIENT_NUM
; i
++) {
127 if (fClientTable
[i
]) {
131 if (i
== CLIENT_NUM
) {
132 // Last client and temporay case: quit the server
133 jack_log("JackEngine::ReleaseRefnum server quit");
134 fEngineControl
->fTemporary
= false;
135 throw JackTemporaryException();
144 void JackEngine::ProcessNext(jack_time_t cur_cycle_begin
)
146 fLastSwitchUsecs
= cur_cycle_begin
;
147 if (fGraphManager
->RunNextGraph()) { // True if the graph actually switched to a new state
148 fChannel
.Notify(ALL_CLIENTS
, kGraphOrderCallback
, 0);
150 fSignal
.Signal(); // Signal for threads waiting for next cycle
153 void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin
)
155 if (cur_cycle_begin
< fLastSwitchUsecs
+ 2 * fEngineControl
->fPeriodUsecs
) { // Signal XRun only for the first failing cycle
156 CheckXRun(cur_cycle_begin
);
158 fGraphManager
->RunCurrentGraph();
161 bool JackEngine::Process(jack_time_t cur_cycle_begin
, jack_time_t prev_cycle_end
)
166 fEngineControl
->CycleBegin(fClientTable
, fGraphManager
, cur_cycle_begin
, prev_cycle_end
);
169 if (fGraphManager
->IsFinishedGraph()) {
170 ProcessNext(cur_cycle_begin
);
173 jack_log("Process: graph not finished!");
174 if (cur_cycle_begin
> fLastSwitchUsecs
+ fEngineControl
->fTimeOutUsecs
) {
175 jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin
- fLastSwitchUsecs
));
176 ProcessNext(cur_cycle_begin
);
179 jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin
- fLastSwitchUsecs
));
180 ProcessCurrent(cur_cycle_begin
);
186 fEngineControl
->CycleEnd(fClientTable
);
191 Client that finish *after* the callback date are considered late even if their output buffers may have been
192 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
195 static const char* State2String(jack_client_state_t state
)
199 return "NotTriggered";
211 void JackEngine::CheckXRun(jack_time_t callback_usecs
) // REVOIR les conditions de fin
213 for (int i
= fEngineControl
->fDriverNum
; i
< CLIENT_NUM
; i
++) {
214 JackClientInterface
* client
= fClientTable
[i
];
215 if (client
&& client
->GetClientControl()->fActive
) {
216 JackClientTiming
* timing
= fGraphManager
->GetClientTiming(i
);
217 jack_client_state_t status
= timing
->fStatus
;
218 jack_time_t finished_date
= timing
->fFinishedAt
;
220 if (status
!= NotTriggered
&& status
!= Finished
) {
221 jack_error("JackEngine::XRun: client = %s was not finished, state = %s", client
->GetClientControl()->fName
, State2String(status
));
222 fChannel
.Notify(ALL_CLIENTS
, kXRunCallback
, 0); // Notify all clients
225 if (status
== Finished
&& (long)(finished_date
- callback_usecs
) > 0) {
226 jack_error("JackEngine::XRun: client %s finished after current callback", client
->GetClientControl()->fName
);
227 fChannel
.Notify(ALL_CLIENTS
, kXRunCallback
, 0); // Notify all clients
233 int JackEngine::ComputeTotalLatencies()
235 std::vector
<jack_int_t
> sorted
;
236 std::vector
<jack_int_t
>::iterator it
;
237 std::vector
<jack_int_t
>::reverse_iterator rit
;
239 fGraphManager
->TopologicalSort(sorted
);
241 /* iterate over all clients in graph order, and emit
242 * capture latency callback.
245 for (it
= sorted
.begin(); it
!= sorted
.end(); it
++) {
246 NotifyClient(*it
, kLatencyCallback
, true, "", 0, 0);
249 /* now issue playback latency callbacks in reverse graph order.
251 for (rit
= sorted
.rbegin(); rit
!= sorted
.rend(); rit
++) {
252 NotifyClient(*rit
, kLatencyCallback
, true, "", 1, 0);
262 int JackEngine::ClientNotify(JackClientInterface
* client
, int refnum
, const char* name
, int notify
, int sync
, const char* message
, int value1
, int value2
)
264 // Check if notification is needed
265 if (!client
->GetClientControl()->fCallback
[notify
]) {
266 jack_log("JackEngine::ClientNotify: no callback for notification = %ld", notify
);
273 if (dynamic_cast<JackExternalClient
*>(client
)) {
274 res1
= client
->ClientNotify(refnum
, name
, notify
, sync
, message
, value1
, value2
);
275 // Important for internal client : unlock before calling the notification callbacks
277 bool res2
= Unlock();
278 res1
= client
->ClientNotify(refnum
, name
, notify
, sync
, message
, value1
, value2
);
285 jack_error("ClientNotify fails name = %s notification = %ld val1 = %ld val2 = %ld", name
, notify
, value1
, value2
);
290 void JackEngine::NotifyClient(int refnum
, int event
, int sync
, const char* message
, int value1
, int value2
)
292 JackClientInterface
* client
= fClientTable
[refnum
];
294 ClientNotify(client
, refnum
, client
->GetClientControl()->fName
, event
, sync
, message
, value1
, value2
);
298 void JackEngine::NotifyClients(int event
, int sync
, const char* message
, int value1
, int value2
)
300 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
301 NotifyClient(i
, event
, sync
, message
, value1
, value2
);
305 int JackEngine::NotifyAddClient(JackClientInterface
* new_client
, const char* new_name
, int refnum
)
307 jack_log("JackEngine::NotifyAddClient: name = %s", new_name
);
309 // Notify existing clients of the new client and new client of existing clients.
310 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
311 JackClientInterface
* old_client
= fClientTable
[i
];
312 if (old_client
&& old_client
!= new_client
) {
313 char* old_name
= old_client
->GetClientControl()->fName
;
314 if (ClientNotify(old_client
, refnum
, new_name
, kAddClient
, false, "", 0, 0) < 0) {
315 jack_error("NotifyAddClient old_client fails name = %s", old_name
);
316 // Not considered as a failure...
318 if (ClientNotify(new_client
, i
, old_name
, kAddClient
, true, "", 0, 0) < 0) {
319 jack_error("NotifyAddClient new_client fails name = %s", new_name
);
328 void JackEngine::NotifyRemoveClient(const char* name
, int refnum
)
330 // Notify existing clients (including the one beeing suppressed) of the removed client
331 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
332 JackClientInterface
* client
= fClientTable
[i
];
334 ClientNotify(client
, refnum
, name
, kRemoveClient
, false, "", 0, 0);
339 // Coming from the driver
340 void JackEngine::NotifyDriverXRun()
342 // Use the audio thread => request thread communication channel
343 fChannel
.Notify(ALL_CLIENTS
, kXRunCallback
, 0);
346 void JackEngine::NotifyClientXRun(int refnum
)
348 if (refnum
== ALL_CLIENTS
) {
349 NotifyClients(kXRunCallback
, false, "", 0, 0);
351 NotifyClient(refnum
, kXRunCallback
, false, "", 0, 0);
355 void JackEngine::NotifyGraphReorder()
357 ComputeTotalLatencies();
358 NotifyClients(kGraphOrderCallback
, false, "", 0, 0);
361 void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size
)
363 NotifyClients(kBufferSizeCallback
, true, "", buffer_size
, 0);
366 void JackEngine::NotifySampleRate(jack_nframes_t sample_rate
)
368 NotifyClients(kSampleRateCallback
, true, "", sample_rate
, 0);
371 void JackEngine::NotifyFailure(int code
, const char* reason
)
373 NotifyClients(kShutDownCallback
, false, reason
, code
, 0);
376 void JackEngine::NotifyFreewheel(bool onoff
)
380 fEngineControl
->fSavedRealTime
= fEngineControl
->fRealTime
;
381 fEngineControl
->fRealTime
= false;
384 fEngineControl
->fRealTime
= fEngineControl
->fSavedRealTime
;
385 fEngineControl
->fSavedRealTime
= false;
387 NotifyClients((onoff
? kStartFreewheelCallback
: kStopFreewheelCallback
), true, "", 0, 0);
390 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index
, bool onoff
)
392 NotifyClients((onoff
? kPortRegistrationOnCallback
: kPortRegistrationOffCallback
), false, "", port_index
, 0);
395 void JackEngine::NotifyPortRename(jack_port_id_t port
, const char* old_name
)
397 NotifyClients(kPortRenameCallback
, false, old_name
, port
, 0);
400 void JackEngine::NotifyPortConnect(jack_port_id_t src
, jack_port_id_t dst
, bool onoff
)
402 NotifyClients((onoff
? kPortConnectCallback
: kPortDisconnectCallback
), false, "", src
, dst
);
405 void JackEngine::NotifyActivate(int refnum
)
407 NotifyClient(refnum
, kActivateClient
, true, "", 0, 0);
410 //----------------------------
411 // Loadable client management
412 //----------------------------
414 int JackEngine::GetInternalClientName(int refnum
, char* name_res
)
416 JackClientInterface
* client
= fClientTable
[refnum
];
418 strncpy(name_res
, client
->GetClientControl()->fName
, JACK_CLIENT_NAME_SIZE
);
422 int JackEngine::InternalClientHandle(const char* client_name
, int* status
, int* int_ref
)
427 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
428 JackClientInterface
* client
= fClientTable
[i
];
429 if (client
&& dynamic_cast<JackLoadableInternalClient
*>(client
) && (strcmp(client
->GetClientControl()->fName
, client_name
) == 0)) {
430 jack_log("InternalClientHandle found client name = %s ref = %ld", client_name
, i
);
436 *status
|= (JackNoSuchClient
| JackFailure
);
440 int JackEngine::InternalClientUnload(int refnum
, int* status
)
442 JackClientInterface
* client
= fClientTable
[refnum
];
444 int res
= client
->Close();
449 *status
= (JackNoSuchClient
| JackFailure
);
454 //-------------------
456 //-------------------
458 int JackEngine::ClientCheck(const char* name
, int uuid
, char* name_res
, int protocol
, int options
, int* status
)
462 strcpy(name_res
, name
);
464 jack_log("Check protocol client = %ld server = %ld", protocol
, JACK_PROTOCOL_VERSION
);
466 if (protocol
!= JACK_PROTOCOL_VERSION
) {
467 *status
|= (JackFailure
| JackVersionError
);
468 jack_error("JACK protocol mismatch (%d vs %d)", protocol
, JACK_PROTOCOL_VERSION
);
472 std::map
<int,std::string
>::iterator res
= fReservationMap
.find(uuid
);
474 if (res
!= fReservationMap
.end()) {
475 strncpy(name_res
, res
->second
.c_str(), JACK_CLIENT_NAME_SIZE
);
476 } else if (ClientCheckName(name
)) {
478 *status
|= JackNameNotUnique
;
480 if (options
& JackUseExactName
) {
481 jack_error("cannot create new client; %s already exists", name
);
482 *status
|= JackFailure
;
486 if (GenerateUniqueName(name_res
)) {
487 *status
|= JackFailure
;
495 bool JackEngine::GenerateUniqueName(char* name
)
498 int length
= strlen(name
);
500 if (length
> JACK_CLIENT_NAME_SIZE
- 4) {
501 jack_error("%s exists and is too long to make unique", name
);
502 return true; /* failure */
505 /* generate a unique name by appending "-01".."-99" */
506 name
[length
++] = '-';
513 while (ClientCheckName(name
)) {
514 if (name
[ones
] == '9') {
515 if (name
[tens
] == '9') {
516 jack_error("client %s has 99 extra instances already", name
);
517 return true; /* give up */
528 bool JackEngine::ClientCheckName(const char* name
)
530 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
531 JackClientInterface
* client
= fClientTable
[i
];
532 if (client
&& (strcmp(client
->GetClientControl()->fName
, name
) == 0)) {
537 for (std::map
<int,std::string
>::iterator i
= fReservationMap
.begin(); i
!= fReservationMap
.end(); i
++) {
538 if (i
->second
== name
) {
546 int JackEngine::GetNewUUID()
551 void JackEngine::EnsureUUID(int uuid
)
553 if (uuid
> fMaxUUID
) {
557 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
558 JackClientInterface
* client
= fClientTable
[i
];
559 if (client
&& (client
->GetClientControl()->fSessionID
== uuid
)) {
560 client
->GetClientControl()->fSessionID
= GetNewUUID();
565 int JackEngine::GetClientPID(const char* name
)
567 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
568 JackClientInterface
* client
= fClientTable
[i
];
569 if (client
&& (strcmp(client
->GetClientControl()->fName
, name
) == 0)) {
570 return client
->GetClientControl()->fPID
;
577 int JackEngine::GetClientRefNum(const char* name
)
579 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
580 JackClientInterface
* client
= fClientTable
[i
];
581 if (client
&& (strcmp(client
->GetClientControl()->fName
, name
) == 0)) {
582 return client
->GetClientControl()->fRefNum
;
589 // Used for external clients
590 int JackEngine::ClientExternalOpen(const char* name
, int pid
, int uuid
, int* ref
, int* shared_engine
, int* shared_client
, int* shared_graph_manager
)
592 char real_name
[JACK_CLIENT_NAME_SIZE
+ 1];
596 strncpy(real_name
, name
, JACK_CLIENT_NAME_SIZE
);
598 std::map
<int, std::string
>::iterator res
= fReservationMap
.find(uuid
);
599 if (res
!= fReservationMap
.end()) {
600 strncpy(real_name
, res
->second
.c_str(), JACK_CLIENT_NAME_SIZE
);
601 fReservationMap
.erase(uuid
);
603 strncpy(real_name
, name
, JACK_CLIENT_NAME_SIZE
);
608 jack_log("JackEngine::ClientExternalOpen: uuid = %d, name = %s", uuid
, real_name
);
610 int refnum
= AllocateRefnum();
612 jack_error("No more refnum available");
616 JackExternalClient
* client
= new JackExternalClient();
618 if (!fSynchroTable
[refnum
].Allocate(real_name
, fEngineControl
->fServerName
, 0)) {
619 jack_error("Cannot allocate synchro");
623 if (client
->Open(real_name
, pid
, refnum
, uuid
, shared_client
) < 0) {
624 jack_error("Cannot open client");
628 if (!fSignal
.LockedTimedWait(DRIVER_OPEN_TIMEOUT
* 1000000)) {
629 // Failure if RT thread is not running (problem with the driver...)
630 jack_error("Driver is not running");
634 fClientTable
[refnum
] = client
;
636 if (NotifyAddClient(client
, real_name
, refnum
) < 0) {
637 jack_error("Cannot notify add client");
641 fGraphManager
->InitRefNum(refnum
);
642 fEngineControl
->ResetRollingUsecs();
643 *shared_engine
= fEngineControl
->GetShmIndex();
644 *shared_graph_manager
= fGraphManager
->GetShmIndex();
650 fSynchroTable
[refnum
].Destroy();
651 fClientTable
[refnum
] = 0;
657 // Used for server driver clients
658 int JackEngine::ClientInternalOpen(const char* name
, int* ref
, JackEngineControl
** shared_engine
, JackGraphManager
** shared_manager
, JackClientInterface
* client
, bool wait
)
660 jack_log("JackEngine::ClientInternalOpen: name = %s", name
);
662 int refnum
= AllocateRefnum();
664 jack_error("No more refnum available");
668 if (!fSynchroTable
[refnum
].Allocate(name
, fEngineControl
->fServerName
, 0)) {
669 jack_error("Cannot allocate synchro");
673 if (wait
&& !fSignal
.LockedTimedWait(DRIVER_OPEN_TIMEOUT
* 1000000)) {
674 // Failure if RT thread is not running (problem with the driver...)
675 jack_error("Driver is not running");
679 fClientTable
[refnum
] = client
;
681 if (NotifyAddClient(client
, name
, refnum
) < 0) {
682 jack_error("Cannot notify add client");
686 fGraphManager
->InitRefNum(refnum
);
687 fEngineControl
->ResetRollingUsecs();
688 *shared_engine
= fEngineControl
;
689 *shared_manager
= fGraphManager
;
695 fSynchroTable
[refnum
].Destroy();
696 fClientTable
[refnum
] = 0;
700 // Used for external clients
701 int JackEngine::ClientExternalClose(int refnum
)
703 jack_log("JackEngine::ClientExternalClose ref = %ld", refnum
);
704 JackClientInterface
* client
= fClientTable
[refnum
];
706 int res
= ClientCloseAux(refnum
, true);
712 // Used for server internal clients or drivers when the RT thread is stopped
713 int JackEngine::ClientInternalClose(int refnum
, bool wait
)
715 jack_log("JackEngine::ClientInternalClose ref = %ld", refnum
);
716 return ClientCloseAux(refnum
, wait
);
719 int JackEngine::ClientCloseAux(int refnum
, bool wait
)
721 jack_log("JackEngine::ClientCloseAux ref = %ld", refnum
);
723 JackClientInterface
* client
= fClientTable
[refnum
];
724 fEngineControl
->fTransport
.ResetTimebase(refnum
);
726 // Unregister all ports ==> notifications are sent
727 jack_int_t ports
[PORT_NUM_FOR_CLIENT
];
730 fGraphManager
->GetInputPorts(refnum
, ports
);
731 for (i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (ports
[i
] != EMPTY
); i
++) {
732 PortUnRegister(refnum
, ports
[i
]);
735 fGraphManager
->GetOutputPorts(refnum
, ports
);
736 for (i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (ports
[i
] != EMPTY
); i
++) {
737 PortUnRegister(refnum
, ports
[i
]);
740 // Remove the client from the table
741 ReleaseRefnum(refnum
);
744 fGraphManager
->RemoveAllPorts(refnum
);
746 // Wait until next cycle to be sure client is not used anymore
748 if (!fSignal
.LockedTimedWait(fEngineControl
->fTimeOutUsecs
* 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
749 jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum
);
753 // Notify running clients
754 NotifyRemoveClient(client
->GetClientControl()->fName
, refnum
);
757 fSynchroTable
[refnum
].Destroy();
758 fEngineControl
->ResetRollingUsecs();
762 int JackEngine::ClientActivate(int refnum
, bool is_real_time
)
764 JackClientInterface
* client
= fClientTable
[refnum
];
765 jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum
, client
->GetClientControl()->fName
);
768 fGraphManager
->Activate(refnum
);
771 // Wait for graph state change to be effective
772 if (!fSignal
.LockedTimedWait(fEngineControl
->fTimeOutUsecs
* 10)) {
773 jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum
, client
->GetClientControl()->fName
);
776 jack_int_t input_ports
[PORT_NUM_FOR_CLIENT
];
777 jack_int_t output_ports
[PORT_NUM_FOR_CLIENT
];
778 fGraphManager
->GetInputPorts(refnum
, input_ports
);
779 fGraphManager
->GetOutputPorts(refnum
, output_ports
);
782 NotifyActivate(refnum
);
784 // Then issue port registration notification
785 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (input_ports
[i
] != EMPTY
); i
++) {
786 NotifyPortRegistation(input_ports
[i
], true);
788 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (output_ports
[i
] != EMPTY
); i
++) {
789 NotifyPortRegistation(output_ports
[i
], true);
796 // May be called without client
797 int JackEngine::ClientDeactivate(int refnum
)
799 JackClientInterface
* client
= fClientTable
[refnum
];
800 jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum
, client
->GetClientControl()->fName
);
802 jack_int_t input_ports
[PORT_NUM_FOR_CLIENT
];
803 jack_int_t output_ports
[PORT_NUM_FOR_CLIENT
];
804 fGraphManager
->GetInputPorts(refnum
, input_ports
);
805 fGraphManager
->GetOutputPorts(refnum
, output_ports
);
807 // First disconnect all ports
808 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (input_ports
[i
] != EMPTY
); i
++) {
809 PortDisconnect(-1, input_ports
[i
], ALL_PORTS
);
811 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (output_ports
[i
] != EMPTY
); i
++) {
812 PortDisconnect(-1, output_ports
[i
], ALL_PORTS
);
815 // Then issue port registration notification
816 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (input_ports
[i
] != EMPTY
); i
++) {
817 NotifyPortRegistation(input_ports
[i
], false);
819 for (int i
= 0; (i
< PORT_NUM_FOR_CLIENT
) && (output_ports
[i
] != EMPTY
); i
++) {
820 NotifyPortRegistation(output_ports
[i
], false);
823 fGraphManager
->Deactivate(refnum
);
824 fLastSwitchUsecs
= 0; // Force switch to occur next cycle, even when called with "dead" clients
826 // Wait for graph state change to be effective
827 if (!fSignal
.LockedTimedWait(fEngineControl
->fTimeOutUsecs
* 10)) {
828 jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum
, client
->GetClientControl()->fName
);
835 void JackEngine::ClientKill(int refnum
)
837 jack_log("JackEngine::ClientKill ref = %ld", refnum
);
838 if (ClientDeactivate(refnum
) < 0) {
839 jack_error("JackEngine::ClientKill ref = %ld cannot be removed from the graph !!", refnum
);
841 if (ClientExternalClose(refnum
) < 0) {
842 jack_error("JackEngine::ClientKill ref = %ld cannot be closed", refnum
);
850 int JackEngine::PortRegister(int refnum
, const char* name
, const char *type
, unsigned int flags
, unsigned int buffer_size
, jack_port_id_t
* port_index
)
852 jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum
, name
, type
, flags
, buffer_size
);
853 JackClientInterface
* client
= fClientTable
[refnum
];
855 // Check if port name already exists
856 if (fGraphManager
->GetPort(name
) != NO_PORT
) {
857 jack_error("port_name \"%s\" already exists", name
);
861 // buffer_size is actually ignored...
862 *port_index
= fGraphManager
->AllocatePort(refnum
, name
, type
, (JackPortFlags
)flags
, fEngineControl
->fBufferSize
);
863 if (*port_index
!= NO_PORT
) {
864 if (client
->GetClientControl()->fActive
) {
865 NotifyPortRegistation(*port_index
, true);
873 int JackEngine::PortUnRegister(int refnum
, jack_port_id_t port_index
)
875 jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum
, port_index
);
876 JackClientInterface
* client
= fClientTable
[refnum
];
879 // Disconnect port ==> notification is sent
880 PortDisconnect(-1, port_index
, ALL_PORTS
);
882 if (fGraphManager
->ReleasePort(refnum
, port_index
) == 0) {
883 if (client
->GetClientControl()->fActive
) {
884 NotifyPortRegistation(port_index
, false);
892 // this check is to prevent apps to self connect to other apps
893 // TODO: make this work with multiple clients per app
894 int JackEngine::CheckPortsConnect(int refnum
, jack_port_id_t src
, jack_port_id_t dst
)
896 if (fSelfConnectMode
== ' ') return 1;
898 JackPort
* src_port
= fGraphManager
->GetPort(src
);
899 JackPort
* dst_port
= fGraphManager
->GetPort(dst
);
901 jack_log("JackEngine::CheckPortsConnect(ref = %d, src = %d, dst = %d)", refnum
, src_port
->GetRefNum(), dst_port
->GetRefNum());
903 //jack_log("%s -> %s", src_port->GetName(), dst_port->GetName());
904 //jack_log("mode = '%c'", fSelfConnectMode);
906 int src_self
= src_port
->GetRefNum() == refnum
? 1 : 0;
907 int dst_self
= dst_port
->GetRefNum() == refnum
? 1 : 0;
909 //jack_log("src_self is %s", src_self ? "true" : "false");
910 //jack_log("dst_self is %s", dst_self ? "true" : "false");
912 // 0 means client is connecting other client ports (control app patchbay functionality)
913 // 1 means client is connecting its own port to port of other client (e.g. self connecting into "system" client)
914 // 2 means client is connecting its own ports (for app internal functionality)
915 int sum
= src_self
+ dst_self
;
916 //jack_log("sum = %d", sum);
917 if (sum
== 0) return 1;
918 char lmode
= tolower(fSelfConnectMode
);
919 //jack_log("lmode = '%c'", lmode);
920 if (sum
== 2 && lmode
== 'e') return 1;
921 bool fail
= lmode
!= fSelfConnectMode
; // fail modes are upper case
922 //jack_log("fail = %d", (int)fail);
925 "%s port self connect request%s (%s -> %s)",
926 fail
? "rejecting" : "ignoring",
927 sum
== 1 ? " to external port" : "",
929 dst_port
->GetName());
931 return fail
? -1 : 0;
934 int JackEngine::PortConnect(int refnum
, const char* src
, const char* dst
)
936 jack_log("JackEngine::PortConnect ref = %d src = %s dst = %s", refnum
, src
, dst
);
937 jack_port_id_t port_src
, port_dst
;
939 return (fGraphManager
->GetTwoPorts(src
, dst
, &port_src
, &port_dst
) < 0)
941 : PortConnect(refnum
, port_src
, port_dst
);
944 int JackEngine::PortConnect(int refnum
, jack_port_id_t src
, jack_port_id_t dst
)
946 jack_log("JackEngine::PortConnect ref = %d src = %d dst = %d", refnum
, src
, dst
);
947 JackClientInterface
* client
;
950 if (fGraphManager
->CheckPorts(src
, dst
) < 0) {
954 ref
= fGraphManager
->GetOutputRefNum(src
);
956 client
= fClientTable
[ref
];
958 if (!client
->GetClientControl()->fActive
) {
959 jack_error("Cannot connect ports owned by inactive clients:"
960 " \"%s\" is not active", client
->GetClientControl()->fName
);
964 ref
= fGraphManager
->GetInputRefNum(dst
);
966 client
= fClientTable
[ref
];
968 if (!client
->GetClientControl()->fActive
) {
969 jack_error("Cannot connect ports owned by inactive clients:"
970 " \"%s\" is not active", client
->GetClientControl()->fName
);
974 int res
= CheckPortsConnect(refnum
, src
, dst
);
979 res
= fGraphManager
->Connect(src
, dst
);
981 NotifyPortConnect(src
, dst
, true);
986 int JackEngine::PortDisconnect(int refnum
, const char* src
, const char* dst
)
988 jack_log("JackEngine::PortDisconnect ref = %d src = %s dst = %s", refnum
, src
, dst
);
989 jack_port_id_t port_src
, port_dst
;
991 return (fGraphManager
->GetTwoPorts(src
, dst
, &port_src
, &port_dst
) < 0)
993 : PortDisconnect(refnum
, port_src
, port_dst
);
996 int JackEngine::PortDisconnect(int refnum
, jack_port_id_t src
, jack_port_id_t dst
)
998 jack_log("JackEngine::PortDisconnect ref = %d src = %d dst = %d", refnum
, src
, dst
);
1000 if (dst
== ALL_PORTS
) {
1002 jack_int_t connections
[CONNECTION_NUM_FOR_PORT
];
1003 fGraphManager
->GetConnections(src
, connections
);
1005 JackPort
* port
= fGraphManager
->GetPort(src
);
1007 if (port
->GetFlags() & JackPortIsOutput
) {
1008 for (int i
= 0; (i
< CONNECTION_NUM_FOR_PORT
) && (connections
[i
] != EMPTY
); i
++) {
1009 if (PortDisconnect(refnum
, src
, connections
[i
]) != 0) {
1014 for (int i
= 0; (i
< CONNECTION_NUM_FOR_PORT
) && (connections
[i
] != EMPTY
); i
++) {
1015 if (PortDisconnect(refnum
, connections
[i
], src
) != 0) {
1024 if (fGraphManager
->CheckPorts(src
, dst
) < 0) {
1028 int res
= CheckPortsConnect(refnum
, src
, dst
);
1033 res
= fGraphManager
->Disconnect(src
, dst
);
1035 NotifyPortConnect(src
, dst
, false);
1039 int JackEngine::PortRename(int refnum
, jack_port_id_t port
, const char* name
)
1041 char old_name
[REAL_JACK_PORT_NAME_SIZE
+1];
1042 strcpy(old_name
, fGraphManager
->GetPort(port
)->GetName());
1043 fGraphManager
->GetPort(port
)->SetName(name
);
1044 NotifyPortRename(port
, old_name
);
1048 //--------------------
1049 // Session management
1050 //--------------------
1052 void JackEngine::SessionNotify(int refnum
, const char *target
, jack_session_event_type_t type
, const char *path
, detail::JackChannelTransactionInterface
*socket
, JackSessionNotifyResult
** result
)
1054 if (fSessionPendingReplies
!= 0) {
1055 JackSessionNotifyResult
res(-1);
1057 jack_log("JackEngine::SessionNotify ... busy");
1058 if (result
!= NULL
) *result
= NULL
;
1062 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
1063 JackClientInterface
* client
= fClientTable
[i
];
1064 if (client
&& (client
->GetClientControl()->fSessionID
< 0)) {
1065 client
->GetClientControl()->fSessionID
= GetNewUUID();
1068 fSessionResult
= new JackSessionNotifyResult();
1070 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
1071 JackClientInterface
* client
= fClientTable
[i
];
1072 if (client
&& client
->GetClientControl()->fCallback
[kSessionCallback
]) {
1074 // check if this is a notification to a specific client.
1075 if (target
!= NULL
&& strlen(target
) != 0) {
1076 if (strcmp(target
, client
->GetClientControl()->fName
)) {
1081 char path_buf
[JACK_PORT_NAME_SIZE
];
1082 if (path
[strlen(path
) - 1] == DIR_SEPARATOR
) {
1083 snprintf(path_buf
, sizeof path_buf
, "%s%s%c", path
, client
->GetClientControl()->fName
, DIR_SEPARATOR
);
1085 snprintf(path_buf
, sizeof path_buf
, "%s%c%s%c", path
, DIR_SEPARATOR
, client
->GetClientControl()->fName
, DIR_SEPARATOR
);
1088 int res
= JackTools::MkDir(path_buf
);
1089 if (res
) jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf
);
1091 int result
= client
->ClientNotify(i
, client
->GetClientControl()->fName
, kSessionCallback
, true, path_buf
, (int)type
, 0);
1093 if (result
== kPendingSessionReply
) {
1094 fSessionPendingReplies
+= 1;
1095 } else if (result
== kImmediateSessionReply
) {
1096 char uuid_buf
[JACK_UUID_SIZE
];
1097 snprintf(uuid_buf
, sizeof(uuid_buf
), "%d", client
->GetClientControl()->fSessionID
);
1098 fSessionResult
->fCommandList
.push_back(JackSessionCommand(uuid_buf
,
1099 client
->GetClientControl()->fName
,
1100 client
->GetClientControl()->fSessionCommand
,
1101 client
->GetClientControl()->fSessionFlags
));
1106 if (result
!= NULL
) *result
= fSessionResult
;
1108 if (fSessionPendingReplies
== 0) {
1109 fSessionResult
->Write(socket
);
1110 if (result
== NULL
) delete fSessionResult
;
1111 fSessionResult
= NULL
;
1113 fSessionTransaction
= socket
;
1117 int JackEngine::SessionReply(int refnum
)
1119 JackClientInterface
* client
= fClientTable
[refnum
];
1121 char uuid_buf
[JACK_UUID_SIZE
];
1122 snprintf(uuid_buf
, sizeof(uuid_buf
), "%d", client
->GetClientControl()->fSessionID
);
1123 fSessionResult
->fCommandList
.push_back(JackSessionCommand(uuid_buf
,
1124 client
->GetClientControl()->fName
,
1125 client
->GetClientControl()->fSessionCommand
,
1126 client
->GetClientControl()->fSessionFlags
));
1127 fSessionPendingReplies
-= 1;
1129 if (fSessionPendingReplies
== 0) {
1130 fSessionResult
->Write(fSessionTransaction
);
1131 if (fSessionTransaction
!= NULL
) {
1132 delete fSessionResult
;
1134 fSessionResult
= NULL
;
1140 int JackEngine::GetUUIDForClientName(const char *client_name
, char *uuid_res
)
1142 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
1143 JackClientInterface
* client
= fClientTable
[i
];
1145 if (client
&& (strcmp(client_name
, client
->GetClientControl()->fName
) == 0)) {
1146 snprintf(uuid_res
, JACK_UUID_SIZE
, "%d", client
->GetClientControl()->fSessionID
);
1150 // Did not find name.
1154 int JackEngine::GetClientNameForUUID(const char *uuid
, char *name_res
)
1156 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
1157 JackClientInterface
* client
= fClientTable
[i
];
1163 char uuid_buf
[JACK_UUID_SIZE
];
1164 snprintf(uuid_buf
, JACK_UUID_SIZE
, "%d", client
->GetClientControl()->fSessionID
);
1166 if (strcmp(uuid
,uuid_buf
) == 0) {
1167 strncpy(name_res
, client
->GetClientControl()->fName
, JACK_CLIENT_NAME_SIZE
);
1171 // Did not find uuid.
1175 int JackEngine::ReserveClientName(const char *name
, const char *uuid
)
1177 jack_log("JackEngine::ReserveClientName ( name = %s, uuid = %s )", name
, uuid
);
1179 if (ClientCheckName(name
)) {
1180 jack_log("name already taken");
1184 EnsureUUID(atoi(uuid
));
1185 fReservationMap
[atoi(uuid
)] = name
;
1189 int JackEngine::ClientHasSessionCallback(const char *name
)
1191 JackClientInterface
* client
= NULL
;
1192 for (int i
= 0; i
< CLIENT_NUM
; i
++) {
1193 client
= fClientTable
[i
];
1194 if (client
&& (strcmp(client
->GetClientControl()->fName
, name
) == 0)) {
1200 return client
->GetClientControl()->fCallback
[kSessionCallback
];
1206 } // end of namespace