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 linphone_call_enable_video(call, responseid==GTK_RESPONSE_YES);
1085 linphone_call_unref(call);
1086 g_source_remove_by_user_data(dialog);
1087 gtk_widget_destroy(dialog);
1090 static void on_call_updated_timeout(GtkWidget *dialog){
1091 gtk_widget_destroy(dialog);
1094 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1095 LinphoneCore *lc=linphone_call_get_core(call);
1096 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1097 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1098 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1099 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1100 gboolean video_used=linphone_call_params_video_enabled(current_params);
1101 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1102 video_used,video_requested,pol->automatically_accept);
1103 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1104 linphone_core_defer_call_update(lc,call);
1106 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1108 const char *dname=linphone_address_get_display_name(addr);
1109 if (dname==NULL) dname=linphone_address_get_username(addr);
1110 if (dname==NULL) dname=linphone_address_get_domain(addr);
1111 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1112 GTK_DIALOG_DESTROY_WITH_PARENT,
1113 GTK_MESSAGE_WARNING,
1115 _("%s proposed to start video. Do you accept ?"),dname);
1116 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1117 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1118 gtk_widget_show(dialog);
1123 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1125 case LinphoneCallOutgoingInit:
1126 linphone_gtk_create_in_call_view (call);
1128 case LinphoneCallOutgoingProgress:
1129 linphone_gtk_in_call_view_set_calling (call);
1131 case LinphoneCallStreamsRunning:
1132 linphone_gtk_in_call_view_set_in_call(call);
1134 case LinphoneCallUpdatedByRemote:
1135 linphone_gtk_call_updated_by_remote(call);
1137 case LinphoneCallError:
1138 linphone_gtk_in_call_view_terminate (call,msg);
1140 case LinphoneCallEnd:
1141 linphone_gtk_in_call_view_terminate(call,NULL);
1142 linphone_gtk_status_icon_set_blinking(FALSE);
1144 case LinphoneCallIncomingReceived:
1145 linphone_gtk_create_in_call_view(call);
1146 linphone_gtk_in_call_view_set_incoming(call);
1147 linphone_gtk_status_icon_set_blinking(TRUE);
1149 linphone_call_ref(call);
1150 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1153 case LinphoneCallResuming:
1154 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1155 linphone_gtk_in_call_view_set_in_call (call);
1157 case LinphoneCallPausing:
1158 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1159 case LinphoneCallPausedByRemote:
1160 linphone_gtk_in_call_view_set_paused(call);
1162 case LinphoneCallConnected:
1163 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1164 linphone_gtk_status_icon_set_blinking(FALSE);
1169 linphone_gtk_notify(call, msg);
1170 linphone_gtk_update_call_buttons (call);
1173 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1174 linphone_gtk_in_call_view_show_encryption(call);
1177 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1178 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1181 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1182 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1183 GtkTreeModel *model=gtk_combo_box_get_model(box);
1185 gboolean found=FALSE;
1186 const char *stock_id=NULL;
1188 if (gtk_tree_model_get_iter_first(model,&iter)){
1191 gtk_tree_model_get(model,&iter,2,&p,-1);
1196 }while(gtk_tree_model_iter_next(model,&iter));
1199 g_warning("Could not find proxy config in combo box of identities.");
1203 case LinphoneRegistrationOk:
1204 stock_id=GTK_STOCK_YES;
1206 case LinphoneRegistrationProgress:
1207 stock_id=GTK_STOCK_REFRESH;
1209 case LinphoneRegistrationCleared:
1212 case LinphoneRegistrationFailed:
1213 stock_id=GTK_STOCK_DIALOG_WARNING;
1218 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1221 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1222 LinphoneRegistrationState rs, const char *msg){
1224 case LinphoneRegistrationOk:
1226 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1227 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1228 linphone_gtk_exit_login_frame();
1235 update_registration_status(cfg,rs);
1238 void linphone_gtk_open_browser(const char *url){
1239 /*in gtk 2.16, gtk_show_uri does not work...*/
1241 #if GTK_CHECK_VERSION(2,18,3)
1242 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1245 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1246 g_spawn_command_line_async(cl,NULL);
1249 ShellExecute(0,"open",url,NULL,NULL,1);
1253 void linphone_gtk_link_to_website(GtkWidget *item){
1254 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1255 linphone_gtk_open_browser(home);
1258 #ifndef HAVE_GTK_OSX
1260 static GtkStatusIcon *icon=NULL;
1262 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1263 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1264 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1267 static GtkWidget *create_icon_menu(){
1268 GtkWidget *menu=gtk_menu_new();
1269 GtkWidget *menu_item;
1272 const gchar *homesite;
1274 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1275 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1276 tmp=g_strdup(homesite);
1277 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1278 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1280 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1281 gtk_widget_show(image);
1282 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1283 //g_object_unref(G_OBJECT(image));
1284 gtk_widget_show(menu_item);
1285 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1286 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1288 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1289 gtk_widget_show(menu_item);
1290 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1291 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1292 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,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)gtk_main_quit,NULL);
1296 gtk_widget_show(menu);
1300 static void handle_icon_click() {
1301 GtkWidget *mw=linphone_gtk_get_main_window();
1302 if (!gtk_window_is_active((GtkWindow*)mw)) {
1303 linphone_gtk_show_main_window();
1305 gtk_widget_hide(mw);
1309 static void linphone_gtk_init_status_icon(){
1310 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1311 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1312 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1313 GtkWidget *menu=create_icon_menu();
1315 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1316 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1317 #if GTK_CHECK_VERSION(2,20,0)
1318 gtk_status_icon_set_name(icon,title);
1320 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1321 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1322 gtk_status_icon_set_tooltip(icon,title);
1323 gtk_status_icon_set_visible(icon,TRUE);
1324 g_object_set_data(G_OBJECT(icon),"menu",menu);
1325 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1326 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1327 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1328 pbuf=create_pixbuf(call_icon_path);
1329 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1332 static gboolean do_icon_blink(GtkStatusIcon *gi){
1333 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1334 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1335 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1336 if (cur_icon==call_icon){
1337 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1339 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1346 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1348 static gint attention_id;
1349 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1351 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1352 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1356 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1357 if (val && tout==0){
1358 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1359 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1360 }else if (!val && tout!=0){
1361 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1362 g_source_remove(tout);
1363 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1364 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1370 void linphone_gtk_options_activate(GtkWidget *item){
1371 #ifndef HAVE_GTK_OSX
1372 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1377 static void init_identity_combo(GtkComboBox *box){
1378 GtkListStore *store;
1379 GtkCellRenderer *r1,*r2;
1380 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1381 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1382 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1383 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1384 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1385 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1386 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1387 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1390 void linphone_gtk_load_identities(void){
1392 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1394 LinphoneProxyConfig *def=NULL;
1396 GtkListStore *store;
1399 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1400 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1401 /* model is empty, this is the first time we go here */
1402 init_identity_combo(box);
1403 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1405 gtk_list_store_clear(store);
1406 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1407 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1408 gtk_list_store_append(store,&iter);
1409 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1410 g_free(def_identity);
1411 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1413 elem=ms_list_next(elem),i++){
1414 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1415 gtk_list_store_append(store,&iter);
1416 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1417 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1423 gtk_combo_box_set_active(box,def_index);
1426 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1427 const char *label=gtk_button_get_label(button);
1428 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1430 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1431 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1432 if (linphone_core_in_call(linphone_gtk_get_core())){
1433 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1437 static void linphone_gtk_dtmf_released(GtkButton *button){
1438 linphone_core_stop_dtmf (linphone_gtk_get_core());
1441 static void linphone_gtk_connect_digits(void){
1442 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1443 GList *children=gtk_container_get_children(cont);
1445 for(elem=children;elem!=NULL;elem=elem->next){
1446 GtkButton *button=GTK_BUTTON(elem->data);
1447 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1448 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1452 static void linphone_gtk_check_menu_items(void){
1453 bool_t video_enabled=linphone_gtk_video_enabled();
1454 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1455 GtkWidget *selfview_item=linphone_gtk_get_widget(
1456 linphone_gtk_get_main_window(),"selfview_item");
1457 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1458 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1459 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1462 static gboolean linphone_gtk_can_manage_accounts(){
1463 LinphoneCore *lc=linphone_gtk_get_core();
1465 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1466 SipSetup *ss=(SipSetup*)elem->data;
1467 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1474 static void linphone_gtk_configure_main_window(){
1475 static gboolean config_loaded=FALSE;
1476 static const char *title;
1477 static const char *home;
1478 static const char *start_call_icon;
1479 static const char *add_call_icon;
1480 static const char *stop_call_icon;
1481 static const char *search_icon;
1482 static gboolean update_check_menu;
1483 static gboolean buttons_have_borders;
1484 static gboolean show_abcd;
1485 GtkWidget *w=linphone_gtk_get_main_window();
1486 if (!config_loaded){
1487 title=linphone_gtk_get_ui_config("title","Linphone");
1488 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1489 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1490 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1491 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1492 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1493 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1494 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1495 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1498 linphone_gtk_configure_window(w,"main_window");
1500 gtk_window_set_title(GTK_WINDOW(w),title);
1502 if (start_call_icon){
1503 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1504 create_pixmap (start_call_icon));
1505 if (!buttons_have_borders)
1506 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1509 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1510 create_pixmap (add_call_icon));
1511 if (!buttons_have_borders)
1512 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1514 if (stop_call_icon){
1515 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1516 create_pixmap (stop_call_icon));
1517 if (!buttons_have_borders)
1518 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1521 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1522 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1523 g_object_unref(G_OBJECT(pbuf));
1527 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1529 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1533 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1535 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1536 g_object_unref(G_OBJECT(pbuf));
1541 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1543 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1546 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1547 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1548 gtk_image_set_from_pixbuf(img,scaled);
1549 g_object_unref(G_OBJECT(scaled));
1550 g_object_unref(G_OBJECT(pbuf));
1553 if (linphone_gtk_can_manage_accounts()) {
1554 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1556 if (update_check_menu){
1557 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1560 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1561 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1562 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1563 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1564 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1568 void linphone_gtk_manage_login(void){
1569 LinphoneCore *lc=linphone_gtk_get_core();
1570 LinphoneProxyConfig *cfg=NULL;
1571 linphone_core_get_default_proxy(lc,&cfg);
1573 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1574 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1575 linphone_gtk_show_login_frame(cfg);
1581 gboolean linphone_gtk_close(GtkWidget *mw){
1582 /*shutdown calls if any*/
1583 LinphoneCore *lc=linphone_gtk_get_core();
1584 if (linphone_core_in_call(lc)){
1585 linphone_core_terminate_all_calls(lc);
1587 linphone_core_enable_video_preview(lc,FALSE);
1588 #ifdef __APPLE__ /*until with have a better option*/
1589 gtk_window_iconify(GTK_WINDOW(mw));
1591 gtk_widget_hide(mw);
1597 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1598 bool_t video_enabled=linphone_gtk_video_enabled();
1599 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1600 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1602 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1603 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1610 static void linphone_gtk_init_main_window(){
1611 GtkWidget *main_window;
1613 linphone_gtk_configure_main_window();
1614 linphone_gtk_manage_login();
1616 linphone_gtk_load_identities();
1617 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1618 linphone_gtk_show_friends();
1619 linphone_gtk_connect_digits();
1620 main_window=linphone_gtk_get_main_window();
1621 linphone_gtk_call_log_update(main_window);
1623 linphone_gtk_update_call_buttons (NULL);
1624 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1625 g_signal_connect (G_OBJECT (main_window), "delete-event",
1626 G_CALLBACK (linphone_gtk_close), main_window);
1629 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1630 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1631 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1632 gtk_widget_hide(menubar);
1633 gtk_osxapplication_ready(theMacApp);
1635 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1637 linphone_gtk_check_menu_items();
1641 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1643 const char *lname="undef";
1645 #if defined(__linux) || defined(__APPLE__)
1646 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1665 g_error("Bad level !");
1667 #if defined(__linux) || defined(__APPLE__)
1669 msg=g_strdup_vprintf(fmt,cap);
1672 msg=g_strdup_vprintf(fmt,args);
1674 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1677 linphone_gtk_log_push(lev,fmt,args);
1681 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1682 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1683 linphone_gtk_get_main_window(), "uribar"));
1685 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1687 gtk_entry_set_text(uri_bar, refer_to);
1688 linphone_gtk_start_call(linphone_gtk_get_main_window());
1691 static void linphone_gtk_check_soundcards(){
1692 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1693 if (devices==NULL || devices[0]==NULL){
1694 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1695 _("No sound cards have been detected on this computer.\n"
1696 "You won't be able to send or receive audio calls."));
1701 // Display the account wizard
1702 void linphone_gtk_display_wizard() {
1703 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1704 the_wizard = linphone_gtk_create_assistant();
1709 static void linphone_gtk_quit(void){
1710 static gboolean quit_done=FALSE;
1713 linphone_gtk_unmonitor_usb();
1714 g_source_remove_by_user_data(linphone_gtk_get_core());
1715 linphone_gtk_uninit_instance();
1716 linphone_gtk_destroy_log_window();
1717 linphone_core_destroy(the_core);
1718 linphone_gtk_log_uninit();
1722 gdk_threads_leave();
1728 This is not the correct way to implement block termination.
1729 The good way would be to call gtk_main_quit(), and return TRUE.
1730 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1731 As a result the program cannot exit at all.
1732 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1734 static gboolean on_block_termination(void){
1736 linphone_gtk_quit();
1741 int main(int argc, char *argv[]){
1746 const char *factory_config_file;
1748 GtkSettings *settings;
1750 const char *app_name="Linphone";
1752 #if !GLIB_CHECK_VERSION(2, 31, 0)
1753 g_thread_init(NULL);
1757 progpath = strdup(argv[0]);
1759 config_file=linphone_gtk_get_config_file(NULL);
1763 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1764 if ((lang=getenv("LANG"))!=NULL){
1767 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1773 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1776 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1779 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1781 if (strncmp(lang,"zh",2)==0){
1782 workaround_gtk_entry_chinese_bug=TRUE;
1785 setenv("LANG",lang,1);
1790 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1791 if (p==NULL) perror("bindtextdomain failed");
1792 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1793 textdomain (GETTEXT_PACKAGE);
1795 g_message("NLS disabled.\n");
1798 gtk_rc_add_default_file("./gtkrc");
1800 gdk_threads_enter();
1802 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1803 linphone_options,NULL,NULL)){
1804 gdk_threads_leave();
1808 settings=gtk_settings_get_default();
1809 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1810 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1811 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1812 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1814 if (workingdir!=NULL){
1815 if (chdir(workingdir)==-1){
1816 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1820 /* Now, look for the factory configuration file, we do it this late
1821 since we want to have had time to change directory and to parse
1822 the options, in case we needed to access the working directory */
1823 factory_config_file = linphone_gtk_get_factory_config_file();
1825 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1826 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1827 g_warning("This instance is going to exit now.");
1828 gdk_threads_leave();
1832 add_pixmap_directory("pixmaps");
1833 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1836 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1837 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1838 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1839 /*never block termination:*/
1840 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1843 the_ui=linphone_gtk_create_window("main");
1845 linphone_gtk_create_log_window();
1846 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1848 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1850 g_set_application_name(app_name);
1851 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1852 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1854 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1855 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1856 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1857 linphone_gtk_init_main_window();
1860 // Veryfing if at least one sip account is configured. If not, show wizard
1861 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1862 linphone_gtk_display_wizard();
1866 #ifndef HAVE_GTK_OSX
1867 linphone_gtk_init_status_icon();
1870 linphone_gtk_show_main_window();
1871 linphone_gtk_check_soundcards();
1873 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1874 linphone_gtk_check_for_new_version();
1875 linphone_gtk_monitor_usb();
1878 linphone_gtk_quit();
1879 #ifndef HAVE_GTK_OSX
1880 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1881 gtk_status_icon_set_visible(icon,FALSE);