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>
28 static GtkWidget *log_window=NULL;
29 static GStaticMutex log_mutex=G_STATIC_MUTEX_INIT;
30 static GList *log_queue=NULL;
32 typedef struct _LinphoneGtkLog{
39 * Module to log to a file
42 /* Marker to insert as a line at the start of every log file */
43 #define LOGFILE_MARKER_START "----<start>----"
44 /* Marker to insert as a line at the end of every log file */
45 #define LOGFILE_MARKER_STOP "----<end>----"
46 /* Number of files to keep in history, log file rotation will be
48 #define LOGFILE_ROTATION 4
49 /* Pointer to opened log file */
50 static FILE *_logfile = NULL;
53 /* Called on exit, print out the marker, close the file and avoid to
55 static void linphone_gtk_log_uninit()
57 if (_logfile != NULL) {
58 fprintf(_logfile, "%s\n", LOGFILE_MARKER_STOP);
64 /* Called when we start logging, find a good place for the log files,
65 perform rotation, insert the start marker and return the pointer to
66 the file that should be used for logging, or NULL on errors or if
68 static FILE *linphone_gtk_log_init()
70 static char _logdir[1024];
71 static char _logfname[1024];
72 static gboolean _log_init = FALSE;
73 const char *dst_fname;
76 dst_fname = linphone_gtk_get_ui_config("logfile",NULL);
77 /* For anything to happen, we need a logfile configuration variable,
78 this is our trigger */
80 /* arrange for _logdir to contain a
81 directory that has been created and _logfname to contain the
82 path to a file to which we will log */
84 const char *appdata=getenv("LOCALAPPDATA");
86 snprintf(_logdir, sizeof(_logdir),"%s\\Linphone", appdata);
91 #define PATH_SEPARATOR '\\'
93 const char *home=getenv("HOME");
95 snprintf(_logdir, sizeof(_logdir),"%s/.linphone", home);
96 mkdir(_logdir,S_IRUSR | S_IWUSR | S_IRGRP);
100 #define PATH_SEPARATOR '/'
102 /* We have a directory, fix the path to the log file in it and
103 open the file so that we will be appending to it. */
104 if (_logdir[0] != '\0') {
105 snprintf(_logfname, sizeof(_logfname), "%s%c%s",
106 _logdir, PATH_SEPARATOR, dst_fname);
107 /* If the constant LOGFILE_ROTATION is greater than zero, then
108 we kick away a simple rotation that will ensure that there
109 are never more than LOGFILE_ROTATION+1 old copies of the
110 log file on the disk. The oldest file is always rotated
111 "away" as expected. Rotated files have the same name as
112 the main log file, though with a number 0..LOGFILE_ROTATION
113 at the end, where the greater the number is, the older the
115 if (ortp_file_exist(_logfname)==0 && LOGFILE_ROTATION > 0) {
117 char old_fname[1024];
118 char new_fname[1024];
120 /* Rotate away existing files. We make sure to remove the
121 old files otherwise rename() would not work properly. We
122 have to loop in reverse here. */
123 for (i=LOGFILE_ROTATION-1;i>=0;i--) {
124 snprintf(old_fname, sizeof(old_fname), "%s%c%s.%d",
125 _logdir, PATH_SEPARATOR, dst_fname, i);
126 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
127 _logdir, PATH_SEPARATOR, dst_fname, i+1);
128 if (ortp_file_exist(old_fname)==0) {
129 if (ortp_file_exist(new_fname)==0)
131 rename(old_fname, new_fname);
134 /* Move current log file as the first of the rotation. Make
135 sure to remove the old .0 also, since otherwise rename()
136 would not work as expected. */
137 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
138 _logdir, PATH_SEPARATOR, dst_fname, 0);
139 if (ortp_file_exist(new_fname)==0)
141 rename(_logfname, new_fname);
143 /* Start a new log file and mark that we have now initialised */
144 _logfile = fopen(_logfname, "w");
145 fprintf(_logfile, "%s\n", LOGFILE_MARKER_START);
153 static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg)
159 lc = linphone_gtk_get_core();
160 /* Nothing to do until the core has initialised */
164 /* lc->config will turn NULL at exit, close the file to flush and
165 return to stop logging */
166 if (linphone_core_get_config(lc) == NULL) {
167 linphone_gtk_log_uninit();
171 outlog = linphone_gtk_log_init();
172 if (outlog != NULL) {
173 /* We have an opened file and we have initialised properly, it's
174 time to write all these log messages. We convert the log level
175 from oRTP into something readable and timestamp each log
176 message. The format of the timestamp can be controlled by
177 logfile_date_format in the GtkUi section of the config file,
178 but it defaults to something compact, but yet readable. */
179 const char *lname="undef";
180 const char *dateformat=linphone_gtk_get_ui_config("logfile_date_format",
184 /* Convert level constant to text */
205 /* Get current time and format it properly */
207 strftime(date, sizeof(date), dateformat, localtime(&now));
208 /* Now print out the message to the logfile. We don't flush,
209 maybe we should do to ensure that we have all the messages in
210 case of a crash (which is one of the main reasons we have a
211 log facility in the first place). */
212 fprintf(outlog, "[%s] [%s] %s\n", date, lname, msg);
218 static gboolean delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
220 gtk_widget_hide (widget);
224 void linphone_gtk_log_hide(){
226 gtk_widget_hide(log_window);
229 void linphone_gtk_create_log_window(void){
231 log_window=linphone_gtk_create_window("log");
232 b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview")));
233 gtk_text_buffer_create_tag(b,"red","foreground","red",NULL);
234 gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL);
235 /*prevent the log window from being destroyed*/
236 g_signal_connect (G_OBJECT (log_window), "delete-event",
237 G_CALLBACK (delete_event_cb), NULL);
241 void linphone_gtk_destroy_log_window(void){
242 GtkWidget *w=log_window;
243 g_static_mutex_lock(&log_mutex);
245 gtk_widget_destroy(w);
246 g_static_mutex_unlock(&log_mutex);
249 void linphone_gtk_log_show(void){
250 gtk_widget_show(log_window);
251 gtk_window_present(GTK_WINDOW(log_window));
254 static void linphone_gtk_display_log(OrtpLogLevel lev, const char *msg){
255 GtkTextIter iter,begin;
257 static GtkTextView *v=NULL;
259 const char *lname="undef";
261 if (log_window==NULL) {
265 if (v==NULL) v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
266 b=gtk_text_view_get_buffer(v);
284 g_error("Bad level !");
286 gtk_text_buffer_get_end_iter(b,&iter);
287 off=gtk_text_iter_get_offset(&iter);
288 gtk_text_buffer_insert(b,&iter,lname,-1);
289 gtk_text_buffer_get_end_iter(b,&iter);
290 gtk_text_buffer_insert(b,&iter,": ",-1);
291 gtk_text_buffer_get_end_iter(b,&iter);
292 gtk_text_buffer_insert(b,&iter,msg,-1);
293 gtk_text_buffer_get_end_iter(b,&iter);
294 gtk_text_buffer_insert(b,&iter,"\n",-1);
295 gtk_text_buffer_get_end_iter(b,&iter);
296 gtk_text_buffer_get_iter_at_offset(b,&begin,off);
297 if (lev==ORTP_ERROR || lev==ORTP_FATAL) gtk_text_buffer_apply_tag_by_name(b,"red",&begin,&iter);
298 else if (lev==ORTP_WARNING) gtk_text_buffer_apply_tag_by_name(b,"orange",&begin,&iter);
299 gtk_text_buffer_get_end_iter(b,&iter);
300 //gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,0,0);
303 gboolean linphone_gtk_check_logs(){
305 g_static_mutex_lock(&log_mutex);
306 for(elem=log_queue;elem!=NULL;elem=elem->next){
307 LinphoneGtkLog *lgl=(LinphoneGtkLog*)elem->data;
308 linphone_gtk_display_log(lgl->lev,lgl->msg);
312 if (log_queue) g_list_free(log_queue);
314 g_static_mutex_unlock(&log_mutex);
318 void linphone_gtk_log_push(OrtpLogLevel lev, const char *fmt, va_list args){
319 gchar *msg=g_strdup_vprintf(fmt,args);
320 LinphoneGtkLog *lgl=g_new(LinphoneGtkLog,1);
323 g_static_mutex_lock(&log_mutex);
324 log_queue=g_list_append(log_queue,lgl);
325 linphone_gtk_log_file(lev, msg);
326 g_static_mutex_unlock(&log_mutex);