2 linphone, gtk-glade interface.
3 Copyright (C) 2008 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define VIDEOSELFVIEW_DEFAULT 1
27 #include <sys/types.h>
32 #include <gtkosxapplication.h>
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53 GtkWidget *the_wizard=NULL;
55 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static 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.notify_presence_recv=linphone_gtk_notify_recv;
214 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
215 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
216 vtable.display_status=linphone_gtk_display_status;
217 vtable.display_message=linphone_gtk_display_message;
218 vtable.display_warning=linphone_gtk_display_warning;
219 vtable.display_url=linphone_gtk_display_url;
220 vtable.call_log_updated=linphone_gtk_call_log_updated;
221 vtable.text_received=linphone_gtk_text_received;
222 vtable.refer_received=linphone_gtk_refer_received;
223 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
225 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
226 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
227 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
232 LinphoneCore *linphone_gtk_get_core(void){
236 GtkWidget *linphone_gtk_get_main_window(){
240 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
241 static const char *icon_path=NULL;
242 static const char *hiddens=NULL;
243 static const char *shown=NULL;
244 static bool_t config_loaded=FALSE;
245 if (linphone_gtk_get_core()==NULL) return;
246 if (config_loaded==FALSE){
247 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
248 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
249 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
253 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
255 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
257 GdkPixbuf *pbuf=create_pixbuf(icon_path);
258 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
259 g_object_unref(G_OBJECT(pbuf));
263 static int get_ui_file(const char *name, char *path, int pathsize){
264 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
265 if (access(path,F_OK)!=0){
266 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
267 if (access(path,F_OK)!=0){
268 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
269 INSTALLED_XML_DIR,name);
276 GtkWidget *linphone_gtk_create_window(const char *window_name){
277 GError* error = NULL;
278 GtkBuilder* builder = gtk_builder_new ();
282 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
284 if (!gtk_builder_add_from_file (builder, path, &error)){
285 g_error("Couldn't load builder file: %s", error->message);
286 g_error_free (error);
289 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
291 g_error("Could not retrieve '%s' window from xml file",window_name);
294 g_object_set_data(G_OBJECT(w),"builder",builder);
295 gtk_builder_connect_signals(builder,w);
296 linphone_gtk_configure_window(w,window_name);
300 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
303 GtkBuilder* builder = gtk_builder_new ();
305 gchar *object_ids[2];
306 object_ids[0]=g_strdup(widget_name);
309 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
310 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
311 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
312 g_error_free (error);
313 g_free(object_ids[0]);
316 g_free(object_ids[0]);
317 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
319 g_error("Could not retrieve '%s' window from xml file",widget_name);
322 g_object_set_data(G_OBJECT(w),"builder",builder);
323 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
324 gtk_builder_connect_signals(builder,w);
328 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
329 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
332 g_error("Fail to retrieve builder from window !");
335 w=gtk_builder_get_object(builder,name);
337 g_error("No widget named %s found in xml interface.",name);
339 return GTK_WIDGET(w);
343 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
345 GtkWidget *main_window=linphone_gtk_get_main_window();
347 gtk_widget_show(main_window);
348 if (type==GTK_MESSAGE_QUESTION)
350 /* draw a question box. link to dialog_click callback */
351 dialog = gtk_message_dialog_new (
352 GTK_WINDOW(main_window),
353 GTK_DIALOG_DESTROY_WITH_PARENT,
354 GTK_MESSAGE_QUESTION,
357 (const gchar*)message);
358 /* connect to some callback : REVISIT */
360 g_signal_connect_swapped (G_OBJECT (dialog), "response",
361 G_CALLBACK (dialog_click),
364 /* actually show the box */
365 gtk_widget_show(dialog);
369 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
370 GTK_DIALOG_DESTROY_WITH_PARENT,
374 (const gchar*)message);
375 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
376 g_signal_connect_swapped (G_OBJECT (dialog), "response",
377 G_CALLBACK (gtk_widget_destroy),
379 gtk_widget_show(dialog);
383 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
384 if (id==GTK_RESPONSE_CANCEL){
385 gtk_widget_destroy(GTK_WIDGET(dialog));
389 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
390 g_message("About url clicked");
391 linphone_gtk_open_browser(url);
394 void linphone_gtk_show_about(){
395 struct stat filestat;
396 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
399 GdkPixbuf *logo=create_pixbuf(
400 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
401 static const char *defcfg="defcfg";
403 about=linphone_gtk_create_window("about");
404 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
405 memset(&filestat,0,sizeof(filestat));
406 if (stat(license_file,&filestat)!=0){
407 license_file="COPYING";
408 stat(license_file,&filestat);
410 if (filestat.st_size>0){
411 char *license=g_malloc(filestat.st_size+1);
412 FILE *f=fopen(license_file,"r");
413 if (f && fread(license,filestat.st_size,1,f)==1){
414 license[filestat.st_size]='\0';
415 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
419 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
420 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
421 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
422 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
423 tmp=linphone_gtk_get_ui_config("artists",defcfg);
428 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
430 tmp=linphone_gtk_get_ui_config("translators",defcfg);
432 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
433 tmp=linphone_gtk_get_ui_config("comments",defcfg);
435 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
436 gtk_widget_show(about);
439 static void set_video_window_decorations(GdkWindow *w){
440 const char *title=linphone_gtk_get_ui_config("title","Linphone");
441 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
442 char video_title[256];
443 GdkPixbuf *pbuf=create_pixbuf(icon_path);
444 if (!linphone_core_in_call(linphone_gtk_get_core())){
445 snprintf(video_title,sizeof(video_title),"%s video",title);
446 /* When not in call, treat the video as a normal window */
447 gdk_window_set_keep_above(w, FALSE);
449 LinphoneAddress *uri =
450 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
453 linphone_address_clean(uri);
454 if (linphone_address_get_display_name(uri)!=NULL){
455 display_name=ms_strdup(linphone_address_get_display_name(uri));
457 display_name=linphone_address_as_string(uri);
459 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
460 linphone_address_destroy(uri);
461 ms_free(display_name);
463 /* During calls, bring up the video window, arrange so that
464 it is above all the other windows */
465 gdk_window_deiconify(w);
466 gdk_window_set_keep_above(w,TRUE);
467 /* Maybe we should have the following, but then we want to
468 have a timer that turns it off after a little while. */
469 /* gdk_window_set_urgency_hint(w,TRUE); */
471 gdk_window_set_title(w,video_title);
472 /* Refrain the video window to be closed at all times. */
473 gdk_window_set_functions(w,
474 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
475 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
478 l=g_list_append(l,pbuf);
479 gdk_window_set_icon_list(w,l);
481 g_object_unref(G_OBJECT(pbuf));
485 static gboolean video_needs_update=FALSE;
487 static void update_video_title(){
488 video_needs_update=TRUE;
491 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
492 static gboolean first_time=TRUE;
494 static unsigned long previd=0;
495 static unsigned long preview_previd=0;
496 static gboolean in_iterate=FALSE;
499 if (in_iterate) return TRUE;
501 linphone_core_iterate(lc);
503 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
504 linphone_gtk_show_directory_search();
508 id=linphone_core_get_native_video_window_id(lc);
509 if (id!=previd || video_needs_update){
513 ms_message("Updating window decorations");
515 w=gdk_window_foreign_new(id);
517 w=gdk_window_foreign_new((HANDLE)id);
520 set_video_window_decorations(w);
521 g_object_unref(G_OBJECT(w));
523 else ms_error("gdk_window_foreign_new() failed");
524 if (video_needs_update) video_needs_update=FALSE;
527 id=linphone_core_get_native_preview_window_id (lc);
528 if (id!=preview_previd ){
532 ms_message("Updating window decorations for preview");
534 w=gdk_window_foreign_new(id);
536 w=gdk_window_foreign_new((HANDLE)id);
539 set_video_window_decorations(w);
540 g_object_unref(G_OBJECT(w));
542 else ms_error("gdk_window_foreign_new() failed");
543 if (video_needs_update) video_needs_update=FALSE;
546 if (addr_to_call!=NULL){
547 /*make sure we are not showing the login screen*/
548 GtkWidget *mw=linphone_gtk_get_main_window();
549 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
550 if (!GTK_WIDGET_VISIBLE(login_frame)){
551 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
552 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
554 linphone_gtk_start_call(uri_bar);
561 static void load_uri_history(){
562 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
565 GtkEntryCompletion *gep=gtk_entry_completion_new();
566 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
569 snprintf(key,sizeof(key),"uri%i",i);
570 uri=linphone_gtk_get_ui_config(key,NULL);
573 gtk_list_store_append(model,&iter);
574 gtk_list_store_set(model,&iter,0,uri,-1);
575 if (i==0) gtk_entry_set_text(uribar,uri);
579 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
580 gtk_entry_completion_set_text_column(gep,0);
581 gtk_entry_set_completion(uribar,gep);
584 static void save_uri_history(){
585 LinphoneCore *lc=linphone_gtk_get_core();
586 LpConfig *cfg=linphone_core_get_config(lc);
587 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
592 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
594 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
596 gtk_tree_model_get(model,&iter,0,&uri,-1);
598 snprintf(key,sizeof(key),"uri%i",i);
599 lp_config_set_string(cfg,"GtkUi",key,uri);
604 }while(gtk_tree_model_iter_next(model,&iter));
608 static void completion_add_text(GtkEntry *entry, const char *text){
610 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
612 if (gtk_tree_model_get_iter_first(model,&iter)){
615 gtk_tree_model_get(model,&iter,0,&uri,-1);
617 if (strcmp(uri,text)==0) {
619 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
625 }while (gtk_tree_model_iter_next(model,&iter));
627 /* and prepend it on top of the list */
628 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
629 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
634 void linphone_gtk_show_main_window(){
635 GtkWidget *w=linphone_gtk_get_main_window();
636 LinphoneCore *lc=linphone_gtk_get_core();
637 if (linphone_core_video_enabled(lc)){
638 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
639 VIDEOSELFVIEW_DEFAULT));
642 gtk_window_present(GTK_WINDOW(w));
645 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
646 GtkWidget *mw=linphone_gtk_get_main_window();
647 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
648 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
649 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
651 if (linphone_gtk_use_in_call_view() && call)
652 linphone_gtk_in_call_view_terminate(call,error);
653 update_video_title();
656 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
657 LinphoneCore *lc=linphone_gtk_get_core();
658 GtkWidget *mw=linphone_gtk_get_main_window();
659 const MSList *calls=linphone_core_get_calls(lc);
661 bool_t start_active=TRUE;
662 bool_t stop_active=FALSE;
663 bool_t add_call=FALSE;
664 int call_list_size=ms_list_size(calls);
674 button=linphone_gtk_get_widget(mw,"start_call");
675 gtk_widget_set_sensitive(button,start_active);
676 gtk_widget_set_visible(button,!add_call);
678 button=linphone_gtk_get_widget(mw,"add_call");
679 gtk_widget_set_sensitive(button,start_active);
680 gtk_widget_set_visible(button,add_call);
682 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
684 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
685 linphone_gtk_enable_conference_button(lc,call_list_size>1);
686 update_video_title();
689 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
690 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
691 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
692 completion_add_text(GTK_ENTRY(uri_bar),entered);
694 linphone_gtk_call_terminated(NULL,NULL);
699 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
700 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
701 linphone_core_accept_call (linphone_gtk_get_core(),call);
702 linphone_call_unref(call);
708 void linphone_gtk_start_call(GtkWidget *w){
709 LinphoneCore *lc=linphone_gtk_get_core();
711 /*change into in-call mode, then do the work later as it might block a bit */
712 GtkWidget *mw=gtk_widget_get_toplevel(w);
713 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
715 call=linphone_gtk_get_currently_displayed_call(NULL);
716 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
717 linphone_core_accept_call(lc,call);
719 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
720 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
721 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
722 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
727 void linphone_gtk_uri_bar_activate(GtkWidget *w){
728 linphone_gtk_start_call(w);
732 void linphone_gtk_terminate_call(GtkWidget *button){
734 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
736 linphone_core_terminate_call(linphone_gtk_get_core(),call);
738 linphone_core_terminate_conference(linphone_gtk_get_core());
742 void linphone_gtk_decline_clicked(GtkWidget *button){
743 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
745 linphone_core_terminate_call(linphone_gtk_get_core(),call);
748 void linphone_gtk_answer_clicked(GtkWidget *button){
749 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
751 linphone_core_accept_call(linphone_gtk_get_core(),call);
752 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
756 void linphone_gtk_enable_video(GtkWidget *w){
757 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
758 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
759 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
760 gtk_widget_set_sensitive(selfview_item,val);
762 linphone_core_enable_video_preview(linphone_gtk_get_core(),
763 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
765 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
769 void linphone_gtk_enable_self_view(GtkWidget *w){
770 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
771 LinphoneCore *lc=linphone_gtk_get_core();
772 linphone_core_enable_video_preview(lc,val);
773 linphone_core_enable_self_view(lc,val);
774 linphone_gtk_set_ui_config_int("videoselfview",val);
777 void linphone_gtk_used_identity_changed(GtkWidget *w){
778 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
779 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
780 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
781 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
782 linphone_gtk_show_directory_search();
784 if (sel) g_free(sel);
787 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
788 linphone_gtk_show_friends();
791 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
793 case GTK_RESPONSE_YES:
794 linphone_gtk_show_contact(lf);
797 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
799 gtk_widget_destroy(dialog);
802 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
805 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
806 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
810 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);
811 dialog = gtk_message_dialog_new (
812 GTK_WINDOW(linphone_gtk_get_main_window()),
813 GTK_DIALOG_DESTROY_WITH_PARENT,
814 GTK_MESSAGE_QUESTION,
819 g_signal_connect(G_OBJECT (dialog), "response",
820 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
821 /* actually show the box */
822 gtk_widget_show(dialog);
825 typedef struct _AuthTimeout{
830 static void auth_timeout_clean(AuthTimeout *tout){
834 static gboolean auth_timeout_destroy(AuthTimeout *tout){
836 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
837 gtk_widget_destroy(tout->w);
843 static AuthTimeout * auth_timeout_new(GtkWidget *w){
844 AuthTimeout *tout=g_new(AuthTimeout,1);
846 /*so that the timeout no more references the widget when it is destroyed:*/
847 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
848 /*so that the widget is automatically destroyed after some time */
849 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
853 void linphone_gtk_password_cancel(GtkWidget *w){
854 LinphoneAuthInfo *info;
855 GtkWidget *window=gtk_widget_get_toplevel(w);
856 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
857 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
858 gtk_widget_destroy(window);
861 void linphone_gtk_password_ok(GtkWidget *w){
863 GtkWidget *window=gtk_widget_get_toplevel(w);
864 LinphoneAuthInfo *info;
865 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
866 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
867 entry=linphone_gtk_get_widget(window,"password_entry");
868 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
869 linphone_auth_info_set_userid(info,
870 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
871 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
872 gtk_widget_destroy(window);
875 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
876 GtkWidget *w=linphone_gtk_create_window("password");
877 GtkWidget *label=linphone_gtk_get_widget(w,"message");
878 LinphoneAuthInfo *info;
880 GtkWidget *mw=linphone_gtk_get_main_window();
882 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
883 /*don't prompt for authentication when login frame is visible*/
884 linphone_core_abort_authentication(lc,NULL);
888 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
890 gtk_label_set_markup(GTK_LABEL(label),msg);
892 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
893 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
894 g_object_set_data(G_OBJECT(w),"auth_info",info);
895 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
900 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
901 GtkWidget *w=linphone_gtk_get_main_window();
902 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
903 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
904 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
908 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
909 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
912 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
913 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
916 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
918 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
919 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
922 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
923 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
924 if (w) linphone_gtk_call_log_update(w);
925 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
929 static bool_t notify_actions_supported() {
930 bool_t accepts_actions = FALSE;
931 GList *capabilities = notify_get_server_caps();
933 if(capabilities != NULL) {
934 for(c = capabilities; c != NULL; c = c->next) {
935 if(strcmp((char*)c->data, "actions") == 0 ) {
936 accepts_actions = TRUE;
940 g_list_foreach(capabilities, (GFunc)g_free, NULL);
941 g_list_free(capabilities);
943 return accepts_actions;
946 static NotifyNotification* build_notification(const char *title, const char *body){
947 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
954 static void show_notification(NotifyNotification* n){
955 if (n && !notify_notification_show(n,NULL))
956 ms_error("Failed to send notification.");
959 static void make_notification(const char *title, const char *body){
960 show_notification(build_notification(title,body));
965 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
967 if (!notify_is_initted())
968 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
972 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
977 ms_error("Failed to send notification.");
979 linphone_gtk_show_main_window();
981 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
984 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
985 NotifyNotification *n;
986 switch(linphone_call_get_state(call)){
987 case LinphoneCallError:
988 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
990 case LinphoneCallEnd:
991 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
993 case LinphoneCallIncomingReceived:
994 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
995 if (notify_actions_supported()) {
996 notify_notification_add_action (n,"answer", _("Answer"),
997 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
998 notify_notification_add_action (n,"decline",_("Decline"),
999 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1001 show_notification(n);
1003 case LinphoneCallPausedByRemote:
1004 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1009 if (body) g_free(body);
1010 if (remote) g_free(remote);
1015 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1017 case LinphoneCallOutgoingInit:
1018 linphone_gtk_create_in_call_view (call);
1020 case LinphoneCallOutgoingProgress:
1021 linphone_gtk_in_call_view_set_calling (call);
1023 case LinphoneCallStreamsRunning:
1024 linphone_gtk_in_call_view_set_in_call(call);
1026 case LinphoneCallError:
1027 linphone_gtk_in_call_view_terminate (call,msg);
1029 case LinphoneCallEnd:
1030 linphone_gtk_in_call_view_terminate(call,NULL);
1031 linphone_gtk_status_icon_set_blinking(FALSE);
1033 case LinphoneCallIncomingReceived:
1034 linphone_gtk_create_in_call_view(call);
1035 linphone_gtk_in_call_view_set_incoming(call);
1036 linphone_gtk_status_icon_set_blinking(TRUE);
1038 linphone_call_ref(call);
1039 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1042 case LinphoneCallResuming:
1043 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1044 linphone_gtk_in_call_view_set_in_call (call);
1046 case LinphoneCallPausing:
1047 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1048 case LinphoneCallPausedByRemote:
1049 linphone_gtk_in_call_view_set_paused(call);
1051 case LinphoneCallConnected:
1052 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1053 linphone_gtk_status_icon_set_blinking(FALSE);
1058 linphone_gtk_notify(call, msg);
1059 linphone_gtk_update_call_buttons (call);
1062 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1063 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1064 GtkTreeModel *model=gtk_combo_box_get_model(box);
1066 gboolean found=FALSE;
1067 const char *stock_id=NULL;
1069 if (gtk_tree_model_get_iter_first(model,&iter)){
1072 gtk_tree_model_get(model,&iter,2,&p,-1);
1077 }while(gtk_tree_model_iter_next(model,&iter));
1080 g_warning("Could not find proxy config in combo box of identities.");
1084 case LinphoneRegistrationOk:
1085 stock_id=GTK_STOCK_YES;
1087 case LinphoneRegistrationProgress:
1088 stock_id=GTK_STOCK_REFRESH;
1090 case LinphoneRegistrationCleared:
1093 case LinphoneRegistrationFailed:
1094 stock_id=GTK_STOCK_DIALOG_WARNING;
1099 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1102 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1103 LinphoneRegistrationState rs, const char *msg){
1105 case LinphoneRegistrationOk:
1107 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1108 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1109 linphone_gtk_exit_login_frame();
1116 update_registration_status(cfg,rs);
1119 void linphone_gtk_open_browser(const char *url){
1120 /*in gtk 2.16, gtk_show_uri does not work...*/
1122 #if GTK_CHECK_VERSION(2,18,3)
1123 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1126 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1127 g_spawn_command_line_async(cl,NULL);
1130 ShellExecute(0,"open",url,NULL,NULL,1);
1134 void linphone_gtk_link_to_website(GtkWidget *item){
1135 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1136 linphone_gtk_open_browser(home);
1139 #ifndef HAVE_GTK_OSX
1141 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1142 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1143 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1146 static GtkWidget *create_icon_menu(){
1147 GtkWidget *menu=gtk_menu_new();
1148 GtkWidget *menu_item;
1151 const gchar *homesite;
1153 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1154 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1155 tmp=g_strdup(homesite);
1156 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1157 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1159 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1160 gtk_widget_show(image);
1161 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1162 //g_object_unref(G_OBJECT(image));
1163 gtk_widget_show(menu_item);
1164 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1165 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1167 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1168 gtk_widget_show(menu_item);
1169 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1170 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1171 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1172 gtk_widget_show(menu_item);
1173 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1174 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1175 gtk_widget_show(menu);
1179 static GtkStatusIcon *icon=NULL;
1181 static void handle_icon_click() {
1182 GtkWidget *mw=linphone_gtk_get_main_window();
1183 if (!gtk_window_is_active((GtkWindow*)mw)) {
1184 linphone_gtk_show_main_window();
1186 gtk_widget_hide(mw);
1190 static void linphone_gtk_init_status_icon(){
1191 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1192 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1193 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1194 GtkWidget *menu=create_icon_menu();
1196 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1197 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1198 gtk_status_icon_set_name(icon,title);
1199 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1200 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1201 gtk_status_icon_set_tooltip(icon,title);
1202 gtk_status_icon_set_visible(icon,TRUE);
1203 g_object_set_data(G_OBJECT(icon),"menu",menu);
1204 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1205 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1206 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1207 pbuf=create_pixbuf(call_icon_path);
1208 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1211 static gboolean do_icon_blink(GtkStatusIcon *gi){
1212 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1213 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1214 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1215 if (cur_icon==call_icon){
1216 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1218 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1225 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1227 static gint attention_id;
1228 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1230 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1231 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1235 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1236 if (val && tout==0){
1237 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1238 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1239 }else if (!val && tout!=0){
1240 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1241 g_source_remove(tout);
1242 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1243 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1249 static void init_identity_combo(GtkComboBox *box){
1250 GtkListStore *store;
1251 GtkCellRenderer *r1,*r2;
1252 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1253 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1254 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1255 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1256 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1257 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1258 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1259 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1262 void linphone_gtk_load_identities(void){
1264 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1266 LinphoneProxyConfig *def=NULL;
1268 GtkListStore *store;
1271 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1272 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1273 /* model is empty, this is the first time we go here */
1274 init_identity_combo(box);
1275 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1277 gtk_list_store_clear(store);
1278 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1279 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1280 gtk_list_store_append(store,&iter);
1281 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1282 g_free(def_identity);
1283 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1285 elem=ms_list_next(elem),i++){
1286 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1287 gtk_list_store_append(store,&iter);
1288 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1289 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1295 gtk_combo_box_set_active(box,def_index);
1298 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1299 const char *label=gtk_button_get_label(button);
1300 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1302 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1303 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1304 if (linphone_core_in_call(linphone_gtk_get_core())){
1305 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1309 static void linphone_gtk_dtmf_released(GtkButton *button){
1310 linphone_core_stop_dtmf (linphone_gtk_get_core());
1313 static void linphone_gtk_connect_digits(void){
1314 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1315 GList *children=gtk_container_get_children(cont);
1317 for(elem=children;elem!=NULL;elem=elem->next){
1318 GtkButton *button=GTK_BUTTON(elem->data);
1319 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1320 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1324 static void linphone_gtk_check_menu_items(void){
1325 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1326 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1327 GtkWidget *selfview_item=linphone_gtk_get_widget(
1328 linphone_gtk_get_main_window(),"selfview_item");
1329 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1330 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1331 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1332 gtk_widget_set_sensitive(selfview_item,video_enabled);
1335 static gboolean linphone_gtk_can_manage_accounts(){
1336 LinphoneCore *lc=linphone_gtk_get_core();
1338 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1339 SipSetup *ss=(SipSetup*)elem->data;
1340 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1347 static void linphone_gtk_configure_main_window(){
1348 static gboolean config_loaded=FALSE;
1349 static const char *title;
1350 static const char *home;
1351 static const char *start_call_icon;
1352 static const char *add_call_icon;
1353 static const char *stop_call_icon;
1354 static const char *search_icon;
1355 static gboolean update_check_menu;
1356 static gboolean buttons_have_borders;
1357 static gboolean show_abcd;
1358 GtkWidget *w=linphone_gtk_get_main_window();
1359 if (!config_loaded){
1360 title=linphone_gtk_get_ui_config("title","Linphone");
1361 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1362 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1363 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1364 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1365 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1366 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1367 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1368 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1371 linphone_gtk_configure_window(w,"main_window");
1373 gtk_window_set_title(GTK_WINDOW(w),title);
1375 if (start_call_icon){
1376 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1377 create_pixmap (start_call_icon));
1378 if (!buttons_have_borders)
1379 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1382 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1383 create_pixmap (add_call_icon));
1384 if (!buttons_have_borders)
1385 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1387 if (stop_call_icon){
1388 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1389 create_pixmap (stop_call_icon));
1390 if (!buttons_have_borders)
1391 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1394 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1395 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1396 g_object_unref(G_OBJECT(pbuf));
1400 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1402 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1406 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1408 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1409 g_object_unref(G_OBJECT(pbuf));
1414 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1416 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1417 g_object_unref(G_OBJECT(pbuf));
1420 if (linphone_gtk_can_manage_accounts()) {
1421 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1423 if (update_check_menu){
1424 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1427 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1428 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1429 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1430 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1431 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1435 void linphone_gtk_manage_login(void){
1436 LinphoneCore *lc=linphone_gtk_get_core();
1437 LinphoneProxyConfig *cfg=NULL;
1438 linphone_core_get_default_proxy(lc,&cfg);
1440 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1441 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1442 linphone_gtk_show_login_frame(cfg);
1448 gboolean linphone_gtk_close(GtkWidget *mw){
1449 /*shutdown calls if any*/
1450 LinphoneCore *lc=linphone_gtk_get_core();
1451 if (linphone_core_in_call(lc)){
1452 linphone_core_terminate_all_calls(lc);
1454 linphone_core_enable_video_preview(lc,FALSE);
1455 #ifdef __APPLE__ /*until with have a better option*/
1456 gtk_window_iconify(GTK_WINDOW(mw));
1458 gtk_widget_hide(mw);
1464 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1465 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1466 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1468 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1469 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1476 static void linphone_gtk_init_main_window(){
1477 GtkWidget *main_window;
1479 linphone_gtk_configure_main_window();
1480 linphone_gtk_manage_login();
1482 linphone_gtk_load_identities();
1483 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1484 linphone_gtk_show_friends();
1485 linphone_gtk_connect_digits();
1486 main_window=linphone_gtk_get_main_window();
1487 linphone_gtk_call_log_update(main_window);
1489 linphone_gtk_update_call_buttons (NULL);
1490 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1491 g_signal_connect (G_OBJECT (main_window), "delete-event",
1492 G_CALLBACK (linphone_gtk_close), main_window);
1495 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1496 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1497 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1498 gtk_widget_hide(menubar);
1499 gtk_osxapplication_ready(theMacApp);
1501 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1503 linphone_gtk_check_menu_items();
1507 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1509 const char *lname="undef";
1511 #if defined(__linux) || defined(__APPLE__)
1512 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1531 g_error("Bad level !");
1533 #if defined(__linux) || defined(__APPLE__)
1535 msg=g_strdup_vprintf(fmt,cap);
1538 msg=g_strdup_vprintf(fmt,args);
1540 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1543 linphone_gtk_log_push(lev,fmt,args);
1547 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1548 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1549 linphone_gtk_get_main_window(), "uribar"));
1551 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1553 gtk_entry_set_text(uri_bar, refer_to);
1554 linphone_gtk_start_call(linphone_gtk_get_main_window());
1557 static void linphone_gtk_check_soundcards(){
1558 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1559 if (devices==NULL || devices[0]==NULL){
1560 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1561 _("No sound cards have been detected on this computer.\n"
1562 "You won't be able to send or receive audio calls."));
1566 // Display the account wizard
1567 void linphone_gtk_display_wizard() {
1568 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1569 the_wizard = linphone_gtk_create_assistant();
1573 static void linphone_gtk_quit(void){
1574 linphone_gtk_uninit_instance();
1575 linphone_gtk_destroy_log_window();
1576 linphone_core_destroy(the_core);
1577 linphone_gtk_log_uninit();
1581 gdk_threads_leave();
1586 This is not the correct way to implement block termination.
1587 The good way would be to call gtk_main_quit(), and return TRUE.
1588 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1589 As a result the program cannot exit at all.
1590 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1592 static gboolean on_block_termination(void){
1594 linphone_gtk_quit();
1599 int main(int argc, char *argv[]){
1603 const char *config_file;
1604 const char *factory_config_file;
1606 GtkSettings *settings;
1608 const char *app_name="Linphone";
1610 g_thread_init(NULL);
1613 progpath = strdup(argv[0]);
1615 config_file=linphone_gtk_get_config_file();
1619 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1620 if ((lang=getenv("LANG"))!=NULL){
1623 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1629 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1632 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1635 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1638 setenv("LANG",lang,1);
1643 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1644 if (p==NULL) perror("bindtextdomain failed");
1645 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1646 textdomain (GETTEXT_PACKAGE);
1648 g_message("NLS disabled.\n");
1651 gtk_rc_add_default_file("./gtkrc");
1653 gdk_threads_enter();
1655 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1656 linphone_options,NULL,NULL)){
1657 gdk_threads_leave();
1661 settings=gtk_settings_get_default();
1662 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1663 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1664 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1665 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1667 if (workingdir!=NULL){
1668 if (chdir(workingdir)==-1){
1669 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1673 /* Now, look for the factory configuration file, we do it this late
1674 since we want to have had time to change directory and to parse
1675 the options, in case we needed to access the working directory */
1676 factory_config_file = linphone_gtk_get_factory_config_file();
1678 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1679 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1680 g_warning("This instance is going to exit now.");
1681 gdk_threads_leave();
1685 add_pixmap_directory("pixmaps");
1686 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1689 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1690 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1691 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1692 /*never block termination:*/
1693 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1696 the_ui=linphone_gtk_create_window("main");
1698 linphone_gtk_create_log_window();
1699 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1701 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1703 g_set_application_name(app_name);
1704 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1705 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1707 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1708 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1709 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1710 linphone_gtk_init_main_window();
1712 // Veryfing if at least one sip account is configured. If not, show wizard
1713 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1714 linphone_gtk_display_wizard();
1717 #ifndef HAVE_GTK_OSX
1718 linphone_gtk_init_status_icon();
1721 linphone_gtk_show_main_window();
1722 linphone_gtk_check_soundcards();
1724 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1725 linphone_gtk_check_for_new_version();
1728 linphone_gtk_quit();
1729 #ifndef HAVE_GTK_OSX
1730 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1731 gtk_status_icon_set_visible(icon,FALSE);