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);
282 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
283 g_object_unref(G_OBJECT(pbuf));
288 static int get_ui_file(const char *name, char *path, int pathsize){
289 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
290 if (access(path,F_OK)!=0){
291 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
292 if (access(path,F_OK)!=0){
293 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
294 INSTALLED_XML_DIR,name);
301 GtkWidget *linphone_gtk_create_window(const char *window_name){
302 GError* error = NULL;
303 GtkBuilder* builder = gtk_builder_new ();
307 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
309 if (!gtk_builder_add_from_file (builder, path, &error)){
310 g_error("Couldn't load builder file: %s", error->message);
311 g_error_free (error);
314 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
316 g_error("Could not retrieve '%s' window from xml file",window_name);
319 g_object_set_data(G_OBJECT(w),"builder",builder);
320 gtk_builder_connect_signals(builder,w);
321 linphone_gtk_configure_window(w,window_name);
325 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
328 GtkBuilder* builder = gtk_builder_new ();
330 gchar *object_ids[2];
331 object_ids[0]=g_strdup(widget_name);
334 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
335 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
336 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
337 g_error_free (error);
338 g_free(object_ids[0]);
341 g_free(object_ids[0]);
342 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
344 g_error("Could not retrieve '%s' window from xml file",widget_name);
347 g_object_set_data(G_OBJECT(w),"builder",builder);
348 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
349 gtk_builder_connect_signals(builder,w);
353 static void entry_unmapped(GtkWidget *entry){
354 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
355 gtk_widget_unrealize(entry);
358 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
361 if (window==NULL) return NULL;
362 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
364 g_error("Fail to retrieve builder from window !");
367 w=gtk_builder_get_object(builder,name);
369 g_error("No widget named %s found in xml interface.",name);
371 if (workaround_gtk_entry_chinese_bug){
372 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
373 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
374 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
375 g_message("%s is a GtkEntry",name);
376 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
380 return GTK_WIDGET(w);
384 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
386 GtkWidget *main_window=linphone_gtk_get_main_window();
388 gtk_widget_show(main_window);
389 if (type==GTK_MESSAGE_QUESTION)
391 /* draw a question box. link to dialog_click callback */
392 dialog = gtk_message_dialog_new (
393 GTK_WINDOW(main_window),
394 GTK_DIALOG_DESTROY_WITH_PARENT,
395 GTK_MESSAGE_QUESTION,
398 (const gchar*)message);
399 /* connect to some callback : REVISIT */
401 g_signal_connect_swapped (G_OBJECT (dialog), "response",
402 G_CALLBACK (dialog_click),
405 /* actually show the box */
406 gtk_widget_show(dialog);
410 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
411 GTK_DIALOG_DESTROY_WITH_PARENT,
415 (const gchar*)message);
416 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
417 g_signal_connect_swapped (G_OBJECT (dialog), "response",
418 G_CALLBACK (gtk_widget_destroy),
420 gtk_widget_show(dialog);
424 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
425 if (id==GTK_RESPONSE_CANCEL){
426 gtk_widget_destroy(GTK_WIDGET(dialog));
430 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
431 g_message("About url clicked");
432 linphone_gtk_open_browser(url);
435 void linphone_gtk_show_about(){
436 struct stat filestat;
437 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
440 GdkPixbuf *logo=create_pixbuf(
441 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
442 static const char *defcfg="defcfg";
444 about=linphone_gtk_create_window("about");
445 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
446 memset(&filestat,0,sizeof(filestat));
447 if (stat(license_file,&filestat)!=0){
448 license_file="COPYING";
449 stat(license_file,&filestat);
451 if (filestat.st_size>0){
452 char *license=g_malloc(filestat.st_size+1);
453 FILE *f=fopen(license_file,"r");
454 if (f && fread(license,filestat.st_size,1,f)==1){
455 license[filestat.st_size]='\0';
456 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
460 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
461 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
462 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
463 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
464 tmp=linphone_gtk_get_ui_config("artists",defcfg);
469 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
471 tmp=linphone_gtk_get_ui_config("translators",defcfg);
473 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
474 tmp=linphone_gtk_get_ui_config("comments",defcfg);
476 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
477 gtk_widget_show(about);
480 static void set_video_window_decorations(GdkWindow *w){
481 const char *title=linphone_gtk_get_ui_config("title","Linphone");
482 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
483 char video_title[256];
484 GdkPixbuf *pbuf=create_pixbuf(icon_path);
486 if (!linphone_core_in_call(linphone_gtk_get_core())){
487 snprintf(video_title,sizeof(video_title),"%s video",title);
488 /* When not in call, treat the video as a normal window */
489 gdk_window_set_keep_above(w, FALSE);
491 LinphoneAddress *uri =
492 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
495 linphone_address_clean(uri);
496 if (linphone_address_get_display_name(uri)!=NULL){
497 display_name=ms_strdup(linphone_address_get_display_name(uri));
499 display_name=linphone_address_as_string(uri);
501 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
502 linphone_address_destroy(uri);
503 ms_free(display_name);
505 /* During calls, bring up the video window, arrange so that
506 it is above all the other windows */
507 gdk_window_deiconify(w);
508 gdk_window_set_keep_above(w,TRUE);
509 /* Maybe we should have the following, but then we want to
510 have a timer that turns it off after a little while. */
511 /* gdk_window_set_urgency_hint(w,TRUE); */
513 gdk_window_set_title(w,video_title);
514 /* Refrain the video window to be closed at all times. */
515 gdk_window_set_functions(w,
516 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
517 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
520 l=g_list_append(l,pbuf);
521 gdk_window_set_icon_list(w,l);
523 g_object_unref(G_OBJECT(pbuf));
527 static gboolean video_needs_update=FALSE;
529 static void update_video_title(){
530 video_needs_update=TRUE;
533 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
534 static gboolean first_time=TRUE;
536 static unsigned long previd=0;
537 static unsigned long preview_previd=0;
538 static gboolean in_iterate=FALSE;
541 if (in_iterate) return TRUE;
543 linphone_core_iterate(lc);
545 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
546 linphone_gtk_show_directory_search();
550 id=linphone_core_get_native_video_window_id(lc);
551 if (id!=previd || video_needs_update){
555 ms_message("Updating window decorations");
557 w=gdk_window_foreign_new(id);
559 w=gdk_window_foreign_new((HANDLE)id);
562 set_video_window_decorations(w);
563 g_object_unref(G_OBJECT(w));
565 else ms_error("gdk_window_foreign_new() failed");
566 if (video_needs_update) video_needs_update=FALSE;
569 id=linphone_core_get_native_preview_window_id (lc);
570 if (id!=preview_previd ){
574 ms_message("Updating window decorations for preview");
576 w=gdk_window_foreign_new(id);
578 w=gdk_window_foreign_new((HANDLE)id);
581 set_video_window_decorations(w);
582 g_object_unref(G_OBJECT(w));
584 else ms_error("gdk_window_foreign_new() failed");
585 if (video_needs_update) video_needs_update=FALSE;
588 if (addr_to_call!=NULL){
589 /*make sure we are not showing the login screen*/
590 GtkWidget *mw=linphone_gtk_get_main_window();
591 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
592 if (!GTK_WIDGET_VISIBLE(login_frame)){
593 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
594 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
596 linphone_gtk_start_call(uri_bar);
603 static void load_uri_history(){
604 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
607 GtkEntryCompletion *gep=gtk_entry_completion_new();
608 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
611 snprintf(key,sizeof(key),"uri%i",i);
612 uri=linphone_gtk_get_ui_config(key,NULL);
615 gtk_list_store_append(model,&iter);
616 gtk_list_store_set(model,&iter,0,uri,-1);
617 if (i==0) gtk_entry_set_text(uribar,uri);
621 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
622 gtk_entry_completion_set_text_column(gep,0);
623 gtk_entry_set_completion(uribar,gep);
626 static void save_uri_history(){
627 LinphoneCore *lc=linphone_gtk_get_core();
628 LpConfig *cfg=linphone_core_get_config(lc);
629 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
634 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
636 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
638 gtk_tree_model_get(model,&iter,0,&uri,-1);
640 snprintf(key,sizeof(key),"uri%i",i);
641 lp_config_set_string(cfg,"GtkUi",key,uri);
646 }while(gtk_tree_model_iter_next(model,&iter));
650 static void completion_add_text(GtkEntry *entry, const char *text){
652 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
654 if (gtk_tree_model_get_iter_first(model,&iter)){
657 gtk_tree_model_get(model,&iter,0,&uri,-1);
659 if (strcmp(uri,text)==0) {
661 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
667 }while (gtk_tree_model_iter_next(model,&iter));
669 /* and prepend it on top of the list */
670 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
671 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
676 bool_t linphone_gtk_video_enabled(void){
677 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
678 return vpol->automatically_accept && vpol->automatically_initiate;
681 void linphone_gtk_show_main_window(){
682 GtkWidget *w=linphone_gtk_get_main_window();
683 LinphoneCore *lc=linphone_gtk_get_core();
684 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
685 VIDEOSELFVIEW_DEFAULT));
687 gtk_window_present(GTK_WINDOW(w));
690 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
691 GtkWidget *mw=linphone_gtk_get_main_window();
692 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
693 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
695 if (linphone_gtk_use_in_call_view() && call)
696 linphone_gtk_in_call_view_terminate(call,error);
697 update_video_title();
700 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
701 LinphoneCore *lc=linphone_gtk_get_core();
702 GtkWidget *mw=linphone_gtk_get_main_window();
703 const MSList *calls=linphone_core_get_calls(lc);
705 bool_t start_active=TRUE;
706 //bool_t stop_active=FALSE;
707 bool_t add_call=FALSE;
708 int call_list_size=ms_list_size(calls);
718 button=linphone_gtk_get_widget(mw,"start_call");
719 gtk_widget_set_sensitive(button,start_active);
720 gtk_widget_set_visible(button,!add_call);
722 button=linphone_gtk_get_widget(mw,"add_call");
723 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
724 gtk_widget_set_sensitive(button,FALSE);
726 gtk_widget_set_sensitive(button,start_active);
728 gtk_widget_set_visible(button,add_call);
730 //gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
732 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
733 linphone_gtk_enable_conference_button(lc,call_list_size>1);
734 update_video_title();
735 if (call) linphone_gtk_update_video_button(call);
738 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
739 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
740 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
741 completion_add_text(GTK_ENTRY(uri_bar),entered);
743 linphone_gtk_call_terminated(NULL,NULL);
748 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
749 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
750 linphone_core_accept_call (linphone_gtk_get_core(),call);
751 linphone_call_unref(call);
757 void linphone_gtk_start_call(GtkWidget *w){
758 LinphoneCore *lc=linphone_gtk_get_core();
760 /*change into in-call mode, then do the work later as it might block a bit */
761 GtkWidget *mw=gtk_widget_get_toplevel(w);
762 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
764 call=linphone_gtk_get_currently_displayed_call(NULL);
765 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
766 linphone_core_accept_call(lc,call);
768 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
769 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
770 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
771 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
776 void linphone_gtk_uri_bar_activate(GtkWidget *w){
777 linphone_gtk_start_call(w);
781 void linphone_gtk_terminate_call(GtkWidget *button){
783 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
785 linphone_core_terminate_call(linphone_gtk_get_core(),call);
787 linphone_core_terminate_conference(linphone_gtk_get_core());
791 void linphone_gtk_decline_clicked(GtkWidget *button){
792 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
794 linphone_core_terminate_call(linphone_gtk_get_core(),call);
797 void linphone_gtk_answer_clicked(GtkWidget *button){
798 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
800 linphone_core_accept_call(linphone_gtk_get_core(),call);
801 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
805 void _linphone_gtk_enable_video(gboolean val){
806 LinphoneVideoPolicy policy={0};
807 policy.automatically_initiate=policy.automatically_accept=val;
808 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
809 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
812 linphone_core_enable_video_preview(linphone_gtk_get_core(),
813 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
815 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
819 void linphone_gtk_enable_video(GtkWidget *w){
820 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
821 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
822 _linphone_gtk_enable_video(val);
825 void linphone_gtk_enable_self_view(GtkWidget *w){
826 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
827 LinphoneCore *lc=linphone_gtk_get_core();
828 linphone_core_enable_video_preview(lc,val);
829 linphone_core_enable_self_view(lc,val);
830 linphone_gtk_set_ui_config_int("videoselfview",val);
833 void linphone_gtk_used_identity_changed(GtkWidget *w){
834 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
835 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
836 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
837 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
838 linphone_gtk_show_directory_search();
840 if (sel) g_free(sel);
844 void on_proxy_refresh_button_clicked(GtkWidget *w){
845 LinphoneCore *lc=linphone_gtk_get_core();
846 MSList const *item=linphone_core_get_proxy_config_list(lc);
847 while (item != NULL) {
848 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
849 linphone_proxy_config_edit(lpc);
850 linphone_proxy_config_done(lpc);
855 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
856 linphone_gtk_show_friends();
859 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
861 case GTK_RESPONSE_YES:
862 linphone_gtk_show_contact(lf);
865 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
867 gtk_widget_destroy(dialog);
870 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
873 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
874 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
878 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);
879 dialog = gtk_message_dialog_new (
880 GTK_WINDOW(linphone_gtk_get_main_window()),
881 GTK_DIALOG_DESTROY_WITH_PARENT,
882 GTK_MESSAGE_QUESTION,
887 g_signal_connect(G_OBJECT (dialog), "response",
888 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
889 /* actually show the box */
890 gtk_widget_show(dialog);
893 typedef struct _AuthTimeout{
898 static void auth_timeout_clean(AuthTimeout *tout){
902 static gboolean auth_timeout_destroy(AuthTimeout *tout){
904 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
905 gtk_widget_destroy(tout->w);
911 static AuthTimeout * auth_timeout_new(GtkWidget *w){
912 AuthTimeout *tout=g_new(AuthTimeout,1);
914 /*so that the timeout no more references the widget when it is destroyed:*/
915 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
916 /*so that the widget is automatically destroyed after some time */
917 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
921 void linphone_gtk_password_cancel(GtkWidget *w){
922 LinphoneAuthInfo *info;
923 GtkWidget *window=gtk_widget_get_toplevel(w);
924 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
925 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
926 gtk_widget_destroy(window);
929 void linphone_gtk_password_ok(GtkWidget *w){
931 GtkWidget *window=gtk_widget_get_toplevel(w);
932 LinphoneAuthInfo *info;
933 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
934 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
935 entry=linphone_gtk_get_widget(window,"password_entry");
936 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
937 linphone_auth_info_set_userid(info,
938 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
939 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
940 gtk_widget_destroy(window);
943 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
944 GtkWidget *w=linphone_gtk_create_window("password");
945 GtkWidget *label=linphone_gtk_get_widget(w,"message");
946 LinphoneAuthInfo *info;
948 GtkWidget *mw=linphone_gtk_get_main_window();
950 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
951 /*don't prompt for authentication when login frame is visible*/
952 linphone_core_abort_authentication(lc,NULL);
956 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
958 gtk_label_set_markup(GTK_LABEL(label),msg);
960 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
961 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
962 g_object_set_data(G_OBJECT(w),"auth_info",info);
963 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
968 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
969 GtkWidget *w=linphone_gtk_get_main_window();
970 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
972 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
973 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
977 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
978 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
981 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
982 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
985 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
987 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
988 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
991 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
992 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
993 if (w) linphone_gtk_call_log_update(w);
994 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
998 static bool_t notify_actions_supported() {
999 bool_t accepts_actions = FALSE;
1000 GList *capabilities = notify_get_server_caps();
1002 if(capabilities != NULL) {
1003 for(c = capabilities; c != NULL; c = c->next) {
1004 if(strcmp((char*)c->data, "actions") == 0 ) {
1005 accepts_actions = TRUE;
1009 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1010 g_list_free(capabilities);
1012 return accepts_actions;
1015 static NotifyNotification* build_notification(const char *title, const char *body){
1016 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1023 static void show_notification(NotifyNotification* n){
1024 if (n && !notify_notification_show(n,NULL))
1025 ms_error("Failed to send notification.");
1028 static void make_notification(const char *title, const char *body){
1029 show_notification(build_notification(title,body));
1034 void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1036 if (!notify_is_initted())
1037 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1042 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1048 ms_error("Failed to send notification.");
1050 linphone_gtk_show_main_window();
1052 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1055 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1056 NotifyNotification *n;
1057 switch(linphone_call_get_state(call)){
1058 case LinphoneCallError:
1059 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1061 case LinphoneCallEnd:
1062 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1064 case LinphoneCallIncomingReceived:
1065 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1066 if (notify_actions_supported()) {
1067 notify_notification_add_action (n,"answer", _("Answer"),
1068 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1069 notify_notification_add_action (n,"decline",_("Decline"),
1070 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1072 show_notification(n);
1074 case LinphoneCallPausedByRemote:
1075 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1080 if (body) g_free(body);
1081 if (remote) g_free(remote);
1086 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1087 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1088 LinphoneCore *lc=linphone_call_get_core(call);
1089 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1090 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1091 linphone_core_accept_call_update(lc,call,params);
1092 linphone_call_params_destroy(params);
1094 linphone_call_unref(call);
1095 g_source_remove_by_user_data(dialog);
1096 gtk_widget_destroy(dialog);
1099 static void on_call_updated_timeout(GtkWidget *dialog){
1100 gtk_widget_destroy(dialog);
1103 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1104 LinphoneCore *lc=linphone_call_get_core(call);
1105 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1106 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1107 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1108 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1109 gboolean video_used=linphone_call_params_video_enabled(current_params);
1110 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1111 video_used,video_requested,pol->automatically_accept);
1112 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1113 linphone_core_defer_call_update(lc,call);
1115 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1117 const char *dname=linphone_address_get_display_name(addr);
1118 if (dname==NULL) dname=linphone_address_get_username(addr);
1119 if (dname==NULL) dname=linphone_address_get_domain(addr);
1120 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1121 GTK_DIALOG_DESTROY_WITH_PARENT,
1122 GTK_MESSAGE_WARNING,
1124 _("%s proposed to start video. Do you accept ?"),dname);
1125 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1126 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1127 gtk_widget_show(dialog);
1132 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1134 case LinphoneCallOutgoingInit:
1135 linphone_gtk_create_in_call_view (call);
1137 case LinphoneCallOutgoingProgress:
1138 linphone_gtk_in_call_view_set_calling (call);
1140 case LinphoneCallStreamsRunning:
1141 linphone_gtk_in_call_view_set_in_call(call);
1143 case LinphoneCallUpdatedByRemote:
1144 linphone_gtk_call_updated_by_remote(call);
1146 case LinphoneCallError:
1147 linphone_gtk_in_call_view_terminate (call,msg);
1149 case LinphoneCallEnd:
1150 linphone_gtk_in_call_view_terminate(call,NULL);
1151 linphone_gtk_status_icon_set_blinking(FALSE);
1153 case LinphoneCallIncomingReceived:
1154 linphone_gtk_create_in_call_view(call);
1155 linphone_gtk_in_call_view_set_incoming(call);
1156 linphone_gtk_status_icon_set_blinking(TRUE);
1158 linphone_call_ref(call);
1159 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1162 case LinphoneCallResuming:
1163 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1164 linphone_gtk_in_call_view_set_in_call (call);
1166 case LinphoneCallPausing:
1167 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1168 case LinphoneCallPausedByRemote:
1169 linphone_gtk_in_call_view_set_paused(call);
1171 case LinphoneCallConnected:
1172 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1173 linphone_gtk_status_icon_set_blinking(FALSE);
1178 linphone_gtk_notify(call, msg);
1179 linphone_gtk_update_call_buttons (call);
1182 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1183 linphone_gtk_in_call_view_show_encryption(call);
1186 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1187 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1190 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1191 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1192 GtkTreeModel *model=gtk_combo_box_get_model(box);
1194 gboolean found=FALSE;
1195 const char *stock_id=NULL;
1197 if (gtk_tree_model_get_iter_first(model,&iter)){
1200 gtk_tree_model_get(model,&iter,2,&p,-1);
1205 }while(gtk_tree_model_iter_next(model,&iter));
1208 g_warning("Could not find proxy config in combo box of identities.");
1212 case LinphoneRegistrationOk:
1213 stock_id=GTK_STOCK_YES;
1215 case LinphoneRegistrationProgress:
1216 stock_id=GTK_STOCK_REFRESH;
1218 case LinphoneRegistrationCleared:
1221 case LinphoneRegistrationFailed:
1222 stock_id=GTK_STOCK_DIALOG_WARNING;
1227 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1230 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1231 LinphoneRegistrationState rs, const char *msg){
1233 case LinphoneRegistrationOk:
1235 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1236 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1237 linphone_gtk_exit_login_frame();
1244 update_registration_status(cfg,rs);
1247 void linphone_gtk_open_browser(const char *url){
1248 /*in gtk 2.16, gtk_show_uri does not work...*/
1250 #if GTK_CHECK_VERSION(2,18,3)
1251 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1254 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1255 g_spawn_command_line_async(cl,NULL);
1258 ShellExecute(0,"open",url,NULL,NULL,1);
1262 void linphone_gtk_link_to_website(GtkWidget *item){
1263 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1264 linphone_gtk_open_browser(home);
1267 #ifndef HAVE_GTK_OSX
1269 static GtkStatusIcon *icon=NULL;
1271 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1272 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1273 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1276 static GtkWidget *create_icon_menu(){
1277 GtkWidget *menu=gtk_menu_new();
1278 GtkWidget *menu_item;
1281 const gchar *homesite;
1283 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1284 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1285 tmp=g_strdup(homesite);
1286 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1287 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1289 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1290 gtk_widget_show(image);
1291 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1292 //g_object_unref(G_OBJECT(image));
1293 gtk_widget_show(menu_item);
1294 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1295 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1297 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,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)linphone_gtk_show_about,NULL);
1301 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1302 gtk_widget_show(menu_item);
1303 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1304 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1305 gtk_widget_show(menu);
1309 static void handle_icon_click() {
1310 GtkWidget *mw=linphone_gtk_get_main_window();
1311 if (!gtk_window_is_active((GtkWindow*)mw)) {
1312 linphone_gtk_show_main_window();
1314 gtk_widget_hide(mw);
1318 static void linphone_gtk_init_status_icon(){
1319 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1320 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1321 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1322 GtkWidget *menu=create_icon_menu();
1324 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1325 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1326 #if GTK_CHECK_VERSION(2,20,0)
1327 gtk_status_icon_set_name(icon,title);
1329 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1330 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1331 gtk_status_icon_set_tooltip(icon,title);
1332 gtk_status_icon_set_visible(icon,TRUE);
1333 g_object_set_data(G_OBJECT(icon),"menu",menu);
1334 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1335 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1336 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1337 pbuf=create_pixbuf(call_icon_path);
1338 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1341 static gboolean do_icon_blink(GtkStatusIcon *gi){
1342 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1343 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1344 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1345 if (cur_icon==call_icon){
1346 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1348 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1355 void linphone_gtk_status_icon_set_blinking(gboolean val){
1357 static gint attention_id;
1358 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1360 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1361 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1365 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1366 if (val && tout==0){
1367 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1368 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1369 }else if (!val && tout!=0){
1370 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1371 g_source_remove(tout);
1372 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1373 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1379 void linphone_gtk_options_activate(GtkWidget *item){
1380 #ifndef HAVE_GTK_OSX
1381 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1386 static void init_identity_combo(GtkComboBox *box){
1387 GtkListStore *store;
1388 GtkCellRenderer *r1,*r2;
1389 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1390 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1391 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1392 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1393 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1394 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1395 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1396 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1399 void linphone_gtk_load_identities(void){
1401 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1403 LinphoneProxyConfig *def=NULL;
1405 GtkListStore *store;
1408 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1409 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1410 /* model is empty, this is the first time we go here */
1411 init_identity_combo(box);
1412 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1414 gtk_list_store_clear(store);
1415 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1416 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1417 gtk_list_store_append(store,&iter);
1418 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1419 g_free(def_identity);
1420 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1422 elem=ms_list_next(elem),i++){
1423 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1424 gtk_list_store_append(store,&iter);
1425 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1426 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1432 gtk_combo_box_set_active(box,def_index);
1435 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1436 const char *label=gtk_button_get_label(button);
1437 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1439 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1440 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1441 if (linphone_core_in_call(linphone_gtk_get_core())){
1442 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1446 static void linphone_gtk_dtmf_released(GtkButton *button){
1447 linphone_core_stop_dtmf (linphone_gtk_get_core());
1450 static void linphone_gtk_connect_digits(void){
1451 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1452 GList *children=gtk_container_get_children(cont);
1454 for(elem=children;elem!=NULL;elem=elem->next){
1455 GtkButton *button=GTK_BUTTON(elem->data);
1456 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1457 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1461 static void linphone_gtk_check_menu_items(void){
1462 bool_t video_enabled=linphone_gtk_video_enabled();
1463 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1464 GtkWidget *selfview_item=linphone_gtk_get_widget(
1465 linphone_gtk_get_main_window(),"selfview_item");
1466 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1467 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1468 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1471 static gboolean linphone_gtk_can_manage_accounts(){
1472 LinphoneCore *lc=linphone_gtk_get_core();
1474 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1475 SipSetup *ss=(SipSetup*)elem->data;
1476 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1483 static void linphone_gtk_configure_main_window(){
1484 static gboolean config_loaded=FALSE;
1485 static const char *title;
1486 static const char *home;
1487 static const char *start_call_icon;
1488 static const char *add_call_icon;
1489 //static const char *stop_call_icon;
1490 static const char *search_icon;
1491 static gboolean update_check_menu;
1492 static gboolean buttons_have_borders;
1493 static gboolean show_abcd;
1494 GtkWidget *w=linphone_gtk_get_main_window();
1498 //RGB(246^2,249^2,252^2)
1503 gtk_widget_modify_bg(GTK_WIDGET(w), GTK_STATE_NORMAL, &color);
1505 if (!config_loaded){
1506 title=linphone_gtk_get_ui_config("title","Linphone");
1507 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1508 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1509 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1510 //stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1511 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1512 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1513 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1514 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1517 linphone_gtk_configure_window(w,"main_window");
1519 gtk_window_set_title(GTK_WINDOW(w),title);
1521 if (start_call_icon){
1522 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1523 create_pixmap (start_call_icon));
1524 if (!buttons_have_borders)
1525 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1528 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1529 create_pixmap (add_call_icon));
1530 if (!buttons_have_borders)
1531 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1534 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1536 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1537 g_object_unref(G_OBJECT(pbuf));
1542 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1544 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1548 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1550 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1551 g_object_unref(G_OBJECT(pbuf));
1556 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1558 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1561 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1562 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1563 gtk_image_set_from_pixbuf(img,scaled);
1564 g_object_unref(G_OBJECT(scaled));
1565 g_object_unref(G_OBJECT(pbuf));
1568 if (linphone_gtk_can_manage_accounts()) {
1569 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1571 if (update_check_menu){
1572 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1575 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1576 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1577 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1578 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1579 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1583 void linphone_gtk_manage_login(void){
1584 LinphoneCore *lc=linphone_gtk_get_core();
1585 LinphoneProxyConfig *cfg=NULL;
1586 linphone_core_get_default_proxy(lc,&cfg);
1588 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1589 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1590 linphone_gtk_show_login_frame(cfg);
1596 gboolean linphone_gtk_close(GtkWidget *mw){
1597 /*shutdown calls if any*/
1598 LinphoneCore *lc=linphone_gtk_get_core();
1599 if (linphone_core_in_call(lc)){
1600 linphone_core_terminate_all_calls(lc);
1602 linphone_core_enable_video_preview(lc,FALSE);
1603 #ifdef __APPLE__ /*until with have a better option*/
1604 gtk_window_iconify(GTK_WINDOW(mw));
1606 gtk_widget_hide(mw);
1612 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1613 bool_t video_enabled=linphone_gtk_video_enabled();
1614 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1615 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1617 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1618 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1625 static void linphone_gtk_init_main_window(){
1626 GtkWidget *main_window;
1628 linphone_gtk_configure_main_window();
1629 linphone_gtk_manage_login();
1631 linphone_gtk_load_identities();
1632 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1633 linphone_gtk_show_friends();
1634 linphone_gtk_connect_digits();
1635 main_window=linphone_gtk_get_main_window();
1636 linphone_gtk_call_log_update(main_window);
1638 linphone_gtk_update_call_buttons (NULL);
1639 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1640 g_signal_connect (G_OBJECT (main_window), "delete-event",
1641 G_CALLBACK (linphone_gtk_close), main_window);
1644 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1645 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1646 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1647 gtk_widget_hide(menubar);
1648 gtk_osxapplication_ready(theMacApp);
1650 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1652 linphone_gtk_check_menu_items();
1656 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1658 const char *lname="undef";
1660 #if defined(__linux) || defined(__APPLE__)
1661 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1680 g_error("Bad level !");
1682 #if defined(__linux) || defined(__APPLE__)
1684 msg=g_strdup_vprintf(fmt,cap);
1687 msg=g_strdup_vprintf(fmt,args);
1689 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1692 linphone_gtk_log_push(lev,fmt,args);
1696 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1697 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1698 linphone_gtk_get_main_window(), "uribar"));
1700 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1702 gtk_entry_set_text(uri_bar, refer_to);
1703 linphone_gtk_start_call(linphone_gtk_get_main_window());
1706 static void linphone_gtk_check_soundcards(){
1707 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1708 if (devices==NULL || devices[0]==NULL){
1709 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1710 _("No sound cards have been detected on this computer.\n"
1711 "You won't be able to send or receive audio calls."));
1716 // Display the account wizard
1717 void linphone_gtk_display_wizard() {
1718 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1719 the_wizard = linphone_gtk_create_assistant();
1724 static void linphone_gtk_quit(void){
1725 static gboolean quit_done=FALSE;
1728 linphone_gtk_unmonitor_usb();
1729 g_source_remove_by_user_data(linphone_gtk_get_core());
1730 linphone_gtk_uninit_instance();
1731 linphone_gtk_destroy_log_window();
1732 linphone_core_destroy(the_core);
1733 linphone_gtk_log_uninit();
1737 gdk_threads_leave();
1743 This is not the correct way to implement block termination.
1744 The good way would be to call gtk_main_quit(), and return TRUE.
1745 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1746 As a result the program cannot exit at all.
1747 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1749 static gboolean on_block_termination(void){
1751 linphone_gtk_quit();
1756 int main(int argc, char *argv[]){
1761 const char *factory_config_file;
1763 GtkSettings *settings;
1765 const char *app_name="Linphone";
1767 #if !GLIB_CHECK_VERSION(2, 31, 0)
1768 g_thread_init(NULL);
1772 progpath = strdup(argv[0]);
1774 config_file=linphone_gtk_get_config_file(NULL);
1778 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1779 if ((lang=getenv("LANG"))!=NULL){
1782 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1788 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1791 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1794 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1796 if (strncmp(lang,"zh",2)==0){
1797 workaround_gtk_entry_chinese_bug=TRUE;
1800 setenv("LANG",lang,1);
1805 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1806 if (p==NULL) perror("bindtextdomain failed");
1807 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1808 textdomain (GETTEXT_PACKAGE);
1810 g_message("NLS disabled.\n");
1813 gtk_rc_add_default_file("./gtkrc");
1815 gdk_threads_enter();
1817 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1818 linphone_options,NULL,NULL)){
1819 gdk_threads_leave();
1823 settings=gtk_settings_get_default();
1824 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1825 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1826 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1827 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1829 if (workingdir!=NULL){
1830 if (chdir(workingdir)==-1){
1831 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1835 /* Now, look for the factory configuration file, we do it this late
1836 since we want to have had time to change directory and to parse
1837 the options, in case we needed to access the working directory */
1838 factory_config_file = linphone_gtk_get_factory_config_file();
1840 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1841 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1842 g_warning("This instance is going to exit now.");
1843 gdk_threads_leave();
1847 add_pixmap_directory("pixmaps");
1848 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1851 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1852 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1853 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1854 /*never block termination:*/
1855 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1858 the_ui=linphone_gtk_create_window("main");
1860 linphone_gtk_create_log_window();
1861 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1863 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1865 g_set_application_name(app_name);
1866 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1867 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1869 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1870 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1871 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1872 linphone_gtk_init_main_window();
1875 // Veryfing if at least one sip account is configured. If not, show wizard
1876 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1877 linphone_gtk_display_wizard();
1881 #ifndef HAVE_GTK_OSX
1882 linphone_gtk_init_status_icon();
1885 linphone_gtk_show_main_window();
1886 linphone_gtk_check_soundcards();
1888 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1889 linphone_gtk_check_for_new_version();
1890 linphone_gtk_monitor_usb();
1893 linphone_gtk_quit();
1894 #ifndef HAVE_GTK_OSX
1895 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1896 gtk_status_icon_set_visible(icon,FALSE);