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();
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){
740 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
742 linphone_core_terminate_call(linphone_gtk_get_core(),call);
745 void linphone_gtk_decline_clicked(GtkWidget *button){
746 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
748 linphone_core_terminate_call(linphone_gtk_get_core(),call);
751 void linphone_gtk_answer_clicked(GtkWidget *button){
752 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
754 linphone_core_pause_all_calls(linphone_gtk_get_core());
755 linphone_core_accept_call(linphone_gtk_get_core(),call);
756 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
760 void linphone_gtk_enable_video(GtkWidget *w){
761 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
762 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
763 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
764 gtk_widget_set_sensitive(selfview_item,val);
766 linphone_core_enable_video_preview(linphone_gtk_get_core(),
767 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
769 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
773 void linphone_gtk_enable_self_view(GtkWidget *w){
774 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
775 LinphoneCore *lc=linphone_gtk_get_core();
776 linphone_core_enable_video_preview(lc,val);
777 linphone_core_enable_self_view(lc,val);
778 linphone_gtk_set_ui_config_int("videoselfview",val);
781 void linphone_gtk_used_identity_changed(GtkWidget *w){
782 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
783 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
784 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
785 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
786 linphone_gtk_show_directory_search();
788 if (sel) g_free(sel);
791 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
792 linphone_gtk_show_friends();
795 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
797 case GTK_RESPONSE_YES:
798 linphone_gtk_show_contact(lf);
801 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
803 gtk_widget_destroy(dialog);
806 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
809 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
810 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
814 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);
815 dialog = gtk_message_dialog_new (
816 GTK_WINDOW(linphone_gtk_get_main_window()),
817 GTK_DIALOG_DESTROY_WITH_PARENT,
818 GTK_MESSAGE_QUESTION,
823 g_signal_connect(G_OBJECT (dialog), "response",
824 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
825 /* actually show the box */
826 gtk_widget_show(dialog);
829 typedef struct _AuthTimeout{
834 static void auth_timeout_clean(AuthTimeout *tout){
838 static gboolean auth_timeout_destroy(AuthTimeout *tout){
840 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
841 gtk_widget_destroy(tout->w);
847 static AuthTimeout * auth_timeout_new(GtkWidget *w){
848 AuthTimeout *tout=g_new(AuthTimeout,1);
850 /*so that the timeout no more references the widget when it is destroyed:*/
851 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
852 /*so that the widget is automatically destroyed after some time */
853 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
857 void linphone_gtk_password_cancel(GtkWidget *w){
858 LinphoneAuthInfo *info;
859 GtkWidget *window=gtk_widget_get_toplevel(w);
860 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
861 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
862 gtk_widget_destroy(window);
865 void linphone_gtk_password_ok(GtkWidget *w){
867 GtkWidget *window=gtk_widget_get_toplevel(w);
868 LinphoneAuthInfo *info;
869 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
870 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
871 entry=linphone_gtk_get_widget(window,"password_entry");
872 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
873 linphone_auth_info_set_userid(info,
874 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
875 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
876 gtk_widget_destroy(window);
879 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
880 GtkWidget *w=linphone_gtk_create_window("password");
881 GtkWidget *label=linphone_gtk_get_widget(w,"message");
882 LinphoneAuthInfo *info;
884 GtkWidget *mw=linphone_gtk_get_main_window();
886 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
887 /*don't prompt for authentication when login frame is visible*/
888 linphone_core_abort_authentication(lc,NULL);
892 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
894 gtk_label_set_markup(GTK_LABEL(label),msg);
896 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
897 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
898 g_object_set_data(G_OBJECT(w),"auth_info",info);
899 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
904 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
905 GtkWidget *w=linphone_gtk_get_main_window();
906 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
907 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
908 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
912 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
913 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
916 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
917 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
920 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
922 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
923 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
926 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
927 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
928 if (w) linphone_gtk_call_log_update(w);
929 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
933 static bool_t notify_actions_supported() {
934 bool_t accepts_actions = FALSE;
935 GList *capabilities = notify_get_server_caps();
937 if(capabilities != NULL) {
938 for(c = capabilities; c != NULL; c = c->next) {
939 if(strcmp((char*)c->data, "actions") == 0 ) {
940 accepts_actions = TRUE;
944 g_list_foreach(capabilities, (GFunc)g_free, NULL);
945 g_list_free(capabilities);
947 return accepts_actions;
950 static NotifyNotification* build_notification(const char *title, const char *body){
951 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
958 static void show_notification(NotifyNotification* n){
959 if (n && !notify_notification_show(n,NULL))
960 ms_error("Failed to send notification.");
963 static void make_notification(const char *title, const char *body){
964 show_notification(build_notification(title,body));
969 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
971 if (!notify_is_initted())
972 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
976 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
981 ms_error("Failed to send notification.");
983 linphone_gtk_show_main_window();
985 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
988 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
989 NotifyNotification *n;
990 switch(linphone_call_get_state(call)){
991 case LinphoneCallError:
992 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
994 case LinphoneCallEnd:
995 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
997 case LinphoneCallIncomingReceived:
998 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
999 if (notify_actions_supported()) {
1000 notify_notification_add_action (n,"answer", _("Answer"),
1001 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1002 notify_notification_add_action (n,"decline",_("Decline"),
1003 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1005 show_notification(n);
1007 case LinphoneCallPausedByRemote:
1008 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1013 if (body) g_free(body);
1014 if (remote) g_free(remote);
1019 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1021 case LinphoneCallOutgoingInit:
1022 linphone_gtk_create_in_call_view (call);
1024 case LinphoneCallOutgoingProgress:
1025 linphone_gtk_in_call_view_set_calling (call);
1027 case LinphoneCallStreamsRunning:
1028 linphone_gtk_in_call_view_set_in_call(call);
1030 case LinphoneCallError:
1031 linphone_gtk_in_call_view_terminate (call,msg);
1033 case LinphoneCallEnd:
1034 linphone_gtk_in_call_view_terminate(call,NULL);
1035 linphone_gtk_status_icon_set_blinking(FALSE);
1037 case LinphoneCallIncomingReceived:
1038 linphone_gtk_create_in_call_view (call);
1039 linphone_gtk_in_call_view_set_incoming(call);
1040 linphone_gtk_status_icon_set_blinking(TRUE);
1042 linphone_call_ref(call);
1043 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1046 case LinphoneCallResuming:
1047 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1048 linphone_gtk_in_call_view_set_in_call (call);
1050 case LinphoneCallPausing:
1051 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1052 case LinphoneCallPausedByRemote:
1053 linphone_gtk_in_call_view_set_paused(call);
1055 case LinphoneCallConnected:
1056 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1057 linphone_gtk_status_icon_set_blinking(FALSE);
1062 linphone_gtk_notify(call, msg);
1063 linphone_gtk_update_call_buttons (call);
1066 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1067 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1068 GtkTreeModel *model=gtk_combo_box_get_model(box);
1070 gboolean found=FALSE;
1071 const char *stock_id=NULL;
1073 if (gtk_tree_model_get_iter_first(model,&iter)){
1076 gtk_tree_model_get(model,&iter,2,&p,-1);
1081 }while(gtk_tree_model_iter_next(model,&iter));
1084 g_warning("Could not find proxy config in combo box of identities.");
1088 case LinphoneRegistrationOk:
1089 stock_id=GTK_STOCK_YES;
1091 case LinphoneRegistrationProgress:
1092 stock_id=GTK_STOCK_REFRESH;
1094 case LinphoneRegistrationCleared:
1097 case LinphoneRegistrationFailed:
1098 stock_id=GTK_STOCK_DIALOG_WARNING;
1103 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1106 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1107 LinphoneRegistrationState rs, const char *msg){
1109 case LinphoneRegistrationOk:
1111 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1112 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1113 linphone_gtk_exit_login_frame();
1120 update_registration_status(cfg,rs);
1124 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1125 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1126 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1129 void linphone_gtk_open_browser(const char *url){
1130 /*in gtk 2.16, gtk_show_uri does not work...*/
1132 #if GTK_CHECK_VERSION(2,18,3)
1133 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1136 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1137 g_spawn_command_line_async(cl,NULL);
1140 ShellExecute(0,"open",url,NULL,NULL,1);
1144 void linphone_gtk_link_to_website(GtkWidget *item){
1145 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1146 linphone_gtk_open_browser(home);
1149 static GtkWidget *create_icon_menu(){
1150 GtkWidget *menu=gtk_menu_new();
1151 GtkWidget *menu_item;
1154 const gchar *homesite;
1156 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1157 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1158 tmp=g_strdup(homesite);
1159 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1160 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1162 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1163 gtk_widget_show(image);
1164 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1165 //g_object_unref(G_OBJECT(image));
1166 gtk_widget_show(menu_item);
1167 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1168 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1170 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1171 gtk_widget_show(menu_item);
1172 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1173 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1174 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1175 gtk_widget_show(menu_item);
1176 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1177 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1178 gtk_widget_show(menu);
1182 static GtkStatusIcon *icon=NULL;
1184 static void handle_icon_click() {
1185 GtkWidget *mw=linphone_gtk_get_main_window();
1186 if (!gtk_window_is_active((GtkWindow*)mw)) {
1187 linphone_gtk_show_main_window();
1189 gtk_widget_hide(mw);
1193 static void linphone_gtk_init_status_icon(){
1194 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1195 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1196 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1197 GtkWidget *menu=create_icon_menu();
1199 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1200 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1201 gtk_status_icon_set_name(icon,title);
1202 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1203 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1204 gtk_status_icon_set_tooltip(icon,title);
1205 gtk_status_icon_set_visible(icon,TRUE);
1206 g_object_set_data(G_OBJECT(icon),"menu",menu);
1207 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1208 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1209 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1210 pbuf=create_pixbuf(call_icon_path);
1211 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1214 static gboolean do_icon_blink(GtkStatusIcon *gi){
1215 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1216 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1217 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1218 if (cur_icon==call_icon){
1219 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1221 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1226 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1228 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1229 if (val && tout==0){
1230 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1231 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1232 }else if (!val && tout!=0){
1233 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1234 g_source_remove(tout);
1235 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1236 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1240 static void init_identity_combo(GtkComboBox *box){
1241 GtkListStore *store;
1242 GtkCellRenderer *r1,*r2;
1243 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1244 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1245 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1246 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1247 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1248 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1249 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1250 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1253 void linphone_gtk_load_identities(void){
1255 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1257 LinphoneProxyConfig *def=NULL;
1259 GtkListStore *store;
1262 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1263 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1264 /* model is empty, this is the first time we go here */
1265 init_identity_combo(box);
1266 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1268 gtk_list_store_clear(store);
1269 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1270 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1271 gtk_list_store_append(store,&iter);
1272 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1273 g_free(def_identity);
1274 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1276 elem=ms_list_next(elem),i++){
1277 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1278 gtk_list_store_append(store,&iter);
1279 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1280 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1286 gtk_combo_box_set_active(box,def_index);
1289 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1290 const char *label=gtk_button_get_label(button);
1291 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1293 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1294 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1295 if (linphone_core_in_call(linphone_gtk_get_core())){
1296 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1300 static void linphone_gtk_dtmf_released(GtkButton *button){
1301 linphone_core_stop_dtmf (linphone_gtk_get_core());
1304 static void linphone_gtk_connect_digits(void){
1305 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1306 GList *children=gtk_container_get_children(cont);
1308 for(elem=children;elem!=NULL;elem=elem->next){
1309 GtkButton *button=GTK_BUTTON(elem->data);
1310 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1311 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1315 static void linphone_gtk_check_menu_items(void){
1316 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1317 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1318 GtkWidget *selfview_item=linphone_gtk_get_widget(
1319 linphone_gtk_get_main_window(),"selfview_item");
1320 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1321 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1322 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1323 gtk_widget_set_sensitive(selfview_item,video_enabled);
1326 static gboolean linphone_gtk_can_manage_accounts(){
1327 LinphoneCore *lc=linphone_gtk_get_core();
1329 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1330 SipSetup *ss=(SipSetup*)elem->data;
1331 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1338 static void linphone_gtk_configure_main_window(){
1339 static gboolean config_loaded=FALSE;
1340 static const char *title;
1341 static const char *home;
1342 static const char *start_call_icon;
1343 static const char *add_call_icon;
1344 static const char *stop_call_icon;
1345 static const char *search_icon;
1346 static gboolean update_check_menu;
1347 static gboolean buttons_have_borders;
1348 static gboolean show_abcd;
1349 GtkWidget *w=linphone_gtk_get_main_window();
1350 if (!config_loaded){
1351 title=linphone_gtk_get_ui_config("title","Linphone");
1352 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1353 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1354 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1355 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1356 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1357 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1358 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1359 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1362 linphone_gtk_configure_window(w,"main_window");
1364 gtk_window_set_title(GTK_WINDOW(w),title);
1366 if (start_call_icon){
1367 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1368 create_pixmap (start_call_icon));
1369 if (!buttons_have_borders)
1370 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1373 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1374 create_pixmap (add_call_icon));
1375 if (!buttons_have_borders)
1376 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1378 if (stop_call_icon){
1379 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1380 create_pixmap (stop_call_icon));
1381 if (!buttons_have_borders)
1382 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1385 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1386 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1387 g_object_unref(G_OBJECT(pbuf));
1391 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1393 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1397 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1399 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1400 g_object_unref(G_OBJECT(pbuf));
1405 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1407 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1408 g_object_unref(G_OBJECT(pbuf));
1411 if (linphone_gtk_can_manage_accounts())
1412 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1413 if (update_check_menu){
1414 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1417 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1418 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1419 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1420 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1421 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1425 void linphone_gtk_manage_login(void){
1426 LinphoneCore *lc=linphone_gtk_get_core();
1427 LinphoneProxyConfig *cfg=NULL;
1428 linphone_core_get_default_proxy(lc,&cfg);
1430 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1431 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1432 linphone_gtk_show_login_frame(cfg);
1438 gboolean linphone_gtk_close(GtkWidget *mw){
1439 /*shutdown calls if any*/
1440 LinphoneCore *lc=linphone_gtk_get_core();
1441 if (linphone_core_in_call(lc)){
1442 linphone_core_terminate_all_calls(lc);
1444 linphone_core_enable_video_preview(lc,FALSE);
1445 #ifdef __APPLE__ /*until with have a better option*/
1446 gtk_window_iconify(GTK_WINDOW(mw));
1448 gtk_widget_hide(mw);
1454 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1455 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1456 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1458 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1459 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1466 static void linphone_gtk_init_main_window(){
1467 GtkWidget *main_window;
1469 linphone_gtk_configure_main_window();
1470 linphone_gtk_manage_login();
1472 linphone_gtk_load_identities();
1473 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1474 linphone_gtk_show_friends();
1475 linphone_gtk_connect_digits();
1476 main_window=linphone_gtk_get_main_window();
1477 linphone_gtk_call_log_update(main_window);
1479 linphone_gtk_update_call_buttons (NULL);
1480 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1481 g_signal_connect (G_OBJECT (main_window), "delete-event",
1482 G_CALLBACK (linphone_gtk_close), main_window);
1485 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1486 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1487 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1488 gtk_widget_hide(menubar);
1489 gtk_osxapplication_ready(theMacApp);
1491 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1493 linphone_gtk_check_menu_items();
1497 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1499 const char *lname="undef";
1501 #if defined(__linux) || defined(__APPLE__)
1502 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1521 g_error("Bad level !");
1523 #if defined(__linux) || defined(__APPLE__)
1525 msg=g_strdup_vprintf(fmt,cap);
1528 msg=g_strdup_vprintf(fmt,args);
1530 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1533 linphone_gtk_log_push(lev,fmt,args);
1537 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1538 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1539 linphone_gtk_get_main_window(), "uribar"));
1541 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1543 gtk_entry_set_text(uri_bar, refer_to);
1544 linphone_gtk_start_call(linphone_gtk_get_main_window());
1547 static void linphone_gtk_check_soundcards(){
1548 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1549 if (devices==NULL || devices[0]==NULL){
1550 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1551 _("No sound cards have been detected on this computer.\n"
1552 "You won't be able to send or receive audio calls."));
1556 static void linphone_gtk_quit(void){
1557 linphone_gtk_uninit_instance();
1558 linphone_gtk_destroy_log_window();
1559 linphone_core_destroy(the_core);
1560 linphone_gtk_log_uninit();
1564 gdk_threads_leave();
1569 This is not the correct way to implement block termination.
1570 The good way would be to call gtk_main_quit(), and return TRUE.
1571 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1572 As a result the program cannot exit at all.
1573 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1575 static gboolean on_block_termination(void){
1577 linphone_gtk_quit();
1582 int main(int argc, char *argv[]){
1586 const char *config_file;
1587 const char *factory_config_file;
1589 GtkSettings *settings;
1591 const char *app_name="Linphone";
1593 g_thread_init(NULL);
1596 progpath = strdup(argv[0]);
1598 config_file=linphone_gtk_get_config_file();
1602 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1603 if ((lang=getenv("LANG"))!=NULL){
1606 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1612 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1615 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1618 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1621 setenv("LANG",lang,1);
1626 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1627 if (p==NULL) perror("bindtextdomain failed");
1628 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1629 textdomain (GETTEXT_PACKAGE);
1631 g_message("NLS disabled.\n");
1634 gtk_rc_add_default_file("./gtkrc");
1636 gdk_threads_enter();
1638 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1639 linphone_options,NULL,NULL)){
1640 gdk_threads_leave();
1644 settings=gtk_settings_get_default();
1645 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1646 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1647 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1648 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1650 if (workingdir!=NULL){
1651 if (chdir(workingdir)==-1){
1652 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1656 /* Now, look for the factory configuration file, we do it this late
1657 since we want to have had time to change directory and to parse
1658 the options, in case we needed to access the working directory */
1659 factory_config_file = linphone_gtk_get_factory_config_file();
1661 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1662 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1663 g_warning("This instance is going to exit now.");
1664 gdk_threads_leave();
1668 add_pixmap_directory("pixmaps");
1669 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1672 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1673 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1674 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1675 /*never block termination:*/
1676 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1679 the_ui=linphone_gtk_create_window("main");
1681 linphone_gtk_create_log_window();
1682 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1684 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1686 g_set_application_name(app_name);
1687 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1688 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1690 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1691 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1692 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1693 linphone_gtk_init_main_window();
1694 #ifndef HAVE_GTK_OSX
1695 linphone_gtk_init_status_icon();
1698 linphone_gtk_show_main_window();
1699 linphone_gtk_check_soundcards();
1701 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1702 linphone_gtk_check_for_new_version();
1705 linphone_gtk_quit();
1706 #ifndef HAVE_GTK_OSX
1707 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1708 gtk_status_icon_set_visible(icon,FALSE);