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);
173 #define FACTORY_CONFIG_FILE "linphonerc.factory"
174 static char _factory_config_file[1024];
175 static const char *linphone_gtk_get_factory_config_file(){
176 /*try accessing a local file first if exists*/
177 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
178 snprintf(_factory_config_file,sizeof(_factory_config_file),
179 "%s",FACTORY_CONFIG_FILE);
183 if (progpath != NULL) {
185 progdir = strdup(progpath);
187 basename = strrchr(progdir, '\\');
188 if (basename != NULL) {
191 snprintf(_factory_config_file, sizeof(_factory_config_file),
192 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
194 if (workingdir!=NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
203 basename = strrchr(progdir, '/');
204 if (basename != NULL) {
207 snprintf(_factory_config_file, sizeof(_factory_config_file),
208 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
217 return _factory_config_file;
220 static void linphone_gtk_init_liblinphone(const char *config_file,
221 const char *factory_config_file) {
222 LinphoneCoreVTable vtable={0};
223 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
225 vtable.call_state_changed=linphone_gtk_call_state_changed;
226 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
227 vtable.notify_presence_recv=linphone_gtk_notify_recv;
228 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
229 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
230 vtable.display_status=linphone_gtk_display_status;
231 vtable.display_message=linphone_gtk_display_message;
232 vtable.display_warning=linphone_gtk_display_warning;
233 vtable.display_url=linphone_gtk_display_url;
234 vtable.call_log_updated=linphone_gtk_call_log_updated;
235 vtable.text_received=linphone_gtk_text_received;
236 vtable.refer_received=linphone_gtk_refer_received;
237 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
238 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
239 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
241 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
242 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
243 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
244 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
245 g_free(secrets_file);
246 linphone_core_enable_video(the_core,TRUE,TRUE);
248 _linphone_gtk_enable_video(FALSE);
249 linphone_gtk_set_ui_config_int("videoselfview",0);
255 LinphoneCore *linphone_gtk_get_core(void){
259 GtkWidget *linphone_gtk_get_main_window(){
263 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
264 static const char *icon_path=NULL;
265 static const char *hiddens=NULL;
266 static const char *shown=NULL;
267 static bool_t config_loaded=FALSE;
268 if (linphone_gtk_get_core()==NULL) return;
269 if (config_loaded==FALSE){
270 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
271 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
272 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
276 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
278 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
280 GdkPixbuf *pbuf=create_pixbuf(icon_path);
281 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
282 g_object_unref(G_OBJECT(pbuf));
286 static int get_ui_file(const char *name, char *path, int pathsize){
287 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
288 if (access(path,F_OK)!=0){
289 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
290 if (access(path,F_OK)!=0){
291 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
292 INSTALLED_XML_DIR,name);
299 GtkWidget *linphone_gtk_create_window(const char *window_name){
300 GError* error = NULL;
301 GtkBuilder* builder = gtk_builder_new ();
305 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
307 if (!gtk_builder_add_from_file (builder, path, &error)){
308 g_error("Couldn't load builder file: %s", error->message);
309 g_error_free (error);
312 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
314 g_error("Could not retrieve '%s' window from xml file",window_name);
317 g_object_set_data(G_OBJECT(w),"builder",builder);
318 gtk_builder_connect_signals(builder,w);
319 linphone_gtk_configure_window(w,window_name);
323 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
326 GtkBuilder* builder = gtk_builder_new ();
328 gchar *object_ids[2];
329 object_ids[0]=g_strdup(widget_name);
332 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
333 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
334 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
335 g_error_free (error);
336 g_free(object_ids[0]);
339 g_free(object_ids[0]);
340 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
342 g_error("Could not retrieve '%s' window from xml file",widget_name);
345 g_object_set_data(G_OBJECT(w),"builder",builder);
346 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
347 gtk_builder_connect_signals(builder,w);
351 static void entry_unmapped(GtkWidget *entry){
352 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
353 gtk_widget_unrealize(entry);
356 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
359 if (window==NULL) return NULL;
360 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
362 g_error("Fail to retrieve builder from window !");
365 w=gtk_builder_get_object(builder,name);
367 g_error("No widget named %s found in xml interface.",name);
369 if (workaround_gtk_entry_chinese_bug){
370 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
371 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
372 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
373 g_message("%s is a GtkEntry",name);
374 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
378 return GTK_WIDGET(w);
382 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
384 GtkWidget *main_window=linphone_gtk_get_main_window();
386 gtk_widget_show(main_window);
387 if (type==GTK_MESSAGE_QUESTION)
389 /* draw a question box. link to dialog_click callback */
390 dialog = gtk_message_dialog_new (
391 GTK_WINDOW(main_window),
392 GTK_DIALOG_DESTROY_WITH_PARENT,
393 GTK_MESSAGE_QUESTION,
396 (const gchar*)message);
397 /* connect to some callback : REVISIT */
399 g_signal_connect_swapped (G_OBJECT (dialog), "response",
400 G_CALLBACK (dialog_click),
403 /* actually show the box */
404 gtk_widget_show(dialog);
408 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
409 GTK_DIALOG_DESTROY_WITH_PARENT,
413 (const gchar*)message);
414 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
415 g_signal_connect_swapped (G_OBJECT (dialog), "response",
416 G_CALLBACK (gtk_widget_destroy),
418 gtk_widget_show(dialog);
422 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
423 if (id==GTK_RESPONSE_CANCEL){
424 gtk_widget_destroy(GTK_WIDGET(dialog));
428 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
429 g_message("About url clicked");
430 linphone_gtk_open_browser(url);
433 void linphone_gtk_show_about(){
434 struct stat filestat;
435 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
438 GdkPixbuf *logo=create_pixbuf(
439 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
440 static const char *defcfg="defcfg";
442 about=linphone_gtk_create_window("about");
443 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
444 memset(&filestat,0,sizeof(filestat));
445 if (stat(license_file,&filestat)!=0){
446 license_file="COPYING";
447 stat(license_file,&filestat);
449 if (filestat.st_size>0){
450 char *license=g_malloc(filestat.st_size+1);
451 FILE *f=fopen(license_file,"r");
452 if (f && fread(license,filestat.st_size,1,f)==1){
453 license[filestat.st_size]='\0';
454 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
458 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
459 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
460 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
461 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
462 tmp=linphone_gtk_get_ui_config("artists",defcfg);
467 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
469 tmp=linphone_gtk_get_ui_config("translators",defcfg);
471 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
472 tmp=linphone_gtk_get_ui_config("comments",defcfg);
474 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
475 gtk_widget_show(about);
478 static void set_video_window_decorations(GdkWindow *w){
479 const char *title=linphone_gtk_get_ui_config("title","Linphone");
480 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
481 char video_title[256];
482 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,"start_call"),TRUE);
693 if (linphone_gtk_use_in_call_view() && call)
694 linphone_gtk_in_call_view_terminate(call,error);
695 update_video_title();
698 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
699 LinphoneCore *lc=linphone_gtk_get_core();
700 GtkWidget *mw=linphone_gtk_get_main_window();
701 const MSList *calls=linphone_core_get_calls(lc);
703 bool_t start_active=TRUE;
704 //bool_t stop_active=FALSE;
705 bool_t add_call=FALSE;
706 int call_list_size=ms_list_size(calls);
716 button=linphone_gtk_get_widget(mw,"start_call");
717 gtk_widget_set_sensitive(button,start_active);
718 gtk_widget_set_visible(button,!add_call);
720 button=linphone_gtk_get_widget(mw,"add_call");
721 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
722 gtk_widget_set_sensitive(button,FALSE);
724 gtk_widget_set_sensitive(button,start_active);
726 gtk_widget_set_visible(button,add_call);
728 //gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
730 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
731 linphone_gtk_enable_conference_button(lc,call_list_size>1);
732 update_video_title();
733 if (call) linphone_gtk_update_video_button(call);
736 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
737 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
738 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
739 completion_add_text(GTK_ENTRY(uri_bar),entered);
741 linphone_gtk_call_terminated(NULL,NULL);
746 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
747 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
748 linphone_core_accept_call (linphone_gtk_get_core(),call);
749 linphone_call_unref(call);
755 void linphone_gtk_start_call(GtkWidget *w){
756 LinphoneCore *lc=linphone_gtk_get_core();
758 /*change into in-call mode, then do the work later as it might block a bit */
759 GtkWidget *mw=gtk_widget_get_toplevel(w);
760 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
762 call=linphone_gtk_get_currently_displayed_call(NULL);
763 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
764 linphone_core_accept_call(lc,call);
766 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
767 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
768 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
769 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
774 void linphone_gtk_uri_bar_activate(GtkWidget *w){
775 linphone_gtk_start_call(w);
779 void linphone_gtk_terminate_call(GtkWidget *button){
781 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
783 linphone_core_terminate_call(linphone_gtk_get_core(),call);
785 linphone_core_terminate_conference(linphone_gtk_get_core());
789 void linphone_gtk_decline_clicked(GtkWidget *button){
790 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
792 linphone_core_terminate_call(linphone_gtk_get_core(),call);
795 void linphone_gtk_answer_clicked(GtkWidget *button){
796 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
798 linphone_core_accept_call(linphone_gtk_get_core(),call);
799 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
803 void _linphone_gtk_enable_video(gboolean val){
804 LinphoneVideoPolicy policy={0};
805 policy.automatically_initiate=policy.automatically_accept=val;
806 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
807 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
810 linphone_core_enable_video_preview(linphone_gtk_get_core(),
811 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
813 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
817 void linphone_gtk_enable_video(GtkWidget *w){
818 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
819 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
820 _linphone_gtk_enable_video(val);
823 void linphone_gtk_enable_self_view(GtkWidget *w){
824 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
825 LinphoneCore *lc=linphone_gtk_get_core();
826 linphone_core_enable_video_preview(lc,val);
827 linphone_core_enable_self_view(lc,val);
828 linphone_gtk_set_ui_config_int("videoselfview",val);
831 void linphone_gtk_used_identity_changed(GtkWidget *w){
832 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
833 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
834 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
835 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
836 linphone_gtk_show_directory_search();
838 if (sel) g_free(sel);
842 void on_proxy_refresh_button_clicked(GtkWidget *w){
843 LinphoneCore *lc=linphone_gtk_get_core();
844 MSList const *item=linphone_core_get_proxy_config_list(lc);
845 while (item != NULL) {
846 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
847 linphone_proxy_config_edit(lpc);
848 linphone_proxy_config_done(lpc);
853 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
854 linphone_gtk_show_friends();
857 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
859 case GTK_RESPONSE_YES:
860 linphone_gtk_show_contact(lf);
863 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
865 gtk_widget_destroy(dialog);
868 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
871 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
872 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
876 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);
877 dialog = gtk_message_dialog_new (
878 GTK_WINDOW(linphone_gtk_get_main_window()),
879 GTK_DIALOG_DESTROY_WITH_PARENT,
880 GTK_MESSAGE_QUESTION,
885 g_signal_connect(G_OBJECT (dialog), "response",
886 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
887 /* actually show the box */
888 gtk_widget_show(dialog);
891 typedef struct _AuthTimeout{
896 static void auth_timeout_clean(AuthTimeout *tout){
900 static gboolean auth_timeout_destroy(AuthTimeout *tout){
902 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
903 gtk_widget_destroy(tout->w);
909 static AuthTimeout * auth_timeout_new(GtkWidget *w){
910 AuthTimeout *tout=g_new(AuthTimeout,1);
912 /*so that the timeout no more references the widget when it is destroyed:*/
913 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
914 /*so that the widget is automatically destroyed after some time */
915 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
919 void linphone_gtk_password_cancel(GtkWidget *w){
920 LinphoneAuthInfo *info;
921 GtkWidget *window=gtk_widget_get_toplevel(w);
922 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
923 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
924 gtk_widget_destroy(window);
927 void linphone_gtk_password_ok(GtkWidget *w){
929 GtkWidget *window=gtk_widget_get_toplevel(w);
930 LinphoneAuthInfo *info;
931 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
932 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
933 entry=linphone_gtk_get_widget(window,"password_entry");
934 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
935 linphone_auth_info_set_userid(info,
936 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
937 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
938 gtk_widget_destroy(window);
941 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
942 GtkWidget *w=linphone_gtk_create_window("password");
943 GtkWidget *label=linphone_gtk_get_widget(w,"message");
944 LinphoneAuthInfo *info;
946 GtkWidget *mw=linphone_gtk_get_main_window();
948 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
949 /*don't prompt for authentication when login frame is visible*/
950 linphone_core_abort_authentication(lc,NULL);
954 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
956 gtk_label_set_markup(GTK_LABEL(label),msg);
958 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
959 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
960 g_object_set_data(G_OBJECT(w),"auth_info",info);
961 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
966 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
967 GtkWidget *w=linphone_gtk_get_main_window();
968 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();
1496 //RGB(246^2,249^2,252^2)
1501 gtk_widget_modify_bg(GTK_WIDGET(w), GTK_STATE_NORMAL, &color);
1503 if (!config_loaded){
1504 title=linphone_gtk_get_ui_config("title","Linphone");
1505 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1506 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1507 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1508 //stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1509 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1510 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1511 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1512 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1515 linphone_gtk_configure_window(w,"main_window");
1517 gtk_window_set_title(GTK_WINDOW(w),title);
1519 if (start_call_icon){
1520 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1521 create_pixmap (start_call_icon));
1522 if (!buttons_have_borders)
1523 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1526 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1527 create_pixmap (add_call_icon));
1528 if (!buttons_have_borders)
1529 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1532 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1533 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1534 g_object_unref(G_OBJECT(pbuf));
1538 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1540 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1544 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1546 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1547 g_object_unref(G_OBJECT(pbuf));
1552 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1554 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1557 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1558 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1559 gtk_image_set_from_pixbuf(img,scaled);
1560 g_object_unref(G_OBJECT(scaled));
1561 g_object_unref(G_OBJECT(pbuf));
1564 if (linphone_gtk_can_manage_accounts()) {
1565 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1567 if (update_check_menu){
1568 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1571 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1572 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1573 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1574 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1575 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1579 void linphone_gtk_manage_login(void){
1580 LinphoneCore *lc=linphone_gtk_get_core();
1581 LinphoneProxyConfig *cfg=NULL;
1582 linphone_core_get_default_proxy(lc,&cfg);
1584 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1585 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1586 linphone_gtk_show_login_frame(cfg);
1592 gboolean linphone_gtk_close(GtkWidget *mw){
1593 /*shutdown calls if any*/
1594 LinphoneCore *lc=linphone_gtk_get_core();
1595 if (linphone_core_in_call(lc)){
1596 linphone_core_terminate_all_calls(lc);
1598 linphone_core_enable_video_preview(lc,FALSE);
1599 #ifdef __APPLE__ /*until with have a better option*/
1600 gtk_window_iconify(GTK_WINDOW(mw));
1602 gtk_widget_hide(mw);
1608 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1609 bool_t video_enabled=linphone_gtk_video_enabled();
1610 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1611 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1613 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1614 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1621 static void linphone_gtk_init_main_window(){
1622 GtkWidget *main_window;
1624 linphone_gtk_configure_main_window();
1625 linphone_gtk_manage_login();
1627 linphone_gtk_load_identities();
1628 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1629 linphone_gtk_show_friends();
1630 linphone_gtk_connect_digits();
1631 main_window=linphone_gtk_get_main_window();
1632 linphone_gtk_call_log_update(main_window);
1634 linphone_gtk_update_call_buttons (NULL);
1635 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1636 g_signal_connect (G_OBJECT (main_window), "delete-event",
1637 G_CALLBACK (linphone_gtk_close), main_window);
1640 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1641 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1642 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1643 gtk_widget_hide(menubar);
1644 gtk_osxapplication_ready(theMacApp);
1646 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1648 linphone_gtk_check_menu_items();
1652 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1654 const char *lname="undef";
1656 #if defined(__linux) || defined(__APPLE__)
1657 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1676 g_error("Bad level !");
1678 #if defined(__linux) || defined(__APPLE__)
1680 msg=g_strdup_vprintf(fmt,cap);
1683 msg=g_strdup_vprintf(fmt,args);
1685 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1688 linphone_gtk_log_push(lev,fmt,args);
1692 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1693 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1694 linphone_gtk_get_main_window(), "uribar"));
1696 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1698 gtk_entry_set_text(uri_bar, refer_to);
1699 linphone_gtk_start_call(linphone_gtk_get_main_window());
1702 static void linphone_gtk_check_soundcards(){
1703 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1704 if (devices==NULL || devices[0]==NULL){
1705 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1706 _("No sound cards have been detected on this computer.\n"
1707 "You won't be able to send or receive audio calls."));
1712 // Display the account wizard
1713 void linphone_gtk_display_wizard() {
1714 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1715 the_wizard = linphone_gtk_create_assistant();
1720 static void linphone_gtk_quit(void){
1721 static gboolean quit_done=FALSE;
1724 linphone_gtk_unmonitor_usb();
1725 g_source_remove_by_user_data(linphone_gtk_get_core());
1726 linphone_gtk_uninit_instance();
1727 linphone_gtk_destroy_log_window();
1728 linphone_core_destroy(the_core);
1729 linphone_gtk_log_uninit();
1733 gdk_threads_leave();
1739 This is not the correct way to implement block termination.
1740 The good way would be to call gtk_main_quit(), and return TRUE.
1741 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1742 As a result the program cannot exit at all.
1743 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1745 static gboolean on_block_termination(void){
1747 linphone_gtk_quit();
1752 int main(int argc, char *argv[]){
1757 const char *factory_config_file;
1759 GtkSettings *settings;
1761 const char *app_name="Linphone";
1763 #if !GLIB_CHECK_VERSION(2, 31, 0)
1764 g_thread_init(NULL);
1768 progpath = strdup(argv[0]);
1770 config_file=linphone_gtk_get_config_file(NULL);
1774 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1775 if ((lang=getenv("LANG"))!=NULL){
1778 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1784 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1787 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1790 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1792 if (strncmp(lang,"zh",2)==0){
1793 workaround_gtk_entry_chinese_bug=TRUE;
1796 setenv("LANG",lang,1);
1801 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1802 if (p==NULL) perror("bindtextdomain failed");
1803 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1804 textdomain (GETTEXT_PACKAGE);
1806 g_message("NLS disabled.\n");
1809 gtk_rc_add_default_file("./gtkrc");
1811 gdk_threads_enter();
1813 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1814 linphone_options,NULL,NULL)){
1815 gdk_threads_leave();
1819 settings=gtk_settings_get_default();
1820 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1821 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1822 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1823 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1825 if (workingdir!=NULL){
1826 if (chdir(workingdir)==-1){
1827 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1831 /* Now, look for the factory configuration file, we do it this late
1832 since we want to have had time to change directory and to parse
1833 the options, in case we needed to access the working directory */
1834 factory_config_file = linphone_gtk_get_factory_config_file();
1836 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1837 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1838 g_warning("This instance is going to exit now.");
1839 gdk_threads_leave();
1843 add_pixmap_directory("pixmaps");
1844 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1847 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1848 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1849 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1850 /*never block termination:*/
1851 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1854 the_ui=linphone_gtk_create_window("main");
1856 linphone_gtk_create_log_window();
1857 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1859 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1861 g_set_application_name(app_name);
1862 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1863 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1865 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1866 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1867 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1868 linphone_gtk_init_main_window();
1871 // Veryfing if at least one sip account is configured. If not, show wizard
1872 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1873 linphone_gtk_display_wizard();
1877 #ifndef HAVE_GTK_OSX
1878 linphone_gtk_init_status_icon();
1881 linphone_gtk_show_main_window();
1882 linphone_gtk_check_soundcards();
1884 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1885 linphone_gtk_check_for_new_version();
1886 linphone_gtk_monitor_usb();
1889 linphone_gtk_quit();
1890 #ifndef HAVE_GTK_OSX
1891 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1892 gtk_status_icon_set_visible(icon,FALSE);