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 0
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 void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
67 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
68 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
72 static gboolean verbose=0;
73 static gboolean auto_answer = 0;
74 static gchar * addr_to_call = NULL;
75 static gboolean no_video=FALSE;
76 static gboolean iconified=FALSE;
77 static gchar *workingdir=NULL;
78 static char *progpath=NULL;
79 gchar *linphone_logfile=NULL;
80 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
82 static GOptionEntry linphone_options[]={
86 .arg=G_OPTION_ARG_NONE,
87 .arg_data= (gpointer)&verbose,
88 .description=N_("log to stdout some debug information while running.")
91 .long_name = "logfile",
93 .arg = G_OPTION_ARG_STRING,
94 .arg_data = &linphone_logfile,
95 .description = N_("path to a file to write logs into.")
98 .long_name = "no-video",
100 .arg = G_OPTION_ARG_NONE,
101 .arg_data = (gpointer)&no_video,
102 .description = N_("Start linphone with video disabled.")
105 .long_name="iconified",
107 .arg=G_OPTION_ARG_NONE,
108 .arg_data= (gpointer)&iconified,
109 .description=N_("Start only in the system tray, do not show the main interface.")
114 .arg = G_OPTION_ARG_STRING,
115 .arg_data = &addr_to_call,
116 .description = N_("address to call right now")
119 .long_name = "auto-answer",
121 .arg = G_OPTION_ARG_NONE,
122 .arg_data = (gpointer) & auto_answer,
123 .description = N_("if set automatically answer incoming calls")
126 .long_name = "workdir",
128 .arg = G_OPTION_ARG_STRING,
129 .arg_data = (gpointer) & workingdir,
130 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
135 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
136 #define RELATIVE_XML_DIR
137 #define BUILD_TREE_XML_DIR "gtk"
140 #define CONFIG_FILE ".linphonerc"
141 #define SECRETS_FILE ".linphone-zidcache"
143 #define CONFIG_FILE "linphonerc"
144 #define SECRETS_FILE "linphone-zidcache"
148 char *linphone_gtk_get_config_file(const char *filename){
149 const int path_max=1024;
150 char *config_file=g_malloc0(path_max);
151 if (filename==NULL) filename=CONFIG_FILE;
152 /*try accessing a local file first if exists*/
153 if (access(CONFIG_FILE,F_OK)==0){
154 snprintf(config_file,path_max,"%s",filename);
157 const char *appdata=getenv("APPDATA");
159 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
160 CreateDirectory(config_file,NULL);
161 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
164 const char *home=getenv("HOME");
165 if (home==NULL) home=".";
166 snprintf(config_file,path_max,"%s/%s",home,filename);
173 #define FACTORY_CONFIG_FILE "linphonerc.factory"
174 static char _factory_config_file[1024];
175 static const char *linphone_gtk_get_factory_config_file(){
176 /*try accessing a local file first if exists*/
177 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
178 snprintf(_factory_config_file,sizeof(_factory_config_file),
179 "%s",FACTORY_CONFIG_FILE);
183 if (progpath != NULL) {
185 progdir = strdup(progpath);
187 basename = strrchr(progdir, '\\');
188 if (basename != NULL) {
191 snprintf(_factory_config_file, sizeof(_factory_config_file),
192 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
194 if (workingdir!=NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
203 basename = strrchr(progdir, '/');
204 if (basename != NULL) {
207 snprintf(_factory_config_file, sizeof(_factory_config_file),
208 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
217 return _factory_config_file;
220 static void linphone_gtk_init_liblinphone(const char *config_file,
221 const char *factory_config_file) {
222 LinphoneCoreVTable vtable={0};
223 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
225 vtable.call_state_changed=linphone_gtk_call_state_changed;
226 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
227 vtable.notify_presence_recv=linphone_gtk_notify_recv;
228 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
229 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
230 vtable.display_status=linphone_gtk_display_status;
231 vtable.display_message=linphone_gtk_display_message;
232 vtable.display_warning=linphone_gtk_display_warning;
233 vtable.display_url=linphone_gtk_display_url;
234 vtable.call_log_updated=linphone_gtk_call_log_updated;
235 vtable.text_received=linphone_gtk_text_received;
236 vtable.refer_received=linphone_gtk_refer_received;
237 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
238 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
239 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
241 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
242 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
243 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
244 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
245 g_free(secrets_file);
246 linphone_core_enable_video(the_core,TRUE,TRUE);
248 _linphone_gtk_enable_video(FALSE);
249 linphone_gtk_set_ui_config_int("videoselfview",0);
255 LinphoneCore *linphone_gtk_get_core(void){
259 GtkWidget *linphone_gtk_get_main_window(){
263 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
264 static const char *icon_path=NULL;
265 static const char *hiddens=NULL;
266 static const char *shown=NULL;
267 static bool_t config_loaded=FALSE;
268 if (linphone_gtk_get_core()==NULL) return;
269 if (config_loaded==FALSE){
270 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
271 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
272 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
276 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
278 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
280 GdkPixbuf *pbuf=create_pixbuf(icon_path);
281 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
282 g_object_unref(G_OBJECT(pbuf));
286 static int get_ui_file(const char *name, char *path, int pathsize){
287 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
288 if (access(path,F_OK)!=0){
289 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
290 if (access(path,F_OK)!=0){
291 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
292 INSTALLED_XML_DIR,name);
299 GtkWidget *linphone_gtk_create_window(const char *window_name){
300 GError* error = NULL;
301 GtkBuilder* builder = gtk_builder_new ();
305 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
307 if (!gtk_builder_add_from_file (builder, path, &error)){
308 g_error("Couldn't load builder file: %s", error->message);
309 g_error_free (error);
312 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
314 g_error("Could not retrieve '%s' window from xml file",window_name);
317 g_object_set_data(G_OBJECT(w),"builder",builder);
318 gtk_builder_connect_signals(builder,w);
319 linphone_gtk_configure_window(w,window_name);
323 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
326 GtkBuilder* builder = gtk_builder_new ();
328 gchar *object_ids[2];
329 object_ids[0]=g_strdup(widget_name);
332 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
333 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
334 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
335 g_error_free (error);
336 g_free(object_ids[0]);
339 g_free(object_ids[0]);
340 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
342 g_error("Could not retrieve '%s' window from xml file",widget_name);
345 g_object_set_data(G_OBJECT(w),"builder",builder);
346 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
347 gtk_builder_connect_signals(builder,w);
351 static void entry_unmapped(GtkWidget *entry){
352 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
353 gtk_widget_unrealize(entry);
356 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
357 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
360 g_error("Fail to retrieve builder from window !");
363 w=gtk_builder_get_object(builder,name);
365 g_error("No widget named %s found in xml interface.",name);
367 if (workaround_gtk_entry_chinese_bug){
368 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
369 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
370 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
371 g_message("%s is a GtkEntry",name);
372 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
376 return GTK_WIDGET(w);
380 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
382 GtkWidget *main_window=linphone_gtk_get_main_window();
384 gtk_widget_show(main_window);
385 if (type==GTK_MESSAGE_QUESTION)
387 /* draw a question box. link to dialog_click callback */
388 dialog = gtk_message_dialog_new (
389 GTK_WINDOW(main_window),
390 GTK_DIALOG_DESTROY_WITH_PARENT,
391 GTK_MESSAGE_QUESTION,
394 (const gchar*)message);
395 /* connect to some callback : REVISIT */
397 g_signal_connect_swapped (G_OBJECT (dialog), "response",
398 G_CALLBACK (dialog_click),
401 /* actually show the box */
402 gtk_widget_show(dialog);
406 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
407 GTK_DIALOG_DESTROY_WITH_PARENT,
411 (const gchar*)message);
412 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
413 g_signal_connect_swapped (G_OBJECT (dialog), "response",
414 G_CALLBACK (gtk_widget_destroy),
416 gtk_widget_show(dialog);
420 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
421 if (id==GTK_RESPONSE_CANCEL){
422 gtk_widget_destroy(GTK_WIDGET(dialog));
426 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
427 g_message("About url clicked");
428 linphone_gtk_open_browser(url);
431 void linphone_gtk_show_about(){
432 struct stat filestat;
433 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
436 GdkPixbuf *logo=create_pixbuf(
437 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
438 static const char *defcfg="defcfg";
440 about=linphone_gtk_create_window("about");
441 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
442 memset(&filestat,0,sizeof(filestat));
443 if (stat(license_file,&filestat)!=0){
444 license_file="COPYING";
445 stat(license_file,&filestat);
447 if (filestat.st_size>0){
448 char *license=g_malloc(filestat.st_size+1);
449 FILE *f=fopen(license_file,"r");
450 if (f && fread(license,filestat.st_size,1,f)==1){
451 license[filestat.st_size]='\0';
452 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
456 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
457 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
458 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
459 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
460 tmp=linphone_gtk_get_ui_config("artists",defcfg);
465 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
467 tmp=linphone_gtk_get_ui_config("translators",defcfg);
469 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
470 tmp=linphone_gtk_get_ui_config("comments",defcfg);
472 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
473 gtk_widget_show(about);
476 static void set_video_window_decorations(GdkWindow *w){
477 const char *title=linphone_gtk_get_ui_config("title","Linphone");
478 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
479 char video_title[256];
480 GdkPixbuf *pbuf=create_pixbuf(icon_path);
481 if (!linphone_core_in_call(linphone_gtk_get_core())){
482 snprintf(video_title,sizeof(video_title),"%s video",title);
483 /* When not in call, treat the video as a normal window */
484 gdk_window_set_keep_above(w, FALSE);
486 LinphoneAddress *uri =
487 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
490 linphone_address_clean(uri);
491 if (linphone_address_get_display_name(uri)!=NULL){
492 display_name=ms_strdup(linphone_address_get_display_name(uri));
494 display_name=linphone_address_as_string(uri);
496 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
497 linphone_address_destroy(uri);
498 ms_free(display_name);
500 /* During calls, bring up the video window, arrange so that
501 it is above all the other windows */
502 gdk_window_deiconify(w);
503 gdk_window_set_keep_above(w,TRUE);
504 /* Maybe we should have the following, but then we want to
505 have a timer that turns it off after a little while. */
506 /* gdk_window_set_urgency_hint(w,TRUE); */
508 gdk_window_set_title(w,video_title);
509 /* Refrain the video window to be closed at all times. */
510 gdk_window_set_functions(w,
511 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
512 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
515 l=g_list_append(l,pbuf);
516 gdk_window_set_icon_list(w,l);
518 g_object_unref(G_OBJECT(pbuf));
522 static gboolean video_needs_update=FALSE;
524 static void update_video_title(){
525 video_needs_update=TRUE;
528 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
529 static gboolean first_time=TRUE;
531 static unsigned long previd=0;
532 static unsigned long preview_previd=0;
533 static gboolean in_iterate=FALSE;
536 if (in_iterate) return TRUE;
538 linphone_core_iterate(lc);
540 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
541 linphone_gtk_show_directory_search();
545 id=linphone_core_get_native_video_window_id(lc);
546 if (id!=previd || video_needs_update){
550 ms_message("Updating window decorations");
552 w=gdk_window_foreign_new(id);
554 w=gdk_window_foreign_new((HANDLE)id);
557 set_video_window_decorations(w);
558 g_object_unref(G_OBJECT(w));
560 else ms_error("gdk_window_foreign_new() failed");
561 if (video_needs_update) video_needs_update=FALSE;
564 id=linphone_core_get_native_preview_window_id (lc);
565 if (id!=preview_previd ){
569 ms_message("Updating window decorations for preview");
571 w=gdk_window_foreign_new(id);
573 w=gdk_window_foreign_new((HANDLE)id);
576 set_video_window_decorations(w);
577 g_object_unref(G_OBJECT(w));
579 else ms_error("gdk_window_foreign_new() failed");
580 if (video_needs_update) video_needs_update=FALSE;
583 if (addr_to_call!=NULL){
584 /*make sure we are not showing the login screen*/
585 GtkWidget *mw=linphone_gtk_get_main_window();
586 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
587 if (!GTK_WIDGET_VISIBLE(login_frame)){
588 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
589 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
591 linphone_gtk_start_call(uri_bar);
598 static void load_uri_history(){
599 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
602 GtkEntryCompletion *gep=gtk_entry_completion_new();
603 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
606 snprintf(key,sizeof(key),"uri%i",i);
607 uri=linphone_gtk_get_ui_config(key,NULL);
610 gtk_list_store_append(model,&iter);
611 gtk_list_store_set(model,&iter,0,uri,-1);
612 if (i==0) gtk_entry_set_text(uribar,uri);
616 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
617 gtk_entry_completion_set_text_column(gep,0);
618 gtk_entry_set_completion(uribar,gep);
621 static void save_uri_history(){
622 LinphoneCore *lc=linphone_gtk_get_core();
623 LpConfig *cfg=linphone_core_get_config(lc);
624 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
629 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
631 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
633 gtk_tree_model_get(model,&iter,0,&uri,-1);
635 snprintf(key,sizeof(key),"uri%i",i);
636 lp_config_set_string(cfg,"GtkUi",key,uri);
641 }while(gtk_tree_model_iter_next(model,&iter));
645 static void completion_add_text(GtkEntry *entry, const char *text){
647 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
649 if (gtk_tree_model_get_iter_first(model,&iter)){
652 gtk_tree_model_get(model,&iter,0,&uri,-1);
654 if (strcmp(uri,text)==0) {
656 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
662 }while (gtk_tree_model_iter_next(model,&iter));
664 /* and prepend it on top of the list */
665 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
666 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
671 bool_t linphone_gtk_video_enabled(void){
672 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
673 return vpol->automatically_accept && vpol->automatically_initiate;
676 void linphone_gtk_show_main_window(){
677 GtkWidget *w=linphone_gtk_get_main_window();
678 LinphoneCore *lc=linphone_gtk_get_core();
679 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
680 VIDEOSELFVIEW_DEFAULT));
682 gtk_window_present(GTK_WINDOW(w));
685 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
686 GtkWidget *mw=linphone_gtk_get_main_window();
687 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
688 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
689 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
691 if (linphone_gtk_use_in_call_view() && call)
692 linphone_gtk_in_call_view_terminate(call,error);
693 update_video_title();
696 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
697 LinphoneCore *lc=linphone_gtk_get_core();
698 GtkWidget *mw=linphone_gtk_get_main_window();
699 const MSList *calls=linphone_core_get_calls(lc);
701 bool_t start_active=TRUE;
702 bool_t stop_active=FALSE;
703 bool_t add_call=FALSE;
704 int call_list_size=ms_list_size(calls);
714 button=linphone_gtk_get_widget(mw,"start_call");
715 gtk_widget_set_sensitive(button,start_active);
716 gtk_widget_set_visible(button,!add_call);
718 button=linphone_gtk_get_widget(mw,"add_call");
719 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
720 gtk_widget_set_sensitive(button,FALSE);
722 gtk_widget_set_sensitive(button,start_active);
724 gtk_widget_set_visible(button,add_call);
726 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
728 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
729 linphone_gtk_enable_conference_button(lc,call_list_size>1);
730 update_video_title();
731 if (call) linphone_gtk_update_video_button(call);
734 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
735 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
736 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
737 completion_add_text(GTK_ENTRY(uri_bar),entered);
739 linphone_gtk_call_terminated(NULL,NULL);
744 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
745 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
746 linphone_core_accept_call (linphone_gtk_get_core(),call);
747 linphone_call_unref(call);
753 void linphone_gtk_start_call(GtkWidget *w){
754 LinphoneCore *lc=linphone_gtk_get_core();
756 /*change into in-call mode, then do the work later as it might block a bit */
757 GtkWidget *mw=gtk_widget_get_toplevel(w);
758 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
760 call=linphone_gtk_get_currently_displayed_call(NULL);
761 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
762 linphone_core_accept_call(lc,call);
764 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
765 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
766 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
767 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
772 void linphone_gtk_uri_bar_activate(GtkWidget *w){
773 linphone_gtk_start_call(w);
777 void linphone_gtk_terminate_call(GtkWidget *button){
779 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
781 linphone_core_terminate_call(linphone_gtk_get_core(),call);
783 linphone_core_terminate_conference(linphone_gtk_get_core());
787 void linphone_gtk_decline_clicked(GtkWidget *button){
788 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
790 linphone_core_terminate_call(linphone_gtk_get_core(),call);
793 void linphone_gtk_answer_clicked(GtkWidget *button){
794 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
796 linphone_core_accept_call(linphone_gtk_get_core(),call);
797 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
801 void _linphone_gtk_enable_video(gboolean val){
802 LinphoneVideoPolicy policy={0};
803 policy.automatically_initiate=policy.automatically_accept=val;
804 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
805 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
808 linphone_core_enable_video_preview(linphone_gtk_get_core(),
809 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
811 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
815 void linphone_gtk_enable_video(GtkWidget *w){
816 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
817 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
818 _linphone_gtk_enable_video(val);
821 void linphone_gtk_enable_self_view(GtkWidget *w){
822 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
823 LinphoneCore *lc=linphone_gtk_get_core();
824 linphone_core_enable_video_preview(lc,val);
825 linphone_core_enable_self_view(lc,val);
826 linphone_gtk_set_ui_config_int("videoselfview",val);
829 void linphone_gtk_used_identity_changed(GtkWidget *w){
830 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
831 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
832 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
833 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
834 linphone_gtk_show_directory_search();
836 if (sel) g_free(sel);
840 void on_proxy_refresh_button_clicked(GtkWidget *w){
841 LinphoneCore *lc=linphone_gtk_get_core();
842 MSList const *item=linphone_core_get_proxy_config_list(lc);
843 while (item != NULL) {
844 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
845 linphone_proxy_config_edit(lpc);
846 linphone_proxy_config_done(lpc);
851 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
852 linphone_gtk_show_friends();
855 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
857 case GTK_RESPONSE_YES:
858 linphone_gtk_show_contact(lf);
861 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
863 gtk_widget_destroy(dialog);
866 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
869 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
870 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
874 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);
875 dialog = gtk_message_dialog_new (
876 GTK_WINDOW(linphone_gtk_get_main_window()),
877 GTK_DIALOG_DESTROY_WITH_PARENT,
878 GTK_MESSAGE_QUESTION,
883 g_signal_connect(G_OBJECT (dialog), "response",
884 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
885 /* actually show the box */
886 gtk_widget_show(dialog);
889 typedef struct _AuthTimeout{
894 static void auth_timeout_clean(AuthTimeout *tout){
898 static gboolean auth_timeout_destroy(AuthTimeout *tout){
900 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
901 gtk_widget_destroy(tout->w);
907 static AuthTimeout * auth_timeout_new(GtkWidget *w){
908 AuthTimeout *tout=g_new(AuthTimeout,1);
910 /*so that the timeout no more references the widget when it is destroyed:*/
911 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
912 /*so that the widget is automatically destroyed after some time */
913 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
917 void linphone_gtk_password_cancel(GtkWidget *w){
918 LinphoneAuthInfo *info;
919 GtkWidget *window=gtk_widget_get_toplevel(w);
920 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
921 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
922 gtk_widget_destroy(window);
925 void linphone_gtk_password_ok(GtkWidget *w){
927 GtkWidget *window=gtk_widget_get_toplevel(w);
928 LinphoneAuthInfo *info;
929 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
930 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
931 entry=linphone_gtk_get_widget(window,"password_entry");
932 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
933 linphone_auth_info_set_userid(info,
934 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
935 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
936 gtk_widget_destroy(window);
939 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
940 GtkWidget *w=linphone_gtk_create_window("password");
941 GtkWidget *label=linphone_gtk_get_widget(w,"message");
942 LinphoneAuthInfo *info;
944 GtkWidget *mw=linphone_gtk_get_main_window();
946 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
947 /*don't prompt for authentication when login frame is visible*/
948 linphone_core_abort_authentication(lc,NULL);
952 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
954 gtk_label_set_markup(GTK_LABEL(label),msg);
956 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
957 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
958 g_object_set_data(G_OBJECT(w),"auth_info",info);
959 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
964 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
965 GtkWidget *w=linphone_gtk_get_main_window();
966 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
967 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
968 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
972 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
973 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
976 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
977 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
980 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
982 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
983 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
986 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
987 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
988 if (w) linphone_gtk_call_log_update(w);
989 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
993 static bool_t notify_actions_supported() {
994 bool_t accepts_actions = FALSE;
995 GList *capabilities = notify_get_server_caps();
997 if(capabilities != NULL) {
998 for(c = capabilities; c != NULL; c = c->next) {
999 if(strcmp((char*)c->data, "actions") == 0 ) {
1000 accepts_actions = TRUE;
1004 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1005 g_list_free(capabilities);
1007 return accepts_actions;
1010 static NotifyNotification* build_notification(const char *title, const char *body){
1011 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1018 static void show_notification(NotifyNotification* n){
1019 if (n && !notify_notification_show(n,NULL))
1020 ms_error("Failed to send notification.");
1023 static void make_notification(const char *title, const char *body){
1024 show_notification(build_notification(title,body));
1029 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1031 if (!notify_is_initted())
1032 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1036 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1041 ms_error("Failed to send notification.");
1043 linphone_gtk_show_main_window();
1045 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1048 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1049 NotifyNotification *n;
1050 switch(linphone_call_get_state(call)){
1051 case LinphoneCallError:
1052 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1054 case LinphoneCallEnd:
1055 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1057 case LinphoneCallIncomingReceived:
1058 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1059 if (notify_actions_supported()) {
1060 notify_notification_add_action (n,"answer", _("Answer"),
1061 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1062 notify_notification_add_action (n,"decline",_("Decline"),
1063 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1065 show_notification(n);
1067 case LinphoneCallPausedByRemote:
1068 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1073 if (body) g_free(body);
1074 if (remote) g_free(remote);
1079 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1080 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1081 LinphoneCore *lc=linphone_call_get_core(call);
1082 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1083 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1084 linphone_core_accept_call_update(lc,call,params);
1085 linphone_call_params_destroy(params);
1087 linphone_call_unref(call);
1088 g_source_remove_by_user_data(dialog);
1089 gtk_widget_destroy(dialog);
1092 static void on_call_updated_timeout(GtkWidget *dialog){
1093 gtk_widget_destroy(dialog);
1096 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1097 LinphoneCore *lc=linphone_call_get_core(call);
1098 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1099 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1100 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1101 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1102 gboolean video_used=linphone_call_params_video_enabled(current_params);
1103 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1104 video_used,video_requested,pol->automatically_accept);
1105 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1106 linphone_core_defer_call_update(lc,call);
1108 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1110 const char *dname=linphone_address_get_display_name(addr);
1111 if (dname==NULL) dname=linphone_address_get_username(addr);
1112 if (dname==NULL) dname=linphone_address_get_domain(addr);
1113 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1114 GTK_DIALOG_DESTROY_WITH_PARENT,
1115 GTK_MESSAGE_WARNING,
1117 _("%s proposed to start video. Do you accept ?"),dname);
1118 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1119 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1120 gtk_widget_show(dialog);
1125 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1127 case LinphoneCallOutgoingInit:
1128 linphone_gtk_create_in_call_view (call);
1130 case LinphoneCallOutgoingProgress:
1131 linphone_gtk_in_call_view_set_calling (call);
1133 case LinphoneCallStreamsRunning:
1134 linphone_gtk_in_call_view_set_in_call(call);
1136 case LinphoneCallUpdatedByRemote:
1137 linphone_gtk_call_updated_by_remote(call);
1139 case LinphoneCallError:
1140 linphone_gtk_in_call_view_terminate (call,msg);
1142 case LinphoneCallEnd:
1143 linphone_gtk_in_call_view_terminate(call,NULL);
1144 linphone_gtk_status_icon_set_blinking(FALSE);
1146 case LinphoneCallIncomingReceived:
1147 linphone_gtk_create_in_call_view(call);
1148 linphone_gtk_in_call_view_set_incoming(call);
1149 linphone_gtk_status_icon_set_blinking(TRUE);
1151 linphone_call_ref(call);
1152 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1155 case LinphoneCallResuming:
1156 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1157 linphone_gtk_in_call_view_set_in_call (call);
1159 case LinphoneCallPausing:
1160 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1161 case LinphoneCallPausedByRemote:
1162 linphone_gtk_in_call_view_set_paused(call);
1164 case LinphoneCallConnected:
1165 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1166 linphone_gtk_status_icon_set_blinking(FALSE);
1171 linphone_gtk_notify(call, msg);
1172 linphone_gtk_update_call_buttons (call);
1175 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1176 linphone_gtk_in_call_view_show_encryption(call);
1179 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1180 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1183 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1184 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1185 GtkTreeModel *model=gtk_combo_box_get_model(box);
1187 gboolean found=FALSE;
1188 const char *stock_id=NULL;
1190 if (gtk_tree_model_get_iter_first(model,&iter)){
1193 gtk_tree_model_get(model,&iter,2,&p,-1);
1198 }while(gtk_tree_model_iter_next(model,&iter));
1201 g_warning("Could not find proxy config in combo box of identities.");
1205 case LinphoneRegistrationOk:
1206 stock_id=GTK_STOCK_YES;
1208 case LinphoneRegistrationProgress:
1209 stock_id=GTK_STOCK_REFRESH;
1211 case LinphoneRegistrationCleared:
1214 case LinphoneRegistrationFailed:
1215 stock_id=GTK_STOCK_DIALOG_WARNING;
1220 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1223 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1224 LinphoneRegistrationState rs, const char *msg){
1226 case LinphoneRegistrationOk:
1228 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1229 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1230 linphone_gtk_exit_login_frame();
1237 update_registration_status(cfg,rs);
1240 void linphone_gtk_open_browser(const char *url){
1241 /*in gtk 2.16, gtk_show_uri does not work...*/
1243 #if GTK_CHECK_VERSION(2,18,3)
1244 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1247 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1248 g_spawn_command_line_async(cl,NULL);
1251 ShellExecute(0,"open",url,NULL,NULL,1);
1255 void linphone_gtk_link_to_website(GtkWidget *item){
1256 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1257 linphone_gtk_open_browser(home);
1260 #ifndef HAVE_GTK_OSX
1262 static GtkStatusIcon *icon=NULL;
1264 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1265 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1266 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1269 static GtkWidget *create_icon_menu(){
1270 GtkWidget *menu=gtk_menu_new();
1271 GtkWidget *menu_item;
1274 const gchar *homesite;
1276 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1277 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1278 tmp=g_strdup(homesite);
1279 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1280 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1282 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1283 gtk_widget_show(image);
1284 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1285 //g_object_unref(G_OBJECT(image));
1286 gtk_widget_show(menu_item);
1287 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1288 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1290 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1291 gtk_widget_show(menu_item);
1292 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1293 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1294 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1295 gtk_widget_show(menu_item);
1296 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1297 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1298 gtk_widget_show(menu);
1302 static void handle_icon_click() {
1303 GtkWidget *mw=linphone_gtk_get_main_window();
1304 if (!gtk_window_is_active((GtkWindow*)mw)) {
1305 linphone_gtk_show_main_window();
1307 gtk_widget_hide(mw);
1311 static void linphone_gtk_init_status_icon(){
1312 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1313 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1314 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1315 GtkWidget *menu=create_icon_menu();
1317 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1318 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1319 #if GTK_CHECK_VERSION(2,20,0)
1320 gtk_status_icon_set_name(icon,title);
1322 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1323 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1324 gtk_status_icon_set_tooltip(icon,title);
1325 gtk_status_icon_set_visible(icon,TRUE);
1326 g_object_set_data(G_OBJECT(icon),"menu",menu);
1327 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1328 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1329 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1330 pbuf=create_pixbuf(call_icon_path);
1331 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1334 static gboolean do_icon_blink(GtkStatusIcon *gi){
1335 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1336 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1337 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1338 if (cur_icon==call_icon){
1339 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1341 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1348 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1350 static gint attention_id;
1351 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1353 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1354 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1358 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1359 if (val && tout==0){
1360 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1361 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1362 }else if (!val && tout!=0){
1363 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1364 g_source_remove(tout);
1365 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1366 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1372 void linphone_gtk_options_activate(GtkWidget *item){
1373 #ifndef HAVE_GTK_OSX
1374 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1379 static void init_identity_combo(GtkComboBox *box){
1380 GtkListStore *store;
1381 GtkCellRenderer *r1,*r2;
1382 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1383 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1384 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1385 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1386 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1387 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1388 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1389 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1392 void linphone_gtk_load_identities(void){
1394 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1396 LinphoneProxyConfig *def=NULL;
1398 GtkListStore *store;
1401 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1402 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1403 /* model is empty, this is the first time we go here */
1404 init_identity_combo(box);
1405 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1407 gtk_list_store_clear(store);
1408 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1409 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1410 gtk_list_store_append(store,&iter);
1411 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1412 g_free(def_identity);
1413 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1415 elem=ms_list_next(elem),i++){
1416 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1417 gtk_list_store_append(store,&iter);
1418 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1419 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1425 gtk_combo_box_set_active(box,def_index);
1428 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1429 const char *label=gtk_button_get_label(button);
1430 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1432 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1433 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1434 if (linphone_core_in_call(linphone_gtk_get_core())){
1435 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1439 static void linphone_gtk_dtmf_released(GtkButton *button){
1440 linphone_core_stop_dtmf (linphone_gtk_get_core());
1443 static void linphone_gtk_connect_digits(void){
1444 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1445 GList *children=gtk_container_get_children(cont);
1447 for(elem=children;elem!=NULL;elem=elem->next){
1448 GtkButton *button=GTK_BUTTON(elem->data);
1449 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1450 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1454 static void linphone_gtk_check_menu_items(void){
1455 bool_t video_enabled=linphone_gtk_video_enabled();
1456 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1457 GtkWidget *selfview_item=linphone_gtk_get_widget(
1458 linphone_gtk_get_main_window(),"selfview_item");
1459 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1460 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1461 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1464 static gboolean linphone_gtk_can_manage_accounts(){
1465 LinphoneCore *lc=linphone_gtk_get_core();
1467 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1468 SipSetup *ss=(SipSetup*)elem->data;
1469 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1476 static void linphone_gtk_configure_main_window(){
1477 static gboolean config_loaded=FALSE;
1478 static const char *title;
1479 static const char *home;
1480 static const char *start_call_icon;
1481 static const char *add_call_icon;
1482 static const char *stop_call_icon;
1483 static const char *search_icon;
1484 static gboolean update_check_menu;
1485 static gboolean buttons_have_borders;
1486 static gboolean show_abcd;
1487 GtkWidget *w=linphone_gtk_get_main_window();
1488 if (!config_loaded){
1489 title=linphone_gtk_get_ui_config("title","Linphone");
1490 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1491 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1492 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1493 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1494 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1495 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1496 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1497 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1500 linphone_gtk_configure_window(w,"main_window");
1502 gtk_window_set_title(GTK_WINDOW(w),title);
1504 if (start_call_icon){
1505 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1506 create_pixmap (start_call_icon));
1507 if (!buttons_have_borders)
1508 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1511 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1512 create_pixmap (add_call_icon));
1513 if (!buttons_have_borders)
1514 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1516 if (stop_call_icon){
1517 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1518 create_pixmap (stop_call_icon));
1519 if (!buttons_have_borders)
1520 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1523 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1524 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1525 g_object_unref(G_OBJECT(pbuf));
1529 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1531 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1535 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1537 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1538 g_object_unref(G_OBJECT(pbuf));
1543 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1545 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1548 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1549 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1550 gtk_image_set_from_pixbuf(img,scaled);
1551 g_object_unref(G_OBJECT(scaled));
1552 g_object_unref(G_OBJECT(pbuf));
1555 if (linphone_gtk_can_manage_accounts()) {
1556 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1558 if (update_check_menu){
1559 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1562 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1563 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1564 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1565 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1566 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1570 void linphone_gtk_manage_login(void){
1571 LinphoneCore *lc=linphone_gtk_get_core();
1572 LinphoneProxyConfig *cfg=NULL;
1573 linphone_core_get_default_proxy(lc,&cfg);
1575 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1576 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1577 linphone_gtk_show_login_frame(cfg);
1583 gboolean linphone_gtk_close(GtkWidget *mw){
1584 /*shutdown calls if any*/
1585 LinphoneCore *lc=linphone_gtk_get_core();
1586 if (linphone_core_in_call(lc)){
1587 linphone_core_terminate_all_calls(lc);
1589 linphone_core_enable_video_preview(lc,FALSE);
1590 #ifdef __APPLE__ /*until with have a better option*/
1591 gtk_window_iconify(GTK_WINDOW(mw));
1593 gtk_widget_hide(mw);
1599 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1600 bool_t video_enabled=linphone_gtk_video_enabled();
1601 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1602 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1604 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1605 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1612 static void linphone_gtk_init_main_window(){
1613 GtkWidget *main_window;
1615 linphone_gtk_configure_main_window();
1616 linphone_gtk_manage_login();
1618 linphone_gtk_load_identities();
1619 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1620 linphone_gtk_show_friends();
1621 linphone_gtk_connect_digits();
1622 main_window=linphone_gtk_get_main_window();
1623 linphone_gtk_call_log_update(main_window);
1625 linphone_gtk_update_call_buttons (NULL);
1626 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1627 g_signal_connect (G_OBJECT (main_window), "delete-event",
1628 G_CALLBACK (linphone_gtk_close), main_window);
1631 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1632 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1633 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1634 gtk_widget_hide(menubar);
1635 gtk_osxapplication_ready(theMacApp);
1637 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1639 linphone_gtk_check_menu_items();
1643 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1645 const char *lname="undef";
1647 #if defined(__linux) || defined(__APPLE__)
1648 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1667 g_error("Bad level !");
1669 #if defined(__linux) || defined(__APPLE__)
1671 msg=g_strdup_vprintf(fmt,cap);
1674 msg=g_strdup_vprintf(fmt,args);
1676 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1679 linphone_gtk_log_push(lev,fmt,args);
1683 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1684 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1685 linphone_gtk_get_main_window(), "uribar"));
1687 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1689 gtk_entry_set_text(uri_bar, refer_to);
1690 linphone_gtk_start_call(linphone_gtk_get_main_window());
1693 static void linphone_gtk_check_soundcards(){
1694 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1695 if (devices==NULL || devices[0]==NULL){
1696 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1697 _("No sound cards have been detected on this computer.\n"
1698 "You won't be able to send or receive audio calls."));
1703 // Display the account wizard
1704 void linphone_gtk_display_wizard() {
1705 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1706 the_wizard = linphone_gtk_create_assistant();
1711 static void linphone_gtk_quit(void){
1712 static gboolean quit_done=FALSE;
1715 linphone_gtk_unmonitor_usb();
1716 g_source_remove_by_user_data(linphone_gtk_get_core());
1717 linphone_gtk_uninit_instance();
1718 linphone_gtk_destroy_log_window();
1719 linphone_core_destroy(the_core);
1720 linphone_gtk_log_uninit();
1724 gdk_threads_leave();
1730 This is not the correct way to implement block termination.
1731 The good way would be to call gtk_main_quit(), and return TRUE.
1732 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1733 As a result the program cannot exit at all.
1734 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1736 static gboolean on_block_termination(void){
1738 linphone_gtk_quit();
1743 int main(int argc, char *argv[]){
1748 const char *factory_config_file;
1750 GtkSettings *settings;
1752 const char *app_name="Linphone";
1754 #if !GLIB_CHECK_VERSION(2, 31, 0)
1755 g_thread_init(NULL);
1759 progpath = strdup(argv[0]);
1761 config_file=linphone_gtk_get_config_file(NULL);
1765 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1766 if ((lang=getenv("LANG"))!=NULL){
1769 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1775 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1778 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1781 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1783 if (strncmp(lang,"zh",2)==0){
1784 workaround_gtk_entry_chinese_bug=TRUE;
1787 setenv("LANG",lang,1);
1792 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1793 if (p==NULL) perror("bindtextdomain failed");
1794 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1795 textdomain (GETTEXT_PACKAGE);
1797 g_message("NLS disabled.\n");
1800 gtk_rc_add_default_file("./gtkrc");
1802 gdk_threads_enter();
1804 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1805 linphone_options,NULL,NULL)){
1806 gdk_threads_leave();
1810 settings=gtk_settings_get_default();
1811 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1812 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1813 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1814 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1816 if (workingdir!=NULL){
1817 if (chdir(workingdir)==-1){
1818 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1822 /* Now, look for the factory configuration file, we do it this late
1823 since we want to have had time to change directory and to parse
1824 the options, in case we needed to access the working directory */
1825 factory_config_file = linphone_gtk_get_factory_config_file();
1827 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1828 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1829 g_warning("This instance is going to exit now.");
1830 gdk_threads_leave();
1834 add_pixmap_directory("pixmaps");
1835 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1838 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1839 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1840 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1841 /*never block termination:*/
1842 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1845 the_ui=linphone_gtk_create_window("main");
1847 linphone_gtk_create_log_window();
1848 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1850 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1852 g_set_application_name(app_name);
1853 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1854 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1856 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1857 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1858 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1859 linphone_gtk_init_main_window();
1862 // Veryfing if at least one sip account is configured. If not, show wizard
1863 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1864 linphone_gtk_display_wizard();
1868 #ifndef HAVE_GTK_OSX
1869 linphone_gtk_init_status_icon();
1872 linphone_gtk_show_main_window();
1873 linphone_gtk_check_soundcards();
1875 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1876 linphone_gtk_check_for_new_version();
1877 linphone_gtk_monitor_usb();
1880 linphone_gtk_quit();
1881 #ifndef HAVE_GTK_OSX
1882 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1883 gtk_status_icon_set_visible(icon,FALSE);