Compare commits

...

19 Commits

Author SHA1 Message Date
5dbafcb8d5 Merge branch 'multithread-dev' into dev 2019-08-24 14:08:04 +02:00
8d57d63cf3 Update doxyfile and enable timestanp in Latex 2019-08-24 13:59:05 +02:00
a2bcda6752 Update doxygen 2019-08-24 13:50:55 +02:00
17af08b04d GdsOutputrenderer: progress-changed signal: Status message is now freed inside the GdsOutputRenderer.
This is safe because the signals are handled back to back inside the emit function. Therefore, it can be freed directly after emission. This solves the problem of the status message not being freed if no handler is connected to the signal.
2019-08-24 13:49:33 +02:00
94ac44ddc5 Set task name of rendering GTask 2019-08-23 21:50:16 +02:00
1cbacef56c Fix reference counting issue of the LayerSettings inside the
GdsOutputRenderer class.

The LayerSettings element is now disposed of properly.
2019-08-23 21:40:21 +02:00
cae6a9c6c3 LayerSettings: Fix memory leak: GList of layer infos was not freed. Add
dispose to LayerSettings which takes care of this task.
2019-08-23 21:38:59 +02:00
d5dde3658d Enable Release build in PKGBUILD and also use this configuration in the compilation guide 2019-08-23 18:19:46 +02:00
b6bf0c30bf Fix uninitialized variable warning 2019-08-22 19:37:26 +02:00
c908a8be47 Fix bug in progress update from rendering thread which results in the creation of multipe idel callbacks at the same time. 2019-08-22 19:31:47 +02:00
7aa7a0c773 Fix unnecessary whitespace 2019-08-22 19:01:45 +02:00
a10c09c674 Add preliminary status upograde outputs to cairo and latex renderers 2019-08-22 18:56:18 +02:00
a0d19bee39 Add progress updated callback to gui to update titlebar 2019-08-22 18:55:56 +02:00
4115fd97af Add progress updated signal to gds output renderer that can be used when called asynchronously 2019-08-22 18:55:14 +02:00
32b8c4ccd3 Add simple multithreading support. Activity bar is not yet used for rendering status updates 2019-08-21 19:30:52 +02:00
1584769a51 fix deadlock in mutex usage 2019-08-21 19:30:19 +02:00
dc4b377b13 Add preliminary set_busy fuinction to activity bar 2019-08-21 19:29:22 +02:00
08722cd6f9 Add function in GUI to determine the current button states to prevent multiple triggers for rendering/reloading the library during rendering 2019-08-21 18:34:40 +02:00
4b238c55ea Replace 0 by 0.0 for double value comparison. This is nicer. 2019-08-20 22:42:44 +02:00
16 changed files with 322 additions and 54 deletions

View File

@ -1,4 +1,4 @@
/**
* @defgroup Cairo-Renderer Cairo Renderer
* @ingroup renderers
* @ingroup GdsOutputRenderer
*/

View File

@ -1,4 +1,15 @@
/**
* @defgroup external-renderer External Shared Object Renderer
* @ingroup renderers
* @defgroup ExternalRenderer External Shared Object Renderer
* @ingroup GdsOutputRenderer
*
* @subsection ExternalRendererProps Properties
* This class inherits all properties from its parent @ref GdsOutputRenderer.
* In addition to that, it implements the following properties:
*
* Property Name | Description
* -----------------|----------------------------------------------------------------
* shared-object-path | Path to the shared object used for rendering
*
* All these properties have to be set for rendering.
*
*/

View File

