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;
54 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
55 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
56 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
57 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
58 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
59 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
60 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
61 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
62 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
63 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
64 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
65 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
66 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 static gboolean verbose=0;
70 static gboolean auto_answer = 0;
71 static gchar * addr_to_call = NULL;
72 static gboolean iconified=FALSE;
73 static gchar *workingdir=NULL;
74 static char *progpath=NULL;
75 gchar *linphone_logfile=NULL;
77 static GOptionEntry linphone_options[]={
81 .arg=G_OPTION_ARG_NONE,
82 .arg_data= (gpointer)&verbose,
83 .description=N_("log to stdout some debug information while running.")
86 .long_name = "logfile",
88 .arg = G_OPTION_ARG_STRING,
89 .arg_data = &linphone_logfile,
90 .description = N_("path to a file to write logs into.")
93 .long_name="iconified",
95 .arg=G_OPTION_ARG_NONE,
96 .arg_data= (gpointer)&iconified,
97 .description=N_("Start only in the system tray, do not show the main interface.")
102 .arg = G_OPTION_ARG_STRING,
103 .arg_data = &addr_to_call,
104 .description = N_("address to call right now")
107 .long_name = "auto-answer",
109 .arg = G_OPTION_ARG_NONE,
110 .arg_data = (gpointer) & auto_answer,
111 .description = N_("if set automatically answer incoming calls")
114 .long_name = "workdir",
116 .arg = G_OPTION_ARG_STRING,
117 .arg_data = (gpointer) & workingdir,
118 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
123 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
124 #define RELATIVE_XML_DIR
125 #define BUILD_TREE_XML_DIR "gtk"
128 #define CONFIG_FILE ".linphonerc"
129 #define SECRETS_FILE ".linphone-zidcache"
131 #define CONFIG_FILE "linphonerc"
132 #define SECRETS_FILE "linphone-zidcache"
136 char *linphone_gtk_get_config_file(const char *filename){
137 const int path_max=1024;
138 char *config_file=g_malloc0(path_max);
139 if (filename==NULL) filename=CONFIG_FILE;
140 /*try accessing a local file first if exists*/
141 if (access(CONFIG_FILE,F_OK)==0){
142 snprintf(config_file,path_max,"%s",filename);
145 const char *appdata=getenv("APPDATA");
147 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
148 CreateDirectory(config_file,NULL);
149 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
152 const char *home=getenv("HOME");
153 if (home==NULL) home=".";
154 snprintf(config_file,path_max,"%s/%s",home,filename);
161 #define FACTORY_CONFIG_FILE "linphonerc.factory"
162 static char _factory_config_file[1024];
163 static const char *linphone_gtk_get_factory_config_file(){
164 /*try accessing a local file first if exists*/
165 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
166 snprintf(_factory_config_file,sizeof(_factory_config_file),
167 "%s",FACTORY_CONFIG_FILE);
171 if (progpath != NULL) {
173 progdir = strdup(progpath);
175 basename = strrchr(progdir, '\\');
176 if (basename != NULL) {
179 snprintf(_factory_config_file, sizeof(_factory_config_file),
180 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
182 if (workingdir!=NULL) {
183 snprintf(_factory_config_file, sizeof(_factory_config_file),
184 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
191 basename = strrchr(progdir, '/');
192 if (basename != NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
205 return _factory_config_file;
208 static void linphone_gtk_init_liblinphone(const char *config_file,
209 const char *factory_config_file) {
210 LinphoneCoreVTable vtable={0};
211 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
213 vtable.call_state_changed=linphone_gtk_call_state_changed;
214 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
215 vtable.notify_presence_recv=linphone_gtk_notify_recv;
216 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
217 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
218 vtable.display_status=linphone_gtk_display_status;
219 vtable.display_message=linphone_gtk_display_message;
220 vtable.display_warning=linphone_gtk_display_warning;
221 vtable.display_url=linphone_gtk_display_url;
222 vtable.call_log_updated=linphone_gtk_call_log_updated;
223 vtable.text_received=linphone_gtk_text_received;
224 vtable.refer_received=linphone_gtk_refer_received;
225 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
228 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
229 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
230 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
231 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
232 g_free(secrets_file);
237 LinphoneCore *linphone_gtk_get_core(void){
241 GtkWidget *linphone_gtk_get_main_window(){
245 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
246 static const char *icon_path=NULL;
247 static const char *hiddens=NULL;
248 static const char *shown=NULL;
249 static bool_t config_loaded=FALSE;
250 if (linphone_gtk_get_core()==NULL) return;
251 if (config_loaded==FALSE){
252 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
253 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
254 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
258 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
260 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
262 GdkPixbuf *pbuf=create_pixbuf(icon_path);
263 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
264 g_object_unref(G_OBJECT(pbuf));
268 static int get_ui_file(const char *name, char *path, int pathsize){
269 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
270 if (access(path,F_OK)!=0){
271 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
272 if (access(path,F_OK)!=0){
273 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
274 INSTALLED_XML_DIR,name);
281 GtkWidget *linphone_gtk_create_window(const char *window_name){
282 GError* error = NULL;
283 GtkBuilder* builder = gtk_builder_new ();
287 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
289 if (!gtk_builder_add_from_file (builder, path, &error)){
290 g_error("Couldn't load builder file: %s", error->message);
291 g_error_free (error);
294 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
296 g_error("Could not retrieve '%s' window from xml file",window_name);
299 g_object_set_data(G_OBJECT(w),"builder",builder);
300 gtk_builder_connect_signals(builder,w);
301 linphone_gtk_configure_window(w,window_name);
305 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
308 GtkBuilder* builder = gtk_builder_new ();
310 gchar *object_ids[2];
311 object_ids[0]=g_strdup(widget_name);
314 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
315 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
316 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
317 g_error_free (error);
318 g_free(object_ids[0]);
321 g_free(object_ids[0]);
322 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
324 g_error("Could not retrieve '%s' window from xml file",widget_name);
327 g_object_set_data(G_OBJECT(w),"builder",builder);
328 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
329 gtk_builder_connect_signals(builder,w);
333 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
334 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
337 g_error("Fail to retrieve builder from window !");
340 w=gtk_builder_get_object(builder,name);
342 g_error("No widget named %s found in xml interface.",name);
344 return GTK_WIDGET(w);
348 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
350 GtkWidget *main_window=linphone_gtk_get_main_window();
352 gtk_widget_show(main_window);
353 if (type==GTK_MESSAGE_QUESTION)
355 /* draw a question box. link to dialog_click callback */
356 dialog = gtk_message_dialog_new (
357 GTK_WINDOW(main_window),
358 GTK_DIALOG_DESTROY_WITH_PARENT,
359 GTK_MESSAGE_QUESTION,
362 (const gchar*)message);
363 /* connect to some callback : REVISIT */
365 g_signal_connect_swapped (G_OBJECT (dialog), "response",
366 G_CALLBACK (dialog_click),
369 /* actually show the box */
370 gtk_widget_show(dialog);
374 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
375 GTK_DIALOG_DESTROY_WITH_PARENT,
379 (const gchar*)message);
380 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
381 g_signal_connect_swapped (G_OBJECT (dialog), "response",
382 G_CALLBACK (gtk_widget_destroy),
384 gtk_widget_show(dialog);
388 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
389 if (id==GTK_RESPONSE_CANCEL){
390 gtk_widget_destroy(GTK_WIDGET(dialog));
394 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
395 g_message("About url clicked");
396 linphone_gtk_open_browser(url);
399 void linphone_gtk_show_about(){
400 struct stat filestat;
401 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
404 GdkPixbuf *logo=create_pixbuf(
405 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
406 static const char *defcfg="defcfg";
408 about=linphone_gtk_create_window("about");
409 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
410 memset(&filestat,0,sizeof(filestat));
411 if (stat(license_file,&filestat)!=0){
412 license_file="COPYING";
413 stat(license_file,&filestat);
415 if (filestat.st_size>0){
416 char *license=g_malloc(filestat.st_size+1);
417 FILE *f=fopen(license_file,"r");
418 if (f && fread(license,filestat.st_size,1,f)==1){
419 license[filestat.st_size]='\0';
420 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
424 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
425 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
426 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
427 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
428 tmp=linphone_gtk_get_ui_config("artists",defcfg);
433 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
435 tmp=linphone_gtk_get_ui_config("translators",defcfg);
437 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
438 tmp=linphone_gtk_get_ui_config("comments",defcfg);
440 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
441 gtk_widget_show(about);
444 static void set_video_window_decorations(GdkWindow *w){
445 const char *title=linphone_gtk_get_ui_config("title","Linphone");
446 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
447 char video_title[256];
448 GdkPixbuf *pbuf=create_pixbuf(icon_path);
449 if (!linphone_core_in_call(linphone_gtk_get_core())){
450 snprintf(video_title,sizeof(video_title),"%s video",title);
451 /* When not in call, treat the video as a normal window */
452 gdk_window_set_keep_above(w, FALSE);
454 LinphoneAddress *uri =
455 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
458 linphone_address_clean(uri);
459 if (linphone_address_get_display_name(uri)!=NULL){
460 display_name=ms_strdup(linphone_address_get_display_name(uri));
462 display_name=linphone_address_as_string(uri);
464 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
465 linphone_address_destroy(uri);
466 ms_free(display_name);
468 /* During calls, bring up the video window, arrange so that
469 it is above all the other windows */
470 gdk_window_deiconify(w);
471 gdk_window_set_keep_above(w,TRUE);
472 /* Maybe we should have the following, but then we want to
473 have a timer that turns it off after a little while. */
474 /* gdk_window_set_urgency_hint(w,TRUE); */
476 gdk_window_set_title(w,video_title);
477 /* Refrain the video window to be closed at all times. */
478 gdk_window_set_functions(w,
479 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
480 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
483 l=g_list_append(l,pbuf);
484 gdk_window_set_icon_list(w,l);
486 g_object_unref(G_OBJECT(pbuf));
490 static gboolean video_needs_update=FALSE;
492 static void update_video_title(){
493 video_needs_update=TRUE;
496 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
497 static gboolean first_time=TRUE;
499 static unsigned long previd=0;
500 static unsigned long preview_previd=0;
501 static gboolean in_iterate=FALSE;
504 if (in_iterate) return TRUE;
506 linphone_core_iterate(lc);
508 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
509 linphone_gtk_show_directory_search();
513 id=linphone_core_get_native_video_window_id(lc);
514 if (id!=previd || video_needs_update){
518 ms_message("Updating window decorations");
520 w=gdk_window_foreign_new(id);
522 w=gdk_window_foreign_new((HANDLE)id);
525 set_video_window_decorations(w);
526 g_object_unref(G_OBJECT(w));
528 else ms_error("gdk_window_foreign_new() failed");
529 if (video_needs_update) video_needs_update=FALSE;
532 id=linphone_core_get_native_preview_window_id (lc);
533 if (id!=preview_previd ){
537 ms_message("Updating window decorations for preview");
539 w=gdk_window_foreign_new(id);
541 w=gdk_window_foreign_new((HANDLE)id);
544 set_video_window_decorations(w);
545 g_object_unref(G_OBJECT(w));
547 else ms_error("gdk_window_foreign_new() failed");
548 if (video_needs_update) video_needs_update=FALSE;
551 if (addr_to_call!=NULL){
552 /*make sure we are not showing the login screen*/
553 GtkWidget *mw=linphone_gtk_get_main_window();
554 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
555 if (!GTK_WIDGET_VISIBLE(login_frame)){
556 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
557 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
559 linphone_gtk_start_call(uri_bar);
566 static void load_uri_history(){
567 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
570 GtkEntryCompletion *gep=gtk_entry_completion_new();
571 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
574 snprintf(key,sizeof(key),"uri%i",i);
575 uri=linphone_gtk_get_ui_config(key,NULL);
578 gtk_list_store_append(model,&iter);
579 gtk_list_store_set(model,&iter,0,uri,-1);
580 if (i==0) gtk_entry_set_text(uribar,uri);
584 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
585 gtk_entry_completion_set_text_column(gep,0);
586 gtk_entry_set_completion(uribar,gep);
589 static void save_uri_history(){
590 LinphoneCore *lc=linphone_gtk_get_core();
591 LpConfig *cfg=linphone_core_get_config(lc);
592 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
597 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
599 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
601 gtk_tree_model_get(model,&iter,0,&uri,-1);
603 snprintf(key,sizeof(key),"uri%i",i);
604 lp_config_set_string(cfg,"GtkUi",key,uri);
609 }while(gtk_tree_model_iter_next(model,&iter));
613 static void completion_add_text(GtkEntry *entry, const char *text){
615 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
617 if (gtk_tree_model_get_iter_first(model,&iter)){
620 gtk_tree_model_get(model,&iter,0,&uri,-1);
622 if (strcmp(uri,text)==0) {
624 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
630 }while (gtk_tree_model_iter_next(model,&iter));
632 /* and prepend it on top of the list */
633 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
634 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
639 void linphone_gtk_show_main_window(){
640 GtkWidget *w=linphone_gtk_get_main_window();
641 LinphoneCore *lc=linphone_gtk_get_core();
642 if (linphone_core_video_enabled(lc)){
643 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
644 VIDEOSELFVIEW_DEFAULT));
647 gtk_window_present(GTK_WINDOW(w));
650 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
651 GtkWidget *mw=linphone_gtk_get_main_window();
652 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
653 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
654 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
656 if (linphone_gtk_use_in_call_view() && call)
657 linphone_gtk_in_call_view_terminate(call,error);
658 update_video_title();
661 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
662 LinphoneCore *lc=linphone_gtk_get_core();
663 GtkWidget *mw=linphone_gtk_get_main_window();
664 const MSList *calls=linphone_core_get_calls(lc);
666 bool_t start_active=TRUE;
667 bool_t stop_active=FALSE;
668 bool_t add_call=FALSE;
669 int call_list_size=ms_list_size(calls);
679 button=linphone_gtk_get_widget(mw,"start_call");
680 gtk_widget_set_sensitive(button,start_active);
681 gtk_widget_set_visible(button,!add_call);
683 button=linphone_gtk_get_widget(mw,"add_call");
684 gtk_widget_set_sensitive(button,start_active);
685 gtk_widget_set_visible(button,add_call);
687 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
689 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
690 linphone_gtk_enable_conference_button(lc,call_list_size>1);
691 update_video_title();
694 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
695 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
696 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
697 completion_add_text(GTK_ENTRY(uri_bar),entered);
699 linphone_gtk_call_terminated(NULL,NULL);
704 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
705 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
706 linphone_core_accept_call (linphone_gtk_get_core(),call);
707 linphone_call_unref(call);
713 void linphone_gtk_start_call(GtkWidget *w){
714 LinphoneCore *lc=linphone_gtk_get_core();
716 /*change into in-call mode, then do the work later as it might block a bit */
717 GtkWidget *mw=gtk_widget_get_toplevel(w);
718 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
720 call=linphone_gtk_get_currently_displayed_call(NULL);
721 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
722 linphone_core_accept_call(lc,call);
724 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
725 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
726 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
727 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
732 void linphone_gtk_uri_bar_activate(GtkWidget *w){
733 linphone_gtk_start_call(w);
737 void linphone_gtk_terminate_call(GtkWidget *button){
739 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
741 linphone_core_terminate_call(linphone_gtk_get_core(),call);
743 linphone_core_terminate_conference(linphone_gtk_get_core());
747 void linphone_gtk_decline_clicked(GtkWidget *button){
748 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
750 linphone_core_terminate_call(linphone_gtk_get_core(),call);
753 void linphone_gtk_answer_clicked(GtkWidget *button){
754 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
756 linphone_core_accept_call(linphone_gtk_get_core(),call);
757 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
761 void linphone_gtk_enable_video(GtkWidget *w){
762 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
763 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
764 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
765 gtk_widget_set_sensitive(selfview_item,val);
767 linphone_core_enable_video_preview(linphone_gtk_get_core(),
768 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
770 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
774 void linphone_gtk_enable_self_view(GtkWidget *w){
775 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
776 LinphoneCore *lc=linphone_gtk_get_core();
777 linphone_core_enable_video_preview(lc,val);
778 linphone_core_enable_self_view(lc,val);
779 linphone_gtk_set_ui_config_int("videoselfview",val);
782 void linphone_gtk_used_identity_changed(GtkWidget *w){
783 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
784 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
785 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
786 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
787 linphone_gtk_show_directory_search();
789 if (sel) g_free(sel);
792 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
793 linphone_gtk_show_friends();
796 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
798 case GTK_RESPONSE_YES:
799 linphone_gtk_show_contact(lf);
802 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
804 gtk_widget_destroy(dialog);
807 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
810 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
811 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
815 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);
816 dialog = gtk_message_dialog_new (
817 GTK_WINDOW(linphone_gtk_get_main_window()),
818 GTK_DIALOG_DESTROY_WITH_PARENT,
819 GTK_MESSAGE_QUESTION,
824 g_signal_connect(G_OBJECT (dialog), "response",
825 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
826 /* actually show the box */
827 gtk_widget_show(dialog);
830 typedef struct _AuthTimeout{
835 static void auth_timeout_clean(AuthTimeout *tout){
839 static gboolean auth_timeout_destroy(AuthTimeout *tout){
841 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
842 gtk_widget_destroy(tout->w);
848 static AuthTimeout * auth_timeout_new(GtkWidget *w){
849 AuthTimeout *tout=g_new(AuthTimeout,1);
851 /*so that the timeout no more references the widget when it is destroyed:*/
852 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
853 /*so that the widget is automatically destroyed after some time */
854 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
858 void linphone_gtk_password_cancel(GtkWidget *w){
859 LinphoneAuthInfo *info;
860 GtkWidget *window=gtk_widget_get_toplevel(w);
861 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
862 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
863 gtk_widget_destroy(window);
866 void linphone_gtk_password_ok(GtkWidget *w){
868 GtkWidget *window=gtk_widget_get_toplevel(w);
869 LinphoneAuthInfo *info;
870 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
871 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
872 entry=linphone_gtk_get_widget(window,"password_entry");
873 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
874 linphone_auth_info_set_userid(info,
875 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
876 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
877 gtk_widget_destroy(window);
880 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
881 GtkWidget *w=linphone_gtk_create_window("password");
882 GtkWidget *label=linphone_gtk_get_widget(w,"message");
883 LinphoneAuthInfo *info;
885 GtkWidget *mw=linphone_gtk_get_main_window();
887 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
888 /*don't prompt for authentication when login frame is visible*/
889 linphone_core_abort_authentication(lc,NULL);
893 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
895 gtk_label_set_markup(GTK_LABEL(label),msg);
897 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
898 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
899 g_object_set_data(G_OBJECT(w),"auth_info",info);
900 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
905 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
906 GtkWidget *w=linphone_gtk_get_main_window();
907 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
908 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
909 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
913 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
914 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
917 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
918 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
921 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
923 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
924 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
927 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
928 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
929 if (w) linphone_gtk_call_log_update(w);
930 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
934 static bool_t notify_actions_supported() {
935 bool_t accepts_actions = FALSE;
936 GList *capabilities = notify_get_server_caps();
938 if(capabilities != NULL) {
939 for(c = capabilities; c != NULL; c = c->next) {
940 if(strcmp((char*)c->data, "actions") == 0 ) {
941 accepts_actions = TRUE;
945 g_list_foreach(capabilities, (GFunc)g_free, NULL);
946 g_list_free(capabilities);
948 return accepts_actions;
951 static NotifyNotification* build_notification(const char *title, const char *body){
952 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
959 static void show_notification(NotifyNotification* n){
960 if (n && !notify_notification_show(n,NULL))
961 ms_error("Failed to send notification.");
964 static void make_notification(const char *title, const char *body){
965 show_notification(build_notification(title,body));
970 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
972 if (!notify_is_initted())
973 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
977 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
982 ms_error("Failed to send notification.");
984 linphone_gtk_show_main_window();
986 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
989 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
990 NotifyNotification *n;
991 switch(linphone_call_get_state(call)){
992 case LinphoneCallError:
993 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
995 case LinphoneCallEnd:
996 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
998 case LinphoneCallIncomingReceived:
999 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1000 if (notify_actions_supported()) {
1001 notify_notification_add_action (n,"answer", _("Answer"),
1002 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1003 notify_notification_add_action (n,"decline",_("Decline"),
1004 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1006 show_notification(n);
1008 case LinphoneCallPausedByRemote:
1009 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1014 if (body) g_free(body);
1015 if (remote) g_free(remote);
1020 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1022 case LinphoneCallOutgoingInit:
1023 linphone_gtk_create_in_call_view (call);
1025 case LinphoneCallOutgoingProgress:
1026 linphone_gtk_in_call_view_set_calling (call);
1028 case LinphoneCallStreamsRunning:
1029 linphone_gtk_in_call_view_set_in_call(call);
1031 case LinphoneCallError:
1032 linphone_gtk_in_call_view_terminate (call,msg);
1034 case LinphoneCallEnd:
1035 linphone_gtk_in_call_view_terminate(call,NULL);
1036 linphone_gtk_status_icon_set_blinking(FALSE);
1038 case LinphoneCallIncomingReceived:
1039 linphone_gtk_create_in_call_view(call);
1040 linphone_gtk_in_call_view_set_incoming(call);
1041 linphone_gtk_status_icon_set_blinking(TRUE);
1043 linphone_call_ref(call);
1044 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1047 case LinphoneCallResuming:
1048 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1049 linphone_gtk_in_call_view_set_in_call (call);
1051 case LinphoneCallPausing:
1052 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1053 case LinphoneCallPausedByRemote:
1054 linphone_gtk_in_call_view_set_paused(call);
1056 case LinphoneCallConnected:
1057 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1058 linphone_gtk_status_icon_set_blinking(FALSE);
1063 linphone_gtk_notify(call, msg);
1064 linphone_gtk_update_call_buttons (call);
1067 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1068 linphone_gtk_in_call_view_show_encryption(call);
1071 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1072 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1073 GtkTreeModel *model=gtk_combo_box_get_model(box);
1075 gboolean found=FALSE;
1076 const char *stock_id=NULL;
1078 if (gtk_tree_model_get_iter_first(model,&iter)){
1081 gtk_tree_model_get(model,&iter,2,&p,-1);
1086 }while(gtk_tree_model_iter_next(model,&iter));
1089 g_warning("Could not find proxy config in combo box of identities.");
1093 case LinphoneRegistrationOk:
1094 stock_id=GTK_STOCK_YES;
1096 case LinphoneRegistrationProgress:
1097 stock_id=GTK_STOCK_REFRESH;
1099 case LinphoneRegistrationCleared:
1102 case LinphoneRegistrationFailed:
1103 stock_id=GTK_STOCK_DIALOG_WARNING;
1108 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1111 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1112 LinphoneRegistrationState rs, const char *msg){
1114 case LinphoneRegistrationOk:
1116 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1117 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1118 linphone_gtk_exit_login_frame();
1125 update_registration_status(cfg,rs);
1128 void linphone_gtk_open_browser(const char *url){
1129 /*in gtk 2.16, gtk_show_uri does not work...*/
1131 #if GTK_CHECK_VERSION(2,18,3)
1132 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1135 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1136 g_spawn_command_line_async(cl,NULL);
1139 ShellExecute(0,"open",url,NULL,NULL,1);
1143 void linphone_gtk_link_to_website(GtkWidget *item){
1144 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1145 linphone_gtk_open_browser(home);
1148 #ifndef HAVE_GTK_OSX
1150 static GtkStatusIcon *icon=NULL;
1152 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1153 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1154 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1157 static GtkWidget *create_icon_menu(){
1158 GtkWidget *menu=gtk_menu_new();
1159 GtkWidget *menu_item;
1162 const gchar *homesite;
1164 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1165 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1166 tmp=g_strdup(homesite);
1167 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1168 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1170 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1171 gtk_widget_show(image);
1172 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1173 //g_object_unref(G_OBJECT(image));
1174 gtk_widget_show(menu_item);
1175 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1176 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1178 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1179 gtk_widget_show(menu_item);
1180 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1181 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1182 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1183 gtk_widget_show(menu_item);
1184 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1185 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1186 gtk_widget_show(menu);
1190 static void handle_icon_click() {
1191 GtkWidget *mw=linphone_gtk_get_main_window();
1192 if (!gtk_window_is_active((GtkWindow*)mw)) {
1193 linphone_gtk_show_main_window();
1195 gtk_widget_hide(mw);
1199 static void linphone_gtk_init_status_icon(){
1200 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1201 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1202 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1203 GtkWidget *menu=create_icon_menu();
1205 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1206 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1207 gtk_status_icon_set_name(icon,title);
1208 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1209 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1210 gtk_status_icon_set_tooltip(icon,title);
1211 gtk_status_icon_set_visible(icon,TRUE);
1212 g_object_set_data(G_OBJECT(icon),"menu",menu);
1213 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1214 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1215 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1216 pbuf=create_pixbuf(call_icon_path);
1217 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1220 static gboolean do_icon_blink(GtkStatusIcon *gi){
1221 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1222 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1223 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1224 if (cur_icon==call_icon){
1225 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1227 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1234 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1236 static gint attention_id;
1237 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1239 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1240 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1244 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1245 if (val && tout==0){
1246 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1247 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1248 }else if (!val && tout!=0){
1249 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1250 g_source_remove(tout);
1251 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1252 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1258 void linphone_gtk_options_activate(GtkWidget *item){
1259 #ifndef HAVE_GTK_OSX
1260 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1261 icon && !gtk_status_icon_is_embedded(icon));
1265 static void init_identity_combo(GtkComboBox *box){
1266 GtkListStore *store;
1267 GtkCellRenderer *r1,*r2;
1268 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1269 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1270 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1271 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1272 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1273 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1274 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1275 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1278 void linphone_gtk_load_identities(void){
1280 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1282 LinphoneProxyConfig *def=NULL;
1284 GtkListStore *store;
1287 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1288 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1289 /* model is empty, this is the first time we go here */
1290 init_identity_combo(box);
1291 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1293 gtk_list_store_clear(store);
1294 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1295 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1296 gtk_list_store_append(store,&iter);
1297 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1298 g_free(def_identity);
1299 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1301 elem=ms_list_next(elem),i++){
1302 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1303 gtk_list_store_append(store,&iter);
1304 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1305 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1311 gtk_combo_box_set_active(box,def_index);
1314 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1315 const char *label=gtk_button_get_label(button);
1316 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1318 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1319 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1320 if (linphone_core_in_call(linphone_gtk_get_core())){
1321 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1325 static void linphone_gtk_dtmf_released(GtkButton *button){
1326 linphone_core_stop_dtmf (linphone_gtk_get_core());
1329 static void linphone_gtk_connect_digits(void){
1330 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1331 GList *children=gtk_container_get_children(cont);
1333 for(elem=children;elem!=NULL;elem=elem->next){
1334 GtkButton *button=GTK_BUTTON(elem->data);
1335 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1336 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1340 static void linphone_gtk_check_menu_items(void){
1341 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1342 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1343 GtkWidget *selfview_item=linphone_gtk_get_widget(
1344 linphone_gtk_get_main_window(),"selfview_item");
1345 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1346 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1347 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1348 gtk_widget_set_sensitive(selfview_item,video_enabled);
1351 static gboolean linphone_gtk_can_manage_accounts(){
1352 LinphoneCore *lc=linphone_gtk_get_core();
1354 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1355 SipSetup *ss=(SipSetup*)elem->data;
1356 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1363 static void linphone_gtk_configure_main_window(){
1364 static gboolean config_loaded=FALSE;
1365 static const char *title;
1366 static const char *home;
1367 static const char *start_call_icon;
1368 static const char *add_call_icon;
1369 static const char *stop_call_icon;
1370 static const char *search_icon;
1371 static gboolean update_check_menu;
1372 static gboolean buttons_have_borders;
1373 static gboolean show_abcd;
1374 GtkWidget *w=linphone_gtk_get_main_window();
1375 if (!config_loaded){
1376 title=linphone_gtk_get_ui_config("title","Linphone");
1377 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1378 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1379 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1380 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1381 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1382 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1383 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1384 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1387 linphone_gtk_configure_window(w,"main_window");
1389 gtk_window_set_title(GTK_WINDOW(w),title);
1391 if (start_call_icon){
1392 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1393 create_pixmap (start_call_icon));
1394 if (!buttons_have_borders)
1395 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1398 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1399 create_pixmap (add_call_icon));
1400 if (!buttons_have_borders)
1401 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1403 if (stop_call_icon){
1404 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1405 create_pixmap (stop_call_icon));
1406 if (!buttons_have_borders)
1407 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1410 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1411 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1412 g_object_unref(G_OBJECT(pbuf));
1416 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1418 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1422 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1424 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1425 g_object_unref(G_OBJECT(pbuf));
1430 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1432 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1433 g_object_unref(G_OBJECT(pbuf));
1436 if (linphone_gtk_can_manage_accounts())
1437 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1438 if (update_check_menu){
1439 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1442 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1443 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1444 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1445 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1446 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1450 void linphone_gtk_manage_login(void){
1451 LinphoneCore *lc=linphone_gtk_get_core();
1452 LinphoneProxyConfig *cfg=NULL;
1453 linphone_core_get_default_proxy(lc,&cfg);
1455 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1456 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1457 linphone_gtk_show_login_frame(cfg);
1463 gboolean linphone_gtk_close(GtkWidget *mw){
1464 /*shutdown calls if any*/
1465 LinphoneCore *lc=linphone_gtk_get_core();
1466 if (linphone_core_in_call(lc)){
1467 linphone_core_terminate_all_calls(lc);
1469 linphone_core_enable_video_preview(lc,FALSE);
1470 #ifdef __APPLE__ /*until with have a better option*/
1471 gtk_window_iconify(GTK_WINDOW(mw));
1473 gtk_widget_hide(mw);
1479 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1480 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1481 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1483 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1484 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1491 static void linphone_gtk_init_main_window(){
1492 GtkWidget *main_window;
1494 linphone_gtk_configure_main_window();
1495 linphone_gtk_manage_login();
1497 linphone_gtk_load_identities();
1498 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1499 linphone_gtk_show_friends();
1500 linphone_gtk_connect_digits();
1501 main_window=linphone_gtk_get_main_window();
1502 linphone_gtk_call_log_update(main_window);
1504 linphone_gtk_update_call_buttons (NULL);
1505 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1506 g_signal_connect (G_OBJECT (main_window), "delete-event",
1507 G_CALLBACK (linphone_gtk_close), main_window);
1510 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1511 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1512 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1513 gtk_widget_hide(menubar);
1514 gtk_osxapplication_ready(theMacApp);
1516 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1518 linphone_gtk_check_menu_items();
1522 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1524 const char *lname="undef";
1526 #if defined(__linux) || defined(__APPLE__)
1527 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1546 g_error("Bad level !");
1548 #if defined(__linux) || defined(__APPLE__)
1550 msg=g_strdup_vprintf(fmt,cap);
1553 msg=g_strdup_vprintf(fmt,args);
1555 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1558 linphone_gtk_log_push(lev,fmt,args);
1562 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1563 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1564 linphone_gtk_get_main_window(), "uribar"));
1566 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1568 gtk_entry_set_text(uri_bar, refer_to);
1569 linphone_gtk_start_call(linphone_gtk_get_main_window());
1572 static void linphone_gtk_check_soundcards(){
1573 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1574 if (devices==NULL || devices[0]==NULL){
1575 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1576 _("No sound cards have been detected on this computer.\n"
1577 "You won't be able to send or receive audio calls."));
1581 static void linphone_gtk_quit(void){
1582 linphone_gtk_uninit_instance();
1583 linphone_gtk_destroy_log_window();
1584 linphone_core_destroy(the_core);
1585 linphone_gtk_log_uninit();
1589 gdk_threads_leave();
1594 This is not the correct way to implement block termination.
1595 The good way would be to call gtk_main_quit(), and return TRUE.
1596 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1597 As a result the program cannot exit at all.
1598 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1600 static gboolean on_block_termination(void){
1602 linphone_gtk_quit();
1607 int main(int argc, char *argv[]){
1612 const char *factory_config_file;
1614 GtkSettings *settings;
1616 const char *app_name="Linphone";
1618 g_thread_init(NULL);
1621 progpath = strdup(argv[0]);
1623 config_file=linphone_gtk_get_config_file(NULL);
1627 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1628 if ((lang=getenv("LANG"))!=NULL){
1631 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1637 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1640 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1643 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1646 setenv("LANG",lang,1);
1651 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1652 if (p==NULL) perror("bindtextdomain failed");
1653 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1654 textdomain (GETTEXT_PACKAGE);
1656 g_message("NLS disabled.\n");
1659 gtk_rc_add_default_file("./gtkrc");
1661 gdk_threads_enter();
1663 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1664 linphone_options,NULL,NULL)){
1665 gdk_threads_leave();
1669 settings=gtk_settings_get_default();
1670 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1671 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1672 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1673 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1675 if (workingdir!=NULL){
1676 if (chdir(workingdir)==-1){
1677 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1681 /* Now, look for the factory configuration file, we do it this late
1682 since we want to have had time to change directory and to parse
1683 the options, in case we needed to access the working directory */
1684 factory_config_file = linphone_gtk_get_factory_config_file();
1686 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1687 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1688 g_warning("This instance is going to exit now.");
1689 gdk_threads_leave();
1693 add_pixmap_directory("pixmaps");
1694 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1697 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1698 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1699 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1700 /*never block termination:*/
1701 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1704 the_ui=linphone_gtk_create_window("main");
1706 linphone_gtk_create_log_window();
1707 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1709 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1711 g_set_application_name(app_name);
1712 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1713 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1715 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1716 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1717 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1718 linphone_gtk_init_main_window();
1719 #ifndef HAVE_GTK_OSX
1720 linphone_gtk_init_status_icon();
1723 linphone_gtk_show_main_window();
1724 linphone_gtk_check_soundcards();
1726 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1727 linphone_gtk_check_for_new_version();
1730 linphone_gtk_quit();
1731 #ifndef HAVE_GTK_OSX
1732 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1733 gtk_status_icon_set_visible(icon,FALSE);