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;
54 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
55 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
56 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
57 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
58 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
59 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
60 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
61 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
62 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
63 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
64 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
65 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
66 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 static gboolean verbose=0;
70 static gboolean auto_answer = 0;
71 static gchar * addr_to_call = NULL;
72 static gboolean iconified=FALSE;
73 static gchar *workingdir=NULL;
74 static char *progpath=NULL;
75 gchar *linphone_logfile=NULL;
77 static GOptionEntry linphone_options[]={
81 .arg=G_OPTION_ARG_NONE,
82 .arg_data= (gpointer)&verbose,
83 .description=N_("log to stdout some debug information while running.")
86 .long_name = "logfile",
88 .arg = G_OPTION_ARG_STRING,
89 .arg_data = &linphone_logfile,
90 .description = N_("path to a file to write logs into.")
93 .long_name="iconified",
95 .arg=G_OPTION_ARG_NONE,
96 .arg_data= (gpointer)&iconified,
97 .description=N_("Start only in the system tray, do not show the main interface.")
102 .arg = G_OPTION_ARG_STRING,
103 .arg_data = &addr_to_call,
104 .description = N_("address to call right now")
107 .long_name = "auto-answer",
109 .arg = G_OPTION_ARG_NONE,
110 .arg_data = (gpointer) & auto_answer,
111 .description = N_("if set automatically answer incoming calls")
114 .long_name = "workdir",
116 .arg = G_OPTION_ARG_STRING,
117 .arg_data = (gpointer) & workingdir,
118 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
123 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
124 #define RELATIVE_XML_DIR
125 #define BUILD_TREE_XML_DIR "gtk"
128 #define CONFIG_FILE ".linphonerc"
129 #define SECRETS_FILE ".linphone-zidcache"
131 #define CONFIG_FILE "linphonerc"
132 #define SECRETS_FILE "linphone-zidcache"
136 char *linphone_gtk_get_config_file(const char *filename){
137 const int path_max=1024;
138 char *config_file=g_malloc0(path_max);
139 if (filename==NULL) filename=CONFIG_FILE;
140 /*try accessing a local file first if exists*/
141 if (access(CONFIG_FILE,F_OK)==0){
142 snprintf(config_file,path_max,"%s",filename);
145 const char *appdata=getenv("APPDATA");
147 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
148 CreateDirectory(config_file,NULL);
149 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
152 const char *home=getenv("HOME");
153 if (home==NULL) home=".";
154 snprintf(config_file,path_max,"%s/%s",home,filename);
161 #define FACTORY_CONFIG_FILE "linphonerc.factory"
162 static char _factory_config_file[1024];
163 static const char *linphone_gtk_get_factory_config_file(){
164 /*try accessing a local file first if exists*/
165 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
166 snprintf(_factory_config_file,sizeof(_factory_config_file),
167 "%s",FACTORY_CONFIG_FILE);
171 if (progpath != NULL) {
173 progdir = strdup(progpath);
175 basename = strrchr(progdir, '\\');
176 if (basename != NULL) {
179 snprintf(_factory_config_file, sizeof(_factory_config_file),
180 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
182 if (workingdir!=NULL) {
183 snprintf(_factory_config_file, sizeof(_factory_config_file),
184 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
191 basename = strrchr(progdir, '/');
192 if (basename != NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
205 return _factory_config_file;
208 static void linphone_gtk_init_liblinphone(const char *config_file,
209 const char *factory_config_file) {
210 LinphoneCoreVTable vtable={0};
211 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
213 vtable.call_state_changed=linphone_gtk_call_state_changed;
214 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
215 vtable.notify_presence_recv=linphone_gtk_notify_recv;
216 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
217 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
218 vtable.display_status=linphone_gtk_display_status;
219 vtable.display_message=linphone_gtk_display_message;
220 vtable.display_warning=linphone_gtk_display_warning;
221 vtable.display_url=linphone_gtk_display_url;
222 vtable.call_log_updated=linphone_gtk_call_log_updated;
223 vtable.text_received=linphone_gtk_text_received;
224 vtable.refer_received=linphone_gtk_refer_received;
225 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
228 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
229 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
230 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
231 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
232 g_free(secrets_file);
237 LinphoneCore *linphone_gtk_get_core(void){
241 GtkWidget *linphone_gtk_get_main_window(){
245 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
246 static const char *icon_path=NULL;
247 static const char *hiddens=NULL;
248 static const char *shown=NULL;
249 static bool_t config_loaded=FALSE;
250 if (linphone_gtk_get_core()==NULL) return;
251 if (config_loaded==FALSE){
252 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
253 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
254 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
258 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
260 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
262 GdkPixbuf *pbuf=create_pixbuf(icon_path);
263 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
264 g_object_unref(G_OBJECT(pbuf));
268 static int get_ui_file(const char *name, char *path, int pathsize){
269 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
270 if (access(path,F_OK)!=0){
271 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
272 if (access(path,F_OK)!=0){
273 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
274 INSTALLED_XML_DIR,name);
281 GtkWidget *linphone_gtk_create_window(const char *window_name){
282 GError* error = NULL;
283 GtkBuilder* builder = gtk_builder_new ();
287 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
289 if (!gtk_builder_add_from_file (builder, path, &error)){
290 g_error("Couldn't load builder file: %s", error->message);
291 g_error_free (error);
294 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
296 g_error("Could not retrieve '%s' window from xml file",window_name);
299 g_object_set_data(G_OBJECT(w),"builder",builder);
300 gtk_builder_connect_signals(builder,w);
301 linphone_gtk_configure_window(w,window_name);
305 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
308 GtkBuilder* builder = gtk_builder_new ();
310 gchar *object_ids[2];
311 object_ids[0]=g_strdup(widget_name);
314 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
315 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
316 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
317 g_error_free (error);
318 g_free(object_ids[0]);
321 g_free(object_ids[0]);
322 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
324 g_error("Could not retrieve '%s' window from xml file",widget_name);
327 g_object_set_data(G_OBJECT(w),"builder",builder);
328 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
329 gtk_builder_connect_signals(builder,w);
333 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
334 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
337 g_error("Fail to retrieve builder from window !");
340 w=gtk_builder_get_object(builder,name);
342 g_error("No widget named %s found in xml interface.",name);
344 return GTK_WIDGET(w);
348 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
350 GtkWidget *main_window=linphone_gtk_get_main_window();
352 gtk_widget_show(main_window);
353 if (type==GTK_MESSAGE_QUESTION)
355 /* draw a question box. link to dialog_click callback */
356 dialog = gtk_message_dialog_new (
357 GTK_WINDOW(main_window),
358 GTK_DIALOG_DESTROY_WITH_PARENT,
359 GTK_MESSAGE_QUESTION,
362 (const gchar*)message);
363 /* connect to some callback : REVISIT */
365 g_signal_connect_swapped (G_OBJECT (dialog), "response",
366 G_CALLBACK (dialog_click),
369 /* actually show the box */
370 gtk_widget_show(dialog);
374 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
375 GTK_DIALOG_DESTROY_WITH_PARENT,
379 (const gchar*)message);
380 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
381 g_signal_connect_swapped (G_OBJECT (dialog), "response",
382 G_CALLBACK (gtk_widget_destroy),
384 gtk_widget_show(dialog);
388 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
389 if (id==GTK_RESPONSE_CANCEL){
390 gtk_widget_destroy(GTK_WIDGET(dialog));
394 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
395 g_message("About url clicked");
396 linphone_gtk_open_browser(url);
399 void linphone_gtk_show_about(){
400 struct stat filestat;
401 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
404 GdkPixbuf *logo=create_pixbuf(
405 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
406 static const char *defcfg="defcfg";
408 about=linphone_gtk_create_window("about");
409 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
410 memset(&filestat,0,sizeof(filestat));
411 if (stat(license_file,&filestat)!=0){
412 license_file="COPYING";
413 stat(license_file,&filestat);
415 if (filestat.st_size>0){
416 char *license=g_malloc(filestat.st_size+1);
417 FILE *f=fopen(license_file,"r");
418 if (f && fread(license,filestat.st_size,1,f)==1){
419 license[filestat.st_size]='\0';
420 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
424 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
425 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
426 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
427 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
428 tmp=linphone_gtk_get_ui_config("artists",defcfg);
433 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
435 tmp=linphone_gtk_get_ui_config("translators",defcfg);
437 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
438 tmp=linphone_gtk_get_ui_config("comments",defcfg);
440 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
441 gtk_widget_show(about);
444 static void set_video_window_decorations(GdkWindow *w){
445 const char *title=linphone_gtk_get_ui_config("title","Linphone");
446 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
447 char video_title[256];
448 GdkPixbuf *pbuf=create_pixbuf(icon_path);
449 if (!linphone_core_in_call(linphone_gtk_get_core())){
450 snprintf(video_title,sizeof(video_title),"%s video",title);
451 /* When not in call, treat the video as a normal window */
452 gdk_window_set_keep_above(w, FALSE);
454 LinphoneAddress *uri =
455 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
458 linphone_address_clean(uri);
459 if (linphone_address_get_display_name(uri)!=NULL){
460 display_name=ms_strdup(linphone_address_get_display_name(uri));
462 display_name=linphone_address_as_string(uri);
464 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
465 linphone_address_destroy(uri);
466 ms_free(display_name);
468 /* During calls, bring up the video window, arrange so that
469 it is above all the other windows */
470 gdk_window_deiconify(w);
471 gdk_window_set_keep_above(w,TRUE);
472 /* Maybe we should have the following, but then we want to
473 have a timer that turns it off after a little while. */
474 /* gdk_window_set_urgency_hint(w,TRUE); */
476 gdk_window_set_title(w,video_title);
477 /* Refrain the video window to be closed at all times. */
478 gdk_window_set_functions(w,
479 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
480 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
483 l=g_list_append(l,pbuf);
484 gdk_window_set_icon_list(w,l);
486 g_object_unref(G_OBJECT(pbuf));
490 static gboolean video_needs_update=FALSE;
492 static void update_video_title(){
493 video_needs_update=TRUE;
496 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
497 static gboolean first_time=TRUE;
499 static unsigned long previd=0;
500 static unsigned long preview_previd=0;
501 static gboolean in_iterate=FALSE;
504 if (in_iterate) return TRUE;
506 linphone_core_iterate(lc);
508 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
509 linphone_gtk_show_directory_search();
513 id=linphone_core_get_native_video_window_id(lc);
514 if (id!=previd || video_needs_update){
518 ms_message("Updating window decorations");
520 w=gdk_window_foreign_new(id);
522 w=gdk_window_foreign_new((HANDLE)id);
525 set_video_window_decorations(w);
526 g_object_unref(G_OBJECT(w));
528 else ms_error("gdk_window_foreign_new() failed");
529 if (video_needs_update) video_needs_update=FALSE;
532 id=linphone_core_get_native_preview_window_id (lc);
533 if (id!=preview_previd ){
537 ms_message("Updating window decorations for preview");
539 w=gdk_window_foreign_new(id);
541 w=gdk_window_foreign_new((HANDLE)id);
544 set_video_window_decorations(w);
545 g_object_unref(G_OBJECT(w));
547 else ms_error("gdk_window_foreign_new() failed");
548 if (video_needs_update) video_needs_update=FALSE;
551 if (addr_to_call!=NULL){
552 /*make sure we are not showing the login screen*/
553 GtkWidget *mw=linphone_gtk_get_main_window();
554 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
555 if (!GTK_WIDGET_VISIBLE(login_frame)){
556 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
557 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
559 linphone_gtk_start_call(uri_bar);
566 static void load_uri_history(){
567 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
570 GtkEntryCompletion *gep=gtk_entry_completion_new();
571 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
574 snprintf(key,sizeof(key),"uri%i",i);
575 uri=linphone_gtk_get_ui_config(key,NULL);
578 gtk_list_store_append(model,&iter);
579 gtk_list_store_set(model,&iter,0,uri,-1);
580 if (i==0) gtk_entry_set_text(uribar,uri);
584 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
585 gtk_entry_completion_set_text_column(gep,0);
586 gtk_entry_set_completion(uribar,gep);
589 static void save_uri_history(){
590 LinphoneCore *lc=linphone_gtk_get_core();
591 LpConfig *cfg=linphone_core_get_config(lc);
592 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
597 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
599 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
601 gtk_tree_model_get(model,&iter,0,&uri,-1);
603 snprintf(key,sizeof(key),"uri%i",i);
604 lp_config_set_string(cfg,"GtkUi",key,uri);
609 }while(gtk_tree_model_iter_next(model,&iter));
613 static void completion_add_text(GtkEntry *entry, const char *text){
615 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
617 if (gtk_tree_model_get_iter_first(model,&iter)){
620 gtk_tree_model_get(model,&iter,0,&uri,-1);
622 if (strcmp(uri,text)==0) {
624 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
630 }while (gtk_tree_model_iter_next(model,&iter));
632 /* and prepend it on top of the list */
633 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
634 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
639 void linphone_gtk_show_main_window(){
640 GtkWidget *w=linphone_gtk_get_main_window();
641 LinphoneCore *lc=linphone_gtk_get_core();
642 if (linphone_core_video_enabled(lc)){
643 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
644 VIDEOSELFVIEW_DEFAULT));
647 gtk_window_present(GTK_WINDOW(w));
650 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
651 GtkWidget *mw=linphone_gtk_get_main_window();
652 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
653 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
654 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
656 if (linphone_gtk_use_in_call_view() && call)
657 linphone_gtk_in_call_view_terminate(call,error);
658 update_video_title();
661 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
662 LinphoneCore *lc=linphone_gtk_get_core();
663 GtkWidget *mw=linphone_gtk_get_main_window();
664 const MSList *calls=linphone_core_get_calls(lc);
666 bool_t start_active=TRUE;
667 bool_t stop_active=FALSE;
668 bool_t add_call=FALSE;
669 int call_list_size=ms_list_size(calls);
679 button=linphone_gtk_get_widget(mw,"start_call");
680 gtk_widget_set_sensitive(button,start_active);
681 gtk_widget_set_visible(button,!add_call);
683 button=linphone_gtk_get_widget(mw,"add_call");
684 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
685 gtk_widget_set_sensitive(button,FALSE);
687 gtk_widget_set_sensitive(button,start_active);
689 gtk_widget_set_visible(button,add_call);
691 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
693 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
694 linphone_gtk_enable_conference_button(lc,call_list_size>1);
695 update_video_title();
698 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
699 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
700 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
701 completion_add_text(GTK_ENTRY(uri_bar),entered);
703 linphone_gtk_call_terminated(NULL,NULL);
708 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
709 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
710 linphone_core_accept_call (linphone_gtk_get_core(),call);
711 linphone_call_unref(call);
717 void linphone_gtk_start_call(GtkWidget *w){
718 LinphoneCore *lc=linphone_gtk_get_core();
720 /*change into in-call mode, then do the work later as it might block a bit */
721 GtkWidget *mw=gtk_widget_get_toplevel(w);
722 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
724 call=linphone_gtk_get_currently_displayed_call(NULL);
725 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
726 linphone_core_accept_call(lc,call);
728 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
729 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
730 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
731 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
736 void linphone_gtk_uri_bar_activate(GtkWidget *w){
737 linphone_gtk_start_call(w);
741 void linphone_gtk_terminate_call(GtkWidget *button){
743 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
745 linphone_core_terminate_call(linphone_gtk_get_core(),call);
747 linphone_core_terminate_conference(linphone_gtk_get_core());
751 void linphone_gtk_decline_clicked(GtkWidget *button){
752 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
754 linphone_core_terminate_call(linphone_gtk_get_core(),call);
757 void linphone_gtk_answer_clicked(GtkWidget *button){
758 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
760 linphone_core_accept_call(linphone_gtk_get_core(),call);
761 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
765 void linphone_gtk_enable_video(GtkWidget *w){
766 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
767 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
768 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
769 gtk_widget_set_sensitive(selfview_item,val);
771 linphone_core_enable_video_preview(linphone_gtk_get_core(),
772 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
774 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
778 void linphone_gtk_enable_self_view(GtkWidget *w){
779 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
780 LinphoneCore *lc=linphone_gtk_get_core();
781 linphone_core_enable_video_preview(lc,val);
782 linphone_core_enable_self_view(lc,val);
783 linphone_gtk_set_ui_config_int("videoselfview",val);
786 void linphone_gtk_used_identity_changed(GtkWidget *w){
787 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
788 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
789 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
790 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
791 linphone_gtk_show_directory_search();
793 if (sel) g_free(sel);
796 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
797 linphone_gtk_show_friends();
800 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
802 case GTK_RESPONSE_YES:
803 linphone_gtk_show_contact(lf);
806 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
808 gtk_widget_destroy(dialog);
811 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
814 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
815 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
819 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);
820 dialog = gtk_message_dialog_new (
821 GTK_WINDOW(linphone_gtk_get_main_window()),
822 GTK_DIALOG_DESTROY_WITH_PARENT,
823 GTK_MESSAGE_QUESTION,
828 g_signal_connect(G_OBJECT (dialog), "response",
829 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
830 /* actually show the box */
831 gtk_widget_show(dialog);
834 typedef struct _AuthTimeout{
839 static void auth_timeout_clean(AuthTimeout *tout){
843 static gboolean auth_timeout_destroy(AuthTimeout *tout){
845 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
846 gtk_widget_destroy(tout->w);
852 static AuthTimeout * auth_timeout_new(GtkWidget *w){
853 AuthTimeout *tout=g_new(AuthTimeout,1);
855 /*so that the timeout no more references the widget when it is destroyed:*/
856 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
857 /*so that the widget is automatically destroyed after some time */
858 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
862 void linphone_gtk_password_cancel(GtkWidget *w){
863 LinphoneAuthInfo *info;
864 GtkWidget *window=gtk_widget_get_toplevel(w);
865 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
866 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
867 gtk_widget_destroy(window);
870 void linphone_gtk_password_ok(GtkWidget *w){
872 GtkWidget *window=gtk_widget_get_toplevel(w);
873 LinphoneAuthInfo *info;
874 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
875 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
876 entry=linphone_gtk_get_widget(window,"password_entry");
877 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
878 linphone_auth_info_set_userid(info,
879 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
880 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
881 gtk_widget_destroy(window);
884 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
885 GtkWidget *w=linphone_gtk_create_window("password");
886 GtkWidget *label=linphone_gtk_get_widget(w,"message");
887 LinphoneAuthInfo *info;
889 GtkWidget *mw=linphone_gtk_get_main_window();
891 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
892 /*don't prompt for authentication when login frame is visible*/
893 linphone_core_abort_authentication(lc,NULL);
897 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
899 gtk_label_set_markup(GTK_LABEL(label),msg);
901 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
902 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
903 g_object_set_data(G_OBJECT(w),"auth_info",info);
904 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
909 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
910 GtkWidget *w=linphone_gtk_get_main_window();
911 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
912 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
913 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
917 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
918 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
921 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
922 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
925 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
927 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
928 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
931 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
932 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
933 if (w) linphone_gtk_call_log_update(w);
934 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
938 static bool_t notify_actions_supported() {
939 bool_t accepts_actions = FALSE;
940 GList *capabilities = notify_get_server_caps();
942 if(capabilities != NULL) {
943 for(c = capabilities; c != NULL; c = c->next) {
944 if(strcmp((char*)c->data, "actions") == 0 ) {
945 accepts_actions = TRUE;
949 g_list_foreach(capabilities, (GFunc)g_free, NULL);
950 g_list_free(capabilities);
952 return accepts_actions;
955 static NotifyNotification* build_notification(const char *title, const char *body){
956 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
963 static void show_notification(NotifyNotification* n){
964 if (n && !notify_notification_show(n,NULL))
965 ms_error("Failed to send notification.");
968 static void make_notification(const char *title, const char *body){
969 show_notification(build_notification(title,body));
974 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
976 if (!notify_is_initted())
977 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
981 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
986 ms_error("Failed to send notification.");
988 linphone_gtk_show_main_window();
990 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
993 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
994 NotifyNotification *n;
995 switch(linphone_call_get_state(call)){
996 case LinphoneCallError:
997 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
999 case LinphoneCallEnd:
1000 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1002 case LinphoneCallIncomingReceived:
1003 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1004 if (notify_actions_supported()) {
1005 notify_notification_add_action (n,"answer", _("Answer"),
1006 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1007 notify_notification_add_action (n,"decline",_("Decline"),
1008 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1010 show_notification(n);
1012 case LinphoneCallPausedByRemote:
1013 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1018 if (body) g_free(body);
1019 if (remote) g_free(remote);
1024 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1026 case LinphoneCallOutgoingInit:
1027 linphone_gtk_create_in_call_view (call);
1029 case LinphoneCallOutgoingProgress:
1030 linphone_gtk_in_call_view_set_calling (call);
1032 case LinphoneCallStreamsRunning:
1033 linphone_gtk_in_call_view_set_in_call(call);
1035 case LinphoneCallError:
1036 linphone_gtk_in_call_view_terminate (call,msg);
1038 case LinphoneCallEnd:
1039 linphone_gtk_in_call_view_terminate(call,NULL);
1040 linphone_gtk_status_icon_set_blinking(FALSE);
1042 case LinphoneCallIncomingReceived:
1043 linphone_gtk_create_in_call_view(call);
1044 linphone_gtk_in_call_view_set_incoming(call);
1045 linphone_gtk_status_icon_set_blinking(TRUE);
1047 linphone_call_ref(call);
1048 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1051 case LinphoneCallResuming:
1052 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1053 linphone_gtk_in_call_view_set_in_call (call);
1055 case LinphoneCallPausing:
1056 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1057 case LinphoneCallPausedByRemote:
1058 linphone_gtk_in_call_view_set_paused(call);
1060 case LinphoneCallConnected:
1061 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1062 linphone_gtk_status_icon_set_blinking(FALSE);
1067 linphone_gtk_notify(call, msg);
1068 linphone_gtk_update_call_buttons (call);
1071 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1072 linphone_gtk_in_call_view_show_encryption(call);
1075 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1076 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1077 GtkTreeModel *model=gtk_combo_box_get_model(box);
1079 gboolean found=FALSE;
1080 const char *stock_id=NULL;
1082 if (gtk_tree_model_get_iter_first(model,&iter)){
1085 gtk_tree_model_get(model,&iter,2,&p,-1);
1090 }while(gtk_tree_model_iter_next(model,&iter));
1093 g_warning("Could not find proxy config in combo box of identities.");
1097 case LinphoneRegistrationOk:
1098 stock_id=GTK_STOCK_YES;
1100 case LinphoneRegistrationProgress:
1101 stock_id=GTK_STOCK_REFRESH;
1103 case LinphoneRegistrationCleared:
1106 case LinphoneRegistrationFailed:
1107 stock_id=GTK_STOCK_DIALOG_WARNING;
1112 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1115 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1116 LinphoneRegistrationState rs, const char *msg){
1118 case LinphoneRegistrationOk:
1120 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1121 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1122 linphone_gtk_exit_login_frame();
1129 update_registration_status(cfg,rs);
1132 void linphone_gtk_open_browser(const char *url){
1133 /*in gtk 2.16, gtk_show_uri does not work...*/
1135 #if GTK_CHECK_VERSION(2,18,3)
1136 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1139 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1140 g_spawn_command_line_async(cl,NULL);
1143 ShellExecute(0,"open",url,NULL,NULL,1);
1147 void linphone_gtk_link_to_website(GtkWidget *item){
1148 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1149 linphone_gtk_open_browser(home);
1152 #ifndef HAVE_GTK_OSX
1154 static GtkStatusIcon *icon=NULL;
1156 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1157 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1158 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1161 static GtkWidget *create_icon_menu(){
1162 GtkWidget *menu=gtk_menu_new();
1163 GtkWidget *menu_item;
1166 const gchar *homesite;
1168 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1169 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1170 tmp=g_strdup(homesite);
1171 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1172 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1174 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1175 gtk_widget_show(image);
1176 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1177 //g_object_unref(G_OBJECT(image));
1178 gtk_widget_show(menu_item);
1179 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1180 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1182 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1183 gtk_widget_show(menu_item);
1184 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1185 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1186 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1187 gtk_widget_show(menu_item);
1188 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1189 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1190 gtk_widget_show(menu);
1194 static void handle_icon_click() {
1195 GtkWidget *mw=linphone_gtk_get_main_window();
1196 if (!gtk_window_is_active((GtkWindow*)mw)) {
1197 linphone_gtk_show_main_window();
1199 gtk_widget_hide(mw);
1203 static void linphone_gtk_init_status_icon(){
1204 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1205 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1206 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1207 GtkWidget *menu=create_icon_menu();
1209 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1210 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1211 #if GTK_CHECK_VERSION(2,20,0)
1212 gtk_status_icon_set_name(icon,title);
1214 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1215 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1216 gtk_status_icon_set_tooltip(icon,title);
1217 gtk_status_icon_set_visible(icon,TRUE);
1218 g_object_set_data(G_OBJECT(icon),"menu",menu);
1219 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1220 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1221 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1222 pbuf=create_pixbuf(call_icon_path);
1223 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1226 static gboolean do_icon_blink(GtkStatusIcon *gi){
1227 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1228 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1229 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1230 if (cur_icon==call_icon){
1231 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1233 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1240 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1242 static gint attention_id;
1243 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1245 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1246 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1250 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1251 if (val && tout==0){
1252 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1253 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1254 }else if (!val && tout!=0){
1255 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1256 g_source_remove(tout);
1257 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1258 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1264 void linphone_gtk_options_activate(GtkWidget *item){
1265 #ifndef HAVE_GTK_OSX
1266 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1267 icon && !gtk_status_icon_is_embedded(icon));
1271 static void init_identity_combo(GtkComboBox *box){
1272 GtkListStore *store;
1273 GtkCellRenderer *r1,*r2;
1274 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1275 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1276 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1277 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1278 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1279 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1280 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1281 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1284 void linphone_gtk_load_identities(void){
1286 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1288 LinphoneProxyConfig *def=NULL;
1290 GtkListStore *store;
1293 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1294 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1295 /* model is empty, this is the first time we go here */
1296 init_identity_combo(box);
1297 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1299 gtk_list_store_clear(store);
1300 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1301 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1302 gtk_list_store_append(store,&iter);
1303 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1304 g_free(def_identity);
1305 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1307 elem=ms_list_next(elem),i++){
1308 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1309 gtk_list_store_append(store,&iter);
1310 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1311 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1317 gtk_combo_box_set_active(box,def_index);
1320 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1321 const char *label=gtk_button_get_label(button);
1322 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1324 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1325 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1326 if (linphone_core_in_call(linphone_gtk_get_core())){
1327 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1331 static void linphone_gtk_dtmf_released(GtkButton *button){
1332 linphone_core_stop_dtmf (linphone_gtk_get_core());
1335 static void linphone_gtk_connect_digits(void){
1336 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1337 GList *children=gtk_container_get_children(cont);
1339 for(elem=children;elem!=NULL;elem=elem->next){
1340 GtkButton *button=GTK_BUTTON(elem->data);
1341 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1342 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1346 static void linphone_gtk_check_menu_items(void){
1347 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1348 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1349 GtkWidget *selfview_item=linphone_gtk_get_widget(
1350 linphone_gtk_get_main_window(),"selfview_item");
1351 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1352 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1353 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1354 gtk_widget_set_sensitive(selfview_item,video_enabled);
1357 static gboolean linphone_gtk_can_manage_accounts(){
1358 LinphoneCore *lc=linphone_gtk_get_core();
1360 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1361 SipSetup *ss=(SipSetup*)elem->data;
1362 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1369 static void linphone_gtk_configure_main_window(){
1370 static gboolean config_loaded=FALSE;
1371 static const char *title;
1372 static const char *home;
1373 static const char *start_call_icon;
1374 static const char *add_call_icon;
1375 static const char *stop_call_icon;
1376 static const char *search_icon;
1377 static gboolean update_check_menu;
1378 static gboolean buttons_have_borders;
1379 static gboolean show_abcd;
1380 GtkWidget *w=linphone_gtk_get_main_window();
1381 if (!config_loaded){
1382 title=linphone_gtk_get_ui_config("title","Linphone");
1383 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1384 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1385 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1386 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1387 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1388 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1389 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1390 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1393 linphone_gtk_configure_window(w,"main_window");
1395 gtk_window_set_title(GTK_WINDOW(w),title);
1397 if (start_call_icon){
1398 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1399 create_pixmap (start_call_icon));
1400 if (!buttons_have_borders)
1401 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1404 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1405 create_pixmap (add_call_icon));
1406 if (!buttons_have_borders)
1407 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1409 if (stop_call_icon){
1410 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1411 create_pixmap (stop_call_icon));
1412 if (!buttons_have_borders)
1413 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1416 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1417 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1418 g_object_unref(G_OBJECT(pbuf));
1422 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1424 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1428 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1430 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1431 g_object_unref(G_OBJECT(pbuf));
1436 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1438 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1439 g_object_unref(G_OBJECT(pbuf));
1442 if (linphone_gtk_can_manage_accounts())
1443 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1444 if (update_check_menu){
1445 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1448 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1449 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1450 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1451 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1452 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1456 void linphone_gtk_manage_login(void){
1457 LinphoneCore *lc=linphone_gtk_get_core();
1458 LinphoneProxyConfig *cfg=NULL;
1459 linphone_core_get_default_proxy(lc,&cfg);
1461 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1462 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1463 linphone_gtk_show_login_frame(cfg);
1469 gboolean linphone_gtk_close(GtkWidget *mw){
1470 /*shutdown calls if any*/
1471 LinphoneCore *lc=linphone_gtk_get_core();
1472 if (linphone_core_in_call(lc)){
1473 linphone_core_terminate_all_calls(lc);
1475 linphone_core_enable_video_preview(lc,FALSE);
1476 #ifdef __APPLE__ /*until with have a better option*/
1477 gtk_window_iconify(GTK_WINDOW(mw));
1479 gtk_widget_hide(mw);
1485 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1486 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1487 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1489 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1490 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1497 static void linphone_gtk_init_main_window(){
1498 GtkWidget *main_window;
1500 linphone_gtk_configure_main_window();
1501 linphone_gtk_manage_login();
1503 linphone_gtk_load_identities();
1504 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1505 linphone_gtk_show_friends();
1506 linphone_gtk_connect_digits();
1507 main_window=linphone_gtk_get_main_window();
1508 linphone_gtk_call_log_update(main_window);
1510 linphone_gtk_update_call_buttons (NULL);
1511 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1512 g_signal_connect (G_OBJECT (main_window), "delete-event",
1513 G_CALLBACK (linphone_gtk_close), main_window);
1516 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1517 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1518 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1519 gtk_widget_hide(menubar);
1520 gtk_osxapplication_ready(theMacApp);
1522 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1524 linphone_gtk_check_menu_items();
1528 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1530 const char *lname="undef";
1532 #if defined(__linux) || defined(__APPLE__)
1533 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1552 g_error("Bad level !");
1554 #if defined(__linux) || defined(__APPLE__)
1556 msg=g_strdup_vprintf(fmt,cap);
1559 msg=g_strdup_vprintf(fmt,args);
1561 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1564 linphone_gtk_log_push(lev,fmt,args);
1568 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1569 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1570 linphone_gtk_get_main_window(), "uribar"));
1572 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1574 gtk_entry_set_text(uri_bar, refer_to);
1575 linphone_gtk_start_call(linphone_gtk_get_main_window());
1578 static void linphone_gtk_check_soundcards(){
1579 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1580 if (devices==NULL || devices[0]==NULL){
1581 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1582 _("No sound cards have been detected on this computer.\n"
1583 "You won't be able to send or receive audio calls."));
1587 static void linphone_gtk_quit(void){
1588 static gboolean quit_done=FALSE;
1591 g_source_remove_by_user_data(linphone_gtk_get_core());
1592 linphone_gtk_uninit_instance();
1593 linphone_gtk_destroy_log_window();
1594 linphone_core_destroy(the_core);
1595 linphone_gtk_log_uninit();
1599 gdk_threads_leave();
1605 This is not the correct way to implement block termination.
1606 The good way would be to call gtk_main_quit(), and return TRUE.
1607 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1608 As a result the program cannot exit at all.
1609 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1611 static gboolean on_block_termination(void){
1613 linphone_gtk_quit();
1618 int main(int argc, char *argv[]){
1623 const char *factory_config_file;
1625 GtkSettings *settings;
1627 const char *app_name="Linphone";
1629 g_thread_init(NULL);
1632 progpath = strdup(argv[0]);
1634 config_file=linphone_gtk_get_config_file(NULL);
1638 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1639 if ((lang=getenv("LANG"))!=NULL){
1642 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1648 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1651 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1654 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1657 setenv("LANG",lang,1);
1662 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1663 if (p==NULL) perror("bindtextdomain failed");
1664 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1665 textdomain (GETTEXT_PACKAGE);
1667 g_message("NLS disabled.\n");
1670 gtk_rc_add_default_file("./gtkrc");
1672 gdk_threads_enter();
1674 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1675 linphone_options,NULL,NULL)){
1676 gdk_threads_leave();
1680 settings=gtk_settings_get_default();
1681 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1682 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1683 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1684 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1686 if (workingdir!=NULL){
1687 if (chdir(workingdir)==-1){
1688 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1692 /* Now, look for the factory configuration file, we do it this late
1693 since we want to have had time to change directory and to parse
1694 the options, in case we needed to access the working directory */
1695 factory_config_file = linphone_gtk_get_factory_config_file();
1697 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1698 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1699 g_warning("This instance is going to exit now.");
1700 gdk_threads_leave();
1704 add_pixmap_directory("pixmaps");
1705 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1708 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1709 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1710 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1711 /*never block termination:*/
1712 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1715 the_ui=linphone_gtk_create_window("main");
1717 linphone_gtk_create_log_window();
1718 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1720 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1722 g_set_application_name(app_name);
1723 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1724 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1726 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1727 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1728 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1729 linphone_gtk_init_main_window();
1730 #ifndef HAVE_GTK_OSX
1731 linphone_gtk_init_status_icon();
1734 linphone_gtk_show_main_window();
1735 linphone_gtk_check_soundcards();
1737 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1738 linphone_gtk_check_for_new_version();
1741 linphone_gtk_quit();
1742 #ifndef HAVE_GTK_OSX
1743 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1744 gtk_status_icon_set_visible(icon,FALSE);