Re: [AD] log window

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


On Fri, 2010-07-02 at 10:54 +0200, Elias Pschernig wrote:
> 
> The log window seems like a good idea though since also in OSX and Linux
> printfs aren't always visible. I can make the GTK version of it.
> 

I made a quick GTK-only patch. Well, threading issues made it less quick
than I would have liked :P Anyway, the API is this:

window = al_open_native_log_window(title, flags)
al_close_native_log_window(window)
al_append_log(window, text)
al_extend_log(window, text)

append adds the text as a new line at the end of the log, extend just
extends the last line but doesn't at a new one. Not sure how to name
those two or whether there should be two functions. Could just as well
require use of "\n" at the end if you want a new line. Should probably
also make it take printf style parameters.

-- 
Elias Pschernig <elias.pschernig@xxxxxxxxxx>
diff --git a/addons/native_dialog/allegro5/allegro_native_dialog.h b/addons/native_dialog/allegro5/allegro_native_dialog.h
index 900591f..3aa25cb 100644
--- a/addons/native_dialog/allegro5/allegro_native_dialog.h
+++ b/addons/native_dialog/allegro5/allegro_native_dialog.h
@@ -53,6 +53,15 @@ ALLEGRO_DIALOG_FUNC(
 ALLEGRO_DIALOG_FUNC(void, al_destroy_native_dialog, (ALLEGRO_NATIVE_DIALOG *fc));
 ALLEGRO_DIALOG_FUNC(uint32_t, al_get_allegro_native_dialog_version, (void));
 
+ALLEGRO_DIALOG_FUNC(ALLEGRO_NATIVE_DIALOG *, al_open_native_log_window,
+   (char const *title, int flags));
+ALLEGRO_DIALOG_FUNC(void, al_close_native_log_window,
+   (ALLEGRO_NATIVE_DIALOG *textlog));
+ALLEGRO_DIALOG_FUNC(void, al_append_log,
+   (ALLEGRO_NATIVE_DIALOG *textlog, char const *text));
+ALLEGRO_DIALOG_FUNC(void, al_extend_log,
+   (ALLEGRO_NATIVE_DIALOG *textlog, char const *text));
+
 #define ALLEGRO_FILECHOOSER_FILE_MUST_EXIST 1
 #define ALLEGRO_FILECHOOSER_SAVE 2
 #define ALLEGRO_FILECHOOSER_FOLDER 4
diff --git a/addons/native_dialog/allegro5/internal/aintern_native_dialog.h b/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
index 8151fff..471b562 100644
--- a/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
+++ b/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
@@ -1,26 +1,44 @@
 #ifndef __al_included_allegro_aintern_native_dialog_h
 #define __al_included_allegro_aintern_native_dialog_h
 
+/* We could use different structs for the different dialogs. But why
+ * bother.
+ */
 struct ALLEGRO_NATIVE_DIALOG
 {
-   ALLEGRO_PATH *initial_path;
    ALLEGRO_USTR *title;
-   ALLEGRO_USTR *heading;
    ALLEGRO_USTR *text;
-   ALLEGRO_USTR *patterns;
-   ALLEGRO_USTR *buttons;
-   ALLEGRO_PATH **paths;
-
    int mode;
+
+   /* Only used by file chooser. */
+   ALLEGRO_PATH *initial_path;
    size_t count;
+   ALLEGRO_PATH **paths;
+   ALLEGRO_USTR *patterns;
+   
+   /* Only used by message box. */
+   ALLEGRO_USTR *heading;
+   ALLEGRO_USTR *buttons;
    int pressed_button;
+   
+   /* Only used by text log. */
+   ALLEGRO_THREAD *thread;
+   ALLEGRO_COND *text_cond;
+   ALLEGRO_MUTEX *text_mutex;
+   bool done;
+   int new_line;
 
    /* Only used by platform implementations. */
    bool is_active;
    ALLEGRO_COND *cond;
+   void *window;
+   void *textview;
 };
 
 extern int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
    ALLEGRO_NATIVE_DIALOG *fd);
+extern void _al_open_native_log_window(ALLEGRO_NATIVE_DIALOG *textlog);
+extern void _al_close_native_log_window(ALLEGRO_NATIVE_DIALOG *textlog);
+void _al_append_to_textlog(ALLEGRO_NATIVE_DIALOG *textlog);
 
 #endif
