aboutsummaryrefslogtreecommitdiff
path: root/src/ui/chat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/chat.c')
-rw-r--r--src/ui/chat.c461
1 files changed, 445 insertions, 16 deletions
diff --git a/src/ui/chat.c b/src/ui/chat.c
index 71ea810..e445e32 100644
--- a/src/ui/chat.c
+++ b/src/ui/chat.c
@@ -25,16 +25,19 @@
25#include "chat.h" 25#include "chat.h"
26 26
27#include <gdk/gdkkeysyms.h> 27#include <gdk/gdkkeysyms.h>
28#include <stdlib.h>
28 29
29#include "file_load_entry.h" 30#include "file_load_entry.h"
30#include "message.h" 31#include "message.h"
31#include "messenger.h" 32#include "messenger.h"
32#include "picker.h" 33#include "picker.h"
33#include "../application.h"
34#include "../contact.h"
35#include "account_entry.h" 34#include "account_entry.h"
36#include "delete_messages.h" 35#include "delete_messages.h"
37 36
37#include "../application.h"
38#include "../contact.h"
39#include "../file.h"
40
38static gboolean 41static gboolean
39_flap_reveal_switch(gpointer user_data) 42_flap_reveal_switch(gpointer user_data)
40{ 43{
@@ -389,6 +392,12 @@ _send_text_from_view(MESSENGER_Application *app,
389static void 392static void
390_drop_any_recording(UI_CHAT_Handle *handle) 393_drop_any_recording(UI_CHAT_Handle *handle)
391{ 394{
395 if ((handle->play_pipeline) && (handle->playing))
396 {
397 gst_element_set_state(handle->play_pipeline, GST_STATE_NULL);
398 handle->playing = FALSE;
399 }
400
392 _update_send_record_symbol( 401 _update_send_record_symbol(
393 gtk_text_view_get_buffer(handle->send_text_view), 402 gtk_text_view_get_buffer(handle->send_text_view),
394 handle->send_record_symbol, 403 handle->send_record_symbol,
@@ -397,10 +406,33 @@ _drop_any_recording(UI_CHAT_Handle *handle)
397 406
398 gtk_stack_set_visible_child(handle->send_stack, handle->send_text_box); 407 gtk_stack_set_visible_child(handle->send_stack, handle->send_text_box);
399 408
409 if (handle->recording_filename[0])
410 remove(handle->recording_filename);
411
412 handle->recording_filename[0] = 0;
400 handle->recorded = FALSE; 413 handle->recorded = FALSE;
401} 414}
402 415
403static void 416static void
417handle_sending_recording_upload_file(UNUSED void *cls,
418 const struct GNUNET_CHAT_File *file,
419 uint64_t completed,
420 uint64_t size)
421{
422 UI_FILE_LOAD_ENTRY_Handle *file_load = cls;
423
424 gtk_progress_bar_set_fraction(
425 file_load->load_progress_bar,
426 1.0 * completed / size
427 );
428
429 file_update_upload_info(file, completed, size);
430
431 if ((completed >= size) && (file_load->chat))
432 ui_chat_remove_file_load(file_load->chat, file_load);
433}
434
435static void
404handle_send_record_button_click(GtkButton *button, 436handle_send_record_button_click(GtkButton *button,
405 gpointer user_data) 437 gpointer user_data)
406{ 438{
@@ -410,17 +442,42 @@ handle_send_record_button_click(GtkButton *button,
410 g_object_get_qdata(G_OBJECT(button), app->quarks.ui) 442 g_object_get_qdata(G_OBJECT(button), app->quarks.ui)
411 ); 443 );
412 444
413 if ((handle->recorded) && 445 struct GNUNET_CHAT_Context *context = (struct GNUNET_CHAT_Context*) (
414 (!gtk_revealer_get_child_revealed(handle->picker_revealer)) && 446 g_object_get_qdata(G_OBJECT(handle->send_text_view), app->quarks.data)
415 (gtk_stack_get_visible_child(handle->send_stack) == 447 );
416 handle->send_recording_box)) 448
449 if ((handle->recorded) && (context) &&
450 (handle->recording_filename[0]) &&
451 (!gtk_revealer_get_child_revealed(handle->picker_revealer)))
417 { 452 {
418 // TODO: send audio as file! 453 UI_FILE_LOAD_ENTRY_Handle *file_load = ui_file_load_entry_new(app);
454
455 gtk_label_set_text(file_load->file_label, handle->recording_filename);
456 gtk_progress_bar_set_fraction(file_load->load_progress_bar, 0.0);
457
458 struct GNUNET_CHAT_File *file = GNUNET_CHAT_context_send_file(
459 context,
460 handle->recording_filename,
461 handle_sending_recording_upload_file,
462 file_load
463 );
464
465 if (file)
466 {
467 file_create_info(file);
468
469 ui_chat_add_file_load(handle, file_load);
470 }
471 else if (file_load)
472 ui_file_load_entry_delete(file_load);
419 473
420 _drop_any_recording(handle); 474 _drop_any_recording(handle);
421 return; 475 return;
422 } 476 }
423 477
478 if (gtk_stack_get_visible_child(handle->send_stack) != handle->send_text_box)
479 return;
480
424 GtkTextView *text_view = GTK_TEXT_VIEW( 481 GtkTextView *text_view = GTK_TEXT_VIEW(
425 g_object_get_qdata(G_OBJECT(button), app->quarks.widget) 482 g_object_get_qdata(G_OBJECT(button), app->quarks.widget)
426 ); 483 );
@@ -439,11 +496,27 @@ handle_send_record_button_pressed(GtkWidget *widget,
439 g_object_get_qdata(G_OBJECT(widget), app->quarks.ui) 496 g_object_get_qdata(G_OBJECT(widget), app->quarks.ui)
440 ); 497 );
441 498
442 if ((handle->recorded) || 499 if ((handle->recorded) || (!(handle->record_pipeline)) ||
500 (handle->recording_filename[0]) ||
443 (gtk_revealer_get_child_revealed(handle->picker_revealer)) || 501 (gtk_revealer_get_child_revealed(handle->picker_revealer)) ||
444 (handle->send_text_box != gtk_stack_get_visible_child(handle->send_stack))) 502 (handle->send_text_box != gtk_stack_get_visible_child(handle->send_stack)))
445 return FALSE; 503 return FALSE;
446 504
505 strcpy(handle->recording_filename, "/tmp/rec_XXXXXX.ogg");
506
507 int fd = mkstemps(handle->recording_filename, 4);
508
509 if (-1 == fd)
510 return FALSE;
511 else
512 close(fd);
513
514 if ((handle->play_pipeline) && (handle->playing))
515 {
516 gst_element_set_state(handle->play_pipeline, GST_STATE_NULL);
517 handle->playing = FALSE;
518 }
519
447 gtk_image_set_from_icon_name( 520 gtk_image_set_from_icon_name(
448 handle->play_pause_symbol, 521 handle->play_pause_symbol,
449 "media-playback-start-symbolic", 522 "media-playback-start-symbolic",
@@ -456,10 +529,21 @@ handle_send_record_button_pressed(GtkWidget *widget,
456 GTK_ICON_SIZE_BUTTON 529 GTK_ICON_SIZE_BUTTON
457 ); 530 );
458 531
459 gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), FALSE); 532 gtk_label_set_text(handle->recording_label, "00:00:00");
533 gtk_progress_bar_set_fraction(handle->recording_progress_bar, 0.0);
460 534
535 gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), FALSE);
461 gtk_stack_set_visible_child(handle->send_stack, handle->send_recording_box); 536 gtk_stack_set_visible_child(handle->send_stack, handle->send_recording_box);
462 537
538 g_object_set(
539 G_OBJECT(handle->record_sink),
540 "location",
541 handle->recording_filename,
542 NULL
543 );
544
545 gst_element_set_state(handle->record_pipeline, GST_STATE_PLAYING);
546
463 return TRUE; 547 return TRUE;
464} 548}
465 549
@@ -474,7 +558,8 @@ handle_send_record_button_released(GtkWidget *widget,
474 g_object_get_qdata(G_OBJECT(widget), app->quarks.ui) 558 g_object_get_qdata(G_OBJECT(widget), app->quarks.ui)
475 ); 559 );
476 560
477 if ((handle->recorded) || 561 if ((handle->recorded) || (!(handle->record_pipeline)) ||
562 (!(handle->recording_filename[0])) ||
478 (gtk_revealer_get_child_revealed(handle->picker_revealer)) || 563 (gtk_revealer_get_child_revealed(handle->picker_revealer)) ||
479 (handle->send_recording_box != gtk_stack_get_visible_child( 564 (handle->send_recording_box != gtk_stack_get_visible_child(
480 handle->send_stack))) 565 handle->send_stack)))
@@ -482,8 +567,7 @@ handle_send_record_button_released(GtkWidget *widget,
482 567
483 gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), TRUE); 568 gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), TRUE);
484 569
485 gtk_revealer_set_reveal_child(handle->picker_revealer, FALSE); 570 gst_element_set_state(handle->record_pipeline, GST_STATE_NULL);
486
487 handle->recorded = TRUE; 571 handle->recorded = TRUE;
488 572
489 gtk_image_set_from_icon_name( 573 gtk_image_set_from_icon_name(
@@ -521,6 +605,62 @@ handle_recording_close_button_click(UNUSED GtkButton *button,
521} 605}
522 606
523static void 607static void
608_stop_playing_recording(UI_CHAT_Handle *handle,
609 gboolean reset_bar)
610{
611 gst_element_set_state(handle->play_pipeline, GST_STATE_NULL);
612 handle->playing = FALSE;
613
614 gtk_image_set_from_icon_name(
615 handle->play_pause_symbol,
616 "media-playback-start-symbolic",
617 GTK_ICON_SIZE_BUTTON
618 );
619
620 gtk_progress_bar_set_fraction(
621 handle->recording_progress_bar,
622 reset_bar? 0.0 : 1.0
623 );
624
625 if (handle->play_timer)
626 {
627 g_source_remove(handle->play_timer);
628 handle->play_timer = 0;
629 }
630}
631
632static void
633handle_recording_play_button_click(UNUSED GtkButton *button,
634 gpointer user_data)
635{
636 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data;
637
638 if ((!(handle->recorded)) || (!(handle->play_pipeline)))
639 return;
640
641 if (handle->playing)
642 _stop_playing_recording(handle, TRUE);
643 else if (handle->recording_filename[0])
644 {
645 g_object_set(
646 G_OBJECT(handle->play_source),
647 "location",
648 handle->recording_filename,
649 NULL
650 );
651
652 gst_element_set_state(handle->play_pipeline, GST_STATE_PLAYING);
653 handle->playing = TRUE;
654
655 gtk_image_set_from_icon_name(
656 handle->play_pause_symbol,
657 "media-playback-stop-symbolic",
658 GTK_ICON_SIZE_BUTTON
659 );
660 }
661}
662
663static void
524handle_picker_button_click(UNUSED GtkButton *button, 664handle_picker_button_click(UNUSED GtkButton *button,
525 gpointer user_data) 665 gpointer user_data)
526{ 666{
@@ -537,6 +677,268 @@ handle_picker_button_click(UNUSED GtkButton *button,
537 ); 677 );
538} 678}
539 679
680static gboolean
681_record_timer_func(gpointer user_data)
682{
683 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data;
684
685 GString *time_string = g_string_new(NULL);
686
687 g_string_printf(
688 time_string,
689 "%02u:%02u:%02u",
690 (handle->record_time / 3600),
691 (handle->record_time / 60) % 60,
692 (handle->record_time % 60)
693 );
694
695 gtk_label_set_text(handle->recording_label, time_string->str);
696 g_string_free(time_string, TRUE);
697
698 if (!(handle->recorded))
699 {
700 handle->record_time++;
701 handle->record_timer = g_timeout_add_seconds(
702 1,
703 _record_timer_func,
704 handle
705 );
706 }
707 else
708 handle->record_timer = 0;
709
710 return FALSE;
711}
712
713static gboolean
714_play_timer_func(gpointer user_data)
715{
716 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data;
717
718 if (handle->play_time < handle->record_time * 100)
719 gtk_progress_bar_set_fraction(
720 handle->recording_progress_bar,
721 0.01 * handle->play_time / handle->record_time
722 );
723 else
724 gtk_progress_bar_set_fraction(
725 handle->recording_progress_bar,
726 1.0
727 );
728
729 if (handle->playing)
730 {
731 handle->play_time++;
732 handle->play_timer = g_timeout_add(
733 10,
734 _play_timer_func,
735 handle
736 );
737 }
738 else
739 handle->play_timer = 0;
740
741 return FALSE;
742}
743
744static gboolean
745handle_record_bus_watch(UNUSED GstBus *bus,
746 GstMessage *msg,
747 gpointer data)
748{
749 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) data;
750 GstMessageType type = GST_MESSAGE_TYPE(msg);
751
752 switch (type)
753 {
754 case GST_MESSAGE_STREAM_START:
755 handle->record_time = 0;
756 handle->record_timer = g_timeout_add_seconds(
757 0,
758 _record_timer_func,
759 handle
760 );
761
762 break;
763 default:
764 break;
765 }
766
767 return TRUE;
768}
769
770static gboolean
771handle_play_bus_watch(UNUSED GstBus *bus,
772 GstMessage *msg,
773 gpointer data)
774{
775 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) data;
776 GstMessageType type = GST_MESSAGE_TYPE(msg);
777
778 switch (type)
779 {
780 case GST_MESSAGE_STREAM_START:
781 handle->play_time = 0;
782 handle->play_timer = g_timeout_add_seconds(
783 0,
784 _play_timer_func,
785 handle
786 );
787
788 break;
789 case GST_MESSAGE_EOS:
790 if (handle->playing)
791 _stop_playing_recording(handle, FALSE);
792 break;
793 default:
794 break;
795 }
796
797 return TRUE;
798}
799
800static void
801_play_pad_added(UNUSED GstElement *element,
802 GstPad *pad,
803 gpointer data)
804{
805 GstElement *decoder = (GstElement*) data;
806
807 GstPad *sinkpad = gst_element_get_static_pad(decoder, "sink");
808 gst_pad_link(pad, sinkpad);
809 gst_object_unref (sinkpad);
810}
811
812static void
813_setup_gst_pipelines(UI_CHAT_Handle *handle)
814{
815 handle->record_pipeline = gst_pipeline_new("audio-recorder");
816
817 GstElement *audio_source = gst_element_factory_make(
818 "autoaudiosrc",
819 "audio-input"
820 );
821
822 GstElement *audio_converter = gst_element_factory_make(
823 "audioconvert",
824 "audio-converter"
825 );
826
827 GstElement *vorbis_encoder = gst_element_factory_make(
828 "vorbisenc",
829 "vorbis-encoder"
830 );
831
832 GstElement *ogg_muxer = gst_element_factory_make(
833 "oggmux",
834 "ogg-muxer"
835 );
836
837 handle->record_sink = gst_element_factory_make(
838 "filesink",
839 "file-output"
840 );
841
842 gst_bin_add_many(
843 GST_BIN(handle->record_pipeline),
844 audio_source,
845 audio_converter,
846 vorbis_encoder,
847 ogg_muxer,
848 handle->record_sink,
849 NULL
850 );
851
852 gst_element_link_many(
853 audio_source,
854 audio_converter,
855 vorbis_encoder,
856 ogg_muxer,
857 handle->record_sink,
858 NULL
859 );
860
861 {
862 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->record_pipeline));
863
864 handle->record_watch = gst_bus_add_watch(
865 bus,
866 handle_record_bus_watch,
867 handle
868 );
869
870 gst_object_unref(bus);
871 }
872
873 handle->play_pipeline = gst_pipeline_new("audio-previewer");
874
875 handle->play_source = gst_element_factory_make(
876 "filesrc",
877 "file-input"
878 );
879
880 GstElement *ogg_demuxer = gst_element_factory_make(
881 "oggdemux",
882 "ogg-demuxer"
883 );
884
885 GstElement *vorbis_decoder = gst_element_factory_make(
886 "vorbisdec",
887 "vorbis-decoder"
888 );
889
890 GstElement *audio_play_converter = gst_element_factory_make(
891 "audioconvert",
892 "audio-converter"
893 );
894
895 GstElement *audio_sink = gst_element_factory_make(
896 "autoaudiosink",
897 "audio-output"
898 );
899
900 gst_bin_add_many(
901 GST_BIN(handle->play_pipeline),
902 handle->play_source,
903 ogg_demuxer,
904 vorbis_decoder,
905 audio_play_converter,
906 audio_sink,
907 NULL
908 );
909
910 gst_element_link(
911 handle->play_source,
912 ogg_demuxer
913 );
914
915 gst_element_link_many(
916 vorbis_decoder,
917 audio_play_converter,
918 audio_sink,
919 NULL
920 );
921
922 g_signal_connect(
923 ogg_demuxer,
924 "pad-added",
925 G_CALLBACK(_play_pad_added),
926 vorbis_decoder
927 );
928
929 {
930 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->play_pipeline));
931
932 handle->play_watch = gst_bus_add_watch(
933 bus,
934 handle_play_bus_watch,
935 handle
936 );
937
938 gst_object_unref(bus);
939 }
940}
941
540UI_CHAT_Handle* 942UI_CHAT_Handle*
541ui_chat_new(MESSENGER_Application *app) 943ui_chat_new(MESSENGER_Application *app)
542{ 944{
@@ -545,12 +947,11 @@ ui_chat_new(MESSENGER_Application *app)
545 UI_CHAT_Handle *handle = g_malloc(sizeof(UI_CHAT_Handle)); 947 UI_CHAT_Handle *handle = g_malloc(sizeof(UI_CHAT_Handle));
546 UI_MESSENGER_Handle *messenger = &(app->ui.messenger); 948 UI_MESSENGER_Handle *messenger = &(app->ui.messenger);
547 949
548 handle->recorded = FALSE; 950 memset(handle, 0, sizeof(*handle));
549 951
550 handle->app = app; 952 _setup_gst_pipelines(handle);
551 953
552 handle->messages = NULL; 954 handle->app = app;
553 handle->edge_value = 0;
554 955
555 handle->loads = NULL; 956 handle->loads = NULL;
556 957
@@ -845,6 +1246,13 @@ ui_chat_new(MESSENGER_Application *app)
845 gtk_builder_get_object(handle->builder, "recording_play_button") 1246 gtk_builder_get_object(handle->builder, "recording_play_button")
846 ); 1247 );
847 1248
1249 g_signal_connect(
1250 handle->recording_play_button,
1251 "clicked",
1252 G_CALLBACK(handle_recording_play_button_click),
1253 handle
1254 );
1255
848 handle->play_pause_symbol = GTK_IMAGE( 1256 handle->play_pause_symbol = GTK_IMAGE(
849 gtk_builder_get_object(handle->builder, "play_pause_symbol") 1257 gtk_builder_get_object(handle->builder, "play_pause_symbol")
850 ); 1258 );
@@ -1033,6 +1441,27 @@ ui_chat_delete(UI_CHAT_Handle *handle)
1033 if (handle->loads) 1441 if (handle->loads)
1034 g_list_free_full(handle->loads, (GDestroyNotify) ui_file_load_entry_delete); 1442 g_list_free_full(handle->loads, (GDestroyNotify) ui_file_load_entry_delete);
1035 1443
1444 if (handle->record_pipeline)
1445 {
1446 gst_element_set_state (handle->record_pipeline, GST_STATE_NULL);
1447 gst_object_unref(GST_OBJECT(handle->record_pipeline));
1448 }
1449
1450 if (handle->play_pipeline)
1451 {
1452 gst_element_set_state (handle->play_pipeline, GST_STATE_NULL);
1453 gst_object_unref(GST_OBJECT(handle->play_pipeline));
1454 }
1455
1456 if (handle->recording_filename[0])
1457 remove(handle->recording_filename);
1458
1459 if (handle->record_timer)
1460 g_source_remove(handle->record_timer);
1461
1462 if (handle->play_timer)
1463 g_source_remove(handle->play_timer);
1464
1036 g_free(handle); 1465 g_free(handle);
1037} 1466}
1038 1467