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>
40 #include <libnotify/notify.h>
43 #define LINPHONE_ICON "linphone.png"
45 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
47 static LinphoneCore *the_core=NULL;
48 static GtkWidget *the_ui=NULL;
50 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
51 static void linphone_gtk_show(LinphoneCore *lc);
52 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
53 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
54 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
55 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
56 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
57 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
58 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
59 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
60 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to);
61 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
62 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
63 static void linphone_gtk_status_icon_set_blinking(gboolean val);
66 static gboolean verbose=0;
67 static gboolean auto_answer = 0;
68 static gchar * addr_to_call = NULL;
69 static gboolean iconified=FALSE;
70 static gchar *workingdir=NULL;
71 static char *progpath=NULL;
72 gchar *linphone_logfile=NULL;
74 static GOptionEntry linphone_options[]={
78 .arg=G_OPTION_ARG_NONE,
79 .arg_data= (gpointer)&verbose,
80 .description=N_("log to stdout some debug information while running.")
83 .long_name = "logfile",
85 .arg = G_OPTION_ARG_STRING,
86 .arg_data = &linphone_logfile,
87 .description = N_("path to a file to write logs into.")
90 .long_name="iconified",
92 .arg=G_OPTION_ARG_NONE,
93 .arg_data= (gpointer)&iconified,
94 .description=N_("Start only in the system tray, do not show the main interface.")
99 .arg = G_OPTION_ARG_STRING,
100 .arg_data = &addr_to_call,
101 .description = N_("address to call right now")
104 .long_name = "auto-answer",
106 .arg = G_OPTION_ARG_NONE,
107 .arg_data = (gpointer) & auto_answer,
108 .description = N_("if set automatically answer incoming calls")
111 .long_name = "workdir",
113 .arg = G_OPTION_ARG_STRING,
114 .arg_data = (gpointer) & workingdir,
115 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
120 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
121 #define RELATIVE_XML_DIR
122 #define BUILD_TREE_XML_DIR "gtk"
125 #define CONFIG_FILE ".linphonerc"
127 #define CONFIG_FILE "linphonerc"
132 static char _config_file[1024];
135 const char *linphone_gtk_get_config_file(){
136 /*try accessing a local file first if exists*/
137 if (access(CONFIG_FILE,F_OK)==0){
138 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
141 const char *appdata=getenv("APPDATA");
143 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
144 CreateDirectory(_config_file,NULL);
145 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
148 const char *home=getenv("HOME");
149 if (home==NULL) home=".";
150 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
157 #define FACTORY_CONFIG_FILE "linphonerc.factory"
158 static char _factory_config_file[1024];
159 static const char *linphone_gtk_get_factory_config_file(){
160 /*try accessing a local file first if exists*/
161 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
162 snprintf(_factory_config_file,sizeof(_factory_config_file),
163 "%s",FACTORY_CONFIG_FILE);
167 if (progpath != NULL) {
169 progdir = strdup(progpath);
171 basename = strrchr(progdir, '\\');
172 if (basename != NULL) {
175 snprintf(_factory_config_file, sizeof(_factory_config_file),
176 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
178 if (workingdir!=NULL) {
179 snprintf(_factory_config_file, sizeof(_factory_config_file),
180 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
187 basename = strrchr(progdir, '/');
188 if (basename != NULL) {
191 snprintf(_factory_config_file, sizeof(_factory_config_file),
192 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
201 return _factory_config_file;
204 static void linphone_gtk_init_liblinphone(const char *config_file,
205 const char *factory_config_file) {
206 LinphoneCoreVTable vtable={0};
208 vtable.call_state_changed=linphone_gtk_call_state_changed;
209 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
210 vtable.show=linphone_gtk_show;
211 vtable.notify_presence_recv=linphone_gtk_notify_recv;
212 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
213 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
214 vtable.display_status=linphone_gtk_display_status;
215 vtable.display_message=linphone_gtk_display_message;
216 vtable.display_warning=linphone_gtk_display_warning;
217 vtable.display_url=linphone_gtk_display_url;
218 vtable.call_log_updated=linphone_gtk_call_log_updated;
219 vtable.text_received=linphone_gtk_text_received;
220 vtable.refer_received=linphone_gtk_refer_received;
221 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
223 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
224 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
225 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
230 LinphoneCore *linphone_gtk_get_core(void){
234 GtkWidget *linphone_gtk_get_main_window(){
238 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
239 static const char *icon_path=NULL;
240 static const char *hiddens=NULL;
241 static const char *shown=NULL;
242 static bool_t config_loaded=FALSE;
243 if (linphone_gtk_get_core()==NULL) return;
244 if (config_loaded==FALSE){
245 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
246 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
247 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
251 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
253 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
255 GdkPixbuf *pbuf=create_pixbuf(icon_path);
256 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
257 g_object_unref(G_OBJECT(pbuf));
261 static int get_ui_file(const char *name, char *path, int pathsize){
262 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
263 if (access(path,F_OK)!=0){
264 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
265 if (access(path,F_OK)!=0){
266 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
267 INSTALLED_XML_DIR,name);
274 GtkWidget *linphone_gtk_create_window(const char *window_name){
275 GError* error = NULL;
276 GtkBuilder* builder = gtk_builder_new ();
280 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
282 if (!gtk_builder_add_from_file (builder, path, &error)){
283 g_error("Couldn't load builder file: %s", error->message);
284 g_error_free (error);
287 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
289 g_error("Could not retrieve '%s' window from xml file",window_name);
292 g_object_set_data(G_OBJECT(w),"builder",builder);
293 gtk_builder_connect_signals(builder,w);
294 linphone_gtk_configure_window(w,window_name);
298 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
301 GtkBuilder* builder = gtk_builder_new ();
303 gchar *object_ids[2];
304 object_ids[0]=g_strdup(widget_name);
307 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
308 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
309 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
310 g_error_free (error);
311 g_free(object_ids[0]);
314 g_free(object_ids[0]);
315 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
317 g_error("Could not retrieve '%s' window from xml file",widget_name);
320 g_object_set_data(G_OBJECT(w),"builder",builder);
321 gtk_builder_connect_signals(builder,w);
325 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
326 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
329 g_error("Fail to retrieve builder from window !");
332 w=gtk_builder_get_object(builder,name);
334 g_error("No widget named %s found in xml interface.",name);
336 return GTK_WIDGET(w);
340 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
342 GtkWidget *main_window=linphone_gtk_get_main_window();
344 gtk_widget_show(main_window);
345 if (type==GTK_MESSAGE_QUESTION)
347 /* draw a question box. link to dialog_click callback */
348 dialog = gtk_message_dialog_new (
349 GTK_WINDOW(main_window),
350 GTK_DIALOG_DESTROY_WITH_PARENT,
351 GTK_MESSAGE_QUESTION,
354 (const gchar*)message);
355 /* connect to some callback : REVISIT */
357 g_signal_connect_swapped (G_OBJECT (dialog), "response",
358 G_CALLBACK (dialog_click),
361 /* actually show the box */
362 gtk_widget_show(dialog);
366 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
367 GTK_DIALOG_DESTROY_WITH_PARENT,
371 (const gchar*)message);
372 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
373 g_signal_connect_swapped (G_OBJECT (dialog), "response",
374 G_CALLBACK (gtk_widget_destroy),
376 gtk_widget_show(dialog);
380 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
381 if (id==GTK_RESPONSE_CANCEL){
382 gtk_widget_destroy(GTK_WIDGET(dialog));
386 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
387 g_message("About url clicked");
388 linphone_gtk_open_browser(url);
391 void linphone_gtk_show_about(){
392 struct stat filestat;
393 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
396 GdkPixbuf *logo=create_pixbuf(
397 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
398 static const char *defcfg="defcfg";
400 about=linphone_gtk_create_window("about");
401 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
402 memset(&filestat,0,sizeof(filestat));
403 if (stat(license_file,&filestat)!=0){
404 license_file="COPYING";
405 stat(license_file,&filestat);
407 if (filestat.st_size>0){
408 char *license=g_malloc(filestat.st_size+1);
409 FILE *f=fopen(license_file,"r");
410 if (f && fread(license,filestat.st_size,1,f)==1){
411 license[filestat.st_size]='\0';
412 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
416 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
417 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
418 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
419 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
420 tmp=linphone_gtk_get_ui_config("artists",defcfg);
425 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
427 tmp=linphone_gtk_get_ui_config("translators",defcfg);
429 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
430 tmp=linphone_gtk_get_ui_config("comments",defcfg);
432 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
433 gtk_widget_show(about);
436 static void set_video_window_decorations(GdkWindow *w){
437 const char *title=linphone_gtk_get_ui_config("title","Linphone");
438 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
439 char video_title[256];
440 GdkPixbuf *pbuf=create_pixbuf(icon_path);
441 if (!linphone_core_in_call(linphone_gtk_get_core())){
442 snprintf(video_title,sizeof(video_title),"%s video",title);
443 /* When not in call, treat the video as a normal window */
444 gdk_window_set_keep_above(w, FALSE);
446 LinphoneAddress *uri =
447 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
450 linphone_address_clean(uri);
451 if (linphone_address_get_display_name(uri)!=NULL){
452 display_name=ms_strdup(linphone_address_get_display_name(uri));
454 display_name=linphone_address_as_string(uri);
456 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
457 linphone_address_destroy(uri);
458 ms_free(display_name);
460 /* During calls, bring up the video window, arrange so that
461 it is above all the other windows */
462 gdk_window_deiconify(w);
463 gdk_window_set_keep_above(w,TRUE);
464 /* Maybe we should have the following, but then we want to
465 have a timer that turns it off after a little while. */
466 /* gdk_window_set_urgency_hint(w,TRUE); */
468 gdk_window_set_title(w,video_title);
469 /* Refrain the video window to be closed at all times. */
470 gdk_window_set_functions(w,
471 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
472 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
475 l=g_list_append(l,pbuf);
476 gdk_window_set_icon_list(w,l);
478 g_object_unref(G_OBJECT(pbuf));
482 static gboolean video_needs_update=FALSE;
484 static void update_video_title(){
485 video_needs_update=TRUE;
488 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
489 static gboolean first_time=TRUE;
491 static unsigned long previd=0;
492 static unsigned long preview_previd=0;
493 static gboolean in_iterate=FALSE;
496 if (in_iterate) return TRUE;
498 linphone_core_iterate(lc);
500 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
501 linphone_gtk_show_directory_search();
505 id=linphone_core_get_native_video_window_id(lc);
506 if (id!=previd || video_needs_update){
510 ms_message("Updating window decorations");
512 w=gdk_window_foreign_new(id);
514 w=gdk_window_foreign_new((HANDLE)id);
517 set_video_window_decorations(w);
518 g_object_unref(G_OBJECT(w));
520 else ms_error("gdk_window_foreign_new() failed");
521 if (video_needs_update) video_needs_update=FALSE;
524 id=linphone_core_get_native_preview_window_id (lc);
525 if (id!=preview_previd ){
529 ms_message("Updating window decorations for preview");
531 w=gdk_window_foreign_new(id);
533 w=gdk_window_foreign_new((HANDLE)id);
536 set_video_window_decorations(w);
537 g_object_unref(G_OBJECT(w));
539 else ms_error("gdk_window_foreign_new() failed");
540 if (video_needs_update) video_needs_update=FALSE;
543 if (addr_to_call!=NULL){
544 /*make sure we are not showing the login screen*/
545 GtkWidget *mw=linphone_gtk_get_main_window();
546 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
547 if (!GTK_WIDGET_VISIBLE(login_frame)){
548 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
549 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
551 linphone_gtk_start_call(uri_bar);
558 static void load_uri_history(){
559 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
562 GtkEntryCompletion *gep=gtk_entry_completion_new();
563 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
566 snprintf(key,sizeof(key),"uri%i",i);
567 uri=linphone_gtk_get_ui_config(key,NULL);
570 gtk_list_store_append(model,&iter);
571 gtk_list_store_set(model,&iter,0,uri,-1);
572 if (i==0) gtk_entry_set_text(uribar,uri);
576 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
577 gtk_entry_completion_set_text_column(gep,0);
578 gtk_entry_set_completion(uribar,gep);
581 static void save_uri_history(){
582 LinphoneCore *lc=linphone_gtk_get_core();
583 LpConfig *cfg=linphone_core_get_config(lc);
584 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
589 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
591 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
593 gtk_tree_model_get(model,&iter,0,&uri,-1);
595 snprintf(key,sizeof(key),"uri%i",i);
596 lp_config_set_string(cfg,"GtkUi",key,uri);
601 }while(gtk_tree_model_iter_next(model,&iter));
605 static void completion_add_text(GtkEntry *entry, const char *text){
607 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
609 if (gtk_tree_model_get_iter_first(model,&iter)){
612 gtk_tree_model_get(model,&iter,0,&uri,-1);
614 if (strcmp(uri,text)==0) {
616 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
622 }while (gtk_tree_model_iter_next(model,&iter));
624 /* and prepend it on top of the list */
625 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
626 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
631 static void linphone_gtk_show_main_window(){
632 GtkWidget *w=linphone_gtk_get_main_window();
633 LinphoneCore *lc=linphone_gtk_get_core();
634 if (linphone_core_video_enabled(lc)){
635 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
636 VIDEOSELFVIEW_DEFAULT));
639 gtk_window_present(GTK_WINDOW(w));
642 static void linphone_gtk_show(LinphoneCore *lc){
644 linphone_gtk_show_main_window();
648 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
649 GtkWidget *mw=linphone_gtk_get_main_window();
650 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
651 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
652 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
654 if (linphone_gtk_use_in_call_view() && call)
655 linphone_gtk_in_call_view_terminate(call,error);
656 update_video_title();
659 static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){
660 for(;calls!=NULL;calls=calls->next){
661 LinphoneCall *call=(LinphoneCall*)calls->data;
662 LinphoneCallState cs=linphone_call_get_state(call);
664 if (cs!=LinphoneCallPaused && cs!=LinphoneCallPausing)
671 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
672 LinphoneCore *lc=linphone_gtk_get_core();
673 GtkWidget *mw=linphone_gtk_get_main_window();
674 const MSList *calls=linphone_core_get_calls(lc);
676 bool_t start_active=TRUE;
677 bool_t stop_active=FALSE;
678 bool_t add_call=FALSE;
679 int call_list_size=ms_list_size(calls);
686 if (all_other_calls_paused(NULL,calls)){
689 }else if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived && all_other_calls_paused(call,calls)){
690 if (call_list_size>1){
701 button=linphone_gtk_get_widget(mw,"start_call");
702 gtk_widget_set_sensitive(button,start_active);
703 gtk_widget_set_visible(button,!add_call);
705 button=linphone_gtk_get_widget(mw,"add_call");
706 gtk_widget_set_sensitive(button,start_active);
707 gtk_widget_set_visible(button,add_call);
709 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
711 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
712 update_video_title();
715 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
716 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
717 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
718 completion_add_text(GTK_ENTRY(uri_bar),entered);
720 linphone_gtk_call_terminated(NULL,NULL);
725 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
726 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
727 linphone_core_accept_call (linphone_gtk_get_core(),call);
728 linphone_call_unref(call);
734 void linphone_gtk_start_call(GtkWidget *w){
735 LinphoneCore *lc=linphone_gtk_get_core();
737 /*change into in-call mode, then do the work later as it might block a bit */
738 GtkWidget *mw=gtk_widget_get_toplevel(w);
739 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
741 call=linphone_gtk_get_currently_displayed_call ();
742 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
743 linphone_core_accept_call(lc,call);
745 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
746 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
747 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
748 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
753 void linphone_gtk_uri_bar_activate(GtkWidget *w){
754 linphone_gtk_start_call(w);
758 void linphone_gtk_terminate_call(GtkWidget *button){
759 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
761 linphone_core_terminate_call(linphone_gtk_get_core(),call);
764 void linphone_gtk_decline_clicked(GtkWidget *button){
765 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
767 linphone_core_terminate_call(linphone_gtk_get_core(),call);
770 void linphone_gtk_answer_clicked(GtkWidget *button){
771 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
773 linphone_core_pause_all_calls(linphone_gtk_get_core());
774 linphone_core_accept_call(linphone_gtk_get_core(),call);
775 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
779 void linphone_gtk_enable_video(GtkWidget *w){
780 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
781 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
782 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
783 gtk_widget_set_sensitive(selfview_item,val);
785 linphone_core_enable_video_preview(linphone_gtk_get_core(),
786 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
788 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
792 void linphone_gtk_enable_self_view(GtkWidget *w){
793 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
794 LinphoneCore *lc=linphone_gtk_get_core();
795 linphone_core_enable_video_preview(lc,val);
796 linphone_core_enable_self_view(lc,val);
797 linphone_gtk_set_ui_config_int("videoselfview",val);
800 void linphone_gtk_used_identity_changed(GtkWidget *w){
801 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
802 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
803 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
804 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
805 linphone_gtk_show_directory_search();
807 if (sel) g_free(sel);
810 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
811 linphone_gtk_show_friends();
814 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
816 case GTK_RESPONSE_YES:
817 linphone_gtk_show_contact(lf);
820 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
822 gtk_widget_destroy(dialog);
825 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
828 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
829 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
833 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);
834 dialog = gtk_message_dialog_new (
835 GTK_WINDOW(linphone_gtk_get_main_window()),
836 GTK_DIALOG_DESTROY_WITH_PARENT,
837 GTK_MESSAGE_QUESTION,
842 g_signal_connect(G_OBJECT (dialog), "response",
843 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
844 /* actually show the box */
845 gtk_widget_show(dialog);
848 typedef struct _AuthTimeout{
853 static void auth_timeout_clean(AuthTimeout *tout){
857 static gboolean auth_timeout_destroy(AuthTimeout *tout){
859 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
860 gtk_widget_destroy(tout->w);
866 static AuthTimeout * auth_timeout_new(GtkWidget *w){
867 AuthTimeout *tout=g_new(AuthTimeout,1);
869 /*so that the timeout no more references the widget when it is destroyed:*/
870 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
871 /*so that the widget is automatically destroyed after some time */
872 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
876 void linphone_gtk_password_cancel(GtkWidget *w){
877 LinphoneAuthInfo *info;
878 GtkWidget *window=gtk_widget_get_toplevel(w);
879 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
880 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
881 gtk_widget_destroy(window);
884 void linphone_gtk_password_ok(GtkWidget *w){
886 GtkWidget *window=gtk_widget_get_toplevel(w);
887 LinphoneAuthInfo *info;
888 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
889 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
890 entry=linphone_gtk_get_widget(window,"password_entry");
891 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
892 linphone_auth_info_set_userid(info,
893 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
894 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
895 gtk_widget_destroy(window);
898 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
899 GtkWidget *w=linphone_gtk_create_window("password");
900 GtkWidget *label=linphone_gtk_get_widget(w,"message");
901 LinphoneAuthInfo *info;
903 GtkWidget *mw=linphone_gtk_get_main_window();
905 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
906 /*don't prompt for authentication when login frame is visible*/
907 linphone_core_abort_authentication(lc,NULL);
911 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
913 gtk_label_set_markup(GTK_LABEL(label),msg);
915 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
916 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
917 g_object_set_data(G_OBJECT(w),"auth_info",info);
918 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
923 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
924 GtkWidget *w=linphone_gtk_get_main_window();
925 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
926 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
927 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
931 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
932 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
935 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
936 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
939 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
941 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
942 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
945 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
946 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
947 if (w) linphone_gtk_call_log_update(w);
951 static void make_notification(const char *title, const char *body){
952 NotifyNotification *n;
953 n = notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
954 if (n && !notify_notification_show(n,NULL))
955 ms_error("Failed to send notification.");
960 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
962 if (!notify_is_initted())
963 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
967 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL),NULL))
968 ms_error("Failed to send notification.");
970 linphone_gtk_show_main_window();
972 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
975 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
976 switch(linphone_call_get_state(call)){
977 case LinphoneCallError:
978 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
980 case LinphoneCallEnd:
981 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
983 case LinphoneCallIncomingReceived:
984 make_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
986 case LinphoneCallPausedByRemote:
987 make_notification(_("Call paused"),body=g_markup_printf_escaped("<span size=\"large\">by %s</span>",remote));
992 if (body) g_free(body);
993 if (remote) g_free(remote);
998 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1000 case LinphoneCallOutgoingInit:
1001 linphone_gtk_create_in_call_view (call);
1003 case LinphoneCallOutgoingProgress:
1004 linphone_gtk_in_call_view_set_calling (call);
1006 case LinphoneCallStreamsRunning:
1007 linphone_gtk_in_call_view_set_in_call(call);
1009 case LinphoneCallError:
1010 linphone_gtk_in_call_view_terminate (call,msg);
1012 case LinphoneCallEnd:
1013 linphone_gtk_in_call_view_terminate(call,NULL);
1014 linphone_gtk_status_icon_set_blinking(FALSE);
1016 case LinphoneCallIncomingReceived:
1017 linphone_gtk_create_in_call_view (call);
1018 linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc)));
1019 linphone_gtk_status_icon_set_blinking(TRUE);
1021 linphone_call_ref(call);
1022 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1025 case LinphoneCallResuming:
1026 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1027 linphone_gtk_in_call_view_set_in_call (call);
1029 case LinphoneCallPausing:
1030 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1031 case LinphoneCallPausedByRemote:
1032 linphone_gtk_in_call_view_set_paused(call);
1034 case LinphoneCallConnected:
1035 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1036 linphone_gtk_status_icon_set_blinking(FALSE);
1041 linphone_gtk_notify(call, msg);
1042 linphone_gtk_update_call_buttons (call);
1045 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1046 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1047 GtkTreeModel *model=gtk_combo_box_get_model(box);
1049 gboolean found=FALSE;
1050 const char *stock_id=NULL;
1052 if (gtk_tree_model_get_iter_first(model,&iter)){
1055 gtk_tree_model_get(model,&iter,2,&p,-1);
1060 }while(gtk_tree_model_iter_next(model,&iter));
1063 g_warning("Could not find proxy config in combo box of identities.");
1067 case LinphoneRegistrationOk:
1068 stock_id=GTK_STOCK_YES;
1070 case LinphoneRegistrationProgress:
1071 stock_id=GTK_STOCK_REFRESH;
1073 case LinphoneRegistrationCleared:
1076 case LinphoneRegistrationFailed:
1077 stock_id=GTK_STOCK_DIALOG_WARNING;
1082 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1085 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1086 LinphoneRegistrationState rs, const char *msg){
1088 case LinphoneRegistrationOk:
1090 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1091 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1092 linphone_gtk_exit_login_frame();
1099 update_registration_status(cfg,rs);
1103 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1104 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1105 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1108 void linphone_gtk_open_browser(const char *url){
1109 /*in gtk 2.16, gtk_show_uri does not work...*/
1111 #if GTK_CHECK_VERSION(2,18,3)
1112 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1115 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1116 g_spawn_command_line_async(cl,NULL);
1119 ShellExecute(0,"open",url,NULL,NULL,1);
1123 void linphone_gtk_link_to_website(GtkWidget *item){
1124 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1125 linphone_gtk_open_browser(home);
1128 static GtkWidget *create_icon_menu(){
1129 GtkWidget *menu=gtk_menu_new();
1130 GtkWidget *menu_item;
1133 const gchar *homesite;
1135 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1136 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1137 tmp=g_strdup(homesite);
1138 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1139 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1141 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1142 gtk_widget_show(image);
1143 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1144 //g_object_unref(G_OBJECT(image));
1145 gtk_widget_show(menu_item);
1146 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1147 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1149 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1150 gtk_widget_show(menu_item);
1151 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1152 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1153 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1154 gtk_widget_show(menu_item);
1155 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1156 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1157 gtk_widget_show(menu);
1161 static GtkStatusIcon *icon=NULL;
1163 static void linphone_gtk_init_status_icon(){
1164 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1165 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1166 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1167 GtkWidget *menu=create_icon_menu();
1169 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1170 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1171 gtk_status_icon_set_name(icon,title);
1172 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1173 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1174 gtk_status_icon_set_tooltip(icon,title);
1175 gtk_status_icon_set_visible(icon,TRUE);
1176 g_object_set_data(G_OBJECT(icon),"menu",menu);
1177 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1178 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1179 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1180 pbuf=create_pixbuf(call_icon_path);
1181 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1184 static gboolean do_icon_blink(GtkStatusIcon *gi){
1185 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1186 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1187 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1188 if (cur_icon==call_icon){
1189 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1191 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1196 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1198 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1199 if (val && tout==0){
1200 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1201 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1202 }else if (!val && tout!=0){
1203 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1204 g_source_remove(tout);
1205 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1206 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1210 static void init_identity_combo(GtkComboBox *box){
1211 GtkListStore *store;
1212 GtkCellRenderer *r1,*r2;
1213 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1214 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1215 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1216 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1217 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1218 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1219 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1220 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1223 void linphone_gtk_load_identities(void){
1225 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1227 LinphoneProxyConfig *def=NULL;
1229 GtkListStore *store;
1232 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1233 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1234 /* model is empty, this is the first time we go here */
1235 init_identity_combo(box);
1236 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1238 gtk_list_store_clear(store);
1239 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1240 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1241 gtk_list_store_append(store,&iter);
1242 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1243 g_free(def_identity);
1244 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1246 elem=ms_list_next(elem),i++){
1247 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1248 gtk_list_store_append(store,&iter);
1249 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1250 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1256 gtk_combo_box_set_active(box,def_index);
1259 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1260 const char *label=gtk_button_get_label(button);
1261 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1263 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1264 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1265 if (linphone_core_in_call(linphone_gtk_get_core())){
1266 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1270 static void linphone_gtk_dtmf_released(GtkButton *button){
1271 linphone_core_stop_dtmf (linphone_gtk_get_core());
1274 static void linphone_gtk_connect_digits(void){
1275 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1276 GList *children=gtk_container_get_children(cont);
1278 for(elem=children;elem!=NULL;elem=elem->next){
1279 GtkButton *button=GTK_BUTTON(elem->data);
1280 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1281 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1285 static void linphone_gtk_check_menu_items(void){
1286 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1287 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1288 GtkWidget *selfview_item=linphone_gtk_get_widget(
1289 linphone_gtk_get_main_window(),"selfview_item");
1290 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1291 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1292 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1293 gtk_widget_set_sensitive(selfview_item,video_enabled);
1296 static gboolean linphone_gtk_can_manage_accounts(){
1297 LinphoneCore *lc=linphone_gtk_get_core();
1299 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1300 SipSetup *ss=(SipSetup*)elem->data;
1301 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1308 static void linphone_gtk_configure_main_window(){
1309 static gboolean config_loaded=FALSE;
1310 static const char *title;
1311 static const char *home;
1312 static const char *start_call_icon;
1313 static const char *add_call_icon;
1314 static const char *stop_call_icon;
1315 static const char *search_icon;
1316 static gboolean update_check_menu;
1317 static gboolean buttons_have_borders;
1318 static gboolean show_abcd;
1319 GtkWidget *w=linphone_gtk_get_main_window();
1320 if (!config_loaded){
1321 title=linphone_gtk_get_ui_config("title","Linphone");
1322 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1323 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1324 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1325 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1326 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1327 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1328 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1329 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1332 linphone_gtk_configure_window(w,"main_window");
1334 gtk_window_set_title(GTK_WINDOW(w),title);
1336 if (start_call_icon){
1337 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1338 create_pixmap (start_call_icon));
1339 if (!buttons_have_borders)
1340 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1343 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1344 create_pixmap (add_call_icon));
1345 if (!buttons_have_borders)
1346 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1348 if (stop_call_icon){
1349 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1350 create_pixmap (stop_call_icon));
1351 if (!buttons_have_borders)
1352 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1355 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1356 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1357 g_object_unref(G_OBJECT(pbuf));
1361 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1363 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1366 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1368 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1369 g_object_unref(G_OBJECT(pbuf));
1373 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1375 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1376 g_object_unref(G_OBJECT(pbuf));
1379 if (linphone_gtk_can_manage_accounts())
1380 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1381 if (update_check_menu){
1382 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1385 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1386 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1387 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1388 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1389 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1393 void linphone_gtk_manage_login(void){
1394 LinphoneCore *lc=linphone_gtk_get_core();
1395 LinphoneProxyConfig *cfg=NULL;
1396 linphone_core_get_default_proxy(lc,&cfg);
1398 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1399 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1400 linphone_gtk_show_login_frame(cfg);
1406 gboolean linphone_gtk_close(GtkWidget *mw){
1407 /*shutdown calls if any*/
1408 LinphoneCore *lc=linphone_gtk_get_core();
1409 if (linphone_core_in_call(lc)){
1410 linphone_core_terminate_all_calls(lc);
1412 linphone_core_enable_video_preview(lc,FALSE);
1413 #ifdef __APPLE__ /*until with have a better option*/
1414 gtk_window_iconify(GTK_WINDOW(mw));
1416 gtk_widget_hide(mw);
1422 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1423 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1424 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1426 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1427 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1434 static void linphone_gtk_init_main_window(){
1435 GtkWidget *main_window;
1437 linphone_gtk_configure_main_window();
1438 linphone_gtk_manage_login();
1440 linphone_gtk_load_identities();
1441 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1442 linphone_gtk_show_friends();
1443 linphone_gtk_connect_digits();
1444 main_window=linphone_gtk_get_main_window();
1446 linphone_gtk_update_call_buttons (NULL);
1447 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1448 g_signal_connect (G_OBJECT (main_window), "delete-event",
1449 G_CALLBACK (linphone_gtk_close), main_window);
1452 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1453 gtk_widget_destroy(linphone_gtk_get_widget(main_window,"imagemenuitem5"));
1454 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1455 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1456 gtk_widget_hide(menubar);
1457 gtk_osxapplication_ready(theMacApp);
1459 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1461 linphone_gtk_check_menu_items();
1465 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1467 const char *lname="undef";
1469 #if defined(__linux) || defined(__APPLE__)
1470 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1489 g_error("Bad level !");
1491 #if defined(__linux) || defined(__APPLE__)
1493 msg=g_strdup_vprintf(fmt,cap);
1496 msg=g_strdup_vprintf(fmt,args);
1498 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1501 linphone_gtk_log_push(lev,fmt,args);
1505 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1506 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1507 linphone_gtk_get_main_window(), "uribar"));
1509 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1511 gtk_entry_set_text(uri_bar, refer_to);
1512 linphone_gtk_start_call(linphone_gtk_get_main_window());
1515 static void linphone_gtk_check_soundcards(){
1516 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1517 if (devices==NULL || devices[0]==NULL){
1518 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1519 _("No sound cards have been detected on this computer.\n"
1520 "You won't be able to send or receive audio calls."));
1524 static void linphone_gtk_quit(void){
1525 gdk_threads_leave();
1526 linphone_gtk_destroy_log_window();
1527 linphone_core_destroy(the_core);
1528 linphone_gtk_log_uninit();
1536 This is not the correct way to implement block termination.
1537 The good way would be to call gtk_main_quit(), and return TRUE.
1538 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1539 As a result the program cannot exit at all.
1540 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1542 static gboolean on_block_termination(void){
1544 linphone_gtk_quit();
1549 int main(int argc, char *argv[]){
1553 const char *config_file;
1554 const char *factory_config_file;
1556 GtkSettings *settings;
1559 g_thread_init(NULL);
1562 progpath = strdup(argv[0]);
1564 config_file=linphone_gtk_get_config_file();
1567 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1568 if ((lang=getenv("LANG"))!=NULL){
1571 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1577 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1580 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1583 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1586 setenv("LANG",lang,1);
1591 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1592 if (p==NULL) perror("bindtextdomain failed");
1593 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1594 textdomain (GETTEXT_PACKAGE);
1596 g_message("NLS disabled.\n");
1599 gtk_rc_add_default_file("./gtkrc");
1601 gdk_threads_enter();
1603 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1604 linphone_options,NULL,NULL)){
1605 gdk_threads_leave();
1609 settings=gtk_settings_get_default();
1610 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1611 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1612 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1613 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1615 if (workingdir!=NULL){
1616 if (chdir(workingdir)==-1){
1617 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1621 /* Now, look for the factory configuration file, we do it this late
1622 since we want to have had time to change directory and to parse
1623 the options, in case we needed to access the working directory */
1624 factory_config_file = linphone_gtk_get_factory_config_file();
1626 if (linphone_core_wake_up_possible_already_running_instance(
1627 config_file, addr_to_call) == 0){
1628 g_message("addr_to_call=%s",addr_to_call);
1629 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1630 g_warning("This instance is going to exit now.");
1631 gdk_threads_leave();
1635 add_pixmap_directory("pixmaps");
1636 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1639 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1640 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1641 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1642 /*never block termination:*/
1643 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1646 the_ui=linphone_gtk_create_window("main");
1648 linphone_gtk_create_log_window();
1649 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1651 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1653 g_set_application_name(linphone_gtk_get_ui_config("title","Linphone"));
1654 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1655 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1657 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1658 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1659 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1660 linphone_gtk_init_main_window();
1661 #ifndef HAVE_GTK_OSX
1662 linphone_gtk_init_status_icon();
1665 linphone_gtk_show_main_window();
1666 linphone_gtk_check_soundcards();
1668 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1669 linphone_gtk_check_for_new_version();
1672 linphone_gtk_quit();
1673 #ifndef HAVE_GTK_OSX
1674 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1675 gtk_status_icon_set_visible(icon,FALSE);