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_show(LinphoneCore *lc);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static 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"
130 #define CONFIG_FILE "linphonerc"
135 static char _config_file[1024];
138 const char *linphone_gtk_get_config_file(){
139 /*try accessing a local file first if exists*/
140 if (access(CONFIG_FILE,F_OK)==0){
141 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
144 const char *appdata=getenv("APPDATA");
146 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
147 CreateDirectory(_config_file,NULL);
148 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
151 const char *home=getenv("HOME");
152 if (home==NULL) home=".";
153 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
160 #define FACTORY_CONFIG_FILE "linphonerc.factory"
161 static char _factory_config_file[1024];
162 static const char *linphone_gtk_get_factory_config_file(){
163 /*try accessing a local file first if exists*/
164 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
165 snprintf(_factory_config_file,sizeof(_factory_config_file),
166 "%s",FACTORY_CONFIG_FILE);
170 if (progpath != NULL) {
172 progdir = strdup(progpath);
174 basename = strrchr(progdir, '\\');
175 if (basename != NULL) {
178 snprintf(_factory_config_file, sizeof(_factory_config_file),
179 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
181 if (workingdir!=NULL) {
182 snprintf(_factory_config_file, sizeof(_factory_config_file),
183 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
190 basename = strrchr(progdir, '/');
191 if (basename != NULL) {
194 snprintf(_factory_config_file, sizeof(_factory_config_file),
195 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
204 return _factory_config_file;
207 static void linphone_gtk_init_liblinphone(const char *config_file,
208 const char *factory_config_file) {
209 LinphoneCoreVTable vtable={0};
211 vtable.call_state_changed=linphone_gtk_call_state_changed;
212 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
213 vtable.show=linphone_gtk_show;
214 vtable.notify_presence_recv=linphone_gtk_notify_recv;
215 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
216 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
217 vtable.display_status=linphone_gtk_display_status;
218 vtable.display_message=linphone_gtk_display_message;
219 vtable.display_warning=linphone_gtk_display_warning;
220 vtable.display_url=linphone_gtk_display_url;
221 vtable.call_log_updated=linphone_gtk_call_log_updated;
222 vtable.text_received=linphone_gtk_text_received;
223 vtable.refer_received=linphone_gtk_refer_received;
224 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
227 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
228 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
233 LinphoneCore *linphone_gtk_get_core(void){
237 GtkWidget *linphone_gtk_get_main_window(){
241 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
242 static const char *icon_path=NULL;
243 static const char *hiddens=NULL;
244 static const char *shown=NULL;
245 static bool_t config_loaded=FALSE;
246 if (linphone_gtk_get_core()==NULL) return;
247 if (config_loaded==FALSE){
248 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
249 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
250 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
254 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
256 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
258 GdkPixbuf *pbuf=create_pixbuf(icon_path);
259 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
260 g_object_unref(G_OBJECT(pbuf));
264 static int get_ui_file(const char *name, char *path, int pathsize){
265 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
266 if (access(path,F_OK)!=0){
267 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
268 if (access(path,F_OK)!=0){
269 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
270 INSTALLED_XML_DIR,name);
277 GtkWidget *linphone_gtk_create_window(const char *window_name){
278 GError* error = NULL;
279 GtkBuilder* builder = gtk_builder_new ();
283 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
285 if (!gtk_builder_add_from_file (builder, path, &error)){
286 g_error("Couldn't load builder file: %s", error->message);
287 g_error_free (error);
290 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
292 g_error("Could not retrieve '%s' window from xml file",window_name);
295 g_object_set_data(G_OBJECT(w),"builder",builder);
296 gtk_builder_connect_signals(builder,w);
297 linphone_gtk_configure_window(w,window_name);
301 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
304 GtkBuilder* builder = gtk_builder_new ();
306 gchar *object_ids[2];
307 object_ids[0]=g_strdup(widget_name);
310 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
311 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
312 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
313 g_error_free (error);
314 g_free(object_ids[0]);
317 g_free(object_ids[0]);
318 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
320 g_error("Could not retrieve '%s' window from xml file",widget_name);
323 g_object_set_data(G_OBJECT(w),"builder",builder);
324 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
325 gtk_builder_connect_signals(builder,w);
329 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
330 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
333 g_error("Fail to retrieve builder from window !");
336 w=gtk_builder_get_object(builder,name);
338 g_error("No widget named %s found in xml interface.",name);
340 return GTK_WIDGET(w);
344 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
346 GtkWidget *main_window=linphone_gtk_get_main_window();
348 gtk_widget_show(main_window);
349 if (type==GTK_MESSAGE_QUESTION)
351 /* draw a question box. link to dialog_click callback */
352 dialog = gtk_message_dialog_new (
353 GTK_WINDOW(main_window),
354 GTK_DIALOG_DESTROY_WITH_PARENT,
355 GTK_MESSAGE_QUESTION,
358 (const gchar*)message);
359 /* connect to some callback : REVISIT */
361 g_signal_connect_swapped (G_OBJECT (dialog), "response",
362 G_CALLBACK (dialog_click),
365 /* actually show the box */
366 gtk_widget_show(dialog);
370 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
371 GTK_DIALOG_DESTROY_WITH_PARENT,
375 (const gchar*)message);
376 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
377 g_signal_connect_swapped (G_OBJECT (dialog), "response",
378 G_CALLBACK (gtk_widget_destroy),
380 gtk_widget_show(dialog);
384 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
385 if (id==GTK_RESPONSE_CANCEL){
386 gtk_widget_destroy(GTK_WIDGET(dialog));
390 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
391 g_message("About url clicked");
392 linphone_gtk_open_browser(url);
395 void linphone_gtk_show_about(){
396 struct stat filestat;
397 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
400 GdkPixbuf *logo=create_pixbuf(
401 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
402 static const char *defcfg="defcfg";
404 about=linphone_gtk_create_window("about");
405 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
406 memset(&filestat,0,sizeof(filestat));
407 if (stat(license_file,&filestat)!=0){
408 license_file="COPYING";
409 stat(license_file,&filestat);
411 if (filestat.st_size>0){
412 char *license=g_malloc(filestat.st_size+1);
413 FILE *f=fopen(license_file,"r");
414 if (f && fread(license,filestat.st_size,1,f)==1){
415 license[filestat.st_size]='\0';
416 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
420 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
421 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
422 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
423 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
424 tmp=linphone_gtk_get_ui_config("artists",defcfg);
429 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
431 tmp=linphone_gtk_get_ui_config("translators",defcfg);
433 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
434 tmp=linphone_gtk_get_ui_config("comments",defcfg);
436 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
437 gtk_widget_show(about);
440 static void set_video_window_decorations(GdkWindow *w){
441 const char *title=linphone_gtk_get_ui_config("title","Linphone");
442 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
443 char video_title[256];
444 GdkPixbuf *pbuf=create_pixbuf(icon_path);
445 if (!linphone_core_in_call(linphone_gtk_get_core())){
446 snprintf(video_title,sizeof(video_title),"%s video",title);
447 /* When not in call, treat the video as a normal window */
448 gdk_window_set_keep_above(w, FALSE);
450 LinphoneAddress *uri =
451 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
454 linphone_address_clean(uri);
455 if (linphone_address_get_display_name(uri)!=NULL){
456 display_name=ms_strdup(linphone_address_get_display_name(uri));
458 display_name=linphone_address_as_string(uri);
460 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
461 linphone_address_destroy(uri);
462 ms_free(display_name);
464 /* During calls, bring up the video window, arrange so that
465 it is above all the other windows */
466 gdk_window_deiconify(w);
467 gdk_window_set_keep_above(w,TRUE);
468 /* Maybe we should have the following, but then we want to
469 have a timer that turns it off after a little while. */
470 /* gdk_window_set_urgency_hint(w,TRUE); */
472 gdk_window_set_title(w,video_title);
473 /* Refrain the video window to be closed at all times. */
474 gdk_window_set_functions(w,
475 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
476 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
479 l=g_list_append(l,pbuf);
480 gdk_window_set_icon_list(w,l);
482 g_object_unref(G_OBJECT(pbuf));
486 static gboolean video_needs_update=FALSE;
488 static void update_video_title(){
489 video_needs_update=TRUE;
492 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
493 static gboolean first_time=TRUE;
495 static unsigned long previd=0;
496 static unsigned long preview_previd=0;
497 static gboolean in_iterate=FALSE;
500 if (in_iterate) return TRUE;
502 linphone_core_iterate(lc);
504 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
505 linphone_gtk_show_directory_search();
509 id=linphone_core_get_native_video_window_id(lc);
510 if (id!=previd || video_needs_update){
514 ms_message("Updating window decorations");
516 w=gdk_window_foreign_new(id);
518 w=gdk_window_foreign_new((HANDLE)id);
521 set_video_window_decorations(w);
522 g_object_unref(G_OBJECT(w));
524 else ms_error("gdk_window_foreign_new() failed");
525 if (video_needs_update) video_needs_update=FALSE;
528 id=linphone_core_get_native_preview_window_id (lc);
529 if (id!=preview_previd ){
533 ms_message("Updating window decorations for preview");
535 w=gdk_window_foreign_new(id);
537 w=gdk_window_foreign_new((HANDLE)id);
540 set_video_window_decorations(w);
541 g_object_unref(G_OBJECT(w));
543 else ms_error("gdk_window_foreign_new() failed");
544 if (video_needs_update) video_needs_update=FALSE;
547 if (addr_to_call!=NULL){
548 /*make sure we are not showing the login screen*/
549 GtkWidget *mw=linphone_gtk_get_main_window();
550 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
551 if (!GTK_WIDGET_VISIBLE(login_frame)){
552 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
553 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
555 linphone_gtk_start_call(uri_bar);
562 static void load_uri_history(){
563 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
566 GtkEntryCompletion *gep=gtk_entry_completion_new();
567 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
570 snprintf(key,sizeof(key),"uri%i",i);
571 uri=linphone_gtk_get_ui_config(key,NULL);
574 gtk_list_store_append(model,&iter);
575 gtk_list_store_set(model,&iter,0,uri,-1);
576 if (i==0) gtk_entry_set_text(uribar,uri);
580 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
581 gtk_entry_completion_set_text_column(gep,0);
582 gtk_entry_set_completion(uribar,gep);
585 static void save_uri_history(){
586 LinphoneCore *lc=linphone_gtk_get_core();
587 LpConfig *cfg=linphone_core_get_config(lc);
588 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
593 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
595 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
597 gtk_tree_model_get(model,&iter,0,&uri,-1);
599 snprintf(key,sizeof(key),"uri%i",i);
600 lp_config_set_string(cfg,"GtkUi",key,uri);
605 }while(gtk_tree_model_iter_next(model,&iter));
609 static void completion_add_text(GtkEntry *entry, const char *text){
611 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
613 if (gtk_tree_model_get_iter_first(model,&iter)){
616 gtk_tree_model_get(model,&iter,0,&uri,-1);
618 if (strcmp(uri,text)==0) {
620 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
626 }while (gtk_tree_model_iter_next(model,&iter));
628 /* and prepend it on top of the list */
629 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
630 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
635 void linphone_gtk_show_main_window(){
636 GtkWidget *w=linphone_gtk_get_main_window();
637 LinphoneCore *lc=linphone_gtk_get_core();
638 if (linphone_core_video_enabled(lc)){
639 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
640 VIDEOSELFVIEW_DEFAULT));
643 gtk_window_present(GTK_WINDOW(w));
646 static void linphone_gtk_show(LinphoneCore *lc){
648 linphone_gtk_show_main_window();
652 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
653 GtkWidget *mw=linphone_gtk_get_main_window();
654 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
655 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
656 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
658 if (linphone_gtk_use_in_call_view() && call)
659 linphone_gtk_in_call_view_terminate(call,error);
660 update_video_title();
663 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
664 LinphoneCore *lc=linphone_gtk_get_core();
665 GtkWidget *mw=linphone_gtk_get_main_window();
666 const MSList *calls=linphone_core_get_calls(lc);
668 bool_t start_active=TRUE;
669 bool_t stop_active=FALSE;
670 bool_t add_call=FALSE;
671 int call_list_size=ms_list_size(calls);
681 button=linphone_gtk_get_widget(mw,"start_call");
682 gtk_widget_set_sensitive(button,start_active);
683 gtk_widget_set_visible(button,!add_call);
685 button=linphone_gtk_get_widget(mw,"add_call");
686 gtk_widget_set_sensitive(button,start_active);
687 gtk_widget_set_visible(button,add_call);
689 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
691 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
692 linphone_gtk_enable_conference_button(lc,call_list_size>1);
693 update_video_title();
696 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
697 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
698 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
699 completion_add_text(GTK_ENTRY(uri_bar),entered);
701 linphone_gtk_call_terminated(NULL,NULL);
706 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
707 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
708 linphone_core_accept_call (linphone_gtk_get_core(),call);
709 linphone_call_unref(call);
715 void linphone_gtk_start_call(GtkWidget *w){
716 LinphoneCore *lc=linphone_gtk_get_core();
718 /*change into in-call mode, then do the work later as it might block a bit */
719 GtkWidget *mw=gtk_widget_get_toplevel(w);
720 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
722 call=linphone_gtk_get_currently_displayed_call(NULL);
723 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
724 linphone_core_accept_call(lc,call);
726 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
727 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
728 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
729 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
734 void linphone_gtk_uri_bar_activate(GtkWidget *w){
735 linphone_gtk_start_call(w);
739 void linphone_gtk_terminate_call(GtkWidget *button){
741 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
743 linphone_core_terminate_call(linphone_gtk_get_core(),call);
745 linphone_core_terminate_conference(linphone_gtk_get_core());
749 void linphone_gtk_decline_clicked(GtkWidget *button){
750 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
752 linphone_core_terminate_call(linphone_gtk_get_core(),call);
755 void linphone_gtk_answer_clicked(GtkWidget *button){
756 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
758 linphone_core_accept_call(linphone_gtk_get_core(),call);
759 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
763 void linphone_gtk_enable_video(GtkWidget *w){
764 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
765 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
766 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
767 gtk_widget_set_sensitive(selfview_item,val);
769 linphone_core_enable_video_preview(linphone_gtk_get_core(),
770 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
772 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
776 void linphone_gtk_enable_self_view(GtkWidget *w){
777 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
778 LinphoneCore *lc=linphone_gtk_get_core();
779 linphone_core_enable_video_preview(lc,val);
780 linphone_core_enable_self_view(lc,val);
781 linphone_gtk_set_ui_config_int("videoselfview",val);
784 void linphone_gtk_used_identity_changed(GtkWidget *w){
785 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
786 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
787 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
788 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
789 linphone_gtk_show_directory_search();
791 if (sel) g_free(sel);
794 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
795 linphone_gtk_show_friends();
798 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
800 case GTK_RESPONSE_YES:
801 linphone_gtk_show_contact(lf);
804 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
806 gtk_widget_destroy(dialog);
809 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
812 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
813 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
817 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);
818 dialog = gtk_message_dialog_new (
819 GTK_WINDOW(linphone_gtk_get_main_window()),
820 GTK_DIALOG_DESTROY_WITH_PARENT,
821 GTK_MESSAGE_QUESTION,
826 g_signal_connect(G_OBJECT (dialog), "response",
827 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
828 /* actually show the box */
829 gtk_widget_show(dialog);
832 typedef struct _AuthTimeout{
837 static void auth_timeout_clean(AuthTimeout *tout){
841 static gboolean auth_timeout_destroy(AuthTimeout *tout){
843 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
844 gtk_widget_destroy(tout->w);
850 static AuthTimeout * auth_timeout_new(GtkWidget *w){
851 AuthTimeout *tout=g_new(AuthTimeout,1);
853 /*so that the timeout no more references the widget when it is destroyed:*/
854 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
855 /*so that the widget is automatically destroyed after some time */
856 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
860 void linphone_gtk_password_cancel(GtkWidget *w){
861 LinphoneAuthInfo *info;
862 GtkWidget *window=gtk_widget_get_toplevel(w);
863 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
864 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
865 gtk_widget_destroy(window);
868 void linphone_gtk_password_ok(GtkWidget *w){
870 GtkWidget *window=gtk_widget_get_toplevel(w);
871 LinphoneAuthInfo *info;
872 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
873 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
874 entry=linphone_gtk_get_widget(window,"password_entry");
875 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
876 linphone_auth_info_set_userid(info,
877 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
878 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
879 gtk_widget_destroy(window);
882 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
883 GtkWidget *w=linphone_gtk_create_window("password");
884 GtkWidget *label=linphone_gtk_get_widget(w,"message");
885 LinphoneAuthInfo *info;
887 GtkWidget *mw=linphone_gtk_get_main_window();
889 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
890 /*don't prompt for authentication when login frame is visible*/
891 linphone_core_abort_authentication(lc,NULL);
895 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
897 gtk_label_set_markup(GTK_LABEL(label),msg);
899 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
900 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
901 g_object_set_data(G_OBJECT(w),"auth_info",info);
902 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
907 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
908 GtkWidget *w=linphone_gtk_get_main_window();
909 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
910 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
911 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
915 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
916 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
919 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
920 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
923 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
925 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
926 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
929 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
930 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
931 if (w) linphone_gtk_call_log_update(w);
932 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
936 static bool_t notify_actions_supported() {
937 bool_t accepts_actions = FALSE;
938 GList *capabilities = notify_get_server_caps();
940 if(capabilities != NULL) {
941 for(c = capabilities; c != NULL; c = c->next) {
942 if(strcmp((char*)c->data, "actions") == 0 ) {
943 accepts_actions = TRUE;
947 g_list_foreach(capabilities, (GFunc)g_free, NULL);
948 g_list_free(capabilities);
950 return accepts_actions;
953 static NotifyNotification* build_notification(const char *title, const char *body){
954 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
961 static void show_notification(NotifyNotification* n){
962 if (n && !notify_notification_show(n,NULL))
963 ms_error("Failed to send notification.");
966 static void make_notification(const char *title, const char *body){
967 show_notification(build_notification(title,body));
972 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
974 if (!notify_is_initted())
975 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
979 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
984 ms_error("Failed to send notification.");
986 linphone_gtk_show_main_window();
988 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
991 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
992 NotifyNotification *n;
993 switch(linphone_call_get_state(call)){
994 case LinphoneCallError:
995 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
997 case LinphoneCallEnd:
998 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1000 case LinphoneCallIncomingReceived:
1001 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1002 if (notify_actions_supported()) {
1003 notify_notification_add_action (n,"answer", _("Answer"),
1004 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1005 notify_notification_add_action (n,"decline",_("Decline"),
1006 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1008 show_notification(n);
1010 case LinphoneCallPausedByRemote:
1011 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1016 if (body) g_free(body);
1017 if (remote) g_free(remote);
1022 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1024 case LinphoneCallOutgoingInit:
1025 linphone_gtk_create_in_call_view (call);
1027 case LinphoneCallOutgoingProgress:
1028 linphone_gtk_in_call_view_set_calling (call);
1030 case LinphoneCallStreamsRunning:
1031 linphone_gtk_in_call_view_set_in_call(call);
1033 case LinphoneCallError:
1034 linphone_gtk_in_call_view_terminate (call,msg);
1036 case LinphoneCallEnd:
1037 linphone_gtk_in_call_view_terminate(call,NULL);
1038 linphone_gtk_status_icon_set_blinking(FALSE);
1040 case LinphoneCallIncomingReceived:
1041 linphone_gtk_create_in_call_view (call);
1042 linphone_gtk_in_call_view_set_incoming(call);
1043 linphone_gtk_status_icon_set_blinking(TRUE);
1045 linphone_call_ref(call);
1046 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1049 case LinphoneCallResuming:
1050 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1051 linphone_gtk_in_call_view_set_in_call (call);
1053 case LinphoneCallPausing:
1054 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1055 case LinphoneCallPausedByRemote:
1056 linphone_gtk_in_call_view_set_paused(call);
1058 case LinphoneCallConnected:
1059 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1060 linphone_gtk_status_icon_set_blinking(FALSE);
1065 linphone_gtk_notify(call, msg);
1066 linphone_gtk_update_call_buttons (call);
1069 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1070 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1071 GtkTreeModel *model=gtk_combo_box_get_model(box);
1073 gboolean found=FALSE;
1074 const char *stock_id=NULL;
1076 if (gtk_tree_model_get_iter_first(model,&iter)){
1079 gtk_tree_model_get(model,&iter,2,&p,-1);
1084 }while(gtk_tree_model_iter_next(model,&iter));
1087 g_warning("Could not find proxy config in combo box of identities.");
1091 case LinphoneRegistrationOk:
1092 stock_id=GTK_STOCK_YES;
1094 case LinphoneRegistrationProgress:
1095 stock_id=GTK_STOCK_REFRESH;
1097 case LinphoneRegistrationCleared:
1100 case LinphoneRegistrationFailed:
1101 stock_id=GTK_STOCK_DIALOG_WARNING;
1106 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1109 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1110 LinphoneRegistrationState rs, const char *msg){
1112 case LinphoneRegistrationOk:
1114 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1115 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1116 linphone_gtk_exit_login_frame();
1123 update_registration_status(cfg,rs);
1127 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1128 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1129 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1132 void linphone_gtk_open_browser(const char *url){
1133 /*in gtk 2.16, gtk_show_uri does not work...*/
1135 #if GTK_CHECK_VERSION(2,18,3)
1136 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1139 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1140 g_spawn_command_line_async(cl,NULL);
1143 ShellExecute(0,"open",url,NULL,NULL,1);
1147 void linphone_gtk_link_to_website(GtkWidget *item){
1148 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1149 linphone_gtk_open_browser(home);
1152 static GtkWidget *create_icon_menu(){
1153 GtkWidget *menu=gtk_menu_new();
1154 GtkWidget *menu_item;
1157 const gchar *homesite;
1159 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1160 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1161 tmp=g_strdup(homesite);
1162 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1163 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1165 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1166 gtk_widget_show(image);
1167 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1168 //g_object_unref(G_OBJECT(image));
1169 gtk_widget_show(menu_item);
1170 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1171 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1173 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1174 gtk_widget_show(menu_item);
1175 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1176 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1177 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1178 gtk_widget_show(menu_item);
1179 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1180 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1181 gtk_widget_show(menu);
1185 static GtkStatusIcon *icon=NULL;
1187 static void handle_icon_click() {
1188 GtkWidget *mw=linphone_gtk_get_main_window();
1189 if (!gtk_window_is_active((GtkWindow*)mw)) {
1190 linphone_gtk_show_main_window();
1192 gtk_widget_hide(mw);
1196 static void linphone_gtk_init_status_icon(){
1197 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1198 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1199 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1200 GtkWidget *menu=create_icon_menu();
1202 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1203 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1204 gtk_status_icon_set_name(icon,title);
1205 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1206 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1207 gtk_status_icon_set_tooltip(icon,title);
1208 gtk_status_icon_set_visible(icon,TRUE);
1209 g_object_set_data(G_OBJECT(icon),"menu",menu);
1210 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1211 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1212 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1213 pbuf=create_pixbuf(call_icon_path);
1214 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1217 static gboolean do_icon_blink(GtkStatusIcon *gi){
1218 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1219 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1220 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1221 if (cur_icon==call_icon){
1222 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1224 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1229 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1231 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1232 if (val && tout==0){
1233 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1234 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1235 }else if (!val && tout!=0){
1236 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1237 g_source_remove(tout);
1238 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1239 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1243 static void init_identity_combo(GtkComboBox *box){
1244 GtkListStore *store;
1245 GtkCellRenderer *r1,*r2;
1246 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1247 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1248 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1249 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1250 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1251 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1252 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1253 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1256 void linphone_gtk_load_identities(void){
1258 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1260 LinphoneProxyConfig *def=NULL;
1262 GtkListStore *store;
1265 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1266 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1267 /* model is empty, this is the first time we go here */
1268 init_identity_combo(box);
1269 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1271 gtk_list_store_clear(store);
1272 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1273 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1274 gtk_list_store_append(store,&iter);
1275 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1276 g_free(def_identity);
1277 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1279 elem=ms_list_next(elem),i++){
1280 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1281 gtk_list_store_append(store,&iter);
1282 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1283 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1289 gtk_combo_box_set_active(box,def_index);
1292 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1293 const char *label=gtk_button_get_label(button);
1294 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1296 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1297 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1298 if (linphone_core_in_call(linphone_gtk_get_core())){
1299 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1303 static void linphone_gtk_dtmf_released(GtkButton *button){
1304 linphone_core_stop_dtmf (linphone_gtk_get_core());
1307 static void linphone_gtk_connect_digits(void){
1308 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1309 GList *children=gtk_container_get_children(cont);
1311 for(elem=children;elem!=NULL;elem=elem->next){
1312 GtkButton *button=GTK_BUTTON(elem->data);
1313 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1314 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1318 static void linphone_gtk_check_menu_items(void){
1319 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1320 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1321 GtkWidget *selfview_item=linphone_gtk_get_widget(
1322 linphone_gtk_get_main_window(),"selfview_item");
1323 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1324 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1325 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1326 gtk_widget_set_sensitive(selfview_item,video_enabled);
1329 static gboolean linphone_gtk_can_manage_accounts(){
1330 LinphoneCore *lc=linphone_gtk_get_core();
1332 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1333 SipSetup *ss=(SipSetup*)elem->data;
1334 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1341 static void linphone_gtk_configure_main_window(){
1342 static gboolean config_loaded=FALSE;
1343 static const char *title;
1344 static const char *home;
1345 static const char *start_call_icon;
1346 static const char *add_call_icon;
1347 static const char *stop_call_icon;
1348 static const char *search_icon;
1349 static gboolean update_check_menu;
1350 static gboolean buttons_have_borders;
1351 static gboolean show_abcd;
1352 GtkWidget *w=linphone_gtk_get_main_window();
1353 if (!config_loaded){
1354 title=linphone_gtk_get_ui_config("title","Linphone");
1355 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1356 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1357 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1358 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1359 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1360 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1361 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1362 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1365 linphone_gtk_configure_window(w,"main_window");
1367 gtk_window_set_title(GTK_WINDOW(w),title);
1369 if (start_call_icon){
1370 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1371 create_pixmap (start_call_icon));
1372 if (!buttons_have_borders)
1373 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1376 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1377 create_pixmap (add_call_icon));
1378 if (!buttons_have_borders)
1379 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1381 if (stop_call_icon){
1382 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1383 create_pixmap (stop_call_icon));
1384 if (!buttons_have_borders)
1385 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1388 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1389 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1390 g_object_unref(G_OBJECT(pbuf));
1394 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1396 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1400 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1402 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1403 g_object_unref(G_OBJECT(pbuf));
1408 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1410 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1411 g_object_unref(G_OBJECT(pbuf));
1414 if (linphone_gtk_can_manage_accounts())
1415 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1416 if (update_check_menu){
1417 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1420 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1421 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1422 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1423 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1424 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1428 void linphone_gtk_manage_login(void){
1429 LinphoneCore *lc=linphone_gtk_get_core();
1430 LinphoneProxyConfig *cfg=NULL;
1431 linphone_core_get_default_proxy(lc,&cfg);
1433 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1434 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1435 linphone_gtk_show_login_frame(cfg);
1441 gboolean linphone_gtk_close(GtkWidget *mw){
1442 /*shutdown calls if any*/
1443 LinphoneCore *lc=linphone_gtk_get_core();
1444 if (linphone_core_in_call(lc)){
1445 linphone_core_terminate_all_calls(lc);
1447 linphone_core_enable_video_preview(lc,FALSE);
1448 #ifdef __APPLE__ /*until with have a better option*/
1449 gtk_window_iconify(GTK_WINDOW(mw));
1451 gtk_widget_hide(mw);
1457 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1458 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1459 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1461 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1462 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1469 static void linphone_gtk_init_main_window(){
1470 GtkWidget *main_window;
1472 linphone_gtk_configure_main_window();
1473 linphone_gtk_manage_login();
1475 linphone_gtk_load_identities();
1476 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1477 linphone_gtk_show_friends();
1478 linphone_gtk_connect_digits();
1479 main_window=linphone_gtk_get_main_window();
1480 linphone_gtk_call_log_update(main_window);
1482 linphone_gtk_update_call_buttons (NULL);
1483 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1484 g_signal_connect (G_OBJECT (main_window), "delete-event",
1485 G_CALLBACK (linphone_gtk_close), main_window);
1488 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1489 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1490 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1491 gtk_widget_hide(menubar);
1492 gtk_osxapplication_ready(theMacApp);
1494 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1496 linphone_gtk_check_menu_items();
1500 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1502 const char *lname="undef";
1504 #if defined(__linux) || defined(__APPLE__)
1505 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1524 g_error("Bad level !");
1526 #if defined(__linux) || defined(__APPLE__)
1528 msg=g_strdup_vprintf(fmt,cap);
1531 msg=g_strdup_vprintf(fmt,args);
1533 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1536 linphone_gtk_log_push(lev,fmt,args);
1540 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1541 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1542 linphone_gtk_get_main_window(), "uribar"));
1544 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1546 gtk_entry_set_text(uri_bar, refer_to);
1547 linphone_gtk_start_call(linphone_gtk_get_main_window());
1550 static void linphone_gtk_check_soundcards(){
1551 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1552 if (devices==NULL || devices[0]==NULL){
1553 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1554 _("No sound cards have been detected on this computer.\n"
1555 "You won't be able to send or receive audio calls."));
1559 static void linphone_gtk_quit(void){
1560 linphone_gtk_uninit_instance();
1561 linphone_gtk_destroy_log_window();
1562 linphone_core_destroy(the_core);
1563 linphone_gtk_log_uninit();
1567 gdk_threads_leave();
1572 This is not the correct way to implement block termination.
1573 The good way would be to call gtk_main_quit(), and return TRUE.
1574 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1575 As a result the program cannot exit at all.
1576 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1578 static gboolean on_block_termination(void){
1580 linphone_gtk_quit();
1585 int main(int argc, char *argv[]){
1589 const char *config_file;
1590 const char *factory_config_file;
1592 GtkSettings *settings;
1594 const char *app_name="Linphone";
1596 g_thread_init(NULL);
1599 progpath = strdup(argv[0]);
1601 config_file=linphone_gtk_get_config_file();
1605 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1606 if ((lang=getenv("LANG"))!=NULL){
1609 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1615 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1618 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1621 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1624 setenv("LANG",lang,1);
1629 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1630 if (p==NULL) perror("bindtextdomain failed");
1631 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1632 textdomain (GETTEXT_PACKAGE);
1634 g_message("NLS disabled.\n");
1637 gtk_rc_add_default_file("./gtkrc");
1639 gdk_threads_enter();
1641 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1642 linphone_options,NULL,NULL)){
1643 gdk_threads_leave();
1647 settings=gtk_settings_get_default();
1648 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1649 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1650 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1651 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1653 if (workingdir!=NULL){
1654 if (chdir(workingdir)==-1){
1655 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1659 /* Now, look for the factory configuration file, we do it this late
1660 since we want to have had time to change directory and to parse
1661 the options, in case we needed to access the working directory */
1662 factory_config_file = linphone_gtk_get_factory_config_file();
1664 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1665 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1666 g_warning("This instance is going to exit now.");
1667 gdk_threads_leave();
1671 add_pixmap_directory("pixmaps");
1672 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1675 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1676 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1677 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1678 /*never block termination:*/
1679 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1682 the_ui=linphone_gtk_create_window("main");
1684 linphone_gtk_create_log_window();
1685 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1687 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1689 g_set_application_name(app_name);
1690 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1691 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1693 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1694 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1695 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1696 linphone_gtk_init_main_window();
1697 #ifndef HAVE_GTK_OSX
1698 linphone_gtk_init_status_icon();
1701 linphone_gtk_show_main_window();
1702 linphone_gtk_check_soundcards();
1704 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1705 linphone_gtk_check_for_new_version();
1708 linphone_gtk_quit();
1709 #ifndef HAVE_GTK_OSX
1710 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1711 gtk_status_icon_set_visible(icon,FALSE);