diff --git a/addons/native_dialog/dialog.c b/addons/native_dialog/dialog.c
index e841ad2..0bb49f4 100644
--- a/addons/native_dialog/dialog.c
+++ b/addons/native_dialog/dialog.c
@@ -109,3 +109,92 @@ void al_show_native_file_dialog(ALLEGRO_NATIVE_DIALOG *fd)
 }
 
 #endif
+
+/* This will only return when the text window is closed. */
+static void *textlog_proc(ALLEGRO_THREAD *thread, void *arg)
+{
+   ALLEGRO_NATIVE_DIALOG *textlog = arg;
+   _al_open_native_log_window(textlog);
+   return thread;
+}
+
+/* Function: al_show_native_textlog
+ */
+ALLEGRO_NATIVE_DIALOG *al_open_native_log_window(
+   char const *title, int flags)
+{
+   ALLEGRO_NATIVE_DIALOG *textlog = al_calloc(1, sizeof *textlog);
+   textlog->title = al_ustr_new(title);
+   textlog->mode = flags;
+   textlog->thread = al_create_thread(textlog_proc, textlog);
+   textlog->text_cond = al_create_cond();
+   textlog->text_mutex = al_create_mutex();
+
+   /* Unlike the other dialogs, this one never blocks as the intended
+    * use case is a log window running in the background for debugging
+    * purposes when no console can be used. Therefore we have it run
+    * in a separate thread.
+    */
+   al_start_thread(textlog->thread);
+   al_lock_mutex(textlog->text_mutex);
+   textlog->done = false;
+   while (!textlog->done) {
+      al_wait_cond(textlog->text_cond, textlog->text_mutex);
+   }
+   al_unlock_mutex(textlog->text_mutex);
+   
+   return textlog;
+}
+
+void al_close_native_log_window(ALLEGRO_NATIVE_DIALOG *textlog)
+{
+   al_lock_mutex(textlog->text_mutex);
+   textlog->done = false;
+
+   _al_close_native_log_window(textlog);
+   
+   while (!textlog->done) {
+      al_wait_cond(textlog->text_cond, textlog->text_mutex);
+   }
+   
+   al_ustr_free(textlog->title);
+
+   al_unlock_mutex(textlog->text_mutex);
+
+   al_destroy_thread(textlog->thread);
+   al_destroy_cond(textlog->text_cond);
+   al_destroy_mutex(textlog->text_mutex);
+   al_free(textlog);
+}
+
+void al_append_or_extend_log(ALLEGRO_NATIVE_DIALOG *textlog,
+   char const *text, bool append)
+{
+   al_lock_mutex(textlog->text_mutex);
+   textlog->text = al_ustr_new(text);
+   textlog->new_line = append;
+   _al_append_to_textlog(textlog);
+   
+   /* We wait until it is appended - that way we are sure it will
+    * work correctly even if 10 threads are calling us 60 times
+    * a second.  (Of course calling us that often would be slow.)
+    */
+   textlog->done = false;
+   while (!textlog->done) {
+      al_wait_cond(textlog->text_cond, textlog->text_mutex);
+   }
+   
+   al_ustr_free(textlog->text);
+
+   al_unlock_mutex(textlog->text_mutex);
+}
+
+void al_append_log(ALLEGRO_NATIVE_DIALOG *textlog, char const *text)
+{
+   al_append_or_extend_log(textlog, text, true);
+}
+
+void al_extend_log(ALLEGRO_NATIVE_DIALOG *textlog, char const *text)
+{
+   al_append_or_extend_log(textlog, text, false);
+}
diff --git a/addons/native_dialog/gtk_dialog.c b/addons/native_dialog/gtk_dialog.c
index 6631b21..7d91e7f 100644
--- a/addons/native_dialog/gtk_dialog.c
+++ b/addons/native_dialog/gtk_dialog.c
@@ -296,3 +296,96 @@ int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
 
    return fd->pressed_button;
 }