@ -0,0 +1,34 @@
/**
* @defgroup GdsOutputRenderer GDS Output Renderer base class
*
* The renderers are used to convert the cell structures read from the GDS layout file
* into different output formats.
*
* The GdsOutputRenderer base class is used to derive all renderers from.
*
* @warning Although the GdsOutputRenderer class provides compatibility for asynchronous rendering,
* the class is not thread safe / re-entrant. Only use it from a signle context. Not even the rendering function called is allowed to modifiy this object.
*
* A allowed function to be called from the async rendering thread is #gds_output_renderer_update_gui_status_from_async and the get functions for the properties.
*
* @note The context that owned the renderer has to ensure that only one rendering is active at a time for a single instance of a renderer.
*
* By default this class implements the following features:
*
* @subsection GdsOutputRendererProps Properties
* Property Name | Description
* -----------------|----------------------------------------------------------------
* layer-settings | LayerSettings object containing the layer rendering information
* output-file | Output file name for rendering
*
* All these properties have to be set for rendering.
*
* @subsection GdsOutputRendererSignals Signals / Events
* Signal Name | Description | Callback prototype
* -----------------|-------------------------------------------------|-----------------------------------------------------------
* async-finished | The asynchronous rendering is finished | void callback(GdsOutputRenderer *src, gpointer user_data)
* progress-changed | The asynchronous rendering progress changed | void callback(GdsOutputRenderer *src, const char *progress, gpointer user_data)
*
* @note The `char *progress` supplied to the callback function must not be modified or freed.
*
*/

View File

@ -1,4 +1,16 @@
/**
* @defgroup LaTeX-Renderer LaTeX/TikZ Renderer
* @ingroup renderers
* @ingroup GdsOutputRenderer
*
* This is the class implementing the Latex / tikz output rendering
* @subsection LaTeXRendererProps Properties
* This class inherits all properties from its parent @ref GdsOutputRenderer.
* In addition to that, it implements the following properties:
*
* Property Name | Description
* -----------------|----------------------------------------------------------------
* standalone | Configure output LaTeX document to be standalone compilable (requires standalone documentclass)
* pdf-layers | Create OCG layers in LaTeX output
*
*/

View File

@ -1,9 +0,0 @@
/**
* @defgroup renderers Output Renderers
*
* The renderers are used to convert the cell structures read from the GDS layout file
* into different output formats.
*
* Currently the renders are statically implemented without the use of GObjects.
* This will probably change in future releases in order to make it easier to integrate new rendering methods.
*/

View File

