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 static 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.");
1039 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1044 ms_error("Failed to send notification.");
1046 linphone_gtk_show_main_window();
1048 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1051 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1052 NotifyNotification *n;
1053 switch(linphone_call_get_state(call)){
1054 case LinphoneCallError:
1055 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1057 case LinphoneCallEnd:
1058 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1060 case LinphoneCallIncomingReceived:
1061 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1062 if (notify_actions_supported()) {
1063 notify_notification_add_action (n,"answer", _("Answer"),
1064 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1065 notify_notification_add_action (n,"decline",_("Decline"),
1066 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1068 show_notification(n);
1070 case LinphoneCallPausedByRemote:
1071 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1076 if (body) g_free(body);
1077 if (remote) g_free(remote);
1082 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1083 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1084 LinphoneCore *lc=linphone_call_get_core(call);
1085 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1086 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1087 linphone_core_accept_call_update(lc,call,params);
1088 linphone_call_params_destroy(params);
1090 linphone_call_unref(call);
1091 g_source_remove_by_user_data(dialog);
1092 gtk_widget_destroy(dialog);
1095 static void on_call_updated_timeout(GtkWidget *dialog){
1096 gtk_widget_destroy(dialog);
1099 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1100 LinphoneCore *lc=linphone_call_get_core(call);
1101 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1102 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1103 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1104 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1105 gboolean video_used=linphone_call_params_video_enabled(current_params);
1106 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1107 video_used,video_requested,pol->automatically_accept);
1108 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1109 linphone_core_defer_call_update(lc,call);
1111 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1113 const char *dname=linphone_address_get_display_name(addr);
1114 if (dname==NULL) dname=linphone_address_get_username(addr);
1115 if (dname==NULL) dname=linphone_address_get_domain(addr);
1116 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1117 GTK_DIALOG_DESTROY_WITH_PARENT,
1118 GTK_MESSAGE_WARNING,
1120 _("%s proposed to start video. Do you accept ?"),dname);
1121 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1122 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1123 gtk_widget_show(dialog);
1128 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1130 case LinphoneCallOutgoingInit:
1131 linphone_gtk_create_in_call_view (call);
1133 case LinphoneCallOutgoingProgress:
1134 linphone_gtk_in_call_view_set_calling (call);
1136 case LinphoneCallStreamsRunning:
1137 linphone_gtk_in_call_view_set_in_call(call);
1139 case LinphoneCallUpdatedByRemote:
1140 linphone_gtk_call_updated_by_remote(call);
1142 case LinphoneCallError:
1143 linphone_gtk_in_call_view_terminate (call,msg);
1145 case LinphoneCallEnd:
1146 linphone_gtk_in_call_view_terminate(call,NULL);
1147 linphone_gtk_status_icon_set_blinking(FALSE);
1149 case LinphoneCallIncomingReceived:
1150 linphone_gtk_create_in_call_view(call);
1151 linphone_gtk_in_call_view_set_incoming(call);
1152 linphone_gtk_status_icon_set_blinking(TRUE);
1154 linphone_call_ref(call);
1155 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1158 case LinphoneCallResuming:
1159 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1160 linphone_gtk_in_call_view_set_in_call (call);
1162 case LinphoneCallPausing:
1163 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1164 case LinphoneCallPausedByRemote:
1165 linphone_gtk_in_call_view_set_paused(call);
1167 case LinphoneCallConnected:
1168 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1169 linphone_gtk_status_icon_set_blinking(FALSE);
1174 linphone_gtk_notify(call, msg);
1175 linphone_gtk_update_call_buttons (call);
1178 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1179 linphone_gtk_in_call_view_show_encryption(call);
1182 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1183 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1186 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1187 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1188 GtkTreeModel *model=gtk_combo_box_get_model(box);
1190 gboolean found=FALSE;
1191 const char *stock_id=NULL;
1193 if (gtk_tree_model_get_iter_first(model,&iter)){
1196 gtk_tree_model_get(model,&iter,2,&p,-1);
1201 }while(gtk_tree_model_iter_next(model,&iter));
1204 g_warning("Could not find proxy config in combo box of identities.");
1208 case LinphoneRegistrationOk:
1209 stock_id=GTK_STOCK_YES;
1211 case LinphoneRegistrationProgress:
1212 stock_id=GTK_STOCK_REFRESH;
1214 case LinphoneRegistrationCleared:
1217 case LinphoneRegistrationFailed:
1218 stock_id=GTK_STOCK_DIALOG_WARNING;
1223 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1226 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1227 LinphoneRegistrationState rs, const char *msg){
1229 case LinphoneRegistrationOk:
1231 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1232 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1233 linphone_gtk_exit_login_frame();
1240 update_registration_status(cfg,rs);
1243 void linphone_gtk_open_browser(const char *url){
1244 /*in gtk 2.16, gtk_show_uri does not work...*/
1246 #if GTK_CHECK_VERSION(2,18,3)
1247 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1250 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1251 g_spawn_command_line_async(cl,NULL);
1254 ShellExecute(0,"open",url,NULL,NULL,1);
1258 void linphone_gtk_link_to_website(GtkWidget *item){
1259 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1260 linphone_gtk_open_browser(home);
1263 #ifndef HAVE_GTK_OSX
1265 static GtkStatusIcon *icon=NULL;
1267 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1268 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1269 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1272 static GtkWidget *create_icon_menu(){
1273 GtkWidget *menu=gtk_menu_new();
1274 GtkWidget *menu_item;
1277 const gchar *homesite;
1279 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1280 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1281 tmp=g_strdup(homesite);
1282 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1283 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1285 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1286 gtk_widget_show(image);
1287 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1288 //g_object_unref(G_OBJECT(image));
1289 gtk_widget_show(menu_item);
1290 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1291 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1293 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1294 gtk_widget_show(menu_item);
1295 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1296 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1297 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1298 gtk_widget_show(menu_item);
1299 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1300 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1301 gtk_widget_show(menu);
1305 static void handle_icon_click() {
1306 GtkWidget *mw=linphone_gtk_get_main_window();
1307 if (!gtk_window_is_active((GtkWindow*)mw)) {
1308 linphone_gtk_show_main_window();
1310 gtk_widget_hide(mw);
1314 static void linphone_gtk_init_status_icon(){
1315 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1316 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1317 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1318 GtkWidget *menu=create_icon_menu();
1320 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1321 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1322 #if GTK_CHECK_VERSION(2,20,0)
1323 gtk_status_icon_set_name(icon,title);
1325 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1326 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1327 gtk_status_icon_set_tooltip(icon,title);
1328 gtk_status_icon_set_visible(icon,TRUE);
1329 g_object_set_data(G_OBJECT(icon),"menu",menu);
1330 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1331 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1332 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1333 pbuf=create_pixbuf(call_icon_path);
1334 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1337 static gboolean do_icon_blink(GtkStatusIcon *gi){
1338 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1339 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1340 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1341 if (cur_icon==call_icon){
1342 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1344 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1351 void linphone_gtk_status_icon_set_blinking(gboolean val){
1353 static gint attention_id;
1354 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1356 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1357 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1361 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1362 if (val && tout==0){
1363 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1364 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1365 }else if (!val && tout!=0){
1366 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1367 g_source_remove(tout);
1368 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1369 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1375 void linphone_gtk_options_activate(GtkWidget *item){
1376 #ifndef HAVE_GTK_OSX
1377 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1382 static void init_identity_combo(GtkComboBox *box){
1383 GtkListStore *store;
1384 GtkCellRenderer *r1,*r2;
1385 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1386 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1387 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1388 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1389 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1390 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1391 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1392 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1395 void linphone_gtk_load_identities(void){
1397 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1399 LinphoneProxyConfig *def=NULL;
1401 GtkListStore *store;
1404 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1405 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1406 /* model is empty, this is the first time we go here */
1407 init_identity_combo(box);
1408 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1410 gtk_list_store_clear(store);
1411 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1412 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1413 gtk_list_store_append(store,&iter);
1414 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1415 g_free(def_identity);
1416 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1418 elem=ms_list_next(elem),i++){
1419 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1420 gtk_list_store_append(store,&iter);
1421 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1422 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1428 gtk_combo_box_set_active(box,def_index);
1431 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1432 const char *label=gtk_button_get_label(button);
1433 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1435 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1436 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1437 if (linphone_core_in_call(linphone_gtk_get_core())){
1438 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1442 static void linphone_gtk_dtmf_released(GtkButton *button){
1443 linphone_core_stop_dtmf (linphone_gtk_get_core());
1446 static void linphone_gtk_connect_digits(void){
1447 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1448 GList *children=gtk_container_get_children(cont);
1450 for(elem=children;elem!=NULL;elem=elem->next){
1451 GtkButton *button=GTK_BUTTON(elem->data);
1452 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1453 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1457 static void linphone_gtk_check_menu_items(void){
1458 bool_t video_enabled=linphone_gtk_video_enabled();
1459 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1460 GtkWidget *selfview_item=linphone_gtk_get_widget(
1461 linphone_gtk_get_main_window(),"selfview_item");
1462 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1463 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1464 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1467 static gboolean linphone_gtk_can_manage_accounts(){
1468 LinphoneCore *lc=linphone_gtk_get_core();
1470 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1471 SipSetup *ss=(SipSetup*)elem->data;
1472 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1479 static void linphone_gtk_configure_main_window(){
1480 static gboolean config_loaded=FALSE;
1481 static const char *title;
1482 static const char *home;
1483 static const char *start_call_icon;
1484 static const char *add_call_icon;
1485 static const char *stop_call_icon;
1486 static const char *search_icon;
1487 static gboolean update_check_menu;
1488 static gboolean buttons_have_borders;
1489 static gboolean show_abcd;
1490 GtkWidget *w=linphone_gtk_get_main_window();
1491 if (!config_loaded){
1492 title=linphone_gtk_get_ui_config("title","Linphone");
1493 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1494 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1495 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1496 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1497 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1498 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1499 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1500 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1503 linphone_gtk_configure_window(w,"main_window");
1505 gtk_window_set_title(GTK_WINDOW(w),title);
1507 if (start_call_icon){
1508 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1509 create_pixmap (start_call_icon));
1510 if (!buttons_have_borders)
1511 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1514 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1515 create_pixmap (add_call_icon));
1516 if (!buttons_have_borders)
1517 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1519 if (stop_call_icon){
1520 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1521 create_pixmap (stop_call_icon));
1522 if (!buttons_have_borders)
1523 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1526 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1527 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1528 g_object_unref(G_OBJECT(pbuf));
1532 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1534 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1538 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1540 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1541 g_object_unref(G_OBJECT(pbuf));
1546 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1548 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1551 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1552 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1553 gtk_image_set_from_pixbuf(img,scaled);
1554 g_object_unref(G_OBJECT(scaled));
1555 g_object_unref(G_OBJECT(pbuf));
1558 if (linphone_gtk_can_manage_accounts()) {
1559 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1561 if (update_check_menu){
1562 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1565 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1566 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1567 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1568 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1569 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1573 void linphone_gtk_manage_login(void){
1574 LinphoneCore *lc=linphone_gtk_get_core();
1575 LinphoneProxyConfig *cfg=NULL;
1576 linphone_core_get_default_proxy(lc,&cfg);
1578 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1579 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1580 linphone_gtk_show_login_frame(cfg);
1586 gboolean linphone_gtk_close(GtkWidget *mw){
1587 /*shutdown calls if any*/
1588 LinphoneCore *lc=linphone_gtk_get_core();
1589 if (linphone_core_in_call(lc)){
1590 linphone_core_terminate_all_calls(lc);
1592 linphone_core_enable_video_preview(lc,FALSE);
1593 #ifdef __APPLE__ /*until with have a better option*/
1594 gtk_window_iconify(GTK_WINDOW(mw));
1596 gtk_widget_hide(mw);
1602 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1603 bool_t video_enabled=linphone_gtk_video_enabled();
1604 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1605 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1607 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1608 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1615 static void linphone_gtk_init_main_window(){
1616 GtkWidget *main_window;
1618 linphone_gtk_configure_main_window();
1619 linphone_gtk_manage_login();
1621 linphone_gtk_load_identities();
1622 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1623 linphone_gtk_show_friends();
1624 linphone_gtk_connect_digits();
1625 main_window=linphone_gtk_get_main_window();
1626 linphone_gtk_call_log_update(main_window);
1628 linphone_gtk_update_call_buttons (NULL);
1629 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1630 g_signal_connect (G_OBJECT (main_window), "delete-event",
1631 G_CALLBACK (linphone_gtk_close), main_window);
1634 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1635 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1636 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1637 gtk_widget_hide(menubar);
1638 gtk_osxapplication_ready(theMacApp);
1640 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1642 linphone_gtk_check_menu_items();
1646 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1648 const char *lname="undef";
1650 #if defined(__linux) || defined(__APPLE__)
1651 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1670 g_error("Bad level !");
1672 #if defined(__linux) || defined(__APPLE__)
1674 msg=g_strdup_vprintf(fmt,cap);
1677 msg=g_strdup_vprintf(fmt,args);
1679 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1682 linphone_gtk_log_push(lev,fmt,args);
1686 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1687 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1688 linphone_gtk_get_main_window(), "uribar"));
1690 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1692 gtk_entry_set_text(uri_bar, refer_to);
1693 linphone_gtk_start_call(linphone_gtk_get_main_window());
1696 static void linphone_gtk_check_soundcards(){
1697 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1698 if (devices==NULL || devices[0]==NULL){
1699 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1700 _("No sound cards have been detected on this computer.\n"
1701 "You won't be able to send or receive audio calls."));
1706 // Display the account wizard
1707 void linphone_gtk_display_wizard() {
1708 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1709 the_wizard = linphone_gtk_create_assistant();
1714 static void linphone_gtk_quit(void){
1715 static gboolean quit_done=FALSE;
1718 linphone_gtk_unmonitor_usb();
1719 g_source_remove_by_user_data(linphone_gtk_get_core());
1720 linphone_gtk_uninit_instance();
1721 linphone_gtk_destroy_log_window();
1722 linphone_core_destroy(the_core);
1723 linphone_gtk_log_uninit();
1727 gdk_threads_leave();
1733 This is not the correct way to implement block termination.
1734 The good way would be to call gtk_main_quit(), and return TRUE.
1735 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1736 As a result the program cannot exit at all.
1737 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1739 static gboolean on_block_termination(void){
1741 linphone_gtk_quit();
1746 int main(int argc, char *argv[]){
1751 const char *factory_config_file;
1753 GtkSettings *settings;
1755 const char *app_name="Linphone";
1757 #if !GLIB_CHECK_VERSION(2, 31, 0)
1758 g_thread_init(NULL);
1762 progpath = strdup(argv[0]);
1764 config_file=linphone_gtk_get_config_file(NULL);
1768 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1769 if ((lang=getenv("LANG"))!=NULL){
1772 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1778 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1781 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1784 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1786 if (strncmp(lang,"zh",2)==0){
1787 workaround_gtk_entry_chinese_bug=TRUE;
1790 setenv("LANG",lang,1);
1795 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1796 if (p==NULL) perror("bindtextdomain failed");
1797 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1798 textdomain (GETTEXT_PACKAGE);
1800 g_message("NLS disabled.\n");
1803 gtk_rc_add_default_file("./gtkrc");
1805 gdk_threads_enter();
1807 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1808 linphone_options,NULL,NULL)){
1809 gdk_threads_leave();
1813 settings=gtk_settings_get_default();
1814 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1815 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1816 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1817 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1819 if (workingdir!=NULL){
1820 if (chdir(workingdir)==-1){
1821 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1825 /* Now, look for the factory configuration file, we do it this late
1826 since we want to have had time to change directory and to parse
1827 the options, in case we needed to access the working directory */
1828 factory_config_file = linphone_gtk_get_factory_config_file();
1830 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1831 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1832 g_warning("This instance is going to exit now.");
1833 gdk_threads_leave();
1837 add_pixmap_directory("pixmaps");
1838 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1841 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1842 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1843 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1844 /*never block termination:*/
1845 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1848 the_ui=linphone_gtk_create_window("main");
1850 linphone_gtk_create_log_window();
1851 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1853 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1855 g_set_application_name(app_name);
1856 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1857 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1859 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1860 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1861 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1862 linphone_gtk_init_main_window();
1865 // Veryfing if at least one sip account is configured. If not, show wizard
1866 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1867 linphone_gtk_display_wizard();
1871 #ifndef HAVE_GTK_OSX
1872 linphone_gtk_init_status_icon();
1875 linphone_gtk_show_main_window();
1876 linphone_gtk_check_soundcards();
1878 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1879 linphone_gtk_check_for_new_version();
1880 linphone_gtk_monitor_usb();
1883 linphone_gtk_quit();
1884 #ifndef HAVE_GTK_OSX
1885 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1886 gtk_status_icon_set_visible(icon,FALSE);