+
+
+void _al_open_native_log_window(ALLEGRO_NATIVE_DIALOG *textlog)
+{
+   al_lock_mutex(textlog->text_mutex);
+   
+   gtk_start_and_lock();
+
+   /* Create a new text log window. */
+   GtkWidget *top = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+   gtk_window_set_default_size(GTK_WINDOW(top), 640, 480);
+   gtk_window_set_title(GTK_WINDOW(top), al_cstr(textlog->title));
+   gtk_window_set_deletable(GTK_WINDOW(top), false);
+   g_signal_connect(G_OBJECT(top), "destroy", G_CALLBACK(destroy), textlog);
+   GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
+   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+   gtk_container_add(GTK_CONTAINER(top), scroll);
+   GtkWidget *view = gtk_text_view_new();
+   gtk_container_add(GTK_CONTAINER(scroll), view);
+   gtk_widget_show(view);
+   gtk_widget_show(scroll);
+   gtk_widget_show(top);
+   textlog->window = top;
+   textlog->textview = view;
+
+   /* Now notify al_show_native_textlog that the text log is ready. */
+   textlog->done = true;
+   al_signal_cond(textlog->text_cond);
+   al_unlock_mutex(textlog->text_mutex);
+
+   /* Keep running until the textlog is closed. */
+   gtk_unlock_and_wait(textlog);
+   
+   /* Notify everyone that we're gone. */
+   al_lock_mutex(textlog->text_mutex);
+   textlog->done = true;
+   al_signal_cond(textlog->text_cond);
+   al_unlock_mutex(textlog->text_mutex);
+   
+}
+
+static gboolean _al_append_to_textlog_called_in_gtk_thread(gpointer data)
+{
+   ALLEGRO_NATIVE_DIALOG *textlog = data;
+   al_lock_mutex(textlog->text_mutex);
+
+   GtkTextView *tv = GTK_TEXT_VIEW(textlog->textview);
+   GtkTextBuffer *buffer = gtk_text_view_get_buffer(tv);
+   GtkTextIter iter;
+   
+   gtk_text_buffer_get_end_iter(buffer, &iter);
+   if (!textlog->new_line)
+      gtk_text_iter_backward_chars(&iter, 1);
+
+   gtk_text_buffer_insert(buffer, &iter, al_cstr(textlog->text), -1);
+
+   if (textlog->new_line) {
+      gtk_text_buffer_get_end_iter(buffer, &iter);
+      gtk_text_buffer_insert(buffer, &iter, "\n", -1);
+      
+      gtk_text_buffer_get_end_iter(buffer, &iter);
+      gtk_text_view_scroll_to_iter(tv, &iter, 0, false, 0, 0);
+   }
+   
+   /* Notify the original caller that we are all done. */
+   textlog->done = true;
+   al_signal_cond(textlog->text_cond);
+   al_unlock_mutex(textlog->text_mutex);
+   return false;
+}
+
+void _al_append_to_textlog(ALLEGRO_NATIVE_DIALOG *textlog)
+{
+   gdk_threads_add_timeout(0, _al_append_to_textlog_called_in_gtk_thread, textlog);
+}
+
+static gboolean _al_close_native_log_window_called_in_gtk_thread(gpointer data)
+{
+   ALLEGRO_NATIVE_DIALOG *textlog = data;
+   /* This causes the GTK window as well as all of its children to
+    * be freed. Further it will call the destroy function which we
+    * connected to the destroy signal which in turn causes our
+    * gtk thread to quit.
+    */
+   gtk_widget_destroy(textlog->window);
+   return false;
+}
+
+void _al_close_native_log_window(ALLEGRO_NATIVE_DIALOG *textlog)
+{
+   gdk_threads_add_timeout(0, _al_close_native_log_window_called_in_gtk_thread, textlog);
+}
diff --git a/docs/src/refman/native_dialog.txt b/docs/src/refman/native_dialog.txt
index b76afdf..8d2d712 100644
--- a/docs/src/refman/native_dialog.txt
+++ b/docs/src/refman/native_dialog.txt
@@ -121,3 +121,26 @@ Example:
 
 Returns the (compiled) version of the addon, in the same format as
 [al_get_allegro_version].
