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 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
72 static gboolean verbose=0;
73 static gboolean auto_answer = 0;
74 static gchar * addr_to_call = NULL;
75 static gboolean no_video=FALSE;
76 static gboolean iconified=FALSE;
77 static gchar *workingdir=NULL;
78 static char *progpath=NULL;
79 gchar *linphone_logfile=NULL;
80 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
82 static GOptionEntry linphone_options[]={
86 .arg=G_OPTION_ARG_NONE,
87 .arg_data= (gpointer)&verbose,
88 .description=N_("log to stdout some debug information while running.")
91 .long_name = "logfile",
93 .arg = G_OPTION_ARG_STRING,
94 .arg_data = &linphone_logfile,
95 .description = N_("path to a file to write logs into.")
98 .long_name = "no-video",
100 .arg = G_OPTION_ARG_NONE,
101 .arg_data = (gpointer)&no_video,
102 .description = N_("Start linphone with video disabled.")
105 .long_name="iconified",
107 .arg=G_OPTION_ARG_NONE,
108 .arg_data= (gpointer)&iconified,
109 .description=N_("Start only in the system tray, do not show the main interface.")
114 .arg = G_OPTION_ARG_STRING,
115 .arg_data = &addr_to_call,
116 .description = N_("address to call right now")
119 .long_name = "auto-answer",
121 .arg = G_OPTION_ARG_NONE,
122 .arg_data = (gpointer) & auto_answer,
123 .description = N_("if set automatically answer incoming calls")
126 .long_name = "workdir",
128 .arg = G_OPTION_ARG_STRING,
129 .arg_data = (gpointer) & workingdir,
130 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
135 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
136 #define RELATIVE_XML_DIR
137 #define BUILD_TREE_XML_DIR "gtk"
140 #define CONFIG_FILE ".linphonerc"
141 #define SECRETS_FILE ".linphone-zidcache"
143 #define CONFIG_FILE "linphonerc"
144 #define SECRETS_FILE "linphone-zidcache"
148 char *linphone_gtk_get_config_file(const char *filename){
149 const int path_max=1024;
150 char *config_file=g_malloc0(path_max);
151 if (filename==NULL) filename=CONFIG_FILE;
152 /*try accessing a local file first if exists*/
153 if (access(CONFIG_FILE,F_OK)==0){
154 snprintf(config_file,path_max,"%s",filename);
157 const char *appdata=getenv("APPDATA");
159 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
160 CreateDirectory(config_file,NULL);
161 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
164 const char *home=getenv("HOME");
165 if (home==NULL) home=".";
166 snprintf(config_file,path_max,"%s/%s",home,filename);
173 #define FACTORY_CONFIG_FILE "linphonerc.factory"
174 static char _factory_config_file[1024];
175 static const char *linphone_gtk_get_factory_config_file(){
176 /*try accessing a local file first if exists*/
177 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
178 snprintf(_factory_config_file,sizeof(_factory_config_file),
179 "%s",FACTORY_CONFIG_FILE);
183 if (progpath != NULL) {
185 progdir = strdup(progpath);
187 basename = strrchr(progdir, '\\');
188 if (basename != NULL) {
191 snprintf(_factory_config_file, sizeof(_factory_config_file),
192 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
194 if (workingdir!=NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
203 basename = strrchr(progdir, '/');
204 if (basename != NULL) {
207 snprintf(_factory_config_file, sizeof(_factory_config_file),
208 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
217 return _factory_config_file;
220 static void linphone_gtk_init_liblinphone(const char *config_file,
221 const char *factory_config_file) {
222 LinphoneCoreVTable vtable={0};
223 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
225 vtable.call_state_changed=linphone_gtk_call_state_changed;
226 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
227 vtable.notify_presence_recv=linphone_gtk_notify_recv;
228 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
229 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
230 vtable.display_status=linphone_gtk_display_status;
231 vtable.display_message=linphone_gtk_display_message;
232 vtable.display_warning=linphone_gtk_display_warning;
233 vtable.display_url=linphone_gtk_display_url;
234 vtable.call_log_updated=linphone_gtk_call_log_updated;
235 vtable.text_received=linphone_gtk_text_received;
236 vtable.refer_received=linphone_gtk_refer_received;
237 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
238 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
239 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
241 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
242 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
243 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
244 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
245 g_free(secrets_file);
246 linphone_core_enable_video(the_core,TRUE,TRUE);
248 _linphone_gtk_enable_video(FALSE);
249 linphone_gtk_set_ui_config_int("videoselfview",0);
255 LinphoneCore *linphone_gtk_get_core(void){
259 GtkWidget *linphone_gtk_get_main_window(){
263 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
264 static const char *icon_path=NULL;
265 static const char *hiddens=NULL;
266 static const char *shown=NULL;
267 static bool_t config_loaded=FALSE;
268 if (linphone_gtk_get_core()==NULL) return;
269 if (config_loaded==FALSE){
270 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
271 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
272 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
276 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
278 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
280 GdkPixbuf *pbuf=create_pixbuf(icon_path);
281 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
282 g_object_unref(G_OBJECT(pbuf));
286 static int get_ui_file(const char *name, char *path, int pathsize){
287 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
288 if (access(path,F_OK)!=0){
289 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
290 if (access(path,F_OK)!=0){
291 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
292 INSTALLED_XML_DIR,name);
299 GtkWidget *linphone_gtk_create_window(const char *window_name){
300 GError* error = NULL;
301 GtkBuilder* builder = gtk_builder_new ();
305 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
307 if (!gtk_builder_add_from_file (builder, path, &error)){
308 g_error("Couldn't load builder file: %s", error->message);
309 g_error_free (error);
312 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
314 g_error("Could not retrieve '%s' window from xml file",window_name);
317 g_object_set_data(G_OBJECT(w),"builder",builder);
318 gtk_builder_connect_signals(builder,w);
319 linphone_gtk_configure_window(w,window_name);
323 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
326 GtkBuilder* builder = gtk_builder_new ();
328 gchar *object_ids[2];
329 object_ids[0]=g_strdup(widget_name);
332 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
333 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
334 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
335 g_error_free (error);
336 g_free(object_ids[0]);
339 g_free(object_ids[0]);
340 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
342 g_error("Could not retrieve '%s' window from xml file",widget_name);
345 g_object_set_data(G_OBJECT(w),"builder",builder);
346 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
347 gtk_builder_connect_signals(builder,w);
351 static void entry_unmapped(GtkWidget *entry){
352 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
353 gtk_widget_unrealize(entry);
356 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
359 if (window==NULL) return NULL;
360 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
362 g_error("Fail to retrieve builder from window !");
365 w=gtk_builder_get_object(builder,name);
367 g_error("No widget named %s found in xml interface.",name);
369 if (workaround_gtk_entry_chinese_bug){
370 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
371 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
372 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
373 g_message("%s is a GtkEntry",name);
374 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
378 return GTK_WIDGET(w);
382 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
384 GtkWidget *main_window=linphone_gtk_get_main_window();
386 gtk_widget_show(main_window);
387 if (type==GTK_MESSAGE_QUESTION)
389 /* draw a question box. link to dialog_click callback */
390 dialog = gtk_message_dialog_new (
391 GTK_WINDOW(main_window),
392 GTK_DIALOG_DESTROY_WITH_PARENT,
393 GTK_MESSAGE_QUESTION,
396 (const gchar*)message);
397 /* connect to some callback : REVISIT */
399 g_signal_connect_swapped (G_OBJECT (dialog), "response",
400 G_CALLBACK (dialog_click),
403 /* actually show the box */
404 gtk_widget_show(dialog);
408 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
409 GTK_DIALOG_DESTROY_WITH_PARENT,
413 (const gchar*)message);
414 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
415 g_signal_connect_swapped (G_OBJECT (dialog), "response",
416 G_CALLBACK (gtk_widget_destroy),
418 gtk_widget_show(dialog);
422 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
423 if (id==GTK_RESPONSE_CANCEL){
424 gtk_widget_destroy(GTK_WIDGET(dialog));
428 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
429 g_message("About url clicked");
430 linphone_gtk_open_browser(url);
433 void linphone_gtk_show_about(){
434 struct stat filestat;
435 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
438 GdkPixbuf *logo=create_pixbuf(
439 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
440 static const char *defcfg="defcfg";
442 about=linphone_gtk_create_window("about");
443 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
444 memset(&filestat,0,sizeof(filestat));
445 if (stat(license_file,&filestat)!=0){
446 license_file="COPYING";
447 stat(license_file,&filestat);
449 if (filestat.st_size>0){
450 char *license=g_malloc(filestat.st_size+1);
451 FILE *f=fopen(license_file,"r");
452 if (f && fread(license,filestat.st_size,1,f)==1){
453 license[filestat.st_size]='\0';
454 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
458 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
459 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
460 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
461 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
462 tmp=linphone_gtk_get_ui_config("artists",defcfg);
467 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
469 tmp=linphone_gtk_get_ui_config("translators",defcfg);
471 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
472 tmp=linphone_gtk_get_ui_config("comments",defcfg);
474 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
475 gtk_widget_show(about);
478 static void set_video_window_decorations(GdkWindow *w){
479 const char *title=linphone_gtk_get_ui_config("title","Linphone");
480 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
481 char video_title[256];
482 GdkPixbuf *pbuf=create_pixbuf(icon_path);
483 if (!linphone_core_in_call(linphone_gtk_get_core())){
484 snprintf(video_title,sizeof(video_title),"%s video",title);
485 /* When not in call, treat the video as a normal window */
486 gdk_window_set_keep_above(w, FALSE);
488 LinphoneAddress *uri =
489 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
492 linphone_address_clean(uri);
493 if (linphone_address_get_display_name(uri)!=NULL){
494 display_name=ms_strdup(linphone_address_get_display_name(uri));
496 display_name=linphone_address_as_string(uri);
498 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
499 linphone_address_destroy(uri);
500 ms_free(display_name);
502 /* During calls, bring up the video window, arrange so that
503 it is above all the other windows */
504 gdk_window_deiconify(w);
505 gdk_window_set_keep_above(w,TRUE);
506 /* Maybe we should have the following, but then we want to
507 have a timer that turns it off after a little while. */
508 /* gdk_window_set_urgency_hint(w,TRUE); */
510 gdk_window_set_title(w,video_title);
511 /* Refrain the video window to be closed at all times. */
512 gdk_window_set_functions(w,
513 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
514 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
517 l=g_list_append(l,pbuf);
518 gdk_window_set_icon_list(w,l);
520 g_object_unref(G_OBJECT(pbuf));
524 static gboolean video_needs_update=FALSE;
526 static void update_video_title(){
527 video_needs_update=TRUE;
530 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
531 static gboolean first_time=TRUE;
533 static unsigned long previd=0;
534 static unsigned long preview_previd=0;
535 static gboolean in_iterate=FALSE;
538 if (in_iterate) return TRUE;
540 linphone_core_iterate(lc);
542 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
543 linphone_gtk_show_directory_search();
547 id=linphone_core_get_native_video_window_id(lc);
548 if (id!=previd || video_needs_update){
552 ms_message("Updating window decorations");
554 w=gdk_window_foreign_new(id);
556 w=gdk_window_foreign_new((HANDLE)id);
559 set_video_window_decorations(w);
560 g_object_unref(G_OBJECT(w));
562 else ms_error("gdk_window_foreign_new() failed");
563 if (video_needs_update) video_needs_update=FALSE;
566 id=linphone_core_get_native_preview_window_id (lc);
567 if (id!=preview_previd ){
571 ms_message("Updating window decorations for preview");
573 w=gdk_window_foreign_new(id);
575 w=gdk_window_foreign_new((HANDLE)id);
578 set_video_window_decorations(w);
579 g_object_unref(G_OBJECT(w));
581 else ms_error("gdk_window_foreign_new() failed");
582 if (video_needs_update) video_needs_update=FALSE;
585 if (addr_to_call!=NULL){
586 /*make sure we are not showing the login screen*/
587 GtkWidget *mw=linphone_gtk_get_main_window();
588 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
589 if (!GTK_WIDGET_VISIBLE(login_frame)){
590 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
591 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
593 linphone_gtk_start_call(uri_bar);
600 static void load_uri_history(){
601 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
604 GtkEntryCompletion *gep=gtk_entry_completion_new();
605 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
608 snprintf(key,sizeof(key),"uri%i",i);
609 uri=linphone_gtk_get_ui_config(key,NULL);
612 gtk_list_store_append(model,&iter);
613 gtk_list_store_set(model,&iter,0,uri,-1);
614 if (i==0) gtk_entry_set_text(uribar,uri);
618 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
619 gtk_entry_completion_set_text_column(gep,0);
620 gtk_entry_set_completion(uribar,gep);
623 static void save_uri_history(){
624 LinphoneCore *lc=linphone_gtk_get_core();
625 LpConfig *cfg=linphone_core_get_config(lc);
626 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
631 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
633 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
635 gtk_tree_model_get(model,&iter,0,&uri,-1);
637 snprintf(key,sizeof(key),"uri%i",i);
638 lp_config_set_string(cfg,"GtkUi",key,uri);
643 }while(gtk_tree_model_iter_next(model,&iter));
647 static void completion_add_text(GtkEntry *entry, const char *text){
649 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
651 if (gtk_tree_model_get_iter_first(model,&iter)){
654 gtk_tree_model_get(model,&iter,0,&uri,-1);
656 if (strcmp(uri,text)==0) {
658 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
664 }while (gtk_tree_model_iter_next(model,&iter));
666 /* and prepend it on top of the list */
667 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
668 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
673 bool_t linphone_gtk_video_enabled(void){
674 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
675 return vpol->automatically_accept && vpol->automatically_initiate;
678 void linphone_gtk_show_main_window(){
679 GtkWidget *w=linphone_gtk_get_main_window();
680 LinphoneCore *lc=linphone_gtk_get_core();
681 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
682 VIDEOSELFVIEW_DEFAULT));
684 gtk_window_present(GTK_WINDOW(w));
687 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
688 GtkWidget *mw=linphone_gtk_get_main_window();
689 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
690 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
691 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
693 if (linphone_gtk_use_in_call_view() && call)
694 linphone_gtk_in_call_view_terminate(call,error);
695 update_video_title();
698 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
699 LinphoneCore *lc=linphone_gtk_get_core();
700 GtkWidget *mw=linphone_gtk_get_main_window();
701 const MSList *calls=linphone_core_get_calls(lc);
703 bool_t start_active=TRUE;
704 bool_t stop_active=FALSE;
705 bool_t add_call=FALSE;
706 int call_list_size=ms_list_size(calls);
716 button=linphone_gtk_get_widget(mw,"start_call");
717 gtk_widget_set_sensitive(button,start_active);
718 gtk_widget_set_visible(button,!add_call);
720 button=linphone_gtk_get_widget(mw,"add_call");
721 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
722 gtk_widget_set_sensitive(button,FALSE);
724 gtk_widget_set_sensitive(button,start_active);
726 gtk_widget_set_visible(button,add_call);
728 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
730 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
731 linphone_gtk_enable_conference_button(lc,call_list_size>1);
732 update_video_title();
733 if (call) linphone_gtk_update_video_button(call);
736 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
737 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
738 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
739 completion_add_text(GTK_ENTRY(uri_bar),entered);
741 linphone_gtk_call_terminated(NULL,NULL);
746 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
747 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
748 linphone_core_accept_call (linphone_gtk_get_core(),call);
749 linphone_call_unref(call);
755 void linphone_gtk_start_call(GtkWidget *w){
756 LinphoneCore *lc=linphone_gtk_get_core();
758 /*change into in-call mode, then do the work later as it might block a bit */
759 GtkWidget *mw=gtk_widget_get_toplevel(w);
760 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
762 call=linphone_gtk_get_currently_displayed_call(NULL);
763 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
764 linphone_core_accept_call(lc,call);
766 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
767 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
768 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
769 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
774 void linphone_gtk_uri_bar_activate(GtkWidget *w){
775 linphone_gtk_start_call(w);
779 void linphone_gtk_terminate_call(GtkWidget *button){
781 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
783 linphone_core_terminate_call(linphone_gtk_get_core(),call);
785 linphone_core_terminate_conference(linphone_gtk_get_core());
789 void linphone_gtk_decline_clicked(GtkWidget *button){
790 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
792 linphone_core_terminate_call(linphone_gtk_get_core(),call);
795 void linphone_gtk_answer_clicked(GtkWidget *button){
796 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
798 linphone_core_accept_call(linphone_gtk_get_core(),call);
799 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
803 void _linphone_gtk_enable_video(gboolean val){
804 LinphoneVideoPolicy policy={0};
805 policy.automatically_initiate=policy.automatically_accept=val;
806 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
807 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
810 linphone_core_enable_video_preview(linphone_gtk_get_core(),
811 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
813 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
817 void linphone_gtk_enable_video(GtkWidget *w){
818 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
819 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
820 _linphone_gtk_enable_video(val);
823 void linphone_gtk_enable_self_view(GtkWidget *w){
824 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
825 LinphoneCore *lc=linphone_gtk_get_core();
826 linphone_core_enable_video_preview(lc,val);
827 linphone_core_enable_self_view(lc,val);
828 linphone_gtk_set_ui_config_int("videoselfview",val);
831 void linphone_gtk_used_identity_changed(GtkWidget *w){
832 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
833 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
834 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
835 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
836 linphone_gtk_show_directory_search();
838 if (sel) g_free(sel);
842 void on_proxy_refresh_button_clicked(GtkWidget *w){
843 LinphoneCore *lc=linphone_gtk_get_core();
844 MSList const *item=linphone_core_get_proxy_config_list(lc);
845 while (item != NULL) {
846 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
847 linphone_proxy_config_edit(lpc);
848 linphone_proxy_config_done(lpc);
853 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
854 linphone_gtk_show_friends();
857 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
859 case GTK_RESPONSE_YES:
860 linphone_gtk_show_contact(lf);
863 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
865 gtk_widget_destroy(dialog);
868 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
871 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
872 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
876 gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
877 dialog = gtk_message_dialog_new (
878 GTK_WINDOW(linphone_gtk_get_main_window()),
879 GTK_DIALOG_DESTROY_WITH_PARENT,
880 GTK_MESSAGE_QUESTION,
885 g_signal_connect(G_OBJECT (dialog), "response",
886 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
887 /* actually show the box */
888 gtk_widget_show(dialog);
891 typedef struct _AuthTimeout{
896 static void auth_timeout_clean(AuthTimeout *tout){
900 static gboolean auth_timeout_destroy(AuthTimeout *tout){
902 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
903 gtk_widget_destroy(tout->w);
909 static AuthTimeout * auth_timeout_new(GtkWidget *w){
910 AuthTimeout *tout=g_new(AuthTimeout,1);
912 /*so that the timeout no more references the widget when it is destroyed:*/
913 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
914 /*so that the widget is automatically destroyed after some time */
915 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
919 void linphone_gtk_password_cancel(GtkWidget *w){
920 LinphoneAuthInfo *info;
921 GtkWidget *window=gtk_widget_get_toplevel(w);
922 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
923 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
924 gtk_widget_destroy(window);
927 void linphone_gtk_password_ok(GtkWidget *w){
929 GtkWidget *window=gtk_widget_get_toplevel(w);
930 LinphoneAuthInfo *info;
931 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
932 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
933 entry=linphone_gtk_get_widget(window,"password_entry");
934 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
935 linphone_auth_info_set_userid(info,
936 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
937 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
938 gtk_widget_destroy(window);
941 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
942 GtkWidget *w=linphone_gtk_create_window("password");
943 GtkWidget *label=linphone_gtk_get_widget(w,"message");
944 LinphoneAuthInfo *info;
946 GtkWidget *mw=linphone_gtk_get_main_window();
948 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
949 /*don't prompt for authentication when login frame is visible*/
950 linphone_core_abort_authentication(lc,NULL);
954 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
956 gtk_label_set_markup(GTK_LABEL(label),msg);
958 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
959 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
960 g_object_set_data(G_OBJECT(w),"auth_info",info);
961 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
966 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
967 GtkWidget *w=linphone_gtk_get_main_window();
968 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
969 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
970 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
974 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
975 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
978 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
979 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
982 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
984 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
985 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
988 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
989 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
990 if (w) linphone_gtk_call_log_update(w);
991 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
995 static bool_t notify_actions_supported() {
996 bool_t accepts_actions = FALSE;
997 GList *capabilities = notify_get_server_caps();
999 if(capabilities != NULL) {
1000 for(c = capabilities; c != NULL; c = c->next) {
1001 if(strcmp((char*)c->data, "actions") == 0 ) {
1002 accepts_actions = TRUE;
1006 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1007 g_list_free(capabilities);
1009 return accepts_actions;
1012 static NotifyNotification* build_notification(const char *title, const char *body){
1013 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1020 static void show_notification(NotifyNotification* n){
1021 if (n && !notify_notification_show(n,NULL))
1022 ms_error("Failed to send notification.");
1025 static void make_notification(const char *title, const char *body){
1026 show_notification(build_notification(title,body));
1031 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1033 if (!notify_is_initted())
1034 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1038 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1043 ms_error("Failed to send notification.");
1045 linphone_gtk_show_main_window();
1047 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1050 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1051 NotifyNotification *n;
1052 switch(linphone_call_get_state(call)){
1053 case LinphoneCallError:
1054 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1056 case LinphoneCallEnd:
1057 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1059 case LinphoneCallIncomingReceived:
1060 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1061 if (notify_actions_supported()) {
1062 notify_notification_add_action (n,"answer", _("Answer"),
1063 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1064 notify_notification_add_action (n,"decline",_("Decline"),
1065 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1067 show_notification(n);
1069 case LinphoneCallPausedByRemote:
1070 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1075 if (body) g_free(body);
1076 if (remote) g_free(remote);
1081 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1082 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1083 LinphoneCore *lc=linphone_call_get_core(call);
1084 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1085 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1086 linphone_core_accept_call_update(lc,call,params);
1087 linphone_call_params_destroy(params);
1089 linphone_call_unref(call);
1090 g_source_remove_by_user_data(dialog);
1091 gtk_widget_destroy(dialog);
1094 static void on_call_updated_timeout(GtkWidget *dialog){
1095 gtk_widget_destroy(dialog);
1098 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1099 LinphoneCore *lc=linphone_call_get_core(call);
1100 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1101 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1102 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1103 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1104 gboolean video_used=linphone_call_params_video_enabled(current_params);
1105 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1106 video_used,video_requested,pol->automatically_accept);
1107 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1108 linphone_core_defer_call_update(lc,call);
1110 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1112 const char *dname=linphone_address_get_display_name(addr);
1113 if (dname==NULL) dname=linphone_address_get_username(addr);
1114 if (dname==NULL) dname=linphone_address_get_domain(addr);
1115 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1116 GTK_DIALOG_DESTROY_WITH_PARENT,
1117 GTK_MESSAGE_WARNING,
1119 _("%s proposed to start video. Do you accept ?"),dname);
1120 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1121 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1122 gtk_widget_show(dialog);
1127 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1129 case LinphoneCallOutgoingInit:
1130 linphone_gtk_create_in_call_view (call);
1132 case LinphoneCallOutgoingProgress:
1133 linphone_gtk_in_call_view_set_calling (call);
1135 case LinphoneCallStreamsRunning:
1136 linphone_gtk_in_call_view_set_in_call(call);
1138 case LinphoneCallUpdatedByRemote:
1139 linphone_gtk_call_updated_by_remote(call);
1141 case LinphoneCallError:
1142 linphone_gtk_in_call_view_terminate (call,msg);
1144 case LinphoneCallEnd:
1145 linphone_gtk_in_call_view_terminate(call,NULL);
1146 linphone_gtk_status_icon_set_blinking(FALSE);
1148 case LinphoneCallIncomingReceived:
1149 linphone_gtk_create_in_call_view(call);
1150 linphone_gtk_in_call_view_set_incoming(call);
1151 linphone_gtk_status_icon_set_blinking(TRUE);
1153 linphone_call_ref(call);
1154 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1157 case LinphoneCallResuming:
1158 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1159 linphone_gtk_in_call_view_set_in_call (call);
1161 case LinphoneCallPausing:
1162 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1163 case LinphoneCallPausedByRemote:
1164 linphone_gtk_in_call_view_set_paused(call);
1166 case LinphoneCallConnected:
1167 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1168 linphone_gtk_status_icon_set_blinking(FALSE);
1173 linphone_gtk_notify(call, msg);
1174 linphone_gtk_update_call_buttons (call);
1177 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1178 linphone_gtk_in_call_view_show_encryption(call);
1181 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1182 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1185 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1186 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1187 GtkTreeModel *model=gtk_combo_box_get_model(box);
1189 gboolean found=FALSE;
1190 const char *stock_id=NULL;
1192 if (gtk_tree_model_get_iter_first(model,&iter)){
1195 gtk_tree_model_get(model,&iter,2,&p,-1);
1200 }while(gtk_tree_model_iter_next(model,&iter));
1203 g_warning("Could not find proxy config in combo box of identities.");
1207 case LinphoneRegistrationOk:
1208 stock_id=GTK_STOCK_YES;
1210 case LinphoneRegistrationProgress:
1211 stock_id=GTK_STOCK_REFRESH;
1213 case LinphoneRegistrationCleared:
1216 case LinphoneRegistrationFailed:
1217 stock_id=GTK_STOCK_DIALOG_WARNING;
1222 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1225 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1226 LinphoneRegistrationState rs, const char *msg){
1228 case LinphoneRegistrationOk:
1230 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1231 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1232 linphone_gtk_exit_login_frame();
1239 update_registration_status(cfg,rs);
1242 void linphone_gtk_open_browser(const char *url){
1243 /*in gtk 2.16, gtk_show_uri does not work...*/
1245 #if GTK_CHECK_VERSION(2,18,3)
1246 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1249 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1250 g_spawn_command_line_async(cl,NULL);
1253 ShellExecute(0,"open",url,NULL,NULL,1);
1257 void linphone_gtk_link_to_website(GtkWidget *item){
1258 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1259 linphone_gtk_open_browser(home);
1262 #ifndef HAVE_GTK_OSX
1264 static GtkStatusIcon *icon=NULL;
1266 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1267 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1268 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1271 static GtkWidget *create_icon_menu(){
1272 GtkWidget *menu=gtk_menu_new();
1273 GtkWidget *menu_item;
1276 const gchar *homesite;
1278 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1279 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1280 tmp=g_strdup(homesite);
1281 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1282 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1284 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1285 gtk_widget_show(image);
1286 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1287 //g_object_unref(G_OBJECT(image));
1288 gtk_widget_show(menu_item);
1289 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1290 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1292 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1293 gtk_widget_show(menu_item);
1294 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1295 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1296 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1297 gtk_widget_show(menu_item);
1298 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1299 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1300 gtk_widget_show(menu);
1304 static void handle_icon_click() {
1305 GtkWidget *mw=linphone_gtk_get_main_window();
1306 if (!gtk_window_is_active((GtkWindow*)mw)) {
1307 linphone_gtk_show_main_window();
1309 gtk_widget_hide(mw);
1313 static void linphone_gtk_init_status_icon(){
1314 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1315 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1316 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1317 GtkWidget *menu=create_icon_menu();
1319 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1320 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1321 #if GTK_CHECK_VERSION(2,20,0)
1322 gtk_status_icon_set_name(icon,title);
1324 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1325 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1326 gtk_status_icon_set_tooltip(icon,title);
1327 gtk_status_icon_set_visible(icon,TRUE);
1328 g_object_set_data(G_OBJECT(icon),"menu",menu);
1329 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1330 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1331 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1332 pbuf=create_pixbuf(call_icon_path);
1333 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1336 static gboolean do_icon_blink(GtkStatusIcon *gi){
1337 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1338 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1339 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1340 if (cur_icon==call_icon){
1341 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1343 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1350 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1352 static gint attention_id;
1353 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1355 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1356 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1360 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1361 if (val && tout==0){
1362 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1363 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1364 }else if (!val && tout!=0){
1365 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1366 g_source_remove(tout);
1367 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1368 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1374 void linphone_gtk_options_activate(GtkWidget *item){
1375 #ifndef HAVE_GTK_OSX
1376 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1381 static void init_identity_combo(GtkComboBox *box){
1382 GtkListStore *store;
1383 GtkCellRenderer *r1,*r2;
1384 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1385 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1386 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1387 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1388 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1389 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1390 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1391 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1394 void linphone_gtk_load_identities(void){
1396 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1398 LinphoneProxyConfig *def=NULL;
1400 GtkListStore *store;
1403 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1404 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1405 /* model is empty, this is the first time we go here */
1406 init_identity_combo(box);
1407 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1409 gtk_list_store_clear(store);
1410 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1411 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1412 gtk_list_store_append(store,&iter);
1413 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1414 g_free(def_identity);
1415 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1417 elem=ms_list_next(elem),i++){
1418 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1419 gtk_list_store_append(store,&iter);
1420 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1421 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1427 gtk_combo_box_set_active(box,def_index);
1430 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1431 const char *label=gtk_button_get_label(button);
1432 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1434 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1435 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1436 if (linphone_core_in_call(linphone_gtk_get_core())){
1437 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1441 static void linphone_gtk_dtmf_released(GtkButton *button){
1442 linphone_core_stop_dtmf (linphone_gtk_get_core());
1445 static void linphone_gtk_connect_digits(void){
1446 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1447 GList *children=gtk_container_get_children(cont);
1449 for(elem=children;elem!=NULL;elem=elem->next){
1450 GtkButton *button=GTK_BUTTON(elem->data);
1451 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1452 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1456 static void linphone_gtk_check_menu_items(void){
1457 bool_t video_enabled=linphone_gtk_video_enabled();
1458 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1459 GtkWidget *selfview_item=linphone_gtk_get_widget(
1460 linphone_gtk_get_main_window(),"selfview_item");
1461 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1462 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1463 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1466 static gboolean linphone_gtk_can_manage_accounts(){
1467 LinphoneCore *lc=linphone_gtk_get_core();
1469 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1470 SipSetup *ss=(SipSetup*)elem->data;
1471 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1478 static void linphone_gtk_configure_main_window(){
1479 static gboolean config_loaded=FALSE;
1480 static const char *title;
1481 static const char *home;
1482 static const char *start_call_icon;
1483 static const char *add_call_icon;
1484 static const char *stop_call_icon;
1485 static const char *search_icon;
1486 static gboolean update_check_menu;
1487 static gboolean buttons_have_borders;
1488 static gboolean show_abcd;
1489 GtkWidget *w=linphone_gtk_get_main_window();
1490 if (!config_loaded){
1491 title=linphone_gtk_get_ui_config("title","Linphone");
1492 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1493 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1494 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1495 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1496 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1497 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1498 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1499 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1502 linphone_gtk_configure_window(w,"main_window");
1504 gtk_window_set_title(GTK_WINDOW(w),title);
1506 if (start_call_icon){
1507 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1508 create_pixmap (start_call_icon));
1509 if (!buttons_have_borders)
1510 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1513 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1514 create_pixmap (add_call_icon));
1515 if (!buttons_have_borders)
1516 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1518 if (stop_call_icon){
1519 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1520 create_pixmap (stop_call_icon));
1521 if (!buttons_have_borders)
1522 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1525 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1526 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1527 g_object_unref(G_OBJECT(pbuf));
1531 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1533 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1537 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1539 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1540 g_object_unref(G_OBJECT(pbuf));
1545 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1547 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1550 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1551 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1552 gtk_image_set_from_pixbuf(img,scaled);
1553 g_object_unref(G_OBJECT(scaled));
1554 g_object_unref(G_OBJECT(pbuf));
1557 if (linphone_gtk_can_manage_accounts()) {
1558 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1560 if (update_check_menu){
1561 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1564 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1565 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1566 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1567 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1568 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1572 void linphone_gtk_manage_login(void){
1573 LinphoneCore *lc=linphone_gtk_get_core();
1574 LinphoneProxyConfig *cfg=NULL;
1575 linphone_core_get_default_proxy(lc,&cfg);
1577 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1578 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1579 linphone_gtk_show_login_frame(cfg);
1585 gboolean linphone_gtk_close(GtkWidget *mw){
1586 /*shutdown calls if any*/
1587 LinphoneCore *lc=linphone_gtk_get_core();
1588 if (linphone_core_in_call(lc)){
1589 linphone_core_terminate_all_calls(lc);
1591 linphone_core_enable_video_preview(lc,FALSE);
1592 #ifdef __APPLE__ /*until with have a better option*/
1593 gtk_window_iconify(GTK_WINDOW(mw));
1595 gtk_widget_hide(mw);
1601 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1602 bool_t video_enabled=linphone_gtk_video_enabled();
1603 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1604 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1606 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1607 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1614 static void linphone_gtk_init_main_window(){
1615 GtkWidget *main_window;
1617 linphone_gtk_configure_main_window();
1618 linphone_gtk_manage_login();
1620 linphone_gtk_load_identities();
1621 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1622 linphone_gtk_show_friends();
1623 linphone_gtk_connect_digits();
1624 main_window=linphone_gtk_get_main_window();
1625 linphone_gtk_call_log_update(main_window);
1627 linphone_gtk_update_call_buttons (NULL);
1628 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1629 g_signal_connect (G_OBJECT (main_window), "delete-event",
1630 G_CALLBACK (linphone_gtk_close), main_window);
1633 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1634 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1635 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1636 gtk_widget_hide(menubar);
1637 gtk_osxapplication_ready(theMacApp);
1639 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1641 linphone_gtk_check_menu_items();
1645 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1647 const char *lname="undef";
1649 #if defined(__linux) || defined(__APPLE__)
1650 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1669 g_error("Bad level !");
1671 #if defined(__linux) || defined(__APPLE__)
1673 msg=g_strdup_vprintf(fmt,cap);
1676 msg=g_strdup_vprintf(fmt,args);
1678 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1681 linphone_gtk_log_push(lev,fmt,args);
1685 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1686 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1687 linphone_gtk_get_main_window(), "uribar"));
1689 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1691 gtk_entry_set_text(uri_bar, refer_to);
1692 linphone_gtk_start_call(linphone_gtk_get_main_window());
1695 static void linphone_gtk_check_soundcards(){
1696 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1697 if (devices==NULL || devices[0]==NULL){
1698 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1699 _("No sound cards have been detected on this computer.\n"
1700 "You won't be able to send or receive audio calls."));
1705 // Display the account wizard
1706 void linphone_gtk_display_wizard() {
1707 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1708 the_wizard = linphone_gtk_create_assistant();
1713 static void linphone_gtk_quit(void){
1714 static gboolean quit_done=FALSE;
1717 linphone_gtk_unmonitor_usb();
1718 g_source_remove_by_user_data(linphone_gtk_get_core());
1719 linphone_gtk_uninit_instance();
1720 linphone_gtk_destroy_log_window();
1721 linphone_core_destroy(the_core);
1722 linphone_gtk_log_uninit();
1726 gdk_threads_leave();
1732 This is not the correct way to implement block termination.
1733 The good way would be to call gtk_main_quit(), and return TRUE.
1734 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1735 As a result the program cannot exit at all.
1736 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1738 static gboolean on_block_termination(void){
1740 linphone_gtk_quit();
1745 int main(int argc, char *argv[]){
1750 const char *factory_config_file;
1752 GtkSettings *settings;
1754 const char *app_name="Linphone";
1756 #if !GLIB_CHECK_VERSION(2, 31, 0)
1757 g_thread_init(NULL);
1761 progpath = strdup(argv[0]);
1763 config_file=linphone_gtk_get_config_file(NULL);
1767 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1768 if ((lang=getenv("LANG"))!=NULL){
1771 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1777 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1780 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1783 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1785 if (strncmp(lang,"zh",2)==0){
1786 workaround_gtk_entry_chinese_bug=TRUE;
1789 setenv("LANG",lang,1);
1794 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1795 if (p==NULL) perror("bindtextdomain failed");
1796 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1797 textdomain (GETTEXT_PACKAGE);
1799 g_message("NLS disabled.\n");
1802 gtk_rc_add_default_file("./gtkrc");
1804 gdk_threads_enter();
1806 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1807 linphone_options,NULL,NULL)){
1808 gdk_threads_leave();
1812 settings=gtk_settings_get_default();
1813 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1814 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1815 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1816 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1818 if (workingdir!=NULL){
1819 if (chdir(workingdir)==-1){
1820 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1824 /* Now, look for the factory configuration file, we do it this late
1825 since we want to have had time to change directory and to parse
1826 the options, in case we needed to access the working directory */
1827 factory_config_file = linphone_gtk_get_factory_config_file();
1829 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1830 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1831 g_warning("This instance is going to exit now.");
1832 gdk_threads_leave();
1836 add_pixmap_directory("pixmaps");
1837 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1840 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1841 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1842 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1843 /*never block termination:*/
1844 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1847 the_ui=linphone_gtk_create_window("main");
1849 linphone_gtk_create_log_window();
1850 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1852 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1854 g_set_application_name(app_name);
1855 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1856 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1858 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1859 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1860 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1861 linphone_gtk_init_main_window();
1864 // Veryfing if at least one sip account is configured. If not, show wizard
1865 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1866 linphone_gtk_display_wizard();
1870 #ifndef HAVE_GTK_OSX
1871 linphone_gtk_init_status_icon();
1874 linphone_gtk_show_main_window();
1875 linphone_gtk_check_soundcards();
1877 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1878 linphone_gtk_check_for_new_version();
1879 linphone_gtk_monitor_usb();
1882 linphone_gtk_quit();
1883 #ifndef HAVE_GTK_OSX
1884 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1885 gtk_status_icon_set_visible(icon,FALSE);