1
2 /*
3 * speaking.c - Speech Dispatcher speech output functions
4 *
5 * Copyright (C) 2001,2002,2003, 2006, 2007 Brailcom, o.p.s
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 *
20 * $Id: speaking.c,v 1.56 2008-10-15 18:06:48 hanke Exp $
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <glib.h>
28 #include <poll.h>
29 #include <unistd.h>
30 #include <safe_io.h>
31 #include "speechd.h"
32 #include "server.h"
33 #include "index_marking.h"
34 #include "symbols.h"
35 #include "module.h"
36 #include "set.h"
37 #include "alloc.h"
38 #include "msg.h"
39 #include "output.h"
40 #include "speaking.h"
41 #include "sem_functions.h"
42
43 TSpeechDMessage *current_message = NULL;
44 static SPDPriority highest_priority = 0;
45
46 int SPEAKING = 0;
47 int poll_count;
48
49 OutputModule *speaking_module;
50 int speaking_uid;
51 int speaking_gid;
52
53 /* Pause and resume handling */
54 int pause_requested;
55 int pause_requested_fd;
56 int pause_requested_uid;
57 int resume_requested;
58
59 /*
60 Speak() is responsible for getting right text from right
61 queue in right time and saying it loud through the corresponding
62 synthetiser. This runs in a separate thread.
63 */
speak(void * data)64 void *speak(void *data)
65 {
66 TSpeechDMessage *message = NULL;
67 int ret;
68 struct pollfd *poll_fds; /* Descriptors to poll */
69 struct pollfd main_pfd;
70 struct pollfd helper_pfd;
71 int revents;
72 OutputModule *output;
73
74 /* Block all signals and set thread states */
75 set_speak_thread_attributes();
76
77 poll_fds = g_malloc(2 * sizeof(struct pollfd));
78
79 main_pfd.fd = speaking_pipe[0];
80 main_pfd.events = POLLIN;
81 main_pfd.revents = 0;
82
83 helper_pfd.fd = -1;
84 helper_pfd.events = POLLIN;
85 helper_pfd.revents = 0;
86
87 poll_fds[0] = main_pfd;
88 poll_fds[1] = helper_pfd;
89 poll_count = 1;
90
91 while (1) {
92 ret = poll(poll_fds, poll_count, -1);
93 MSG(5,
94 "Poll in speak() returned socket activity, main_pfd revents=%d, poll_pfd revents=%d",
95 poll_fds[0].revents, poll_fds[1].revents);
96 if ((revents = poll_fds[0].revents)) {
97 if (revents & POLLIN) {
98 char buf[1];
99 MSG(5,
100 "wait_for_poll: activity in Speech Dispatcher");
101 const ssize_t rd_bytes =
102 safe_read(poll_fds[0].fd, buf, 1);
103 if (rd_bytes != 1)
104 FATAL
105 ("read from polled fd: could not read 1 byte");
106 }
107 }
108 if (poll_count > 1) {
109 if ((revents = poll_fds[1].revents)) {
110 if (revents & POLLHUP) {
111 // FIXME: We should handle this more gracefully
112 FATAL
113 ("wait_for_poll: output_module disconnected");
114 } else if ((revents & POLLIN)
115 || (revents & POLLPRI)) {
116 MSG(5,
117 "wait_for_poll: activity on output_module");
118 /* Check if sb is speaking or they are all silent.
119 * If some synthesizer is speaking, we must wait. */
120 is_sb_speaking();
121 }
122 }
123 }
124
125 /* Handle pause requests */
126 if (pause_requested) {
127 MSG(4, "Trying to pause...");
128 if (pause_requested == 1)
129 speaking_pause_all(pause_requested_fd);
130 if (pause_requested == 2)
131 speaking_pause(pause_requested_fd,
132 pause_requested_uid);
133 MSG(4, "Paused...");
134 pause_requested = 0;
135 continue;
136 }
137
138 if (SPEAKING) {
139 MSG(5,
140 "Continuing because already speaking in speak()");
141 continue;
142 }
143
144 /* Handle resume requests */
145 if (resume_requested) {
146 GList *gl;
147
148 MSG(5, "Resume requested");
149
150 /* Is there any message after resume? */
151 if (g_list_length(MessagePausedList) != 0) {
152 while (1) {
153 pthread_mutex_lock(&element_free_mutex);
154 gl = g_list_find_custom
155 (MessagePausedList, (void *)NULL,
156 message_nto_speak);
157 MSG(5,
158 "Message insterted back to the queues!");
159 MessagePausedList =
160 g_list_remove_link
161 (MessagePausedList, gl);
162 pthread_mutex_unlock
163 (&element_free_mutex);
164 if ((gl != NULL) && (gl->data != NULL)) {
165 MSG(5, "Reloading message");
166 reload_message((TSpeechDMessage
167 *) gl->data);
168 /* If this resumed message is the same as current_message, then it gets
169 * another trip through the queue. However, some code later in this
170 * function will free current_message, even though it is now requeued!
171 * Hence use-after-free.
172 * current_message is pretty useless after the requeue, make it NULL. */
173 if (current_message == gl->data)
174 current_message = NULL;
175 } else
176 break;
177 }
178 }
179 MSG(5, "End of resume processing");
180 resume_requested = 0;
181 }
182
183 MSG(5, "Locking element_free_mutex in speak()");
184 pthread_mutex_lock(&element_free_mutex);
185 /* Handle postponed priority progress message */
186 check_locked(&element_free_mutex);
187 if ((g_list_length(last_p5_block) != 0)
188 && (g_list_length(MessageQueue->p5) == 0)) {
189 /* Transfer messages from last_p5_block to priority 2 (message) queue */
190 while (g_list_length(last_p5_block) != 0) {
191 GList *item;
192 item = g_list_first(last_p5_block);
193 message = item->data;
194 check_locked(&element_free_mutex);
195 MessageQueue->p2 =
196 g_list_insert_sorted(MessageQueue->p2,
197 message, sortbyuid);
198 last_p5_block =
199 g_list_remove_link(last_p5_block, item);
200 g_list_free1(item);
201 }
202 assert(message != NULL);
203 highest_priority = SPD_MESSAGE;
204 stop_priority_older_than(SPD_TEXT, message->id);
205 stop_priority(SPD_NOTIFICATION);
206 stop_priority(SPD_PROGRESS);
207 check_locked(&element_free_mutex);
208 pthread_mutex_unlock(&element_free_mutex);
209 speaking_semaphore_post();
210 continue;
211 } else {
212 /* Extract the right message from priority queue */
213 message = get_message_from_queues();
214 if (message == NULL) {
215 pthread_mutex_unlock(&element_free_mutex);
216 MSG(5, "No message in the queue");
217 continue;
218 }
219 }
220
221 /* Isn't the parent client of this message paused?
222 * If it is, insert the message to the MessagePausedList. */
223 if (message_nto_speak(message, NULL)) {
224 MSG(4, "Inserting message to paused list...");
225 MessagePausedList =
226 g_list_append(MessagePausedList, message);
227 pthread_mutex_unlock(&element_free_mutex);
228 continue;
229 }
230
231 /* Choose the output module */
232 output = get_output_module(message);
233 if (output == NULL) {
234 MSG(3, "Output module doesn't work...");
235 output_check_module(output);
236 pthread_mutex_unlock(&element_free_mutex);
237 continue;
238 }
239
240 int punct_missing = 0;
241 if (strcmp(output->name, "flite") == 0 ||
242 strcmp(output->name, "dtk-generic") == 0 ||
243 strcmp(output->name, "epos-generic") == 0 ||
244 strcmp(output->name, "llia_phon-generic") == 0 ||
245 strcmp(output->name, "mary-generic") == 0 ||
246 strcmp(output->name, "swift-generic") == 0 ||
247 strcmp(output->name, "pico") == 0)
248 /* These don't support punctuation */
249 /* FIXME: rather make them express it */
250 punct_missing = 1;
251
252 if (message->settings.type == SPD_MSGTYPE_TEXT ||
253 message->settings.type == SPD_MSGTYPE_CHAR) {
254 gchar *normalized = g_utf8_normalize(message->buf, -1,
255 G_NORMALIZE_ALL_COMPOSE);
256 if (!normalized) {
257 MSG(2, "Error: Not UTF-8 valid");
258 pthread_mutex_unlock(&element_free_mutex);
259 continue;
260 }
261 if (strcmp(message->buf, normalized)) {
262 MSG(5, "text: Normalized '%s' to '%s'", message->buf, normalized);
263 }
264 message->buf = normalized;
265 insert_symbols(message, punct_missing);
266 }
267
268 /* Insert index marks into textual messages */
269 if (message->settings.type == SPD_MSGTYPE_TEXT) {
270 insert_index_marks(message,
271 message->settings.ssml_mode);
272 }
273
274 /* Write the message to the output layer. */
275 ret = output_speak(message, output);
276
277 MSG(4, "Message sent to output module");
278 if (ret == -1) {
279 MSG(2, "Error: Output module failed");
280 output_check_module(output);
281 pthread_mutex_unlock(&element_free_mutex);
282 continue;
283 }
284 if (ret != 0) {
285 MSG(2,
286 "ERROR: Can't say message. Module reported error in speaking: %d",
287 ret);
288 pthread_mutex_unlock(&element_free_mutex);
289 continue;
290 }
291 SPEAKING = 1;
292
293 if (speaking_module != NULL) {
294 poll_count = 2;
295 helper_pfd.fd = speaking_module->pipe_out[0];
296 poll_fds[1] = helper_pfd;
297 }
298
299 /* Set the id of the client who is speaking. */
300 speaking_uid = message->settings.uid;
301 if (current_message != NULL) {
302 if (!current_message->settings.paused_while_speaking) {
303 /* Check if the client who emited this message is disconnected
304 by now and this was his last message. If so, delete it's settings
305 from fdset */
306 if (get_client_settings_by_uid
307 (current_message->settings.uid)->active ==
308 0) {
309 if (!client_has_messages
310 (current_message->settings.uid)
311 && (current_message->settings.uid !=
312 message->settings.uid)) {
313 /* client_has_messages does not account for message,
314 which was just retrieved from the queue.
315 We also have to compare the uids of message
316 and current_message to be sure that there are
317 no outstanding messages. */
318 MSG(4,
319 "Removing client settings for uid %d",
320 current_message->
321 settings.uid);
322 remove_client_settings_by_uid
323 (current_message->
324 settings.uid);
325 }
326 }
327 mem_free_message(current_message);
328 }
329 }
330 current_message = message;
331
332 /* Check if the last priority 5 message wasn't said yet */
333 if (last_p5_block != NULL) {
334 GList *elem;
335 TSpeechDMessage *p5_message;
336 elem = g_list_last(last_p5_block);
337 if (elem != NULL) {
338 p5_message = (TSpeechDMessage *) elem->data;
339 if (p5_message->settings.reparted ==
340 message->settings.reparted) {
341 g_list_foreach(last_p5_block,
342 (GFunc) mem_free_message,
343 NULL);
344 g_list_free(last_p5_block);
345 last_p5_block = NULL;
346 }
347 }
348 }
349
350 pthread_mutex_unlock(&element_free_mutex);
351 }
352 }
353
reload_message(TSpeechDMessage * msg)354 int reload_message(TSpeechDMessage * msg)
355 {
356 TFDSetElement *client_settings;
357 int im;
358 char *pos;
359 char *newtext;
360 char *tptr;
361
362 if (msg == NULL) {
363 MSG(4, "Warning: msg == NULL in reload_message()");
364 return -1;
365 }
366
367 if (msg->settings.index_mark != NULL) {
368 MSG(5, "Recovering index mark %s", msg->settings.index_mark);
369 client_settings = get_client_settings_by_uid(msg->settings.uid);
370 /* Scroll back to provide context, if required */
371 /* WARNING: This relies on ordered SD_MARK_BODY index marks! */
372 MSG(5, "Recovering index mark (number)");
373 im = strtol(msg->settings.index_mark + SD_MARK_BODY_LEN, &tptr,
374 10);
375 MSG(5, "Recovering index mark (comparing tptr)");
376 if (msg->settings.index_mark + SD_MARK_BODY_LEN == tptr) {
377 MSG2(2, "index_marking",
378 "ERROR: Invalid index_mark '%s'. Message not reloaded.",
379 msg->settings.index_mark);
380 return -1;
381 }
382 MSG(5, "Recovered index mark number: %d", im);
383
384 im += client_settings->pause_context;
385
386 MSG2(5, "index_marking",
387 "Requested index mark (with context) is %d (%s+%d)", im,
388 msg->settings.index_mark, client_settings->pause_context);
389 if (im < 0) {
390 im = 0;
391 pos = msg->buf;
392 } else {
393 pos = find_index_mark(msg, im);
394 if (pos == NULL)
395 return -1;
396 }
397
398 newtext = strip_index_marks(pos, client_settings->ssml_mode);
399 g_free(msg->buf);
400
401 if (newtext == NULL)
402 return -1;
403 msg->buf = newtext;
404 msg->bytes = strlen(msg->buf);
405
406 if (queue_message
407 (msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
408 if (SPEECHD_DEBUG)
409 FATAL("Can't queue message\n");
410 g_free(msg->buf);
411 g_free(msg);
412 return -1;
413 }
414
415 return 0;
416 } else {
417 MSG(5, "Index mark unknown, inserting the whole message.");
418
419 if (queue_message
420 (msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
421 if (SPEECHD_DEBUG)
422 FATAL("Can't queue message\n");
423 g_free(msg->buf);
424 g_free(msg);
425 return -1;
426 }
427
428 return 0;
429 }
430 return 0;
431 }
432
speaking_stop(int uid)433 void speaking_stop(int uid)
434 {
435 TSpeechDMessage *msg;
436 GList *gl;
437 GList *queue;
438 signed int gid = -1;
439
440 /* Only act if the currently speaking client is the specified one */
441 if (get_speaking_client_uid() == uid) {
442 output_stop();
443
444 /* Get the queue where the message being spoken came from */
445 queue = speaking_get_queue(highest_priority);
446 if (queue == NULL)
447 return;
448
449 /* Get group ID of the current message */
450 gl = g_list_last(queue);
451 if (gl == NULL)
452 return;
453 if (gl->data == NULL)
454 return;
455
456 msg = (TSpeechDMessage *) gl->data;
457 if ((msg->settings.reparted != 0) && (msg->settings.uid == uid)) {
458 gid = msg->settings.reparted;
459 } else {
460 return;
461 }
462
463 while (1) {
464 gl = g_list_last(queue);
465 if (gl == NULL) {
466 speaking_set_queue(highest_priority, queue);
467 return;
468 }
469 if (gl->data == NULL)
470 return;
471
472 msg = (TSpeechDMessage *) gl->data;
473
474 if ((msg->settings.reparted == gid)
475 && (msg->settings.uid == uid)) {
476 queue = g_list_remove_link(queue, gl);
477 assert(gl->data != NULL);
478 mem_free_message(gl->data);
479 } else {
480 speaking_set_queue(highest_priority, queue);
481 return;
482 }
483 }
484 }
485 }
486
speaking_stop_all()487 void speaking_stop_all()
488 {
489 TSpeechDMessage *msg;
490 GList *gl;
491 GList *queue;
492
493 output_stop();
494
495 queue = speaking_get_queue(highest_priority);
496 if (queue == NULL)
497 return;
498
499 gl = g_list_last(queue);
500 if (gl == NULL)
501 return;
502 assert(gl->data != NULL);
503 msg = (TSpeechDMessage *) gl->data;
504
505 if (msg->settings.reparted == 0) {
506 return;
507 }
508
509 while (1) {
510 gl = g_list_last(queue);
511 if (gl == NULL) {
512 speaking_set_queue(highest_priority, queue);
513 return;
514 }
515 if (SPEECHD_DEBUG)
516 assert(gl->data != NULL);
517
518 msg = (TSpeechDMessage *) gl->data;
519 if (msg->settings.reparted == 1) {
520 queue = g_list_remove_link(queue, gl);
521 assert(gl->data != NULL);
522 mem_free_message(gl->data);
523 } else {
524 speaking_set_queue(highest_priority, queue);
525 return;
526 }
527 }
528 }
529
speaking_cancel(int uid)530 void speaking_cancel(int uid)
531 {
532 pthread_mutex_lock(&element_free_mutex);
533 speaking_stop(uid);
534 stop_from_uid(uid);
535 pthread_mutex_unlock(&element_free_mutex);
536 }
537
speaking_cancel_all()538 void speaking_cancel_all()
539 {
540 output_stop();
541 pthread_mutex_lock(&element_free_mutex);
542 stop_priority(SPD_IMPORTANT);
543 stop_priority(SPD_MESSAGE);
544 stop_priority(SPD_TEXT);
545 stop_priority(SPD_NOTIFICATION);
546 stop_priority(SPD_PROGRESS);
547 pthread_mutex_unlock(&element_free_mutex);
548 }
549
speaking_pause_all(int fd)550 int speaking_pause_all(int fd)
551 {
552 int err = 0;
553 int i;
554 int uid;
555
556 for (i = 1; i <= SpeechdStatus.max_fd; i++) {
557 uid = get_client_uid_by_fd(i);
558 if (uid == 0)
559 continue;
560 err += speaking_pause(i, uid);
561 }
562
563 if (err > 0)
564 return 1;
565 else
566 return 0;
567 }
568
speaking_pause(int fd,int uid)569 int speaking_pause(int fd, int uid)
570 {
571 TFDSetElement *settings;
572 int ret;
573
574 MSG(4, "Pause");
575
576 /* Find settings for this particular client */
577 settings = get_client_settings_by_uid(uid);
578 if (settings == NULL) {
579 MSG(4,
580 "ERROR: Can't get settings of active client in speaking_pause()!");
581 return 1;
582 }
583 settings->paused = 1;
584
585 if (speaking_uid != uid) {
586 MSG(5, "given uid %d not speaking_uid %d", uid, speaking_uid);
587 return 0;
588 }
589
590 if (SPEAKING) {
591 if (current_message == NULL) {
592 MSG(5, "current_message is null");
593 return 0;
594 }
595
596 ret = output_pause();
597 if (ret < 0) {
598 MSG(5, "output_pause returned %d", ret);
599 return 0;
600 }
601
602 MSG(5,
603 "Including current message into the message paused list");
604 current_message->settings.paused = 2;
605 current_message->settings.paused_while_speaking = 1;
606 if (g_list_find(MessagePausedList, current_message) == NULL)
607 MessagePausedList =
608 g_list_append(MessagePausedList, current_message);
609 }
610
611 return 0;
612 }
613
speaking_resume_all()614 int speaking_resume_all()
615 {
616 int err = 0;
617 int i;
618 int uid;
619
620 for (i = 1; i <= SpeechdStatus.max_fd; i++) {
621 uid = get_client_uid_by_fd(i);
622 if (uid == 0)
623 continue;
624 err += speaking_resume(uid);
625 }
626
627 if (err > 0)
628 return 1;
629 else
630 return 0;
631 }
632
speaking_resume(int uid)633 int speaking_resume(int uid)
634 {
635 TFDSetElement *settings;
636
637 /* Find settings for this particular client */
638 settings = get_client_settings_by_uid(uid);
639 if (settings == NULL)
640 return 1;
641 /* Set it to speak again. */
642 settings->paused = 0;
643
644 resume_requested = 1;
645 speaking_semaphore_post();
646
647 return 0;
648 }
649
socket_send_msg(int fd,char * msg)650 int socket_send_msg(int fd, char *msg)
651 {
652 int ret;
653
654 assert(msg != NULL);
655 pthread_mutex_lock(&socket_com_mutex);
656 MSG2(5, "protocol", "%d:REPLY:|%s|", fd, msg);
657 ret = write(fd, msg, strlen(msg));
658 pthread_mutex_unlock(&socket_com_mutex);
659 if (ret < 0) {
660 MSG(1, "write() error: %s", strerror(errno));
661 return -1;
662 }
663 return 0;
664 }
665
report_index_mark(TSpeechDMessage * msg,char * index_mark)666 int report_index_mark(TSpeechDMessage * msg, char *index_mark)
667 {
668 char *cmd;
669 int ret;
670
671 cmd = g_strdup_printf(EVENT_INDEX_MARK_C "-%d\r\n"
672 EVENT_INDEX_MARK_C "-%d\r\n"
673 EVENT_INDEX_MARK_C "-%s\r\n"
674 EVENT_INDEX_MARK,
675 msg->id, msg->settings.uid, index_mark);
676 ret = socket_send_msg(msg->settings.fd, cmd);
677 if (ret) {
678 MSG(1, "ERROR: Can't report index mark!");
679 return -1;
680 }
681 g_free(cmd);
682 return 0;
683 }
684
685 #define REPORT_STATE(state, ssip_code, ssip_msg) \
686 int \
687 report_ ## state (TSpeechDMessage *msg) \
688 { \
689 char *cmd; \
690 int ret; \
691 cmd = g_strdup_printf(ssip_code"-%d\r\n"ssip_code"-%d\r\n"ssip_msg, \
692 msg->id, msg->settings.uid); \
693 ret = socket_send_msg(msg->settings.fd, cmd); \
694 if (ret){ \
695 MSG(2, "ERROR: Can't report index mark!"); \
696 return -1; \
697 } \
698 g_free(cmd); \
699 return 0; \
700 }
701
REPORT_STATE(begin,EVENT_BEGIN_C,EVENT_BEGIN)702 REPORT_STATE(begin, EVENT_BEGIN_C, EVENT_BEGIN)
703 REPORT_STATE(end, EVENT_END_C, EVENT_END)
704 REPORT_STATE(pause, EVENT_PAUSED_C, EVENT_PAUSED)
705 REPORT_STATE(resume, EVENT_RESUMED_C, EVENT_RESUMED)
706 REPORT_STATE(cancel, EVENT_CANCELED_C, EVENT_CANCELED)
707
708 int is_sb_speaking(void)
709 {
710 char *index_mark;
711 TFDSetElement *settings;
712
713 MSG(5, "is_sb_speaking(), SPEAKING=%d", SPEAKING);
714
715 /* Determine if the current module is still speaking */
716 if (speaking_module != NULL) {
717 if (current_message == NULL) {
718 MSG(1,
719 "Error: Current message is NULL in is_sb_speaking()");
720 return -1;
721 }
722 settings = &(current_message->settings);
723
724 output_is_speaking(&index_mark);
725 if (index_mark == NULL)
726 return SPEAKING = 0;
727
728 if (!strcmp(index_mark, "no")) {
729 g_free(index_mark);
730 return SPEAKING;
731 }
732
733 MSG(5, "INDEX MARK: %s", index_mark);
734
735 if (!strcmp(index_mark, SD_MARK_BODY "begin")) {
736 SPEAKING = 1;
737 if (!settings->paused_while_speaking) {
738 if (settings->notification & SPD_BEGIN)
739 report_begin(current_message);
740 } else {
741 if (settings->notification & SPD_RESUME)
742 report_resume(current_message);
743 settings->paused_while_speaking = 0;
744 }
745 } else if (!strcmp(index_mark, SD_MARK_BODY "end")) {
746 SPEAKING = 0;
747 poll_count = 1;
748 if (settings->notification & SPD_END)
749 report_end(current_message);
750 speaking_semaphore_post();
751 } else if (!strcmp(index_mark, SD_MARK_BODY "paused")) {
752 SPEAKING = 0;
753 poll_count = 1;
754 if (settings->notification & SPD_PAUSE)
755 report_pause(current_message);
756 /* We don't want to free this message in speak() since we will
757 later copy it in resume() */
758 current_message = NULL;
759 } else if (!strcmp(index_mark, SD_MARK_BODY "stopped")) {
760 SPEAKING = 0;
761 poll_count = 1;
762 if (settings->notification & SPD_CANCEL)
763 report_cancel(current_message);
764 speaking_semaphore_post();
765 } else if (index_mark != NULL) {
766 if (strncmp(index_mark, SD_MARK_BODY, SD_MARK_BODY_LEN)) {
767 if (settings->notification & SPD_INDEX_MARKS)
768 report_index_mark(current_message,
769 index_mark);
770 } else {
771 MSG(5,
772 "Setting current index_mark for the message to %s",
773 index_mark);
774 if (current_message->settings.index_mark !=
775 NULL)
776 g_free(current_message->
777 settings.index_mark);
778 current_message->settings.index_mark =
779 g_strdup(index_mark);
780 }
781
782 }
783 g_free(index_mark);
784 } else {
785 MSG(5, "Speaking module is NULL, SPEAKING==%d", SPEAKING);
786 SPEAKING = 0;
787 }
788
789 if (SPEAKING == 0)
790 speaking_module = NULL;
791
792 return SPEAKING;
793 }
794
get_speaking_client_uid(void)795 int get_speaking_client_uid(void)
796 {
797 int speaking = 0;
798 if (SPEAKING == 0) {
799 speaking_uid = 0;
800 return 0;
801 }
802 if (speaking_uid != 0) {
803 speaking = speaking_uid;
804 }
805 return speaking;
806 }
807
queue_remove_message(GList * queue,GList * gl)808 GList *queue_remove_message(GList * queue, GList * gl)
809 {
810 TSpeechDMessage *msg;
811 assert(gl != NULL);
812 assert(gl->data != NULL);
813 msg = (TSpeechDMessage *) gl->data;
814 if (msg->settings.notification & SPD_CANCEL)
815 report_cancel(msg);
816 mem_free_message(gl->data);
817 queue = g_list_delete_link(queue, gl);
818 return queue;
819 }
820
empty_queue(GList * queue)821 GList *empty_queue(GList * queue)
822 {
823 int num, i;
824 GList *gl;
825
826 num = g_list_length(queue);
827 for (i = 0; i <= num - 1; i++) {
828 gl = g_list_first(queue);
829 queue = queue_remove_message(queue, gl);
830 }
831
832 return queue;
833 }
834
empty_queue_by_time(GList * queue,unsigned int uid)835 GList *empty_queue_by_time(GList * queue, unsigned int uid)
836 {
837 int num, i;
838 GList *gl, *gln;
839 TSpeechDMessage *msg;
840
841 num = g_list_length(queue);
842 gl = g_list_first(queue);
843 for (i = 0; i <= num - 1; i++) {
844 gln = g_list_next(gl);
845 if (gl == NULL)
846 break;
847 assert(gl->data != NULL);
848 msg = gl->data;
849 if (msg->id < uid) {
850 queue = queue_remove_message(queue, gl);
851 }
852 gl = gln;
853 }
854
855 return queue;
856 }
857
stop_priority(SPDPriority priority)858 int stop_priority(SPDPriority priority)
859 {
860 GList *queue;
861
862 queue = speaking_get_queue(priority);
863
864 if (highest_priority == priority) {
865 output_stop();
866 }
867
868 queue = empty_queue(queue);
869
870 speaking_set_queue(priority, queue);
871
872 return 0;
873 }
874
stop_priority_older_than(SPDPriority priority,unsigned int uid)875 int stop_priority_older_than(SPDPriority priority, unsigned int uid)
876 {
877 GList *queue;
878
879 queue = speaking_get_queue(priority);
880
881 if (highest_priority == priority) {
882 output_stop();
883 }
884
885 queue = empty_queue_by_time(queue, uid);
886
887 speaking_set_queue(priority, queue);
888
889 return 0;
890 }
891
stop_priority_from_uid(GList * queue,const int uid)892 GList *stop_priority_from_uid(GList * queue, const int uid)
893 {
894 GList *ret = queue;
895 GList *gl;
896
897 while ((gl = g_list_find_custom(ret, &uid, p_msg_uid_lc)))
898 ret = queue_remove_message(ret, gl);
899
900 return ret;
901 }
902
stop_from_uid(const int uid)903 void stop_from_uid(const int uid)
904 {
905 check_locked(&element_free_mutex);
906 MessageQueue->p1 = stop_priority_from_uid(MessageQueue->p1, uid);
907 MessageQueue->p2 = stop_priority_from_uid(MessageQueue->p2, uid);
908 MessageQueue->p3 = stop_priority_from_uid(MessageQueue->p3, uid);
909 MessageQueue->p4 = stop_priority_from_uid(MessageQueue->p4, uid);
910 MessageQueue->p5 = stop_priority_from_uid(MessageQueue->p5, uid);
911 }
912
913 /* Determines if this messages is to be spoken
914 * (returns 1) or its parent client is paused (returns 0).
915 * Note: If you are wondering why it's reversed (not to speak instead
916 * of to speak), it's because we also use this function for
917 * searching through the list. */
message_nto_speak(gconstpointer data,gconstpointer nothing)918 gint message_nto_speak(gconstpointer data, gconstpointer nothing)
919 {
920 TFDSetElement *global_settings;
921 TSpeechDMessage *message = (TSpeechDMessage *) data;
922
923 /* Is there something in the body of the message? */
924 if (message == NULL)
925 return 0;
926
927 /* Find global settings for this connection. */
928 global_settings = get_client_settings_by_fd(message->settings.fd);
929 if (global_settings == NULL)
930 return 0;
931
932 if (!global_settings->paused)
933 return 0;
934 else
935 return 1;
936 }
937
set_speak_thread_attributes()938 void set_speak_thread_attributes()
939 {
940 int ret;
941 sigset_t all_signals;
942
943 ret = sigfillset(&all_signals);
944 if (ret == 0) {
945 ret = pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
946 if (ret != 0)
947 MSG(1,
948 "Can't set signal set, expect problems when terminating!");
949 } else {
950 MSG(1,
951 "Can't fill signal set, expect problems when terminating!");
952 }
953
954 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
955 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
956 }
957
stop_priority_except_first(SPDPriority priority)958 void stop_priority_except_first(SPDPriority priority)
959 {
960 GList *queue;
961 GList *gl;
962 TSpeechDMessage *msg;
963 GList *gl_next;
964 int gid;
965
966 queue = speaking_get_queue(priority);
967
968 gl = g_list_last(queue);
969
970 if (gl == NULL)
971 return;
972 if (gl->data == NULL)
973 return;
974
975 msg = (TSpeechDMessage *) gl->data;
976 if (msg->settings.reparted <= 0) {
977 queue = g_list_remove_link(queue, gl);
978 speaking_set_queue(priority, queue);
979
980 stop_priority(priority);
981 /* Fill the queue with the list containing only the first message */
982 speaking_set_queue(priority, gl);
983 } else {
984 gid = msg->settings.reparted;
985
986 if (highest_priority == priority && speaking_gid != gid) {
987 output_stop();
988 }
989
990 gl = g_list_first(queue);
991 while (gl) {
992 gl_next = g_list_next(gl);
993 if (gl->data != NULL) {
994 TSpeechDMessage *msgg = gl->data;
995 if (msgg->settings.reparted != gid) {
996 queue = g_list_remove_link(queue, gl);
997 mem_free_message(msgg);
998 }
999 }
1000 gl = gl_next;
1001 }
1002 speaking_set_queue(priority, queue);
1003 }
1004
1005 return;
1006 }
1007
resolve_priorities(SPDPriority priority)1008 void resolve_priorities(SPDPriority priority)
1009 {
1010 if (priority == SPD_IMPORTANT) {
1011 if (SPEAKING && highest_priority != SPD_IMPORTANT)
1012 output_stop();
1013 stop_priority(SPD_NOTIFICATION);
1014 stop_priority(SPD_PROGRESS);
1015 }
1016
1017 if (priority == SPD_MESSAGE) {
1018 if (SPEAKING && highest_priority != SPD_IMPORTANT
1019 && highest_priority != SPD_MESSAGE)
1020 output_stop();
1021 stop_priority(SPD_TEXT);
1022 stop_priority(SPD_NOTIFICATION);
1023 stop_priority(SPD_PROGRESS);
1024 }
1025
1026 if (priority == SPD_TEXT) {
1027 stop_priority_except_first(SPD_TEXT);
1028 stop_priority(SPD_NOTIFICATION);
1029 stop_priority(SPD_PROGRESS);
1030 }
1031
1032 if (priority == SPD_NOTIFICATION) {
1033 stop_priority_except_first(SPD_NOTIFICATION);
1034 if (SPEAKING && highest_priority != SPD_NOTIFICATION)
1035 stop_priority(SPD_NOTIFICATION);
1036 }
1037
1038 if (priority == SPD_PROGRESS) {
1039 stop_priority(SPD_NOTIFICATION);
1040 if (SPEAKING) {
1041 GList *gl;
1042 check_locked(&element_free_mutex);
1043 gl = g_list_last(MessageQueue->p5);
1044 check_locked(&element_free_mutex);
1045 MessageQueue->p5 =
1046 g_list_remove_link(MessageQueue->p5, gl);
1047 if (gl != NULL) {
1048 check_locked(&element_free_mutex);
1049 MessageQueue->p5 =
1050 empty_queue(MessageQueue->p5);
1051 if (gl->data != NULL) {
1052 MessageQueue->p5 = gl;
1053 }
1054 }
1055 }
1056 }
1057
1058 }
1059
get_message_from_queues()1060 TSpeechDMessage *get_message_from_queues()
1061 {
1062 GList *gl;
1063 SPDPriority prio;
1064 TSpeechDMessage *message;
1065
1066 /* We will descend through priorities to say more important
1067 messages first. */
1068 for (prio = SPD_IMPORTANT; prio <= SPD_PROGRESS; prio++) {
1069 GList *current_queue = speaking_get_queue(prio);
1070 check_locked(&element_free_mutex);
1071 gl = g_list_first(current_queue);
1072
1073 while (gl != NULL) {
1074 if (message_nto_speak
1075 ((TSpeechDMessage *) gl->data, NULL)) {
1076 gl = g_list_next(gl);
1077 continue;
1078 }
1079 speaking_set_queue(prio,
1080 g_list_remove_link(current_queue,
1081 gl));
1082 highest_priority = prio;
1083 message = gl->data;
1084 g_list_free(gl);
1085 return (TSpeechDMessage *) message;
1086 }
1087 }
1088
1089 return NULL;
1090 }
1091
message_has_uid(gconstpointer msg,gconstpointer uid)1092 gint message_has_uid(gconstpointer msg, gconstpointer uid)
1093 {
1094 if (((TSpeechDMessage *) msg)->settings.uid == *(int *)uid)
1095 return 0;
1096 else
1097 return 1;
1098 }
1099
1100 /* Return 1 if any message from this client is found
1101 in any of the queues, otherwise return 0 */
client_has_messages(int uid)1102 int client_has_messages(int uid)
1103 {
1104
1105 if (g_list_find_custom
1106 (MessageQueue->p5, (gconstpointer) & uid, message_has_uid)
1107 || g_list_find_custom(MessageQueue->p4, (gconstpointer) & uid,
1108 message_has_uid)
1109 || g_list_find_custom(MessageQueue->p3, (gconstpointer) & uid,
1110 message_has_uid)
1111 || g_list_find_custom(MessageQueue->p2, (gconstpointer) & uid,
1112 message_has_uid)
1113 || g_list_find_custom(MessageQueue->p1, (gconstpointer) & uid,
1114 message_has_uid))
1115 return 1;
1116 else
1117 return 0;
1118 }
1119
speaking_get_queue(SPDPriority priority)1120 GList *speaking_get_queue(SPDPriority priority)
1121 {
1122 GList *queue = NULL;
1123
1124 assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
1125
1126 check_locked(&element_free_mutex);
1127 switch (priority) {
1128 case SPD_IMPORTANT:
1129 queue = MessageQueue->p1;
1130 break;
1131 case SPD_MESSAGE:
1132 queue = MessageQueue->p2;
1133 break;
1134 case SPD_TEXT:
1135 queue = MessageQueue->p3;
1136 break;
1137 case SPD_NOTIFICATION:
1138 queue = MessageQueue->p4;
1139 break;
1140 case SPD_PROGRESS:
1141 queue = MessageQueue->p5;
1142 break;
1143 }
1144
1145 return queue;
1146 }
1147
speaking_set_queue(SPDPriority priority,GList * queue)1148 void speaking_set_queue(SPDPriority priority, GList * queue)
1149 {
1150 assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
1151
1152 check_locked(&element_free_mutex);
1153 switch (priority) {
1154 case SPD_IMPORTANT:
1155 MessageQueue->p1 = queue;
1156 break;
1157 case SPD_MESSAGE:
1158 MessageQueue->p2 = queue;
1159 break;
1160 case SPD_TEXT:
1161 MessageQueue->p3 = queue;
1162 break;
1163 case SPD_NOTIFICATION:
1164 MessageQueue->p4 = queue;
1165 break;
1166 case SPD_PROGRESS:
1167 MessageQueue->p5 = queue;
1168 break;
1169 }
1170 }
1171
sortbyuid(gconstpointer a,gconstpointer b)1172 gint sortbyuid(gconstpointer a, gconstpointer b)
1173 {
1174 const TSpeechDMessage *msg1 = a;
1175 const TSpeechDMessage *msg2 = b;
1176
1177 if ((msg1 == NULL) && (msg2 != NULL))
1178 return -1;
1179 if ((msg1 != NULL) && (msg2 == NULL))
1180 return +1;
1181 if ((msg1 == NULL) && (msg2 == NULL))
1182 return 0;
1183
1184 return msg1->id - msg2->id;
1185 }
1186