2 linphone, gtk-glade interface.
3 Copyright (C) 2008 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (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 General Public License for more details.
15 You should have received a copy of the GNU 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 #define VIDEOSELFVIEW_DEFAULT 1
27 #include <sys/types.h>
32 #include <gtkosxapplication.h>
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53 GtkWidget *the_wizard=NULL;
55 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
66 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
67 static void linphone_gtk_status_icon_set_blinking(gboolean val);
70 static gboolean verbose=0;
71 static gboolean auto_answer = 0;
72 static gchar * addr_to_call = NULL;
73 static gboolean iconified=FALSE;
74 static gchar *workingdir=NULL;
75 static char *progpath=NULL;
76 gchar *linphone_logfile=NULL;
78 static GOptionEntry linphone_options[]={
82 .arg=G_OPTION_ARG_NONE,
83 .arg_data= (gpointer)&verbose,
84 .description=N_("log to stdout some debug information while running.")
87 .long_name = "logfile",
89 .arg = G_OPTION_ARG_STRING,
90 .arg_data = &linphone_logfile,
91 .description = N_("path to a file to write logs into.")
94 .long_name="iconified",
96 .arg=G_OPTION_ARG_NONE,
97 .arg_data= (gpointer)&iconified,
98 .description=N_("Start only in the system tray, do not show the main interface.")
103 .arg = G_OPTION_ARG_STRING,
104 .arg_data = &addr_to_call,
105 .description = N_("address to call right now")
108 .long_name = "auto-answer",
110 .arg = G_OPTION_ARG_NONE,
111 .arg_data = (gpointer) & auto_answer,
112 .description = N_("if set automatically answer incoming calls")
115 .long_name = "workdir",
117 .arg = G_OPTION_ARG_STRING,
118 .arg_data = (gpointer) & workingdir,
119 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
124 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
125 #define RELATIVE_XML_DIR
126 #define BUILD_TREE_XML_DIR "gtk"
129 #define CONFIG_FILE ".linphonerc"
130 #define SECRETS_FILE ".linphone-zidcache"
132 #define CONFIG_FILE "linphonerc"
133 #define SECRETS_FILE "linphone-zidcache"
137 char *linphone_gtk_get_config_file(const char *filename){
138 const int path_max=1024;
139 char *config_file=g_malloc0(path_max);
140 if (filename==NULL) filename=CONFIG_FILE;
141 /*try accessing a local file first if exists*/
142 if (access(CONFIG_FILE,F_OK)==0){
143 snprintf(config_file,path_max,"%s",filename);
146 const char *appdata=getenv("APPDATA");
148 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
149 CreateDirectory(config_file,NULL);
150 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
153 const char *home=getenv("HOME");
154 if (home==NULL) home=".";
155 snprintf(config_file,path_max,"%s/%s",home,filename);
162 #define FACTORY_CONFIG_FILE "linphonerc.factory"
163 static char _factory_config_file[1024];
164 static const char *linphone_gtk_get_factory_config_file(){
165 /*try accessing a local file first if exists*/
166 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
167 snprintf(_factory_config_file,sizeof(_factory_config_file),
168 "%s",FACTORY_CONFIG_FILE);
172 if (progpath != NULL) {
174 progdir = strdup(progpath);
176 basename = strrchr(progdir, '\\');
177 if (basename != NULL) {
180 snprintf(_factory_config_file, sizeof(_factory_config_file),
181 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
183 if (workingdir!=NULL) {
184 snprintf(_factory_config_file, sizeof(_factory_config_file),
185 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
192 basename = strrchr(progdir, '/');
193 if (basename != NULL) {
196 snprintf(_factory_config_file, sizeof(_factory_config_file),
197 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
206 return _factory_config_file;
209 static void linphone_gtk_init_liblinphone(const char *config_file,
210 const char *factory_config_file) {
211 LinphoneCoreVTable vtable={0};
212 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
214 vtable.call_state_changed=linphone_gtk_call_state_changed;
215 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
216 vtable.notify_presence_recv=linphone_gtk_notify_recv;
217 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
218 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
219 vtable.display_status=linphone_gtk_display_status;
220 vtable.display_message=linphone_gtk_display_message;
221 vtable.display_warning=linphone_gtk_display_warning;
222 vtable.display_url=linphone_gtk_display_url;
223 vtable.call_log_updated=linphone_gtk_call_log_updated;
224 vtable.text_received=linphone_gtk_text_received;
225 vtable.refer_received=linphone_gtk_refer_received;
226 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
227 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
229 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
230 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
231 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
232 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
233 g_free(secrets_file);
234 linphone_core_enable_video(the_core,TRUE,TRUE);
239 LinphoneCore *linphone_gtk_get_core(void){
243 GtkWidget *linphone_gtk_get_main_window(){
247 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
248 static const char *icon_path=NULL;
249 static const char *hiddens=NULL;
250 static const char *shown=NULL;
251 static bool_t config_loaded=FALSE;
252 if (linphone_gtk_get_core()==NULL) return;
253 if (config_loaded==FALSE){
254 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
255 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
256 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
260 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
262 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
264 GdkPixbuf *pbuf=create_pixbuf(icon_path);
265 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
266 g_object_unref(G_OBJECT(pbuf));
270 static int get_ui_file(const char *name, char *path, int pathsize){
271 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
272 if (access(path,F_OK)!=0){
273 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
274 if (access(path,F_OK)!=0){
275 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
276 INSTALLED_XML_DIR,name);
283 GtkWidget *linphone_gtk_create_window(const char *window_name){
284 GError* error = NULL;
285 GtkBuilder* builder = gtk_builder_new ();
289 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
291 if (!gtk_builder_add_from_file (builder, path, &error)){
292 g_error("Couldn't load builder file: %s", error->message);
293 g_error_free (error);
296 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
298 g_error("Could not retrieve '%s' window from xml file",window_name);
301 g_object_set_data(G_OBJECT(w),"builder",builder);
302 gtk_builder_connect_signals(builder,w);
303 linphone_gtk_configure_window(w,window_name);
307 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
310 GtkBuilder* builder = gtk_builder_new ();
312 gchar *object_ids[2];
313 object_ids[0]=g_strdup(widget_name);
316 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
317 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
318 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
319 g_error_free (error);
320 g_free(object_ids[0]);
323 g_free(object_ids[0]);
324 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
326 g_error("Could not retrieve '%s' window from xml file",widget_name);
329 g_object_set_data(G_OBJECT(w),"builder",builder);
330 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
331 gtk_builder_connect_signals(builder,w);
335 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
336 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
339 g_error("Fail to retrieve builder from window !");
342 w=gtk_builder_get_object(builder,name);
344 g_error("No widget named %s found in xml interface.",name);
346 return GTK_WIDGET(w);
350 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
352 GtkWidget *main_window=linphone_gtk_get_main_window();
354 gtk_widget_show(main_window);
355 if (type==GTK_MESSAGE_QUESTION)
357 /* draw a question box. link to dialog_click callback */
358 dialog = gtk_message_dialog_new (
359 GTK_WINDOW(main_window),
360 GTK_DIALOG_DESTROY_WITH_PARENT,
361 GTK_MESSAGE_QUESTION,
364 (const gchar*)message);
365 /* connect to some callback : REVISIT */
367 g_signal_connect_swapped (G_OBJECT (dialog), "response",
368 G_CALLBACK (dialog_click),
371 /* actually show the box */
372 gtk_widget_show(dialog);
376 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
377 GTK_DIALOG_DESTROY_WITH_PARENT,
381 (const gchar*)message);
382 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
383 g_signal_connect_swapped (G_OBJECT (dialog), "response",
384 G_CALLBACK (gtk_widget_destroy),
386 gtk_widget_show(dialog);
390 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
391 if (id==GTK_RESPONSE_CANCEL){
392 gtk_widget_destroy(GTK_WIDGET(dialog));
396 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
397 g_message("About url clicked");
398 linphone_gtk_open_browser(url);
401 void linphone_gtk_show_about(){
402 struct stat filestat;
403 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
406 GdkPixbuf *logo=create_pixbuf(
407 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
408 static const char *defcfg="defcfg";
410 about=linphone_gtk_create_window("about");
411 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
412 memset(&filestat,0,sizeof(filestat));
413 if (stat(license_file,&filestat)!=0){
414 license_file="COPYING";
415 stat(license_file,&filestat);
417 if (filestat.st_size>0){
418 char *license=g_malloc(filestat.st_size+1);
419 FILE *f=fopen(license_file,"r");
420 if (f && fread(license,filestat.st_size,1,f)==1){
421 license[filestat.st_size]='\0';
422 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
426 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
427 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
428 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
429 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
430 tmp=linphone_gtk_get_ui_config("artists",defcfg);
435 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
437 tmp=linphone_gtk_get_ui_config("translators",defcfg);
439 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
440 tmp=linphone_gtk_get_ui_config("comments",defcfg);
442 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
443 gtk_widget_show(about);
446 static void set_video_window_decorations(GdkWindow *w){
447 const char *title=linphone_gtk_get_ui_config("title","Linphone");
448 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
449 char video_title[256];
450 GdkPixbuf *pbuf=create_pixbuf(icon_path);
451 if (!linphone_core_in_call(linphone_gtk_get_core())){
452 snprintf(video_title,sizeof(video_title),"%s video",title);
453 /* When not in call, treat the video as a normal window */
454 gdk_window_set_keep_above(w, FALSE);
456 LinphoneAddress *uri =
457 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
460 linphone_address_clean(uri);
461 if (linphone_address_get_display_name(uri)!=NULL){
462 display_name=ms_strdup(linphone_address_get_display_name(uri));
464 display_name=linphone_address_as_string(uri);
466 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
467 linphone_address_destroy(uri);
468 ms_free(display_name);
470 /* During calls, bring up the video window, arrange so that
471 it is above all the other windows */
472 gdk_window_deiconify(w);
473 gdk_window_set_keep_above(w,TRUE);
474 /* Maybe we should have the following, but then we want to
475 have a timer that turns it off after a little while. */
476 /* gdk_window_set_urgency_hint(w,TRUE); */
478 gdk_window_set_title(w,video_title);
479 /* Refrain the video window to be closed at all times. */
480 gdk_window_set_functions(w,
481 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
482 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
485 l=g_list_append(l,pbuf);
486 gdk_window_set_icon_list(w,l);
488 g_object_unref(G_OBJECT(pbuf));
492 static gboolean video_needs_update=FALSE;
494 static void update_video_title(){
495 video_needs_update=TRUE;
498 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
499 static gboolean first_time=TRUE;
501 static unsigned long previd=0;
502 static unsigned long preview_previd=0;
503 static gboolean in_iterate=FALSE;
506 if (in_iterate) return TRUE;
508 linphone_core_iterate(lc);
510 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
511 linphone_gtk_show_directory_search();
515 id=linphone_core_get_native_video_window_id(lc);
516 if (id!=previd || video_needs_update){
520 ms_message("Updating window decorations");
522 w=gdk_window_foreign_new(id);
524 w=gdk_window_foreign_new((HANDLE)id);
527 set_video_window_decorations(w);
528 g_object_unref(G_OBJECT(w));
530 else ms_error("gdk_window_foreign_new() failed");
531 if (video_needs_update) video_needs_update=FALSE;
534 id=linphone_core_get_native_preview_window_id (lc);
535 if (id!=preview_previd ){
539 ms_message("Updating window decorations for preview");
541 w=gdk_window_foreign_new(id);
543 w=gdk_window_foreign_new((HANDLE)id);
546 set_video_window_decorations(w);
547 g_object_unref(G_OBJECT(w));
549 else ms_error("gdk_window_foreign_new() failed");
550 if (video_needs_update) video_needs_update=FALSE;
553 if (addr_to_call!=NULL){
554 /*make sure we are not showing the login screen*/
555 GtkWidget *mw=linphone_gtk_get_main_window();
556 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
557 if (!GTK_WIDGET_VISIBLE(login_frame)){
558 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
559 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
561 linphone_gtk_start_call(uri_bar);
568 static void load_uri_history(){
569 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
572 GtkEntryCompletion *gep=gtk_entry_completion_new();
573 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
576 snprintf(key,sizeof(key),"uri%i",i);
577 uri=linphone_gtk_get_ui_config(key,NULL);
580 gtk_list_store_append(model,&iter);
581 gtk_list_store_set(model,&iter,0,uri,-1);
582 if (i==0) gtk_entry_set_text(uribar,uri);
586 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
587 gtk_entry_completion_set_text_column(gep,0);
588 gtk_entry_set_completion(uribar,gep);
591 static void save_uri_history(){
592 LinphoneCore *lc=linphone_gtk_get_core();
593 LpConfig *cfg=linphone_core_get_config(lc);
594 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
599 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
601 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
603 gtk_tree_model_get(model,&iter,0,&uri,-1);
605 snprintf(key,sizeof(key),"uri%i",i);
606 lp_config_set_string(cfg,"GtkUi",key,uri);
611 }while(gtk_tree_model_iter_next(model,&iter));
615 static void completion_add_text(GtkEntry *entry, const char *text){
617 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
619 if (gtk_tree_model_get_iter_first(model,&iter)){
622 gtk_tree_model_get(model,&iter,0,&uri,-1);
624 if (strcmp(uri,text)==0) {
626 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
632 }while (gtk_tree_model_iter_next(model,&iter));
634 /* and prepend it on top of the list */
635 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
636 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
641 bool_t linphone_gtk_video_enabled(void){
642 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
643 return vpol->automatically_accept && vpol->automatically_initiate;
646 void linphone_gtk_show_main_window(){
647 GtkWidget *w=linphone_gtk_get_main_window();
648 LinphoneCore *lc=linphone_gtk_get_core();
649 if (linphone_gtk_video_enabled()){
650 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
651 VIDEOSELFVIEW_DEFAULT));
654 gtk_window_present(GTK_WINDOW(w));
657 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
658 GtkWidget *mw=linphone_gtk_get_main_window();
659 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
660 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
661 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
663 if (linphone_gtk_use_in_call_view() && call)
664 linphone_gtk_in_call_view_terminate(call,error);
665 update_video_title();
668 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
669 LinphoneCore *lc=linphone_gtk_get_core();
670 GtkWidget *mw=linphone_gtk_get_main_window();
671 const MSList *calls=linphone_core_get_calls(lc);
673 bool_t start_active=TRUE;
674 bool_t stop_active=FALSE;
675 bool_t add_call=FALSE;
676 int call_list_size=ms_list_size(calls);
686 button=linphone_gtk_get_widget(mw,"start_call");
687 gtk_widget_set_sensitive(button,start_active);
688 gtk_widget_set_visible(button,!add_call);
690 button=linphone_gtk_get_widget(mw,"add_call");
691 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
692 gtk_widget_set_sensitive(button,FALSE);
694 gtk_widget_set_sensitive(button,start_active);
696 gtk_widget_set_visible(button,add_call);
698 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
700 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
701 linphone_gtk_enable_conference_button(lc,call_list_size>1);
702 update_video_title();
703 if (call) linphone_gtk_update_video_button(call);
706 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
707 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
708 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
709 completion_add_text(GTK_ENTRY(uri_bar),entered);
711 linphone_gtk_call_terminated(NULL,NULL);
716 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
717 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
718 linphone_core_accept_call (linphone_gtk_get_core(),call);
719 linphone_call_unref(call);
725 void linphone_gtk_start_call(GtkWidget *w){
726 LinphoneCore *lc=linphone_gtk_get_core();
728 /*change into in-call mode, then do the work later as it might block a bit */
729 GtkWidget *mw=gtk_widget_get_toplevel(w);
730 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
732 call=linphone_gtk_get_currently_displayed_call(NULL);
733 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
734 linphone_core_accept_call(lc,call);
736 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
737 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
738 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
739 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
744 void linphone_gtk_uri_bar_activate(GtkWidget *w){
745 linphone_gtk_start_call(w);
749 void linphone_gtk_terminate_call(GtkWidget *button){
751 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
753 linphone_core_terminate_call(linphone_gtk_get_core(),call);
755 linphone_core_terminate_conference(linphone_gtk_get_core());
759 void linphone_gtk_decline_clicked(GtkWidget *button){
760 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
762 linphone_core_terminate_call(linphone_gtk_get_core(),call);
765 void linphone_gtk_answer_clicked(GtkWidget *button){
766 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
768 linphone_core_accept_call(linphone_gtk_get_core(),call);
769 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
773 void linphone_gtk_enable_video(GtkWidget *w){
774 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
775 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
776 LinphoneVideoPolicy policy={0};
777 policy.automatically_initiate=policy.automatically_accept=val;
778 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
779 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
781 gtk_widget_set_sensitive(selfview_item,val);
783 linphone_core_enable_video_preview(linphone_gtk_get_core(),
784 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
786 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
790 void linphone_gtk_enable_self_view(GtkWidget *w){
791 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
792 LinphoneCore *lc=linphone_gtk_get_core();
793 linphone_core_enable_video_preview(lc,val);
794 linphone_core_enable_self_view(lc,val);
795 linphone_gtk_set_ui_config_int("videoselfview",val);
798 void linphone_gtk_used_identity_changed(GtkWidget *w){
799 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
800 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
801 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
802 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
803 linphone_gtk_show_directory_search();
805 if (sel) g_free(sel);
808 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
809 linphone_gtk_show_friends();
812 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
814 case GTK_RESPONSE_YES:
815 linphone_gtk_show_contact(lf);
818 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
820 gtk_widget_destroy(dialog);
823 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
826 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
827 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
831 gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
832 dialog = gtk_message_dialog_new (
833 GTK_WINDOW(linphone_gtk_get_main_window()),
834 GTK_DIALOG_DESTROY_WITH_PARENT,
835 GTK_MESSAGE_QUESTION,
840 g_signal_connect(G_OBJECT (dialog), "response",
841 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
842 /* actually show the box */
843 gtk_widget_show(dialog);
846 typedef struct _AuthTimeout{
851 static void auth_timeout_clean(AuthTimeout *tout){
855 static gboolean auth_timeout_destroy(AuthTimeout *tout){
857 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
858 gtk_widget_destroy(tout->w);
864 static AuthTimeout * auth_timeout_new(GtkWidget *w){
865 AuthTimeout *tout=g_new(AuthTimeout,1);
867 /*so that the timeout no more references the widget when it is destroyed:*/
868 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
869 /*so that the widget is automatically destroyed after some time */
870 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
874 void linphone_gtk_password_cancel(GtkWidget *w){
875 LinphoneAuthInfo *info;
876 GtkWidget *window=gtk_widget_get_toplevel(w);
877 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
878 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
879 gtk_widget_destroy(window);
882 void linphone_gtk_password_ok(GtkWidget *w){
884 GtkWidget *window=gtk_widget_get_toplevel(w);
885 LinphoneAuthInfo *info;
886 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
887 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
888 entry=linphone_gtk_get_widget(window,"password_entry");
889 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
890 linphone_auth_info_set_userid(info,
891 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
892 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
893 gtk_widget_destroy(window);
896 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
897 GtkWidget *w=linphone_gtk_create_window("password");
898 GtkWidget *label=linphone_gtk_get_widget(w,"message");
899 LinphoneAuthInfo *info;
901 GtkWidget *mw=linphone_gtk_get_main_window();
903 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
904 /*don't prompt for authentication when login frame is visible*/
905 linphone_core_abort_authentication(lc,NULL);
909 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
911 gtk_label_set_markup(GTK_LABEL(label),msg);
913 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
914 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
915 g_object_set_data(G_OBJECT(w),"auth_info",info);
916 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
921 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
922 GtkWidget *w=linphone_gtk_get_main_window();
923 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
924 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
925 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
929 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
930 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
933 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
934 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
937 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
939 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
940 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
943 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
944 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
945 if (w) linphone_gtk_call_log_update(w);
946 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
950 static bool_t notify_actions_supported() {
951 bool_t accepts_actions = FALSE;
952 GList *capabilities = notify_get_server_caps();
954 if(capabilities != NULL) {
955 for(c = capabilities; c != NULL; c = c->next) {
956 if(strcmp((char*)c->data, "actions") == 0 ) {
957 accepts_actions = TRUE;
961 g_list_foreach(capabilities, (GFunc)g_free, NULL);
962 g_list_free(capabilities);
964 return accepts_actions;
967 static NotifyNotification* build_notification(const char *title, const char *body){
968 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
975 static void show_notification(NotifyNotification* n){
976 if (n && !notify_notification_show(n,NULL))
977 ms_error("Failed to send notification.");
980 static void make_notification(const char *title, const char *body){
981 show_notification(build_notification(title,body));
986 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
988 if (!notify_is_initted())
989 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
993 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
998 ms_error("Failed to send notification.");
1000 linphone_gtk_show_main_window();
1002 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1005 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1006 NotifyNotification *n;
1007 switch(linphone_call_get_state(call)){
1008 case LinphoneCallError:
1009 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1011 case LinphoneCallEnd:
1012 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1014 case LinphoneCallIncomingReceived:
1015 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1016 if (notify_actions_supported()) {
1017 notify_notification_add_action (n,"answer", _("Answer"),
1018 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1019 notify_notification_add_action (n,"decline",_("Decline"),
1020 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1022 show_notification(n);
1024 case LinphoneCallPausedByRemote:
1025 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1030 if (body) g_free(body);
1031 if (remote) g_free(remote);
1036 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1037 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1038 LinphoneCore *lc=linphone_call_get_core(call);
1039 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1040 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1041 linphone_core_accept_call_update(lc,call,params);
1042 linphone_call_params_destroy(params);
1044 linphone_call_unref(call);
1045 g_source_remove_by_user_data(dialog);
1046 gtk_widget_destroy(dialog);
1049 static void on_call_updated_timeout(GtkWidget *dialog){
1050 gtk_widget_destroy(dialog);
1053 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1054 LinphoneCore *lc=linphone_call_get_core(call);
1055 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1056 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1057 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1058 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1059 gboolean video_used=linphone_call_params_video_enabled(current_params);
1060 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1061 video_used,video_requested,pol->automatically_accept);
1062 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1063 linphone_core_defer_call_update(lc,call);
1065 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1067 const char *dname=linphone_address_get_display_name(addr);
1068 if (dname==NULL) dname=linphone_address_get_username(addr);
1069 if (dname==NULL) dname=linphone_address_get_domain(addr);
1070 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1071 GTK_DIALOG_DESTROY_WITH_PARENT,
1072 GTK_MESSAGE_WARNING,
1074 _("%s proposed to start video. Do you accept ?"),dname);
1075 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1076 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1077 gtk_widget_show(dialog);
1082 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1084 case LinphoneCallOutgoingInit:
1085 linphone_gtk_create_in_call_view (call);
1087 case LinphoneCallOutgoingProgress:
1088 linphone_gtk_in_call_view_set_calling (call);
1090 case LinphoneCallStreamsRunning:
1091 linphone_gtk_in_call_view_set_in_call(call);
1093 case LinphoneCallUpdatedByRemote:
1094 linphone_gtk_call_updated_by_remote(call);
1096 case LinphoneCallError:
1097 linphone_gtk_in_call_view_terminate (call,msg);
1099 case LinphoneCallEnd:
1100 linphone_gtk_in_call_view_terminate(call,NULL);
1101 linphone_gtk_status_icon_set_blinking(FALSE);
1103 case LinphoneCallIncomingReceived:
1104 linphone_gtk_create_in_call_view(call);
1105 linphone_gtk_in_call_view_set_incoming(call);
1106 linphone_gtk_status_icon_set_blinking(TRUE);
1108 linphone_call_ref(call);
1109 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1112 case LinphoneCallResuming:
1113 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1114 linphone_gtk_in_call_view_set_in_call (call);
1116 case LinphoneCallPausing:
1117 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1118 case LinphoneCallPausedByRemote:
1119 linphone_gtk_in_call_view_set_paused(call);
1121 case LinphoneCallConnected:
1122 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1123 linphone_gtk_status_icon_set_blinking(FALSE);
1128 linphone_gtk_notify(call, msg);
1129 linphone_gtk_update_call_buttons (call);
1132 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1133 linphone_gtk_in_call_view_show_encryption(call);
1136 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1137 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1138 GtkTreeModel *model=gtk_combo_box_get_model(box);
1140 gboolean found=FALSE;
1141 const char *stock_id=NULL;
1143 if (gtk_tree_model_get_iter_first(model,&iter)){
1146 gtk_tree_model_get(model,&iter,2,&p,-1);
1151 }while(gtk_tree_model_iter_next(model,&iter));
1154 g_warning("Could not find proxy config in combo box of identities.");
1158 case LinphoneRegistrationOk:
1159 stock_id=GTK_STOCK_YES;
1161 case LinphoneRegistrationProgress:
1162 stock_id=GTK_STOCK_REFRESH;
1164 case LinphoneRegistrationCleared:
1167 case LinphoneRegistrationFailed:
1168 stock_id=GTK_STOCK_DIALOG_WARNING;
1173 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1176 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1177 LinphoneRegistrationState rs, const char *msg){
1179 case LinphoneRegistrationOk:
1181 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1182 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1183 linphone_gtk_exit_login_frame();
1190 update_registration_status(cfg,rs);
1193 void linphone_gtk_open_browser(const char *url){
1194 /*in gtk 2.16, gtk_show_uri does not work...*/
1196 #if GTK_CHECK_VERSION(2,18,3)
1197 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1200 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1201 g_spawn_command_line_async(cl,NULL);
1204 ShellExecute(0,"open",url,NULL,NULL,1);
1208 void linphone_gtk_link_to_website(GtkWidget *item){
1209 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1210 linphone_gtk_open_browser(home);
1213 #ifndef HAVE_GTK_OSX
1215 static GtkStatusIcon *icon=NULL;
1217 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1218 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1219 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1222 static GtkWidget *create_icon_menu(){
1223 GtkWidget *menu=gtk_menu_new();
1224 GtkWidget *menu_item;
1227 const gchar *homesite;
1229 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1230 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1231 tmp=g_strdup(homesite);
1232 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1233 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1235 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1236 gtk_widget_show(image);
1237 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1238 //g_object_unref(G_OBJECT(image));
1239 gtk_widget_show(menu_item);
1240 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1241 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1243 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1244 gtk_widget_show(menu_item);
1245 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1246 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1247 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1248 gtk_widget_show(menu_item);
1249 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1250 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1251 gtk_widget_show(menu);
1255 static void handle_icon_click() {
1256 GtkWidget *mw=linphone_gtk_get_main_window();
1257 if (!gtk_window_is_active((GtkWindow*)mw)) {
1258 linphone_gtk_show_main_window();
1260 gtk_widget_hide(mw);
1264 static void linphone_gtk_init_status_icon(){
1265 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1266 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1267 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1268 GtkWidget *menu=create_icon_menu();
1270 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1271 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1272 #if GTK_CHECK_VERSION(2,20,0)
1273 gtk_status_icon_set_name(icon,title);
1275 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1276 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1277 gtk_status_icon_set_tooltip(icon,title);
1278 gtk_status_icon_set_visible(icon,TRUE);
1279 g_object_set_data(G_OBJECT(icon),"menu",menu);
1280 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1281 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1282 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1283 pbuf=create_pixbuf(call_icon_path);
1284 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1287 static gboolean do_icon_blink(GtkStatusIcon *gi){
1288 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1289 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1290 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1291 if (cur_icon==call_icon){
1292 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1294 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1301 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1303 static gint attention_id;
1304 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1306 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1307 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1311 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1312 if (val && tout==0){
1313 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1314 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1315 }else if (!val && tout!=0){
1316 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1317 g_source_remove(tout);
1318 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1319 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1325 void linphone_gtk_options_activate(GtkWidget *item){
1326 #ifndef HAVE_GTK_OSX
1327 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1332 static void init_identity_combo(GtkComboBox *box){
1333 GtkListStore *store;
1334 GtkCellRenderer *r1,*r2;
1335 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1336 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1337 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1338 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1339 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1340 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1341 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1342 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1345 void linphone_gtk_load_identities(void){
1347 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1349 LinphoneProxyConfig *def=NULL;
1351 GtkListStore *store;
1354 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1355 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1356 /* model is empty, this is the first time we go here */
1357 init_identity_combo(box);
1358 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1360 gtk_list_store_clear(store);
1361 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1362 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1363 gtk_list_store_append(store,&iter);
1364 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1365 g_free(def_identity);
1366 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1368 elem=ms_list_next(elem),i++){
1369 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1370 gtk_list_store_append(store,&iter);
1371 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1372 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1378 gtk_combo_box_set_active(box,def_index);
1381 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1382 const char *label=gtk_button_get_label(button);
1383 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1385 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1386 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1387 if (linphone_core_in_call(linphone_gtk_get_core())){
1388 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1392 static void linphone_gtk_dtmf_released(GtkButton *button){
1393 linphone_core_stop_dtmf (linphone_gtk_get_core());
1396 static void linphone_gtk_connect_digits(void){
1397 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1398 GList *children=gtk_container_get_children(cont);
1400 for(elem=children;elem!=NULL;elem=elem->next){
1401 GtkButton *button=GTK_BUTTON(elem->data);
1402 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1403 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1407 static void linphone_gtk_check_menu_items(void){
1408 bool_t video_enabled=linphone_gtk_video_enabled();
1409 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1410 GtkWidget *selfview_item=linphone_gtk_get_widget(
1411 linphone_gtk_get_main_window(),"selfview_item");
1412 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1413 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1414 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1415 gtk_widget_set_sensitive(selfview_item,video_enabled);
1418 static gboolean linphone_gtk_can_manage_accounts(){
1419 LinphoneCore *lc=linphone_gtk_get_core();
1421 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1422 SipSetup *ss=(SipSetup*)elem->data;
1423 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1430 static void linphone_gtk_configure_main_window(){
1431 static gboolean config_loaded=FALSE;
1432 static const char *title;
1433 static const char *home;
1434 static const char *start_call_icon;
1435 static const char *add_call_icon;
1436 static const char *stop_call_icon;
1437 static const char *search_icon;
1438 static gboolean update_check_menu;
1439 static gboolean buttons_have_borders;
1440 static gboolean show_abcd;
1441 GtkWidget *w=linphone_gtk_get_main_window();
1442 if (!config_loaded){
1443 title=linphone_gtk_get_ui_config("title","Linphone");
1444 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1445 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1446 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1447 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1448 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1449 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1450 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1451 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1454 linphone_gtk_configure_window(w,"main_window");
1456 gtk_window_set_title(GTK_WINDOW(w),title);
1458 if (start_call_icon){
1459 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1460 create_pixmap (start_call_icon));
1461 if (!buttons_have_borders)
1462 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1465 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1466 create_pixmap (add_call_icon));
1467 if (!buttons_have_borders)
1468 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1470 if (stop_call_icon){
1471 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1472 create_pixmap (stop_call_icon));
1473 if (!buttons_have_borders)
1474 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1477 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1478 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1479 g_object_unref(G_OBJECT(pbuf));
1483 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1485 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1489 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1491 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1492 g_object_unref(G_OBJECT(pbuf));
1497 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1499 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1502 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1503 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1504 gtk_image_set_from_pixbuf(img,scaled);
1505 g_object_unref(G_OBJECT(scaled));
1506 g_object_unref(G_OBJECT(pbuf));
1509 if (linphone_gtk_can_manage_accounts()) {
1510 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1512 if (update_check_menu){
1513 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1516 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1517 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1518 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1519 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1520 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1524 void linphone_gtk_manage_login(void){
1525 LinphoneCore *lc=linphone_gtk_get_core();
1526 LinphoneProxyConfig *cfg=NULL;
1527 linphone_core_get_default_proxy(lc,&cfg);
1529 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1530 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1531 linphone_gtk_show_login_frame(cfg);
1537 gboolean linphone_gtk_close(GtkWidget *mw){
1538 /*shutdown calls if any*/
1539 LinphoneCore *lc=linphone_gtk_get_core();
1540 if (linphone_core_in_call(lc)){
1541 linphone_core_terminate_all_calls(lc);
1543 linphone_core_enable_video_preview(lc,FALSE);
1544 #ifdef __APPLE__ /*until with have a better option*/
1545 gtk_window_iconify(GTK_WINDOW(mw));
1547 gtk_widget_hide(mw);
1553 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1554 bool_t video_enabled=linphone_gtk_video_enabled();
1555 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1556 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1558 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1559 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1566 static void linphone_gtk_init_main_window(){
1567 GtkWidget *main_window;
1569 linphone_gtk_configure_main_window();
1570 linphone_gtk_manage_login();
1572 linphone_gtk_load_identities();
1573 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1574 linphone_gtk_show_friends();
1575 linphone_gtk_connect_digits();
1576 main_window=linphone_gtk_get_main_window();
1577 linphone_gtk_call_log_update(main_window);
1579 linphone_gtk_update_call_buttons (NULL);
1580 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1581 g_signal_connect (G_OBJECT (main_window), "delete-event",
1582 G_CALLBACK (linphone_gtk_close), main_window);
1585 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1586 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1587 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1588 gtk_widget_hide(menubar);
1589 gtk_osxapplication_ready(theMacApp);
1591 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1593 linphone_gtk_check_menu_items();
1597 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1599 const char *lname="undef";
1601 #if defined(__linux) || defined(__APPLE__)
1602 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1621 g_error("Bad level !");
1623 #if defined(__linux) || defined(__APPLE__)
1625 msg=g_strdup_vprintf(fmt,cap);
1628 msg=g_strdup_vprintf(fmt,args);
1630 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1633 linphone_gtk_log_push(lev,fmt,args);
1637 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1638 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1639 linphone_gtk_get_main_window(), "uribar"));
1641 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1643 gtk_entry_set_text(uri_bar, refer_to);
1644 linphone_gtk_start_call(linphone_gtk_get_main_window());
1647 static void linphone_gtk_check_soundcards(){
1648 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1649 if (devices==NULL || devices[0]==NULL){
1650 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1651 _("No sound cards have been detected on this computer.\n"
1652 "You won't be able to send or receive audio calls."));
1657 // Display the account wizard
1658 void linphone_gtk_display_wizard() {
1659 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1660 the_wizard = linphone_gtk_create_assistant();
1665 static void linphone_gtk_quit(void){
1666 static gboolean quit_done=FALSE;
1669 linphone_gtk_unmonitor_usb();
1670 g_source_remove_by_user_data(linphone_gtk_get_core());
1671 linphone_gtk_uninit_instance();
1672 linphone_gtk_destroy_log_window();
1673 linphone_core_destroy(the_core);
1674 linphone_gtk_log_uninit();
1678 gdk_threads_leave();
1684 This is not the correct way to implement block termination.
1685 The good way would be to call gtk_main_quit(), and return TRUE.
1686 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1687 As a result the program cannot exit at all.
1688 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1690 static gboolean on_block_termination(void){
1692 linphone_gtk_quit();
1697 int main(int argc, char *argv[]){
1702 const char *factory_config_file;
1704 GtkSettings *settings;
1706 const char *app_name="Linphone";
1708 #if !GLIB_CHECK_VERSION(2, 31, 0)
1709 g_thread_init(NULL);
1713 progpath = strdup(argv[0]);
1715 config_file=linphone_gtk_get_config_file(NULL);
1719 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1720 if ((lang=getenv("LANG"))!=NULL){
1723 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1729 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1732 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1735 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1738 setenv("LANG",lang,1);
1743 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1744 if (p==NULL) perror("bindtextdomain failed");
1745 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1746 textdomain (GETTEXT_PACKAGE);
1748 g_message("NLS disabled.\n");
1751 gtk_rc_add_default_file("./gtkrc");
1753 gdk_threads_enter();
1755 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1756 linphone_options,NULL,NULL)){
1757 gdk_threads_leave();
1761 settings=gtk_settings_get_default();
1762 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1763 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1764 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1765 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1767 if (workingdir!=NULL){
1768 if (chdir(workingdir)==-1){
1769 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1773 /* Now, look for the factory configuration file, we do it this late
1774 since we want to have had time to change directory and to parse
1775 the options, in case we needed to access the working directory */
1776 factory_config_file = linphone_gtk_get_factory_config_file();
1778 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1779 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1780 g_warning("This instance is going to exit now.");
1781 gdk_threads_leave();
1785 add_pixmap_directory("pixmaps");
1786 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1789 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1790 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1791 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1792 /*never block termination:*/
1793 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1796 the_ui=linphone_gtk_create_window("main");
1798 linphone_gtk_create_log_window();
1799 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1801 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1803 g_set_application_name(app_name);
1804 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1805 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1807 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1808 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1809 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1810 linphone_gtk_init_main_window();
1813 // Veryfing if at least one sip account is configured. If not, show wizard
1814 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1815 linphone_gtk_display_wizard();
1819 #ifndef HAVE_GTK_OSX
1820 linphone_gtk_init_status_icon();
1823 linphone_gtk_show_main_window();
1824 linphone_gtk_check_soundcards();
1826 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1827 linphone_gtk_check_for_new_version();
1828 linphone_gtk_monitor_usb();
1831 linphone_gtk_quit();
1832 #ifndef HAVE_GTK_OSX
1833 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1834 gtk_status_icon_set_visible(icon,FALSE);