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);
809 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
810 linphone_gtk_show_friends();
813 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
815 case GTK_RESPONSE_YES:
816 linphone_gtk_show_contact(lf);
819 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
821 gtk_widget_destroy(dialog);
824 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
827 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
828 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
832 gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
833 dialog = gtk_message_dialog_new (
834 GTK_WINDOW(linphone_gtk_get_main_window()),
835 GTK_DIALOG_DESTROY_WITH_PARENT,
836 GTK_MESSAGE_QUESTION,
841 g_signal_connect(G_OBJECT (dialog), "response",
842 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
843 /* actually show the box */
844 gtk_widget_show(dialog);
847 typedef struct _AuthTimeout{
852 static void auth_timeout_clean(AuthTimeout *tout){
856 static gboolean auth_timeout_destroy(AuthTimeout *tout){
858 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
859 gtk_widget_destroy(tout->w);
865 static AuthTimeout * auth_timeout_new(GtkWidget *w){
866 AuthTimeout *tout=g_new(AuthTimeout,1);
868 /*so that the timeout no more references the widget when it is destroyed:*/
869 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
870 /*so that the widget is automatically destroyed after some time */
871 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
875 void linphone_gtk_password_cancel(GtkWidget *w){
876 LinphoneAuthInfo *info;
877 GtkWidget *window=gtk_widget_get_toplevel(w);
878 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
879 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
880 gtk_widget_destroy(window);
883 void linphone_gtk_password_ok(GtkWidget *w){
885 GtkWidget *window=gtk_widget_get_toplevel(w);
886 LinphoneAuthInfo *info;
887 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
888 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
889 entry=linphone_gtk_get_widget(window,"password_entry");
890 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
891 linphone_auth_info_set_userid(info,
892 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
893 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
894 gtk_widget_destroy(window);
897 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
898 GtkWidget *w=linphone_gtk_create_window("password");
899 GtkWidget *label=linphone_gtk_get_widget(w,"message");
900 LinphoneAuthInfo *info;
902 GtkWidget *mw=linphone_gtk_get_main_window();
904 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
905 /*don't prompt for authentication when login frame is visible*/
906 linphone_core_abort_authentication(lc,NULL);
910 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
912 gtk_label_set_markup(GTK_LABEL(label),msg);
914 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
915 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
916 g_object_set_data(G_OBJECT(w),"auth_info",info);
917 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
922 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
923 GtkWidget *w=linphone_gtk_get_main_window();
924 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
925 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
926 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
930 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
931 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
934 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
935 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
938 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
940 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
941 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
944 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
945 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
946 if (w) linphone_gtk_call_log_update(w);
947 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
951 static bool_t notify_actions_supported() {
952 bool_t accepts_actions = FALSE;
953 GList *capabilities = notify_get_server_caps();
955 if(capabilities != NULL) {
956 for(c = capabilities; c != NULL; c = c->next) {
957 if(strcmp((char*)c->data, "actions") == 0 ) {
958 accepts_actions = TRUE;
962 g_list_foreach(capabilities, (GFunc)g_free, NULL);
963 g_list_free(capabilities);
965 return accepts_actions;
968 static NotifyNotification* build_notification(const char *title, const char *body){
969 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
976 static void show_notification(NotifyNotification* n){
977 if (n && !notify_notification_show(n,NULL))
978 ms_error("Failed to send notification.");
981 static void make_notification(const char *title, const char *body){
982 show_notification(build_notification(title,body));
987 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
989 if (!notify_is_initted())
990 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
994 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
999 ms_error("Failed to send notification.");
1001 linphone_gtk_show_main_window();
1003 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1006 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1007 NotifyNotification *n;
1008 switch(linphone_call_get_state(call)){
1009 case LinphoneCallError:
1010 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1012 case LinphoneCallEnd:
1013 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1015 case LinphoneCallIncomingReceived:
1016 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1017 if (notify_actions_supported()) {
1018 notify_notification_add_action (n,"answer", _("Answer"),
1019 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1020 notify_notification_add_action (n,"decline",_("Decline"),
1021 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1023 show_notification(n);
1025 case LinphoneCallPausedByRemote:
1026 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1031 if (body) g_free(body);
1032 if (remote) g_free(remote);
1037 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1038 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1039 LinphoneCore *lc=linphone_call_get_core(call);
1040 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1041 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1042 linphone_core_accept_call_update(lc,call,params);
1043 linphone_call_params_destroy(params);
1045 linphone_call_unref(call);
1046 g_source_remove_by_user_data(dialog);
1047 gtk_widget_destroy(dialog);
1050 static void on_call_updated_timeout(GtkWidget *dialog){
1051 gtk_widget_destroy(dialog);
1054 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1055 LinphoneCore *lc=linphone_call_get_core(call);
1056 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1057 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1058 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1059 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1060 gboolean video_used=linphone_call_params_video_enabled(current_params);
1061 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1062 video_used,video_requested,pol->automatically_accept);
1063 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1064 linphone_core_defer_call_update(lc,call);
1066 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1068 const char *dname=linphone_address_get_display_name(addr);
1069 if (dname==NULL) dname=linphone_address_get_username(addr);
1070 if (dname==NULL) dname=linphone_address_get_domain(addr);
1071 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1072 GTK_DIALOG_DESTROY_WITH_PARENT,
1073 GTK_MESSAGE_WARNING,
1075 _("%s proposed to start video. Do you accept ?"),dname);
1076 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1077 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1078 gtk_widget_show(dialog);
1083 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1085 case LinphoneCallOutgoingInit:
1086 linphone_gtk_create_in_call_view (call);
1088 case LinphoneCallOutgoingProgress:
1089 linphone_gtk_in_call_view_set_calling (call);
1091 case LinphoneCallStreamsRunning:
1092 linphone_gtk_in_call_view_set_in_call(call);
1094 case LinphoneCallUpdatedByRemote:
1095 linphone_gtk_call_updated_by_remote(call);
1097 case LinphoneCallError:
1098 linphone_gtk_in_call_view_terminate (call,msg);
1100 case LinphoneCallEnd:
1101 linphone_gtk_in_call_view_terminate(call,NULL);
1102 linphone_gtk_status_icon_set_blinking(FALSE);
1104 case LinphoneCallIncomingReceived:
1105 linphone_gtk_create_in_call_view(call);
1106 linphone_gtk_in_call_view_set_incoming(call);
1107 linphone_gtk_status_icon_set_blinking(TRUE);
1109 linphone_call_ref(call);
1110 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1113 case LinphoneCallResuming:
1114 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1115 linphone_gtk_in_call_view_set_in_call (call);
1117 case LinphoneCallPausing:
1118 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1119 case LinphoneCallPausedByRemote:
1120 linphone_gtk_in_call_view_set_paused(call);
1122 case LinphoneCallConnected:
1123 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1124 linphone_gtk_status_icon_set_blinking(FALSE);
1129 linphone_gtk_notify(call, msg);
1130 linphone_gtk_update_call_buttons (call);
1133 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1134 linphone_gtk_in_call_view_show_encryption(call);
1137 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1138 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1141 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1142 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1143 GtkTreeModel *model=gtk_combo_box_get_model(box);
1145 gboolean found=FALSE;
1146 const char *stock_id=NULL;
1148 if (gtk_tree_model_get_iter_first(model,&iter)){
1151 gtk_tree_model_get(model,&iter,2,&p,-1);
1156 }while(gtk_tree_model_iter_next(model,&iter));
1159 g_warning("Could not find proxy config in combo box of identities.");
1163 case LinphoneRegistrationOk:
1164 stock_id=GTK_STOCK_YES;
1166 case LinphoneRegistrationProgress:
1167 stock_id=GTK_STOCK_REFRESH;
1169 case LinphoneRegistrationCleared:
1172 case LinphoneRegistrationFailed:
1173 stock_id=GTK_STOCK_DIALOG_WARNING;
1178 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1181 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1182 LinphoneRegistrationState rs, const char *msg){
1184 case LinphoneRegistrationOk:
1186 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1187 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1188 linphone_gtk_exit_login_frame();
1195 update_registration_status(cfg,rs);
1198 void linphone_gtk_open_browser(const char *url){
1199 /*in gtk 2.16, gtk_show_uri does not work...*/
1201 #if GTK_CHECK_VERSION(2,18,3)
1202 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1205 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1206 g_spawn_command_line_async(cl,NULL);
1209 ShellExecute(0,"open",url,NULL,NULL,1);
1213 void linphone_gtk_link_to_website(GtkWidget *item){
1214 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1215 linphone_gtk_open_browser(home);
1218 #ifndef HAVE_GTK_OSX
1220 static GtkStatusIcon *icon=NULL;
1222 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1223 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1224 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1227 static GtkWidget *create_icon_menu(){
1228 GtkWidget *menu=gtk_menu_new();
1229 GtkWidget *menu_item;
1232 const gchar *homesite;
1234 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1235 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1236 tmp=g_strdup(homesite);
1237 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1238 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1240 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1241 gtk_widget_show(image);
1242 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1243 //g_object_unref(G_OBJECT(image));
1244 gtk_widget_show(menu_item);
1245 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1246 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1248 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1249 gtk_widget_show(menu_item);
1250 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1251 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1252 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1253 gtk_widget_show(menu_item);
1254 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1255 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1256 gtk_widget_show(menu);
1260 static void handle_icon_click() {
1261 GtkWidget *mw=linphone_gtk_get_main_window();
1262 if (!gtk_window_is_active((GtkWindow*)mw)) {
1263 linphone_gtk_show_main_window();
1265 gtk_widget_hide(mw);
1269 static void linphone_gtk_init_status_icon(){
1270 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1271 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1272 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1273 GtkWidget *menu=create_icon_menu();
1275 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1276 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1277 #if GTK_CHECK_VERSION(2,20,0)
1278 gtk_status_icon_set_name(icon,title);
1280 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1281 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1282 gtk_status_icon_set_tooltip(icon,title);
1283 gtk_status_icon_set_visible(icon,TRUE);
1284 g_object_set_data(G_OBJECT(icon),"menu",menu);
1285 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1286 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1287 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1288 pbuf=create_pixbuf(call_icon_path);
1289 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1292 static gboolean do_icon_blink(GtkStatusIcon *gi){
1293 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1294 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1295 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1296 if (cur_icon==call_icon){
1297 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1299 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1306 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1308 static gint attention_id;
1309 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1311 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1312 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1316 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1317 if (val && tout==0){
1318 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1319 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1320 }else if (!val && tout!=0){
1321 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1322 g_source_remove(tout);
1323 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1324 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1330 void linphone_gtk_options_activate(GtkWidget *item){
1331 #ifndef HAVE_GTK_OSX
1332 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1337 static void init_identity_combo(GtkComboBox *box){
1338 GtkListStore *store;
1339 GtkCellRenderer *r1,*r2;
1340 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1341 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1342 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1343 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1344 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1345 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1346 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1347 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1350 void linphone_gtk_load_identities(void){
1352 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1354 LinphoneProxyConfig *def=NULL;
1356 GtkListStore *store;
1359 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1360 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1361 /* model is empty, this is the first time we go here */
1362 init_identity_combo(box);
1363 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1365 gtk_list_store_clear(store);
1366 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1367 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1368 gtk_list_store_append(store,&iter);
1369 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1370 g_free(def_identity);
1371 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1373 elem=ms_list_next(elem),i++){
1374 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1375 gtk_list_store_append(store,&iter);
1376 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1377 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1383 gtk_combo_box_set_active(box,def_index);
1386 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1387 const char *label=gtk_button_get_label(button);
1388 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1390 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1391 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1392 if (linphone_core_in_call(linphone_gtk_get_core())){
1393 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1397 static void linphone_gtk_dtmf_released(GtkButton *button){
1398 linphone_core_stop_dtmf (linphone_gtk_get_core());
1401 static void linphone_gtk_connect_digits(void){
1402 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1403 GList *children=gtk_container_get_children(cont);
1405 for(elem=children;elem!=NULL;elem=elem->next){
1406 GtkButton *button=GTK_BUTTON(elem->data);
1407 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1408 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1412 static void linphone_gtk_check_menu_items(void){
1413 bool_t video_enabled=linphone_gtk_video_enabled();
1414 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1415 GtkWidget *selfview_item=linphone_gtk_get_widget(
1416 linphone_gtk_get_main_window(),"selfview_item");
1417 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1418 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1419 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1422 static gboolean linphone_gtk_can_manage_accounts(){
1423 LinphoneCore *lc=linphone_gtk_get_core();
1425 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1426 SipSetup *ss=(SipSetup*)elem->data;
1427 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1434 static void linphone_gtk_configure_main_window(){
1435 static gboolean config_loaded=FALSE;
1436 static const char *title;
1437 static const char *home;
1438 static const char *start_call_icon;
1439 static const char *add_call_icon;
1440 static const char *stop_call_icon;
1441 static const char *search_icon;
1442 static gboolean update_check_menu;
1443 static gboolean buttons_have_borders;
1444 static gboolean show_abcd;
1445 GtkWidget *w=linphone_gtk_get_main_window();
1446 if (!config_loaded){
1447 title=linphone_gtk_get_ui_config("title","Linphone");
1448 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1449 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1450 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1451 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1452 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1453 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1454 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1455 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1458 linphone_gtk_configure_window(w,"main_window");
1460 gtk_window_set_title(GTK_WINDOW(w),title);
1462 if (start_call_icon){
1463 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1464 create_pixmap (start_call_icon));
1465 if (!buttons_have_borders)
1466 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1469 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1470 create_pixmap (add_call_icon));
1471 if (!buttons_have_borders)
1472 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1474 if (stop_call_icon){
1475 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1476 create_pixmap (stop_call_icon));
1477 if (!buttons_have_borders)
1478 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1481 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1482 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1483 g_object_unref(G_OBJECT(pbuf));
1487 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1489 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1493 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1495 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1496 g_object_unref(G_OBJECT(pbuf));
1501 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1503 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1506 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1507 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1508 gtk_image_set_from_pixbuf(img,scaled);
1509 g_object_unref(G_OBJECT(scaled));
1510 g_object_unref(G_OBJECT(pbuf));
1513 if (linphone_gtk_can_manage_accounts()) {
1514 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1516 if (update_check_menu){
1517 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1520 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1521 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1522 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1523 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1524 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1528 void linphone_gtk_manage_login(void){
1529 LinphoneCore *lc=linphone_gtk_get_core();
1530 LinphoneProxyConfig *cfg=NULL;
1531 linphone_core_get_default_proxy(lc,&cfg);
1533 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1534 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1535 linphone_gtk_show_login_frame(cfg);
1541 gboolean linphone_gtk_close(GtkWidget *mw){
1542 /*shutdown calls if any*/
1543 LinphoneCore *lc=linphone_gtk_get_core();
1544 if (linphone_core_in_call(lc)){
1545 linphone_core_terminate_all_calls(lc);
1547 linphone_core_enable_video_preview(lc,FALSE);
1548 #ifdef __APPLE__ /*until with have a better option*/
1549 gtk_window_iconify(GTK_WINDOW(mw));
1551 gtk_widget_hide(mw);
1557 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1558 bool_t video_enabled=linphone_gtk_video_enabled();
1559 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1560 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1562 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1563 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1570 static void linphone_gtk_init_main_window(){
1571 GtkWidget *main_window;
1573 linphone_gtk_configure_main_window();
1574 linphone_gtk_manage_login();
1576 linphone_gtk_load_identities();
1577 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1578 linphone_gtk_show_friends();
1579 linphone_gtk_connect_digits();
1580 main_window=linphone_gtk_get_main_window();
1581 linphone_gtk_call_log_update(main_window);
1583 linphone_gtk_update_call_buttons (NULL);
1584 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1585 g_signal_connect (G_OBJECT (main_window), "delete-event",
1586 G_CALLBACK (linphone_gtk_close), main_window);
1589 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1590 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1591 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1592 gtk_widget_hide(menubar);
1593 gtk_osxapplication_ready(theMacApp);
1595 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1597 linphone_gtk_check_menu_items();
1601 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1603 const char *lname="undef";
1605 #if defined(__linux) || defined(__APPLE__)
1606 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1625 g_error("Bad level !");
1627 #if defined(__linux) || defined(__APPLE__)
1629 msg=g_strdup_vprintf(fmt,cap);
1632 msg=g_strdup_vprintf(fmt,args);
1634 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1637 linphone_gtk_log_push(lev,fmt,args);
1641 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1642 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1643 linphone_gtk_get_main_window(), "uribar"));
1645 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1647 gtk_entry_set_text(uri_bar, refer_to);
1648 linphone_gtk_start_call(linphone_gtk_get_main_window());
1651 static void linphone_gtk_check_soundcards(){
1652 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1653 if (devices==NULL || devices[0]==NULL){
1654 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1655 _("No sound cards have been detected on this computer.\n"
1656 "You won't be able to send or receive audio calls."));
1661 // Display the account wizard
1662 void linphone_gtk_display_wizard() {
1663 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1664 the_wizard = linphone_gtk_create_assistant();
1669 static void linphone_gtk_quit(void){
1670 static gboolean quit_done=FALSE;
1673 linphone_gtk_unmonitor_usb();
1674 g_source_remove_by_user_data(linphone_gtk_get_core());
1675 linphone_gtk_uninit_instance();
1676 linphone_gtk_destroy_log_window();
1677 linphone_core_destroy(the_core);
1678 linphone_gtk_log_uninit();
1682 gdk_threads_leave();
1688 This is not the correct way to implement block termination.
1689 The good way would be to call gtk_main_quit(), and return TRUE.
1690 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1691 As a result the program cannot exit at all.
1692 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1694 static gboolean on_block_termination(void){
1696 linphone_gtk_quit();
1701 int main(int argc, char *argv[]){
1706 const char *factory_config_file;
1708 GtkSettings *settings;
1710 const char *app_name="Linphone";
1712 #if !GLIB_CHECK_VERSION(2, 31, 0)
1713 g_thread_init(NULL);
1717 progpath = strdup(argv[0]);
1719 config_file=linphone_gtk_get_config_file(NULL);
1723 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1724 if ((lang=getenv("LANG"))!=NULL){
1727 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1733 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1736 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1739 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1742 setenv("LANG",lang,1);
1747 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1748 if (p==NULL) perror("bindtextdomain failed");
1749 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1750 textdomain (GETTEXT_PACKAGE);
1752 g_message("NLS disabled.\n");
1755 gtk_rc_add_default_file("./gtkrc");
1757 gdk_threads_enter();
1759 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1760 linphone_options,NULL,NULL)){
1761 gdk_threads_leave();
1765 settings=gtk_settings_get_default();
1766 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1767 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1768 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1769 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1771 if (workingdir!=NULL){
1772 if (chdir(workingdir)==-1){
1773 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1777 /* Now, look for the factory configuration file, we do it this late
1778 since we want to have had time to change directory and to parse
1779 the options, in case we needed to access the working directory */
1780 factory_config_file = linphone_gtk_get_factory_config_file();
1782 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1783 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1784 g_warning("This instance is going to exit now.");
1785 gdk_threads_leave();
1789 add_pixmap_directory("pixmaps");
1790 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1793 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1794 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1795 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1796 /*never block termination:*/
1797 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1800 the_ui=linphone_gtk_create_window("main");
1802 linphone_gtk_create_log_window();
1803 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1805 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1807 g_set_application_name(app_name);
1808 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1809 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1811 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1812 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1813 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1814 linphone_gtk_init_main_window();
1817 // Veryfing if at least one sip account is configured. If not, show wizard
1818 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1819 linphone_gtk_display_wizard();
1823 #ifndef HAVE_GTK_OSX
1824 linphone_gtk_init_status_icon();
1827 linphone_gtk_show_main_window();
1828 linphone_gtk_check_soundcards();
1830 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1831 linphone_gtk_check_for_new_version();
1832 linphone_gtk_monitor_usb();
1835 linphone_gtk_quit();
1836 #ifndef HAVE_GTK_OSX
1837 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1838 gtk_status_icon_set_visible(icon,FALSE);