@ -47,6 +47,11 @@ enum gds_render_gui_signal_sig_ids {SIGNAL_WINDOW_CLOSED = 0, SIGNAL_COUNT};
static guint gds_render_gui_signals[SIGNAL_COUNT];
struct gui_button_states {
gboolean rendering_active;
gboolean valid_cell_selected;
};
struct _GdsRenderGui {
/* Parent GObject */
GObject parent;
@ -54,6 +59,9 @@ struct _GdsRenderGui {
/* Custom fields */
GtkWindow *main_window;
GtkWidget *convert_button;
GtkWidget *open_button;
GtkWidget *load_layer_button;
GtkWidget *save_layer_button;
GtkTreeStore *cell_tree_store;
GtkWidget *cell_search_entry;
LayerSelector *layer_selector;
@ -62,6 +70,7 @@ struct _GdsRenderGui {
ActivityBar *activity_status_bar;
struct render_settings render_dialog_settings;
ColorPalette *palette;
struct gui_button_states button_state_data;
};
G_DEFINE_TYPE(GdsRenderGui, gds_render_gui, G_TYPE_OBJECT)
@ -240,6 +249,23 @@ end_destroy:
gtk_widget_destroy(open_dialog);
}
static void process_button_state_changes(GdsRenderGui *self)
{
gboolean convert_button_state = FALSE;
gboolean open_gds_button_state = FALSE;
/* Calculate states */
if (!self->button_state_data.rendering_active) {
open_gds_button_state = TRUE;
if (self->button_state_data.valid_cell_selected)
convert_button_state = TRUE;
}
/* Apply states */
gtk_widget_set_sensitive(self->convert_button, convert_button_state);
gtk_widget_set_sensitive(self->open_button, open_gds_button_state);
}
/**
* @brief Callback for auto coloring button
* @param button
@ -254,6 +280,29 @@ static void on_auto_color_clicked(gpointer button, gpointer user)
layer_selector_auto_color_layers(self->layer_selector, self->palette, 1.0);
}
static void async_rendering_finished_callback(GdsOutputRenderer *renderer, gpointer gui)
{
GdsRenderGui *self;
self = RENDERER_GUI(gui);
self->button_state_data.rendering_active = FALSE;
process_button_state_changes(self);
activity_bar_set_ready(self->activity_status_bar);
g_object_unref(renderer);
}
static void async_rendering_status_update_callback(GdsOutputRenderer *renderer, const char *status_message, gpointer data)
{
GdsRenderGui *gui;
(void)renderer;
gui = RENDERER_GUI(data);
activity_bar_set_busy(gui->activity_status_bar, status_message);
}
/**
* @brief Convert button callback
* @param button
@ -283,6 +332,10 @@ static void on_convert_clicked(gpointer button, gpointer user)
if (!self)
return;
/* Abort if rendering is already active */
if (self->button_state_data.rendering_active == TRUE)
return;
sett = &self->render_dialog_settings;
/* Get selected cell */
@ -369,16 +422,33 @@ static void on_convert_clicked(gpointer button, gpointer user)
case RENDERER_CAIROGRAPHICS_PDF:
render_engine = GDS_RENDER_OUTPUT_RENDERER(cairo_renderer_new_pdf());
break;
default:
/* Abort rendering */
render_engine = NULL;
break;
}
if (render_engine) {
gds_output_renderer_set_output_file(render_engine, file_name);
gds_output_renderer_set_layer_settings(render_engine, layer_settings);
/* Prevent user from overwriting library or triggering additional conversion */
self->button_state_data.rendering_active = TRUE;
process_button_state_changes(self);
g_signal_connect(render_engine, "async-finished", G_CALLBACK(async_rendering_finished_callback),
self);
activity_bar_set_busy(self->activity_status_bar, "Rendering cell...");
/* TODO: Replace this with asynchronous rendering. However, this fixes issue #19 */
gds_output_renderer_render_output(render_engine, cell_to_render, sett->scale);
g_object_unref(render_engine);
g_signal_connect(render_engine, "progress-changed",
G_CALLBACK(async_rendering_status_update_callback), self);
gds_output_renderer_render_output_async(render_engine, cell_to_render, sett->scale);
//self->button_state_data.rendering_active = FALSE;
//g_object_unref(render_engine);
}
g_free(file_name);
@ -407,7 +477,6 @@ static void cell_tree_view_activated(gpointer tree_view, GtkTreePath *path,
on_convert_clicked(NULL, user);
}
/**
* @brief Callback for cell-selection change event
*
@ -423,10 +492,12 @@ static void cell_selection_changed(GtkTreeSelection *sel, GdsRenderGui *self)
if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
/* Node selected. Show button */
gtk_widget_set_sensitive(self->convert_button, TRUE);
self->button_state_data.valid_cell_selected = TRUE;
} else {
gtk_widget_set_sensitive(self->convert_button, FALSE);
self->button_state_data.valid_cell_selected = FALSE;
}
process_button_state_changes(self);
}
static void sort_up_callback(GtkWidget *widget, gpointer user)
@ -466,6 +537,9 @@ static void gds_render_gui_dispose(GObject *gobject)
g_clear_object(&self->cell_search_entry);
g_clear_object(&self->activity_status_bar);
g_clear_object(&self->palette);
g_clear_object(&self->load_layer_button);
g_clear_object(&self->save_layer_button);
g_clear_object(&self->open_button);
if (self->main_window) {
g_signal_handlers_destroy(self->main_window);
@ -521,7 +595,8 @@ static void gds_render_gui_init(GdsRenderGui *self)
self->cell_tree_store = cell_selector_stores->base_store;
self->main_window = GTK_WINDOW(gtk_builder_get_object(main_builder, "main-window"));
g_signal_connect(GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-gds")),
self->open_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-gds"));
g_signal_connect(self->open_button,
"clicked", G_CALLBACK(on_load_gds), (gpointer)self);
self->convert_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "convert-button"));
@ -550,11 +625,10 @@ static void gds_render_gui_init(GdsRenderGui *self)
g_signal_connect(sort_down_button, "clicked", G_CALLBACK(sort_down_callback), self);
/* Set buttons for loading and saving */
layer_selector_set_load_mapping_button(self->layer_selector,
GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-mapping")),
self->main_window);
layer_selector_set_save_mapping_button(self->layer_selector, GTK_WIDGET(gtk_builder_get_object(main_builder, "button-save-mapping")),
self->main_window);
self->load_layer_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-mapping"));
self->save_layer_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-save-mapping"));
layer_selector_set_load_mapping_button(self->layer_selector, self->load_layer_button, self->main_window);
layer_selector_set_save_mapping_button(self->layer_selector, self->save_layer_button, self->main_window);
/* Connect delete-event */
g_signal_connect(GTK_WIDGET(self->main_window), "delete-event",
@ -579,6 +653,10 @@ static void gds_render_gui_init(GdsRenderGui *self)
g_object_unref(main_builder);
/* Setup default button sensibility data */
self->button_state_data.rendering_active = FALSE;
self->button_state_data.valid_cell_selected = FALSE;
/* Reference all objects referenced by this object */
g_object_ref(self->activity_status_bar);
g_object_ref(self->main_window);
@ -588,6 +666,9 @@ static void gds_render_gui_init(GdsRenderGui *self)
g_object_ref(self->cell_tree_store);
g_object_ref(self->cell_search_entry);
g_object_ref(self->palette);
g_object_ref(self->open_button);
g_object_ref(self->load_layer_button);
g_object_ref(self->save_layer_button);
}
GdsRenderGui *gds_render_gui_new()

