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 void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
73 static gboolean verbose=0;
74 static gboolean auto_answer = 0;
75 static gchar * addr_to_call = NULL;
76 static gboolean no_video=FALSE;
77 static gboolean iconified=FALSE;
78 static gchar *workingdir=NULL;
79 static char *progpath=NULL;
80 gchar *linphone_logfile=NULL;
81 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
83 static GOptionEntry linphone_options[]={
87 .arg=G_OPTION_ARG_NONE,
88 .arg_data= (gpointer)&verbose,
89 .description=N_("log to stdout some debug information while running.")
92 .long_name = "logfile",
94 .arg = G_OPTION_ARG_STRING,
95 .arg_data = &linphone_logfile,
96 .description = N_("path to a file to write logs into.")
99 .long_name = "no-video",
101 .arg = G_OPTION_ARG_NONE,
102 .arg_data = (gpointer)&no_video,
103 .description = N_("Start linphone with video disabled.")
106 .long_name="iconified",
108 .arg=G_OPTION_ARG_NONE,
109 .arg_data= (gpointer)&iconified,
110 .description=N_("Start only in the system tray, do not show the main interface.")
115 .arg = G_OPTION_ARG_STRING,
116 .arg_data = &addr_to_call,
117 .description = N_("address to call right now")
120 .long_name = "auto-answer",
122 .arg = G_OPTION_ARG_NONE,
123 .arg_data = (gpointer) & auto_answer,
124 .description = N_("if set automatically answer incoming calls")
127 .long_name = "workdir",
129 .arg = G_OPTION_ARG_STRING,
130 .arg_data = (gpointer) & workingdir,
131 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
136 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
137 #define RELATIVE_XML_DIR
138 #define BUILD_TREE_XML_DIR "gtk"
141 #define CONFIG_FILE ".linphonerc"
142 #define SECRETS_FILE ".linphone-zidcache"
144 #define CONFIG_FILE "linphonerc"
145 #define SECRETS_FILE "linphone-zidcache"
149 char *linphone_gtk_get_config_file(const char *filename){
150 const int path_max=1024;
151 char *config_file=g_malloc0(path_max);
152 if (filename==NULL) filename=CONFIG_FILE;
153 /*try accessing a local file first if exists*/
154 if (access(CONFIG_FILE,F_OK)==0){
155 snprintf(config_file,path_max,"%s",filename);
158 const char *appdata=getenv("APPDATA");
160 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
161 CreateDirectory(config_file,NULL);
162 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
165 const char *home=getenv("HOME");
166 if (home==NULL) home=".";
167 snprintf(config_file,path_max,"%s/%s",home,filename);
174 #define FACTORY_CONFIG_FILE "linphonerc.factory"
175 static char _factory_config_file[1024];
176 static const char *linphone_gtk_get_factory_config_file(){
177 /*try accessing a local file first if exists*/
178 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
179 snprintf(_factory_config_file,sizeof(_factory_config_file),
180 "%s",FACTORY_CONFIG_FILE);
184 if (progpath != NULL) {
186 progdir = strdup(progpath);
188 basename = strrchr(progdir, '\\');
189 if (basename != NULL) {
192 snprintf(_factory_config_file, sizeof(_factory_config_file),
193 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
195 if (workingdir!=NULL) {
196 snprintf(_factory_config_file, sizeof(_factory_config_file),
197 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
204 basename = strrchr(progdir, '/');
205 if (basename != NULL) {
208 snprintf(_factory_config_file, sizeof(_factory_config_file),
209 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
218 return _factory_config_file;
221 static void linphone_gtk_init_liblinphone(const char *config_file,
222 const char *factory_config_file) {
223 LinphoneCoreVTable vtable={0};
224 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
226 vtable.call_state_changed=linphone_gtk_call_state_changed;
227 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
228 vtable.notify_presence_recv=linphone_gtk_notify_recv;
229 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
230 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
231 vtable.display_status=linphone_gtk_display_status;
232 vtable.display_message=linphone_gtk_display_message;
233 vtable.display_warning=linphone_gtk_display_warning;
234 vtable.display_url=linphone_gtk_display_url;
235 vtable.call_log_updated=linphone_gtk_call_log_updated;
236 vtable.text_received=linphone_gtk_text_received;
237 vtable.refer_received=linphone_gtk_refer_received;
238 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
239 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
240 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
242 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
243 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
244 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
245 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
246 g_free(secrets_file);
247 linphone_core_enable_video(the_core,TRUE,TRUE);
249 _linphone_gtk_enable_video(FALSE);
250 linphone_gtk_set_ui_config_int("videoselfview",0);
256 LinphoneCore *linphone_gtk_get_core(void){
260 GtkWidget *linphone_gtk_get_main_window(){
264 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
265 static const char *icon_path=NULL;
266 static const char *hiddens=NULL;
267 static const char *shown=NULL;
268 static bool_t config_loaded=FALSE;
269 if (linphone_gtk_get_core()==NULL) return;
270 if (config_loaded==FALSE){
271 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
272 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
273 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
277 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
279 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
281 GdkPixbuf *pbuf=create_pixbuf(icon_path);
282 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
283 g_object_unref(G_OBJECT(pbuf));
287 static int get_ui_file(const char *name, char *path, int pathsize){
288 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
289 if (access(path,F_OK)!=0){
290 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
291 if (access(path,F_OK)!=0){
292 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
293 INSTALLED_XML_DIR,name);
300 GtkWidget *linphone_gtk_create_window(const char *window_name){
301 GError* error = NULL;
302 GtkBuilder* builder = gtk_builder_new ();
306 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
308 if (!gtk_builder_add_from_file (builder, path, &error)){
309 g_error("Couldn't load builder file: %s", error->message);
310 g_error_free (error);
313 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
315 g_error("Could not retrieve '%s' window from xml file",window_name);
318 g_object_set_data(G_OBJECT(w),"builder",builder);
319 gtk_builder_connect_signals(builder,w);
320 linphone_gtk_configure_window(w,window_name);
324 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
327 GtkBuilder* builder = gtk_builder_new ();
329 gchar *object_ids[2];
330 object_ids[0]=g_strdup(widget_name);
333 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
334 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
335 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
336 g_error_free (error);
337 g_free(object_ids[0]);
340 g_free(object_ids[0]);
341 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
343 g_error("Could not retrieve '%s' window from xml file",widget_name);
346 g_object_set_data(G_OBJECT(w),"builder",builder);
347 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
348 gtk_builder_connect_signals(builder,w);
352 static void entry_unmapped(GtkWidget *entry){
353 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
354 gtk_widget_unrealize(entry);
357 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
360 if (window==NULL) return NULL;
361 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
363 g_error("Fail to retrieve builder from window !");
366 w=gtk_builder_get_object(builder,name);
368 g_error("No widget named %s found in xml interface.",name);
370 if (workaround_gtk_entry_chinese_bug){
371 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
372 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
373 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
374 g_message("%s is a GtkEntry",name);
375 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
379 return GTK_WIDGET(w);
383 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
385 GtkWidget *main_window=linphone_gtk_get_main_window();
387 gtk_widget_show(main_window);
388 if (type==GTK_MESSAGE_QUESTION)
390 /* draw a question box. link to dialog_click callback */
391 dialog = gtk_message_dialog_new (
392 GTK_WINDOW(main_window),
393 GTK_DIALOG_DESTROY_WITH_PARENT,
394 GTK_MESSAGE_QUESTION,
397 (const gchar*)message);
398 /* connect to some callback : REVISIT */
400 g_signal_connect_swapped (G_OBJECT (dialog), "response",
401 G_CALLBACK (dialog_click),
404 /* actually show the box */
405 gtk_widget_show(dialog);
409 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
410 GTK_DIALOG_DESTROY_WITH_PARENT,
414 (const gchar*)message);
415 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
416 g_signal_connect_swapped (G_OBJECT (dialog), "response",
417 G_CALLBACK (gtk_widget_destroy),
419 gtk_widget_show(dialog);
423 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
424 if (id==GTK_RESPONSE_CANCEL){
425 gtk_widget_destroy(GTK_WIDGET(dialog));
429 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
430 g_message("About url clicked");
431 linphone_gtk_open_browser(url);
434 void linphone_gtk_show_about(){
435 struct stat filestat;
436 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
439 GdkPixbuf *logo=create_pixbuf(
440 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
441 static const char *defcfg="defcfg";
443 about=linphone_gtk_create_window("about");
444 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
445 memset(&filestat,0,sizeof(filestat));
446 if (stat(license_file,&filestat)!=0){
447 license_file="COPYING";
448 stat(license_file,&filestat);
450 if (filestat.st_size>0){
451 char *license=g_malloc(filestat.st_size+1);
452 FILE *f=fopen(license_file,"r");
453 if (f && fread(license,filestat.st_size,1,f)==1){
454 license[filestat.st_size]='\0';
455 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
459 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
460 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
461 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
462 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
463 tmp=linphone_gtk_get_ui_config("artists",defcfg);
468 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
470 tmp=linphone_gtk_get_ui_config("translators",defcfg);
472 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
473 tmp=linphone_gtk_get_ui_config("comments",defcfg);
475 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
476 gtk_widget_show(about);
479 static void set_video_window_decorations(GdkWindow *w){
480 const char *title=linphone_gtk_get_ui_config("title","Linphone");
481 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
482 char video_title[256];
483 GdkPixbuf *pbuf=create_pixbuf(icon_path);
484 if (!linphone_core_in_call(linphone_gtk_get_core())){
485 snprintf(video_title,sizeof(video_title),"%s video",title);
486 /* When not in call, treat the video as a normal window */
487 gdk_window_set_keep_above(w, FALSE);
489 LinphoneAddress *uri =
490 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
493 linphone_address_clean(uri);
494 if (linphone_address_get_display_name(uri)!=NULL){
495 display_name=ms_strdup(linphone_address_get_display_name(uri));
497 display_name=linphone_address_as_string(uri);
499 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
500 linphone_address_destroy(uri);
501 ms_free(display_name);
503 /* During calls, bring up the video window, arrange so that
504 it is above all the other windows */
505 gdk_window_deiconify(w);
506 gdk_window_set_keep_above(w,TRUE);
507 /* Maybe we should have the following, but then we want to
508 have a timer that turns it off after a little while. */
509 /* gdk_window_set_urgency_hint(w,TRUE); */
511 gdk_window_set_title(w,video_title);
512 /* Refrain the video window to be closed at all times. */
513 gdk_window_set_functions(w,
514 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
515 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
518 l=g_list_append(l,pbuf);
519 gdk_window_set_icon_list(w,l);
521 g_object_unref(G_OBJECT(pbuf));
525 static gboolean video_needs_update=FALSE;
527 static void update_video_title(){
528 video_needs_update=TRUE;
531 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
532 static gboolean first_time=TRUE;
534 static unsigned long previd=0;
535 static unsigned long preview_previd=0;
536 static gboolean in_iterate=FALSE;
539 if (in_iterate) return TRUE;
541 linphone_core_iterate(lc);
543 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
544 linphone_gtk_show_directory_search();
548 id=linphone_core_get_native_video_window_id(lc);
549 if (id!=previd || video_needs_update){
553 ms_message("Updating window decorations");
555 w=gdk_window_foreign_new(id);
557 w=gdk_window_foreign_new((HANDLE)id);
560 set_video_window_decorations(w);
561 g_object_unref(G_OBJECT(w));
563 else ms_error("gdk_window_foreign_new() failed");
564 if (video_needs_update) video_needs_update=FALSE;
567 id=linphone_core_get_native_preview_window_id (lc);
568 if (id!=preview_previd ){
572 ms_message("Updating window decorations for preview");
574 w=gdk_window_foreign_new(id);
576 w=gdk_window_foreign_new((HANDLE)id);
579 set_video_window_decorations(w);
580 g_object_unref(G_OBJECT(w));
582 else ms_error("gdk_window_foreign_new() failed");
583 if (video_needs_update) video_needs_update=FALSE;
586 if (addr_to_call!=NULL){
587 /*make sure we are not showing the login screen*/
588 GtkWidget *mw=linphone_gtk_get_main_window();
589 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
590 if (!GTK_WIDGET_VISIBLE(login_frame)){
591 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
592 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
594 linphone_gtk_start_call(uri_bar);
601 static void load_uri_history(){
602 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
605 GtkEntryCompletion *gep=gtk_entry_completion_new();
606 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
609 snprintf(key,sizeof(key),"uri%i",i);
610 uri=linphone_gtk_get_ui_config(key,NULL);
613 gtk_list_store_append(model,&iter);
614 gtk_list_store_set(model,&iter,0,uri,-1);
615 if (i==0) gtk_entry_set_text(uribar,uri);
619 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
620 gtk_entry_completion_set_text_column(gep,0);
621 gtk_entry_set_completion(uribar,gep);
624 static void save_uri_history(){
625 LinphoneCore *lc=linphone_gtk_get_core();
626 LpConfig *cfg=linphone_core_get_config(lc);
627 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
632 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
634 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
636 gtk_tree_model_get(model,&iter,0,&uri,-1);
638 snprintf(key,sizeof(key),"uri%i",i);
639 lp_config_set_string(cfg,"GtkUi",key,uri);
644 }while(gtk_tree_model_iter_next(model,&iter));
648 static void completion_add_text(GtkEntry *entry, const char *text){
650 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
652 if (gtk_tree_model_get_iter_first(model,&iter)){
655 gtk_tree_model_get(model,&iter,0,&uri,-1);
657 if (strcmp(uri,text)==0) {
659 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
665 }while (gtk_tree_model_iter_next(model,&iter));
667 /* and prepend it on top of the list */
668 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
669 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
674 bool_t linphone_gtk_video_enabled(void){
675 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
676 return vpol->automatically_accept && vpol->automatically_initiate;
679 void linphone_gtk_show_main_window(){
680 GtkWidget *w=linphone_gtk_get_main_window();
681 LinphoneCore *lc=linphone_gtk_get_core();
682 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
683 VIDEOSELFVIEW_DEFAULT));
685 gtk_window_present(GTK_WINDOW(w));
688 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
689 GtkWidget *mw=linphone_gtk_get_main_window();
690 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
691 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
692 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
694 if (linphone_gtk_use_in_call_view() && call)
695 linphone_gtk_in_call_view_terminate(call,error);
696 update_video_title();
699 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
700 LinphoneCore *lc=linphone_gtk_get_core();
701 GtkWidget *mw=linphone_gtk_get_main_window();
702 const MSList *calls=linphone_core_get_calls(lc);
704 bool_t start_active=TRUE;
705 bool_t stop_active=FALSE;
706 bool_t add_call=FALSE;
707 int call_list_size=ms_list_size(calls);
717 button=linphone_gtk_get_widget(mw,"start_call");
718 gtk_widget_set_sensitive(button,start_active);
719 gtk_widget_set_visible(button,!add_call);
721 button=linphone_gtk_get_widget(mw,"add_call");
722 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
723 gtk_widget_set_sensitive(button,FALSE);
725 gtk_widget_set_sensitive(button,start_active);
727 gtk_widget_set_visible(button,add_call);
729 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
731 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
732 linphone_gtk_enable_conference_button(lc,call_list_size>1);
733 update_video_title();
734 if (call) linphone_gtk_update_video_button(call);
737 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
738 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
739 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
740 completion_add_text(GTK_ENTRY(uri_bar),entered);
742 linphone_gtk_call_terminated(NULL,NULL);
747 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
748 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
749 linphone_core_accept_call (linphone_gtk_get_core(),call);
750 linphone_call_unref(call);
756 void linphone_gtk_start_call(GtkWidget *w){
757 LinphoneCore *lc=linphone_gtk_get_core();
759 /*change into in-call mode, then do the work later as it might block a bit */
760 GtkWidget *mw=gtk_widget_get_toplevel(w);
761 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
763 call=linphone_gtk_get_currently_displayed_call(NULL);
764 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
765 linphone_core_accept_call(lc,call);
767 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
768 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
769 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
770 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
775 void linphone_gtk_uri_bar_activate(GtkWidget *w){
776 linphone_gtk_start_call(w);
780 void linphone_gtk_terminate_call(GtkWidget *button){
782 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
784 linphone_core_terminate_call(linphone_gtk_get_core(),call);
786 linphone_core_terminate_conference(linphone_gtk_get_core());
790 void linphone_gtk_decline_clicked(GtkWidget *button){
791 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
793 linphone_core_terminate_call(linphone_gtk_get_core(),call);
796 void linphone_gtk_answer_clicked(GtkWidget *button){
797 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
799 linphone_core_accept_call(linphone_gtk_get_core(),call);
800 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
804 void _linphone_gtk_enable_video(gboolean val){
805 LinphoneVideoPolicy policy={0};
806 policy.automatically_initiate=policy.automatically_accept=val;
807 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
808 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
811 linphone_core_enable_video_preview(linphone_gtk_get_core(),
812 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
814 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
818 void linphone_gtk_enable_video(GtkWidget *w){
819 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
820 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
821 _linphone_gtk_enable_video(val);
824 void linphone_gtk_enable_self_view(GtkWidget *w){
825 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
826 LinphoneCore *lc=linphone_gtk_get_core();
827 linphone_core_enable_video_preview(lc,val);
828 linphone_core_enable_self_view(lc,val);
829 linphone_gtk_set_ui_config_int("videoselfview",val);
832 void linphone_gtk_used_identity_changed(GtkWidget *w){
833 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
834 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
835 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
836 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
837 linphone_gtk_show_directory_search();
839 if (sel) g_free(sel);
843 void on_proxy_refresh_button_clicked(GtkWidget *w){
844 LinphoneCore *lc=linphone_gtk_get_core();
845 MSList const *item=linphone_core_get_proxy_config_list(lc);
846 while (item != NULL) {
847 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
848 linphone_proxy_config_edit(lpc);
849 linphone_proxy_config_done(lpc);
854 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
855 linphone_gtk_show_friends();
858 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
860 case GTK_RESPONSE_YES:
861 linphone_gtk_show_contact(lf);
864 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
866 gtk_widget_destroy(dialog);
869 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
872 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
873 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
877 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);
878 dialog = gtk_message_dialog_new (
879 GTK_WINDOW(linphone_gtk_get_main_window()),
880 GTK_DIALOG_DESTROY_WITH_PARENT,
881 GTK_MESSAGE_QUESTION,
886 g_signal_connect(G_OBJECT (dialog), "response",
887 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
888 /* actually show the box */
889 gtk_widget_show(dialog);
892 typedef struct _AuthTimeout{
897 static void auth_timeout_clean(AuthTimeout *tout){
901 static gboolean auth_timeout_destroy(AuthTimeout *tout){
903 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
904 gtk_widget_destroy(tout->w);
910 static AuthTimeout * auth_timeout_new(GtkWidget *w){
911 AuthTimeout *tout=g_new(AuthTimeout,1);
913 /*so that the timeout no more references the widget when it is destroyed:*/
914 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
915 /*so that the widget is automatically destroyed after some time */
916 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
920 void linphone_gtk_password_cancel(GtkWidget *w){
921 LinphoneAuthInfo *info;
922 GtkWidget *window=gtk_widget_get_toplevel(w);
923 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
924 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
925 gtk_widget_destroy(window);
928 void linphone_gtk_password_ok(GtkWidget *w){
930 GtkWidget *window=gtk_widget_get_toplevel(w);
931 LinphoneAuthInfo *info;
932 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
933 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
934 entry=linphone_gtk_get_widget(window,"password_entry");
935 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
936 linphone_auth_info_set_userid(info,
937 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
938 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
939 gtk_widget_destroy(window);
942 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
943 GtkWidget *w=linphone_gtk_create_window("password");
944 GtkWidget *label=linphone_gtk_get_widget(w,"message");
945 LinphoneAuthInfo *info;
947 GtkWidget *mw=linphone_gtk_get_main_window();
949 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
950 /*don't prompt for authentication when login frame is visible*/
951 linphone_core_abort_authentication(lc,NULL);
955 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
957 gtk_label_set_markup(GTK_LABEL(label),msg);
959 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
960 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
961 g_object_set_data(G_OBJECT(w),"auth_info",info);
962 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
967 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
968 GtkWidget *w=linphone_gtk_get_main_window();
969 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
970 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
971 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
975 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
976 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
979 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
980 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
983 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
985 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
986 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
989 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
990 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
991 if (w) linphone_gtk_call_log_update(w);
992 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
996 static bool_t notify_actions_supported() {
997 bool_t accepts_actions = FALSE;
998 GList *capabilities = notify_get_server_caps();
1000 if(capabilities != NULL) {
1001 for(c = capabilities; c != NULL; c = c->next) {
1002 if(strcmp((char*)c->data, "actions") == 0 ) {
1003 accepts_actions = TRUE;
1007 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1008 g_list_free(capabilities);
1010 return accepts_actions;
1013 static NotifyNotification* build_notification(const char *title, const char *body){
1014 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1021 static void show_notification(NotifyNotification* n){
1022 if (n && !notify_notification_show(n,NULL))
1023 ms_error("Failed to send notification.");
1026 static void make_notification(const char *title, const char *body){
1027 show_notification(build_notification(title,body));
1032 void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1034 if (!notify_is_initted())
1035 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1040 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1046 ms_error("Failed to send notification.");
1048 linphone_gtk_show_main_window();
1050 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1053 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1054 NotifyNotification *n;
1055 switch(linphone_call_get_state(call)){
1056 case LinphoneCallError:
1057 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1059 case LinphoneCallEnd:
1060 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1062 case LinphoneCallIncomingReceived:
1063 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1064 if (notify_actions_supported()) {
1065 notify_notification_add_action (n,"answer", _("Answer"),
1066 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1067 notify_notification_add_action (n,"decline",_("Decline"),
1068 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1070 show_notification(n);
1072 case LinphoneCallPausedByRemote:
1073 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1078 if (body) g_free(body);
1079 if (remote) g_free(remote);
1084 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1085 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1086 LinphoneCore *lc=linphone_call_get_core(call);
1087 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1088 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1089 linphone_core_accept_call_update(lc,call,params);
1090 linphone_call_params_destroy(params);
1092 linphone_call_unref(call);
1093 g_source_remove_by_user_data(dialog);
1094 gtk_widget_destroy(dialog);
1097 static void on_call_updated_timeout(GtkWidget *dialog){
1098 gtk_widget_destroy(dialog);
1101 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1102 LinphoneCore *lc=linphone_call_get_core(call);
1103 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1104 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1105 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1106 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1107 gboolean video_used=linphone_call_params_video_enabled(current_params);
1108 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1109 video_used,video_requested,pol->automatically_accept);
1110 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1111 linphone_core_defer_call_update(lc,call);
1113 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1115 const char *dname=linphone_address_get_display_name(addr);
1116 if (dname==NULL) dname=linphone_address_get_username(addr);
1117 if (dname==NULL) dname=linphone_address_get_domain(addr);
1118 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1119 GTK_DIALOG_DESTROY_WITH_PARENT,
1120 GTK_MESSAGE_WARNING,
1122 _("%s proposed to start video. Do you accept ?"),dname);
1123 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1124 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1125 gtk_widget_show(dialog);
1130 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1132 case LinphoneCallOutgoingInit:
1133 linphone_gtk_create_in_call_view (call);
1135 case LinphoneCallOutgoingProgress:
1136 linphone_gtk_in_call_view_set_calling (call);
1138 case LinphoneCallStreamsRunning:
1139 linphone_gtk_in_call_view_set_in_call(call);
1141 case LinphoneCallUpdatedByRemote:
1142 linphone_gtk_call_updated_by_remote(call);
1144 case LinphoneCallError:
1145 linphone_gtk_in_call_view_terminate (call,msg);
1147 case LinphoneCallEnd:
1148 linphone_gtk_in_call_view_terminate(call,NULL);
1149 linphone_gtk_status_icon_set_blinking(FALSE);
1151 case LinphoneCallIncomingReceived:
1152 linphone_gtk_create_in_call_view(call);
1153 linphone_gtk_in_call_view_set_incoming(call);
1154 linphone_gtk_status_icon_set_blinking(TRUE);
1156 linphone_call_ref(call);
1157 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1160 case LinphoneCallResuming:
1161 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1162 linphone_gtk_in_call_view_set_in_call (call);
1164 case LinphoneCallPausing:
1165 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1166 case LinphoneCallPausedByRemote:
1167 linphone_gtk_in_call_view_set_paused(call);
1169 case LinphoneCallConnected:
1170 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1171 linphone_gtk_status_icon_set_blinking(FALSE);
1176 linphone_gtk_notify(call, msg);
1177 linphone_gtk_update_call_buttons (call);
1180 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1181 linphone_gtk_in_call_view_show_encryption(call);
1184 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1185 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1188 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1189 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1190 GtkTreeModel *model=gtk_combo_box_get_model(box);
1192 gboolean found=FALSE;
1193 const char *stock_id=NULL;
1195 if (gtk_tree_model_get_iter_first(model,&iter)){
1198 gtk_tree_model_get(model,&iter,2,&p,-1);
1203 }while(gtk_tree_model_iter_next(model,&iter));
1206 g_warning("Could not find proxy config in combo box of identities.");
1210 case LinphoneRegistrationOk:
1211 stock_id=GTK_STOCK_YES;
1213 case LinphoneRegistrationProgress:
1214 stock_id=GTK_STOCK_REFRESH;
1216 case LinphoneRegistrationCleared:
1219 case LinphoneRegistrationFailed:
1220 stock_id=GTK_STOCK_DIALOG_WARNING;
1225 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1228 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1229 LinphoneRegistrationState rs, const char *msg){
1231 case LinphoneRegistrationOk:
1233 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1234 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1235 linphone_gtk_exit_login_frame();
1242 update_registration_status(cfg,rs);
1245 void linphone_gtk_open_browser(const char *url){
1246 /*in gtk 2.16, gtk_show_uri does not work...*/
1248 #if GTK_CHECK_VERSION(2,18,3)
1249 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1252 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1253 g_spawn_command_line_async(cl,NULL);
1256 ShellExecute(0,"open",url,NULL,NULL,1);
1260 void linphone_gtk_link_to_website(GtkWidget *item){
1261 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1262 linphone_gtk_open_browser(home);
1265 #ifndef HAVE_GTK_OSX
1267 static GtkStatusIcon *icon=NULL;
1269 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1270 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1271 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1274 static GtkWidget *create_icon_menu(){
1275 GtkWidget *menu=gtk_menu_new();
1276 GtkWidget *menu_item;
1279 const gchar *homesite;
1281 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1282 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1283 tmp=g_strdup(homesite);
1284 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1285 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1287 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1288 gtk_widget_show(image);
1289 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1290 //g_object_unref(G_OBJECT(image));
1291 gtk_widget_show(menu_item);
1292 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1293 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1295 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1296 gtk_widget_show(menu_item);
1297 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1298 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1299 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1300 gtk_widget_show(menu_item);
1301 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1302 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1303 gtk_widget_show(menu);
1307 static void handle_icon_click() {
1308 GtkWidget *mw=linphone_gtk_get_main_window();
1309 if (!gtk_window_is_active((GtkWindow*)mw)) {
1310 linphone_gtk_show_main_window();
1312 gtk_widget_hide(mw);
1316 static void linphone_gtk_init_status_icon(){
1317 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1318 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1319 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1320 GtkWidget *menu=create_icon_menu();
1322 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1323 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1324 #if GTK_CHECK_VERSION(2,20,0)
1325 gtk_status_icon_set_name(icon,title);
1327 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1328 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1329 gtk_status_icon_set_tooltip(icon,title);
1330 gtk_status_icon_set_visible(icon,TRUE);
1331 g_object_set_data(G_OBJECT(icon),"menu",menu);
1332 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1333 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1334 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1335 pbuf=create_pixbuf(call_icon_path);
1336 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1339 static gboolean do_icon_blink(GtkStatusIcon *gi){
1340 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1341 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1342 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1343 if (cur_icon==call_icon){
1344 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1346 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1353 void linphone_gtk_status_icon_set_blinking(gboolean val){
1355 static gint attention_id;
1356 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1358 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1359 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1363 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1364 if (val && tout==0){
1365 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1366 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1367 }else if (!val && tout!=0){
1368 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1369 g_source_remove(tout);
1370 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1371 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1377 void linphone_gtk_options_activate(GtkWidget *item){
1378 #ifndef HAVE_GTK_OSX
1379 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1384 static void init_identity_combo(GtkComboBox *box){
1385 GtkListStore *store;
1386 GtkCellRenderer *r1,*r2;
1387 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1388 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1389 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1390 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1391 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1392 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1393 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1394 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1397 void linphone_gtk_load_identities(void){
1399 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1401 LinphoneProxyConfig *def=NULL;
1403 GtkListStore *store;
1406 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1407 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1408 /* model is empty, this is the first time we go here */
1409 init_identity_combo(box);
1410 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1412 gtk_list_store_clear(store);
1413 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1414 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1415 gtk_list_store_append(store,&iter);
1416 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1417 g_free(def_identity);
1418 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1420 elem=ms_list_next(elem),i++){
1421 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1422 gtk_list_store_append(store,&iter);
1423 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1424 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1430 gtk_combo_box_set_active(box,def_index);
1433 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1434 const char *label=gtk_button_get_label(button);
1435 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1437 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1438 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1439 if (linphone_core_in_call(linphone_gtk_get_core())){
1440 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1444 static void linphone_gtk_dtmf_released(GtkButton *button){
1445 linphone_core_stop_dtmf (linphone_gtk_get_core());
1448 static void linphone_gtk_connect_digits(void){
1449 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1450 GList *children=gtk_container_get_children(cont);
1452 for(elem=children;elem!=NULL;elem=elem->next){
1453 GtkButton *button=GTK_BUTTON(elem->data);
1454 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1455 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1459 static void linphone_gtk_check_menu_items(void){
1460 bool_t video_enabled=linphone_gtk_video_enabled();
1461 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1462 GtkWidget *selfview_item=linphone_gtk_get_widget(
1463 linphone_gtk_get_main_window(),"selfview_item");
1464 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1465 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1466 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1469 static gboolean linphone_gtk_can_manage_accounts(){
1470 LinphoneCore *lc=linphone_gtk_get_core();
1472 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1473 SipSetup *ss=(SipSetup*)elem->data;
1474 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1481 static void linphone_gtk_configure_main_window(){
1482 static gboolean config_loaded=FALSE;
1483 static const char *title;
1484 static const char *home;
1485 static const char *start_call_icon;
1486 static const char *add_call_icon;
1487 static const char *stop_call_icon;
1488 static const char *search_icon;
1489 static gboolean update_check_menu;
1490 static gboolean buttons_have_borders;
1491 static gboolean show_abcd;
1492 GtkWidget *w=linphone_gtk_get_main_window();
1493 if (!config_loaded){
1494 title=linphone_gtk_get_ui_config("title","Linphone");
1495 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1496 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1497 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1498 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1499 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1500 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1501 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1502 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1505 linphone_gtk_configure_window(w,"main_window");
1507 gtk_window_set_title(GTK_WINDOW(w),title);
1509 if (start_call_icon){
1510 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1511 create_pixmap (start_call_icon));
1512 if (!buttons_have_borders)
1513 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1516 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1517 create_pixmap (add_call_icon));
1518 if (!buttons_have_borders)
1519 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1521 if (stop_call_icon){
1522 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1523 create_pixmap (stop_call_icon));
1524 if (!buttons_have_borders)
1525 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1528 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1529 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1530 g_object_unref(G_OBJECT(pbuf));
1534 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1536 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1540 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1542 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1543 g_object_unref(G_OBJECT(pbuf));
1548 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1550 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1553 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1554 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1555 gtk_image_set_from_pixbuf(img,scaled);
1556 g_object_unref(G_OBJECT(scaled));
1557 g_object_unref(G_OBJECT(pbuf));
1560 if (linphone_gtk_can_manage_accounts()) {
1561 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1563 if (update_check_menu){
1564 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1567 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1568 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1569 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1570 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1571 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1575 void linphone_gtk_manage_login(void){
1576 LinphoneCore *lc=linphone_gtk_get_core();
1577 LinphoneProxyConfig *cfg=NULL;
1578 linphone_core_get_default_proxy(lc,&cfg);
1580 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1581 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1582 linphone_gtk_show_login_frame(cfg);
1588 gboolean linphone_gtk_close(GtkWidget *mw){
1589 /*shutdown calls if any*/
1590 LinphoneCore *lc=linphone_gtk_get_core();
1591 if (linphone_core_in_call(lc)){
1592 linphone_core_terminate_all_calls(lc);
1594 linphone_core_enable_video_preview(lc,FALSE);
1595 #ifdef __APPLE__ /*until with have a better option*/
1596 gtk_window_iconify(GTK_WINDOW(mw));
1598 gtk_widget_hide(mw);
1604 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1605 bool_t video_enabled=linphone_gtk_video_enabled();
1606 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1607 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1609 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1610 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1617 static void linphone_gtk_init_main_window(){
1618 GtkWidget *main_window;
1620 linphone_gtk_configure_main_window();
1621 linphone_gtk_manage_login();
1623 linphone_gtk_load_identities();
1624 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1625 linphone_gtk_show_friends();
1626 linphone_gtk_connect_digits();
1627 main_window=linphone_gtk_get_main_window();
1628 linphone_gtk_call_log_update(main_window);
1630 linphone_gtk_update_call_buttons (NULL);
1631 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1632 g_signal_connect (G_OBJECT (main_window), "delete-event",
1633 G_CALLBACK (linphone_gtk_close), main_window);
1636 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1637 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1638 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1639 gtk_widget_hide(menubar);
1640 gtk_osxapplication_ready(theMacApp);
1642 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1644 linphone_gtk_check_menu_items();
1648 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1650 const char *lname="undef";
1652 #if defined(__linux) || defined(__APPLE__)
1653 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1672 g_error("Bad level !");
1674 #if defined(__linux) || defined(__APPLE__)
1676 msg=g_strdup_vprintf(fmt,cap);
1679 msg=g_strdup_vprintf(fmt,args);
1681 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1684 linphone_gtk_log_push(lev,fmt,args);
1688 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1689 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1690 linphone_gtk_get_main_window(), "uribar"));
1692 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1694 gtk_entry_set_text(uri_bar, refer_to);
1695 linphone_gtk_start_call(linphone_gtk_get_main_window());
1698 static void linphone_gtk_check_soundcards(){
1699 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1700 if (devices==NULL || devices[0]==NULL){
1701 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1702 _("No sound cards have been detected on this computer.\n"
1703 "You won't be able to send or receive audio calls."));
1708 // Display the account wizard
1709 void linphone_gtk_display_wizard() {
1710 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1711 the_wizard = linphone_gtk_create_assistant();
1716 static void linphone_gtk_quit(void){
1717 static gboolean quit_done=FALSE;
1720 linphone_gtk_unmonitor_usb();
1721 g_source_remove_by_user_data(linphone_gtk_get_core());
1722 linphone_gtk_uninit_instance();
1723 linphone_gtk_destroy_log_window();
1724 linphone_core_destroy(the_core);
1725 linphone_gtk_log_uninit();
1729 gdk_threads_leave();
1735 This is not the correct way to implement block termination.
1736 The good way would be to call gtk_main_quit(), and return TRUE.
1737 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1738 As a result the program cannot exit at all.
1739 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1741 static gboolean on_block_termination(void){
1743 linphone_gtk_quit();
1748 int main(int argc, char *argv[]){
1753 const char *factory_config_file;
1755 GtkSettings *settings;
1757 const char *app_name="Linphone";
1759 #if !GLIB_CHECK_VERSION(2, 31, 0)
1760 g_thread_init(NULL);
1764 progpath = strdup(argv[0]);
1766 config_file=linphone_gtk_get_config_file(NULL);
1770 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1771 if ((lang=getenv("LANG"))!=NULL){
1774 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1780 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1783 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1786 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1788 if (strncmp(lang,"zh",2)==0){
1789 workaround_gtk_entry_chinese_bug=TRUE;
1792 setenv("LANG",lang,1);
1797 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1798 if (p==NULL) perror("bindtextdomain failed");
1799 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1800 textdomain (GETTEXT_PACKAGE);
1802 g_message("NLS disabled.\n");
1805 gtk_rc_add_default_file("./gtkrc");
1807 gdk_threads_enter();
1809 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1810 linphone_options,NULL,NULL)){
1811 gdk_threads_leave();
1815 settings=gtk_settings_get_default();
1816 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1817 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1818 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1819 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1821 if (workingdir!=NULL){
1822 if (chdir(workingdir)==-1){
1823 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1827 /* Now, look for the factory configuration file, we do it this late
1828 since we want to have had time to change directory and to parse
1829 the options, in case we needed to access the working directory */
1830 factory_config_file = linphone_gtk_get_factory_config_file();
1832 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1833 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1834 g_warning("This instance is going to exit now.");
1835 gdk_threads_leave();
1839 add_pixmap_directory("pixmaps");
1840 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1843 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1844 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1845 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1846 /*never block termination:*/
1847 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1850 the_ui=linphone_gtk_create_window("main");
1852 linphone_gtk_create_log_window();
1853 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1855 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1857 g_set_application_name(app_name);
1858 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1859 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1861 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1862 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1863 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1864 linphone_gtk_init_main_window();
1867 // Veryfing if at least one sip account is configured. If not, show wizard
1868 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1869 linphone_gtk_display_wizard();
1873 #ifndef HAVE_GTK_OSX
1874 linphone_gtk_init_status_icon();
1877 linphone_gtk_show_main_window();
1878 linphone_gtk_check_soundcards();
1880 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1881 linphone_gtk_check_for_new_version();
1882 linphone_gtk_monitor_usb();
1885 linphone_gtk_quit();
1886 #ifndef HAVE_GTK_OSX
1887 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1888 gtk_status_icon_set_visible(icon,FALSE);