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",CONFIG_FILE);
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 void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1151 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1152 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1155 static GtkWidget *create_icon_menu(){
1156 GtkWidget *menu=gtk_menu_new();
1157 GtkWidget *menu_item;
1160 const gchar *homesite;
1162 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1163 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1164 tmp=g_strdup(homesite);
1165 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1166 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1168 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1169 gtk_widget_show(image);
1170 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1171 //g_object_unref(G_OBJECT(image));
1172 gtk_widget_show(menu_item);
1173 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1174 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1176 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1177 gtk_widget_show(menu_item);
1178 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1179 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1180 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1181 gtk_widget_show(menu_item);
1182 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1183 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1184 gtk_widget_show(menu);
1188 static GtkStatusIcon *icon=NULL;
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 static void init_identity_combo(GtkComboBox *box){
1259 GtkListStore *store;
1260 GtkCellRenderer *r1,*r2;
1261 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1262 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1263 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1264 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1265 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1266 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1267 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1268 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1271 void linphone_gtk_load_identities(void){
1273 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1275 LinphoneProxyConfig *def=NULL;
1277 GtkListStore *store;
1280 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1281 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1282 /* model is empty, this is the first time we go here */
1283 init_identity_combo(box);
1284 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1286 gtk_list_store_clear(store);
1287 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1288 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1289 gtk_list_store_append(store,&iter);
1290 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1291 g_free(def_identity);
1292 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1294 elem=ms_list_next(elem),i++){
1295 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1296 gtk_list_store_append(store,&iter);
1297 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1298 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1304 gtk_combo_box_set_active(box,def_index);
1307 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1308 const char *label=gtk_button_get_label(button);
1309 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1311 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1312 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1313 if (linphone_core_in_call(linphone_gtk_get_core())){
1314 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1318 static void linphone_gtk_dtmf_released(GtkButton *button){
1319 linphone_core_stop_dtmf (linphone_gtk_get_core());
1322 static void linphone_gtk_connect_digits(void){
1323 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1324 GList *children=gtk_container_get_children(cont);
1326 for(elem=children;elem!=NULL;elem=elem->next){
1327 GtkButton *button=GTK_BUTTON(elem->data);
1328 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1329 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1333 static void linphone_gtk_check_menu_items(void){
1334 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1335 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1336 GtkWidget *selfview_item=linphone_gtk_get_widget(
1337 linphone_gtk_get_main_window(),"selfview_item");
1338 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1339 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1340 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1341 gtk_widget_set_sensitive(selfview_item,video_enabled);
1344 static gboolean linphone_gtk_can_manage_accounts(){
1345 LinphoneCore *lc=linphone_gtk_get_core();
1347 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1348 SipSetup *ss=(SipSetup*)elem->data;
1349 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1356 static void linphone_gtk_configure_main_window(){
1357 static gboolean config_loaded=FALSE;
1358 static const char *title;
1359 static const char *home;
1360 static const char *start_call_icon;
1361 static const char *add_call_icon;
1362 static const char *stop_call_icon;
1363 static const char *search_icon;
1364 static gboolean update_check_menu;
1365 static gboolean buttons_have_borders;
1366 static gboolean show_abcd;
1367 GtkWidget *w=linphone_gtk_get_main_window();
1368 if (!config_loaded){
1369 title=linphone_gtk_get_ui_config("title","Linphone");
1370 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1371 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1372 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1373 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1374 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1375 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1376 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1377 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1380 linphone_gtk_configure_window(w,"main_window");
1382 gtk_window_set_title(GTK_WINDOW(w),title);
1384 if (start_call_icon){
1385 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1386 create_pixmap (start_call_icon));
1387 if (!buttons_have_borders)
1388 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1391 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1392 create_pixmap (add_call_icon));
1393 if (!buttons_have_borders)
1394 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1396 if (stop_call_icon){
1397 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1398 create_pixmap (stop_call_icon));
1399 if (!buttons_have_borders)
1400 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1403 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1404 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1405 g_object_unref(G_OBJECT(pbuf));
1409 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1411 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1415 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1417 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1418 g_object_unref(G_OBJECT(pbuf));
1423 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1425 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1426 g_object_unref(G_OBJECT(pbuf));
1429 if (linphone_gtk_can_manage_accounts())
1430 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1431 if (update_check_menu){
1432 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1435 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1436 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1437 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1438 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1439 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1443 void linphone_gtk_manage_login(void){
1444 LinphoneCore *lc=linphone_gtk_get_core();
1445 LinphoneProxyConfig *cfg=NULL;
1446 linphone_core_get_default_proxy(lc,&cfg);
1448 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1449 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1450 linphone_gtk_show_login_frame(cfg);
1456 gboolean linphone_gtk_close(GtkWidget *mw){
1457 /*shutdown calls if any*/
1458 LinphoneCore *lc=linphone_gtk_get_core();
1459 if (linphone_core_in_call(lc)){
1460 linphone_core_terminate_all_calls(lc);
1462 linphone_core_enable_video_preview(lc,FALSE);
1463 #ifdef __APPLE__ /*until with have a better option*/
1464 gtk_window_iconify(GTK_WINDOW(mw));
1466 gtk_widget_hide(mw);
1472 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1473 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1474 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1476 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1477 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1484 static void linphone_gtk_init_main_window(){
1485 GtkWidget *main_window;
1487 linphone_gtk_configure_main_window();
1488 linphone_gtk_manage_login();
1490 linphone_gtk_load_identities();
1491 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1492 linphone_gtk_show_friends();
1493 linphone_gtk_connect_digits();
1494 main_window=linphone_gtk_get_main_window();
1495 linphone_gtk_call_log_update(main_window);
1497 linphone_gtk_update_call_buttons (NULL);
1498 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1499 g_signal_connect (G_OBJECT (main_window), "delete-event",
1500 G_CALLBACK (linphone_gtk_close), main_window);
1503 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1504 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1505 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1506 gtk_widget_hide(menubar);
1507 gtk_osxapplication_ready(theMacApp);
1509 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1511 linphone_gtk_check_menu_items();
1515 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1517 const char *lname="undef";
1519 #if defined(__linux) || defined(__APPLE__)
1520 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1539 g_error("Bad level !");
1541 #if defined(__linux) || defined(__APPLE__)
1543 msg=g_strdup_vprintf(fmt,cap);
1546 msg=g_strdup_vprintf(fmt,args);
1548 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1551 linphone_gtk_log_push(lev,fmt,args);
1555 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1556 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1557 linphone_gtk_get_main_window(), "uribar"));
1559 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1561 gtk_entry_set_text(uri_bar, refer_to);
1562 linphone_gtk_start_call(linphone_gtk_get_main_window());
1565 static void linphone_gtk_check_soundcards(){
1566 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1567 if (devices==NULL || devices[0]==NULL){
1568 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1569 _("No sound cards have been detected on this computer.\n"
1570 "You won't be able to send or receive audio calls."));
1574 static void linphone_gtk_quit(void){
1575 linphone_gtk_uninit_instance();
1576 linphone_gtk_destroy_log_window();
1577 linphone_core_destroy(the_core);
1578 linphone_gtk_log_uninit();
1582 gdk_threads_leave();
1587 This is not the correct way to implement block termination.
1588 The good way would be to call gtk_main_quit(), and return TRUE.
1589 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1590 As a result the program cannot exit at all.
1591 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1593 static gboolean on_block_termination(void){
1595 linphone_gtk_quit();
1600 int main(int argc, char *argv[]){
1605 const char *factory_config_file;
1607 GtkSettings *settings;
1609 const char *app_name="Linphone";
1611 g_thread_init(NULL);
1614 progpath = strdup(argv[0]);
1616 config_file=linphone_gtk_get_config_file(NULL);
1620 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1621 if ((lang=getenv("LANG"))!=NULL){
1624 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1630 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1633 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1636 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1639 setenv("LANG",lang,1);
1644 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1645 if (p==NULL) perror("bindtextdomain failed");
1646 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1647 textdomain (GETTEXT_PACKAGE);
1649 g_message("NLS disabled.\n");
1652 gtk_rc_add_default_file("./gtkrc");
1654 gdk_threads_enter();
1656 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1657 linphone_options,NULL,NULL)){
1658 gdk_threads_leave();
1662 settings=gtk_settings_get_default();
1663 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1664 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1665 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1666 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1668 if (workingdir!=NULL){
1669 if (chdir(workingdir)==-1){
1670 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1674 /* Now, look for the factory configuration file, we do it this late
1675 since we want to have had time to change directory and to parse
1676 the options, in case we needed to access the working directory */
1677 factory_config_file = linphone_gtk_get_factory_config_file();
1679 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1680 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1681 g_warning("This instance is going to exit now.");
1682 gdk_threads_leave();
1686 add_pixmap_directory("pixmaps");
1687 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1690 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1691 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1692 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1693 /*never block termination:*/
1694 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1697 the_ui=linphone_gtk_create_window("main");
1699 linphone_gtk_create_log_window();
1700 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1702 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1704 g_set_application_name(app_name);
1705 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1706 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1708 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1709 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1710 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1711 linphone_gtk_init_main_window();
1712 #ifndef HAVE_GTK_OSX
1713 linphone_gtk_init_status_icon();
1716 linphone_gtk_show_main_window();
1717 linphone_gtk_check_soundcards();
1719 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1720 linphone_gtk_check_for_new_version();
1723 linphone_gtk_quit();
1724 #ifndef HAVE_GTK_OSX
1725 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1726 gtk_status_icon_set_visible(icon,FALSE);