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_NOTIFIED4
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
54 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
55 static void linphone_gtk_show(LinphoneCore *lc);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
66 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 static gboolean verbose=0;
70 static gboolean auto_answer = 0;
71 static gchar * addr_to_call = NULL;
72 static gboolean iconified=FALSE;
73 static gchar *workingdir=NULL;
74 static char *progpath=NULL;
75 gchar *linphone_logfile=NULL;
77 static GOptionEntry linphone_options[]={
81 .arg=G_OPTION_ARG_NONE,
82 .arg_data= (gpointer)&verbose,
83 .description=N_("log to stdout some debug information while running.")
86 .long_name = "logfile",
88 .arg = G_OPTION_ARG_STRING,
89 .arg_data = &linphone_logfile,
90 .description = N_("path to a file to write logs into.")
93 .long_name="iconified",
95 .arg=G_OPTION_ARG_NONE,
96 .arg_data= (gpointer)&iconified,
97 .description=N_("Start only in the system tray, do not show the main interface.")
102 .arg = G_OPTION_ARG_STRING,
103 .arg_data = &addr_to_call,
104 .description = N_("address to call right now")
107 .long_name = "auto-answer",
109 .arg = G_OPTION_ARG_NONE,
110 .arg_data = (gpointer) & auto_answer,
111 .description = N_("if set automatically answer incoming calls")
114 .long_name = "workdir",
116 .arg = G_OPTION_ARG_STRING,
117 .arg_data = (gpointer) & workingdir,
118 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
123 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
124 #define RELATIVE_XML_DIR
125 #define BUILD_TREE_XML_DIR "gtk"
128 #define CONFIG_FILE ".linphonerc"
130 #define CONFIG_FILE "linphonerc"
135 static char _config_file[1024];
138 const char *linphone_gtk_get_config_file(){
139 /*try accessing a local file first if exists*/
140 if (access(CONFIG_FILE,F_OK)==0){
141 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
144 const char *appdata=getenv("APPDATA");
146 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
147 CreateDirectory(_config_file,NULL);
148 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
151 const char *home=getenv("HOME");
152 if (home==NULL) home=".";
153 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
160 #define FACTORY_CONFIG_FILE "linphonerc.factory"
161 static char _factory_config_file[1024];
162 static const char *linphone_gtk_get_factory_config_file(){
163 /*try accessing a local file first if exists*/
164 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
165 snprintf(_factory_config_file,sizeof(_factory_config_file),
166 "%s",FACTORY_CONFIG_FILE);
170 if (progpath != NULL) {
172 progdir = strdup(progpath);
174 basename = strrchr(progdir, '\\');
175 if (basename != NULL) {
178 snprintf(_factory_config_file, sizeof(_factory_config_file),
179 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
181 if (workingdir!=NULL) {
182 snprintf(_factory_config_file, sizeof(_factory_config_file),
183 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
190 basename = strrchr(progdir, '/');
191 if (basename != NULL) {
194 snprintf(_factory_config_file, sizeof(_factory_config_file),
195 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
204 return _factory_config_file;
207 static void linphone_gtk_init_liblinphone(const char *config_file,
208 const char *factory_config_file) {
209 LinphoneCoreVTable vtable={0};
211 vtable.call_state_changed=linphone_gtk_call_state_changed;
212 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
213 vtable.show=linphone_gtk_show;
214 vtable.notify_presence_recv=linphone_gtk_notify_recv;
215 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
216 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
217 vtable.display_status=linphone_gtk_display_status;
218 vtable.display_message=linphone_gtk_display_message;
219 vtable.display_warning=linphone_gtk_display_warning;
220 vtable.display_url=linphone_gtk_display_url;
221 vtable.call_log_updated=linphone_gtk_call_log_updated;
222 vtable.text_received=linphone_gtk_text_received;
223 vtable.refer_received=linphone_gtk_refer_received;
224 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
227 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
228 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
233 LinphoneCore *linphone_gtk_get_core(void){
237 GtkWidget *linphone_gtk_get_main_window(){
241 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
242 static const char *icon_path=NULL;
243 static const char *hiddens=NULL;
244 static const char *shown=NULL;
245 static bool_t config_loaded=FALSE;
246 if (linphone_gtk_get_core()==NULL) return;
247 if (config_loaded==FALSE){
248 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
249 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
250 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
254 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
256 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
258 GdkPixbuf *pbuf=create_pixbuf(icon_path);
259 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
260 g_object_unref(G_OBJECT(pbuf));
264 static int get_ui_file(const char *name, char *path, int pathsize){
265 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
266 if (access(path,F_OK)!=0){
267 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
268 if (access(path,F_OK)!=0){
269 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
270 INSTALLED_XML_DIR,name);
277 GtkWidget *linphone_gtk_create_window(const char *window_name){
278 GError* error = NULL;
279 GtkBuilder* builder = gtk_builder_new ();
283 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
285 if (!gtk_builder_add_from_file (builder, path, &error)){
286 g_error("Couldn't load builder file: %s", error->message);
287 g_error_free (error);
290 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
292 g_error("Could not retrieve '%s' window from xml file",window_name);
295 g_object_set_data(G_OBJECT(w),"builder",builder);
296 gtk_builder_connect_signals(builder,w);
297 linphone_gtk_configure_window(w,window_name);
301 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
304 GtkBuilder* builder = gtk_builder_new ();
306 gchar *object_ids[2];
307 object_ids[0]=g_strdup(widget_name);
310 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
311 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
312 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
313 g_error_free (error);
314 g_free(object_ids[0]);
317 g_free(object_ids[0]);
318 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
320 g_error("Could not retrieve '%s' window from xml file",widget_name);
323 g_object_set_data(G_OBJECT(w),"builder",builder);
324 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 static void linphone_gtk_show(LinphoneCore *lc){
647 linphone_gtk_show_main_window();
651 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
652 GtkWidget *mw=linphone_gtk_get_main_window();
653 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
654 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
655 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
657 if (linphone_gtk_use_in_call_view() && call)
658 linphone_gtk_in_call_view_terminate(call,error);
659 update_video_title();
662 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
663 LinphoneCore *lc=linphone_gtk_get_core();
664 GtkWidget *mw=linphone_gtk_get_main_window();
665 const MSList *calls=linphone_core_get_calls(lc);
667 bool_t start_active=TRUE;
668 bool_t stop_active=FALSE;
669 bool_t add_call=FALSE;
670 int call_list_size=ms_list_size(calls);
680 button=linphone_gtk_get_widget(mw,"start_call");
681 gtk_widget_set_sensitive(button,start_active);
682 gtk_widget_set_visible(button,!add_call);
684 button=linphone_gtk_get_widget(mw,"add_call");
685 gtk_widget_set_sensitive(button,start_active);
686 gtk_widget_set_visible(button,add_call);
688 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
690 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
691 linphone_gtk_enable_conference_button(lc,call_list_size>1);
692 update_video_title();
695 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
696 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
697 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
698 completion_add_text(GTK_ENTRY(uri_bar),entered);
700 linphone_gtk_call_terminated(NULL,NULL);
705 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
706 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
707 linphone_core_accept_call (linphone_gtk_get_core(),call);
708 linphone_call_unref(call);
714 void linphone_gtk_start_call(GtkWidget *w){
715 LinphoneCore *lc=linphone_gtk_get_core();
717 /*change into in-call mode, then do the work later as it might block a bit */
718 GtkWidget *mw=gtk_widget_get_toplevel(w);
719 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
721 call=linphone_gtk_get_currently_displayed_call();
722 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
723 linphone_core_accept_call(lc,call);
725 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
726 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
727 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
728 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
733 void linphone_gtk_uri_bar_activate(GtkWidget *w){
734 linphone_gtk_start_call(w);
738 void linphone_gtk_terminate_call(GtkWidget *button){
739 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
741 linphone_core_terminate_call(linphone_gtk_get_core(),call);
744 void linphone_gtk_decline_clicked(GtkWidget *button){
745 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
747 linphone_core_terminate_call(linphone_gtk_get_core(),call);
750 void linphone_gtk_answer_clicked(GtkWidget *button){
751 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
753 linphone_core_pause_all_calls(linphone_gtk_get_core());
754 linphone_core_accept_call(linphone_gtk_get_core(),call);
755 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
759 void linphone_gtk_enable_video(GtkWidget *w){
760 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
761 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
762 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
763 gtk_widget_set_sensitive(selfview_item,val);
765 linphone_core_enable_video_preview(linphone_gtk_get_core(),
766 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
768 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
772 void linphone_gtk_enable_self_view(GtkWidget *w){
773 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
774 LinphoneCore *lc=linphone_gtk_get_core();
775 linphone_core_enable_video_preview(lc,val);
776 linphone_core_enable_self_view(lc,val);
777 linphone_gtk_set_ui_config_int("videoselfview",val);
780 void linphone_gtk_used_identity_changed(GtkWidget *w){
781 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
782 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
783 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
784 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
785 linphone_gtk_show_directory_search();
787 if (sel) g_free(sel);
790 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
791 linphone_gtk_show_friends();
794 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
796 case GTK_RESPONSE_YES:
797 linphone_gtk_show_contact(lf);
800 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
802 gtk_widget_destroy(dialog);
805 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
808 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
809 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
813 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);
814 dialog = gtk_message_dialog_new (
815 GTK_WINDOW(linphone_gtk_get_main_window()),
816 GTK_DIALOG_DESTROY_WITH_PARENT,
817 GTK_MESSAGE_QUESTION,
822 g_signal_connect(G_OBJECT (dialog), "response",
823 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
824 /* actually show the box */
825 gtk_widget_show(dialog);
828 typedef struct _AuthTimeout{
833 static void auth_timeout_clean(AuthTimeout *tout){
837 static gboolean auth_timeout_destroy(AuthTimeout *tout){
839 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
840 gtk_widget_destroy(tout->w);
846 static AuthTimeout * auth_timeout_new(GtkWidget *w){
847 AuthTimeout *tout=g_new(AuthTimeout,1);
849 /*so that the timeout no more references the widget when it is destroyed:*/
850 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
851 /*so that the widget is automatically destroyed after some time */
852 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
856 void linphone_gtk_password_cancel(GtkWidget *w){
857 LinphoneAuthInfo *info;
858 GtkWidget *window=gtk_widget_get_toplevel(w);
859 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
860 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
861 gtk_widget_destroy(window);
864 void linphone_gtk_password_ok(GtkWidget *w){
866 GtkWidget *window=gtk_widget_get_toplevel(w);
867 LinphoneAuthInfo *info;
868 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
869 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
870 entry=linphone_gtk_get_widget(window,"password_entry");
871 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
872 linphone_auth_info_set_userid(info,
873 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
874 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
875 gtk_widget_destroy(window);
878 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
879 GtkWidget *w=linphone_gtk_create_window("password");
880 GtkWidget *label=linphone_gtk_get_widget(w,"message");
881 LinphoneAuthInfo *info;
883 GtkWidget *mw=linphone_gtk_get_main_window();
885 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
886 /*don't prompt for authentication when login frame is visible*/
887 linphone_core_abort_authentication(lc,NULL);
891 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
893 gtk_label_set_markup(GTK_LABEL(label),msg);
895 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
896 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
897 g_object_set_data(G_OBJECT(w),"auth_info",info);
898 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
903 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
904 GtkWidget *w=linphone_gtk_get_main_window();
905 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
906 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
907 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
911 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
912 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
915 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
916 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
919 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
921 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
922 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
925 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
926 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
927 if (w) linphone_gtk_call_log_update(w);
928 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
932 static bool_t notify_actions_supported() {
933 bool_t accepts_actions = FALSE;
934 GList *capabilities = notify_get_server_caps();
936 if(capabilities != NULL) {
937 for(c = capabilities; c != NULL; c = c->next) {
938 if(strcmp((char*)c->data, "actions") == 0 ) {
939 accepts_actions = TRUE;
943 g_list_foreach(capabilities, (GFunc)g_free, NULL);
944 g_list_free(capabilities);
946 return accepts_actions;
949 static NotifyNotification* build_notification(const char *title, const char *body){
950 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
957 static void show_notification(NotifyNotification* n){
958 if (n && !notify_notification_show(n,NULL))
959 ms_error("Failed to send notification.");
962 static void make_notification(const char *title, const char *body){
963 show_notification(build_notification(title,body));
968 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
970 if (!notify_is_initted())
971 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
975 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
980 ms_error("Failed to send notification.");
982 linphone_gtk_show_main_window();
984 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
987 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
988 NotifyNotification *n;
989 switch(linphone_call_get_state(call)){
990 case LinphoneCallError:
991 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
993 case LinphoneCallEnd:
994 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
996 case LinphoneCallIncomingReceived:
997 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
998 if (notify_actions_supported()) {
999 notify_notification_add_action (n,"answer", _("Answer"),
1000 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1001 notify_notification_add_action (n,"decline",_("Decline"),
1002 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1004 show_notification(n);
1006 case LinphoneCallPausedByRemote:
1007 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1012 if (body) g_free(body);
1013 if (remote) g_free(remote);
1018 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1020 case LinphoneCallOutgoingInit:
1021 linphone_gtk_create_in_call_view (call);
1023 case LinphoneCallOutgoingProgress:
1024 linphone_gtk_in_call_view_set_calling (call);
1026 case LinphoneCallStreamsRunning:
1027 linphone_gtk_in_call_view_set_in_call(call);
1029 case LinphoneCallError:
1030 linphone_gtk_in_call_view_terminate (call,msg);
1032 case LinphoneCallEnd:
1033 linphone_gtk_in_call_view_terminate(call,NULL);
1034 linphone_gtk_status_icon_set_blinking(FALSE);
1036 case LinphoneCallIncomingReceived:
1037 linphone_gtk_create_in_call_view (call);
1038 linphone_gtk_in_call_view_set_incoming(call);
1039 linphone_gtk_status_icon_set_blinking(TRUE);
1041 linphone_call_ref(call);
1042 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1045 case LinphoneCallResuming:
1046 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1047 linphone_gtk_in_call_view_set_in_call (call);
1049 case LinphoneCallPausing:
1050 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1051 case LinphoneCallPausedByRemote:
1052 linphone_gtk_in_call_view_set_paused(call);
1054 case LinphoneCallConnected:
1055 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1056 linphone_gtk_status_icon_set_blinking(FALSE);
1061 linphone_gtk_notify(call, msg);
1062 linphone_gtk_update_call_buttons (call);
1065 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1066 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1067 GtkTreeModel *model=gtk_combo_box_get_model(box);
1069 gboolean found=FALSE;
1070 const char *stock_id=NULL;
1072 if (gtk_tree_model_get_iter_first(model,&iter)){
1075 gtk_tree_model_get(model,&iter,2,&p,-1);
1080 }while(gtk_tree_model_iter_next(model,&iter));
1083 g_warning("Could not find proxy config in combo box of identities.");
1087 case LinphoneRegistrationOk:
1088 stock_id=GTK_STOCK_YES;
1090 case LinphoneRegistrationProgress:
1091 stock_id=GTK_STOCK_REFRESH;
1093 case LinphoneRegistrationCleared:
1096 case LinphoneRegistrationFailed:
1097 stock_id=GTK_STOCK_DIALOG_WARNING;
1102 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1105 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1106 LinphoneRegistrationState rs, const char *msg){
1108 case LinphoneRegistrationOk:
1110 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1111 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1112 linphone_gtk_exit_login_frame();
1119 update_registration_status(cfg,rs);
1123 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1124 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1125 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1128 void linphone_gtk_open_browser(const char *url){
1129 /*in gtk 2.16, gtk_show_uri does not work...*/
1131 #if GTK_CHECK_VERSION(2,18,3)
1132 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1135 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1136 g_spawn_command_line_async(cl,NULL);
1139 ShellExecute(0,"open",url,NULL,NULL,1);
1143 void linphone_gtk_link_to_website(GtkWidget *item){
1144 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1145 linphone_gtk_open_browser(home);
1148 static GtkWidget *create_icon_menu(){
1149 GtkWidget *menu=gtk_menu_new();
1150 GtkWidget *menu_item;
1153 const gchar *homesite;
1155 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1156 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1157 tmp=g_strdup(homesite);
1158 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1159 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1161 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1162 gtk_widget_show(image);
1163 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1164 //g_object_unref(G_OBJECT(image));
1165 gtk_widget_show(menu_item);
1166 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1167 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1169 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1170 gtk_widget_show(menu_item);
1171 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1172 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1173 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1174 gtk_widget_show(menu_item);
1175 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1176 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1177 gtk_widget_show(menu);
1181 static GtkStatusIcon *icon=NULL;
1183 static void handle_icon_click() {
1184 GtkWidget *mw=linphone_gtk_get_main_window();
1185 if (!gtk_window_is_active((GtkWindow*)mw)) {
1186 linphone_gtk_show_main_window();
1188 gtk_widget_hide(mw);
1192 static void linphone_gtk_init_status_icon(){
1193 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1194 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1195 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1196 GtkWidget *menu=create_icon_menu();
1198 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1199 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1200 gtk_status_icon_set_name(icon,title);
1201 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1202 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1203 gtk_status_icon_set_tooltip(icon,title);
1204 gtk_status_icon_set_visible(icon,TRUE);
1205 g_object_set_data(G_OBJECT(icon),"menu",menu);
1206 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1207 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1208 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1209 pbuf=create_pixbuf(call_icon_path);
1210 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1213 static gboolean do_icon_blink(GtkStatusIcon *gi){
1214 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1215 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1216 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1217 if (cur_icon==call_icon){
1218 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1220 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1225 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1227 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1228 if (val && tout==0){
1229 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1230 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1231 }else if (!val && tout!=0){
1232 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1233 g_source_remove(tout);
1234 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1235 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1239 static void init_identity_combo(GtkComboBox *box){
1240 GtkListStore *store;
1241 GtkCellRenderer *r1,*r2;
1242 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1243 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1244 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1245 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1246 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1247 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1248 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1249 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1252 void linphone_gtk_load_identities(void){
1254 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1256 LinphoneProxyConfig *def=NULL;
1258 GtkListStore *store;
1261 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1262 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1263 /* model is empty, this is the first time we go here */
1264 init_identity_combo(box);
1265 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1267 gtk_list_store_clear(store);
1268 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1269 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1270 gtk_list_store_append(store,&iter);
1271 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1272 g_free(def_identity);
1273 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1275 elem=ms_list_next(elem),i++){
1276 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1277 gtk_list_store_append(store,&iter);
1278 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1279 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1285 gtk_combo_box_set_active(box,def_index);
1288 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1289 const char *label=gtk_button_get_label(button);
1290 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1292 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1293 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1294 if (linphone_core_in_call(linphone_gtk_get_core())){
1295 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1299 static void linphone_gtk_dtmf_released(GtkButton *button){
1300 linphone_core_stop_dtmf (linphone_gtk_get_core());
1303 static void linphone_gtk_connect_digits(void){
1304 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1305 GList *children=gtk_container_get_children(cont);
1307 for(elem=children;elem!=NULL;elem=elem->next){
1308 GtkButton *button=GTK_BUTTON(elem->data);
1309 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1310 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1314 static void linphone_gtk_check_menu_items(void){
1315 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1316 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1317 GtkWidget *selfview_item=linphone_gtk_get_widget(
1318 linphone_gtk_get_main_window(),"selfview_item");
1319 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1320 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1321 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1322 gtk_widget_set_sensitive(selfview_item,video_enabled);
1325 static gboolean linphone_gtk_can_manage_accounts(){
1326 LinphoneCore *lc=linphone_gtk_get_core();
1328 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1329 SipSetup *ss=(SipSetup*)elem->data;
1330 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1337 static void linphone_gtk_configure_main_window(){
1338 static gboolean config_loaded=FALSE;
1339 static const char *title;
1340 static const char *home;
1341 static const char *start_call_icon;
1342 static const char *add_call_icon;
1343 static const char *stop_call_icon;
1344 static const char *search_icon;
1345 static gboolean update_check_menu;
1346 static gboolean buttons_have_borders;
1347 static gboolean show_abcd;
1348 GtkWidget *w=linphone_gtk_get_main_window();
1349 if (!config_loaded){
1350 title=linphone_gtk_get_ui_config("title","Linphone");
1351 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1352 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1353 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1354 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1355 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1356 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1357 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1358 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1361 linphone_gtk_configure_window(w,"main_window");
1363 gtk_window_set_title(GTK_WINDOW(w),title);
1365 if (start_call_icon){
1366 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1367 create_pixmap (start_call_icon));
1368 if (!buttons_have_borders)
1369 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1372 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1373 create_pixmap (add_call_icon));
1374 if (!buttons_have_borders)
1375 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1377 if (stop_call_icon){
1378 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1379 create_pixmap (stop_call_icon));
1380 if (!buttons_have_borders)
1381 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1384 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1385 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1386 g_object_unref(G_OBJECT(pbuf));
1390 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1392 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1396 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1398 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1399 g_object_unref(G_OBJECT(pbuf));
1404 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1406 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1407 g_object_unref(G_OBJECT(pbuf));
1410 if (linphone_gtk_can_manage_accounts())
1411 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1412 if (update_check_menu){
1413 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1416 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1417 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1418 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1419 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1420 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1424 void linphone_gtk_manage_login(void){
1425 LinphoneCore *lc=linphone_gtk_get_core();
1426 LinphoneProxyConfig *cfg=NULL;
1427 linphone_core_get_default_proxy(lc,&cfg);
1429 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1430 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1431 linphone_gtk_show_login_frame(cfg);
1437 gboolean linphone_gtk_close(GtkWidget *mw){
1438 /*shutdown calls if any*/
1439 LinphoneCore *lc=linphone_gtk_get_core();
1440 if (linphone_core_in_call(lc)){
1441 linphone_core_terminate_all_calls(lc);
1443 linphone_core_enable_video_preview(lc,FALSE);
1444 #ifdef __APPLE__ /*until with have a better option*/
1445 gtk_window_iconify(GTK_WINDOW(mw));
1447 gtk_widget_hide(mw);
1453 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1454 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1455 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1457 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1458 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1465 static void linphone_gtk_init_main_window(){
1466 GtkWidget *main_window;
1468 linphone_gtk_configure_main_window();
1469 linphone_gtk_manage_login();
1471 linphone_gtk_load_identities();
1472 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1473 linphone_gtk_show_friends();
1474 linphone_gtk_connect_digits();
1475 main_window=linphone_gtk_get_main_window();
1476 linphone_gtk_call_log_update(main_window);
1478 linphone_gtk_update_call_buttons (NULL);
1479 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1480 g_signal_connect (G_OBJECT (main_window), "delete-event",
1481 G_CALLBACK (linphone_gtk_close), main_window);
1484 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1485 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1486 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1487 gtk_widget_hide(menubar);
1488 gtk_osxapplication_ready(theMacApp);
1490 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1492 linphone_gtk_check_menu_items();
1496 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1498 const char *lname="undef";
1500 #if defined(__linux) || defined(__APPLE__)
1501 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1520 g_error("Bad level !");
1522 #if defined(__linux) || defined(__APPLE__)
1524 msg=g_strdup_vprintf(fmt,cap);
1527 msg=g_strdup_vprintf(fmt,args);
1529 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1532 linphone_gtk_log_push(lev,fmt,args);
1536 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1537 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1538 linphone_gtk_get_main_window(), "uribar"));
1540 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1542 gtk_entry_set_text(uri_bar, refer_to);
1543 linphone_gtk_start_call(linphone_gtk_get_main_window());
1546 static void linphone_gtk_check_soundcards(){
1547 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1548 if (devices==NULL || devices[0]==NULL){
1549 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1550 _("No sound cards have been detected on this computer.\n"
1551 "You won't be able to send or receive audio calls."));
1555 static void linphone_gtk_quit(void){
1556 linphone_gtk_uninit_instance();
1557 gdk_threads_leave();
1558 linphone_gtk_destroy_log_window();
1559 linphone_core_destroy(the_core);
1560 linphone_gtk_log_uninit();
1568 This is not the correct way to implement block termination.
1569 The good way would be to call gtk_main_quit(), and return TRUE.
1570 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1571 As a result the program cannot exit at all.
1572 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1574 static gboolean on_block_termination(void){
1576 linphone_gtk_quit();
1581 int main(int argc, char *argv[]){
1585 const char *config_file;
1586 const char *factory_config_file;
1588 GtkSettings *settings;
1590 const char *app_name="Linphone";
1592 g_thread_init(NULL);
1595 progpath = strdup(argv[0]);
1597 config_file=linphone_gtk_get_config_file();
1601 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1602 if ((lang=getenv("LANG"))!=NULL){
1605 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1611 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1614 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1617 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1620 setenv("LANG",lang,1);
1625 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1626 if (p==NULL) perror("bindtextdomain failed");
1627 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1628 textdomain (GETTEXT_PACKAGE);
1630 g_message("NLS disabled.\n");
1633 gtk_rc_add_default_file("./gtkrc");
1635 gdk_threads_enter();
1637 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1638 linphone_options,NULL,NULL)){
1639 gdk_threads_leave();
1643 settings=gtk_settings_get_default();
1644 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1645 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1646 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1647 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1649 if (workingdir!=NULL){
1650 if (chdir(workingdir)==-1){
1651 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1655 /* Now, look for the factory configuration file, we do it this late
1656 since we want to have had time to change directory and to parse
1657 the options, in case we needed to access the working directory */
1658 factory_config_file = linphone_gtk_get_factory_config_file();
1660 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1661 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1662 g_warning("This instance is going to exit now.");
1663 gdk_threads_leave();
1667 add_pixmap_directory("pixmaps");
1668 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1671 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1672 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1673 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1674 /*never block termination:*/
1675 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1678 the_ui=linphone_gtk_create_window("main");
1680 linphone_gtk_create_log_window();
1681 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1683 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1685 g_set_application_name(app_name);
1686 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1687 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1689 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1690 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1691 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1692 linphone_gtk_init_main_window();
1693 #ifndef HAVE_GTK_OSX
1694 linphone_gtk_init_status_icon();
1697 linphone_gtk_show_main_window();
1698 linphone_gtk_check_soundcards();
1700 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1701 linphone_gtk_check_for_new_version();
1704 linphone_gtk_quit();
1705 #ifndef HAVE_GTK_OSX
1706 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1707 gtk_status_icon_set_visible(icon,FALSE);