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 void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
67 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
68 static void linphone_gtk_status_icon_set_blinking(gboolean val);
71 static gboolean verbose=0;
72 static gboolean auto_answer = 0;
73 static gchar * addr_to_call = NULL;
74 static gboolean iconified=FALSE;
75 static gchar *workingdir=NULL;
76 static char *progpath=NULL;
77 gchar *linphone_logfile=NULL;
79 static GOptionEntry linphone_options[]={
83 .arg=G_OPTION_ARG_NONE,
84 .arg_data= (gpointer)&verbose,
85 .description=N_("log to stdout some debug information while running.")
88 .long_name = "logfile",
90 .arg = G_OPTION_ARG_STRING,
91 .arg_data = &linphone_logfile,
92 .description = N_("path to a file to write logs into.")
95 .long_name="iconified",
97 .arg=G_OPTION_ARG_NONE,
98 .arg_data= (gpointer)&iconified,
99 .description=N_("Start only in the system tray, do not show the main interface.")
104 .arg = G_OPTION_ARG_STRING,
105 .arg_data = &addr_to_call,
106 .description = N_("address to call right now")
109 .long_name = "auto-answer",
111 .arg = G_OPTION_ARG_NONE,
112 .arg_data = (gpointer) & auto_answer,
113 .description = N_("if set automatically answer incoming calls")
116 .long_name = "workdir",
118 .arg = G_OPTION_ARG_STRING,
119 .arg_data = (gpointer) & workingdir,
120 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
125 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
126 #define RELATIVE_XML_DIR
127 #define BUILD_TREE_XML_DIR "gtk"
130 #define CONFIG_FILE ".linphonerc"
131 #define SECRETS_FILE ".linphone-zidcache"
133 #define CONFIG_FILE "linphonerc"
134 #define SECRETS_FILE "linphone-zidcache"
138 char *linphone_gtk_get_config_file(const char *filename){
139 const int path_max=1024;
140 char *config_file=g_malloc0(path_max);
141 if (filename==NULL) filename=CONFIG_FILE;
142 /*try accessing a local file first if exists*/
143 if (access(CONFIG_FILE,F_OK)==0){
144 snprintf(config_file,path_max,"%s",filename);
147 const char *appdata=getenv("APPDATA");
149 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
150 CreateDirectory(config_file,NULL);
151 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
154 const char *home=getenv("HOME");
155 if (home==NULL) home=".";
156 snprintf(config_file,path_max,"%s/%s",home,filename);
163 #define FACTORY_CONFIG_FILE "linphonerc.factory"
164 static char _factory_config_file[1024];
165 static const char *linphone_gtk_get_factory_config_file(){
166 /*try accessing a local file first if exists*/
167 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
168 snprintf(_factory_config_file,sizeof(_factory_config_file),
169 "%s",FACTORY_CONFIG_FILE);
173 if (progpath != NULL) {
175 progdir = strdup(progpath);
177 basename = strrchr(progdir, '\\');
178 if (basename != NULL) {
181 snprintf(_factory_config_file, sizeof(_factory_config_file),
182 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
184 if (workingdir!=NULL) {
185 snprintf(_factory_config_file, sizeof(_factory_config_file),
186 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
193 basename = strrchr(progdir, '/');
194 if (basename != NULL) {
197 snprintf(_factory_config_file, sizeof(_factory_config_file),
198 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
207 return _factory_config_file;
210 static void linphone_gtk_init_liblinphone(const char *config_file,
211 const char *factory_config_file) {
212 LinphoneCoreVTable vtable={0};
213 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
215 vtable.call_state_changed=linphone_gtk_call_state_changed;
216 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
217 vtable.notify_presence_recv=linphone_gtk_notify_recv;
218 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
219 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
220 vtable.display_status=linphone_gtk_display_status;
221 vtable.display_message=linphone_gtk_display_message;
222 vtable.display_warning=linphone_gtk_display_warning;
223 vtable.display_url=linphone_gtk_display_url;
224 vtable.call_log_updated=linphone_gtk_call_log_updated;
225 vtable.text_received=linphone_gtk_text_received;
226 vtable.refer_received=linphone_gtk_refer_received;
227 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
228 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
229 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
231 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
232 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
233 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
234 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
235 g_free(secrets_file);
236 linphone_core_enable_video(the_core,TRUE,TRUE);
241 LinphoneCore *linphone_gtk_get_core(void){
245 GtkWidget *linphone_gtk_get_main_window(){
249 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
250 static const char *icon_path=NULL;
251 static const char *hiddens=NULL;
252 static const char *shown=NULL;
253 static bool_t config_loaded=FALSE;
254 if (linphone_gtk_get_core()==NULL) return;
255 if (config_loaded==FALSE){
256 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
257 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
258 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
262 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
264 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
266 GdkPixbuf *pbuf=create_pixbuf(icon_path);
267 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
268 g_object_unref(G_OBJECT(pbuf));
272 static int get_ui_file(const char *name, char *path, int pathsize){
273 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
274 if (access(path,F_OK)!=0){
275 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
276 if (access(path,F_OK)!=0){
277 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
278 INSTALLED_XML_DIR,name);
285 GtkWidget *linphone_gtk_create_window(const char *window_name){
286 GError* error = NULL;
287 GtkBuilder* builder = gtk_builder_new ();
291 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
293 if (!gtk_builder_add_from_file (builder, path, &error)){
294 g_error("Couldn't load builder file: %s", error->message);
295 g_error_free (error);
298 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
300 g_error("Could not retrieve '%s' window from xml file",window_name);
303 g_object_set_data(G_OBJECT(w),"builder",builder);
304 gtk_builder_connect_signals(builder,w);
305 linphone_gtk_configure_window(w,window_name);
309 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
312 GtkBuilder* builder = gtk_builder_new ();
314 gchar *object_ids[2];
315 object_ids[0]=g_strdup(widget_name);
318 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
319 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
320 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
321 g_error_free (error);
322 g_free(object_ids[0]);
325 g_free(object_ids[0]);
326 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
328 g_error("Could not retrieve '%s' window from xml file",widget_name);
331 g_object_set_data(G_OBJECT(w),"builder",builder);
332 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
333 gtk_builder_connect_signals(builder,w);
337 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
338 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
341 g_error("Fail to retrieve builder from window !");
344 w=gtk_builder_get_object(builder,name);
346 g_error("No widget named %s found in xml interface.",name);
348 return GTK_WIDGET(w);
352 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
354 GtkWidget *main_window=linphone_gtk_get_main_window();
356 gtk_widget_show(main_window);
357 if (type==GTK_MESSAGE_QUESTION)
359 /* draw a question box. link to dialog_click callback */
360 dialog = gtk_message_dialog_new (
361 GTK_WINDOW(main_window),
362 GTK_DIALOG_DESTROY_WITH_PARENT,
363 GTK_MESSAGE_QUESTION,
366 (const gchar*)message);
367 /* connect to some callback : REVISIT */
369 g_signal_connect_swapped (G_OBJECT (dialog), "response",
370 G_CALLBACK (dialog_click),
373 /* actually show the box */
374 gtk_widget_show(dialog);
378 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
379 GTK_DIALOG_DESTROY_WITH_PARENT,
383 (const gchar*)message);
384 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
385 g_signal_connect_swapped (G_OBJECT (dialog), "response",
386 G_CALLBACK (gtk_widget_destroy),
388 gtk_widget_show(dialog);
392 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
393 if (id==GTK_RESPONSE_CANCEL){
394 gtk_widget_destroy(GTK_WIDGET(dialog));
398 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
399 g_message("About url clicked");
400 linphone_gtk_open_browser(url);
403 void linphone_gtk_show_about(){
404 struct stat filestat;
405 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
408 GdkPixbuf *logo=create_pixbuf(
409 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
410 static const char *defcfg="defcfg";
412 about=linphone_gtk_create_window("about");
413 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
414 memset(&filestat,0,sizeof(filestat));
415 if (stat(license_file,&filestat)!=0){
416 license_file="COPYING";
417 stat(license_file,&filestat);
419 if (filestat.st_size>0){
420 char *license=g_malloc(filestat.st_size+1);
421 FILE *f=fopen(license_file,"r");
422 if (f && fread(license,filestat.st_size,1,f)==1){
423 license[filestat.st_size]='\0';
424 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
428 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
429 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
430 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
431 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
432 tmp=linphone_gtk_get_ui_config("artists",defcfg);
437 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
439 tmp=linphone_gtk_get_ui_config("translators",defcfg);
441 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
442 tmp=linphone_gtk_get_ui_config("comments",defcfg);
444 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
445 gtk_widget_show(about);
448 static void set_video_window_decorations(GdkWindow *w){
449 const char *title=linphone_gtk_get_ui_config("title","Linphone");
450 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
451 char video_title[256];
452 GdkPixbuf *pbuf=create_pixbuf(icon_path);
453 if (!linphone_core_in_call(linphone_gtk_get_core())){
454 snprintf(video_title,sizeof(video_title),"%s video",title);
455 /* When not in call, treat the video as a normal window */
456 gdk_window_set_keep_above(w, FALSE);
458 LinphoneAddress *uri =
459 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
462 linphone_address_clean(uri);
463 if (linphone_address_get_display_name(uri)!=NULL){
464 display_name=ms_strdup(linphone_address_get_display_name(uri));
466 display_name=linphone_address_as_string(uri);
468 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
469 linphone_address_destroy(uri);
470 ms_free(display_name);
472 /* During calls, bring up the video window, arrange so that
473 it is above all the other windows */
474 gdk_window_deiconify(w);
475 gdk_window_set_keep_above(w,TRUE);
476 /* Maybe we should have the following, but then we want to
477 have a timer that turns it off after a little while. */
478 /* gdk_window_set_urgency_hint(w,TRUE); */
480 gdk_window_set_title(w,video_title);
481 /* Refrain the video window to be closed at all times. */
482 gdk_window_set_functions(w,
483 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
484 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
487 l=g_list_append(l,pbuf);
488 gdk_window_set_icon_list(w,l);
490 g_object_unref(G_OBJECT(pbuf));
494 static gboolean video_needs_update=FALSE;
496 static void update_video_title(){
497 video_needs_update=TRUE;
500 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
501 static gboolean first_time=TRUE;
503 static unsigned long previd=0;
504 static unsigned long preview_previd=0;
505 static gboolean in_iterate=FALSE;
508 if (in_iterate) return TRUE;
510 linphone_core_iterate(lc);
512 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
513 linphone_gtk_show_directory_search();
517 id=linphone_core_get_native_video_window_id(lc);
518 if (id!=previd || video_needs_update){
522 ms_message("Updating window decorations");
524 w=gdk_window_foreign_new(id);
526 w=gdk_window_foreign_new((HANDLE)id);
529 set_video_window_decorations(w);
530 g_object_unref(G_OBJECT(w));
532 else ms_error("gdk_window_foreign_new() failed");
533 if (video_needs_update) video_needs_update=FALSE;
536 id=linphone_core_get_native_preview_window_id (lc);
537 if (id!=preview_previd ){
541 ms_message("Updating window decorations for preview");
543 w=gdk_window_foreign_new(id);
545 w=gdk_window_foreign_new((HANDLE)id);
548 set_video_window_decorations(w);
549 g_object_unref(G_OBJECT(w));
551 else ms_error("gdk_window_foreign_new() failed");
552 if (video_needs_update) video_needs_update=FALSE;
555 if (addr_to_call!=NULL){
556 /*make sure we are not showing the login screen*/
557 GtkWidget *mw=linphone_gtk_get_main_window();
558 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
559 if (!GTK_WIDGET_VISIBLE(login_frame)){
560 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
561 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
563 linphone_gtk_start_call(uri_bar);
570 static void load_uri_history(){
571 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
574 GtkEntryCompletion *gep=gtk_entry_completion_new();
575 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
578 snprintf(key,sizeof(key),"uri%i",i);
579 uri=linphone_gtk_get_ui_config(key,NULL);
582 gtk_list_store_append(model,&iter);
583 gtk_list_store_set(model,&iter,0,uri,-1);
584 if (i==0) gtk_entry_set_text(uribar,uri);
588 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
589 gtk_entry_completion_set_text_column(gep,0);
590 gtk_entry_set_completion(uribar,gep);
593 static void save_uri_history(){
594 LinphoneCore *lc=linphone_gtk_get_core();
595 LpConfig *cfg=linphone_core_get_config(lc);
596 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
601 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
603 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
605 gtk_tree_model_get(model,&iter,0,&uri,-1);
607 snprintf(key,sizeof(key),"uri%i",i);
608 lp_config_set_string(cfg,"GtkUi",key,uri);
613 }while(gtk_tree_model_iter_next(model,&iter));
617 static void completion_add_text(GtkEntry *entry, const char *text){
619 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
621 if (gtk_tree_model_get_iter_first(model,&iter)){
624 gtk_tree_model_get(model,&iter,0,&uri,-1);
626 if (strcmp(uri,text)==0) {
628 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
634 }while (gtk_tree_model_iter_next(model,&iter));
636 /* and prepend it on top of the list */
637 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
638 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
643 bool_t linphone_gtk_video_enabled(void){
644 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
645 return vpol->automatically_accept && vpol->automatically_initiate;
648 void linphone_gtk_show_main_window(){
649 GtkWidget *w=linphone_gtk_get_main_window();
650 LinphoneCore *lc=linphone_gtk_get_core();
651 if (linphone_gtk_video_enabled()){
652 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
653 VIDEOSELFVIEW_DEFAULT));
656 gtk_window_present(GTK_WINDOW(w));
659 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
660 GtkWidget *mw=linphone_gtk_get_main_window();
661 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
662 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
663 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
665 if (linphone_gtk_use_in_call_view() && call)
666 linphone_gtk_in_call_view_terminate(call,error);
667 update_video_title();
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);
688 button=linphone_gtk_get_widget(mw,"start_call");
689 gtk_widget_set_sensitive(button,start_active);
690 gtk_widget_set_visible(button,!add_call);
692 button=linphone_gtk_get_widget(mw,"add_call");
693 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
694 gtk_widget_set_sensitive(button,FALSE);
696 gtk_widget_set_sensitive(button,start_active);
698 gtk_widget_set_visible(button,add_call);
700 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
702 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
703 linphone_gtk_enable_conference_button(lc,call_list_size>1);
704 update_video_title();
705 if (call) linphone_gtk_update_video_button(call);
708 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
709 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
710 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
711 completion_add_text(GTK_ENTRY(uri_bar),entered);
713 linphone_gtk_call_terminated(NULL,NULL);
718 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
719 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
720 linphone_core_accept_call (linphone_gtk_get_core(),call);
721 linphone_call_unref(call);
727 void linphone_gtk_start_call(GtkWidget *w){
728 LinphoneCore *lc=linphone_gtk_get_core();
730 /*change into in-call mode, then do the work later as it might block a bit */
731 GtkWidget *mw=gtk_widget_get_toplevel(w);
732 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
734 call=linphone_gtk_get_currently_displayed_call(NULL);
735 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
736 linphone_core_accept_call(lc,call);
738 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
739 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
740 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
741 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
746 void linphone_gtk_uri_bar_activate(GtkWidget *w){
747 linphone_gtk_start_call(w);
751 void linphone_gtk_terminate_call(GtkWidget *button){
753 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
755 linphone_core_terminate_call(linphone_gtk_get_core(),call);
757 linphone_core_terminate_conference(linphone_gtk_get_core());
761 void linphone_gtk_decline_clicked(GtkWidget *button){
762 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
764 linphone_core_terminate_call(linphone_gtk_get_core(),call);
767 void linphone_gtk_answer_clicked(GtkWidget *button){
768 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
770 linphone_core_accept_call(linphone_gtk_get_core(),call);
771 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
775 void linphone_gtk_enable_video(GtkWidget *w){
776 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
777 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
778 LinphoneVideoPolicy policy={0};
779 policy.automatically_initiate=policy.automatically_accept=val;
780 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
781 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
783 gtk_widget_set_sensitive(selfview_item,val);
785 linphone_core_enable_video_preview(linphone_gtk_get_core(),
786 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
788 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
792 void linphone_gtk_enable_self_view(GtkWidget *w){
793 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
794 LinphoneCore *lc=linphone_gtk_get_core();
795 linphone_core_enable_video_preview(lc,val);
796 linphone_core_enable_self_view(lc,val);
797 linphone_gtk_set_ui_config_int("videoselfview",val);
800 void linphone_gtk_used_identity_changed(GtkWidget *w){
801 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
802 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
803 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
804 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
805 linphone_gtk_show_directory_search();
807 if (sel) g_free(sel);
810 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
811 linphone_gtk_show_friends();
814 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
816 case GTK_RESPONSE_YES:
817 linphone_gtk_show_contact(lf);
820 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
822 gtk_widget_destroy(dialog);
825 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
828 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
829 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
833 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);
834 dialog = gtk_message_dialog_new (
835 GTK_WINDOW(linphone_gtk_get_main_window()),
836 GTK_DIALOG_DESTROY_WITH_PARENT,
837 GTK_MESSAGE_QUESTION,
842 g_signal_connect(G_OBJECT (dialog), "response",
843 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
844 /* actually show the box */
845 gtk_widget_show(dialog);
848 typedef struct _AuthTimeout{
853 static void auth_timeout_clean(AuthTimeout *tout){
857 static gboolean auth_timeout_destroy(AuthTimeout *tout){
859 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
860 gtk_widget_destroy(tout->w);
866 static AuthTimeout * auth_timeout_new(GtkWidget *w){
867 AuthTimeout *tout=g_new(AuthTimeout,1);
869 /*so that the timeout no more references the widget when it is destroyed:*/
870 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
871 /*so that the widget is automatically destroyed after some time */
872 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
876 void linphone_gtk_password_cancel(GtkWidget *w){
877 LinphoneAuthInfo *info;
878 GtkWidget *window=gtk_widget_get_toplevel(w);
879 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
880 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
881 gtk_widget_destroy(window);
884 void linphone_gtk_password_ok(GtkWidget *w){
886 GtkWidget *window=gtk_widget_get_toplevel(w);
887 LinphoneAuthInfo *info;
888 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
889 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
890 entry=linphone_gtk_get_widget(window,"password_entry");
891 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
892 linphone_auth_info_set_userid(info,
893 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
894 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
895 gtk_widget_destroy(window);
898 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
899 GtkWidget *w=linphone_gtk_create_window("password");
900 GtkWidget *label=linphone_gtk_get_widget(w,"message");
901 LinphoneAuthInfo *info;
903 GtkWidget *mw=linphone_gtk_get_main_window();
905 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
906 /*don't prompt for authentication when login frame is visible*/
907 linphone_core_abort_authentication(lc,NULL);
911 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
913 gtk_label_set_markup(GTK_LABEL(label),msg);
915 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
916 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
917 g_object_set_data(G_OBJECT(w),"auth_info",info);
918 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
923 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
924 GtkWidget *w=linphone_gtk_get_main_window();
925 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
926 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
927 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
931 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
932 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
935 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
936 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
939 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
941 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
942 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
945 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
946 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
947 if (w) linphone_gtk_call_log_update(w);
948 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
952 static bool_t notify_actions_supported() {
953 bool_t accepts_actions = FALSE;
954 GList *capabilities = notify_get_server_caps();
956 if(capabilities != NULL) {
957 for(c = capabilities; c != NULL; c = c->next) {
958 if(strcmp((char*)c->data, "actions") == 0 ) {
959 accepts_actions = TRUE;
963 g_list_foreach(capabilities, (GFunc)g_free, NULL);
964 g_list_free(capabilities);
966 return accepts_actions;
969 static NotifyNotification* build_notification(const char *title, const char *body){
970 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
977 static void show_notification(NotifyNotification* n){
978 if (n && !notify_notification_show(n,NULL))
979 ms_error("Failed to send notification.");
982 static void make_notification(const char *title, const char *body){
983 show_notification(build_notification(title,body));
988 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
990 if (!notify_is_initted())
991 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
995 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1000 ms_error("Failed to send notification.");
1002 linphone_gtk_show_main_window();
1004 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1007 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1008 NotifyNotification *n;
1009 switch(linphone_call_get_state(call)){
1010 case LinphoneCallError:
1011 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1013 case LinphoneCallEnd:
1014 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1016 case LinphoneCallIncomingReceived:
1017 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1018 if (notify_actions_supported()) {
1019 notify_notification_add_action (n,"answer", _("Answer"),
1020 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1021 notify_notification_add_action (n,"decline",_("Decline"),
1022 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1024 show_notification(n);
1026 case LinphoneCallPausedByRemote:
1027 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1032 if (body) g_free(body);
1033 if (remote) g_free(remote);
1038 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1039 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1040 LinphoneCore *lc=linphone_call_get_core(call);
1041 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1042 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1043 linphone_core_accept_call_update(lc,call,params);
1044 linphone_call_params_destroy(params);
1046 linphone_call_unref(call);
1047 g_source_remove_by_user_data(dialog);
1048 gtk_widget_destroy(dialog);
1051 static void on_call_updated_timeout(GtkWidget *dialog){
1052 gtk_widget_destroy(dialog);
1055 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1056 LinphoneCore *lc=linphone_call_get_core(call);
1057 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1058 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1059 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1060 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1061 gboolean video_used=linphone_call_params_video_enabled(current_params);
1062 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1063 video_used,video_requested,pol->automatically_accept);
1064 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1065 linphone_core_defer_call_update(lc,call);
1067 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1069 const char *dname=linphone_address_get_display_name(addr);
1070 if (dname==NULL) dname=linphone_address_get_username(addr);
1071 if (dname==NULL) dname=linphone_address_get_domain(addr);
1072 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1073 GTK_DIALOG_DESTROY_WITH_PARENT,
1074 GTK_MESSAGE_WARNING,
1076 _("%s proposed to start video. Do you accept ?"),dname);
1077 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1078 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1079 gtk_widget_show(dialog);
1084 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1086 case LinphoneCallOutgoingInit:
1087 linphone_gtk_create_in_call_view (call);
1089 case LinphoneCallOutgoingProgress:
1090 linphone_gtk_in_call_view_set_calling (call);
1092 case LinphoneCallStreamsRunning:
1093 linphone_gtk_in_call_view_set_in_call(call);
1095 case LinphoneCallUpdatedByRemote:
1096 linphone_gtk_call_updated_by_remote(call);
1098 case LinphoneCallError:
1099 linphone_gtk_in_call_view_terminate (call,msg);
1101 case LinphoneCallEnd:
1102 linphone_gtk_in_call_view_terminate(call,NULL);
1103 linphone_gtk_status_icon_set_blinking(FALSE);
1105 case LinphoneCallIncomingReceived:
1106 linphone_gtk_create_in_call_view(call);
1107 linphone_gtk_in_call_view_set_incoming(call);
1108 linphone_gtk_status_icon_set_blinking(TRUE);
1110 linphone_call_ref(call);
1111 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1114 case LinphoneCallResuming:
1115 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1116 linphone_gtk_in_call_view_set_in_call (call);
1118 case LinphoneCallPausing:
1119 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1120 case LinphoneCallPausedByRemote:
1121 linphone_gtk_in_call_view_set_paused(call);
1123 case LinphoneCallConnected:
1124 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1125 linphone_gtk_status_icon_set_blinking(FALSE);
1130 linphone_gtk_notify(call, msg);
1131 linphone_gtk_update_call_buttons (call);
1134 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1135 linphone_gtk_in_call_view_show_encryption(call);
1138 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1139 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1142 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1143 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1144 GtkTreeModel *model=gtk_combo_box_get_model(box);
1146 gboolean found=FALSE;
1147 const char *stock_id=NULL;
1149 if (gtk_tree_model_get_iter_first(model,&iter)){
1152 gtk_tree_model_get(model,&iter,2,&p,-1);
1157 }while(gtk_tree_model_iter_next(model,&iter));
1160 g_warning("Could not find proxy config in combo box of identities.");
1164 case LinphoneRegistrationOk:
1165 stock_id=GTK_STOCK_YES;
1167 case LinphoneRegistrationProgress:
1168 stock_id=GTK_STOCK_REFRESH;
1170 case LinphoneRegistrationCleared:
1173 case LinphoneRegistrationFailed:
1174 stock_id=GTK_STOCK_DIALOG_WARNING;
1179 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1182 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1183 LinphoneRegistrationState rs, const char *msg){
1185 case LinphoneRegistrationOk:
1187 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1188 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1189 linphone_gtk_exit_login_frame();
1196 update_registration_status(cfg,rs);
1199 void linphone_gtk_open_browser(const char *url){
1200 /*in gtk 2.16, gtk_show_uri does not work...*/
1202 #if GTK_CHECK_VERSION(2,18,3)
1203 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1206 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1207 g_spawn_command_line_async(cl,NULL);
1210 ShellExecute(0,"open",url,NULL,NULL,1);
1214 void linphone_gtk_link_to_website(GtkWidget *item){
1215 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1216 linphone_gtk_open_browser(home);
1219 #ifndef HAVE_GTK_OSX
1221 static GtkStatusIcon *icon=NULL;
1223 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1224 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1225 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1228 static GtkWidget *create_icon_menu(){
1229 GtkWidget *menu=gtk_menu_new();
1230 GtkWidget *menu_item;
1233 const gchar *homesite;
1235 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1236 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1237 tmp=g_strdup(homesite);
1238 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1239 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1241 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1242 gtk_widget_show(image);
1243 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1244 //g_object_unref(G_OBJECT(image));
1245 gtk_widget_show(menu_item);
1246 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1247 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1249 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1250 gtk_widget_show(menu_item);
1251 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1252 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1253 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1254 gtk_widget_show(menu_item);
1255 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1256 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1257 gtk_widget_show(menu);
1261 static void handle_icon_click() {
1262 GtkWidget *mw=linphone_gtk_get_main_window();
1263 if (!gtk_window_is_active((GtkWindow*)mw)) {
1264 linphone_gtk_show_main_window();
1266 gtk_widget_hide(mw);
1270 static void linphone_gtk_init_status_icon(){
1271 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1272 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1273 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1274 GtkWidget *menu=create_icon_menu();
1276 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1277 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1278 #if GTK_CHECK_VERSION(2,20,0)
1279 gtk_status_icon_set_name(icon,title);
1281 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1282 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1283 gtk_status_icon_set_tooltip(icon,title);
1284 gtk_status_icon_set_visible(icon,TRUE);
1285 g_object_set_data(G_OBJECT(icon),"menu",menu);
1286 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1287 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1288 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1289 pbuf=create_pixbuf(call_icon_path);
1290 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1293 static gboolean do_icon_blink(GtkStatusIcon *gi){
1294 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1295 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1296 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1297 if (cur_icon==call_icon){
1298 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1300 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1307 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1309 static gint attention_id;
1310 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1312 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1313 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1317 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1318 if (val && tout==0){
1319 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1320 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1321 }else if (!val && tout!=0){
1322 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1323 g_source_remove(tout);
1324 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1325 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1331 void linphone_gtk_options_activate(GtkWidget *item){
1332 #ifndef HAVE_GTK_OSX
1333 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1338 static void init_identity_combo(GtkComboBox *box){
1339 GtkListStore *store;
1340 GtkCellRenderer *r1,*r2;
1341 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1342 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1343 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1344 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1345 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1346 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1347 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1348 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1351 void linphone_gtk_load_identities(void){
1353 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1355 LinphoneProxyConfig *def=NULL;
1357 GtkListStore *store;
1360 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1361 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1362 /* model is empty, this is the first time we go here */
1363 init_identity_combo(box);
1364 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1366 gtk_list_store_clear(store);
1367 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1368 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1369 gtk_list_store_append(store,&iter);
1370 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1371 g_free(def_identity);
1372 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1374 elem=ms_list_next(elem),i++){
1375 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1376 gtk_list_store_append(store,&iter);
1377 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1378 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1384 gtk_combo_box_set_active(box,def_index);
1387 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1388 const char *label=gtk_button_get_label(button);
1389 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1391 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1392 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1393 if (linphone_core_in_call(linphone_gtk_get_core())){
1394 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1398 static void linphone_gtk_dtmf_released(GtkButton *button){
1399 linphone_core_stop_dtmf (linphone_gtk_get_core());
1402 static void linphone_gtk_connect_digits(void){
1403 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1404 GList *children=gtk_container_get_children(cont);
1406 for(elem=children;elem!=NULL;elem=elem->next){
1407 GtkButton *button=GTK_BUTTON(elem->data);
1408 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1409 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1413 static void linphone_gtk_check_menu_items(void){
1414 bool_t video_enabled=linphone_gtk_video_enabled();
1415 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1416 GtkWidget *selfview_item=linphone_gtk_get_widget(
1417 linphone_gtk_get_main_window(),"selfview_item");
1418 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1419 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1420 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1421 gtk_widget_set_sensitive(selfview_item,video_enabled);
1424 static gboolean linphone_gtk_can_manage_accounts(){
1425 LinphoneCore *lc=linphone_gtk_get_core();
1427 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1428 SipSetup *ss=(SipSetup*)elem->data;
1429 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1436 static void linphone_gtk_configure_main_window(){
1437 static gboolean config_loaded=FALSE;
1438 static const char *title;
1439 static const char *home;
1440 static const char *start_call_icon;
1441 static const char *add_call_icon;
1442 static const char *stop_call_icon;
1443 static const char *search_icon;
1444 static gboolean update_check_menu;
1445 static gboolean buttons_have_borders;
1446 static gboolean show_abcd;
1447 GtkWidget *w=linphone_gtk_get_main_window();
1448 if (!config_loaded){
1449 title=linphone_gtk_get_ui_config("title","Linphone");
1450 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1451 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1452 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1453 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1454 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1455 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1456 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1457 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1460 linphone_gtk_configure_window(w,"main_window");
1462 gtk_window_set_title(GTK_WINDOW(w),title);
1464 if (start_call_icon){
1465 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1466 create_pixmap (start_call_icon));
1467 if (!buttons_have_borders)
1468 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1471 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1472 create_pixmap (add_call_icon));
1473 if (!buttons_have_borders)
1474 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1476 if (stop_call_icon){
1477 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1478 create_pixmap (stop_call_icon));
1479 if (!buttons_have_borders)
1480 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1483 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1484 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1485 g_object_unref(G_OBJECT(pbuf));
1489 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1491 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1495 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1497 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1498 g_object_unref(G_OBJECT(pbuf));
1503 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1505 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1508 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1509 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1510 gtk_image_set_from_pixbuf(img,scaled);
1511 g_object_unref(G_OBJECT(scaled));
1512 g_object_unref(G_OBJECT(pbuf));
1515 if (linphone_gtk_can_manage_accounts()) {
1516 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1518 if (update_check_menu){
1519 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1522 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1523 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1524 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1525 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1526 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1530 void linphone_gtk_manage_login(void){
1531 LinphoneCore *lc=linphone_gtk_get_core();
1532 LinphoneProxyConfig *cfg=NULL;
1533 linphone_core_get_default_proxy(lc,&cfg);
1535 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1536 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1537 linphone_gtk_show_login_frame(cfg);
1543 gboolean linphone_gtk_close(GtkWidget *mw){
1544 /*shutdown calls if any*/
1545 LinphoneCore *lc=linphone_gtk_get_core();
1546 if (linphone_core_in_call(lc)){
1547 linphone_core_terminate_all_calls(lc);
1549 linphone_core_enable_video_preview(lc,FALSE);
1550 #ifdef __APPLE__ /*until with have a better option*/
1551 gtk_window_iconify(GTK_WINDOW(mw));
1553 gtk_widget_hide(mw);
1559 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1560 bool_t video_enabled=linphone_gtk_video_enabled();
1561 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1562 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1564 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1565 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1572 static void linphone_gtk_init_main_window(){
1573 GtkWidget *main_window;
1575 linphone_gtk_configure_main_window();
1576 linphone_gtk_manage_login();
1578 linphone_gtk_load_identities();
1579 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1580 linphone_gtk_show_friends();
1581 linphone_gtk_connect_digits();
1582 main_window=linphone_gtk_get_main_window();
1583 linphone_gtk_call_log_update(main_window);
1585 linphone_gtk_update_call_buttons (NULL);
1586 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1587 g_signal_connect (G_OBJECT (main_window), "delete-event",
1588 G_CALLBACK (linphone_gtk_close), main_window);
1591 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1592 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1593 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1594 gtk_widget_hide(menubar);
1595 gtk_osxapplication_ready(theMacApp);
1597 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1599 linphone_gtk_check_menu_items();
1603 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1605 const char *lname="undef";
1607 #if defined(__linux) || defined(__APPLE__)
1608 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1627 g_error("Bad level !");
1629 #if defined(__linux) || defined(__APPLE__)
1631 msg=g_strdup_vprintf(fmt,cap);
1634 msg=g_strdup_vprintf(fmt,args);
1636 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1639 linphone_gtk_log_push(lev,fmt,args);
1643 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1644 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1645 linphone_gtk_get_main_window(), "uribar"));
1647 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1649 gtk_entry_set_text(uri_bar, refer_to);
1650 linphone_gtk_start_call(linphone_gtk_get_main_window());
1653 static void linphone_gtk_check_soundcards(){
1654 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1655 if (devices==NULL || devices[0]==NULL){
1656 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1657 _("No sound cards have been detected on this computer.\n"
1658 "You won't be able to send or receive audio calls."));
1663 // Display the account wizard
1664 void linphone_gtk_display_wizard() {
1665 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1666 the_wizard = linphone_gtk_create_assistant();
1671 static void linphone_gtk_quit(void){
1672 static gboolean quit_done=FALSE;
1675 linphone_gtk_unmonitor_usb();
1676 g_source_remove_by_user_data(linphone_gtk_get_core());
1677 linphone_gtk_uninit_instance();
1678 linphone_gtk_destroy_log_window();
1679 linphone_core_destroy(the_core);
1680 linphone_gtk_log_uninit();
1684 gdk_threads_leave();
1690 This is not the correct way to implement block termination.
1691 The good way would be to call gtk_main_quit(), and return TRUE.
1692 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1693 As a result the program cannot exit at all.
1694 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1696 static gboolean on_block_termination(void){
1698 linphone_gtk_quit();
1703 int main(int argc, char *argv[]){
1708 const char *factory_config_file;
1710 GtkSettings *settings;
1712 const char *app_name="Linphone";
1714 #if !GLIB_CHECK_VERSION(2, 31, 0)
1715 g_thread_init(NULL);
1719 progpath = strdup(argv[0]);
1721 config_file=linphone_gtk_get_config_file(NULL);
1725 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1726 if ((lang=getenv("LANG"))!=NULL){
1729 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1735 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1738 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1741 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1744 setenv("LANG",lang,1);
1749 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1750 if (p==NULL) perror("bindtextdomain failed");
1751 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1752 textdomain (GETTEXT_PACKAGE);
1754 g_message("NLS disabled.\n");
1757 gtk_rc_add_default_file("./gtkrc");
1759 gdk_threads_enter();
1761 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1762 linphone_options,NULL,NULL)){
1763 gdk_threads_leave();
1767 settings=gtk_settings_get_default();
1768 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1769 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1770 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1771 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1773 if (workingdir!=NULL){
1774 if (chdir(workingdir)==-1){
1775 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1779 /* Now, look for the factory configuration file, we do it this late
1780 since we want to have had time to change directory and to parse
1781 the options, in case we needed to access the working directory */
1782 factory_config_file = linphone_gtk_get_factory_config_file();
1784 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1785 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1786 g_warning("This instance is going to exit now.");
1787 gdk_threads_leave();
1791 add_pixmap_directory("pixmaps");
1792 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1795 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1796 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1797 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1798 /*never block termination:*/
1799 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1802 the_ui=linphone_gtk_create_window("main");
1804 linphone_gtk_create_log_window();
1805 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1807 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1809 g_set_application_name(app_name);
1810 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1811 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1813 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1814 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1815 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1816 linphone_gtk_init_main_window();
1819 // Veryfing if at least one sip account is configured. If not, show wizard
1820 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1821 linphone_gtk_display_wizard();
1825 #ifndef HAVE_GTK_OSX
1826 linphone_gtk_init_status_icon();
1829 linphone_gtk_show_main_window();
1830 linphone_gtk_check_soundcards();
1832 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1833 linphone_gtk_check_for_new_version();
1834 linphone_gtk_monitor_usb();
1837 linphone_gtk_quit();
1838 #ifndef HAVE_GTK_OSX
1839 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1840 gtk_status_icon_set_visible(icon,FALSE);