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>
40 #include <libnotify/notify.h>
43 #define LINPHONE_ICON "linphone.png"
45 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
47 static LinphoneCore *the_core=NULL;
48 static GtkWidget *the_ui=NULL;
50 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
51 static void linphone_gtk_show(LinphoneCore *lc);
52 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
53 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
54 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
55 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
56 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
57 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
58 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
59 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
60 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
61 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
62 static void linphone_gtk_status_icon_set_blinking(gboolean val);
65 static gboolean verbose=0;
66 static gboolean auto_answer = 0;
67 static gchar * addr_to_call = NULL;
68 static gboolean iconified=FALSE;
69 static gchar *workingdir=NULL;
70 static char *progpath=NULL;
71 gchar *linphone_logfile=NULL;
73 static GOptionEntry linphone_options[]={
77 .arg=G_OPTION_ARG_NONE,
78 .arg_data= (gpointer)&verbose,
79 .description=N_("log to stdout some debug information while running.")
82 .long_name = "logfile",
84 .arg = G_OPTION_ARG_STRING,
85 .arg_data = &linphone_logfile,
86 .description = N_("path to a file to write logs into.")
89 .long_name="iconified",
91 .arg=G_OPTION_ARG_NONE,
92 .arg_data= (gpointer)&iconified,
93 .description=N_("Start only in the system tray, do not show the main interface.")
98 .arg = G_OPTION_ARG_STRING,
99 .arg_data = &addr_to_call,
100 .description = N_("address to call right now")
103 .long_name = "auto-answer",
105 .arg = G_OPTION_ARG_NONE,
106 .arg_data = (gpointer) & auto_answer,
107 .description = N_("if set automatically answer incoming calls")
110 .long_name = "workdir",
112 .arg = G_OPTION_ARG_STRING,
113 .arg_data = (gpointer) & workingdir,
114 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
119 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
120 #define RELATIVE_XML_DIR
121 #define BUILD_TREE_XML_DIR "gtk"
124 #define CONFIG_FILE ".linphonerc"
126 #define CONFIG_FILE "linphonerc"
131 static char _config_file[1024];
134 const char *linphone_gtk_get_config_file(){
135 /*try accessing a local file first if exists*/
136 if (access(CONFIG_FILE,F_OK)==0){
137 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
140 const char *appdata=getenv("APPDATA");
142 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
143 CreateDirectory(_config_file,NULL);
144 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
147 const char *home=getenv("HOME");
148 if (home==NULL) home=".";
149 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
156 #define FACTORY_CONFIG_FILE "linphonerc.factory"
157 static char _factory_config_file[1024];
158 static const char *linphone_gtk_get_factory_config_file(){
159 /*try accessing a local file first if exists*/
160 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
161 snprintf(_factory_config_file,sizeof(_factory_config_file),
162 "%s",FACTORY_CONFIG_FILE);
166 if (progpath != NULL) {
168 progdir = strdup(progpath);
170 basename = strrchr(progdir, '\\');
171 if (basename != NULL) {
174 snprintf(_factory_config_file, sizeof(_factory_config_file),
175 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
177 if (workingdir!=NULL) {
178 snprintf(_factory_config_file, sizeof(_factory_config_file),
179 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
186 basename = strrchr(progdir, '/');
187 if (basename != NULL) {
190 snprintf(_factory_config_file, sizeof(_factory_config_file),
191 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
200 return _factory_config_file;
203 static void linphone_gtk_init_liblinphone(const char *config_file,
204 const char *factory_config_file) {
205 LinphoneCoreVTable vtable={0};
207 vtable.call_state_changed=linphone_gtk_call_state_changed;
208 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
209 vtable.show=linphone_gtk_show;
210 vtable.notify_presence_recv=linphone_gtk_notify_recv;
211 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
212 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
213 vtable.display_status=linphone_gtk_display_status;
214 vtable.display_message=linphone_gtk_display_message;
215 vtable.display_warning=linphone_gtk_display_warning;
216 vtable.display_url=linphone_gtk_display_url;
217 vtable.call_log_updated=linphone_gtk_call_log_updated;
218 vtable.text_received=linphone_gtk_text_received;
219 vtable.refer_received=linphone_gtk_refer_received;
220 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
222 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
223 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
224 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
229 LinphoneCore *linphone_gtk_get_core(void){
233 GtkWidget *linphone_gtk_get_main_window(){
237 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
238 static const char *icon_path=NULL;
239 static const char *hiddens=NULL;
240 static const char *shown=NULL;
241 static bool_t config_loaded=FALSE;
242 if (linphone_gtk_get_core()==NULL) return;
243 if (config_loaded==FALSE){
244 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
245 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
246 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
250 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
252 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
254 GdkPixbuf *pbuf=create_pixbuf(icon_path);
255 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
256 g_object_unref(G_OBJECT(pbuf));
260 static int get_ui_file(const char *name, char *path, int pathsize){
261 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
262 if (access(path,F_OK)!=0){
263 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
264 if (access(path,F_OK)!=0){
265 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
266 INSTALLED_XML_DIR,name);
273 GtkWidget *linphone_gtk_create_window(const char *window_name){
274 GError* error = NULL;
275 GtkBuilder* builder = gtk_builder_new ();
279 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
281 if (!gtk_builder_add_from_file (builder, path, &error)){
282 g_error("Couldn't load builder file: %s", error->message);
283 g_error_free (error);
286 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
288 g_error("Could not retrieve '%s' window from xml file",window_name);
291 g_object_set_data(G_OBJECT(w),"builder",builder);
292 gtk_builder_connect_signals(builder,w);
293 linphone_gtk_configure_window(w,window_name);
297 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
300 GtkBuilder* builder = gtk_builder_new ();
302 gchar *object_ids[2];
303 object_ids[0]=g_strdup(widget_name);
306 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
307 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
308 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
309 g_error_free (error);
310 g_free(object_ids[0]);
313 g_free(object_ids[0]);
314 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
316 g_error("Could not retrieve '%s' window from xml file",widget_name);
319 g_object_set_data(G_OBJECT(w),"builder",builder);
320 gtk_builder_connect_signals(builder,w);
324 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
325 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
328 g_error("Fail to retrieve builder from window !");
331 w=gtk_builder_get_object(builder,name);
333 g_error("No widget named %s found in xml interface.",name);
335 return GTK_WIDGET(w);
339 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
341 GtkWidget *main_window=linphone_gtk_get_main_window();
343 gtk_widget_show(main_window);
344 if (type==GTK_MESSAGE_QUESTION)
346 /* draw a question box. link to dialog_click callback */
347 dialog = gtk_message_dialog_new (
348 GTK_WINDOW(main_window),
349 GTK_DIALOG_DESTROY_WITH_PARENT,
350 GTK_MESSAGE_QUESTION,
353 (const gchar*)message);
354 /* connect to some callback : REVISIT */
356 g_signal_connect_swapped (G_OBJECT (dialog), "response",
357 G_CALLBACK (dialog_click),
360 /* actually show the box */
361 gtk_widget_show(dialog);
365 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
366 GTK_DIALOG_DESTROY_WITH_PARENT,
370 (const gchar*)message);
371 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
372 g_signal_connect_swapped (G_OBJECT (dialog), "response",
373 G_CALLBACK (gtk_widget_destroy),
375 gtk_widget_show(dialog);
379 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
380 if (id==GTK_RESPONSE_CANCEL){
381 gtk_widget_destroy(GTK_WIDGET(dialog));
385 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
386 g_message("About url clicked");
387 linphone_gtk_open_browser(url);
390 void linphone_gtk_show_about(){
391 struct stat filestat;
392 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
395 GdkPixbuf *logo=create_pixbuf(
396 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
397 static const char *defcfg="defcfg";
399 about=linphone_gtk_create_window("about");
400 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
401 memset(&filestat,0,sizeof(filestat));
402 if (stat(license_file,&filestat)!=0){
403 license_file="COPYING";
404 stat(license_file,&filestat);
406 if (filestat.st_size>0){
407 char *license=g_malloc(filestat.st_size+1);
408 FILE *f=fopen(license_file,"r");
409 if (f && fread(license,filestat.st_size,1,f)==1){
410 license[filestat.st_size]='\0';
411 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
415 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
416 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
417 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
418 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
419 tmp=linphone_gtk_get_ui_config("artists",defcfg);
424 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
426 tmp=linphone_gtk_get_ui_config("translators",defcfg);
428 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
429 tmp=linphone_gtk_get_ui_config("comments",defcfg);
431 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
432 gtk_widget_show(about);
435 static void set_video_window_decorations(GdkWindow *w){
436 const char *title=linphone_gtk_get_ui_config("title","Linphone");
437 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
438 char video_title[256];
439 GdkPixbuf *pbuf=create_pixbuf(icon_path);
440 if (!linphone_core_in_call(linphone_gtk_get_core())){
441 snprintf(video_title,sizeof(video_title),"%s video",title);
442 /* When not in call, treat the video as a normal window */
443 gdk_window_set_keep_above(w, FALSE);
445 LinphoneAddress *uri =
446 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
449 linphone_address_clean(uri);
450 if (linphone_address_get_display_name(uri)!=NULL){
451 display_name=ms_strdup(linphone_address_get_display_name(uri));
453 display_name=linphone_address_as_string(uri);
455 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
456 linphone_address_destroy(uri);
457 ms_free(display_name);
459 /* During calls, bring up the video window, arrange so that
460 it is above all the other windows */
461 gdk_window_deiconify(w);
462 gdk_window_set_keep_above(w,TRUE);
463 /* Maybe we should have the following, but then we want to
464 have a timer that turns it off after a little while. */
465 /* gdk_window_set_urgency_hint(w,TRUE); */
467 gdk_window_set_title(w,video_title);
468 /* Refrain the video window to be closed at all times. */
469 gdk_window_set_functions(w,
470 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
471 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
474 l=g_list_append(l,pbuf);
475 gdk_window_set_icon_list(w,l);
477 g_object_unref(G_OBJECT(pbuf));
481 static gboolean video_needs_update=FALSE;
483 static void update_video_title(){
484 video_needs_update=TRUE;
487 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
488 static gboolean first_time=TRUE;
490 static unsigned long previd=0;
491 static unsigned long preview_previd=0;
492 static gboolean in_iterate=FALSE;
495 if (in_iterate) return TRUE;
497 linphone_core_iterate(lc);
499 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
500 linphone_gtk_show_directory_search();
504 id=linphone_core_get_native_video_window_id(lc);
505 if (id!=previd || video_needs_update){
509 ms_message("Updating window decorations");
511 w=gdk_window_foreign_new(id);
513 w=gdk_window_foreign_new((HANDLE)id);
516 set_video_window_decorations(w);
517 g_object_unref(G_OBJECT(w));
519 else ms_error("gdk_window_foreign_new() failed");
520 if (video_needs_update) video_needs_update=FALSE;
523 id=linphone_core_get_native_preview_window_id (lc);
524 if (id!=preview_previd ){
528 ms_message("Updating window decorations for preview");
530 w=gdk_window_foreign_new(id);
532 w=gdk_window_foreign_new((HANDLE)id);
535 set_video_window_decorations(w);
536 g_object_unref(G_OBJECT(w));
538 else ms_error("gdk_window_foreign_new() failed");
539 if (video_needs_update) video_needs_update=FALSE;
542 if (addr_to_call!=NULL){
543 /*make sure we are not showing the login screen*/
544 GtkWidget *mw=linphone_gtk_get_main_window();
545 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
546 if (!GTK_WIDGET_VISIBLE(login_frame)){
547 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
548 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
550 linphone_gtk_start_call(uri_bar);
557 static void load_uri_history(){
558 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
561 GtkEntryCompletion *gep=gtk_entry_completion_new();
562 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
565 snprintf(key,sizeof(key),"uri%i",i);
566 uri=linphone_gtk_get_ui_config(key,NULL);
569 gtk_list_store_append(model,&iter);
570 gtk_list_store_set(model,&iter,0,uri,-1);
571 if (i==0) gtk_entry_set_text(uribar,uri);
575 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
576 gtk_entry_completion_set_text_column(gep,0);
577 gtk_entry_set_completion(uribar,gep);
580 static void save_uri_history(){
581 LinphoneCore *lc=linphone_gtk_get_core();
582 LpConfig *cfg=linphone_core_get_config(lc);
583 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
588 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
590 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
592 gtk_tree_model_get(model,&iter,0,&uri,-1);
594 snprintf(key,sizeof(key),"uri%i",i);
595 lp_config_set_string(cfg,"GtkUi",key,uri);
600 }while(gtk_tree_model_iter_next(model,&iter));
604 static void completion_add_text(GtkEntry *entry, const char *text){
606 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
608 if (gtk_tree_model_get_iter_first(model,&iter)){
611 gtk_tree_model_get(model,&iter,0,&uri,-1);
613 if (strcmp(uri,text)==0) {
615 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
621 }while (gtk_tree_model_iter_next(model,&iter));
623 /* and prepend it on top of the list */
624 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
625 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
630 void linphone_gtk_show_main_window(){
631 GtkWidget *w=linphone_gtk_get_main_window();
632 LinphoneCore *lc=linphone_gtk_get_core();
633 if (linphone_core_video_enabled(lc)){
634 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
635 VIDEOSELFVIEW_DEFAULT));
638 gtk_window_present(GTK_WINDOW(w));
641 static void linphone_gtk_show(LinphoneCore *lc){
643 linphone_gtk_show_main_window();
647 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
648 GtkWidget *mw=linphone_gtk_get_main_window();
649 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
650 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
651 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
653 if (linphone_gtk_use_in_call_view() && call)
654 linphone_gtk_in_call_view_terminate(call,error);
655 update_video_title();
658 static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){
659 for(;calls!=NULL;calls=calls->next){
660 LinphoneCall *call=(LinphoneCall*)calls->data;
661 LinphoneCallState cs=linphone_call_get_state(call);
663 if (cs!=LinphoneCallPaused && cs!=LinphoneCallPausing)
670 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
671 LinphoneCore *lc=linphone_gtk_get_core();
672 GtkWidget *mw=linphone_gtk_get_main_window();
673 const MSList *calls=linphone_core_get_calls(lc);
675 bool_t start_active=TRUE;
676 bool_t stop_active=FALSE;
677 bool_t add_call=FALSE;
678 int call_list_size=ms_list_size(calls);
685 if (all_other_calls_paused(NULL,calls)){
688 }else if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived && all_other_calls_paused(call,calls)){
689 if (call_list_size>1){
700 button=linphone_gtk_get_widget(mw,"start_call");
701 gtk_widget_set_sensitive(button,start_active);
702 gtk_widget_set_visible(button,!add_call);
704 button=linphone_gtk_get_widget(mw,"add_call");
705 gtk_widget_set_sensitive(button,start_active);
706 gtk_widget_set_visible(button,add_call);
708 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
710 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
711 update_video_title();
714 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
715 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
716 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
717 completion_add_text(GTK_ENTRY(uri_bar),entered);
719 linphone_gtk_call_terminated(NULL,NULL);
724 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
725 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
726 linphone_core_accept_call (linphone_gtk_get_core(),call);
727 linphone_call_unref(call);
733 void linphone_gtk_start_call(GtkWidget *w){
734 LinphoneCore *lc=linphone_gtk_get_core();
736 /*change into in-call mode, then do the work later as it might block a bit */
737 GtkWidget *mw=gtk_widget_get_toplevel(w);
738 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
740 call=linphone_gtk_get_currently_displayed_call ();
741 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
742 linphone_core_accept_call(lc,call);
744 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
745 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
746 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
747 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
752 void linphone_gtk_uri_bar_activate(GtkWidget *w){
753 linphone_gtk_start_call(w);
757 void linphone_gtk_terminate_call(GtkWidget *button){
758 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
760 linphone_core_terminate_call(linphone_gtk_get_core(),call);
763 void linphone_gtk_decline_clicked(GtkWidget *button){
764 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
766 linphone_core_terminate_call(linphone_gtk_get_core(),call);
769 void linphone_gtk_answer_clicked(GtkWidget *button){
770 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
772 linphone_core_pause_all_calls(linphone_gtk_get_core());
773 linphone_core_accept_call(linphone_gtk_get_core(),call);
774 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
778 void linphone_gtk_enable_video(GtkWidget *w){
779 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
780 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
781 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
782 gtk_widget_set_sensitive(selfview_item,val);
784 linphone_core_enable_video_preview(linphone_gtk_get_core(),
785 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
787 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
791 void linphone_gtk_enable_self_view(GtkWidget *w){
792 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
793 LinphoneCore *lc=linphone_gtk_get_core();
794 linphone_core_enable_video_preview(lc,val);
795 linphone_core_enable_self_view(lc,val);
796 linphone_gtk_set_ui_config_int("videoselfview",val);
799 void linphone_gtk_used_identity_changed(GtkWidget *w){
800 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
801 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
802 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
803 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
804 linphone_gtk_show_directory_search();
806 if (sel) g_free(sel);
809 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
810 linphone_gtk_show_friends();
813 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
815 case GTK_RESPONSE_YES:
816 linphone_gtk_show_contact(lf);
819 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
821 gtk_widget_destroy(dialog);
824 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
827 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
828 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
832 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);
833 dialog = gtk_message_dialog_new (
834 GTK_WINDOW(linphone_gtk_get_main_window()),
835 GTK_DIALOG_DESTROY_WITH_PARENT,
836 GTK_MESSAGE_QUESTION,
841 g_signal_connect(G_OBJECT (dialog), "response",
842 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
843 /* actually show the box */
844 gtk_widget_show(dialog);
847 typedef struct _AuthTimeout{
852 static void auth_timeout_clean(AuthTimeout *tout){
856 static gboolean auth_timeout_destroy(AuthTimeout *tout){
858 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
859 gtk_widget_destroy(tout->w);
865 static AuthTimeout * auth_timeout_new(GtkWidget *w){
866 AuthTimeout *tout=g_new(AuthTimeout,1);
868 /*so that the timeout no more references the widget when it is destroyed:*/
869 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
870 /*so that the widget is automatically destroyed after some time */
871 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
875 void linphone_gtk_password_cancel(GtkWidget *w){
876 LinphoneAuthInfo *info;
877 GtkWidget *window=gtk_widget_get_toplevel(w);
878 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
879 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
880 gtk_widget_destroy(window);
883 void linphone_gtk_password_ok(GtkWidget *w){
885 GtkWidget *window=gtk_widget_get_toplevel(w);
886 LinphoneAuthInfo *info;
887 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
888 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
889 entry=linphone_gtk_get_widget(window,"password_entry");
890 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
891 linphone_auth_info_set_userid(info,
892 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
893 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
894 gtk_widget_destroy(window);
897 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
898 GtkWidget *w=linphone_gtk_create_window("password");
899 GtkWidget *label=linphone_gtk_get_widget(w,"message");
900 LinphoneAuthInfo *info;
902 GtkWidget *mw=linphone_gtk_get_main_window();
904 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
905 /*don't prompt for authentication when login frame is visible*/
906 linphone_core_abort_authentication(lc,NULL);
910 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
912 gtk_label_set_markup(GTK_LABEL(label),msg);
914 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
915 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
916 g_object_set_data(G_OBJECT(w),"auth_info",info);
917 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
922 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
923 GtkWidget *w=linphone_gtk_get_main_window();
924 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
925 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
926 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
930 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
931 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
934 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
935 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
938 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
940 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
941 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
944 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
945 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
946 if (w) linphone_gtk_call_log_update(w);
947 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
951 static void make_notification(const char *title, const char *body){
952 NotifyNotification *n;
953 n = notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
954 if (n && !notify_notification_show(n,NULL))
955 ms_error("Failed to send notification.");
960 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
962 if (!notify_is_initted())
963 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
967 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL),NULL))
968 ms_error("Failed to send notification.");
970 linphone_gtk_show_main_window();
972 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
975 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
976 switch(linphone_call_get_state(call)){
977 case LinphoneCallError:
978 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
980 case LinphoneCallEnd:
981 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
983 case LinphoneCallIncomingReceived:
984 make_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
986 case LinphoneCallPausedByRemote:
987 make_notification(_("Call paused"),body=g_markup_printf_escaped("<span size=\"large\">by %s</span>",remote));
992 if (body) g_free(body);
993 if (remote) g_free(remote);
998 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1000 case LinphoneCallOutgoingInit:
1001 linphone_gtk_create_in_call_view (call);
1003 case LinphoneCallOutgoingProgress:
1004 linphone_gtk_in_call_view_set_calling (call);
1006 case LinphoneCallStreamsRunning:
1007 linphone_gtk_in_call_view_set_in_call(call);
1009 case LinphoneCallError:
1010 linphone_gtk_in_call_view_terminate (call,msg);
1012 case LinphoneCallEnd:
1013 linphone_gtk_in_call_view_terminate(call,NULL);
1014 linphone_gtk_status_icon_set_blinking(FALSE);
1016 case LinphoneCallIncomingReceived:
1017 linphone_gtk_create_in_call_view (call);
1018 linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc)));
1019 linphone_gtk_status_icon_set_blinking(TRUE);
1021 linphone_call_ref(call);
1022 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1025 case LinphoneCallResuming:
1026 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1027 linphone_gtk_in_call_view_set_in_call (call);
1029 case LinphoneCallPausing:
1030 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1031 case LinphoneCallPausedByRemote:
1032 linphone_gtk_in_call_view_set_paused(call);
1034 case LinphoneCallConnected:
1035 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1036 linphone_gtk_status_icon_set_blinking(FALSE);
1041 linphone_gtk_notify(call, msg);
1042 linphone_gtk_update_call_buttons (call);
1045 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1046 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1047 GtkTreeModel *model=gtk_combo_box_get_model(box);
1049 gboolean found=FALSE;
1050 const char *stock_id=NULL;
1052 if (gtk_tree_model_get_iter_first(model,&iter)){
1055 gtk_tree_model_get(model,&iter,2,&p,-1);
1060 }while(gtk_tree_model_iter_next(model,&iter));
1063 g_warning("Could not find proxy config in combo box of identities.");
1067 case LinphoneRegistrationOk:
1068 stock_id=GTK_STOCK_YES;
1070 case LinphoneRegistrationProgress:
1071 stock_id=GTK_STOCK_REFRESH;
1073 case LinphoneRegistrationCleared:
1076 case LinphoneRegistrationFailed:
1077 stock_id=GTK_STOCK_DIALOG_WARNING;
1082 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1085 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1086 LinphoneRegistrationState rs, const char *msg){
1088 case LinphoneRegistrationOk:
1090 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1091 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1092 linphone_gtk_exit_login_frame();
1099 update_registration_status(cfg,rs);
1103 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1104 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1105 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1108 void linphone_gtk_open_browser(const char *url){
1109 /*in gtk 2.16, gtk_show_uri does not work...*/
1111 #if GTK_CHECK_VERSION(2,18,3)
1112 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1115 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1116 g_spawn_command_line_async(cl,NULL);
1119 ShellExecute(0,"open",url,NULL,NULL,1);
1123 void linphone_gtk_link_to_website(GtkWidget *item){
1124 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1125 linphone_gtk_open_browser(home);
1128 static GtkWidget *create_icon_menu(){
1129 GtkWidget *menu=gtk_menu_new();
1130 GtkWidget *menu_item;
1133 const gchar *homesite;
1135 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1136 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1137 tmp=g_strdup(homesite);
1138 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1139 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1141 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1142 gtk_widget_show(image);
1143 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1144 //g_object_unref(G_OBJECT(image));
1145 gtk_widget_show(menu_item);
1146 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1147 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1149 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1150 gtk_widget_show(menu_item);
1151 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1152 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1153 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1154 gtk_widget_show(menu_item);
1155 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1156 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1157 gtk_widget_show(menu);
1161 static GtkStatusIcon *icon=NULL;
1163 static void linphone_gtk_init_status_icon(){
1164 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1165 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1166 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1167 GtkWidget *menu=create_icon_menu();
1169 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1170 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1171 gtk_status_icon_set_name(icon,title);
1172 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1173 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1174 gtk_status_icon_set_tooltip(icon,title);
1175 gtk_status_icon_set_visible(icon,TRUE);
1176 g_object_set_data(G_OBJECT(icon),"menu",menu);
1177 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1178 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1179 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1180 pbuf=create_pixbuf(call_icon_path);
1181 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1184 static gboolean do_icon_blink(GtkStatusIcon *gi){
1185 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1186 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1187 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1188 if (cur_icon==call_icon){
1189 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1191 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1196 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1198 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1199 if (val && tout==0){
1200 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1201 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1202 }else if (!val && tout!=0){
1203 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1204 g_source_remove(tout);
1205 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1206 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1210 static void init_identity_combo(GtkComboBox *box){
1211 GtkListStore *store;
1212 GtkCellRenderer *r1,*r2;
1213 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1214 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1215 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1216 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1217 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1218 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1219 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1220 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1223 void linphone_gtk_load_identities(void){
1225 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1227 LinphoneProxyConfig *def=NULL;
1229 GtkListStore *store;
1232 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1233 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1234 /* model is empty, this is the first time we go here */
1235 init_identity_combo(box);
1236 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1238 gtk_list_store_clear(store);
1239 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1240 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1241 gtk_list_store_append(store,&iter);
1242 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1243 g_free(def_identity);
1244 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1246 elem=ms_list_next(elem),i++){
1247 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1248 gtk_list_store_append(store,&iter);
1249 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1250 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1256 gtk_combo_box_set_active(box,def_index);
1259 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1260 const char *label=gtk_button_get_label(button);
1261 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1263 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1264 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1265 if (linphone_core_in_call(linphone_gtk_get_core())){
1266 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1270 static void linphone_gtk_dtmf_released(GtkButton *button){
1271 linphone_core_stop_dtmf (linphone_gtk_get_core());
1274 static void linphone_gtk_connect_digits(void){
1275 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1276 GList *children=gtk_container_get_children(cont);
1278 for(elem=children;elem!=NULL;elem=elem->next){
1279 GtkButton *button=GTK_BUTTON(elem->data);
1280 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1281 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1285 static void linphone_gtk_check_menu_items(void){
1286 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1287 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1288 GtkWidget *selfview_item=linphone_gtk_get_widget(
1289 linphone_gtk_get_main_window(),"selfview_item");
1290 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1291 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1292 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1293 gtk_widget_set_sensitive(selfview_item,video_enabled);
1296 static gboolean linphone_gtk_can_manage_accounts(){
1297 LinphoneCore *lc=linphone_gtk_get_core();
1299 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1300 SipSetup *ss=(SipSetup*)elem->data;
1301 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1308 static void linphone_gtk_configure_main_window(){
1309 static gboolean config_loaded=FALSE;
1310 static const char *title;
1311 static const char *home;
1312 static const char *start_call_icon;
1313 static const char *add_call_icon;
1314 static const char *stop_call_icon;
1315 static const char *search_icon;
1316 static gboolean update_check_menu;
1317 static gboolean buttons_have_borders;
1318 static gboolean show_abcd;
1319 GtkWidget *w=linphone_gtk_get_main_window();
1320 if (!config_loaded){
1321 title=linphone_gtk_get_ui_config("title","Linphone");
1322 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1323 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1324 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1325 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1326 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1327 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1328 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1329 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1332 linphone_gtk_configure_window(w,"main_window");
1334 gtk_window_set_title(GTK_WINDOW(w),title);
1336 if (start_call_icon){
1337 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1338 create_pixmap (start_call_icon));
1339 if (!buttons_have_borders)
1340 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1343 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1344 create_pixmap (add_call_icon));
1345 if (!buttons_have_borders)
1346 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1348 if (stop_call_icon){
1349 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1350 create_pixmap (stop_call_icon));
1351 if (!buttons_have_borders)
1352 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1355 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1356 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1357 g_object_unref(G_OBJECT(pbuf));
1361 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1363 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1367 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1369 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1370 g_object_unref(G_OBJECT(pbuf));
1375 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1377 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1378 g_object_unref(G_OBJECT(pbuf));
1381 if (linphone_gtk_can_manage_accounts())
1382 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1383 if (update_check_menu){
1384 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1387 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1388 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1389 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1390 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1391 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1395 void linphone_gtk_manage_login(void){
1396 LinphoneCore *lc=linphone_gtk_get_core();
1397 LinphoneProxyConfig *cfg=NULL;
1398 linphone_core_get_default_proxy(lc,&cfg);
1400 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1401 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1402 linphone_gtk_show_login_frame(cfg);
1408 gboolean linphone_gtk_close(GtkWidget *mw){
1409 /*shutdown calls if any*/
1410 LinphoneCore *lc=linphone_gtk_get_core();
1411 if (linphone_core_in_call(lc)){
1412 linphone_core_terminate_all_calls(lc);
1414 linphone_core_enable_video_preview(lc,FALSE);
1415 #ifdef __APPLE__ /*until with have a better option*/
1416 gtk_window_iconify(GTK_WINDOW(mw));
1418 gtk_widget_hide(mw);
1424 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1425 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1426 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1428 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1429 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1436 static void linphone_gtk_init_main_window(){
1437 GtkWidget *main_window;
1439 linphone_gtk_configure_main_window();
1440 linphone_gtk_manage_login();
1442 linphone_gtk_load_identities();
1443 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1444 linphone_gtk_show_friends();
1445 linphone_gtk_connect_digits();
1446 main_window=linphone_gtk_get_main_window();
1447 linphone_gtk_call_log_update(main_window);
1449 linphone_gtk_update_call_buttons (NULL);
1450 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1451 g_signal_connect (G_OBJECT (main_window), "delete-event",
1452 G_CALLBACK (linphone_gtk_close), main_window);
1455 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1456 gtk_widget_destroy(linphone_gtk_get_widget(main_window,"imagemenuitem5"));
1457 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1458 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1459 gtk_widget_hide(menubar);
1460 gtk_osxapplication_ready(theMacApp);
1462 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1464 linphone_gtk_check_menu_items();
1468 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1470 const char *lname="undef";
1472 #if defined(__linux) || defined(__APPLE__)
1473 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1492 g_error("Bad level !");
1494 #if defined(__linux) || defined(__APPLE__)
1496 msg=g_strdup_vprintf(fmt,cap);
1499 msg=g_strdup_vprintf(fmt,args);
1501 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1504 linphone_gtk_log_push(lev,fmt,args);
1508 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1509 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1510 linphone_gtk_get_main_window(), "uribar"));
1512 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1514 gtk_entry_set_text(uri_bar, refer_to);
1515 linphone_gtk_start_call(linphone_gtk_get_main_window());
1518 static void linphone_gtk_check_soundcards(){
1519 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1520 if (devices==NULL || devices[0]==NULL){
1521 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1522 _("No sound cards have been detected on this computer.\n"
1523 "You won't be able to send or receive audio calls."));
1527 static void linphone_gtk_quit(void){
1528 linphone_gtk_uninit_instance();
1529 gdk_threads_leave();
1530 linphone_gtk_destroy_log_window();
1531 linphone_core_destroy(the_core);
1532 linphone_gtk_log_uninit();
1540 This is not the correct way to implement block termination.
1541 The good way would be to call gtk_main_quit(), and return TRUE.
1542 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1543 As a result the program cannot exit at all.
1544 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1546 static gboolean on_block_termination(void){
1548 linphone_gtk_quit();
1553 int main(int argc, char *argv[]){
1557 const char *config_file;
1558 const char *factory_config_file;
1560 GtkSettings *settings;
1562 const char *app_name="Linphone";
1564 g_thread_init(NULL);
1567 progpath = strdup(argv[0]);
1569 config_file=linphone_gtk_get_config_file();
1573 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1574 if ((lang=getenv("LANG"))!=NULL){
1577 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1583 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1586 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1589 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1592 setenv("LANG",lang,1);
1597 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1598 if (p==NULL) perror("bindtextdomain failed");
1599 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1600 textdomain (GETTEXT_PACKAGE);
1602 g_message("NLS disabled.\n");
1605 gtk_rc_add_default_file("./gtkrc");
1607 gdk_threads_enter();
1609 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1610 linphone_options,NULL,NULL)){
1611 gdk_threads_leave();
1615 settings=gtk_settings_get_default();
1616 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1617 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1618 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1619 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1621 if (workingdir!=NULL){
1622 if (chdir(workingdir)==-1){
1623 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1627 /* Now, look for the factory configuration file, we do it this late
1628 since we want to have had time to change directory and to parse
1629 the options, in case we needed to access the working directory */
1630 factory_config_file = linphone_gtk_get_factory_config_file();
1632 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1633 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1634 g_warning("This instance is going to exit now.");
1635 gdk_threads_leave();
1639 add_pixmap_directory("pixmaps");
1640 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1643 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1644 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1645 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1646 /*never block termination:*/
1647 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1650 the_ui=linphone_gtk_create_window("main");
1652 linphone_gtk_create_log_window();
1653 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1655 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1657 g_set_application_name(app_name);
1658 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1659 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1661 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1662 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1663 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1664 linphone_gtk_init_main_window();
1665 #ifndef HAVE_GTK_OSX
1666 linphone_gtk_init_status_icon();
1669 linphone_gtk_show_main_window();
1670 linphone_gtk_check_soundcards();
1672 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1673 linphone_gtk_check_for_new_version();
1676 linphone_gtk_quit();
1677 #ifndef HAVE_GTK_OSX
1678 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1679 gtk_status_icon_set_visible(icon,FALSE);