2 linphone, gtk-glade interface.
3 Copyright (C) 2008 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define VIDEOSELFVIEW_DEFAULT 0
27 #include <sys/types.h>
32 #include <gtkosxapplication.h>
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53 GtkWidget *the_wizard=NULL;
55 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
66 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
67 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
68 void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
73 static gboolean verbose=0;
74 static gboolean auto_answer = 0;
75 static gchar * addr_to_call = NULL;
76 static gboolean no_video=FALSE;
77 static gboolean iconified=FALSE;
78 static gchar *workingdir=NULL;
79 static char *progpath=NULL;
80 gchar *linphone_logfile=NULL;
81 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
83 static GOptionEntry linphone_options[]={
87 .arg=G_OPTION_ARG_NONE,
88 .arg_data= (gpointer)&verbose,
89 .description=N_("log to stdout some debug information while running.")
92 .long_name = "logfile",
94 .arg = G_OPTION_ARG_STRING,
95 .arg_data = &linphone_logfile,
96 .description = N_("path to a file to write logs into.")
99 .long_name = "no-video",
101 .arg = G_OPTION_ARG_NONE,
102 .arg_data = (gpointer)&no_video,
103 .description = N_("Start linphone with video disabled.")
106 .long_name="iconified",
108 .arg=G_OPTION_ARG_NONE,
109 .arg_data= (gpointer)&iconified,
110 .description=N_("Start only in the system tray, do not show the main interface.")
115 .arg = G_OPTION_ARG_STRING,
116 .arg_data = &addr_to_call,
117 .description = N_("address to call right now")
120 .long_name = "auto-answer",
122 .arg = G_OPTION_ARG_NONE,
123 .arg_data = (gpointer) & auto_answer,
124 .description = N_("if set automatically answer incoming calls")
127 .long_name = "workdir",
129 .arg = G_OPTION_ARG_STRING,
130 .arg_data = (gpointer) & workingdir,
131 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
136 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
137 #define RELATIVE_XML_DIR
138 #define BUILD_TREE_XML_DIR "gtk"
141 #define CONFIG_FILE ".linphonerc"
142 #define SECRETS_FILE ".linphone-zidcache"
144 #define CONFIG_FILE "linphonerc"
145 #define SECRETS_FILE "linphone-zidcache"
149 char *linphone_gtk_get_config_file(const char *filename){
150 const int path_max=1024;
151 char *config_file=g_malloc0(path_max);
152 if (filename==NULL) filename=CONFIG_FILE;
153 /*try accessing a local file first if exists*/
154 if (access(CONFIG_FILE,F_OK)==0){
155 snprintf(config_file,path_max,"%s",filename);
158 const char *appdata=getenv("APPDATA");
160 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
161 CreateDirectory(config_file,NULL);
162 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
165 const char *home=getenv("HOME");
166 if (home==NULL) home=".";
167 snprintf(config_file,path_max,"%s/%s",home,filename);
174 #define FACTORY_CONFIG_FILE "linphonerc.factory"
175 static char _factory_config_file[1024];
176 static const char *linphone_gtk_get_factory_config_file(){
177 /*try accessing a local file first if exists*/
178 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
179 snprintf(_factory_config_file,sizeof(_factory_config_file),
180 "%s",FACTORY_CONFIG_FILE);
184 if (progpath != NULL) {
186 progdir = strdup(progpath);
188 basename = strrchr(progdir, '\\');
189 if (basename != NULL) {
192 snprintf(_factory_config_file, sizeof(_factory_config_file),
193 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
195 if (workingdir!=NULL) {
196 snprintf(_factory_config_file, sizeof(_factory_config_file),
197 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
204 basename = strrchr(progdir, '/');
205 if (basename != NULL) {
208 snprintf(_factory_config_file, sizeof(_factory_config_file),
209 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
218 return _factory_config_file;
221 static void linphone_gtk_init_liblinphone(const char *config_file,
222 const char *factory_config_file) {
223 LinphoneCoreVTable vtable={0};
224 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
226 vtable.call_state_changed=linphone_gtk_call_state_changed;
227 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
228 vtable.notify_presence_recv=linphone_gtk_notify_recv;
229 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
230 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
231 vtable.display_status=linphone_gtk_display_status;
232 vtable.display_message=linphone_gtk_display_message;
233 vtable.display_warning=linphone_gtk_display_warning;
234 vtable.display_url=linphone_gtk_display_url;
235 vtable.call_log_updated=linphone_gtk_call_log_updated;
236 vtable.text_received=linphone_gtk_text_received;
237 vtable.refer_received=linphone_gtk_refer_received;
238 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
239 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
240 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
242 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
243 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
244 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
245 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
246 g_free(secrets_file);
247 linphone_core_enable_video(the_core,TRUE,TRUE);
249 _linphone_gtk_enable_video(FALSE);
250 linphone_gtk_set_ui_config_int("videoselfview",0);
256 LinphoneCore *linphone_gtk_get_core(void){
260 GtkWidget *linphone_gtk_get_main_window(){
264 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
265 static const char *icon_path=NULL;
266 static const char *hiddens=NULL;
267 static const char *shown=NULL;
268 static bool_t config_loaded=FALSE;
269 if (linphone_gtk_get_core()==NULL) return;
270 if (config_loaded==FALSE){
271 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
272 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
273 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
277 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
279 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
281 GdkPixbuf *pbuf=create_pixbuf(icon_path);
283 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
284 g_object_unref(G_OBJECT(pbuf));
289 static int get_ui_file(const char *name, char *path, int pathsize){
290 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
291 if (access(path,F_OK)!=0){
292 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
293 if (access(path,F_OK)!=0){
294 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
295 INSTALLED_XML_DIR,name);
302 GtkWidget *linphone_gtk_create_window(const char *window_name){
303 GError* error = NULL;
304 GtkBuilder* builder = gtk_builder_new ();
308 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
310 if (!gtk_builder_add_from_file (builder, path, &error)){
311 g_error("Couldn't load builder file: %s", error->message);
312 g_error_free (error);
315 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
317 g_error("Could not retrieve '%s' window from xml file",window_name);
320 g_object_set_data(G_OBJECT(w),"builder",builder);
321 gtk_builder_connect_signals(builder,w);
322 linphone_gtk_configure_window(w,window_name);
326 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
329 GtkBuilder* builder = gtk_builder_new ();
331 gchar *object_ids[2];
332 object_ids[0]=g_strdup(widget_name);
335 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
336 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
337 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
338 g_error_free (error);
339 g_free(object_ids[0]);
342 g_free(object_ids[0]);
343 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
345 g_error("Could not retrieve '%s' window from xml file",widget_name);
348 g_object_set_data(G_OBJECT(w),"builder",builder);
349 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
350 gtk_builder_connect_signals(builder,w);
354 static void entry_unmapped(GtkWidget *entry){
355 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
356 gtk_widget_unrealize(entry);
359 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
362 if (window==NULL) return NULL;
363 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
365 g_error("Fail to retrieve builder from window !");
368 w=gtk_builder_get_object(builder,name);
370 g_error("No widget named %s found in xml interface.",name);
372 if (workaround_gtk_entry_chinese_bug){
373 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
374 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
375 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
376 g_message("%s is a GtkEntry",name);
377 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
381 return GTK_WIDGET(w);
385 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
387 GtkWidget *main_window=linphone_gtk_get_main_window();
389 gtk_widget_show(main_window);
390 if (type==GTK_MESSAGE_QUESTION)
392 /* draw a question box. link to dialog_click callback */
393 dialog = gtk_message_dialog_new (
394 GTK_WINDOW(main_window),
395 GTK_DIALOG_DESTROY_WITH_PARENT,
396 GTK_MESSAGE_QUESTION,
399 (const gchar*)message);
400 /* connect to some callback : REVISIT */
402 g_signal_connect_swapped (G_OBJECT (dialog), "response",
403 G_CALLBACK (dialog_click),
406 /* actually show the box */
407 gtk_widget_show(dialog);
411 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
412 GTK_DIALOG_DESTROY_WITH_PARENT,
416 (const gchar*)message);
417 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
418 g_signal_connect_swapped (G_OBJECT (dialog), "response",
419 G_CALLBACK (gtk_widget_destroy),
421 gtk_widget_show(dialog);
425 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
426 if (id==GTK_RESPONSE_CANCEL){
427 gtk_widget_destroy(GTK_WIDGET(dialog));
431 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
432 g_message("About url clicked");
433 linphone_gtk_open_browser(url);
436 void linphone_gtk_show_about(){
437 struct stat filestat;
438 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
441 GdkPixbuf *logo=create_pixbuf(
442 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
443 static const char *defcfg="defcfg";
445 about=linphone_gtk_create_window("about");
446 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
447 memset(&filestat,0,sizeof(filestat));
448 if (stat(license_file,&filestat)!=0){
449 license_file="COPYING";
450 stat(license_file,&filestat);
452 if (filestat.st_size>0){
453 char *license=g_malloc(filestat.st_size+1);
454 FILE *f=fopen(license_file,"r");
455 if (f && fread(license,filestat.st_size,1,f)==1){
456 license[filestat.st_size]='\0';
457 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
461 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
462 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
463 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
464 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
465 tmp=linphone_gtk_get_ui_config("artists",defcfg);
470 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
472 tmp=linphone_gtk_get_ui_config("translators",defcfg);
474 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
475 tmp=linphone_gtk_get_ui_config("comments",defcfg);
477 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
478 gtk_widget_show(about);
481 static void set_video_window_decorations(GdkWindow *w){
482 const char *title=linphone_gtk_get_ui_config("title","Linphone");
483 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
484 char video_title[256];
485 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,"terminate_call"),FALSE);
694 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
696 if (linphone_gtk_use_in_call_view() && call)
697 linphone_gtk_in_call_view_terminate(call,error);
698 update_video_title();
701 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
702 LinphoneCore *lc=linphone_gtk_get_core();
703 GtkWidget *mw=linphone_gtk_get_main_window();
704 const MSList *calls=linphone_core_get_calls(lc);
706 bool_t start_active=TRUE;
707 bool_t stop_active=FALSE;
708 bool_t add_call=FALSE;
709 int call_list_size=ms_list_size(calls);
719 button=linphone_gtk_get_widget(mw,"start_call");
720 gtk_widget_set_sensitive(button,start_active);
721 gtk_widget_set_visible(button,!add_call);
723 button=linphone_gtk_get_widget(mw,"add_call");
724 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
725 gtk_widget_set_sensitive(button,FALSE);
727 gtk_widget_set_sensitive(button,start_active);
729 gtk_widget_set_visible(button,add_call);
731 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
733 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
734 linphone_gtk_enable_conference_button(lc,call_list_size>1);
735 update_video_title();
736 if (call) linphone_gtk_update_video_button(call);
739 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
740 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
741 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
742 completion_add_text(GTK_ENTRY(uri_bar),entered);
744 linphone_gtk_call_terminated(NULL,NULL);
749 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
750 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
751 linphone_core_accept_call (linphone_gtk_get_core(),call);
752 linphone_call_unref(call);
758 void linphone_gtk_start_call(GtkWidget *w){
759 LinphoneCore *lc=linphone_gtk_get_core();
761 /*change into in-call mode, then do the work later as it might block a bit */
762 GtkWidget *mw=gtk_widget_get_toplevel(w);
763 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
765 call=linphone_gtk_get_currently_displayed_call(NULL);
766 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
767 linphone_core_accept_call(lc,call);
769 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
770 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
771 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
772 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
777 void linphone_gtk_uri_bar_activate(GtkWidget *w){
778 linphone_gtk_start_call(w);
782 void linphone_gtk_terminate_call(GtkWidget *button){
784 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
786 linphone_core_terminate_call(linphone_gtk_get_core(),call);
788 linphone_core_terminate_conference(linphone_gtk_get_core());
792 void linphone_gtk_decline_clicked(GtkWidget *button){
793 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
795 linphone_core_terminate_call(linphone_gtk_get_core(),call);
798 void linphone_gtk_answer_clicked(GtkWidget *button){
799 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
801 linphone_core_accept_call(linphone_gtk_get_core(),call);
802 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
806 void _linphone_gtk_enable_video(gboolean val){
807 LinphoneVideoPolicy policy={0};
808 policy.automatically_initiate=policy.automatically_accept=val;
809 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
810 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
813 linphone_core_enable_video_preview(linphone_gtk_get_core(),
814 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
816 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
820 void linphone_gtk_enable_video(GtkWidget *w){
821 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
822 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
823 _linphone_gtk_enable_video(val);
826 void linphone_gtk_enable_self_view(GtkWidget *w){
827 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
828 LinphoneCore *lc=linphone_gtk_get_core();
829 linphone_core_enable_video_preview(lc,val);
830 linphone_core_enable_self_view(lc,val);
831 linphone_gtk_set_ui_config_int("videoselfview",val);
834 void linphone_gtk_used_identity_changed(GtkWidget *w){
835 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
836 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
837 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
838 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
839 linphone_gtk_show_directory_search();
841 if (sel) g_free(sel);
845 void on_proxy_refresh_button_clicked(GtkWidget *w){
846 LinphoneCore *lc=linphone_gtk_get_core();
847 MSList const *item=linphone_core_get_proxy_config_list(lc);
848 while (item != NULL) {
849 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
850 linphone_proxy_config_edit(lpc);
851 linphone_proxy_config_done(lpc);
856 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
857 linphone_gtk_show_friends();
860 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
862 case GTK_RESPONSE_YES:
863 linphone_gtk_show_contact(lf);
866 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
868 gtk_widget_destroy(dialog);
871 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
874 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
875 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
879 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);
880 dialog = gtk_message_dialog_new (
881 GTK_WINDOW(linphone_gtk_get_main_window()),
882 GTK_DIALOG_DESTROY_WITH_PARENT,
883 GTK_MESSAGE_QUESTION,
888 g_signal_connect(G_OBJECT (dialog), "response",
889 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
890 /* actually show the box */
891 gtk_widget_show(dialog);
894 typedef struct _AuthTimeout{
899 static void auth_timeout_clean(AuthTimeout *tout){
903 static gboolean auth_timeout_destroy(AuthTimeout *tout){
905 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
906 gtk_widget_destroy(tout->w);
912 static AuthTimeout * auth_timeout_new(GtkWidget *w){
913 AuthTimeout *tout=g_new(AuthTimeout,1);
915 /*so that the timeout no more references the widget when it is destroyed:*/
916 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
917 /*so that the widget is automatically destroyed after some time */
918 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
922 void linphone_gtk_password_cancel(GtkWidget *w){
923 LinphoneAuthInfo *info;
924 GtkWidget *window=gtk_widget_get_toplevel(w);
925 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
926 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
927 gtk_widget_destroy(window);
930 void linphone_gtk_password_ok(GtkWidget *w){
932 GtkWidget *window=gtk_widget_get_toplevel(w);
933 LinphoneAuthInfo *info;
934 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
935 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
936 entry=linphone_gtk_get_widget(window,"password_entry");
937 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
938 linphone_auth_info_set_userid(info,
939 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
940 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
941 gtk_widget_destroy(window);
944 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
945 GtkWidget *w=linphone_gtk_create_window("password");
946 GtkWidget *label=linphone_gtk_get_widget(w,"message");
947 LinphoneAuthInfo *info;
949 GtkWidget *mw=linphone_gtk_get_main_window();
951 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
952 /*don't prompt for authentication when login frame is visible*/
953 linphone_core_abort_authentication(lc,NULL);
957 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
959 gtk_label_set_markup(GTK_LABEL(label),msg);
961 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
962 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
963 g_object_set_data(G_OBJECT(w),"auth_info",info);
964 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
969 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
970 GtkWidget *w=linphone_gtk_get_main_window();
971 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();
1495 if (!config_loaded){
1496 title=linphone_gtk_get_ui_config("title","Linphone");
1497 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1498 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1499 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1500 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1501 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1502 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1503 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1504 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1507 linphone_gtk_configure_window(w,"main_window");
1509 gtk_window_set_title(GTK_WINDOW(w),title);
1511 if (start_call_icon){
1512 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1513 create_pixmap (start_call_icon));
1514 if (!buttons_have_borders)
1515 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1518 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1519 create_pixmap (add_call_icon));
1520 if (!buttons_have_borders)
1521 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1523 if (stop_call_icon){
1524 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1525 create_pixmap (stop_call_icon));
1526 if (!buttons_have_borders)
1527 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1530 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1532 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1533 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);