View File

@ -33,7 +33,7 @@
#include <gds-render/geometric/vector-operations.h>
#define ABS_DBL(a) ((a) < 0 ? -(a) : (a))
#define ABS_DBL(a) ((a) < 0.0 ? -(a) : (a))
double vector_2d_scalar_multipy(struct vector_2d *a, struct vector_2d *b)
{

View File

@ -58,7 +58,7 @@ enum graphics_type
enum path_type {PATH_FLUSH = 0, PATH_ROUNDED = 1, PATH_SQUARED = 2}; /**< Path line caps */
/**
* @brief A point in the 2D plane. Sometimes references as vertex
* @brief A point in the 2D plane. Sometimes reffered to as vertex
*/
struct gds_point {
int x;

View File

@ -144,6 +144,16 @@ void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSe
*/
int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale);
/**
* @brief This function emits the 'progress-changed' in the thread/context that triggered an asynchronous rendering
*
* If the rendering is not asynchronous, this function has no effect.
*
* @param renderer GdsOutputrenderer object
* @param status Status to supply to signal emission
*/
void gds_output_renderer_update_gui_status_from_async(GdsOutputRenderer *renderer, const char *status);
G_END_DECLS
#endif /* _GDS_OUTPUT_RENDERER_H_ */

View File

@ -53,6 +53,14 @@ ActivityBar *activity_bar_new();
*/
void activity_bar_set_ready(ActivityBar *bar);
/**
* @brief Enable spinner and set \p text. If text is NULL, 'Working...' is displayed
* @param bar Activity bar object
* @param text Text to display, may be NULL
*/
void activity_bar_set_busy(ActivityBar *bar, const char *text);
G_END_DECLS
#endif /* __LAYER_ELEMENT_H__ */

View File

@ -39,9 +39,36 @@ static void layer_settings_init(LayerSettings *self)
self->layer_infos = NULL;
}
static void layer_info_delete_with_name(struct layer_info *const info)
{
if (!info)
return;
if (info->name)
free(info->name);
free(info);
}
static void layer_settings_dispose(GObject *obj)
{
LayerSettings *self;
self = GDS_RENDER_LAYER_SETTINGS(obj);
if (self->layer_infos) {
g_list_free_full(self->layer_infos, (GDestroyNotify)layer_info_delete_with_name);
self->layer_infos = NULL;
}
G_OBJECT_CLASS(layer_settings_parent_class)->dispose(obj);
}
static void layer_settings_class_init(LayerSettingsClass *klass)
{
(void)klass;
GObjectClass *oclass;
oclass = G_OBJECT_CLASS(klass);
oclass->dispose = layer_settings_dispose;
return;
}
@ -76,16 +103,6 @@ static struct layer_info *layer_info_copy(const struct layer_info * const info)
return copy;
}
static void layer_info_delete_with_name(struct layer_info *const info)
{
if (!info)
return;
if (info->name)
free(info->name);
free(info);
}
LayerSettings *layer_settings_new()
{
return g_object_new(GDS_RENDER_TYPE_LAYER_SETTINGS, NULL);

View File

@ -400,6 +400,7 @@ static int cairo_renderer_render_output(GdsOutputRenderer *renderer,
else
pdf_file = output_file;
gds_output_renderer_update_gui_status_from_async(renderer, "Rendering Cairo Output...");
ret = cairo_renderer_render_cell_to_vector_file(cell, layer_infos, pdf_file, svg_file, scale);
if (settings)

View File

@ -50,6 +50,7 @@ G_DEFINE_TYPE(ExternalRenderer, external_renderer, GDS_RENDER_TYPE_OUTPUT_RENDER
* @param toplevel_cell Cell to render
* @param layer_info_list Layer information (Color etc.)
* @param output_file Destination file
* @param scale the scaling value to scale the output cell down by.
* @param so_path Path to shared object
* @return 0 if successful
*/

View File

@ -20,9 +20,6 @@
/**
* @file gds-output-renderer.c
* @brief Base GObject class for output renderers
*
* All output renderers are derived from this class
*
* @author Mario Hüttel <mario.huettel@gmx.net>
*/
@ -38,13 +35,20 @@ struct renderer_params {
double scale;
};
struct idle_function_params {
GMutex message_lock;
char *status_message;
};
typedef struct {
gchar *output_file;
LayerSettings *layer_settings;
GMutex settings_lock;
gboolean mutex_init_status;
GTask *task;
GMainContext *main_context;
struct renderer_params async_params;
struct idle_function_params idle_function_parameters;
gpointer padding[11];
} GdsOutputRendererPrivate;
@ -56,7 +60,7 @@ enum {
G_DEFINE_TYPE_WITH_PRIVATE(GdsOutputRenderer, gds_output_renderer, G_TYPE_OBJECT)
enum gds_output_renderer_signal_ids {ASYNC_FINISHED = 0, ASYNC_PROGRESS_UPDATE, GDS_OUTPUT_RENDERER_SIGNAL_COUNT};
enum gds_output_renderer_signal_ids {ASYNC_FINISHED = 0, ASYNC_PROGRESS_CHANGED, GDS_OUTPUT_RENDERER_SIGNAL_COUNT};
static guint gds_output_renderer_signals[GDS_OUTPUT_RENDERER_SIGNAL_COUNT];
static int gds_output_renderer_render_dummy(GdsOutputRenderer *renderer,
@ -83,6 +87,11 @@ static void gds_output_renderer_dispose(GObject *self_obj)
g_mutex_lock(&priv->settings_lock);
g_mutex_unlock(&priv->settings_lock);
g_mutex_clear(&priv->settings_lock);
g_mutex_lock(&priv->idle_function_parameters.message_lock);
g_mutex_unlock(&priv->idle_function_parameters.message_lock);
g_mutex_clear(&priv->idle_function_parameters.message_lock);
priv->mutex_init_status = FALSE;
}
@ -150,6 +159,7 @@ static GParamSpec *gds_output_renderer_properties[N_PROPERTIES] = {NULL};
static void gds_output_renderer_class_init(GdsOutputRendererClass *klass)
{
GObjectClass *oclass = G_OBJECT_CLASS(klass);
GType progress_changed_param_types[1] = {G_TYPE_POINTER};
klass->render_output = gds_output_renderer_render_dummy;
@ -178,16 +188,16 @@ static void gds_output_renderer_class_init(GdsOutputRendererClass *klass)
G_TYPE_NONE,
0,
NULL);
gds_output_renderer_signals[ASYNC_PROGRESS_UPDATE] =
g_signal_newv("progress-update", GDS_RENDER_TYPE_OUTPUT_RENDERER,
gds_output_renderer_signals[ASYNC_PROGRESS_CHANGED] =
g_signal_newv("progress-changed", GDS_RENDER_TYPE_OUTPUT_RENDERER,
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
NULL,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0,
NULL);
1,
progress_changed_param_types);
}
void gds_output_renderer_init(GdsOutputRenderer *self)
@ -200,7 +210,10 @@ void gds_output_renderer_init(GdsOutputRenderer *self)
priv->output_file = NULL;
priv->task = NULL;
priv->mutex_init_status = TRUE;
priv->main_context = NULL;
priv->idle_function_parameters.status_message = NULL;
g_mutex_init(&priv->settings_lock);
g_mutex_init(&priv->idle_function_parameters.message_lock);
return;
}
@ -321,9 +334,7 @@ static void gds_output_renderer_async_wrapper(GTask *task,
goto ret_from_task;
}
g_mutex_lock(&priv->settings_lock);
ret = gds_output_renderer_render_output(renderer, priv->async_params.cell, priv->async_params.scale);
g_mutex_unlock(&priv->settings_lock);
ret_from_task:
g_task_return_int(task, ret);
@ -337,8 +348,13 @@ static void gds_output_renderer_async_finished(GObject *src_obj, GAsyncResult *r
priv = gds_output_renderer_get_instance_private(GDS_RENDER_OUTPUT_RENDERER(src_obj));
priv->main_context = NULL;
g_signal_emit(src_obj, gds_output_renderer_signals[ASYNC_FINISHED], 0);
g_clear_object(&priv->task);
/* Clear reference set in gds_output_renderer_render_output_async() */
g_object_unref(src_obj);
}
int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
@ -353,14 +369,82 @@ int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct
}
priv->task = g_task_new(renderer, NULL, gds_output_renderer_async_finished, NULL);
g_task_set_name(priv->task, "Rendering Thread");
g_mutex_lock(&priv->settings_lock);
priv->async_params.cell = cell;
priv->async_params.scale = scale;
priv->main_context = g_main_context_default();
g_mutex_unlock(&priv->settings_lock);
/* Self reference. This could end up being nasty... */
g_object_ref(renderer);
/* Do the magic */
g_task_run_in_thread(priv->task, gds_output_renderer_async_wrapper);
return ret;
}
static gboolean idle_event_processor_callback(gpointer user_data)
{
GdsOutputRenderer *renderer;
GdsOutputRendererPrivate *priv;
char *status_message;
/* If the rendering is finished before the mainloop gets to this point
* the renderer is already disposed. Catch this!
*/
if (!GDS_RENDER_IS_OUTPUT_RENDERER(user_data))
return FALSE;
renderer = GDS_RENDER_OUTPUT_RENDERER(user_data);
priv = gds_output_renderer_get_instance_private(renderer);
if (g_mutex_trylock(&priv->idle_function_parameters.message_lock)) {
status_message = priv->idle_function_parameters.status_message;
g_signal_emit(renderer, gds_output_renderer_signals[ASYNC_PROGRESS_CHANGED], 0, status_message);
g_free(priv->idle_function_parameters.status_message);
priv->idle_function_parameters.status_message = NULL;
g_mutex_unlock(&priv->idle_function_parameters.message_lock);
} else {
return TRUE;
}
return FALSE;
}
void gds_output_renderer_update_gui_status_from_async(GdsOutputRenderer *renderer, const char *status)
{
GSource *idle_event_processor;
GdsOutputRendererPrivate *priv;
gboolean skip_source = FALSE;
g_return_if_fail(GDS_RENDER_IS_OUTPUT_RENDERER(renderer));
if (!status)
return;
priv = gds_output_renderer_get_instance_private(renderer);
/* If rendering is not async */
if (!priv->main_context)
return;
g_mutex_lock(&priv->idle_function_parameters.message_lock);
if (priv->idle_function_parameters.status_message) {
g_free(priv->idle_function_parameters.status_message);
/* Skip adding new idle source because there's already an active one */
skip_source = TRUE;
}
priv->idle_function_parameters.status_message = g_strdup(status);
g_mutex_unlock(&priv->idle_function_parameters.message_lock);
if (!skip_source) {
idle_event_processor = g_idle_source_new();
g_source_set_callback(idle_event_processor, idle_event_processor_callback, (gpointer)renderer, NULL);
g_source_attach(idle_event_processor, priv->main_context);
}
}
/** @} */

