2 linphone, gtk-glade interface.
3 Copyright (C) 2008 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define VIDEOSELFVIEW_DEFAULT 1
27 #include <sys/types.h>
32 #include <gtkosxapplication.h>
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53 GtkWidget *the_wizard=NULL;
55 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
66 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
67 static void linphone_gtk_status_icon_set_blinking(gboolean val);
70 static gboolean verbose=0;
71 static gboolean auto_answer = 0;
72 static gchar * addr_to_call = NULL;
73 static gboolean iconified=FALSE;
74 static gchar *workingdir=NULL;
75 static char *progpath=NULL;
76 gchar *linphone_logfile=NULL;
78 static GOptionEntry linphone_options[]={
82 .arg=G_OPTION_ARG_NONE,
83 .arg_data= (gpointer)&verbose,
84 .description=N_("log to stdout some debug information while running.")
87 .long_name = "logfile",
89 .arg = G_OPTION_ARG_STRING,
90 .arg_data = &linphone_logfile,
91 .description = N_("path to a file to write logs into.")
94 .long_name="iconified",
96 .arg=G_OPTION_ARG_NONE,
97 .arg_data= (gpointer)&iconified,
98 .description=N_("Start only in the system tray, do not show the main interface.")
103 .arg = G_OPTION_ARG_STRING,
104 .arg_data = &addr_to_call,
105 .description = N_("address to call right now")
108 .long_name = "auto-answer",
110 .arg = G_OPTION_ARG_NONE,
111 .arg_data = (gpointer) & auto_answer,
112 .description = N_("if set automatically answer incoming calls")
115 .long_name = "workdir",
117 .arg = G_OPTION_ARG_STRING,
118 .arg_data = (gpointer) & workingdir,
119 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
124 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
125 #define RELATIVE_XML_DIR
126 #define BUILD_TREE_XML_DIR "gtk"
129 #define CONFIG_FILE ".linphonerc"
130 #define SECRETS_FILE ".linphone-zidcache"
132 #define CONFIG_FILE "linphonerc"
133 #define SECRETS_FILE "linphone-zidcache"
137 char *linphone_gtk_get_config_file(const char *filename){
138 const int path_max=1024;
139 char *config_file=g_malloc0(path_max);
140 if (filename==NULL) filename=CONFIG_FILE;
141 /*try accessing a local file first if exists*/
142 if (access(CONFIG_FILE,F_OK)==0){
143 snprintf(config_file,path_max,"%s",filename);
146 const char *appdata=getenv("APPDATA");
148 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
149 CreateDirectory(config_file,NULL);
150 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
153 const char *home=getenv("HOME");
154 if (home==NULL) home=".";
155 snprintf(config_file,path_max,"%s/%s",home,filename);
162 #define FACTORY_CONFIG_FILE "linphonerc.factory"
163 static char _factory_config_file[1024];
164 static const char *linphone_gtk_get_factory_config_file(){
165 /*try accessing a local file first if exists*/
166 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
167 snprintf(_factory_config_file,sizeof(_factory_config_file),
168 "%s",FACTORY_CONFIG_FILE);
172 if (progpath != NULL) {
174 progdir = strdup(progpath);
176 basename = strrchr(progdir, '\\');
177 if (basename != NULL) {
180 snprintf(_factory_config_file, sizeof(_factory_config_file),
181 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
183 if (workingdir!=NULL) {
184 snprintf(_factory_config_file, sizeof(_factory_config_file),
185 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
192 basename = strrchr(progdir, '/');
193 if (basename != NULL) {
196 snprintf(_factory_config_file, sizeof(_factory_config_file),
197 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
206 return _factory_config_file;
209 static void linphone_gtk_init_liblinphone(const char *config_file,
210 const char *factory_config_file) {
211 LinphoneCoreVTable vtable={0};
212 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
214 vtable.call_state_changed=linphone_gtk_call_state_changed;
215 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
216 vtable.notify_presence_recv=linphone_gtk_notify_recv;
217 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
218 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
219 vtable.display_status=linphone_gtk_display_status;
220 vtable.display_message=linphone_gtk_display_message;
221 vtable.display_warning=linphone_gtk_display_warning;
222 vtable.display_url=linphone_gtk_display_url;
223 vtable.call_log_updated=linphone_gtk_call_log_updated;
224 vtable.text_received=linphone_gtk_text_received;
225 vtable.refer_received=linphone_gtk_refer_received;
226 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
227 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
229 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
230 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
231 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
232 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
233 g_free(secrets_file);
238 LinphoneCore *linphone_gtk_get_core(void){
242 GtkWidget *linphone_gtk_get_main_window(){
246 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
247 static const char *icon_path=NULL;
248 static const char *hiddens=NULL;
249 static const char *shown=NULL;
250 static bool_t config_loaded=FALSE;
251 if (linphone_gtk_get_core()==NULL) return;
252 if (config_loaded==FALSE){
253 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
254 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
255 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
259 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
261 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
263 GdkPixbuf *pbuf=create_pixbuf(icon_path);
264 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
265 g_object_unref(G_OBJECT(pbuf));
269 static int get_ui_file(const char *name, char *path, int pathsize){
270 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
271 if (access(path,F_OK)!=0){
272 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
273 if (access(path,F_OK)!=0){
274 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
275 INSTALLED_XML_DIR,name);
282 GtkWidget *linphone_gtk_create_window(const char *window_name){
283 GError* error = NULL;
284 GtkBuilder* builder = gtk_builder_new ();
288 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
290 if (!gtk_builder_add_from_file (builder, path, &error)){
291 g_error("Couldn't load builder file: %s", error->message);
292 g_error_free (error);
295 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
297 g_error("Could not retrieve '%s' window from xml file",window_name);
300 g_object_set_data(G_OBJECT(w),"builder",builder);
301 gtk_builder_connect_signals(builder,w);
302 linphone_gtk_configure_window(w,window_name);
306 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
309 GtkBuilder* builder = gtk_builder_new ();
311 gchar *object_ids[2];
312 object_ids[0]=g_strdup(widget_name);
315 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
316 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
317 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
318 g_error_free (error);
319 g_free(object_ids[0]);
322 g_free(object_ids[0]);
323 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
325 g_error("Could not retrieve '%s' window from xml file",widget_name);
328 g_object_set_data(G_OBJECT(w),"builder",builder);
329 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
330 gtk_builder_connect_signals(builder,w);
334 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
335 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
338 g_error("Fail to retrieve builder from window !");
341 w=gtk_builder_get_object(builder,name);
343 g_error("No widget named %s found in xml interface.",name);
345 return GTK_WIDGET(w);
349 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
351 GtkWidget *main_window=linphone_gtk_get_main_window();
353 gtk_widget_show(main_window);
354 if (type==GTK_MESSAGE_QUESTION)
356 /* draw a question box. link to dialog_click callback */
357 dialog = gtk_message_dialog_new (
358 GTK_WINDOW(main_window),
359 GTK_DIALOG_DESTROY_WITH_PARENT,
360 GTK_MESSAGE_QUESTION,
363 (const gchar*)message);
364 /* connect to some callback : REVISIT */
366 g_signal_connect_swapped (G_OBJECT (dialog), "response",
367 G_CALLBACK (dialog_click),
370 /* actually show the box */
371 gtk_widget_show(dialog);
375 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
376 GTK_DIALOG_DESTROY_WITH_PARENT,
380 (const gchar*)message);
381 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
382 g_signal_connect_swapped (G_OBJECT (dialog), "response",
383 G_CALLBACK (gtk_widget_destroy),
385 gtk_widget_show(dialog);
389 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
390 if (id==GTK_RESPONSE_CANCEL){
391 gtk_widget_destroy(GTK_WIDGET(dialog));
395 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
396 g_message("About url clicked");
397 linphone_gtk_open_browser(url);
400 void linphone_gtk_show_about(){
401 struct stat filestat;
402 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
405 GdkPixbuf *logo=create_pixbuf(
406 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
407 static const char *defcfg="defcfg";
409 about=linphone_gtk_create_window("about");
410 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
411 memset(&filestat,0,sizeof(filestat));
412 if (stat(license_file,&filestat)!=0){
413 license_file="COPYING";
414 stat(license_file,&filestat);
416 if (filestat.st_size>0){
417 char *license=g_malloc(filestat.st_size+1);
418 FILE *f=fopen(license_file,"r");
419 if (f && fread(license,filestat.st_size,1,f)==1){
420 license[filestat.st_size]='\0';
421 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
425 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
426 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
427 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
428 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
429 tmp=linphone_gtk_get_ui_config("artists",defcfg);
434 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
436 tmp=linphone_gtk_get_ui_config("translators",defcfg);
438 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
439 tmp=linphone_gtk_get_ui_config("comments",defcfg);
441 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
442 gtk_widget_show(about);
445 static void set_video_window_decorations(GdkWindow *w){
446 const char *title=linphone_gtk_get_ui_config("title","Linphone");
447 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
448 char video_title[256];
449 GdkPixbuf *pbuf=create_pixbuf(icon_path);
450 if (!linphone_core_in_call(linphone_gtk_get_core())){
451 snprintf(video_title,sizeof(video_title),"%s video",title);
452 /* When not in call, treat the video as a normal window */
453 gdk_window_set_keep_above(w, FALSE);
455 LinphoneAddress *uri =
456 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
459 linphone_address_clean(uri);
460 if (linphone_address_get_display_name(uri)!=NULL){
461 display_name=ms_strdup(linphone_address_get_display_name(uri));
463 display_name=linphone_address_as_string(uri);
465 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
466 linphone_address_destroy(uri);
467 ms_free(display_name);
469 /* During calls, bring up the video window, arrange so that
470 it is above all the other windows */
471 gdk_window_deiconify(w);
472 gdk_window_set_keep_above(w,TRUE);
473 /* Maybe we should have the following, but then we want to
474 have a timer that turns it off after a little while. */
475 /* gdk_window_set_urgency_hint(w,TRUE); */
477 gdk_window_set_title(w,video_title);
478 /* Refrain the video window to be closed at all times. */
479 gdk_window_set_functions(w,
480 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
481 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
484 l=g_list_append(l,pbuf);
485 gdk_window_set_icon_list(w,l);
487 g_object_unref(G_OBJECT(pbuf));
491 static gboolean video_needs_update=FALSE;
493 static void update_video_title(){
494 video_needs_update=TRUE;
497 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
498 static gboolean first_time=TRUE;
500 static unsigned long previd=0;
501 static unsigned long preview_previd=0;
502 static gboolean in_iterate=FALSE;
505 if (in_iterate) return TRUE;
507 linphone_core_iterate(lc);
509 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
510 linphone_gtk_show_directory_search();
514 id=linphone_core_get_native_video_window_id(lc);
515 if (id!=previd || video_needs_update){
519 ms_message("Updating window decorations");
521 w=gdk_window_foreign_new(id);
523 w=gdk_window_foreign_new((HANDLE)id);
526 set_video_window_decorations(w);
527 g_object_unref(G_OBJECT(w));
529 else ms_error("gdk_window_foreign_new() failed");
530 if (video_needs_update) video_needs_update=FALSE;
533 id=linphone_core_get_native_preview_window_id (lc);
534 if (id!=preview_previd ){
538 ms_message("Updating window decorations for preview");
540 w=gdk_window_foreign_new(id);
542 w=gdk_window_foreign_new((HANDLE)id);
545 set_video_window_decorations(w);
546 g_object_unref(G_OBJECT(w));
548 else ms_error("gdk_window_foreign_new() failed");
549 if (video_needs_update) video_needs_update=FALSE;
552 if (addr_to_call!=NULL){
553 /*make sure we are not showing the login screen*/
554 GtkWidget *mw=linphone_gtk_get_main_window();
555 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
556 if (!GTK_WIDGET_VISIBLE(login_frame)){
557 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
558 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
560 linphone_gtk_start_call(uri_bar);
567 static void load_uri_history(){
568 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
571 GtkEntryCompletion *gep=gtk_entry_completion_new();
572 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
575 snprintf(key,sizeof(key),"uri%i",i);
576 uri=linphone_gtk_get_ui_config(key,NULL);
579 gtk_list_store_append(model,&iter);
580 gtk_list_store_set(model,&iter,0,uri,-1);
581 if (i==0) gtk_entry_set_text(uribar,uri);
585 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
586 gtk_entry_completion_set_text_column(gep,0);
587 gtk_entry_set_completion(uribar,gep);
590 static void save_uri_history(){
591 LinphoneCore *lc=linphone_gtk_get_core();
592 LpConfig *cfg=linphone_core_get_config(lc);
593 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
598 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
600 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
602 gtk_tree_model_get(model,&iter,0,&uri,-1);
604 snprintf(key,sizeof(key),"uri%i",i);
605 lp_config_set_string(cfg,"GtkUi",key,uri);
610 }while(gtk_tree_model_iter_next(model,&iter));
614 static void completion_add_text(GtkEntry *entry, const char *text){
616 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
618 if (gtk_tree_model_get_iter_first(model,&iter)){
621 gtk_tree_model_get(model,&iter,0,&uri,-1);
623 if (strcmp(uri,text)==0) {
625 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
631 }while (gtk_tree_model_iter_next(model,&iter));
633 /* and prepend it on top of the list */
634 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
635 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
640 void linphone_gtk_show_main_window(){
641 GtkWidget *w=linphone_gtk_get_main_window();
642 LinphoneCore *lc=linphone_gtk_get_core();
643 if (linphone_core_video_enabled(lc)){
644 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
645 VIDEOSELFVIEW_DEFAULT));
648 gtk_window_present(GTK_WINDOW(w));
651 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
652 GtkWidget *mw=linphone_gtk_get_main_window();
653 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
654 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
655 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
657 if (linphone_gtk_use_in_call_view() && call)
658 linphone_gtk_in_call_view_terminate(call,error);
659 update_video_title();
662 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
663 LinphoneCore *lc=linphone_gtk_get_core();
664 GtkWidget *mw=linphone_gtk_get_main_window();
665 const MSList *calls=linphone_core_get_calls(lc);
667 bool_t start_active=TRUE;
668 bool_t stop_active=FALSE;
669 bool_t add_call=FALSE;
670 int call_list_size=ms_list_size(calls);
680 button=linphone_gtk_get_widget(mw,"start_call");
681 gtk_widget_set_sensitive(button,start_active);
682 gtk_widget_set_visible(button,!add_call);
684 button=linphone_gtk_get_widget(mw,"add_call");
685 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
686 gtk_widget_set_sensitive(button,FALSE);
688 gtk_widget_set_sensitive(button,start_active);
690 gtk_widget_set_visible(button,add_call);
692 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
694 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
695 linphone_gtk_enable_conference_button(lc,call_list_size>1);
696 update_video_title();
699 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
700 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
701 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
702 completion_add_text(GTK_ENTRY(uri_bar),entered);
704 linphone_gtk_call_terminated(NULL,NULL);
709 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
710 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
711 linphone_core_accept_call (linphone_gtk_get_core(),call);
712 linphone_call_unref(call);
718 void linphone_gtk_start_call(GtkWidget *w){
719 LinphoneCore *lc=linphone_gtk_get_core();
721 /*change into in-call mode, then do the work later as it might block a bit */
722 GtkWidget *mw=gtk_widget_get_toplevel(w);
723 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
725 call=linphone_gtk_get_currently_displayed_call(NULL);
726 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
727 linphone_core_accept_call(lc,call);
729 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
730 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
731 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
732 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
737 void linphone_gtk_uri_bar_activate(GtkWidget *w){
738 linphone_gtk_start_call(w);
742 void linphone_gtk_terminate_call(GtkWidget *button){
744 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
746 linphone_core_terminate_call(linphone_gtk_get_core(),call);
748 linphone_core_terminate_conference(linphone_gtk_get_core());
752 void linphone_gtk_decline_clicked(GtkWidget *button){
753 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
755 linphone_core_terminate_call(linphone_gtk_get_core(),call);
758 void linphone_gtk_answer_clicked(GtkWidget *button){
759 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
761 linphone_core_accept_call(linphone_gtk_get_core(),call);
762 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
766 void linphone_gtk_enable_video(GtkWidget *w){
767 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
768 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
769 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
770 gtk_widget_set_sensitive(selfview_item,val);
772 linphone_core_enable_video_preview(linphone_gtk_get_core(),
773 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
775 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
779 void linphone_gtk_enable_self_view(GtkWidget *w){
780 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
781 LinphoneCore *lc=linphone_gtk_get_core();
782 linphone_core_enable_video_preview(lc,val);
783 linphone_core_enable_self_view(lc,val);
784 linphone_gtk_set_ui_config_int("videoselfview",val);
787 void linphone_gtk_used_identity_changed(GtkWidget *w){
788 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
789 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
790 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
791 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
792 linphone_gtk_show_directory_search();
794 if (sel) g_free(sel);
797 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
798 linphone_gtk_show_friends();
801 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
803 case GTK_RESPONSE_YES:
804 linphone_gtk_show_contact(lf);
807 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
809 gtk_widget_destroy(dialog);
812 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
815 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
816 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
820 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);
821 dialog = gtk_message_dialog_new (
822 GTK_WINDOW(linphone_gtk_get_main_window()),
823 GTK_DIALOG_DESTROY_WITH_PARENT,
824 GTK_MESSAGE_QUESTION,
829 g_signal_connect(G_OBJECT (dialog), "response",
830 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
831 /* actually show the box */
832 gtk_widget_show(dialog);
835 typedef struct _AuthTimeout{
840 static void auth_timeout_clean(AuthTimeout *tout){
844 static gboolean auth_timeout_destroy(AuthTimeout *tout){
846 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
847 gtk_widget_destroy(tout->w);
853 static AuthTimeout * auth_timeout_new(GtkWidget *w){
854 AuthTimeout *tout=g_new(AuthTimeout,1);
856 /*so that the timeout no more references the widget when it is destroyed:*/
857 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
858 /*so that the widget is automatically destroyed after some time */
859 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
863 void linphone_gtk_password_cancel(GtkWidget *w){
864 LinphoneAuthInfo *info;
865 GtkWidget *window=gtk_widget_get_toplevel(w);
866 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
867 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
868 gtk_widget_destroy(window);
871 void linphone_gtk_password_ok(GtkWidget *w){
873 GtkWidget *window=gtk_widget_get_toplevel(w);
874 LinphoneAuthInfo *info;
875 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
876 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
877 entry=linphone_gtk_get_widget(window,"password_entry");
878 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
879 linphone_auth_info_set_userid(info,
880 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
881 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
882 gtk_widget_destroy(window);
885 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
886 GtkWidget *w=linphone_gtk_create_window("password");
887 GtkWidget *label=linphone_gtk_get_widget(w,"message");
888 LinphoneAuthInfo *info;
890 GtkWidget *mw=linphone_gtk_get_main_window();
892 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
893 /*don't prompt for authentication when login frame is visible*/
894 linphone_core_abort_authentication(lc,NULL);
898 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
900 gtk_label_set_markup(GTK_LABEL(label),msg);
902 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
903 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
904 g_object_set_data(G_OBJECT(w),"auth_info",info);
905 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
910 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
911 GtkWidget *w=linphone_gtk_get_main_window();
912 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
913 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
914 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
918 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
919 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
922 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
923 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
926 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
928 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
929 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
932 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
933 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
934 if (w) linphone_gtk_call_log_update(w);
935 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
939 static bool_t notify_actions_supported() {
940 bool_t accepts_actions = FALSE;
941 GList *capabilities = notify_get_server_caps();
943 if(capabilities != NULL) {
944 for(c = capabilities; c != NULL; c = c->next) {
945 if(strcmp((char*)c->data, "actions") == 0 ) {
946 accepts_actions = TRUE;
950 g_list_foreach(capabilities, (GFunc)g_free, NULL);
951 g_list_free(capabilities);
953 return accepts_actions;
956 static NotifyNotification* build_notification(const char *title, const char *body){
957 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
964 static void show_notification(NotifyNotification* n){
965 if (n && !notify_notification_show(n,NULL))
966 ms_error("Failed to send notification.");
969 static void make_notification(const char *title, const char *body){
970 show_notification(build_notification(title,body));
975 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
977 if (!notify_is_initted())
978 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
982 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
987 ms_error("Failed to send notification.");
989 linphone_gtk_show_main_window();
991 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
994 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
995 NotifyNotification *n;
996 switch(linphone_call_get_state(call)){
997 case LinphoneCallError:
998 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1000 case LinphoneCallEnd:
1001 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1003 case LinphoneCallIncomingReceived:
1004 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1005 if (notify_actions_supported()) {
1006 notify_notification_add_action (n,"answer", _("Answer"),
1007 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1008 notify_notification_add_action (n,"decline",_("Decline"),
1009 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1011 show_notification(n);
1013 case LinphoneCallPausedByRemote:
1014 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1019 if (body) g_free(body);
1020 if (remote) g_free(remote);
1025 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1027 case LinphoneCallOutgoingInit:
1028 linphone_gtk_create_in_call_view (call);
1030 case LinphoneCallOutgoingProgress:
1031 linphone_gtk_in_call_view_set_calling (call);
1033 case LinphoneCallStreamsRunning:
1034 linphone_gtk_in_call_view_set_in_call(call);
1036 case LinphoneCallError:
1037 linphone_gtk_in_call_view_terminate (call,msg);
1039 case LinphoneCallEnd:
1040 linphone_gtk_in_call_view_terminate(call,NULL);
1041 linphone_gtk_status_icon_set_blinking(FALSE);
1043 case LinphoneCallIncomingReceived:
1044 linphone_gtk_create_in_call_view(call);
1045 linphone_gtk_in_call_view_set_incoming(call);
1046 linphone_gtk_status_icon_set_blinking(TRUE);
1048 linphone_call_ref(call);
1049 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1052 case LinphoneCallResuming:
1053 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1054 linphone_gtk_in_call_view_set_in_call (call);
1056 case LinphoneCallPausing:
1057 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1058 case LinphoneCallPausedByRemote:
1059 linphone_gtk_in_call_view_set_paused(call);
1061 case LinphoneCallConnected:
1062 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1063 linphone_gtk_status_icon_set_blinking(FALSE);
1068 linphone_gtk_notify(call, msg);
1069 linphone_gtk_update_call_buttons (call);
1072 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1073 linphone_gtk_in_call_view_show_encryption(call);
1076 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1077 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1078 GtkTreeModel *model=gtk_combo_box_get_model(box);
1080 gboolean found=FALSE;
1081 const char *stock_id=NULL;
1083 if (gtk_tree_model_get_iter_first(model,&iter)){
1086 gtk_tree_model_get(model,&iter,2,&p,-1);
1091 }while(gtk_tree_model_iter_next(model,&iter));
1094 g_warning("Could not find proxy config in combo box of identities.");
1098 case LinphoneRegistrationOk:
1099 stock_id=GTK_STOCK_YES;
1101 case LinphoneRegistrationProgress:
1102 stock_id=GTK_STOCK_REFRESH;
1104 case LinphoneRegistrationCleared:
1107 case LinphoneRegistrationFailed:
1108 stock_id=GTK_STOCK_DIALOG_WARNING;
1113 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1116 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1117 LinphoneRegistrationState rs, const char *msg){
1119 case LinphoneRegistrationOk:
1121 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1122 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1123 linphone_gtk_exit_login_frame();
1130 update_registration_status(cfg,rs);
1133 void linphone_gtk_open_browser(const char *url){
1134 /*in gtk 2.16, gtk_show_uri does not work...*/
1136 #if GTK_CHECK_VERSION(2,18,3)
1137 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1140 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1141 g_spawn_command_line_async(cl,NULL);
1144 ShellExecute(0,"open",url,NULL,NULL,1);
1148 void linphone_gtk_link_to_website(GtkWidget *item){
1149 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1150 linphone_gtk_open_browser(home);
1153 #ifndef HAVE_GTK_OSX
1155 static GtkStatusIcon *icon=NULL;
1157 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1158 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1159 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1162 static GtkWidget *create_icon_menu(){
1163 GtkWidget *menu=gtk_menu_new();
1164 GtkWidget *menu_item;
1167 const gchar *homesite;
1169 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1170 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1171 tmp=g_strdup(homesite);
1172 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1173 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1175 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1176 gtk_widget_show(image);
1177 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1178 //g_object_unref(G_OBJECT(image));
1179 gtk_widget_show(menu_item);
1180 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1181 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1183 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1184 gtk_widget_show(menu_item);
1185 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1186 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1187 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1188 gtk_widget_show(menu_item);
1189 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1190 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1191 gtk_widget_show(menu);
1195 static void handle_icon_click() {
1196 GtkWidget *mw=linphone_gtk_get_main_window();
1197 if (!gtk_window_is_active((GtkWindow*)mw)) {
1198 linphone_gtk_show_main_window();
1200 gtk_widget_hide(mw);
1204 static void linphone_gtk_init_status_icon(){
1205 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1206 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1207 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1208 GtkWidget *menu=create_icon_menu();
1210 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1211 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1212 #if GTK_CHECK_VERSION(2,20,0)
1213 gtk_status_icon_set_name(icon,title);
1215 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1216 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1217 gtk_status_icon_set_tooltip(icon,title);
1218 gtk_status_icon_set_visible(icon,TRUE);
1219 g_object_set_data(G_OBJECT(icon),"menu",menu);
1220 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1221 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1222 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1223 pbuf=create_pixbuf(call_icon_path);
1224 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1227 static gboolean do_icon_blink(GtkStatusIcon *gi){
1228 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1229 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1230 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1231 if (cur_icon==call_icon){
1232 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1234 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1241 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1243 static gint attention_id;
1244 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1246 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1247 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1251 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1252 if (val && tout==0){
1253 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1254 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1255 }else if (!val && tout!=0){
1256 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1257 g_source_remove(tout);
1258 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1259 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1265 void linphone_gtk_options_activate(GtkWidget *item){
1266 #ifndef HAVE_GTK_OSX
1267 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1268 icon && !gtk_status_icon_is_embedded(icon));
1272 static void init_identity_combo(GtkComboBox *box){
1273 GtkListStore *store;
1274 GtkCellRenderer *r1,*r2;
1275 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1276 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1277 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1278 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1279 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1280 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1281 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1282 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1285 void linphone_gtk_load_identities(void){
1287 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1289 LinphoneProxyConfig *def=NULL;
1291 GtkListStore *store;
1294 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1295 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1296 /* model is empty, this is the first time we go here */
1297 init_identity_combo(box);
1298 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1300 gtk_list_store_clear(store);
1301 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1302 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1303 gtk_list_store_append(store,&iter);
1304 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1305 g_free(def_identity);
1306 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1308 elem=ms_list_next(elem),i++){
1309 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1310 gtk_list_store_append(store,&iter);
1311 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1312 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1318 gtk_combo_box_set_active(box,def_index);
1321 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1322 const char *label=gtk_button_get_label(button);
1323 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1325 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1326 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1327 if (linphone_core_in_call(linphone_gtk_get_core())){
1328 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1332 static void linphone_gtk_dtmf_released(GtkButton *button){
1333 linphone_core_stop_dtmf (linphone_gtk_get_core());
1336 static void linphone_gtk_connect_digits(void){
1337 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1338 GList *children=gtk_container_get_children(cont);
1340 for(elem=children;elem!=NULL;elem=elem->next){
1341 GtkButton *button=GTK_BUTTON(elem->data);
1342 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1343 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1347 static void linphone_gtk_check_menu_items(void){
1348 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1349 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1350 GtkWidget *selfview_item=linphone_gtk_get_widget(
1351 linphone_gtk_get_main_window(),"selfview_item");
1352 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1353 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1354 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1355 gtk_widget_set_sensitive(selfview_item,video_enabled);
1358 static gboolean linphone_gtk_can_manage_accounts(){
1359 LinphoneCore *lc=linphone_gtk_get_core();
1361 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1362 SipSetup *ss=(SipSetup*)elem->data;
1363 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1370 static void linphone_gtk_configure_main_window(){
1371 static gboolean config_loaded=FALSE;
1372 static const char *title;
1373 static const char *home;
1374 static const char *start_call_icon;
1375 static const char *add_call_icon;
1376 static const char *stop_call_icon;
1377 static const char *search_icon;
1378 static gboolean update_check_menu;
1379 static gboolean buttons_have_borders;
1380 static gboolean show_abcd;
1381 GtkWidget *w=linphone_gtk_get_main_window();
1382 if (!config_loaded){
1383 title=linphone_gtk_get_ui_config("title","Linphone");
1384 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1385 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1386 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1387 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1388 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1389 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1390 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1391 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1394 linphone_gtk_configure_window(w,"main_window");
1396 gtk_window_set_title(GTK_WINDOW(w),title);
1398 if (start_call_icon){
1399 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1400 create_pixmap (start_call_icon));
1401 if (!buttons_have_borders)
1402 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1405 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1406 create_pixmap (add_call_icon));
1407 if (!buttons_have_borders)
1408 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1410 if (stop_call_icon){
1411 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1412 create_pixmap (stop_call_icon));
1413 if (!buttons_have_borders)
1414 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1417 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1418 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1419 g_object_unref(G_OBJECT(pbuf));
1423 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1425 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1429 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1431 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1432 g_object_unref(G_OBJECT(pbuf));
1437 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1439 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1442 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1443 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1444 gtk_image_set_from_pixbuf(img,scaled);
1445 g_object_unref(G_OBJECT(scaled));
1446 g_object_unref(G_OBJECT(pbuf));
1449 if (linphone_gtk_can_manage_accounts()) {
1450 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1452 if (update_check_menu){
1453 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1456 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1457 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1458 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1459 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1460 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1464 void linphone_gtk_manage_login(void){
1465 LinphoneCore *lc=linphone_gtk_get_core();
1466 LinphoneProxyConfig *cfg=NULL;
1467 linphone_core_get_default_proxy(lc,&cfg);
1469 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1470 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1471 linphone_gtk_show_login_frame(cfg);
1477 gboolean linphone_gtk_close(GtkWidget *mw){
1478 /*shutdown calls if any*/
1479 LinphoneCore *lc=linphone_gtk_get_core();
1480 if (linphone_core_in_call(lc)){
1481 linphone_core_terminate_all_calls(lc);
1483 linphone_core_enable_video_preview(lc,FALSE);
1484 #ifdef __APPLE__ /*until with have a better option*/
1485 gtk_window_iconify(GTK_WINDOW(mw));
1487 gtk_widget_hide(mw);
1493 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1494 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1495 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1497 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1498 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1505 static void linphone_gtk_init_main_window(){
1506 GtkWidget *main_window;
1508 linphone_gtk_configure_main_window();
1509 linphone_gtk_manage_login();
1511 linphone_gtk_load_identities();
1512 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1513 linphone_gtk_show_friends();
1514 linphone_gtk_connect_digits();
1515 main_window=linphone_gtk_get_main_window();
1516 linphone_gtk_call_log_update(main_window);
1518 linphone_gtk_update_call_buttons (NULL);
1519 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1520 g_signal_connect (G_OBJECT (main_window), "delete-event",
1521 G_CALLBACK (linphone_gtk_close), main_window);
1524 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1525 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1526 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1527 gtk_widget_hide(menubar);
1528 gtk_osxapplication_ready(theMacApp);
1530 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1532 linphone_gtk_check_menu_items();
1536 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1538 const char *lname="undef";
1540 #if defined(__linux) || defined(__APPLE__)
1541 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1560 g_error("Bad level !");
1562 #if defined(__linux) || defined(__APPLE__)
1564 msg=g_strdup_vprintf(fmt,cap);
1567 msg=g_strdup_vprintf(fmt,args);
1569 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1572 linphone_gtk_log_push(lev,fmt,args);
1576 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1577 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1578 linphone_gtk_get_main_window(), "uribar"));
1580 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1582 gtk_entry_set_text(uri_bar, refer_to);
1583 linphone_gtk_start_call(linphone_gtk_get_main_window());
1586 static void linphone_gtk_check_soundcards(){
1587 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1588 if (devices==NULL || devices[0]==NULL){
1589 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1590 _("No sound cards have been detected on this computer.\n"
1591 "You won't be able to send or receive audio calls."));
1596 // Display the account wizard
1597 void linphone_gtk_display_wizard() {
1598 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1599 the_wizard = linphone_gtk_create_assistant();
1604 static void linphone_gtk_quit(void){
1605 static gboolean quit_done=FALSE;
1608 g_source_remove_by_user_data(linphone_gtk_get_core());
1609 linphone_gtk_uninit_instance();
1610 linphone_gtk_destroy_log_window();
1611 linphone_core_destroy(the_core);
1612 linphone_gtk_log_uninit();
1616 gdk_threads_leave();
1622 This is not the correct way to implement block termination.
1623 The good way would be to call gtk_main_quit(), and return TRUE.
1624 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1625 As a result the program cannot exit at all.
1626 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1628 static gboolean on_block_termination(void){
1630 linphone_gtk_quit();
1635 int main(int argc, char *argv[]){
1640 const char *factory_config_file;
1642 GtkSettings *settings;
1644 const char *app_name="Linphone";
1646 #if !GLIB_CHECK_VERSION(2, 31, 0)
1647 g_thread_init(NULL);
1651 progpath = strdup(argv[0]);
1653 config_file=linphone_gtk_get_config_file(NULL);
1657 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1658 if ((lang=getenv("LANG"))!=NULL){
1661 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1667 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1670 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1673 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1676 setenv("LANG",lang,1);
1681 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1682 if (p==NULL) perror("bindtextdomain failed");
1683 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1684 textdomain (GETTEXT_PACKAGE);
1686 g_message("NLS disabled.\n");
1689 gtk_rc_add_default_file("./gtkrc");
1691 gdk_threads_enter();
1693 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1694 linphone_options,NULL,NULL)){
1695 gdk_threads_leave();
1699 settings=gtk_settings_get_default();
1700 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1701 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1702 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1703 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1705 if (workingdir!=NULL){
1706 if (chdir(workingdir)==-1){
1707 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1711 /* Now, look for the factory configuration file, we do it this late
1712 since we want to have had time to change directory and to parse
1713 the options, in case we needed to access the working directory */
1714 factory_config_file = linphone_gtk_get_factory_config_file();
1716 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1717 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1718 g_warning("This instance is going to exit now.");
1719 gdk_threads_leave();
1723 add_pixmap_directory("pixmaps");
1724 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1727 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1728 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1729 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1730 /*never block termination:*/
1731 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1734 the_ui=linphone_gtk_create_window("main");
1736 linphone_gtk_create_log_window();
1737 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1739 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1741 g_set_application_name(app_name);
1742 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1743 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1745 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1746 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1747 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1748 linphone_gtk_init_main_window();
1751 // Veryfing if at least one sip account is configured. If not, show wizard
1752 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1753 linphone_gtk_display_wizard();
1757 #ifndef HAVE_GTK_OSX
1758 linphone_gtk_init_status_icon();
1761 linphone_gtk_show_main_window();
1762 linphone_gtk_check_soundcards();
1764 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1765 linphone_gtk_check_for_new_version();
1768 linphone_gtk_quit();
1769 #ifndef HAVE_GTK_OSX
1770 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1771 gtk_status_icon_set_visible(icon,FALSE);