+
+## API: al_open_native_log_window
+
+Shows a text window to which you can append log messages with
+[al_append_log]. This can be useful for debugging if you don't want
+to depend on a console being available.
+
+Use [al_close_native_log_window] to close the window again.
+
+## API: al_close_native_log_window
+
+Closes a message log window opened with [al_open_native_log_window]
+earlier.
+
+## API: al_append_log
+
+Appends a line of text to the message log window and scrolls to the
+bottom (if the line would not be visible otherwise).
+
+## API: al_extend_log
+
+Like [al_append_log] but instead of appending a new line, appends to
+the last existing line.
diff --git a/examples/ex_native_filechooser.c b/examples/ex_native_filechooser.c
index 494170c..dd983de 100644
--- a/examples/ex_native_filechooser.c
+++ b/examples/ex_native_filechooser.c
@@ -28,6 +28,31 @@ typedef struct
    ALLEGRO_THREAD *thread;
 } AsyncDialog;
 
+ALLEGRO_NATIVE_DIALOG *textlog;
+
+static void message(char const *format, ...)
+{
+   if (!textlog) return;
+   char str[1024];
+   va_list args;
+   va_start(args, format);
+   vsnprintf(str, sizeof str, format, args);
+   va_end(args);
+   
+   al_append_log(textlog, str);
+}
+
+static void message_ext(char const *format, ...)
+{
+   if (!textlog) return;
+   char str[1024];
+   va_list args;
+   va_start(args, format);
+   vsnprintf(str, sizeof str, format, args);
+   va_end(args);
+   
+   al_extend_log(textlog, str);
+}
 
 /* Our thread to show the native file dialog. */
 static void *async_file_dialog_thread_func(ALLEGRO_THREAD *thread, void *arg)
@@ -156,8 +181,13 @@ int main(void)
    AsyncDialog *message_box = NULL;
    bool redraw = false;
    int button;
+   bool message_log = true;
 
    al_init();
+   
+   textlog = al_open_native_log_window("Log", 0);
+   message("Starting up log window.");
+   
    al_init_image_addon();
    al_init_font_addon();
 
@@ -169,20 +199,28 @@ int main(void)
    al_install_mouse();
    al_install_keyboard();
 
+   message("Creating 640x480 window...");
+
    display = al_create_display(640, 480);
    if (!display) {
+      message_ext("failure.");
       abort_example("Error creating display\n");
       return 1;
    }
+   message_ext("success.");
 
+   message("Loading font '%s'...", "data/fixed_font.tga");
    font = al_load_font("data/fixed_font.tga", 0, 0);
    if (!font) {
+      message_ext("failure.");
       abort_example("Error loading data/fixed_font.tga\n");
       return 1;
    }
+   message_ext("success.");
 
    timer = al_install_timer(1.0 / 30);
 restart:
+   message("Starting main loop.");
    queue = al_create_event_queue();
    al_register_event_source(queue, al_get_keyboard_event_source());
    al_register_event_source(queue, al_get_mouse_event_source());
@@ -191,6 +229,7 @@ restart:
    al_start_timer(timer);
 
    while (1) {
+      float h = al_get_display_height(display);
       ALLEGRO_EVENT event;
       al_wait_for_event(queue, &event);
 
@@ -206,8 +245,19 @@ restart:
        * shown already, we show a new one.
        */
       if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
+          message("Mouse clicked at %d,%d.", event.mouse.x, event.mouse.y);
           if (event.mouse.y > 30) {
-             if (!message_box) {
+             if (event.mouse.y > h - 30) {
+                message_log = !message_log;
+                if (message_log) {
+                   textlog = al_open_native_log_window("Log", 0);
+                }
+                else {
+                   al_close_native_log_window(textlog);
+                   textlog = NULL;
+                }
+             }
+             else if (!message_box) {
                 message_box = spawn_async_message_dialog(display);
                 al_register_event_source(queue, &message_box->event_source);
              }
@@ -261,11 +311,15 @@ restart:
          al_clear_to_color(background);
          al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
          al_draw_textf(font, cur_dialog ? inactive : active, x, y, ALLEGRO_ALIGN_CENTRE, "Open");
+         al_draw_textf(font, cur_dialog ? inactive : active, x, h - 30,
+            ALLEGRO_ALIGN_CENTRE, message_log ? "Close Message Log" : "Open Message Log");
          if (old_dialog)
             show_files_list(old_dialog->file_dialog, font, info);
          al_flip_display();
       }
    }
+   
+   message("Exiting.");
 
    al_destroy_event_queue(queue);
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/