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;
81 static GOptionEntry linphone_options[]={
85 .arg=G_OPTION_ARG_NONE,
86 .arg_data= (gpointer)&verbose,
87 .description=N_("log to stdout some debug information while running.")
90 .long_name = "logfile",
92 .arg = G_OPTION_ARG_STRING,
93 .arg_data = &linphone_logfile,
94 .description = N_("path to a file to write logs into.")
97 .long_name = "no-video",
99 .arg = G_OPTION_ARG_NONE,
100 .arg_data = (gpointer)&no_video,
101 .description = N_("Start linphone with video disabled.")
104 .long_name="iconified",
106 .arg=G_OPTION_ARG_NONE,
107 .arg_data= (gpointer)&iconified,
108 .description=N_("Start only in the system tray, do not show the main interface.")
113 .arg = G_OPTION_ARG_STRING,
114 .arg_data = &addr_to_call,
115 .description = N_("address to call right now")
118 .long_name = "auto-answer",
120 .arg = G_OPTION_ARG_NONE,
121 .arg_data = (gpointer) & auto_answer,
122 .description = N_("if set automatically answer incoming calls")
125 .long_name = "workdir",
127 .arg = G_OPTION_ARG_STRING,
128 .arg_data = (gpointer) & workingdir,
129 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
134 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
135 #define RELATIVE_XML_DIR
136 #define BUILD_TREE_XML_DIR "gtk"
139 #define CONFIG_FILE ".linphonerc"
140 #define SECRETS_FILE ".linphone-zidcache"
142 #define CONFIG_FILE "linphonerc"
143 #define SECRETS_FILE "linphone-zidcache"
147 char *linphone_gtk_get_config_file(const char *filename){
148 const int path_max=1024;
149 char *config_file=g_malloc0(path_max);
150 if (filename==NULL) filename=CONFIG_FILE;
151 /*try accessing a local file first if exists*/
152 if (access(CONFIG_FILE,F_OK)==0){
153 snprintf(config_file,path_max,"%s",filename);
156 const char *appdata=getenv("APPDATA");
158 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
159 CreateDirectory(config_file,NULL);
160 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
163 const char *home=getenv("HOME");
164 if (home==NULL) home=".";
165 snprintf(config_file,path_max,"%s/%s",home,filename);
172 #define FACTORY_CONFIG_FILE "linphonerc.factory"
173 static char _factory_config_file[1024];
174 static const char *linphone_gtk_get_factory_config_file(){
175 /*try accessing a local file first if exists*/
176 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
177 snprintf(_factory_config_file,sizeof(_factory_config_file),
178 "%s",FACTORY_CONFIG_FILE);
182 if (progpath != NULL) {
184 progdir = strdup(progpath);
186 basename = strrchr(progdir, '\\');
187 if (basename != NULL) {
190 snprintf(_factory_config_file, sizeof(_factory_config_file),
191 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
193 if (workingdir!=NULL) {
194 snprintf(_factory_config_file, sizeof(_factory_config_file),
195 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
202 basename = strrchr(progdir, '/');
203 if (basename != NULL) {
206 snprintf(_factory_config_file, sizeof(_factory_config_file),
207 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
216 return _factory_config_file;
219 static void linphone_gtk_init_liblinphone(const char *config_file,
220 const char *factory_config_file) {
221 LinphoneCoreVTable vtable={0};
222 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
224 vtable.call_state_changed=linphone_gtk_call_state_changed;
225 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
226 vtable.notify_presence_recv=linphone_gtk_notify_recv;
227 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
228 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
229 vtable.display_status=linphone_gtk_display_status;
230 vtable.display_message=linphone_gtk_display_message;
231 vtable.display_warning=linphone_gtk_display_warning;
232 vtable.display_url=linphone_gtk_display_url;
233 vtable.call_log_updated=linphone_gtk_call_log_updated;
234 vtable.text_received=linphone_gtk_text_received;
235 vtable.refer_received=linphone_gtk_refer_received;
236 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
237 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
238 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
240 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
241 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
242 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
243 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
244 g_free(secrets_file);
245 linphone_core_enable_video(the_core,TRUE,TRUE);
247 _linphone_gtk_enable_video(FALSE);
248 linphone_gtk_set_ui_config_int("videoselfview",0);
254 LinphoneCore *linphone_gtk_get_core(void){
258 GtkWidget *linphone_gtk_get_main_window(){
262 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
263 static const char *icon_path=NULL;
264 static const char *hiddens=NULL;
265 static const char *shown=NULL;
266 static bool_t config_loaded=FALSE;
267 if (linphone_gtk_get_core()==NULL) return;
268 if (config_loaded==FALSE){
269 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
270 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
271 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
275 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
277 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
279 GdkPixbuf *pbuf=create_pixbuf(icon_path);
280 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
281 g_object_unref(G_OBJECT(pbuf));
285 static int get_ui_file(const char *name, char *path, int pathsize){
286 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
287 if (access(path,F_OK)!=0){
288 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
289 if (access(path,F_OK)!=0){
290 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
291 INSTALLED_XML_DIR,name);
298 GtkWidget *linphone_gtk_create_window(const char *window_name){
299 GError* error = NULL;
300 GtkBuilder* builder = gtk_builder_new ();
304 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
306 if (!gtk_builder_add_from_file (builder, path, &error)){
307 g_error("Couldn't load builder file: %s", error->message);
308 g_error_free (error);
311 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
313 g_error("Could not retrieve '%s' window from xml file",window_name);
316 g_object_set_data(G_OBJECT(w),"builder",builder);
317 gtk_builder_connect_signals(builder,w);
318 linphone_gtk_configure_window(w,window_name);
322 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
325 GtkBuilder* builder = gtk_builder_new ();
327 gchar *object_ids[2];
328 object_ids[0]=g_strdup(widget_name);
331 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
332 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
333 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
334 g_error_free (error);
335 g_free(object_ids[0]);
338 g_free(object_ids[0]);
339 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
341 g_error("Could not retrieve '%s' window from xml file",widget_name);
344 g_object_set_data(G_OBJECT(w),"builder",builder);
345 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
346 gtk_builder_connect_signals(builder,w);
350 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
351 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
354 g_error("Fail to retrieve builder from window !");
357 w=gtk_builder_get_object(builder,name);
359 g_error("No widget named %s found in xml interface.",name);
361 return GTK_WIDGET(w);
365 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
367 GtkWidget *main_window=linphone_gtk_get_main_window();
369 gtk_widget_show(main_window);
370 if (type==GTK_MESSAGE_QUESTION)
372 /* draw a question box. link to dialog_click callback */
373 dialog = gtk_message_dialog_new (
374 GTK_WINDOW(main_window),
375 GTK_DIALOG_DESTROY_WITH_PARENT,
376 GTK_MESSAGE_QUESTION,
379 (const gchar*)message);
380 /* connect to some callback : REVISIT */
382 g_signal_connect_swapped (G_OBJECT (dialog), "response",
383 G_CALLBACK (dialog_click),
386 /* actually show the box */
387 gtk_widget_show(dialog);
391 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
392 GTK_DIALOG_DESTROY_WITH_PARENT,
396 (const gchar*)message);
397 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
398 g_signal_connect_swapped (G_OBJECT (dialog), "response",
399 G_CALLBACK (gtk_widget_destroy),
401 gtk_widget_show(dialog);
405 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
406 if (id==GTK_RESPONSE_CANCEL){
407 gtk_widget_destroy(GTK_WIDGET(dialog));
411 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
412 g_message("About url clicked");
413 linphone_gtk_open_browser(url);
416 void linphone_gtk_show_about(){
417 struct stat filestat;
418 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
421 GdkPixbuf *logo=create_pixbuf(
422 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
423 static const char *defcfg="defcfg";
425 about=linphone_gtk_create_window("about");
426 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
427 memset(&filestat,0,sizeof(filestat));
428 if (stat(license_file,&filestat)!=0){
429 license_file="COPYING";
430 stat(license_file,&filestat);
432 if (filestat.st_size>0){
433 char *license=g_malloc(filestat.st_size+1);
434 FILE *f=fopen(license_file,"r");
435 if (f && fread(license,filestat.st_size,1,f)==1){
436 license[filestat.st_size]='\0';
437 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
441 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
442 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
443 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
444 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
445 tmp=linphone_gtk_get_ui_config("artists",defcfg);
450 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
452 tmp=linphone_gtk_get_ui_config("translators",defcfg);
454 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
455 tmp=linphone_gtk_get_ui_config("comments",defcfg);
457 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
458 gtk_widget_show(about);
461 static void set_video_window_decorations(GdkWindow *w){
462 const char *title=linphone_gtk_get_ui_config("title","Linphone");
463 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
464 char video_title[256];
465 GdkPixbuf *pbuf=create_pixbuf(icon_path);
466 if (!linphone_core_in_call(linphone_gtk_get_core())){
467 snprintf(video_title,sizeof(video_title),"%s video",title);
468 /* When not in call, treat the video as a normal window */
469 gdk_window_set_keep_above(w, FALSE);
471 LinphoneAddress *uri =
472 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
475 linphone_address_clean(uri);
476 if (linphone_address_get_display_name(uri)!=NULL){
477 display_name=ms_strdup(linphone_address_get_display_name(uri));
479 display_name=linphone_address_as_string(uri);
481 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
482 linphone_address_destroy(uri);
483 ms_free(display_name);
485 /* During calls, bring up the video window, arrange so that
486 it is above all the other windows */
487 gdk_window_deiconify(w);
488 gdk_window_set_keep_above(w,TRUE);
489 /* Maybe we should have the following, but then we want to
490 have a timer that turns it off after a little while. */
491 /* gdk_window_set_urgency_hint(w,TRUE); */
493 gdk_window_set_title(w,video_title);
494 /* Refrain the video window to be closed at all times. */
495 gdk_window_set_functions(w,
496 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
497 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
500 l=g_list_append(l,pbuf);
501 gdk_window_set_icon_list(w,l);
503 g_object_unref(G_OBJECT(pbuf));
507 static gboolean video_needs_update=FALSE;
509 static void update_video_title(){
510 video_needs_update=TRUE;
513 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
514 static gboolean first_time=TRUE;
516 static unsigned long previd=0;
517 static unsigned long preview_previd=0;
518 static gboolean in_iterate=FALSE;
521 if (in_iterate) return TRUE;
523 linphone_core_iterate(lc);
525 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
526 linphone_gtk_show_directory_search();
530 id=linphone_core_get_native_video_window_id(lc);
531 if (id!=previd || video_needs_update){
535 ms_message("Updating window decorations");
537 w=gdk_window_foreign_new(id);
539 w=gdk_window_foreign_new((HANDLE)id);
542 set_video_window_decorations(w);
543 g_object_unref(G_OBJECT(w));
545 else ms_error("gdk_window_foreign_new() failed");
546 if (video_needs_update) video_needs_update=FALSE;
549 id=linphone_core_get_native_preview_window_id (lc);
550 if (id!=preview_previd ){
554 ms_message("Updating window decorations for preview");
556 w=gdk_window_foreign_new(id);
558 w=gdk_window_foreign_new((HANDLE)id);
561 set_video_window_decorations(w);
562 g_object_unref(G_OBJECT(w));
564 else ms_error("gdk_window_foreign_new() failed");
565 if (video_needs_update) video_needs_update=FALSE;
568 if (addr_to_call!=NULL){
569 /*make sure we are not showing the login screen*/
570 GtkWidget *mw=linphone_gtk_get_main_window();
571 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
572 if (!GTK_WIDGET_VISIBLE(login_frame)){
573 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
574 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
576 linphone_gtk_start_call(uri_bar);
583 static void load_uri_history(){
584 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
587 GtkEntryCompletion *gep=gtk_entry_completion_new();
588 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
591 snprintf(key,sizeof(key),"uri%i",i);
592 uri=linphone_gtk_get_ui_config(key,NULL);
595 gtk_list_store_append(model,&iter);
596 gtk_list_store_set(model,&iter,0,uri,-1);
597 if (i==0) gtk_entry_set_text(uribar,uri);
601 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
602 gtk_entry_completion_set_text_column(gep,0);
603 gtk_entry_set_completion(uribar,gep);
606 static void save_uri_history(){
607 LinphoneCore *lc=linphone_gtk_get_core();
608 LpConfig *cfg=linphone_core_get_config(lc);
609 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
614 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
616 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
618 gtk_tree_model_get(model,&iter,0,&uri,-1);
620 snprintf(key,sizeof(key),"uri%i",i);
621 lp_config_set_string(cfg,"GtkUi",key,uri);
626 }while(gtk_tree_model_iter_next(model,&iter));
630 static void completion_add_text(GtkEntry *entry, const char *text){
632 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
634 if (gtk_tree_model_get_iter_first(model,&iter)){
637 gtk_tree_model_get(model,&iter,0,&uri,-1);
639 if (strcmp(uri,text)==0) {
641 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
647 }while (gtk_tree_model_iter_next(model,&iter));
649 /* and prepend it on top of the list */
650 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
651 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
656 bool_t linphone_gtk_video_enabled(void){
657 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
658 return vpol->automatically_accept && vpol->automatically_initiate;
661 void linphone_gtk_show_main_window(){
662 GtkWidget *w=linphone_gtk_get_main_window();
663 LinphoneCore *lc=linphone_gtk_get_core();
664 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
665 VIDEOSELFVIEW_DEFAULT));
667 gtk_window_present(GTK_WINDOW(w));
670 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
671 GtkWidget *mw=linphone_gtk_get_main_window();
672 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
673 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
674 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
676 if (linphone_gtk_use_in_call_view() && call)
677 linphone_gtk_in_call_view_terminate(call,error);
678 update_video_title();
681 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
682 LinphoneCore *lc=linphone_gtk_get_core();
683 GtkWidget *mw=linphone_gtk_get_main_window();
684 const MSList *calls=linphone_core_get_calls(lc);
686 bool_t start_active=TRUE;
687 bool_t stop_active=FALSE;
688 bool_t add_call=FALSE;
689 int call_list_size=ms_list_size(calls);
699 button=linphone_gtk_get_widget(mw,"start_call");
700 gtk_widget_set_sensitive(button,start_active);
701 gtk_widget_set_visible(button,!add_call);
703 button=linphone_gtk_get_widget(mw,"add_call");
704 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
705 gtk_widget_set_sensitive(button,FALSE);
707 gtk_widget_set_sensitive(button,start_active);
709 gtk_widget_set_visible(button,add_call);
711 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
713 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
714 linphone_gtk_enable_conference_button(lc,call_list_size>1);
715 update_video_title();
716 if (call) linphone_gtk_update_video_button(call);
719 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
720 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
721 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
722 completion_add_text(GTK_ENTRY(uri_bar),entered);
724 linphone_gtk_call_terminated(NULL,NULL);
729 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
730 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
731 linphone_core_accept_call (linphone_gtk_get_core(),call);
732 linphone_call_unref(call);
738 void linphone_gtk_start_call(GtkWidget *w){
739 LinphoneCore *lc=linphone_gtk_get_core();
741 /*change into in-call mode, then do the work later as it might block a bit */
742 GtkWidget *mw=gtk_widget_get_toplevel(w);
743 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
745 call=linphone_gtk_get_currently_displayed_call(NULL);
746 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
747 linphone_core_accept_call(lc,call);
749 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
750 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
751 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
752 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
757 void linphone_gtk_uri_bar_activate(GtkWidget *w){
758 linphone_gtk_start_call(w);
762 void linphone_gtk_terminate_call(GtkWidget *button){
764 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
766 linphone_core_terminate_call(linphone_gtk_get_core(),call);
768 linphone_core_terminate_conference(linphone_gtk_get_core());
772 void linphone_gtk_decline_clicked(GtkWidget *button){
773 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
775 linphone_core_terminate_call(linphone_gtk_get_core(),call);
778 void linphone_gtk_answer_clicked(GtkWidget *button){
779 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
781 linphone_core_accept_call(linphone_gtk_get_core(),call);
782 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
786 void _linphone_gtk_enable_video(gboolean val){
787 LinphoneVideoPolicy policy={0};
788 policy.automatically_initiate=policy.automatically_accept=val;
789 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
790 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
793 linphone_core_enable_video_preview(linphone_gtk_get_core(),
794 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
796 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
800 void linphone_gtk_enable_video(GtkWidget *w){
801 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
802 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
803 _linphone_gtk_enable_video(val);
806 void linphone_gtk_enable_self_view(GtkWidget *w){
807 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
808 LinphoneCore *lc=linphone_gtk_get_core();
809 linphone_core_enable_video_preview(lc,val);
810 linphone_core_enable_self_view(lc,val);
811 linphone_gtk_set_ui_config_int("videoselfview",val);
814 void linphone_gtk_used_identity_changed(GtkWidget *w){
815 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
816 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
817 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
818 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
819 linphone_gtk_show_directory_search();
821 if (sel) g_free(sel);
825 void on_proxy_refresh_button_clicked(GtkWidget *w){
826 LinphoneCore *lc=linphone_gtk_get_core();
827 MSList const *item=linphone_core_get_proxy_config_list(lc);
828 while (item != NULL) {
829 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
830 linphone_proxy_config_edit(lpc);
831 linphone_proxy_config_done(lpc);
836 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
837 linphone_gtk_show_friends();
840 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
842 case GTK_RESPONSE_YES:
843 linphone_gtk_show_contact(lf);
846 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
848 gtk_widget_destroy(dialog);
851 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
854 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
855 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
859 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);
860 dialog = gtk_message_dialog_new (
861 GTK_WINDOW(linphone_gtk_get_main_window()),
862 GTK_DIALOG_DESTROY_WITH_PARENT,
863 GTK_MESSAGE_QUESTION,
868 g_signal_connect(G_OBJECT (dialog), "response",
869 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
870 /* actually show the box */
871 gtk_widget_show(dialog);
874 typedef struct _AuthTimeout{
879 static void auth_timeout_clean(AuthTimeout *tout){
883 static gboolean auth_timeout_destroy(AuthTimeout *tout){
885 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
886 gtk_widget_destroy(tout->w);
892 static AuthTimeout * auth_timeout_new(GtkWidget *w){
893 AuthTimeout *tout=g_new(AuthTimeout,1);
895 /*so that the timeout no more references the widget when it is destroyed:*/
896 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
897 /*so that the widget is automatically destroyed after some time */
898 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
902 void linphone_gtk_password_cancel(GtkWidget *w){
903 LinphoneAuthInfo *info;
904 GtkWidget *window=gtk_widget_get_toplevel(w);
905 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
906 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
907 gtk_widget_destroy(window);
910 void linphone_gtk_password_ok(GtkWidget *w){
912 GtkWidget *window=gtk_widget_get_toplevel(w);
913 LinphoneAuthInfo *info;
914 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
915 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
916 entry=linphone_gtk_get_widget(window,"password_entry");
917 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
918 linphone_auth_info_set_userid(info,
919 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
920 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
921 gtk_widget_destroy(window);
924 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
925 GtkWidget *w=linphone_gtk_create_window("password");
926 GtkWidget *label=linphone_gtk_get_widget(w,"message");
927 LinphoneAuthInfo *info;
929 GtkWidget *mw=linphone_gtk_get_main_window();
931 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
932 /*don't prompt for authentication when login frame is visible*/
933 linphone_core_abort_authentication(lc,NULL);
937 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
939 gtk_label_set_markup(GTK_LABEL(label),msg);
941 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
942 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
943 g_object_set_data(G_OBJECT(w),"auth_info",info);
944 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
949 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
950 GtkWidget *w=linphone_gtk_get_main_window();
951 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
952 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
953 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
957 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
958 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
961 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
962 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
965 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
967 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
968 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
971 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
972 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
973 if (w) linphone_gtk_call_log_update(w);
974 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
978 static bool_t notify_actions_supported() {
979 bool_t accepts_actions = FALSE;
980 GList *capabilities = notify_get_server_caps();
982 if(capabilities != NULL) {
983 for(c = capabilities; c != NULL; c = c->next) {
984 if(strcmp((char*)c->data, "actions") == 0 ) {
985 accepts_actions = TRUE;
989 g_list_foreach(capabilities, (GFunc)g_free, NULL);
990 g_list_free(capabilities);
992 return accepts_actions;
995 static NotifyNotification* build_notification(const char *title, const char *body){
996 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1003 static void show_notification(NotifyNotification* n){
1004 if (n && !notify_notification_show(n,NULL))
1005 ms_error("Failed to send notification.");
1008 static void make_notification(const char *title, const char *body){
1009 show_notification(build_notification(title,body));
1014 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1016 if (!notify_is_initted())
1017 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1021 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1026 ms_error("Failed to send notification.");
1028 linphone_gtk_show_main_window();
1030 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1033 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1034 NotifyNotification *n;
1035 switch(linphone_call_get_state(call)){
1036 case LinphoneCallError:
1037 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1039 case LinphoneCallEnd:
1040 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1042 case LinphoneCallIncomingReceived:
1043 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1044 if (notify_actions_supported()) {
1045 notify_notification_add_action (n,"answer", _("Answer"),
1046 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1047 notify_notification_add_action (n,"decline",_("Decline"),
1048 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1050 show_notification(n);
1052 case LinphoneCallPausedByRemote:
1053 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1058 if (body) g_free(body);
1059 if (remote) g_free(remote);
1064 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1065 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1066 LinphoneCore *lc=linphone_call_get_core(call);
1067 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1068 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1069 linphone_core_accept_call_update(lc,call,params);
1070 linphone_call_params_destroy(params);
1072 linphone_call_unref(call);
1073 g_source_remove_by_user_data(dialog);
1074 gtk_widget_destroy(dialog);
1077 static void on_call_updated_timeout(GtkWidget *dialog){
1078 gtk_widget_destroy(dialog);
1081 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1082 LinphoneCore *lc=linphone_call_get_core(call);
1083 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1084 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1085 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1086 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1087 gboolean video_used=linphone_call_params_video_enabled(current_params);
1088 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1089 video_used,video_requested,pol->automatically_accept);
1090 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1091 linphone_core_defer_call_update(lc,call);
1093 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1095 const char *dname=linphone_address_get_display_name(addr);
1096 if (dname==NULL) dname=linphone_address_get_username(addr);
1097 if (dname==NULL) dname=linphone_address_get_domain(addr);
1098 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1099 GTK_DIALOG_DESTROY_WITH_PARENT,
1100 GTK_MESSAGE_WARNING,
1102 _("%s proposed to start video. Do you accept ?"),dname);
1103 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1104 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1105 gtk_widget_show(dialog);
1110 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1112 case LinphoneCallOutgoingInit:
1113 linphone_gtk_create_in_call_view (call);
1115 case LinphoneCallOutgoingProgress:
1116 linphone_gtk_in_call_view_set_calling (call);
1118 case LinphoneCallStreamsRunning:
1119 linphone_gtk_in_call_view_set_in_call(call);
1121 case LinphoneCallUpdatedByRemote:
1122 linphone_gtk_call_updated_by_remote(call);
1124 case LinphoneCallError:
1125 linphone_gtk_in_call_view_terminate (call,msg);
1127 case LinphoneCallEnd:
1128 linphone_gtk_in_call_view_terminate(call,NULL);
1129 linphone_gtk_status_icon_set_blinking(FALSE);
1131 case LinphoneCallIncomingReceived:
1132 linphone_gtk_create_in_call_view(call);
1133 linphone_gtk_in_call_view_set_incoming(call);
1134 linphone_gtk_status_icon_set_blinking(TRUE);
1136 linphone_call_ref(call);
1137 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1140 case LinphoneCallResuming:
1141 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1142 linphone_gtk_in_call_view_set_in_call (call);
1144 case LinphoneCallPausing:
1145 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1146 case LinphoneCallPausedByRemote:
1147 linphone_gtk_in_call_view_set_paused(call);
1149 case LinphoneCallConnected:
1150 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1151 linphone_gtk_status_icon_set_blinking(FALSE);
1156 linphone_gtk_notify(call, msg);
1157 linphone_gtk_update_call_buttons (call);
1160 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1161 linphone_gtk_in_call_view_show_encryption(call);
1164 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1165 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1168 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1169 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1170 GtkTreeModel *model=gtk_combo_box_get_model(box);
1172 gboolean found=FALSE;
1173 const char *stock_id=NULL;
1175 if (gtk_tree_model_get_iter_first(model,&iter)){
1178 gtk_tree_model_get(model,&iter,2,&p,-1);
1183 }while(gtk_tree_model_iter_next(model,&iter));
1186 g_warning("Could not find proxy config in combo box of identities.");
1190 case LinphoneRegistrationOk:
1191 stock_id=GTK_STOCK_YES;
1193 case LinphoneRegistrationProgress:
1194 stock_id=GTK_STOCK_REFRESH;
1196 case LinphoneRegistrationCleared:
1199 case LinphoneRegistrationFailed:
1200 stock_id=GTK_STOCK_DIALOG_WARNING;
1205 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1208 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1209 LinphoneRegistrationState rs, const char *msg){
1211 case LinphoneRegistrationOk:
1213 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1214 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1215 linphone_gtk_exit_login_frame();
1222 update_registration_status(cfg,rs);
1225 void linphone_gtk_open_browser(const char *url){
1226 /*in gtk 2.16, gtk_show_uri does not work...*/
1228 #if GTK_CHECK_VERSION(2,18,3)
1229 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1232 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1233 g_spawn_command_line_async(cl,NULL);
1236 ShellExecute(0,"open",url,NULL,NULL,1);
1240 void linphone_gtk_link_to_website(GtkWidget *item){
1241 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1242 linphone_gtk_open_browser(home);
1245 #ifndef HAVE_GTK_OSX
1247 static GtkStatusIcon *icon=NULL;
1249 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1250 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1251 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1254 static GtkWidget *create_icon_menu(){
1255 GtkWidget *menu=gtk_menu_new();
1256 GtkWidget *menu_item;
1259 const gchar *homesite;
1261 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1262 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1263 tmp=g_strdup(homesite);
1264 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1265 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1267 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1268 gtk_widget_show(image);
1269 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1270 //g_object_unref(G_OBJECT(image));
1271 gtk_widget_show(menu_item);
1272 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1273 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1275 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1276 gtk_widget_show(menu_item);
1277 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1278 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1279 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1280 gtk_widget_show(menu_item);
1281 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1282 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1283 gtk_widget_show(menu);
1287 static void handle_icon_click() {
1288 GtkWidget *mw=linphone_gtk_get_main_window();
1289 if (!gtk_window_is_active((GtkWindow*)mw)) {
1290 linphone_gtk_show_main_window();
1292 gtk_widget_hide(mw);
1296 static void linphone_gtk_init_status_icon(){
1297 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1298 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1299 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1300 GtkWidget *menu=create_icon_menu();
1302 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1303 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1304 #if GTK_CHECK_VERSION(2,20,0)
1305 gtk_status_icon_set_name(icon,title);
1307 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1308 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1309 gtk_status_icon_set_tooltip(icon,title);
1310 gtk_status_icon_set_visible(icon,TRUE);
1311 g_object_set_data(G_OBJECT(icon),"menu",menu);
1312 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1313 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1314 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1315 pbuf=create_pixbuf(call_icon_path);
1316 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1319 static gboolean do_icon_blink(GtkStatusIcon *gi){
1320 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1321 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1322 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1323 if (cur_icon==call_icon){
1324 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1326 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1333 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1335 static gint attention_id;
1336 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1338 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1339 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1343 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1344 if (val && tout==0){
1345 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1346 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1347 }else if (!val && tout!=0){
1348 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1349 g_source_remove(tout);
1350 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1351 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1357 void linphone_gtk_options_activate(GtkWidget *item){
1358 #ifndef HAVE_GTK_OSX
1359 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1364 static void init_identity_combo(GtkComboBox *box){
1365 GtkListStore *store;
1366 GtkCellRenderer *r1,*r2;
1367 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1368 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1369 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1370 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1371 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1372 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1373 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1374 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1377 void linphone_gtk_load_identities(void){
1379 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1381 LinphoneProxyConfig *def=NULL;
1383 GtkListStore *store;
1386 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1387 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1388 /* model is empty, this is the first time we go here */
1389 init_identity_combo(box);
1390 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1392 gtk_list_store_clear(store);
1393 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1394 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1395 gtk_list_store_append(store,&iter);
1396 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1397 g_free(def_identity);
1398 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1400 elem=ms_list_next(elem),i++){
1401 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1402 gtk_list_store_append(store,&iter);
1403 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1404 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1410 gtk_combo_box_set_active(box,def_index);
1413 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1414 const char *label=gtk_button_get_label(button);
1415 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1417 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1418 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1419 if (linphone_core_in_call(linphone_gtk_get_core())){
1420 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1424 static void linphone_gtk_dtmf_released(GtkButton *button){
1425 linphone_core_stop_dtmf (linphone_gtk_get_core());
1428 static void linphone_gtk_connect_digits(void){
1429 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1430 GList *children=gtk_container_get_children(cont);
1432 for(elem=children;elem!=NULL;elem=elem->next){
1433 GtkButton *button=GTK_BUTTON(elem->data);
1434 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1435 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1439 static void linphone_gtk_check_menu_items(void){
1440 bool_t video_enabled=linphone_gtk_video_enabled();
1441 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1442 GtkWidget *selfview_item=linphone_gtk_get_widget(
1443 linphone_gtk_get_main_window(),"selfview_item");
1444 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1445 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1446 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1449 static gboolean linphone_gtk_can_manage_accounts(){
1450 LinphoneCore *lc=linphone_gtk_get_core();
1452 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1453 SipSetup *ss=(SipSetup*)elem->data;
1454 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1461 static void linphone_gtk_configure_main_window(){
1462 static gboolean config_loaded=FALSE;
1463 static const char *title;
1464 static const char *home;
1465 static const char *start_call_icon;
1466 static const char *add_call_icon;
1467 static const char *stop_call_icon;
1468 static const char *search_icon;
1469 static gboolean update_check_menu;
1470 static gboolean buttons_have_borders;
1471 static gboolean show_abcd;
1472 GtkWidget *w=linphone_gtk_get_main_window();
1473 if (!config_loaded){
1474 title=linphone_gtk_get_ui_config("title","Linphone");
1475 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1476 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1477 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1478 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1479 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1480 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1481 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1482 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1485 linphone_gtk_configure_window(w,"main_window");
1487 gtk_window_set_title(GTK_WINDOW(w),title);
1489 if (start_call_icon){
1490 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1491 create_pixmap (start_call_icon));
1492 if (!buttons_have_borders)
1493 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1496 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1497 create_pixmap (add_call_icon));
1498 if (!buttons_have_borders)
1499 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1501 if (stop_call_icon){
1502 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1503 create_pixmap (stop_call_icon));
1504 if (!buttons_have_borders)
1505 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1508 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1509 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1510 g_object_unref(G_OBJECT(pbuf));
1514 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1516 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1520 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1522 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1523 g_object_unref(G_OBJECT(pbuf));
1528 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1530 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1533 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1534 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1535 gtk_image_set_from_pixbuf(img,scaled);
1536 g_object_unref(G_OBJECT(scaled));
1537 g_object_unref(G_OBJECT(pbuf));
1540 if (linphone_gtk_can_manage_accounts()) {
1541 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1543 if (update_check_menu){
1544 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1547 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1548 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1549 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1550 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1551 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1555 void linphone_gtk_manage_login(void){
1556 LinphoneCore *lc=linphone_gtk_get_core();
1557 LinphoneProxyConfig *cfg=NULL;
1558 linphone_core_get_default_proxy(lc,&cfg);
1560 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1561 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1562 linphone_gtk_show_login_frame(cfg);
1568 gboolean linphone_gtk_close(GtkWidget *mw){
1569 /*shutdown calls if any*/
1570 LinphoneCore *lc=linphone_gtk_get_core();
1571 if (linphone_core_in_call(lc)){
1572 linphone_core_terminate_all_calls(lc);
1574 linphone_core_enable_video_preview(lc,FALSE);
1575 #ifdef __APPLE__ /*until with have a better option*/
1576 gtk_window_iconify(GTK_WINDOW(mw));
1578 gtk_widget_hide(mw);
1584 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1585 bool_t video_enabled=linphone_gtk_video_enabled();
1586 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1587 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1589 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1590 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1597 static void linphone_gtk_init_main_window(){
1598 GtkWidget *main_window;
1600 linphone_gtk_configure_main_window();
1601 linphone_gtk_manage_login();
1603 linphone_gtk_load_identities();
1604 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1605 linphone_gtk_show_friends();
1606 linphone_gtk_connect_digits();
1607 main_window=linphone_gtk_get_main_window();
1608 linphone_gtk_call_log_update(main_window);
1610 linphone_gtk_update_call_buttons (NULL);
1611 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1612 g_signal_connect (G_OBJECT (main_window), "delete-event",
1613 G_CALLBACK (linphone_gtk_close), main_window);
1616 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1617 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1618 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1619 gtk_widget_hide(menubar);
1620 gtk_osxapplication_ready(theMacApp);
1622 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1624 linphone_gtk_check_menu_items();
1628 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1630 const char *lname="undef";
1632 #if defined(__linux) || defined(__APPLE__)
1633 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1652 g_error("Bad level !");
1654 #if defined(__linux) || defined(__APPLE__)
1656 msg=g_strdup_vprintf(fmt,cap);
1659 msg=g_strdup_vprintf(fmt,args);
1661 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1664 linphone_gtk_log_push(lev,fmt,args);
1668 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1669 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1670 linphone_gtk_get_main_window(), "uribar"));
1672 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1674 gtk_entry_set_text(uri_bar, refer_to);
1675 linphone_gtk_start_call(linphone_gtk_get_main_window());
1678 static void linphone_gtk_check_soundcards(){
1679 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1680 if (devices==NULL || devices[0]==NULL){
1681 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1682 _("No sound cards have been detected on this computer.\n"
1683 "You won't be able to send or receive audio calls."));
1688 // Display the account wizard
1689 void linphone_gtk_display_wizard() {
1690 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1691 the_wizard = linphone_gtk_create_assistant();
1696 static void linphone_gtk_quit(void){
1697 static gboolean quit_done=FALSE;
1700 linphone_gtk_unmonitor_usb();
1701 g_source_remove_by_user_data(linphone_gtk_get_core());
1702 linphone_gtk_uninit_instance();
1703 linphone_gtk_destroy_log_window();
1704 linphone_core_destroy(the_core);
1705 linphone_gtk_log_uninit();
1709 gdk_threads_leave();
1715 This is not the correct way to implement block termination.
1716 The good way would be to call gtk_main_quit(), and return TRUE.
1717 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1718 As a result the program cannot exit at all.
1719 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1721 static gboolean on_block_termination(void){
1723 linphone_gtk_quit();
1728 int main(int argc, char *argv[]){
1733 const char *factory_config_file;
1735 GtkSettings *settings;
1737 const char *app_name="Linphone";
1739 #if !GLIB_CHECK_VERSION(2, 31, 0)
1740 g_thread_init(NULL);
1744 progpath = strdup(argv[0]);
1746 config_file=linphone_gtk_get_config_file(NULL);
1750 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1751 if ((lang=getenv("LANG"))!=NULL){
1754 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1760 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1763 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1766 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1769 setenv("LANG",lang,1);
1774 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1775 if (p==NULL) perror("bindtextdomain failed");
1776 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1777 textdomain (GETTEXT_PACKAGE);
1779 g_message("NLS disabled.\n");
1782 gtk_rc_add_default_file("./gtkrc");
1784 gdk_threads_enter();
1786 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1787 linphone_options,NULL,NULL)){
1788 gdk_threads_leave();
1792 settings=gtk_settings_get_default();
1793 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1794 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1795 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1796 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1798 if (workingdir!=NULL){
1799 if (chdir(workingdir)==-1){
1800 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1804 /* Now, look for the factory configuration file, we do it this late
1805 since we want to have had time to change directory and to parse
1806 the options, in case we needed to access the working directory */
1807 factory_config_file = linphone_gtk_get_factory_config_file();
1809 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1810 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1811 g_warning("This instance is going to exit now.");
1812 gdk_threads_leave();
1816 add_pixmap_directory("pixmaps");
1817 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1820 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1821 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1822 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1823 /*never block termination:*/
1824 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1827 the_ui=linphone_gtk_create_window("main");
1829 linphone_gtk_create_log_window();
1830 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1832 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1834 g_set_application_name(app_name);
1835 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1836 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1838 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1839 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1840 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1841 linphone_gtk_init_main_window();
1844 // Veryfing if at least one sip account is configured. If not, show wizard
1845 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1846 linphone_gtk_display_wizard();
1850 #ifndef HAVE_GTK_OSX
1851 linphone_gtk_init_status_icon();
1854 linphone_gtk_show_main_window();
1855 linphone_gtk_check_soundcards();
1857 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1858 linphone_gtk_check_for_new_version();
1859 linphone_gtk_monitor_usb();
1862 linphone_gtk_quit();
1863 #ifndef HAVE_GTK_OSX
1864 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1865 gtk_status_icon_set_visible(icon,FALSE);