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);
784 linphone_core_enable_video_preview(linphone_gtk_get_core(),
785 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
787 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
791 void linphone_gtk_enable_self_view(GtkWidget *w){
792 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
793 LinphoneCore *lc=linphone_gtk_get_core();
794 linphone_core_enable_video_preview(lc,val);
795 linphone_core_enable_self_view(lc,val);
796 linphone_gtk_set_ui_config_int("videoselfview",val);
799 void linphone_gtk_used_identity_changed(GtkWidget *w){
800 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
801 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
802 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
803 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
804 linphone_gtk_show_directory_search();
806 if (sel) g_free(sel);
810 void on_proxy_refresh_button_clicked(GtkWidget *w){
811 LinphoneCore *lc=linphone_gtk_get_core();
812 MSList const *item=linphone_core_get_proxy_config_list(lc);
813 while (item != NULL) {
814 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
815 linphone_proxy_config_edit(lpc);
816 linphone_proxy_config_done(lpc);
821 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
822 linphone_gtk_show_friends();
825 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
827 case GTK_RESPONSE_YES:
828 linphone_gtk_show_contact(lf);
831 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
833 gtk_widget_destroy(dialog);
836 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
839 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
840 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
844 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);
845 dialog = gtk_message_dialog_new (
846 GTK_WINDOW(linphone_gtk_get_main_window()),
847 GTK_DIALOG_DESTROY_WITH_PARENT,
848 GTK_MESSAGE_QUESTION,
853 g_signal_connect(G_OBJECT (dialog), "response",
854 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
855 /* actually show the box */
856 gtk_widget_show(dialog);
859 typedef struct _AuthTimeout{
864 static void auth_timeout_clean(AuthTimeout *tout){
868 static gboolean auth_timeout_destroy(AuthTimeout *tout){
870 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
871 gtk_widget_destroy(tout->w);
877 static AuthTimeout * auth_timeout_new(GtkWidget *w){
878 AuthTimeout *tout=g_new(AuthTimeout,1);
880 /*so that the timeout no more references the widget when it is destroyed:*/
881 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
882 /*so that the widget is automatically destroyed after some time */
883 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
887 void linphone_gtk_password_cancel(GtkWidget *w){
888 LinphoneAuthInfo *info;
889 GtkWidget *window=gtk_widget_get_toplevel(w);
890 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
891 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
892 gtk_widget_destroy(window);
895 void linphone_gtk_password_ok(GtkWidget *w){
897 GtkWidget *window=gtk_widget_get_toplevel(w);
898 LinphoneAuthInfo *info;
899 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
900 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
901 entry=linphone_gtk_get_widget(window,"password_entry");
902 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
903 linphone_auth_info_set_userid(info,
904 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
905 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
906 gtk_widget_destroy(window);
909 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
910 GtkWidget *w=linphone_gtk_create_window("password");
911 GtkWidget *label=linphone_gtk_get_widget(w,"message");
912 LinphoneAuthInfo *info;
914 GtkWidget *mw=linphone_gtk_get_main_window();
916 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
917 /*don't prompt for authentication when login frame is visible*/
918 linphone_core_abort_authentication(lc,NULL);
922 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
924 gtk_label_set_markup(GTK_LABEL(label),msg);
926 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
927 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
928 g_object_set_data(G_OBJECT(w),"auth_info",info);
929 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
934 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
935 GtkWidget *w=linphone_gtk_get_main_window();
936 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
937 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
938 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
942 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
943 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
946 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
947 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
950 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
952 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
953 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
956 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
957 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
958 if (w) linphone_gtk_call_log_update(w);
959 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
963 static bool_t notify_actions_supported() {
964 bool_t accepts_actions = FALSE;
965 GList *capabilities = notify_get_server_caps();
967 if(capabilities != NULL) {
968 for(c = capabilities; c != NULL; c = c->next) {
969 if(strcmp((char*)c->data, "actions") == 0 ) {
970 accepts_actions = TRUE;
974 g_list_foreach(capabilities, (GFunc)g_free, NULL);
975 g_list_free(capabilities);
977 return accepts_actions;
980 static NotifyNotification* build_notification(const char *title, const char *body){
981 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
988 static void show_notification(NotifyNotification* n){
989 if (n && !notify_notification_show(n,NULL))
990 ms_error("Failed to send notification.");
993 static void make_notification(const char *title, const char *body){
994 show_notification(build_notification(title,body));
999 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1001 if (!notify_is_initted())
1002 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1006 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1011 ms_error("Failed to send notification.");
1013 linphone_gtk_show_main_window();
1015 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1018 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1019 NotifyNotification *n;
1020 switch(linphone_call_get_state(call)){
1021 case LinphoneCallError:
1022 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1024 case LinphoneCallEnd:
1025 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1027 case LinphoneCallIncomingReceived:
1028 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1029 if (notify_actions_supported()) {
1030 notify_notification_add_action (n,"answer", _("Answer"),
1031 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1032 notify_notification_add_action (n,"decline",_("Decline"),
1033 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1035 show_notification(n);
1037 case LinphoneCallPausedByRemote:
1038 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1043 if (body) g_free(body);
1044 if (remote) g_free(remote);
1049 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1050 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1051 LinphoneCore *lc=linphone_call_get_core(call);
1052 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1053 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1054 linphone_core_accept_call_update(lc,call,params);
1055 linphone_call_params_destroy(params);
1057 linphone_call_unref(call);
1058 g_source_remove_by_user_data(dialog);
1059 gtk_widget_destroy(dialog);
1062 static void on_call_updated_timeout(GtkWidget *dialog){
1063 gtk_widget_destroy(dialog);
1066 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1067 LinphoneCore *lc=linphone_call_get_core(call);
1068 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1069 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1070 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1071 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1072 gboolean video_used=linphone_call_params_video_enabled(current_params);
1073 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1074 video_used,video_requested,pol->automatically_accept);
1075 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1076 linphone_core_defer_call_update(lc,call);
1078 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1080 const char *dname=linphone_address_get_display_name(addr);
1081 if (dname==NULL) dname=linphone_address_get_username(addr);
1082 if (dname==NULL) dname=linphone_address_get_domain(addr);
1083 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1084 GTK_DIALOG_DESTROY_WITH_PARENT,
1085 GTK_MESSAGE_WARNING,
1087 _("%s proposed to start video. Do you accept ?"),dname);
1088 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1089 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1090 gtk_widget_show(dialog);
1095 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1097 case LinphoneCallOutgoingInit:
1098 linphone_gtk_create_in_call_view (call);
1100 case LinphoneCallOutgoingProgress:
1101 linphone_gtk_in_call_view_set_calling (call);
1103 case LinphoneCallStreamsRunning:
1104 linphone_gtk_in_call_view_set_in_call(call);
1106 case LinphoneCallUpdatedByRemote:
1107 linphone_gtk_call_updated_by_remote(call);
1109 case LinphoneCallError:
1110 linphone_gtk_in_call_view_terminate (call,msg);
1112 case LinphoneCallEnd:
1113 linphone_gtk_in_call_view_terminate(call,NULL);
1114 linphone_gtk_status_icon_set_blinking(FALSE);
1116 case LinphoneCallIncomingReceived:
1117 linphone_gtk_create_in_call_view(call);
1118 linphone_gtk_in_call_view_set_incoming(call);
1119 linphone_gtk_status_icon_set_blinking(TRUE);
1121 linphone_call_ref(call);
1122 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1125 case LinphoneCallResuming:
1126 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1127 linphone_gtk_in_call_view_set_in_call (call);
1129 case LinphoneCallPausing:
1130 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1131 case LinphoneCallPausedByRemote:
1132 linphone_gtk_in_call_view_set_paused(call);
1134 case LinphoneCallConnected:
1135 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1136 linphone_gtk_status_icon_set_blinking(FALSE);
1141 linphone_gtk_notify(call, msg);
1142 linphone_gtk_update_call_buttons (call);
1145 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1146 linphone_gtk_in_call_view_show_encryption(call);
1149 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1150 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1153 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1154 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1155 GtkTreeModel *model=gtk_combo_box_get_model(box);
1157 gboolean found=FALSE;
1158 const char *stock_id=NULL;
1160 if (gtk_tree_model_get_iter_first(model,&iter)){
1163 gtk_tree_model_get(model,&iter,2,&p,-1);
1168 }while(gtk_tree_model_iter_next(model,&iter));
1171 g_warning("Could not find proxy config in combo box of identities.");
1175 case LinphoneRegistrationOk:
1176 stock_id=GTK_STOCK_YES;
1178 case LinphoneRegistrationProgress:
1179 stock_id=GTK_STOCK_REFRESH;
1181 case LinphoneRegistrationCleared:
1184 case LinphoneRegistrationFailed:
1185 stock_id=GTK_STOCK_DIALOG_WARNING;
1190 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1193 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1194 LinphoneRegistrationState rs, const char *msg){
1196 case LinphoneRegistrationOk:
1198 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1199 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1200 linphone_gtk_exit_login_frame();
1207 update_registration_status(cfg,rs);
1210 void linphone_gtk_open_browser(const char *url){
1211 /*in gtk 2.16, gtk_show_uri does not work...*/
1213 #if GTK_CHECK_VERSION(2,18,3)
1214 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1217 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1218 g_spawn_command_line_async(cl,NULL);
1221 ShellExecute(0,"open",url,NULL,NULL,1);
1225 void linphone_gtk_link_to_website(GtkWidget *item){
1226 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1227 linphone_gtk_open_browser(home);
1230 #ifndef HAVE_GTK_OSX
1232 static GtkStatusIcon *icon=NULL;
1234 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1235 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1236 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1239 static GtkWidget *create_icon_menu(){
1240 GtkWidget *menu=gtk_menu_new();
1241 GtkWidget *menu_item;
1244 const gchar *homesite;
1246 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1247 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1248 tmp=g_strdup(homesite);
1249 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1250 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1252 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1253 gtk_widget_show(image);
1254 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1255 //g_object_unref(G_OBJECT(image));
1256 gtk_widget_show(menu_item);
1257 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1258 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1260 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1261 gtk_widget_show(menu_item);
1262 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1263 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1264 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1265 gtk_widget_show(menu_item);
1266 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1267 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1268 gtk_widget_show(menu);
1272 static void handle_icon_click() {
1273 GtkWidget *mw=linphone_gtk_get_main_window();
1274 if (!gtk_window_is_active((GtkWindow*)mw)) {
1275 linphone_gtk_show_main_window();
1277 gtk_widget_hide(mw);
1281 static void linphone_gtk_init_status_icon(){
1282 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1283 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1284 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1285 GtkWidget *menu=create_icon_menu();
1287 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1288 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1289 #if GTK_CHECK_VERSION(2,20,0)
1290 gtk_status_icon_set_name(icon,title);
1292 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1293 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1294 gtk_status_icon_set_tooltip(icon,title);
1295 gtk_status_icon_set_visible(icon,TRUE);
1296 g_object_set_data(G_OBJECT(icon),"menu",menu);
1297 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1298 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1299 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1300 pbuf=create_pixbuf(call_icon_path);
1301 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1304 static gboolean do_icon_blink(GtkStatusIcon *gi){
1305 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1306 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1307 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1308 if (cur_icon==call_icon){
1309 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1311 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1318 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1320 static gint attention_id;
1321 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1323 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1324 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1328 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1329 if (val && tout==0){
1330 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1331 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1332 }else if (!val && tout!=0){
1333 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1334 g_source_remove(tout);
1335 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1336 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1342 void linphone_gtk_options_activate(GtkWidget *item){
1343 #ifndef HAVE_GTK_OSX
1344 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1349 static void init_identity_combo(GtkComboBox *box){
1350 GtkListStore *store;
1351 GtkCellRenderer *r1,*r2;
1352 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1353 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1354 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1355 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1356 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1357 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1358 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1359 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1362 void linphone_gtk_load_identities(void){
1364 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1366 LinphoneProxyConfig *def=NULL;
1368 GtkListStore *store;
1371 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1372 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1373 /* model is empty, this is the first time we go here */
1374 init_identity_combo(box);
1375 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1377 gtk_list_store_clear(store);
1378 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1379 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1380 gtk_list_store_append(store,&iter);
1381 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1382 g_free(def_identity);
1383 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1385 elem=ms_list_next(elem),i++){
1386 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1387 gtk_list_store_append(store,&iter);
1388 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1389 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1395 gtk_combo_box_set_active(box,def_index);
1398 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1399 const char *label=gtk_button_get_label(button);
1400 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1402 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1403 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1404 if (linphone_core_in_call(linphone_gtk_get_core())){
1405 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1409 static void linphone_gtk_dtmf_released(GtkButton *button){
1410 linphone_core_stop_dtmf (linphone_gtk_get_core());
1413 static void linphone_gtk_connect_digits(void){
1414 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1415 GList *children=gtk_container_get_children(cont);
1417 for(elem=children;elem!=NULL;elem=elem->next){
1418 GtkButton *button=GTK_BUTTON(elem->data);
1419 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1420 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1424 static void linphone_gtk_check_menu_items(void){
1425 bool_t video_enabled=linphone_gtk_video_enabled();
1426 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1427 GtkWidget *selfview_item=linphone_gtk_get_widget(
1428 linphone_gtk_get_main_window(),"selfview_item");
1429 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1430 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1431 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1434 static gboolean linphone_gtk_can_manage_accounts(){
1435 LinphoneCore *lc=linphone_gtk_get_core();
1437 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1438 SipSetup *ss=(SipSetup*)elem->data;
1439 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1446 static void linphone_gtk_configure_main_window(){
1447 static gboolean config_loaded=FALSE;
1448 static const char *title;
1449 static const char *home;
1450 static const char *start_call_icon;
1451 static const char *add_call_icon;
1452 static const char *stop_call_icon;
1453 static const char *search_icon;
1454 static gboolean update_check_menu;
1455 static gboolean buttons_have_borders;
1456 static gboolean show_abcd;
1457 GtkWidget *w=linphone_gtk_get_main_window();
1458 if (!config_loaded){
1459 title=linphone_gtk_get_ui_config("title","Linphone");
1460 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1461 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1462 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1463 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1464 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1465 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1466 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1467 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1470 linphone_gtk_configure_window(w,"main_window");
1472 gtk_window_set_title(GTK_WINDOW(w),title);
1474 if (start_call_icon){
1475 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1476 create_pixmap (start_call_icon));
1477 if (!buttons_have_borders)
1478 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1481 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1482 create_pixmap (add_call_icon));
1483 if (!buttons_have_borders)
1484 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1486 if (stop_call_icon){
1487 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1488 create_pixmap (stop_call_icon));
1489 if (!buttons_have_borders)
1490 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1493 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1494 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1495 g_object_unref(G_OBJECT(pbuf));
1499 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1501 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1505 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1507 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1508 g_object_unref(G_OBJECT(pbuf));
1513 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1515 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1518 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1519 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1520 gtk_image_set_from_pixbuf(img,scaled);
1521 g_object_unref(G_OBJECT(scaled));
1522 g_object_unref(G_OBJECT(pbuf));
1525 if (linphone_gtk_can_manage_accounts()) {
1526 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1528 if (update_check_menu){
1529 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1532 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1533 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1534 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1535 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1536 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1540 void linphone_gtk_manage_login(void){
1541 LinphoneCore *lc=linphone_gtk_get_core();
1542 LinphoneProxyConfig *cfg=NULL;
1543 linphone_core_get_default_proxy(lc,&cfg);
1545 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1546 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1547 linphone_gtk_show_login_frame(cfg);
1553 gboolean linphone_gtk_close(GtkWidget *mw){
1554 /*shutdown calls if any*/
1555 LinphoneCore *lc=linphone_gtk_get_core();
1556 if (linphone_core_in_call(lc)){
1557 linphone_core_terminate_all_calls(lc);
1559 linphone_core_enable_video_preview(lc,FALSE);
1560 #ifdef __APPLE__ /*until with have a better option*/
1561 gtk_window_iconify(GTK_WINDOW(mw));
1563 gtk_widget_hide(mw);
1569 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1570 bool_t video_enabled=linphone_gtk_video_enabled();
1571 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1572 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1574 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1575 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1582 static void linphone_gtk_init_main_window(){
1583 GtkWidget *main_window;
1585 linphone_gtk_configure_main_window();
1586 linphone_gtk_manage_login();
1588 linphone_gtk_load_identities();
1589 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1590 linphone_gtk_show_friends();
1591 linphone_gtk_connect_digits();
1592 main_window=linphone_gtk_get_main_window();
1593 linphone_gtk_call_log_update(main_window);
1595 linphone_gtk_update_call_buttons (NULL);
1596 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1597 g_signal_connect (G_OBJECT (main_window), "delete-event",
1598 G_CALLBACK (linphone_gtk_close), main_window);
1601 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1602 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1603 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1604 gtk_widget_hide(menubar);
1605 gtk_osxapplication_ready(theMacApp);
1607 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1609 linphone_gtk_check_menu_items();
1613 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1615 const char *lname="undef";
1617 #if defined(__linux) || defined(__APPLE__)
1618 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1637 g_error("Bad level !");
1639 #if defined(__linux) || defined(__APPLE__)
1641 msg=g_strdup_vprintf(fmt,cap);
1644 msg=g_strdup_vprintf(fmt,args);
1646 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1649 linphone_gtk_log_push(lev,fmt,args);
1653 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1654 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1655 linphone_gtk_get_main_window(), "uribar"));
1657 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1659 gtk_entry_set_text(uri_bar, refer_to);
1660 linphone_gtk_start_call(linphone_gtk_get_main_window());
1663 static void linphone_gtk_check_soundcards(){
1664 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1665 if (devices==NULL || devices[0]==NULL){
1666 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1667 _("No sound cards have been detected on this computer.\n"
1668 "You won't be able to send or receive audio calls."));
1673 // Display the account wizard
1674 void linphone_gtk_display_wizard() {
1675 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1676 the_wizard = linphone_gtk_create_assistant();
1681 static void linphone_gtk_quit(void){
1682 static gboolean quit_done=FALSE;
1685 linphone_gtk_unmonitor_usb();
1686 g_source_remove_by_user_data(linphone_gtk_get_core());
1687 linphone_gtk_uninit_instance();
1688 linphone_gtk_destroy_log_window();
1689 linphone_core_destroy(the_core);
1690 linphone_gtk_log_uninit();
1694 gdk_threads_leave();
1700 This is not the correct way to implement block termination.
1701 The good way would be to call gtk_main_quit(), and return TRUE.
1702 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1703 As a result the program cannot exit at all.
1704 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1706 static gboolean on_block_termination(void){
1708 linphone_gtk_quit();
1713 int main(int argc, char *argv[]){
1718 const char *factory_config_file;
1720 GtkSettings *settings;
1722 const char *app_name="Linphone";
1724 #if !GLIB_CHECK_VERSION(2, 31, 0)
1725 g_thread_init(NULL);
1729 progpath = strdup(argv[0]);
1731 config_file=linphone_gtk_get_config_file(NULL);
1735 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1736 if ((lang=getenv("LANG"))!=NULL){
1739 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1745 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1748 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1751 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1754 setenv("LANG",lang,1);
1759 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1760 if (p==NULL) perror("bindtextdomain failed");
1761 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1762 textdomain (GETTEXT_PACKAGE);
1764 g_message("NLS disabled.\n");
1767 gtk_rc_add_default_file("./gtkrc");
1769 gdk_threads_enter();
1771 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1772 linphone_options,NULL,NULL)){
1773 gdk_threads_leave();
1777 settings=gtk_settings_get_default();
1778 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1779 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1780 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1781 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1783 if (workingdir!=NULL){
1784 if (chdir(workingdir)==-1){
1785 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1789 /* Now, look for the factory configuration file, we do it this late
1790 since we want to have had time to change directory and to parse
1791 the options, in case we needed to access the working directory */
1792 factory_config_file = linphone_gtk_get_factory_config_file();
1794 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1795 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1796 g_warning("This instance is going to exit now.");
1797 gdk_threads_leave();
1801 add_pixmap_directory("pixmaps");
1802 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1805 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1806 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1807 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1808 /*never block termination:*/
1809 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1812 the_ui=linphone_gtk_create_window("main");
1814 linphone_gtk_create_log_window();
1815 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1817 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1819 g_set_application_name(app_name);
1820 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1821 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1823 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1824 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1825 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1826 linphone_gtk_init_main_window();
1829 // Veryfing if at least one sip account is configured. If not, show wizard
1830 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1831 linphone_gtk_display_wizard();
1835 #ifndef HAVE_GTK_OSX
1836 linphone_gtk_init_status_icon();
1839 linphone_gtk_show_main_window();
1840 linphone_gtk_check_soundcards();
1842 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1843 linphone_gtk_check_for_new_version();
1844 linphone_gtk_monitor_usb();
1847 linphone_gtk_quit();
1848 #ifndef HAVE_GTK_OSX
1849 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1850 gtk_status_icon_set_visible(icon,FALSE);