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 1
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 gboolean linphone_gtk_auto_answer(LinphoneCall *call);
67 static void linphone_gtk_status_icon_set_blinking(gboolean val);
70 static gboolean verbose=0;
71 static gboolean auto_answer = 0;
72 static gchar * addr_to_call = NULL;
73 static gboolean iconified=FALSE;
74 static gchar *workingdir=NULL;
75 static char *progpath=NULL;
76 gchar *linphone_logfile=NULL;
78 static GOptionEntry linphone_options[]={
82 .arg=G_OPTION_ARG_NONE,
83 .arg_data= (gpointer)&verbose,
84 .description=N_("log to stdout some debug information while running.")
87 .long_name = "logfile",
89 .arg = G_OPTION_ARG_STRING,
90 .arg_data = &linphone_logfile,
91 .description = N_("path to a file to write logs into.")
94 .long_name="iconified",
96 .arg=G_OPTION_ARG_NONE,
97 .arg_data= (gpointer)&iconified,
98 .description=N_("Start only in the system tray, do not show the main interface.")
103 .arg = G_OPTION_ARG_STRING,
104 .arg_data = &addr_to_call,
105 .description = N_("address to call right now")
108 .long_name = "auto-answer",
110 .arg = G_OPTION_ARG_NONE,
111 .arg_data = (gpointer) & auto_answer,
112 .description = N_("if set automatically answer incoming calls")
115 .long_name = "workdir",
117 .arg = G_OPTION_ARG_STRING,
118 .arg_data = (gpointer) & workingdir,
119 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
124 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
125 #define RELATIVE_XML_DIR
126 #define BUILD_TREE_XML_DIR "gtk"
129 #define CONFIG_FILE ".linphonerc"
130 #define SECRETS_FILE ".linphone-zidcache"
132 #define CONFIG_FILE "linphonerc"
133 #define SECRETS_FILE "linphone-zidcache"
137 char *linphone_gtk_get_config_file(const char *filename){
138 const int path_max=1024;
139 char *config_file=g_malloc0(path_max);
140 if (filename==NULL) filename=CONFIG_FILE;
141 /*try accessing a local file first if exists*/
142 if (access(CONFIG_FILE,F_OK)==0){
143 snprintf(config_file,path_max,"%s",filename);
146 const char *appdata=getenv("APPDATA");
148 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
149 CreateDirectory(config_file,NULL);
150 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
153 const char *home=getenv("HOME");
154 if (home==NULL) home=".";
155 snprintf(config_file,path_max,"%s/%s",home,filename);
162 #define FACTORY_CONFIG_FILE "linphonerc.factory"
163 static char _factory_config_file[1024];
164 static const char *linphone_gtk_get_factory_config_file(){
165 /*try accessing a local file first if exists*/
166 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
167 snprintf(_factory_config_file,sizeof(_factory_config_file),
168 "%s",FACTORY_CONFIG_FILE);
172 if (progpath != NULL) {
174 progdir = strdup(progpath);
176 basename = strrchr(progdir, '\\');
177 if (basename != NULL) {
180 snprintf(_factory_config_file, sizeof(_factory_config_file),
181 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
183 if (workingdir!=NULL) {
184 snprintf(_factory_config_file, sizeof(_factory_config_file),
185 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
192 basename = strrchr(progdir, '/');
193 if (basename != NULL) {
196 snprintf(_factory_config_file, sizeof(_factory_config_file),
197 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
206 return _factory_config_file;
209 static void linphone_gtk_init_liblinphone(const char *config_file,
210 const char *factory_config_file) {
211 LinphoneCoreVTable vtable={0};
212 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
214 vtable.call_state_changed=linphone_gtk_call_state_changed;
215 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
216 vtable.notify_presence_recv=linphone_gtk_notify_recv;
217 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
218 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
219 vtable.display_status=linphone_gtk_display_status;
220 vtable.display_message=linphone_gtk_display_message;
221 vtable.display_warning=linphone_gtk_display_warning;
222 vtable.display_url=linphone_gtk_display_url;
223 vtable.call_log_updated=linphone_gtk_call_log_updated;
224 vtable.text_received=linphone_gtk_text_received;
225 vtable.refer_received=linphone_gtk_refer_received;
226 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
227 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
229 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
230 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
231 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
232 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
233 g_free(secrets_file);
234 linphone_core_enable_video(the_core,TRUE,TRUE);
239 LinphoneCore *linphone_gtk_get_core(void){
243 GtkWidget *linphone_gtk_get_main_window(){
247 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
248 static const char *icon_path=NULL;
249 static const char *hiddens=NULL;
250 static const char *shown=NULL;
251 static bool_t config_loaded=FALSE;
252 if (linphone_gtk_get_core()==NULL) return;
253 if (config_loaded==FALSE){
254 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
255 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
256 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
260 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
262 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
264 GdkPixbuf *pbuf=create_pixbuf(icon_path);
265 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
266 g_object_unref(G_OBJECT(pbuf));
270 static int get_ui_file(const char *name, char *path, int pathsize){
271 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
272 if (access(path,F_OK)!=0){
273 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
274 if (access(path,F_OK)!=0){
275 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
276 INSTALLED_XML_DIR,name);
283 GtkWidget *linphone_gtk_create_window(const char *window_name){
284 GError* error = NULL;
285 GtkBuilder* builder = gtk_builder_new ();
289 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
291 if (!gtk_builder_add_from_file (builder, path, &error)){
292 g_error("Couldn't load builder file: %s", error->message);
293 g_error_free (error);
296 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
298 g_error("Could not retrieve '%s' window from xml file",window_name);
301 g_object_set_data(G_OBJECT(w),"builder",builder);
302 gtk_builder_connect_signals(builder,w);
303 linphone_gtk_configure_window(w,window_name);
307 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
310 GtkBuilder* builder = gtk_builder_new ();
312 gchar *object_ids[2];
313 object_ids[0]=g_strdup(widget_name);
316 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
317 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
318 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
319 g_error_free (error);
320 g_free(object_ids[0]);
323 g_free(object_ids[0]);
324 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
326 g_error("Could not retrieve '%s' window from xml file",widget_name);
329 g_object_set_data(G_OBJECT(w),"builder",builder);
330 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
331 gtk_builder_connect_signals(builder,w);
335 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
336 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
339 g_error("Fail to retrieve builder from window !");
342 w=gtk_builder_get_object(builder,name);
344 g_error("No widget named %s found in xml interface.",name);
346 return GTK_WIDGET(w);
350 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
352 GtkWidget *main_window=linphone_gtk_get_main_window();
354 gtk_widget_show(main_window);
355 if (type==GTK_MESSAGE_QUESTION)
357 /* draw a question box. link to dialog_click callback */
358 dialog = gtk_message_dialog_new (
359 GTK_WINDOW(main_window),
360 GTK_DIALOG_DESTROY_WITH_PARENT,
361 GTK_MESSAGE_QUESTION,
364 (const gchar*)message);
365 /* connect to some callback : REVISIT */
367 g_signal_connect_swapped (G_OBJECT (dialog), "response",
368 G_CALLBACK (dialog_click),
371 /* actually show the box */
372 gtk_widget_show(dialog);
376 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
377 GTK_DIALOG_DESTROY_WITH_PARENT,
381 (const gchar*)message);
382 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
383 g_signal_connect_swapped (G_OBJECT (dialog), "response",
384 G_CALLBACK (gtk_widget_destroy),
386 gtk_widget_show(dialog);
390 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
391 if (id==GTK_RESPONSE_CANCEL){
392 gtk_widget_destroy(GTK_WIDGET(dialog));
396 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
397 g_message("About url clicked");
398 linphone_gtk_open_browser(url);
401 void linphone_gtk_show_about(){
402 struct stat filestat;
403 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
406 GdkPixbuf *logo=create_pixbuf(
407 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
408 static const char *defcfg="defcfg";
410 about=linphone_gtk_create_window("about");
411 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
412 memset(&filestat,0,sizeof(filestat));
413 if (stat(license_file,&filestat)!=0){
414 license_file="COPYING";
415 stat(license_file,&filestat);
417 if (filestat.st_size>0){
418 char *license=g_malloc(filestat.st_size+1);
419 FILE *f=fopen(license_file,"r");
420 if (f && fread(license,filestat.st_size,1,f)==1){
421 license[filestat.st_size]='\0';
422 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
426 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
427 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
428 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
429 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
430 tmp=linphone_gtk_get_ui_config("artists",defcfg);
435 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
437 tmp=linphone_gtk_get_ui_config("translators",defcfg);
439 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
440 tmp=linphone_gtk_get_ui_config("comments",defcfg);
442 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
443 gtk_widget_show(about);
446 static void set_video_window_decorations(GdkWindow *w){
447 const char *title=linphone_gtk_get_ui_config("title","Linphone");
448 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
449 char video_title[256];
450 GdkPixbuf *pbuf=create_pixbuf(icon_path);
451 if (!linphone_core_in_call(linphone_gtk_get_core())){
452 snprintf(video_title,sizeof(video_title),"%s video",title);
453 /* When not in call, treat the video as a normal window */
454 gdk_window_set_keep_above(w, FALSE);
456 LinphoneAddress *uri =
457 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
460 linphone_address_clean(uri);
461 if (linphone_address_get_display_name(uri)!=NULL){
462 display_name=ms_strdup(linphone_address_get_display_name(uri));
464 display_name=linphone_address_as_string(uri);
466 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
467 linphone_address_destroy(uri);
468 ms_free(display_name);
470 /* During calls, bring up the video window, arrange so that
471 it is above all the other windows */
472 gdk_window_deiconify(w);
473 gdk_window_set_keep_above(w,TRUE);
474 /* Maybe we should have the following, but then we want to
475 have a timer that turns it off after a little while. */
476 /* gdk_window_set_urgency_hint(w,TRUE); */
478 gdk_window_set_title(w,video_title);
479 /* Refrain the video window to be closed at all times. */
480 gdk_window_set_functions(w,
481 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
482 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
485 l=g_list_append(l,pbuf);
486 gdk_window_set_icon_list(w,l);
488 g_object_unref(G_OBJECT(pbuf));
492 static gboolean video_needs_update=FALSE;
494 static void update_video_title(){
495 video_needs_update=TRUE;
498 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
499 static gboolean first_time=TRUE;
501 static unsigned long previd=0;
502 static unsigned long preview_previd=0;
503 static gboolean in_iterate=FALSE;
506 if (in_iterate) return TRUE;
508 linphone_core_iterate(lc);
510 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
511 linphone_gtk_show_directory_search();
515 id=linphone_core_get_native_video_window_id(lc);
516 if (id!=previd || video_needs_update){
520 ms_message("Updating window decorations");
522 w=gdk_window_foreign_new(id);
524 w=gdk_window_foreign_new((HANDLE)id);
527 set_video_window_decorations(w);
528 g_object_unref(G_OBJECT(w));
530 else ms_error("gdk_window_foreign_new() failed");
531 if (video_needs_update) video_needs_update=FALSE;
534 id=linphone_core_get_native_preview_window_id (lc);
535 if (id!=preview_previd ){
539 ms_message("Updating window decorations for preview");
541 w=gdk_window_foreign_new(id);
543 w=gdk_window_foreign_new((HANDLE)id);
546 set_video_window_decorations(w);
547 g_object_unref(G_OBJECT(w));
549 else ms_error("gdk_window_foreign_new() failed");
550 if (video_needs_update) video_needs_update=FALSE;
553 if (addr_to_call!=NULL){
554 /*make sure we are not showing the login screen*/
555 GtkWidget *mw=linphone_gtk_get_main_window();
556 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
557 if (!GTK_WIDGET_VISIBLE(login_frame)){
558 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
559 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
561 linphone_gtk_start_call(uri_bar);
568 static void load_uri_history(){
569 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
572 GtkEntryCompletion *gep=gtk_entry_completion_new();
573 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
576 snprintf(key,sizeof(key),"uri%i",i);
577 uri=linphone_gtk_get_ui_config(key,NULL);
580 gtk_list_store_append(model,&iter);
581 gtk_list_store_set(model,&iter,0,uri,-1);
582 if (i==0) gtk_entry_set_text(uribar,uri);
586 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
587 gtk_entry_completion_set_text_column(gep,0);
588 gtk_entry_set_completion(uribar,gep);
591 static void save_uri_history(){
592 LinphoneCore *lc=linphone_gtk_get_core();
593 LpConfig *cfg=linphone_core_get_config(lc);
594 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
599 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
601 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
603 gtk_tree_model_get(model,&iter,0,&uri,-1);
605 snprintf(key,sizeof(key),"uri%i",i);
606 lp_config_set_string(cfg,"GtkUi",key,uri);
611 }while(gtk_tree_model_iter_next(model,&iter));
615 static void completion_add_text(GtkEntry *entry, const char *text){
617 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
619 if (gtk_tree_model_get_iter_first(model,&iter)){
622 gtk_tree_model_get(model,&iter,0,&uri,-1);
624 if (strcmp(uri,text)==0) {
626 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
632 }while (gtk_tree_model_iter_next(model,&iter));
634 /* and prepend it on top of the list */
635 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
636 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
641 void linphone_gtk_show_main_window(){
642 GtkWidget *w=linphone_gtk_get_main_window();
643 LinphoneCore *lc=linphone_gtk_get_core();
644 if (linphone_core_video_enabled(lc)){
645 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
646 VIDEOSELFVIEW_DEFAULT));
649 gtk_window_present(GTK_WINDOW(w));
652 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
653 GtkWidget *mw=linphone_gtk_get_main_window();
654 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
655 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
656 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
658 if (linphone_gtk_use_in_call_view() && call)
659 linphone_gtk_in_call_view_terminate(call,error);
660 update_video_title();
663 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
664 LinphoneCore *lc=linphone_gtk_get_core();
665 GtkWidget *mw=linphone_gtk_get_main_window();
666 const MSList *calls=linphone_core_get_calls(lc);
668 bool_t start_active=TRUE;
669 bool_t stop_active=FALSE;
670 bool_t add_call=FALSE;
671 int call_list_size=ms_list_size(calls);
681 button=linphone_gtk_get_widget(mw,"start_call");
682 gtk_widget_set_sensitive(button,start_active);
683 gtk_widget_set_visible(button,!add_call);
685 button=linphone_gtk_get_widget(mw,"add_call");
686 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
687 gtk_widget_set_sensitive(button,FALSE);
689 gtk_widget_set_sensitive(button,start_active);
691 gtk_widget_set_visible(button,add_call);
693 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
695 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
696 linphone_gtk_enable_conference_button(lc,call_list_size>1);
697 update_video_title();
698 if (call) linphone_gtk_update_video_button(call);
701 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
702 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
703 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
704 completion_add_text(GTK_ENTRY(uri_bar),entered);
706 linphone_gtk_call_terminated(NULL,NULL);
711 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
712 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
713 linphone_core_accept_call (linphone_gtk_get_core(),call);
714 linphone_call_unref(call);
720 void linphone_gtk_start_call(GtkWidget *w){
721 LinphoneCore *lc=linphone_gtk_get_core();
723 /*change into in-call mode, then do the work later as it might block a bit */
724 GtkWidget *mw=gtk_widget_get_toplevel(w);
725 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
727 call=linphone_gtk_get_currently_displayed_call(NULL);
728 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
729 linphone_core_accept_call(lc,call);
731 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
732 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
733 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
734 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
739 void linphone_gtk_uri_bar_activate(GtkWidget *w){
740 linphone_gtk_start_call(w);
744 void linphone_gtk_terminate_call(GtkWidget *button){
746 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
748 linphone_core_terminate_call(linphone_gtk_get_core(),call);
750 linphone_core_terminate_conference(linphone_gtk_get_core());
754 void linphone_gtk_decline_clicked(GtkWidget *button){
755 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
757 linphone_core_terminate_call(linphone_gtk_get_core(),call);
760 void linphone_gtk_answer_clicked(GtkWidget *button){
761 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
763 linphone_core_accept_call(linphone_gtk_get_core(),call);
764 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
768 void linphone_gtk_enable_video(GtkWidget *w){
769 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
770 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
771 LinphoneVideoPolicy policy={0};
772 policy.automatically_initiate=policy.automatically_accept=val;
773 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
774 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
776 gtk_widget_set_sensitive(selfview_item,val);
778 linphone_core_enable_video_preview(linphone_gtk_get_core(),
779 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
781 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
785 void linphone_gtk_enable_self_view(GtkWidget *w){
786 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
787 LinphoneCore *lc=linphone_gtk_get_core();
788 linphone_core_enable_video_preview(lc,val);
789 linphone_core_enable_self_view(lc,val);
790 linphone_gtk_set_ui_config_int("videoselfview",val);
793 void linphone_gtk_used_identity_changed(GtkWidget *w){
794 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
795 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
796 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
797 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
798 linphone_gtk_show_directory_search();
800 if (sel) g_free(sel);
803 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
804 linphone_gtk_show_friends();
807 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
809 case GTK_RESPONSE_YES:
810 linphone_gtk_show_contact(lf);
813 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
815 gtk_widget_destroy(dialog);
818 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
821 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
822 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
826 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);
827 dialog = gtk_message_dialog_new (
828 GTK_WINDOW(linphone_gtk_get_main_window()),
829 GTK_DIALOG_DESTROY_WITH_PARENT,
830 GTK_MESSAGE_QUESTION,
835 g_signal_connect(G_OBJECT (dialog), "response",
836 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
837 /* actually show the box */
838 gtk_widget_show(dialog);
841 typedef struct _AuthTimeout{
846 static void auth_timeout_clean(AuthTimeout *tout){
850 static gboolean auth_timeout_destroy(AuthTimeout *tout){
852 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
853 gtk_widget_destroy(tout->w);
859 static AuthTimeout * auth_timeout_new(GtkWidget *w){
860 AuthTimeout *tout=g_new(AuthTimeout,1);
862 /*so that the timeout no more references the widget when it is destroyed:*/
863 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
864 /*so that the widget is automatically destroyed after some time */
865 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
869 void linphone_gtk_password_cancel(GtkWidget *w){
870 LinphoneAuthInfo *info;
871 GtkWidget *window=gtk_widget_get_toplevel(w);
872 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
873 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
874 gtk_widget_destroy(window);
877 void linphone_gtk_password_ok(GtkWidget *w){
879 GtkWidget *window=gtk_widget_get_toplevel(w);
880 LinphoneAuthInfo *info;
881 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
882 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
883 entry=linphone_gtk_get_widget(window,"password_entry");
884 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
885 linphone_auth_info_set_userid(info,
886 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
887 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
888 gtk_widget_destroy(window);
891 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
892 GtkWidget *w=linphone_gtk_create_window("password");
893 GtkWidget *label=linphone_gtk_get_widget(w,"message");
894 LinphoneAuthInfo *info;
896 GtkWidget *mw=linphone_gtk_get_main_window();
898 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
899 /*don't prompt for authentication when login frame is visible*/
900 linphone_core_abort_authentication(lc,NULL);
904 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
906 gtk_label_set_markup(GTK_LABEL(label),msg);
908 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
909 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
910 g_object_set_data(G_OBJECT(w),"auth_info",info);
911 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
916 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
917 GtkWidget *w=linphone_gtk_get_main_window();
918 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
919 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
920 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
924 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
925 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
928 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
929 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
932 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
934 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
935 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
938 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
939 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
940 if (w) linphone_gtk_call_log_update(w);
941 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
945 static bool_t notify_actions_supported() {
946 bool_t accepts_actions = FALSE;
947 GList *capabilities = notify_get_server_caps();
949 if(capabilities != NULL) {
950 for(c = capabilities; c != NULL; c = c->next) {
951 if(strcmp((char*)c->data, "actions") == 0 ) {
952 accepts_actions = TRUE;
956 g_list_foreach(capabilities, (GFunc)g_free, NULL);
957 g_list_free(capabilities);
959 return accepts_actions;
962 static NotifyNotification* build_notification(const char *title, const char *body){
963 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
970 static void show_notification(NotifyNotification* n){
971 if (n && !notify_notification_show(n,NULL))
972 ms_error("Failed to send notification.");
975 static void make_notification(const char *title, const char *body){
976 show_notification(build_notification(title,body));
981 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
983 if (!notify_is_initted())
984 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
988 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
993 ms_error("Failed to send notification.");
995 linphone_gtk_show_main_window();
997 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1000 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1001 NotifyNotification *n;
1002 switch(linphone_call_get_state(call)){
1003 case LinphoneCallError:
1004 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1006 case LinphoneCallEnd:
1007 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1009 case LinphoneCallIncomingReceived:
1010 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1011 if (notify_actions_supported()) {
1012 notify_notification_add_action (n,"answer", _("Answer"),
1013 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1014 notify_notification_add_action (n,"decline",_("Decline"),
1015 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1017 show_notification(n);
1019 case LinphoneCallPausedByRemote:
1020 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1025 if (body) g_free(body);
1026 if (remote) g_free(remote);
1031 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1032 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1033 LinphoneCore *lc=linphone_call_get_core(call);
1034 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1035 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1036 linphone_core_accept_call_update(lc,call,params);
1037 linphone_call_params_destroy(params);
1039 linphone_call_unref(call);
1040 g_source_remove_by_user_data(dialog);
1041 gtk_widget_destroy(dialog);
1044 static void on_call_updated_timeout(GtkWidget *dialog){
1045 gtk_widget_destroy(dialog);
1048 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1049 LinphoneCore *lc=linphone_call_get_core(call);
1050 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1051 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1052 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1053 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1054 gboolean video_used=linphone_call_params_video_enabled(current_params);
1055 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1056 video_used,video_requested,pol->automatically_accept);
1057 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1058 linphone_core_defer_call_update(lc,call);
1060 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1062 const char *dname=linphone_address_get_display_name(addr);
1063 if (dname==NULL) dname=linphone_address_get_username(addr);
1064 if (dname==NULL) dname=linphone_address_get_domain(addr);
1065 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1066 GTK_DIALOG_DESTROY_WITH_PARENT,
1067 GTK_MESSAGE_WARNING,
1069 _("%s proposed to start video. Do you accept ?"),dname);
1070 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1071 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1072 gtk_widget_show(dialog);
1077 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1079 case LinphoneCallOutgoingInit:
1080 linphone_gtk_create_in_call_view (call);
1082 case LinphoneCallOutgoingProgress:
1083 linphone_gtk_in_call_view_set_calling (call);
1085 case LinphoneCallStreamsRunning:
1086 linphone_gtk_in_call_view_set_in_call(call);
1088 case LinphoneCallUpdatedByRemote:
1089 linphone_gtk_call_updated_by_remote(call);
1091 case LinphoneCallError:
1092 linphone_gtk_in_call_view_terminate (call,msg);
1094 case LinphoneCallEnd:
1095 linphone_gtk_in_call_view_terminate(call,NULL);
1096 linphone_gtk_status_icon_set_blinking(FALSE);
1098 case LinphoneCallIncomingReceived:
1099 linphone_gtk_create_in_call_view(call);
1100 linphone_gtk_in_call_view_set_incoming(call);
1101 linphone_gtk_status_icon_set_blinking(TRUE);
1103 linphone_call_ref(call);
1104 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1107 case LinphoneCallResuming:
1108 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1109 linphone_gtk_in_call_view_set_in_call (call);
1111 case LinphoneCallPausing:
1112 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1113 case LinphoneCallPausedByRemote:
1114 linphone_gtk_in_call_view_set_paused(call);
1116 case LinphoneCallConnected:
1117 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1118 linphone_gtk_status_icon_set_blinking(FALSE);
1123 linphone_gtk_notify(call, msg);
1124 linphone_gtk_update_call_buttons (call);
1127 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1128 linphone_gtk_in_call_view_show_encryption(call);
1131 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1132 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1133 GtkTreeModel *model=gtk_combo_box_get_model(box);
1135 gboolean found=FALSE;
1136 const char *stock_id=NULL;
1138 if (gtk_tree_model_get_iter_first(model,&iter)){
1141 gtk_tree_model_get(model,&iter,2,&p,-1);
1146 }while(gtk_tree_model_iter_next(model,&iter));
1149 g_warning("Could not find proxy config in combo box of identities.");
1153 case LinphoneRegistrationOk:
1154 stock_id=GTK_STOCK_YES;
1156 case LinphoneRegistrationProgress:
1157 stock_id=GTK_STOCK_REFRESH;
1159 case LinphoneRegistrationCleared:
1162 case LinphoneRegistrationFailed:
1163 stock_id=GTK_STOCK_DIALOG_WARNING;
1168 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1171 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1172 LinphoneRegistrationState rs, const char *msg){
1174 case LinphoneRegistrationOk:
1176 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1177 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1178 linphone_gtk_exit_login_frame();
1185 update_registration_status(cfg,rs);
1188 void linphone_gtk_open_browser(const char *url){
1189 /*in gtk 2.16, gtk_show_uri does not work...*/
1191 #if GTK_CHECK_VERSION(2,18,3)
1192 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1195 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1196 g_spawn_command_line_async(cl,NULL);
1199 ShellExecute(0,"open",url,NULL,NULL,1);
1203 void linphone_gtk_link_to_website(GtkWidget *item){
1204 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1205 linphone_gtk_open_browser(home);
1208 #ifndef HAVE_GTK_OSX
1210 static GtkStatusIcon *icon=NULL;
1212 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1213 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1214 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1217 static GtkWidget *create_icon_menu(){
1218 GtkWidget *menu=gtk_menu_new();
1219 GtkWidget *menu_item;
1222 const gchar *homesite;
1224 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1225 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1226 tmp=g_strdup(homesite);
1227 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1228 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1230 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1231 gtk_widget_show(image);
1232 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1233 //g_object_unref(G_OBJECT(image));
1234 gtk_widget_show(menu_item);
1235 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1236 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1238 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1239 gtk_widget_show(menu_item);
1240 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1241 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1242 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1243 gtk_widget_show(menu_item);
1244 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1245 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1246 gtk_widget_show(menu);
1250 static void handle_icon_click() {
1251 GtkWidget *mw=linphone_gtk_get_main_window();
1252 if (!gtk_window_is_active((GtkWindow*)mw)) {
1253 linphone_gtk_show_main_window();
1255 gtk_widget_hide(mw);
1259 static void linphone_gtk_init_status_icon(){
1260 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1261 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1262 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1263 GtkWidget *menu=create_icon_menu();
1265 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1266 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1267 #if GTK_CHECK_VERSION(2,20,0)
1268 gtk_status_icon_set_name(icon,title);
1270 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1271 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1272 gtk_status_icon_set_tooltip(icon,title);
1273 gtk_status_icon_set_visible(icon,TRUE);
1274 g_object_set_data(G_OBJECT(icon),"menu",menu);
1275 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1276 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1277 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1278 pbuf=create_pixbuf(call_icon_path);
1279 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1282 static gboolean do_icon_blink(GtkStatusIcon *gi){
1283 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1284 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1285 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1286 if (cur_icon==call_icon){
1287 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1289 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1296 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1298 static gint attention_id;
1299 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1301 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1302 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1306 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1307 if (val && tout==0){
1308 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1309 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1310 }else if (!val && tout!=0){
1311 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1312 g_source_remove(tout);
1313 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1314 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1320 void linphone_gtk_options_activate(GtkWidget *item){
1321 #ifndef HAVE_GTK_OSX
1322 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1327 static void init_identity_combo(GtkComboBox *box){
1328 GtkListStore *store;
1329 GtkCellRenderer *r1,*r2;
1330 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1331 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1332 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1333 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1334 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1335 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1336 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1337 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1340 void linphone_gtk_load_identities(void){
1342 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1344 LinphoneProxyConfig *def=NULL;
1346 GtkListStore *store;
1349 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1350 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1351 /* model is empty, this is the first time we go here */
1352 init_identity_combo(box);
1353 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1355 gtk_list_store_clear(store);
1356 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1357 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1358 gtk_list_store_append(store,&iter);
1359 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1360 g_free(def_identity);
1361 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1363 elem=ms_list_next(elem),i++){
1364 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1365 gtk_list_store_append(store,&iter);
1366 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1367 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1373 gtk_combo_box_set_active(box,def_index);
1376 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1377 const char *label=gtk_button_get_label(button);
1378 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1380 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1381 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1382 if (linphone_core_in_call(linphone_gtk_get_core())){
1383 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1387 static void linphone_gtk_dtmf_released(GtkButton *button){
1388 linphone_core_stop_dtmf (linphone_gtk_get_core());
1391 static void linphone_gtk_connect_digits(void){
1392 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1393 GList *children=gtk_container_get_children(cont);
1395 for(elem=children;elem!=NULL;elem=elem->next){
1396 GtkButton *button=GTK_BUTTON(elem->data);
1397 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1398 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1402 static void linphone_gtk_check_menu_items(void){
1403 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(linphone_gtk_get_core());
1404 bool_t video_enabled=pol->automatically_accept && pol->automatically_initiate;
1405 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1406 GtkWidget *selfview_item=linphone_gtk_get_widget(
1407 linphone_gtk_get_main_window(),"selfview_item");
1408 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1409 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1410 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1411 gtk_widget_set_sensitive(selfview_item,video_enabled);
1414 static gboolean linphone_gtk_can_manage_accounts(){
1415 LinphoneCore *lc=linphone_gtk_get_core();
1417 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1418 SipSetup *ss=(SipSetup*)elem->data;
1419 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1426 static void linphone_gtk_configure_main_window(){
1427 static gboolean config_loaded=FALSE;
1428 static const char *title;
1429 static const char *home;
1430 static const char *start_call_icon;
1431 static const char *add_call_icon;
1432 static const char *stop_call_icon;
1433 static const char *search_icon;
1434 static gboolean update_check_menu;
1435 static gboolean buttons_have_borders;
1436 static gboolean show_abcd;
1437 GtkWidget *w=linphone_gtk_get_main_window();
1438 if (!config_loaded){
1439 title=linphone_gtk_get_ui_config("title","Linphone");
1440 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1441 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1442 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1443 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1444 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1445 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1446 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1447 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1450 linphone_gtk_configure_window(w,"main_window");
1452 gtk_window_set_title(GTK_WINDOW(w),title);
1454 if (start_call_icon){
1455 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1456 create_pixmap (start_call_icon));
1457 if (!buttons_have_borders)
1458 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1461 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1462 create_pixmap (add_call_icon));
1463 if (!buttons_have_borders)
1464 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1466 if (stop_call_icon){
1467 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1468 create_pixmap (stop_call_icon));
1469 if (!buttons_have_borders)
1470 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1473 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1474 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1475 g_object_unref(G_OBJECT(pbuf));
1479 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1481 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1485 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1487 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1488 g_object_unref(G_OBJECT(pbuf));
1493 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1495 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1498 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1499 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1500 gtk_image_set_from_pixbuf(img,scaled);
1501 g_object_unref(G_OBJECT(scaled));
1502 g_object_unref(G_OBJECT(pbuf));
1505 if (linphone_gtk_can_manage_accounts()) {
1506 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1508 if (update_check_menu){
1509 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1512 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1513 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1514 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1515 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1516 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1520 void linphone_gtk_manage_login(void){
1521 LinphoneCore *lc=linphone_gtk_get_core();
1522 LinphoneProxyConfig *cfg=NULL;
1523 linphone_core_get_default_proxy(lc,&cfg);
1525 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1526 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1527 linphone_gtk_show_login_frame(cfg);
1533 gboolean linphone_gtk_close(GtkWidget *mw){
1534 /*shutdown calls if any*/
1535 LinphoneCore *lc=linphone_gtk_get_core();
1536 if (linphone_core_in_call(lc)){
1537 linphone_core_terminate_all_calls(lc);
1539 linphone_core_enable_video_preview(lc,FALSE);
1540 #ifdef __APPLE__ /*until with have a better option*/
1541 gtk_window_iconify(GTK_WINDOW(mw));
1543 gtk_widget_hide(mw);
1549 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1550 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1551 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1553 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1554 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1561 static void linphone_gtk_init_main_window(){
1562 GtkWidget *main_window;
1564 linphone_gtk_configure_main_window();
1565 linphone_gtk_manage_login();
1567 linphone_gtk_load_identities();
1568 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1569 linphone_gtk_show_friends();
1570 linphone_gtk_connect_digits();
1571 main_window=linphone_gtk_get_main_window();
1572 linphone_gtk_call_log_update(main_window);
1574 linphone_gtk_update_call_buttons (NULL);
1575 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1576 g_signal_connect (G_OBJECT (main_window), "delete-event",
1577 G_CALLBACK (linphone_gtk_close), main_window);
1580 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1581 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1582 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1583 gtk_widget_hide(menubar);
1584 gtk_osxapplication_ready(theMacApp);
1586 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1588 linphone_gtk_check_menu_items();
1592 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1594 const char *lname="undef";
1596 #if defined(__linux) || defined(__APPLE__)
1597 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1616 g_error("Bad level !");
1618 #if defined(__linux) || defined(__APPLE__)
1620 msg=g_strdup_vprintf(fmt,cap);
1623 msg=g_strdup_vprintf(fmt,args);
1625 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1628 linphone_gtk_log_push(lev,fmt,args);
1632 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1633 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1634 linphone_gtk_get_main_window(), "uribar"));
1636 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1638 gtk_entry_set_text(uri_bar, refer_to);
1639 linphone_gtk_start_call(linphone_gtk_get_main_window());
1642 static void linphone_gtk_check_soundcards(){
1643 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1644 if (devices==NULL || devices[0]==NULL){
1645 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1646 _("No sound cards have been detected on this computer.\n"
1647 "You won't be able to send or receive audio calls."));
1652 // Display the account wizard
1653 void linphone_gtk_display_wizard() {
1654 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1655 the_wizard = linphone_gtk_create_assistant();
1660 static void linphone_gtk_quit(void){
1661 static gboolean quit_done=FALSE;
1664 g_source_remove_by_user_data(linphone_gtk_get_core());
1665 linphone_gtk_uninit_instance();
1666 linphone_gtk_destroy_log_window();
1667 linphone_core_destroy(the_core);
1668 linphone_gtk_log_uninit();
1672 gdk_threads_leave();
1678 This is not the correct way to implement block termination.
1679 The good way would be to call gtk_main_quit(), and return TRUE.
1680 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1681 As a result the program cannot exit at all.
1682 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1684 static gboolean on_block_termination(void){
1686 linphone_gtk_quit();
1691 int main(int argc, char *argv[]){
1696 const char *factory_config_file;
1698 GtkSettings *settings;
1700 const char *app_name="Linphone";
1702 #if !GLIB_CHECK_VERSION(2, 31, 0)
1703 g_thread_init(NULL);
1707 progpath = strdup(argv[0]);
1709 config_file=linphone_gtk_get_config_file(NULL);
1713 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1714 if ((lang=getenv("LANG"))!=NULL){
1717 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1723 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1726 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1729 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1732 setenv("LANG",lang,1);
1737 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1738 if (p==NULL) perror("bindtextdomain failed");
1739 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1740 textdomain (GETTEXT_PACKAGE);
1742 g_message("NLS disabled.\n");
1745 gtk_rc_add_default_file("./gtkrc");
1747 gdk_threads_enter();
1749 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1750 linphone_options,NULL,NULL)){
1751 gdk_threads_leave();
1755 settings=gtk_settings_get_default();
1756 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1757 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1758 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1759 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1761 if (workingdir!=NULL){
1762 if (chdir(workingdir)==-1){
1763 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1767 /* Now, look for the factory configuration file, we do it this late
1768 since we want to have had time to change directory and to parse
1769 the options, in case we needed to access the working directory */
1770 factory_config_file = linphone_gtk_get_factory_config_file();
1772 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1773 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1774 g_warning("This instance is going to exit now.");
1775 gdk_threads_leave();
1779 add_pixmap_directory("pixmaps");
1780 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1783 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1784 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1785 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1786 /*never block termination:*/
1787 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1790 the_ui=linphone_gtk_create_window("main");
1792 linphone_gtk_create_log_window();
1793 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1795 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1797 g_set_application_name(app_name);
1798 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1799 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1801 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1802 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1803 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1804 linphone_gtk_init_main_window();
1807 // Veryfing if at least one sip account is configured. If not, show wizard
1808 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1809 linphone_gtk_display_wizard();
1813 #ifndef HAVE_GTK_OSX
1814 linphone_gtk_init_status_icon();
1817 linphone_gtk_show_main_window();
1818 linphone_gtk_check_soundcards();
1820 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1821 linphone_gtk_check_for_new_version();
1824 linphone_gtk_quit();
1825 #ifndef HAVE_GTK_OSX
1826 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1827 gtk_status_icon_set_visible(icon,FALSE);