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.
24 #include <sys/types.h>
27 extern gchar *linphone_logfile;
29 static GtkWidget *log_window=NULL;
30 static GStaticMutex log_mutex=G_STATIC_MUTEX_INIT;
31 static GList *log_queue=NULL;
32 static const char *dateformat="%Y%m%d-%H:%M:%S";
34 #define LOG_MAX_CHARS 1000000 /*1 mega bytes of traces*/
36 typedef struct _LinphoneGtkLog{
43 * Module to log to a file
46 /* Marker to insert as a line at the start of every log file */
47 #define LOGFILE_MARKER_START "----<start>----"
48 /* Marker to insert as a line at the end of every log file */
49 #define LOGFILE_MARKER_STOP "----<end>----"
50 /* Number of files to keep in history, log file rotation will be
52 #define LOGFILE_ROTATION 4
53 /* Pointer to opened log file */
54 static FILE *_logfile = NULL;
57 /* Called on exit, print out the marker, close the file and avoid to
59 void linphone_gtk_log_uninit()
61 if (_logfile != NULL) {
62 fprintf(_logfile, "%s\n", LOGFILE_MARKER_STOP);
68 /* Called when we start logging, find a good place for the log files,
69 perform rotation, insert the start marker and return the pointer to
70 the file that should be used for logging, or NULL on errors or if
72 static FILE *linphone_gtk_log_init()
74 static char _logdir[1024];
75 static char _logfname[1024];
76 static gboolean _log_init = FALSE;
77 const char *dst_fname=NULL;
80 if (linphone_gtk_get_core()!=NULL){
81 dst_fname = linphone_gtk_get_ui_config("logfile",NULL);
82 dateformat=linphone_gtk_get_ui_config("logfile_date_format",dateformat);
84 /* For anything to happen, we need a logfile configuration variable,
85 this is our trigger */
87 /* arrange for _logdir to contain a
88 directory that has been created and _logfname to contain the
89 path to a file to which we will log */
91 const char *appdata=getenv("LOCALAPPDATA");
93 snprintf(_logdir, sizeof(_logdir),"%s\\Linphone", appdata);
98 #define PATH_SEPARATOR '\\'
100 const char *home=getenv("HOME");
102 snprintf(_logdir, sizeof(_logdir),"%s/.linphone", home);
103 mkdir(_logdir,S_IRUSR | S_IWUSR | S_IRGRP);
107 #define PATH_SEPARATOR '/'
109 if (_logdir[0] != '\0') {
110 /* We have a directory, fix the path to the log file in it and
111 open the file so that we will be appending to it. */
112 snprintf(_logfname, sizeof(_logfname), "%s%c%s",_logdir, PATH_SEPARATOR, dst_fname);
114 }else if (linphone_logfile!=NULL){
115 snprintf(_logfname,sizeof(_logfname),"%s",linphone_logfile);
118 if (_logfname[0]!='\0'){
119 /* If the constant LOGFILE_ROTATION is greater than zero, then
120 we kick away a simple rotation that will ensure that there
121 are never more than LOGFILE_ROTATION+1 old copies of the
122 log file on the disk. The oldest file is always rotated
123 "away" as expected. Rotated files have the same name as
124 the main log file, though with a number 0..LOGFILE_ROTATION
125 at the end, where the greater the number is, the older the
127 if (ortp_file_exist(_logfname)==0 && LOGFILE_ROTATION > 0) {
129 char old_fname[1024];
130 char new_fname[1024];
132 /* Rotate away existing files. We make sure to remove the
133 old files otherwise rename() would not work properly. We
134 have to loop in reverse here. */
135 for (i=LOGFILE_ROTATION-1;i>=0;i--) {
136 snprintf(old_fname, sizeof(old_fname), "%s%c%s.%d",
137 _logdir, PATH_SEPARATOR, dst_fname, i);
138 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
139 _logdir, PATH_SEPARATOR, dst_fname, i+1);
140 if (ortp_file_exist(old_fname)==0) {
141 if (ortp_file_exist(new_fname)==0)
143 rename(old_fname, new_fname);
146 /* Move current log file as the first of the rotation. Make
147 sure to remove the old .0 also, since otherwise rename()
148 would not work as expected. */
149 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
150 _logdir, PATH_SEPARATOR, dst_fname, 0);
151 if (ortp_file_exist(new_fname)==0)
153 rename(_logfname, new_fname);
155 /* Start a new log file and mark that we have now initialised */
156 _logfile = fopen(_logfname, "w");
157 fprintf(_logfile, "%s\n", LOGFILE_MARKER_START);
164 static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg)
169 outlog = linphone_gtk_log_init();
170 if (outlog != NULL) {
171 /* We have an opened file and we have initialised properly, it's
172 time to write all these log messages. We convert the log level
173 from oRTP into something readable and timestamp each log
174 message. The format of the time stamp can be controlled by
175 logfile_date_format in the GtkUi section of the config file,
176 but it defaults to something compact, but yet readable. */
177 const char *lname="undef";
180 /* Convert level constant to text */
201 /* Get current time and format it properly */
203 strftime(date, sizeof(date), dateformat, localtime(&now));
204 /* Now print out the message to the logfile. We don't flush,
205 maybe we should do to ensure that we have all the messages in
206 case of a crash (which is one of the main reasons we have a
207 log facility in the first place). */
208 fprintf(outlog, "[%s] [%s] %s\n", date, lname, msg);
212 void linphone_gtk_log_hide(){
214 gtk_widget_hide(log_window);
217 void linphone_gtk_create_log_window(void){
219 log_window=linphone_gtk_create_window("log");
220 b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview")));
221 gtk_text_buffer_create_tag(b,"red","foreground","red",NULL);
222 gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL);
223 /*prevent the log window from being destroyed*/
224 g_signal_connect (G_OBJECT (log_window), "delete-event",
225 G_CALLBACK (gtk_widget_hide_on_delete), log_window);
229 void linphone_gtk_destroy_log_window(void){
230 GtkWidget *w=log_window;
231 g_static_mutex_lock(&log_mutex);
233 gtk_widget_destroy(w);
234 g_static_mutex_unlock(&log_mutex);
237 void linphone_gtk_log_show(void){
238 gtk_widget_show(log_window);
239 gtk_window_present(GTK_WINDOW(log_window));
242 static void linphone_gtk_display_log(GtkTextView *v, OrtpLogLevel lev, const char *msg){
243 GtkTextIter iter,begin;
246 const char *lname="undef";
248 b=gtk_text_view_get_buffer(v);
266 g_error("Bad level !");
269 gtk_text_buffer_get_end_iter(b,&iter);
270 off=gtk_text_iter_get_offset(&iter);
271 gtk_text_buffer_insert(b,&iter,lname,-1);
272 gtk_text_buffer_get_end_iter(b,&iter);
273 gtk_text_buffer_insert(b,&iter,": ",-1);
274 gtk_text_buffer_get_end_iter(b,&iter);
275 gtk_text_buffer_insert(b,&iter,msg,-1);
276 gtk_text_buffer_get_end_iter(b,&iter);
277 gtk_text_buffer_insert(b,&iter,"\n",-1);
278 gtk_text_buffer_get_end_iter(b,&iter);
279 gtk_text_buffer_get_iter_at_offset(b,&begin,off);
280 if (lev==ORTP_ERROR || lev==ORTP_FATAL) gtk_text_buffer_apply_tag_by_name(b,"red",&begin,&iter);
281 else if (lev==ORTP_WARNING) gtk_text_buffer_apply_tag_by_name(b,"orange",&begin,&iter);
283 while(gtk_text_buffer_get_char_count(b)>LOG_MAX_CHARS){
284 GtkTextIter iter_line_after;
285 gtk_text_buffer_get_start_iter(b,&iter);
286 iter_line_after=iter;
287 if (gtk_text_iter_forward_line(&iter_line_after)){
288 gtk_text_buffer_delete(b,&iter,&iter_line_after);
294 static void stick_to_end(GtkTextView *v){
297 b=gtk_text_view_get_buffer(v);
298 gtk_text_buffer_get_end_iter(b,&iter);
299 gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,1.0,0);
302 void linphone_gtk_log_scroll_to_end(GtkToggleButton *button){
303 if (gtk_toggle_button_get_active(button)){
304 GtkTextView *v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
310 * called from Gtk main loop.
312 gboolean linphone_gtk_check_logs(){
315 if (log_window) v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
316 g_static_mutex_lock(&log_mutex);
317 for(elem=log_queue;elem!=NULL;elem=elem->next){
318 LinphoneGtkLog *lgl=(LinphoneGtkLog*)elem->data;
319 if (v) linphone_gtk_display_log(v,lgl->lev,lgl->msg);
323 if (log_queue) g_list_free(log_queue);
325 g_static_mutex_unlock(&log_mutex);
327 linphone_gtk_log_scroll_to_end(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(log_window,"scroll_to_end")));
333 * Called from any linphone thread.
335 void linphone_gtk_log_push(OrtpLogLevel lev, const char *fmt, va_list args){
336 gchar *msg=g_strdup_vprintf(fmt,args);
337 LinphoneGtkLog *lgl=g_new(LinphoneGtkLog,1);
340 g_static_mutex_lock(&log_mutex);
341 log_queue=g_list_append(log_queue,lgl);
342 linphone_gtk_log_file(lev, msg);
343 g_static_mutex_unlock(&log_mutex);
346 void linphone_gtk_log_clear(void){
348 GtkTextIter end,begin;
351 v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
352 b=gtk_text_view_get_buffer(v);
353 gtk_text_buffer_get_start_iter(b,&begin);
354 gtk_text_buffer_get_end_iter(b,&end);
355 gtk_text_buffer_delete(b,&begin,&end);