From fabc7538f4d758eb29a567ba9a24fc7787389aae Mon Sep 17 00:00:00 2001 From: pascal Date: Mon, 26 Feb 2024 08:50:17 +0100 Subject: [PATCH] config, callback, drawing, main: add LINE and RECT tools, and arrowtype option --- src/callbacks.c | 154 ++++++++++++++++++++++++++++++++++++------------ src/config.c | 44 ++++++++++++-- src/drawing.c | 38 ++++++++---- src/drawing.h | 11 ++-- src/main.c | 40 ++++++++++--- src/main.h | 16 ++++- 6 files changed, 234 insertions(+), 69 deletions(-) diff --git a/src/callbacks.c b/src/callbacks.c index 8fe4a56..63bd10d 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "main.h" #include "input.h" #include "callbacks.h" @@ -122,7 +123,11 @@ void on_monitors_changed ( GdkScreen *screen, cairo_destroy (cr); cairo_surface_destroy(data->backbuffer); data->backbuffer = new_shape; - + + // recreate temp_buffer + cairo_surface_destroy(data->temp_buffer); + data->temp_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height); + /* these depend on the shape surface */ @@ -137,10 +142,10 @@ void on_monitors_changed ( GdkScreen *screen, parse_config(data); // also calls paint_context_new() :-( - data->default_pen = paint_context_new (data, GROMIT_PEN, - data->red, 7, 0, 1, G_MAXUINT); - data->default_eraser = paint_context_new (data, GROMIT_ERASER, - data->red, 75, 0, 1, G_MAXUINT); + data->default_pen = paint_context_new (data, GROMIT_PEN, data->red, 7, + 0, GROMIT_ARROW_NONE, 1, G_MAXUINT); + data->default_eraser = paint_context_new (data, GROMIT_ERASER, data->red, 75, + 0, GROMIT_ARROW_NONE, 1, G_MAXUINT); if(!data->composited) // set shape { @@ -273,6 +278,14 @@ gboolean on_buttonpress (GtkWidget *win, devdata->lastslave != gdk_event_get_source_device ((GdkEvent *) ev)) select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), ev->state); + GromitPaintType type = devdata->cur_context->type; + + // store original state to have dynamic update of line and rect + if (type == GROMIT_LINE || type == GROMIT_RECT) + { + copy_surface(data->temp_buffer, data->backbuffer); + } + devdata->lastx = ev->x; devdata->lasty = ev->y; devdata->motion_time = ev->time; @@ -319,38 +332,43 @@ gboolean on_motion (GtkWidget *win, devdata->lastslave != gdk_event_get_source_device ((GdkEvent *) ev)) select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), ev->state); + GromitPaintType type = devdata->cur_context->type; + gdk_device_get_history (ev->device, ev->window, devdata->motion_time, ev->time, &coords, &nevents); if(!data->xinerama && nevents > 0) { - for (i=0; i < nevents; i++) + if (type != GROMIT_LINE && type != GROMIT_RECT) { - gdouble x, y; - - gdk_device_get_axis (ev->device, coords[i]->axes, - GDK_AXIS_PRESSURE, &pressure); - if (pressure > 0) + for (i=0; i < nevents; i++) { - data->maxwidth = (CLAMP (pressure + line_thickener, 0, 1) * - (double) (devdata->cur_context->width - - devdata->cur_context->minwidth) + - devdata->cur_context->minwidth); - - if(data->maxwidth > devdata->cur_context->maxwidth) - data->maxwidth = devdata->cur_context->maxwidth; - - gdk_device_get_axis(ev->device, coords[i]->axes, - GDK_AXIS_X, &x); - gdk_device_get_axis(ev->device, coords[i]->axes, - GDK_AXIS_Y, &y); - - draw_line (data, ev->device, devdata->lastx, devdata->lasty, x, y); - - coord_list_prepend (data, ev->device, x, y, data->maxwidth); - devdata->lastx = x; - devdata->lasty = y; + gdouble x, y; + + gdk_device_get_axis (ev->device, coords[i]->axes, + GDK_AXIS_PRESSURE, &pressure); + if (pressure > 0) + { + data->maxwidth = (CLAMP (pressure + line_thickener, 0, 1) * + (double) (devdata->cur_context->width - + devdata->cur_context->minwidth) + + devdata->cur_context->minwidth); + + if(data->maxwidth > devdata->cur_context->maxwidth) + data->maxwidth = devdata->cur_context->maxwidth; + + gdk_device_get_axis(ev->device, coords[i]->axes, + GDK_AXIS_X, &x); + gdk_device_get_axis(ev->device, coords[i]->axes, + GDK_AXIS_Y, &y); + + draw_line (data, ev->device, devdata->lastx, devdata->lasty, x, y); + + coord_list_prepend (data, ev->device, x, y, data->maxwidth); + devdata->lastx = x; + devdata->lasty = y; + } } } @@ -373,13 +391,46 @@ gboolean on_motion (GtkWidget *win, if(devdata->motion_time > 0) { - draw_line (data, ev->device, devdata->lastx, devdata->lasty, ev->x, ev->y); - coord_list_prepend (data, ev->device, ev->x, ev->y, data->maxwidth); + if (type == GROMIT_LINE || type == GROMIT_RECT) { + copy_surface(data->backbuffer, data->temp_buffer); + GdkRectangle rect = {0, 0, data->width, data->height}; + gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0); + } + if (type == GROMIT_LINE) + { + GromitArrowType atype = devdata->cur_context->arrow_type; + draw_line (data, ev->device, devdata->lastx, devdata->lasty, ev->x, ev->y); + if (atype != GROMIT_ARROW_NONE) + { + gint width = devdata->cur_context->arrowsize * devdata->cur_context->width / 2; + gfloat direction = + atan2(ev->y - devdata->lasty, ev->x - devdata->lastx); + if (atype & GROMIT_ARROW_END) + draw_arrow(data, ev->device, ev->x, ev->y, width * 2, direction); + if (atype & GROMIT_ARROW_START) + draw_arrow(data, ev->device, devdata->lastx, devdata->lasty, width * 2, M_PI + direction); + } + } + else if (type == GROMIT_RECT) + { + draw_line (data, ev->device, devdata->lastx, devdata->lasty, ev->x, devdata->lasty); + draw_line (data, ev->device, ev->x, devdata->lasty, ev->x, ev->y); + draw_line (data, ev->device, ev->x, ev->y, devdata->lastx, ev->y); + draw_line (data, ev->device, devdata->lastx, ev->y, devdata->lastx, devdata->lasty); + } + else + { + draw_line (data, ev->device, devdata->lastx, devdata->lasty, ev->x, ev->y); + coord_list_prepend (data, ev->device, ev->x, ev->y, data->maxwidth); + } } } - devdata->lastx = ev->x; - devdata->lasty = ev->y; + if (type != GROMIT_LINE && type != GROMIT_RECT) + { + devdata->lastx = ev->x; + devdata->lasty = ev->y; + } devdata->motion_time = ev->time; return TRUE; @@ -406,11 +457,34 @@ gboolean on_buttonrelease (GtkWidget *win, if (!devdata->is_grabbed) return FALSE; - - if (devdata->cur_context->arrowsize != 0 && - coord_list_get_arrow_param (data, ev->device, width * 3, - &width, &direction)) - draw_arrow (data, ev->device, ev->x, ev->y, width, direction); + + GromitPaintType type = devdata->cur_context->type; + + if (devdata->cur_context->arrowsize != 0) + { + GromitArrowType atype = devdata->cur_context->arrow_type; + if (type == GROMIT_LINE) + { + direction = atan2 (ev->y - devdata->lasty, ev->x - devdata->lastx); + if (atype & GROMIT_ARROW_END) + draw_arrow(data, ev->device, ev->x, ev->y, width * 2, direction); + if (atype & GROMIT_ARROW_START) + draw_arrow(data, ev->device, devdata->lastx, devdata->lasty, width * 2, M_PI + direction); + } + else + { + gint x0, y0; + if ((atype & GROMIT_ARROW_END) && + coord_list_get_arrow_param (data, ev->device, width * 3, + GROMIT_ARROW_END, &x0, &y0, &width, &direction)) + draw_arrow (data, ev->device, x0, y0, width, direction); + if ((atype & GROMIT_ARROW_START) && + coord_list_get_arrow_param (data, ev->device, width * 3, + GROMIT_ARROW_START, &x0, &y0, &width, &direction)) { + draw_arrow (data, ev->device, x0, y0, width, direction); + } + } + } coord_list_free (data, ev->device); @@ -540,7 +614,9 @@ void on_mainapp_selection_received (GtkWidget *widget, g_printerr ("Unable to parse color. " "Keeping default.\n"); } - GromitPaintContext* line_ctx = paint_context_new(data, GROMIT_PEN, fg_color, thickness, 0, thickness, thickness); + GromitPaintContext* line_ctx = + paint_context_new(data, GROMIT_PEN, fg_color, thickness, + 0, GROMIT_ARROW_NONE, thickness, thickness); GdkRectangle rect; rect.x = MIN (startX,endX) - thickness / 2; diff --git a/src/config.c b/src/config.c index 393bf04..063fe04 100644 --- a/src/config.c +++ b/src/config.c @@ -125,6 +125,7 @@ gboolean parse_config (GromitData *data) GromitPaintType type; GdkRGBA *fg_color=NULL; guint width, arrowsize, minwidth, maxwidth; + GromitArrowType arrowtype; /* try user config location */ filename = g_strjoin (G_DIR_SEPARATOR_S, @@ -168,6 +169,8 @@ gboolean parse_config (GromitData *data) scanner->config->int_2_float = 1; g_scanner_scope_add_symbol (scanner, 0, "PEN", (gpointer) GROMIT_PEN); + g_scanner_scope_add_symbol (scanner, 0, "LINE", (gpointer) GROMIT_LINE); + g_scanner_scope_add_symbol (scanner, 0, "RECT", (gpointer) GROMIT_RECT); g_scanner_scope_add_symbol (scanner, 0, "ERASER", (gpointer) GROMIT_ERASER); g_scanner_scope_add_symbol (scanner, 0, "RECOLOR",(gpointer) GROMIT_RECOLOR); g_scanner_scope_add_symbol (scanner, 0, "HOTKEY", HOTKEY_SYMBOL_VALUE); @@ -186,8 +189,9 @@ gboolean parse_config (GromitData *data) g_scanner_scope_add_symbol (scanner, 2, "size", (gpointer) 1); g_scanner_scope_add_symbol (scanner, 2, "color", (gpointer) 2); g_scanner_scope_add_symbol (scanner, 2, "arrowsize", (gpointer) 3); - g_scanner_scope_add_symbol (scanner, 2, "minsize", (gpointer) 4); - g_scanner_scope_add_symbol (scanner, 2, "maxsize", (gpointer) 5); + g_scanner_scope_add_symbol (scanner, 2, "arrowtype", (gpointer) 4); + g_scanner_scope_add_symbol (scanner, 2, "minsize", (gpointer) 5); + g_scanner_scope_add_symbol (scanner, 2, "maxsize", (gpointer) 6); g_scanner_set_scope (scanner, 0); scanner->config->scope_0_fallback = 0; @@ -223,6 +227,7 @@ gboolean parse_config (GromitData *data) type = GROMIT_PEN; width = 7; arrowsize = 0; + arrowtype = GROMIT_ARROW_NONE; minwidth = 1; maxwidth = G_MAXUINT; fg_color = data->red; @@ -244,6 +249,7 @@ gboolean parse_config (GromitData *data) type = context_template->type; width = context_template->width; arrowsize = context_template->arrowsize; + arrowtype = context_template->arrow_type; minwidth = context_template->minwidth; maxwidth = context_template->maxwidth; fg_color = context_template->paint_color; @@ -334,8 +340,37 @@ gboolean parse_config (GromitData *data) goto cleanup; } arrowsize = scanner->value.v_float; + arrowtype = GROMIT_ARROW_END; } else if ((intptr_t) scanner->value.v_symbol == 4) + { + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + { + g_printerr ("Missing \"=\"... aborting\n"); + goto cleanup; + } + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_STRING) + { + g_printerr ("Missing Arrowsize (string)... " + "aborting\n"); + goto cleanup; + } + if (! strcmp(scanner->value.v_string, "->")) + arrowtype = GROMIT_ARROW_END; + else if (! strcmp(scanner->value.v_string, "<-")) + arrowtype = GROMIT_ARROW_START; + else if (! strcmp(scanner->value.v_string, "<->")) + arrowtype = GROMIT_ARROW_DOUBLE; + else + { + g_printerr ("Arrow type must be one of -> <- <-> ... " + "aborting\n"); + goto cleanup; + } + } + else if ((intptr_t) scanner->value.v_symbol == 5) { token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_EQUAL_SIGN) @@ -352,7 +387,7 @@ gboolean parse_config (GromitData *data) } minwidth = scanner->value.v_float; } - else if ((intptr_t) scanner->value.v_symbol == 5) + else if ((intptr_t) scanner->value.v_symbol == 6) { token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_EQUAL_SIGN) @@ -394,7 +429,8 @@ gboolean parse_config (GromitData *data) goto cleanup; } - context = paint_context_new (data, type, fg_color, width, arrowsize, minwidth, maxwidth); + context = paint_context_new (data, type, fg_color, width, + arrowsize, arrowtype, minwidth, maxwidth); g_hash_table_insert (data->tool_config, name, context); } else if (token == G_TOKEN_SYMBOL && diff --git a/src/drawing.c b/src/drawing.c index bf34a6b..adfaf0a 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -1,6 +1,7 @@ #include #include "drawing.h" +#include "main.h" typedef struct { @@ -153,14 +154,20 @@ void coord_list_free (GromitData *data, devdata->coordlist = NULL; } - -gboolean coord_list_get_arrow_param (GromitData *data, - GdkDevice *dev, - gint search_radius, - gint *ret_width, - gfloat *ret_direction) +/* + * for double-ended arrows, two separate calls are required + */ + +gboolean coord_list_get_arrow_param (GromitData *data, + GdkDevice *dev, + gint search_radius, + GromitArrowType arrow_end, + gint *x0, + gint *y0, + gint *ret_width, + gfloat *ret_direction) { - gint x0, y0, r2, dist; + gint r2, dist; gboolean success = FALSE; GromitStrokeCoordinate *cur_point, *valid_point; /* get the data for this device */ @@ -172,20 +179,25 @@ gboolean coord_list_get_arrow_param (GromitData *data, if (ptr) { + if (arrow_end == GROMIT_ARROW_START) + ptr = g_list_last(ptr); cur_point = ptr->data; - x0 = cur_point->x; - y0 = cur_point->y; + *x0 = cur_point->x; + *y0 = cur_point->y; r2 = search_radius * search_radius; dist = 0; while (ptr && dist < r2) { - ptr = ptr->next; + if (arrow_end == GROMIT_ARROW_END) + ptr = ptr->next; + else + ptr = ptr->prev; if (ptr) { cur_point = ptr->data; - dist = (cur_point->x - x0) * (cur_point->x - x0) + - (cur_point->y - y0) * (cur_point->y - y0); + dist = (cur_point->x - *x0) * (cur_point->x - *x0) + + (cur_point->y - *y0) * (cur_point->y - *y0); width = cur_point->width * devdata->cur_context->arrowsize; if (width * 2 <= dist && (!valid_point || valid_point->width < cur_point->width)) @@ -197,7 +209,7 @@ gboolean coord_list_get_arrow_param (GromitData *data, { *ret_width = MAX (valid_point->width * devdata->cur_context->arrowsize, 2); - *ret_direction = atan2 (y0 - valid_point->y, x0 - valid_point->x); + *ret_direction = atan2 (*y0 - valid_point->y, *x0 - valid_point->x); success = TRUE; } } diff --git a/src/drawing.h b/src/drawing.h index 3b4fbf8..d1954a6 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -12,10 +12,13 @@ void draw_line (GromitData *data, GdkDevice *dev, gint x1, gint y1, gint x2, gin void draw_arrow (GromitData *data, GdkDevice *dev, gint x1, gint y1, gint width, gfloat direction); gboolean coord_list_get_arrow_param (GromitData *data, - GdkDevice *dev, - gint search_radius, - gint *ret_width, - gfloat *ret_direction); + GdkDevice *dev, + gint search_radius, + GromitArrowType arrow_end, + gint *x0, + gint *y0, + gint *ret_width, + gfloat *ret_direction); void coord_list_prepend (GromitData *data, GdkDevice* dev, gint x, gint y, gint width); void coord_list_free (GromitData *data, GdkDevice* dev); diff --git a/src/main.c b/src/main.c index b1507d3..e4e2f61 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ GromitPaintContext *paint_context_new (GromitData *data, GdkRGBA *paint_color, guint width, guint arrowsize, + GromitArrowType arrowtype, guint minwidth, guint maxwidth) { @@ -50,11 +51,12 @@ GromitPaintContext *paint_context_new (GromitData *data, context->type = type; context->width = width; context->arrowsize = arrowsize; + context->arrow_type = arrowtype; context->minwidth = minwidth; context->maxwidth = maxwidth; context->paint_color = paint_color; - + context->paint_ctx = cairo_create (data->backbuffer); gdk_cairo_set_source_rgba(context->paint_ctx, paint_color); @@ -84,6 +86,10 @@ void paint_context_print (gchar *name, { case GROMIT_PEN: g_printerr ("Pen, "); break; + case GROMIT_LINE: + g_printerr ("Line, "); break; + case GROMIT_RECT: + g_printerr ("Rect, "); break; case GROMIT_ERASER: g_printerr ("Eraser, "); break; case GROMIT_RECOLOR: @@ -96,6 +102,22 @@ void paint_context_print (gchar *name, g_printerr ("minwidth: %u, ", context->minwidth); g_printerr ("maxwidth: %u, ", context->maxwidth); g_printerr ("arrowsize: %.2f, ", context->arrowsize); + if (context->arrowsize > 0) + { + switch (context->arrow_type) { + case GROMIT_ARROW_START: + g_printerr(" arrowtype: <- , "); + break; + case GROMIT_ARROW_END: + g_printerr(" arrowtype: ->, "); + break; + case GROMIT_ARROW_DOUBLE: + g_printerr(" arrowtype: <->, "); + break; + case GROMIT_ARROW_NONE: + break; + } + } g_printerr ("color: %s\n", gdk_rgba_to_string(context->paint_color)); } @@ -592,8 +614,10 @@ void setup_main_app (GromitData *data, int argc, char ** argv) cairo_surface_destroy(data->undobuffer[i]); data->undobuffer[i] = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height); } - + // original state for LINE and RECT tool + cairo_surface_destroy(data->temp_buffer); + data->temp_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height); /* EVENTS */ gtk_widget_add_events (data->win, GROMIT_WINDOW_EVENTS); @@ -752,12 +776,12 @@ void setup_main_app (GromitData *data, int argc, char ** argv) data->modified = 0; - data->default_pen = paint_context_new (data, GROMIT_PEN, - data->red, 7, 0, 1, G_MAXUINT); - data->default_eraser = paint_context_new (data, GROMIT_ERASER, - data->red, 75, 0, 1, G_MAXUINT); - - + data->default_pen = + paint_context_new (data, GROMIT_PEN, data->red, 7, + 0, GROMIT_ARROW_NONE, 1, G_MAXUINT); + data->default_eraser = + paint_context_new (data, GROMIT_ERASER, data->red, 75, + 0, GROMIT_ARROW_NONE, 1, G_MAXUINT); gdk_event_handler_set ((GdkEventFunc) main_do_event, data, NULL); gtk_key_snooper_install (snoop_key_press, data); diff --git a/src/main.h b/src/main.h index ae89970..e817392 100644 --- a/src/main.h +++ b/src/main.h @@ -64,15 +64,26 @@ typedef enum { GROMIT_PEN, + GROMIT_LINE, + GROMIT_RECT, GROMIT_ERASER, GROMIT_RECOLOR } GromitPaintType; +typedef enum +{ + GROMIT_ARROW_NONE = 0, + GROMIT_ARROW_START = 1, + GROMIT_ARROW_END = 2, + GROMIT_ARROW_DOUBLE = 3 +} GromitArrowType; + typedef struct { GromitPaintType type; guint width; gfloat arrowsize; + GromitArrowType arrow_type; guint minwidth; guint maxwidth; GdkRGBA *paint_color; @@ -144,6 +155,8 @@ typedef struct cairo_surface_t *undobuffer[GROMIT_MAX_UNDO]; gint undo_head, undo_depth, redo_depth; + cairo_surface_t *temp_buffer; + gboolean show_intro_on_startup; } GromitData; @@ -166,7 +179,8 @@ void redo_drawing (GromitData *data); void clear_screen (GromitData *data); GromitPaintContext *paint_context_new (GromitData *data, GromitPaintType type, - GdkRGBA *fg_color, guint width, guint arrowsize, + GdkRGBA *fg_color, guint width, + guint arrowsize, GromitArrowType arrowtype, guint minwidth, guint maxwidth); void paint_context_free (GromitPaintContext *context);