View File

@ -29,10 +29,15 @@
#include <gdk/gdk.h>
#include <gds-render/layer/layer-info.h>
/**
* @addtogroup LatexRenderer
* @addtogroup LaTeX-Renderer
* @{
*/
/**
* @brief Struct representing the LaTeX-Renderer object.
*
* This struct holds the LaTeX renderer internal data. It is only used inside the @ref LatexRenderer class.
*/
struct _LatexRenderer {
GdsOutputRenderer parent;
gboolean tex_standalone;
@ -226,13 +231,20 @@ static void generate_graphics(FILE *tex_file, GList *graphics, GList *linfo, GSt
* @param tex_file File to write to
* @param buffer Working buffer
* @param scale Scale output down by this value
* @param renderer The current renderer as GdsOutputRenderer. This is used to emit the status updates to the GUI
*/
static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, GString *buffer, double scale)
static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, GString *buffer, double scale,
GdsOutputRenderer *renderer)
{
GString *status;
GList *list_child;
struct gds_cell_instance *inst;
status = g_string_new(NULL);
g_string_printf(status, "Generating cell %s", cell->name);
gds_output_renderer_update_gui_status_from_async(renderer, status->str);
g_string_free(status, TRUE);
/* Draw polygons of current cell */
generate_graphics(tex_file, cell->graphic_objs, layer_infos, buffer, scale);
@ -256,7 +268,7 @@ static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_fil
inst->magnification);
WRITEOUT_BUFFER(buffer);
render_cell(inst->cell_ref, layer_infos, tex_file, buffer, scale);
render_cell(inst->cell_ref, layer_infos, tex_file, buffer, scale, renderer);
g_string_printf(buffer, "\\end{scope}\n");
WRITEOUT_BUFFER(buffer);
@ -271,7 +283,7 @@ static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_fil
}
static int latex_render_cell_to_code(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, double scale,
gboolean create_pdf_layers, gboolean standalone_document)
gboolean create_pdf_layers, gboolean standalone_document, GdsOutputRenderer *renderer)
{
GString *working_line;
@ -304,7 +316,7 @@ static int latex_render_cell_to_code(struct gds_cell *cell, GList *layer_infos,
WRITEOUT_BUFFER(working_line);
/* Generate graphics output */
render_cell(cell, layer_infos, tex_file, working_line, scale);
render_cell(cell, layer_infos, tex_file, working_line, scale, renderer);
g_string_printf(working_line, "\\end{tikzpicture}\n");
@ -344,7 +356,7 @@ static int latex_renderer_render_output(GdsOutputRenderer *renderer,
tex_file = fopen(output_file, "w");
if (tex_file) {
ret = latex_render_cell_to_code(cell, layer_infos, tex_file, scale,
l_renderer->pdf_layers, l_renderer->tex_standalone);
l_renderer->pdf_layers, l_renderer->tex_standalone, renderer);
fclose(tex_file);
} else {
g_error("Could not open LaTeX output file");

View File

@ -105,5 +105,11 @@ void activity_bar_set_ready(ActivityBar *bar)
gtk_spinner_stop(GTK_SPINNER(bar->spinner));
}
void activity_bar_set_busy(ActivityBar *bar, const char *text)
{
gtk_label_set_text(GTK_LABEL(bar->label), (text ? text : "Working..."));
gtk_spinner_start(GTK_SPINNER(bar->spinner));
}
/** @} */