Index: hid_gtk3_cairo/Makefile =================================================================== --- hid_gtk3_cairo/Makefile (nonexistent) +++ hid_gtk3_cairo/Makefile (revision 27398) @@ -0,0 +1,6 @@ +all: + cd ../../src && $(MAKE) mod_hid_gtk3_cairo + +clean: + rm *.o *.so 2>/dev/null ; true + Index: hid_gtk3_cairo/Plug.tmpasm =================================================================== --- hid_gtk3_cairo/Plug.tmpasm (nonexistent) +++ hid_gtk3_cairo/Plug.tmpasm (revision 27398) @@ -0,0 +1,20 @@ +put /local/pcb/mod {hid_gtk3_cairo} +put /local/pcb/mod/OBJS_C99 [@ + $(PLUGDIR)/hid_gtk3_cairo/gtkhid-main.o + $(PLUGDIR)/hid_gtk3_cairo/gtkhid-cairo.o +@] + +switch /local/pcb/hid_gtk3_cairo/controls + case {disable} end; + default + put /local/pcb/mod/CFLAGS /target/libs/gui/gtk3/cflags + put /local/pcb/mod/LDFLAGS /target/libs/gui/gtk3/ldflags + append /local/pcb/CFLAGS { -DPCB_GTK3=1 } + end +end + +switch /local/pcb/hid_gtk3_cairo/controls + case {buildin} include /local/pcb/tmpasm/buildin; end; + case {plugin} include /local/pcb/tmpasm/plugin; end; + case {disable} include /local/pcb/tmpasm/disable; end; +end Index: hid_gtk3_cairo/gtkhid-cairo.c =================================================================== --- hid_gtk3_cairo/gtkhid-cairo.c (nonexistent) +++ hid_gtk3_cairo/gtkhid-cairo.c (revision 27398) @@ -0,0 +1,1592 @@ +/* + * COPYRIGHT + * + * pcb-rnd, interactive printed circuit board design + * (this file is based on PCB, interactive printed circuit board design) + * Copyright (C) 1994,1995,1996 Thomas Nau + * pcb-rnd Copyright (C) 2017-2019 Alain Vigne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Contact: + * Project page: http://repo.hu/projects/pcb-rnd + * lead developer: http://repo.hu/projects/pcb-rnd/contact.html + * mailing list: pcb-rnd (at) list.repo.hu (send "subscribe") + * + */ + +#include "config.h" +#include "hidlib_conf.h" + +#include + +#include "crosshair.h" +#include "draw.h" +#include "grid.h" +#include "color.h" +#include "color_cache.h" +#include "hid_attrib.h" +#include "funchash_core.h" + +#include "../src_plugins/lib_gtk_common/pcb_gtk.h" +#include "../src_plugins/lib_gtk_common/glue_common.h" +#include "../src_plugins/lib_gtk_common/coord_conv.h" + +#include "../src_plugins/lib_gtk_common/hid_gtk_conf.h" +#include "../src_plugins/lib_gtk_common/lib_gtk_config.h" + +#include "../src_plugins/lib_hid_common/clip.h" + +extern pcb_hid_t gtk3_cairo_hid; +static void ghid_cairo_screen_update(void); + +/* Sets priv->u_gc to the "right" GC to use (wrt mask or window) +*/ +#define USE_GC(gc) if (!use_gc(gc)) return + +//static int cur_mask = -1; +//static int mask_seq = 0; +static pcb_composite_op_t curr_drawing_mode; + +typedef struct render_priv_s { + GdkRGBA bg_color; /* cached back-ground color */ + GdkRGBA offlimits_color; /* cached external board color */ + GdkRGBA grid_color; /* cached grid color */ + GdkRGBA crosshair_color; /* cached crosshair color */ + + cairo_t *cr; /* pointer to current drawing context */ + cairo_t *cr_target; /* pointer to destination widget drawing context */ + + cairo_surface_t *surf_da; /* cairo surface connected to port->drawing_area */ + cairo_t *cr_drawing_area; /* cairo context created from surf_da */ + + cairo_surface_t *surf_layer; /* cairo surface for temporary layer composition */ + cairo_t *cr_layer; /* cairo context created from surf_layer */ + + //GdkPixmap *pixmap, *mask; + + //GdkGC *bg_gc; + //GdkGC *offlimits_gc; + //GdkGC *mask_gc; + //GdkGC *u_gc; + //GdkGC *grid_gc; + pcb_bool clip; + GdkRectangle clip_rect; + int xor_mode; /* 1 if drawn in XOR mode, 0 otherwise*/ + int attached_invalidate_depth; + int mark_invalidate_depth; + + /* color cache for set_color */ + pcb_clrcache_t ccache; + int ccache_inited; +} render_priv_t; + +typedef struct hid_gc_s { + pcb_core_gc_t core_gc; + pcb_hid_t *me_pointer; + + pcb_color_t pcolor; + GdkRGBA color; /* current color for this GC. */ + + pcb_coord_t width; + cairo_line_cap_t cap; + cairo_line_join_t join; + gchar xor_mask; + //gint mask_seq; +} hid_gc_s; + +static void copy_color(GdkRGBA * dest, GdkRGBA * source) +{ + dest->red = source->red; + dest->green = source->green; + dest->blue = source->blue; + dest->alpha = source->alpha; +} + +static const gchar *get_color_name(pcb_gtk_color_t * color) +{ + static char tmp[16]; + + if (!color) + return "#000000"; + + sprintf(tmp, "#%2.2x%2.2x%2.2x", + (int) (color->red * 255) & 0xff, (int) (color->green * 255) & 0xff, (int) (color->blue * 255) & 0xff); + return tmp; +} + +/* Returns TRUE if inclr has been successfully converted to color. */ +static pcb_bool map_color(const pcb_color_t *inclr, pcb_gtk_color_t *color) +{ + if (!color || !ghidgui->port.top_window) + return FALSE; + + color->red = inclr->fr; + color->green = inclr->fg; + color->blue = inclr->fb; + color->alpha = inclr->fa; + + return TRUE; +} + +static void cr_draw_line(cairo_t * cr, int fill, double x1, double y1, double x2, double y2) +{ + if (cr == NULL) + return; + + cairo_move_to(cr, x1, y1); + cairo_line_to(cr, x2, y2); +TODO("gtk3: What means 'fill a line'? cairo_fill is not appropriate if path is only a line.") + if (fill) + cairo_fill(cr); + else + cairo_stroke(cr); +} + +static void cr_destroy_surf_and_context(cairo_surface_t ** psurf, cairo_t ** pcr) +{ + if (*psurf) + cairo_surface_destroy(*psurf); + if (*pcr) + cairo_destroy(*pcr); + + *psurf = NULL; + *pcr = NULL; +} + +/* First, frees previous surface and context, then creates new ones, similar to drawing_area. */ +static void cr_create_similar_surface_and_context(cairo_surface_t ** psurf, cairo_t ** pcr, void *vport) +{ + pcb_gtk_port_t *port = vport; + cairo_surface_t *surface; + cairo_t *cr; + + cr_destroy_surf_and_context(psurf, pcr); + + surface = gdk_window_create_similar_surface(gtk_widget_get_window(port->drawing_area), + CAIRO_CONTENT_COLOR_ALPHA, + gtk_widget_get_allocated_width(port->drawing_area), + gtk_widget_get_allocated_height(port->drawing_area)); + cr = cairo_create(surface); + *psurf = surface; + *pcr = cr; +} + +/* Creates or reuses a context to render a single layer group with "union" type of shape rendering. */ +static void start_subcomposite(void) +{ + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr; + + if (priv->surf_layer == NULL) + cr_create_similar_surface_and_context(&priv->surf_layer, &priv->cr_layer, &ghidgui->port); + cr = priv->cr_layer; + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_paint(cr); + cairo_restore(cr); + cairo_set_operator(priv->cr, CAIRO_OPERATOR_SOURCE); + priv->cr = priv->cr_layer; +} + +/* Applies the layer composited so far, to the target, using translucency. */ +static void end_subcomposite(void) +{ + render_priv_t *priv = ghidgui->port.render_priv; + + cairo_set_operator(priv->cr_target, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(priv->cr_target, priv->surf_layer, 0, 0); + cairo_paint_with_alpha(priv->cr_target, pcbhl_conf.appearance.layer_alpha); + priv->cr = priv->cr_target; +} + +static int ghid_cairo_set_layer_group(pcb_hid_t *hid, pcb_layergrp_id_t group, const char *purpose, int purpi, pcb_layer_id_t layer, unsigned int flags, int is_empty, pcb_xform_t **xform) +{ + /* draw anything */ + return 1; +} + +static void ghid_cairo_end_layer_group(pcb_hid_t *hid) +{ + end_subcomposite(); +} + +TODO(": misleading comment or broken code") +/* Do not clean up internal structures, as they will be used probably elsewhere. */ +static void ghid_cairo_destroy_gc(pcb_hid_gc_t gc) +{ + g_free(gc); +} + +/* called from pcb_hid_t->make_gc() . Use only this to hold pointers, do not + own references, avoid costly memory allocation that needs to be destroyed with + ghid_cairo_destroy_gc(). */ +static pcb_hid_gc_t ghid_cairo_make_gc(pcb_hid_t *hid) +{ + pcb_hid_gc_t rv; + + rv = g_new0(hid_gc_s, 1); + rv->me_pointer = >k3_cairo_hid; + rv->pcolor = pcbhl_conf.appearance.color.background; + return rv; +} + +TODO("gtk3: check if clipping is not necessary") +//static void set_clip(render_priv_t * priv, cairo_t * cr) +//{ +// if (cr == NULL) +// return; +// +// if (priv->clip) { +// gdk_cairo_rectangle(cr, &priv->clip_rect); +// cairo_clip(cr); +// } +// else { +// /*FIXME: do nothing if no clipping ? */ +// //cairo_mask(cr, NULL); +// //gdk_gc_set_clip_mask(gc, NULL); +// } +//} + +static inline void ghid_cairo_draw_grid_global(pcb_hidlib_t *hidlib, cairo_t *cr) +{ + //render_priv_t *priv = gport->render_priv; + pcb_coord_t x, y, x1, y1, x2, y2, grd; + int n, i; + static GdkPoint *points = NULL; + static int npoints = 0; + + x1 = pcb_grid_fit(MAX(0, SIDE_X(&ghidgui->port.view, ghidgui->port.view.x0)), hidlib->grid, hidlib->grid_ox); + y1 = pcb_grid_fit(MAX(0, SIDE_Y(&ghidgui->port.view, ghidgui->port.view.y0)), hidlib->grid, hidlib->grid_oy); + x2 = pcb_grid_fit(MIN(hidlib->size_x, SIDE_X(&ghidgui->port.view, ghidgui->port.view.x0 + ghidgui->port.view.width - 1)), hidlib->grid, hidlib->grid_ox); + y2 = pcb_grid_fit(MIN(hidlib->size_y, SIDE_Y(&ghidgui->port.view, ghidgui->port.view.y0 + ghidgui->port.view.height - 1)), hidlib->grid, hidlib->grid_oy); + + grd = hidlib->grid; + + if (Vz(grd) < pcb_conf_hid_gtk.plugins.hid_gtk.global_grid.min_dist_px) { + if (!pcb_conf_hid_gtk.plugins.hid_gtk.global_grid.sparse) + return; + grd *= (pcb_conf_hid_gtk.plugins.hid_gtk.global_grid.min_dist_px / Vz(grd)); + } + + if (x1 > x2) { + pcb_coord_t tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) { + pcb_coord_t tmp = y1; + y1 = y2; + y2 = tmp; + } + if (Vx(x1) < 0) + x1 += grd; + if (Vy(y1) < 0) + y1 += grd; + if (Vx(x2) >= ghidgui->port.view.canvas_width) + x2 -= grd; + if (Vy(y2) >= ghidgui->port.view.canvas_height) + y2 -= grd; + + + n = (x2 - x1) / grd + 1; + if (n > npoints) { + npoints = n + 10; + points = (GdkPoint *) realloc(points, npoints * sizeof(GdkPoint)); + } + n = 0; + for (x = x1; x <= x2; x += grd) { + points[n].x = Vx(x); + n++; + } + if (n == 0) + return; + + /* Draw N points... is this the most efficient ? At least, it works. + From cairo development : + " cairo_move_to (cr, x, y); + . cairo_line_to (cr, x, y); + . .. repeat for each point .. + . + . cairo_stroke (cr); + . + . Within the implementation (and test suite) we call these "degenerate" + . paths and we explicitly support drawing round caps for such degenerate + . paths. So this should work perfectly for the case of + . CAIRO_LINE_CAP_ROUND and you'll get the diameter controlled by + . cairo_set_line_width just like you want. + " + */ + for (y = y1; y <= y2; y += grd) { + for (i = 0; i < n; i++) { + points[i].y = Vy(y); + cairo_move_to(cr, points[i].x, points[i].y); + cairo_line_to(cr, points[i].x, points[i].y); + } + cairo_stroke(cr); + } +} + +static void ghid_cairo_draw_grid_local_(pcb_hidlib_t *hidlib, pcb_coord_t cx, pcb_coord_t cy, int radius) +{ + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr = priv->cr_target; + static GdkPoint *points_base = NULL; + static GdkPoint *points_abs = NULL; + static int apoints = 0, npoints = 0, old_radius = 0; + static pcb_coord_t last_grid = 0; + int recalc = 0, n, r2; + pcb_coord_t x, y; + + /* PI is approximated with 3.25 here - allows a minimal overallocation, speeds up calculations */ + r2 = radius * radius; + n = r2 * 3 + r2 / 4 + 1; + if (n > apoints) { + apoints = n; + points_base = (GdkPoint *) realloc(points_base, apoints * sizeof(GdkPoint)); + points_abs = (GdkPoint *) realloc(points_abs, apoints * sizeof(GdkPoint)); + } + + if (radius != old_radius) { + old_radius = radius; + recalc = 1; + } + + if (last_grid != hidlib->grid) { + last_grid = hidlib->grid; + recalc = 1; + } + + /* recaculate the 'filled circle' mask (base relative coords) if grid or radius changed */ + if (recalc) { + + npoints = 0; + for (y = -radius; y <= radius; y++) { + int y2 = y * y; + for (x = -radius; x <= radius; x++) { + if (x * x + y2 < r2) { + points_base[npoints].x = x * hidlib->grid; + points_base[npoints].y = y * hidlib->grid; + npoints++; + } + } + } + } + + /* calculate absolute positions */ + for (n = 0; n < npoints; n++) { + points_abs[n].x = Vx(points_base[n].x + cx); + points_abs[n].y = Vy(points_base[n].y + cy); + cairo_move_to(cr, points_abs[n].x, points_abs[n].y); + cairo_line_to(cr, points_abs[n].x, points_abs[n].y); + } + cairo_stroke(cr); + //gdk_draw_points(gport->drawable, priv->grid_gc, points_abs, npoints); +} + +static int grid_local_have_old = 0, grid_local_old_r = 0; +static pcb_coord_t grid_local_old_x, grid_local_old_y; + +static void ghid_cairo_draw_grid_local(pcb_hidlib_t *hidlib, pcb_coord_t cx, pcb_coord_t cy) +{ + if (grid_local_have_old) { + ghid_cairo_draw_grid_local_(hidlib, grid_local_old_x, grid_local_old_y, grid_local_old_r); + grid_local_have_old = 0; + } + + if (!pcb_conf_hid_gtk.plugins.hid_gtk.local_grid.enable) + return; + + if ((Vz(hidlib->grid) < PCB_MIN_GRID_DISTANCE) || (!pcbhl_conf.editor.draw_grid)) + return; + + /* cx and cy are the actual cursor snapped to wherever - round them to the nearest real grid point */ + cx = (cx / hidlib->grid) * hidlib->grid + hidlib->grid_ox; + cy = (cy / hidlib->grid) * hidlib->grid + hidlib->grid_oy; + + grid_local_have_old = 1; + ghid_cairo_draw_grid_local_(hidlib, cx, cy, pcb_conf_hid_gtk.plugins.hid_gtk.local_grid.radius); + grid_local_old_x = cx; + grid_local_old_y = cy; + grid_local_old_r = pcb_conf_hid_gtk.plugins.hid_gtk.local_grid.radius; +} + +static void ghid_cairo_draw_grid(pcb_hidlib_t *hidlib) +{ + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr = priv->cr_target; + + if (cr == NULL) + return; + + grid_local_have_old = 0; + + if (!pcbhl_conf.editor.draw_grid) + return; + //if (!priv->grid_gc) { + // if (gdk_color_parse(pcbhl_conf.appearance.color.grid, &gport->grid_color)) { + // gport->grid_color.red ^= gport->bg_color.red; + // gport->grid_color.green ^= gport->bg_color.green; + // gport->grid_color.blue ^= gport->bg_color.blue; + // gdk_color_alloc(gport->colormap, &gport->grid_color); + // } + // priv->grid_gc = gdk_gc_new(gport->drawable); + // gdk_gc_set_function(priv->grid_gc, GDK_XOR); + // gdk_gc_set_foreground(priv->grid_gc, &gport->grid_color); + // gdk_gc_set_clip_origin(priv->grid_gc, 0, 0); + // set_clip(priv, priv->grid_gc); + //} + + cairo_save(cr); +TODO("gtk3: deal with gc") + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(cr, 1.0); + gdk_cairo_set_source_rgba(cr, &priv->grid_color); + + if (pcb_conf_hid_gtk.plugins.hid_gtk.local_grid.enable) { + ghid_cairo_draw_grid_local(hidlib, grid_local_old_x, grid_local_old_y); + cairo_restore(cr); + return; + } + + ghid_cairo_draw_grid_global(hidlib, cr); + cairo_restore(cr); +} + +/* ------------------------------------------------------------ */ +static void ghid_cairo_draw_bg_image(pcb_hidlib_t *hidlib) +{ + static GdkPixbuf *pixbuf; + GdkInterpType interp_type; + gint src_x, src_y, dst_x, dst_y, w, h, w_src, h_src; + static gint w_scaled, h_scaled; + render_priv_t *priv = ghidgui->port.render_priv; + + if (!ghidgui->bg_pixbuf) + return; + + src_x = ghidgui->port.view.x0; + src_y = ghidgui->port.view.y0; + dst_x = 0; + dst_y = 0; + + if (src_x < 0) { + dst_x = -src_x; + src_x = 0; + } + if (src_y < 0) { + dst_y = -src_y; + src_y = 0; + } + + w = hidlib->size_x / ghidgui->port.view.coord_per_px; + h = hidlib->size_y / ghidgui->port.view.coord_per_px; + src_x = src_x / ghidgui->port.view.coord_per_px; + src_y = src_y / ghidgui->port.view.coord_per_px; + dst_x = dst_x / ghidgui->port.view.coord_per_px; + dst_y = dst_y / ghidgui->port.view.coord_per_px; + + if (w_scaled != w || h_scaled != h) { + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + + w_src = gdk_pixbuf_get_width(ghidgui->bg_pixbuf); + h_src = gdk_pixbuf_get_height(ghidgui->bg_pixbuf); + if (w > w_src && h > h_src) + interp_type = GDK_INTERP_NEAREST; + else + interp_type = GDK_INTERP_BILINEAR; + + pixbuf = gdk_pixbuf_scale_simple(ghidgui->bg_pixbuf, w, h, interp_type); + w_scaled = w; + h_scaled = h; + } + + if (pixbuf) { + gdk_cairo_set_source_pixbuf(priv->cr, pixbuf, src_x, src_y); + cairo_rectangle(priv->cr, dst_x, dst_y, ghidgui->port.view.canvas_width, ghidgui->port.view.canvas_height); + cairo_fill(priv->cr); + //gdk_pixbuf_render_to_drawable(pixbuf, gport->drawable, priv->bg_gc, + // src_x, src_y, dst_x, dst_y, w - src_x, h - src_y, GDK_RGB_DITHER_NORMAL, 0, 0); + } +} + +static void ghid_cairo_render_burst(pcb_hid_t *hid, pcb_burst_op_t op, const pcb_box_t *screen) +{ + pcb_gui->coord_per_pix = ghidgui->port.view.coord_per_px; +} + +/* Drawing modes usually cycle from RESET to (POSITIVE | NEGATIVE) to FLUSH. screen is not used in this HID. */ +static void ghid_cairo_set_drawing_mode(pcb_hid_t *hid, pcb_composite_op_t op, pcb_bool direct, const pcb_box_t *screen) +{ + render_priv_t *priv = ghidgui->port.render_priv; + + if (!priv->cr) { + //abort(); + return; + } + + switch(op) { + case PCB_HID_COMP_RESET: + //ghid_sketch_setup(priv); + start_subcomposite(); + + /* clear the canvas */ + //priv->clip_color.pixel = 0; + //if (priv->clear_gc == NULL) + // priv->clear_gc = gdk_gc_new(priv->out_clip); + //gdk_gc_set_foreground(priv->clear_gc, &priv->clip_color); + //set_clip(priv, priv->clear_gc); + //gdk_draw_rectangle(priv->out_clip, priv->clear_gc, TRUE, 0, 0, gport->view.canvas_width, gport->view.canvas_height); + break; + + case PCB_HID_COMP_POSITIVE: + case PCB_HID_COMP_POSITIVE_XOR: + //priv->clip_color.pixel = 1; + cairo_set_operator(priv->cr, CAIRO_OPERATOR_SOURCE); + break; + + case PCB_HID_COMP_NEGATIVE: + //priv->clip_color.pixel = 0; + cairo_set_operator(priv->cr, CAIRO_OPERATOR_CLEAR); + break; + + case PCB_HID_COMP_FLUSH: + if (direct) + end_subcomposite(); + else { + priv->cr = priv->cr_target; + cairo_mask_surface(priv->cr, priv->surf_layer, 0, 0); + cairo_fill(priv->cr); + } + //cairo_paint_with_alpha(priv->cr, pcbhl_conf.appearance.layer_alpha); + //cairo_paint_with_alpha(priv->cr, 1.0); + + //if (priv->copy_gc == NULL) + // priv->copy_gc = gdk_gc_new(priv->out_pixel); + //gdk_gc_set_clip_mask(priv->copy_gc, priv->sketch_clip); + //gdk_gc_set_clip_origin(priv->copy_gc, 0, 0); + //gdk_draw_drawable(priv->base_pixel, priv->copy_gc, priv->sketch_pixel, 0, 0, 0, 0, gport->view.canvas_width, gport->view.canvas_height); + // + //priv->out_pixel = priv->base_pixel; + //priv->out_clip = NULL; + break; + } + curr_drawing_mode = op; +} + +typedef struct { + int color_set; + pcb_gtk_color_t color; + int xor_set; + pcb_gtk_color_t xor_color; +} pcb_gtk_color_cache_t; + + +/* Config helper functions for when the user changes color preferences. + set_special colors used in the gtkhid. + */ +//static void set_special_grid_color(void) +//{ +// render_priv_t *priv = gport->render_priv; +// int red, green, blue; +// +// //if (!gport->colormap) +// // return; +// +// red = priv->grid_color.red; +// green = priv->grid_color.green; +// blue = priv->grid_color.blue; +// pcb_conf_setf(CFR_DESIGN, "appearance/color/grid", -1, "#%02x%02x%02x", red, green, blue); +// map_color_string(pcbhl_conf.appearance.color.grid, &priv->grid_color); +// +// config_color_button_update(&ghidgui->common, pcb_conf_get_field("appearance/color/grid"), -1); +// +// //if (priv->grid_gc) +// // gdk_gc_set_foreground(priv->grid_gc, &gport->grid_color); +//} + +/* Assign "identified" colors to background, offlimits and grid cached structures */ +static void ghid_cairo_set_special_colors(conf_native_t * cfg) +{ + render_priv_t *priv = ghidgui->port.render_priv; + + if (((CFT_COLOR *) cfg->val.color == &pcbhl_conf.appearance.color.background) /*&& priv->bg_gc */ ) { + if (map_color(&cfg->val.color[0], &priv->bg_color)) { + //gdk_gc_set_foreground(priv->bg_gc, &priv->bg_color); + //set_special_grid_color(); + } + } + else if (((CFT_COLOR *) cfg->val.color == &pcbhl_conf.appearance.color.off_limit) /*&& priv->offlimits_gc */ ) { + if (map_color(&cfg->val.color[0], &priv->offlimits_color)) { + //gdk_gc_set_foreground(priv->offlimits_gc, &priv->offlimits_color); + } + } + else if (((CFT_COLOR *) cfg->val.color == &pcbhl_conf.appearance.color.grid) /*&& priv->grid_gc */ ) { + if (map_color(&cfg->val.color[0], &priv->grid_color)) { + //set_special_grid_color(); + } + } +} + +static void ghid_cairo_set_color(pcb_hid_gc_t gc, const pcb_color_t *color) +{ + render_priv_t *priv = ghidgui->port.render_priv; + const char *name = color->str; + + if (name == NULL) { + fprintf(stderr, "ghid_cairo_set_color(): name = NULL, setting to magenta\n"); + color = pcb_color_magenta; + } + + gc->pcolor = *color; + + //if (!gc->gc) + // return; + //if (gport->colormap == 0) + // gport->colormap = gtk_widget_get_colormap(gport->top_window); + + if (pcb_color_is_drill(color)) { + copy_color(&gc->color, &priv->offlimits_color); + //gdk_cairo_set_source_rgba(cr, &priv->offlimits_color); + //gdk_gc_set_foreground(gc->gc, &gport->offlimits_color); + } + else { + pcb_gtk_color_cache_t *cc; + + if (!priv->ccache_inited) { + pcb_clrcache_init(&priv->ccache, sizeof(pcb_gtk_color_cache_t), NULL); + priv->ccache_inited = 1; + } + cc = pcb_clrcache_get(&priv->ccache, color, 1); + if (!cc->color_set) { + map_color(color, &cc->color); + cc->color_set = 1; + } + //if (gc->xor_mask) { + // if (!cc->xor_set) { + // cc->xor_color.red = cc->color.red ^ gport->bg_color.red; + // cc->xor_color.green = cc->color.green ^ gport->bg_color.green; + // cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue; + // gdk_color_alloc(gport->colormap, &cc->xor_color); + // cc->xor_set = 1; + // } + // gdk_gc_set_foreground(gc->gc, &cc->xor_color); + //} + //else { + // gdk_gc_set_foreground(gc->gc, &cc->color); + //} + cc->color.alpha = 1.0; + copy_color(&gc->color, &cc->color); + } +} + +static void ghid_cairo_set_line_cap(pcb_hid_gc_t gc, pcb_cap_style_t style) +{ + cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT; + cairo_line_join_t join = CAIRO_LINE_JOIN_MITER; + + switch (style) { + case pcb_cap_round: + cap = CAIRO_LINE_CAP_ROUND; + join = CAIRO_LINE_JOIN_ROUND; + break; + case pcb_cap_square: + cap = CAIRO_LINE_CAP_SQUARE; + join = CAIRO_LINE_JOIN_MITER; + break; + default: + assert(!"unhandled cap"); + } + gc->cap = cap; + gc->join = join; + + //if (gc->gc) + // gdk_gc_set_line_attributes(WHICH_GC(gc), Vz(gc->width), GDK_LINE_SOLID, (GdkCapStyle) gc->cap, (GdkJoinStyle) gc->join); +} + +static void ghid_cairo_set_line_width(pcb_hid_gc_t gc, pcb_coord_t width) +{ + //render_priv_t *priv = gport->render_priv; + + //if (priv->cr == NULL) + // return; + + gc->width = width; + //cairo_set_line_width(priv->cr, Vz(gc->width)); + //if (gc->gc) + // gdk_gc_set_line_attributes(WHICH_GC(gc), Vz(gc->width), GDK_LINE_SOLID, (GdkCapStyle) gc->cap, (GdkJoinStyle) gc->join); +} + +static void ghid_cairo_set_draw_xor(pcb_hid_gc_t gc, int xor_mask) +{ + render_priv_t *priv = ghidgui->port.render_priv; + + priv->xor_mode = (xor_mask) ? 1 : 0; + //gc->xor_mask = xor_mask; + //if (!gc->gc) + // return; + //gdk_gc_set_function(gc->gc, xor_mask ? GDK_XOR : GDK_COPY); + //ghid_cairo_set_color(gc, gc->pcolor.str); +} + +static int use_gc(pcb_hid_gc_t gc) +{ + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr = priv->cr; + //GdkWindow *window = gtk_widget_get_window(gport->top_window); + + if (gc->me_pointer != >k3_cairo_hid) { + fprintf(stderr, "Fatal: GC from another HID passed to GTK HID\n"); + abort(); + } + + if (cr == NULL) + return 0; + + //ghid_cairo_set_color(gc, gc->pcolor.str); + gdk_cairo_set_source_rgba(cr, &gc->color); + + /* negative line width means "unit is pixels", so -1 is "1 pixel", -5 is "5 pixels", regardless of the zoom. */ + if (gc->width <= 0) + cairo_set_line_width(cr, 1.0 - gc->width); + else + cairo_set_line_width(cr, Vz(gc->width)); + + //ghid_cairo_set_line_cap(gc, (pcb_cap_style_t) gc->cap); + cairo_set_line_cap(cr, gc->cap); + cairo_set_line_join(cr, gc->join); + + + //if (!gc->gc) { + // gc->gc = gdk_gc_new(window); + // ghid_cairo_set_color(gc, gc->colorname); + // ghid_cairo_set_line_width(gc, gc->width); + // ghid_cairo_set_line_cap(gc, (pcb_cap_style_t) gc->cap); + // ghid_cairo_set_draw_xor(gc, gc->xor_mask); + // gdk_gc_set_clip_origin(gc->gc, 0, 0); + //} + //if (gc->mask_seq != mask_seq) { + // if (mask_seq) + // gdk_gc_set_clip_mask(gc->gc, gport->mask); + // else + // set_clip(priv, gc->gc); + // gc->mask_seq = mask_seq; + //} + //priv->u_gc = WHICH_GC(gc); + return 1; +} + +static void ghid_cairo_draw_line(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2) +{ + double dx1, dy1, dx2, dy2; + render_priv_t *priv = ghidgui->port.render_priv; + + dx1 = Vx((double) x1); + dy1 = Vy((double) y1); + dx2 = Vx((double) x2); + dy2 = Vy((double) y2); + + if (!pcb_line_clip + (0, 0, ghidgui->port.view.canvas_width, ghidgui->port.view.canvas_height, &dx1, &dy1, &dx2, &dy2, gc->width / ghidgui->port.view.coord_per_px)) + return; + + USE_GC(gc); + cr_draw_line(priv->cr, FALSE, dx1, dy1, dx2, dy2); + //gdk_draw_line(gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2); +} + +/* Draws an arc from center (cx, cy), cx>0 to the right, cy>0 to the bottom, using + xradius on X axis, and yradius on Y axis, sweeping a delta_angle from start_angle. + + Angles (in degres) originate at 9 o'clock and are positive CCW (counter clock wise). + delta_angle sweeps the angle from start_angle, CW if negative. */ +static void ghid_cairo_draw_arc(pcb_hid_gc_t gc, pcb_coord_t cx, pcb_coord_t cy, + pcb_coord_t xradius, pcb_coord_t yradius, pcb_angle_t start_angle, pcb_angle_t delta_angle) +{ + double w, h, radius, angle1, angle2; + render_priv_t *priv = ghidgui->port.render_priv; + + w = ghidgui->port.view.canvas_width * ghidgui->port.view.coord_per_px; + h = ghidgui->port.view.canvas_height * ghidgui->port.view.coord_per_px; + radius = (xradius > yradius) ? xradius : yradius; + if (SIDE_X(&ghidgui->port.view, cx) < ghidgui->port.view.x0 - radius + || SIDE_X(&ghidgui->port.view, cx) > ghidgui->port.view.x0 + w + radius + || SIDE_Y(&ghidgui->port.view, cy) < ghidgui->port.view.y0 - radius || SIDE_Y(&ghidgui->port.view, cy) > ghidgui->port.view.y0 + h + radius) + return; + + USE_GC(gc); + + if ((delta_angle > 360.0) || (delta_angle < -360.0)) { + start_angle = 0; + delta_angle = 360; + } + + if (pcbhl_conf.editor.view.flip_x) { + start_angle = 180 - start_angle; + delta_angle = -delta_angle; + } + if (pcbhl_conf.editor.view.flip_y) { + start_angle = -start_angle; + delta_angle = -delta_angle; + } + /* make sure we fall in the -180 to +180 range */ + start_angle = pcb_normalize_angle(start_angle); + if (start_angle >= 180) + start_angle -= 360; + + /* cairo lib. originates angles at 3 o'clock, positive traveling CW, requires angle1 < angle2 */ + angle1 = (180.0 - start_angle); + angle2 = (delta_angle < 0) ? (angle1 - delta_angle) : angle1; + if (delta_angle > 0) { + angle1 = (180.0 - start_angle - delta_angle); + } + angle1 *= (M_PI / 180.0); + angle2 *= (M_PI / 180.0); + cairo_save(priv->cr); +TODO("gtk3: this will draw an arc of a circle, not an ellipse! Explore matrix transformation here.") + cairo_arc(priv->cr, pcb_round(Vxd(cx)), pcb_round(Vyd(cy)), Vzd(radius), angle1, angle2); + cairo_stroke(priv->cr); + cairo_restore(priv->cr); + //gdk_draw_arc(gport->drawable, priv->u_gc, 0, + // pcb_round(Vxd(cx) - Vzd(xradius) + 0.5), pcb_round(Vyd(cy) - Vzd(yradius) + 0.5), + // pcb_round(vrx2), pcb_round(vry2), (start_angle + 180) * 64, delta_angle * 64); +} + +static void cr_draw_rect(pcb_hid_gc_t gc, int fill, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2) +{ + gint w, h, lw; + render_priv_t *priv = ghidgui->port.render_priv; + + if (priv->cr == NULL) + return; + + lw = gc->width; + w = ghidgui->port.view.canvas_width * ghidgui->port.view.coord_per_px; + h = ghidgui->port.view.canvas_height * ghidgui->port.view.coord_per_px; + + if ((SIDE_X(&ghidgui->port.view, x1) < ghidgui->port.view.x0 - lw && SIDE_X(&ghidgui->port.view, x2) < ghidgui->port.view.x0 - lw) + || (SIDE_X(&ghidgui->port.view, x1) > ghidgui->port.view.x0 + w + lw && SIDE_X(&ghidgui->port.view, x2) > ghidgui->port.view.x0 + w + lw) + || (SIDE_Y(&ghidgui->port.view, y1) < ghidgui->port.view.y0 - lw && SIDE_Y(&ghidgui->port.view, y2) < ghidgui->port.view.y0 - lw) + || (SIDE_Y(&ghidgui->port.view, y1) > ghidgui->port.view.y0 + h + lw && SIDE_Y(&ghidgui->port.view, y2) > ghidgui->port.view.y0 + h + lw)) + return; + + x1 = Vx(x1); + y1 = Vy(y1); + x2 = Vx(x2); + y2 = Vy(y2); + + if (x1 > x2) { + gint xt = x1; + x1 = x2; + x2 = xt; + } + if (y1 > y2) { + gint yt = y1; + y1 = y2; + y2 = yt; + } + + USE_GC(gc); + cairo_rectangle(priv->cr, x1, y1, x2 - x1 + 1, y2 - y1 + 1); + //gdk_draw_rectangle(gport->drawable, priv->u_gc, FALSE, x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + if (fill) + cairo_fill(priv->cr); + else + cairo_stroke(priv->cr); +} + +static void cr_paint_from_surface(cairo_t * cr, cairo_surface_t * surface) +{ + cairo_set_source_surface(cr, surface, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha(cr, 1.0); +} + +static void ghid_cairo_draw_rect(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2) +{ + cr_draw_rect(gc, FALSE, x1, y1,x2, y2); +} + +static void ghid_cairo_fill_rect(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2) +{ + cr_draw_rect(gc, TRUE, x1, y1,x2, y2); +} + +static void ghid_cairo_fill_circle(pcb_hid_gc_t gc, pcb_coord_t cx, pcb_coord_t cy, pcb_coord_t radius) +{ + gint w, h, vr; + render_priv_t *priv = ghidgui->port.render_priv; + + if (priv->cr == NULL) + return; + + w = ghidgui->port.view.canvas_width * ghidgui->port.view.coord_per_px; + h = ghidgui->port.view.canvas_height * ghidgui->port.view.coord_per_px; + if (SIDE_X(&ghidgui->port.view, cx) < ghidgui->port.view.x0 - radius + || SIDE_X(&ghidgui->port.view, cx) > ghidgui->port.view.x0 + w + radius + || SIDE_Y(&ghidgui->port.view, cy) < ghidgui->port.view.y0 - radius || SIDE_Y(&ghidgui->port.view, cy) > ghidgui->port.view.y0 + h + radius) + return; + + USE_GC(gc); + vr = Vz(radius); + cairo_arc(priv->cr, Vx(cx), Vy(cy), vr, 0.0, 2 * M_PI); + cairo_fill(priv->cr); + //gdk_draw_arc(gport->drawable, priv->u_gc, TRUE, Vx(cx) - vr, Vy(cy) - vr, vr * 2, vr * 2, 0, 360 * 64); +} + +/* Intentional code duplication from ghid_cairo_fill_polygon_offs(), for performance */ +static void ghid_cairo_fill_polygon(pcb_hid_gc_t gc, int n_coords, pcb_coord_t * x, pcb_coord_t * y) +{ + int i; + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr = priv->cr; + int _x, _y; + + if (priv->cr == NULL) + return; + + USE_GC(gc); + + for (i = 0; i < n_coords; i++) { + _x = Vx(x[i]); + _y = Vy(y[i]); + if (i == 0) + cairo_move_to(cr, _x, _y); + else + cairo_line_to(cr, _x, _y); + } + cairo_fill(cr); +} + +static void ghid_cairo_fill_polygon_offs(pcb_hid_gc_t gc, int n_coords, pcb_coord_t *x, pcb_coord_t *y, pcb_coord_t dx, pcb_coord_t dy) +{ + int i; + render_priv_t *priv = ghidgui->port.render_priv; + cairo_t *cr = priv->cr; + int _x, _y; + + if (priv->cr == NULL) + return; + + USE_GC(gc); + + for (i = 0; i < n_coords; i++) { + _x = Vx(x[i]); + _y = Vy(y[i]); + if (i == 0) + cairo_move_to(cr, _x + Vz(dx), _y + Vz(dy)); + else + cairo_line_to(cr, _x + Vz(dx), _y + Vz(dy)); + } + cairo_fill(cr); +} + +/* Fills the drawing with only bg_color */ +static void erase_with_background_color(cairo_t * cr, GdkRGBA * bg_color) +{ + gdk_cairo_set_source_rgba(cr, bg_color); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint_with_alpha(cr, 1.0); +} + +static void redraw_region(pcb_hidlib_t *hidlib, GdkRectangle *rect) +{ + int eleft, eright, etop, ebottom; + pcb_hid_expose_ctx_t ctx; + render_priv_t *priv = ghidgui->port.render_priv; + + if (priv->cr_drawing_area == NULL) + return; + + if (rect != NULL) { + priv->clip_rect = *rect; + priv->clip = pcb_true; + } + else { + priv->clip_rect.x = 0; + priv->clip_rect.y = 0; + priv->clip_rect.width = ghidgui->port.view.canvas_width; + priv->clip_rect.height = ghidgui->port.view.canvas_height; + priv->clip = pcb_false; + } + + //set_clip(priv, priv->bg_gc); + //set_clip(priv, priv->offlimits_gc); + //set_clip(priv, priv->mask_gc); + //set_clip(priv, priv->grid_gc); + + ctx.view.X1 = MIN(Px(priv->clip_rect.x), Px(priv->clip_rect.x + priv->clip_rect.width + 1)); + ctx.view.Y1 = MIN(Py(priv->clip_rect.y), Py(priv->clip_rect.y + priv->clip_rect.height + 1)); + ctx.view.X2 = MAX(Px(priv->clip_rect.x), Px(priv->clip_rect.x + priv->clip_rect.width + 1)); + ctx.view.Y2 = MAX(Py(priv->clip_rect.y), Py(priv->clip_rect.y + priv->clip_rect.height + 1)); + + ctx.view.X1 = MAX(0, MIN(hidlib->size_x, ctx.view.X1)); + ctx.view.X2 = MAX(0, MIN(hidlib->size_x, ctx.view.X2)); + ctx.view.Y1 = MAX(0, MIN(hidlib->size_y, ctx.view.Y1)); + ctx.view.Y2 = MAX(0, MIN(hidlib->size_y, ctx.view.Y2)); + + eleft = Vx(0); + eright = Vx(hidlib->size_x); + etop = Vy(0); + ebottom = Vy(hidlib->size_y); + if (eleft > eright) { + int tmp = eleft; + eleft = eright; + eright = tmp; + } + if (etop > ebottom) { + int tmp = etop; + etop = ebottom; + ebottom = tmp; + } + + /* Paints only on the drawing_area */ + priv->cr = priv->cr_drawing_area; + priv->cr_target = priv->cr_drawing_area; + + erase_with_background_color(priv->cr, &priv->bg_color); + + gdk_cairo_set_source_rgba(priv->cr, &priv->offlimits_color); + if (eleft > 0) { + cairo_rectangle(priv->cr, 0.0, 0.0, eleft, ghidgui->port.view.canvas_height); + } + else + eleft = 0; + if (eright < ghidgui->port.view.canvas_width) { + cairo_rectangle(priv->cr, eright, 0.0, ghidgui->port.view.canvas_width - eright, ghidgui->port.view.canvas_height); + } + else + eright = ghidgui->port.view.canvas_width; + if (etop > 0) { + cairo_rectangle(priv->cr, eleft, 0.0, eright - eleft + 1, etop); + } + else + etop = 0; + if (ebottom < ghidgui->port.view.canvas_height) { + cairo_rectangle(priv->cr, eleft, ebottom, eright - eleft + 1, ghidgui->port.view.canvas_height - ebottom); + } + else + ebottom = ghidgui->port.view.canvas_height; + + cairo_fill(priv->cr); + //gdk_draw_rectangle(gport->drawable, priv->bg_gc, 1, eleft, etop, eright - eleft + 1, ebottom - etop + 1); + + ghid_cairo_draw_bg_image(hidlib); + + pcbhl_expose_main(>k3_cairo_hid, &ctx, NULL); + ghid_cairo_draw_grid(hidlib); + + /* Draws "GUI" information on top of design */ + priv->cr = priv->cr_drawing_area; + /* In some cases we are called with the crosshair still off */ + if (priv->attached_invalidate_depth == 0) + pcbhl_draw_attached(hidlib, 0); + + /* In some cases we are called with the mark still off */ + if (priv->mark_invalidate_depth == 0) + pcbhl_draw_marks(hidlib, 0); + + priv->clip = pcb_false; + + /* Rest the clip for bg_gc, as it is used outside this function */ + //gdk_gc_set_clip_mask(priv->bg_gc, NULL); + ghid_cairo_screen_update(); +} + +static int preview_lock = 0; +static void ghid_cairo_invalidate_lr(pcb_hid_t *hid, pcb_coord_t left, pcb_coord_t right, pcb_coord_t top, pcb_coord_t bottom) +{ + pcb_hidlib_t *hidlib = ghidgui->hidlib; + int dleft, dright, dtop, dbottom; + int minx, maxx, miny, maxy; + GdkRectangle rect; + + dleft = Vx(left); + dright = Vx(right); + dtop = Vy(top); + dbottom = Vy(bottom); + + minx = MIN(dleft, dright); + maxx = MAX(dleft, dright); + miny = MIN(dtop, dbottom); + maxy = MAX(dtop, dbottom); + + rect.x = minx; + rect.y = miny; + rect.width = maxx - minx; + rect.height = maxy - miny; + + redraw_region(hidlib, &rect); + if (!preview_lock) { + preview_lock++; + pcb_gtk_previews_invalidate_lr(minx, maxx, miny, maxy); + preview_lock--; + } + + ghid_cairo_screen_update(); +} + +static void ghid_cairo_invalidate_all(pcb_hid_t *hid) +{ + pcb_hidlib_t *hidlib = ghidgui->hidlib; + + if (ghidgui && ghidgui->topwin.menu.menu_bar) { + redraw_region(hidlib, NULL); + if (!preview_lock) { + preview_lock++; + pcb_gtk_previews_invalidate_all(); + preview_lock--; + } + ghid_cairo_screen_update(); + } +} + +static void ghid_cairo_notify_crosshair_change(pcb_hid_t *hid, pcb_bool changes_complete) +{ + pcb_hidlib_t *hidlib = ghidgui->hidlib; + render_priv_t *priv = ghidgui->port.render_priv; + + /* We sometimes get called before the GUI is up */ + if (ghidgui->port.drawing_area == NULL) + return; + + /* Keeps track of calls (including XOR draw mode), and draws only when complete and final. */ + if (changes_complete) + priv->attached_invalidate_depth--; + else + priv->attached_invalidate_depth = 1 + priv->xor_mode; + + if (changes_complete && ghidgui->port.drawing_area != NULL && priv->attached_invalidate_depth <= 0) { + /* Queues a GTK redraw when changes are complete */ + ghid_cairo_invalidate_all(pcb_gui); + priv->attached_invalidate_depth = 0; + } +} + +static void ghid_cairo_notify_mark_change(pcb_hid_t *hid, pcb_bool changes_complete) +{ + pcb_hidlib_t *hidlib = ghidgui->hidlib; + render_priv_t *priv = ghidgui->port.render_priv; + + /* We sometimes get called before the GUI is up */ + if (ghidgui->port.drawing_area == NULL) + return; + + if (changes_complete) + priv->mark_invalidate_depth--; + + if (priv->mark_invalidate_depth < 0) { + priv->mark_invalidate_depth = 0; + /* A mismatch of changes_complete == pcb_false and == pcb_true notifications + is not expected to occur, but we will try to handle it gracefully. + As we know the mark will have been shown already, we must + repaint the entire view to be sure not to leave an artefact. + */ + ghid_cairo_invalidate_all(pcb_gui); + return; + } + + if (priv->mark_invalidate_depth == 0) + pcbhl_draw_marks(hidlib, 0); + + if (!changes_complete) { + priv->mark_invalidate_depth++; + } + else if (ghidgui->port.drawing_area != NULL) { + /* Queue a GTK expose when changes are complete */ + ghid_draw_area_update(&ghidgui->port, NULL); + } +} + +static void draw_right_cross(cairo_t * cr, gint x, gint y) +{ + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + + cr_draw_line(cr, FALSE, x, 0, x, ghidgui->port.view.canvas_height); + cr_draw_line(cr, FALSE, 0, y, ghidgui->port.view.canvas_width, y); + //gdk_draw_line(window, xor_gc, x, 0, x, gport->view.canvas_height); + //gdk_draw_line(window, xor_gc, 0, y, gport->view.canvas_width, y); +} + +static void draw_slanted_cross(cairo_t * xor_gc, gint x, gint y) +{ + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + gint x0, y0, x1, y1; + + x0 = x + (ghidgui->port.view.canvas_height - y); + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x - y; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + (ghidgui->port.view.canvas_width - x); + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - x; + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); + + x0 = x - (ghidgui->port.view.canvas_height - y); + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x + y; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + x; + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - (ghidgui->port.view.canvas_width - x); + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); +} + +static void draw_dozen_cross(cairo_t * xor_gc, gint x, gint y) +{ + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + gint x0, y0, x1, y1; + gdouble tan60 = sqrt(3); + + x0 = x + (ghidgui->port.view.canvas_height - y) / tan60; + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x - y / tan60; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + (ghidgui->port.view.canvas_width - x) * tan60; + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - x * tan60; + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); + + x0 = x + (ghidgui->port.view.canvas_height - y) * tan60; + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x - y * tan60; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + (ghidgui->port.view.canvas_width - x) / tan60; + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - x / tan60; + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); + + x0 = x - (ghidgui->port.view.canvas_height - y) / tan60; + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x + y / tan60; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + x * tan60; + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - (ghidgui->port.view.canvas_width - x) * tan60; + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); + + x0 = x - (ghidgui->port.view.canvas_height - y) * tan60; + x0 = MAX(0, MIN(x0, ghidgui->port.view.canvas_width)); + x1 = x + y * tan60; + x1 = MAX(0, MIN(x1, ghidgui->port.view.canvas_width)); + y0 = y + x / tan60; + y0 = MAX(0, MIN(y0, ghidgui->port.view.canvas_height)); + y1 = y - (ghidgui->port.view.canvas_width - x) / tan60; + y1 = MAX(0, MIN(y1, ghidgui->port.view.canvas_height)); + cr_draw_line(ghidgui->port.render_priv->cr, FALSE, x0, y0, x1, y1); + //gdk_draw_line(window, xor_gc, x0, y0, x1, y1); +} + +static void draw_crosshair(cairo_t * xor_gc, gint x, gint y) +{ + static enum pcb_crosshair_shape_e prev = pcb_ch_shape_basic; + + if (ghidgui->port.view.crosshair_x < 0 || !ghidgui->topwin.active || !ghidgui->port.view.has_entered) + return; + + draw_right_cross(xor_gc, x, y); + if (prev == pcb_ch_shape_union_jack) + draw_slanted_cross(xor_gc, x, y); + if (prev == pcb_ch_shape_dozen) + draw_dozen_cross(xor_gc, x, y); + prev = pcbhl_conf.editor.crosshair_shape_idx; +} + +static void show_crosshair(gboolean paint_new_location) +{ + render_priv_t *priv = ghidgui->port.render_priv; + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + //GtkStyle *style = gtk_widget_get_style(gport->drawing_area); + gint x, y; + static gint x_prev = -1, y_prev = -1; + //static cairo_t *xor_gc; + //static GdkColor cross_color; + cairo_t *cr; + + if (ghidgui->port.view.crosshair_x < 0 || !ghidgui->topwin.active || !ghidgui->port.view.has_entered) { + x_prev = y_prev = -1; /* if leaving the drawing area, invalidate last known coord to make sure we redraw on reenter, even on the same coords */ + return; + } + +TODO("gtk3: when CrossColor changed from config") + map_color(&pcbhl_conf.appearance.color.cross, &priv->crosshair_color); + //cr = priv->cr_drawing_area; + cr = priv->cr; + gdk_cairo_set_source_rgba(cr, &priv->crosshair_color); + cairo_set_line_width(cr, 1.0); + + //if (!xor_gc) { + // xor_gc = gdk_gc_new(window); + // gdk_gc_copy(xor_gc, style->white_gc); + // gdk_gc_set_function(xor_gc, GDK_XOR); + // gdk_gc_set_clip_origin(xor_gc, 0, 0); + // set_clip(priv, xor_gc); +TODO("gtk3:FIXME: when CrossColor changed from config") + // map_color(&pcbhl_conf.appearance.color.cross, &cross_color); + //} + x = Vx(ghidgui->port.view.crosshair_x); + y = Vy(ghidgui->port.view.crosshair_y); + + //gdk_gc_set_foreground(xor_gc, &cross_color); + + if (x_prev >= 0 && !paint_new_location) + draw_crosshair(cr, x_prev, y_prev); + + if (x >= 0 && paint_new_location) { + draw_crosshair(cr, x, y); + x_prev = x; + y_prev = y; + } + else + x_prev = y_prev = -1; +} + +static void ghid_cairo_init_renderer(int *argc, char ***argv, void *vport) +{ + pcb_gtk_port_t *port = vport; + + /* Init any GC's required */ + port->render_priv = g_new0(render_priv_t, 1); + port->render_priv->cr = NULL; + port->render_priv->surf_da = NULL; + port->render_priv->cr_drawing_area = NULL; +} + +static void ghid_cairo_shutdown_renderer(void *vport) +{ + pcb_gtk_port_t *port = vport; + render_priv_t *priv = port->render_priv; + + cairo_surface_destroy(priv->surf_da); + priv->surf_da = NULL; + cairo_destroy(priv->cr_drawing_area); + priv->cr_drawing_area = NULL; + priv->cr = NULL; + + g_free(port->render_priv); + port->render_priv = NULL; +} + +static void ghid_cairo_init_drawing_widget(GtkWidget * widget, void *vport) +{ +} + +/* Callback function only applied to drawing_area, after some resizing or initialisation. */ +static void ghid_cairo_drawing_area_configure_hook(void *vport) +{ + pcb_gtk_port_t *port = vport; + static int done_once = 0; + render_priv_t *priv = port->render_priv; + cairo_t *cr; + + ghidgui->port.drawing_allowed = pcb_true; + + if (!done_once) { + //priv->bg_gc = gdk_gc_new(port->drawable); + //gdk_gc_set_foreground(priv->bg_gc, &port->bg_color); + //gdk_gc_set_clip_origin(priv->bg_gc, 0, 0); + // + //priv->offlimits_gc = gdk_gc_new(port->drawable); + //gdk_gc_set_foreground(priv->offlimits_gc, &port->offlimits_color); + //gdk_gc_set_clip_origin(priv->offlimits_gc, 0, 0); + + if (!map_color(&pcbhl_conf.appearance.color.background, &priv->bg_color)) + map_color(pcb_color_white, &priv->bg_color); + + if (!map_color(&pcbhl_conf.appearance.color.off_limit, &priv->offlimits_color)) + map_color(pcb_color_white, &priv->offlimits_color); + + if (!map_color(&pcbhl_conf.appearance.color.grid, &priv->grid_color)) + map_color(pcb_color_blue, &priv->grid_color); + //set_special_grid_color(); + + //if (port->mask) { + // gdk_pixmap_unref(port->mask); + // port->mask = gdk_pixmap_new(0, port->view.canvas_width, port->view.canvas_height, 1); + //} + done_once = 1; + } + + /* Creates a single cairo surface/context for off-line painting */ + cr_create_similar_surface_and_context(&priv->surf_da, &priv->cr_drawing_area, port); + cr_create_similar_surface_and_context(&priv->surf_layer, &priv->cr_layer, port); + priv->cr = priv->cr_drawing_area; +} + +/* GtkDrawingArea -> GtkWidget "draw" signal Call-Back function */ +static gboolean ghid_cairo_drawing_area_expose_cb(GtkWidget * widget, pcb_gtk_expose_t * p, void *vport) +{ + pcb_gtk_port_t *port = vport; + render_priv_t *priv = port->render_priv; + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + + //gdk_draw_drawable(window, priv->bg_gc, port->pixmap, + // ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height); + + cr_paint_from_surface(p, priv->surf_da); + + priv->cr = p; + show_crosshair(TRUE); + priv->cr = priv->cr_drawing_area; + + return FALSE; +} + +static void ghid_cairo_screen_update(void) +{ + render_priv_t *priv = ghidgui->port.render_priv; + //GdkWindow *window = gtk_widget_get_window(gport->drawing_area); + + if (priv->cr == NULL) + return; + + //gdk_draw_drawable(window, priv->bg_gc, gport->pixmap, 0, 0, 0, 0, gport->view.canvas_width, gport->view.canvas_height); + //show_crosshair(TRUE); + gtk_widget_queue_draw(ghidgui->port.drawing_area); +} + +static void ghid_cairo_port_drawing_realize_cb(GtkWidget * widget, gpointer data) +{ +} + +static gboolean ghid_cairo_preview_expose(GtkWidget * widget, pcb_gtk_expose_t * p, + pcb_hid_expose_t expcall, pcb_hid_expose_ctx_t *ctx) +{ + //GdkWindow *window = gtk_widget_get_window(widget); + //GdkDrawable *save_drawable; + GtkAllocation allocation; /* Assuming widget is a drawing widget, get the Rectangle allowed for drawing. */ + pcb_gtk_view_t save_view; + int save_width, save_height; + double xz, yz, vw, vh; + render_priv_t *priv = ghidgui->port.render_priv; + pcb_coord_t ox1 = ctx->view.X1, oy1 = ctx->view.Y1, ox2 = ctx->view.X2, oy2 = ctx->view.Y2; + pcb_coord_t save_cpp; + + vw = ctx->view.X2 - ctx->view.X1; + vh = ctx->view.Y2 - ctx->view.Y1; + + /* Setup drawable and zoom factor for drawing routines + */ + //save_drawable = gport->drawable; + save_view = ghidgui->port.view; + save_width = ghidgui->port.view.canvas_width; + save_height = ghidgui->port.view.canvas_height; + save_cpp = pcb_gui->coord_per_pix; + + gtk_widget_get_allocation(widget, &allocation); + xz = vw / (double) allocation.width; + yz = vh / (double) allocation.height; + if (xz > yz) + ghidgui->port.view.coord_per_px = xz; + else + ghidgui->port.view.coord_per_px = yz; + + //gport->drawable = window; + ghidgui->port.view.canvas_width = allocation.width; + ghidgui->port.view.canvas_height = allocation.height; + ghidgui->port.view.width = allocation.width * ghidgui->port.view.coord_per_px; + ghidgui->port.view.height = allocation.height * ghidgui->port.view.coord_per_px; + ghidgui->port.view.x0 = (vw - ghidgui->port.view.width) / 2 + ctx->view.X1; + ghidgui->port.view.y0 = (vh - ghidgui->port.view.height) / 2 + ctx->view.Y1; + + PCB_GTK_PREVIEW_TUNE_EXTENT(ctx, allocation); + pcb_gui->coord_per_pix = ghidgui->port.view.coord_per_px; + + /* Change current pointer to draw in this widget, draws, then come back to pointer to drawing_area. */ + priv->cr_target = p; + priv->cr = p; + cairo_save(p); + /* clear background */ + erase_with_background_color(p, &priv->bg_color); + /* calls the off-line drawing routine */ + expcall(>k3_cairo_hid, ctx); + cairo_restore(p); + priv->cr = priv->cr_drawing_area; + //gport->drawable = save_drawable; + + /* restore the original context and priv */ + ctx->view.X1 = ox1; ctx->view.X2 = ox2; ctx->view.Y1 = oy1; ctx->view.Y2 = oy2; + pcb_gui->coord_per_pix = save_cpp; + ghidgui->port.view = save_view; + ghidgui->port.view.canvas_width = save_width; + ghidgui->port.view.canvas_height = save_height; + + return FALSE; +} + +static GtkWidget *ghid_cairo_new_drawing_widget(pcb_gtk_impl_t *common) +{ + GtkWidget *w = gtk_drawing_area_new(); + + g_signal_connect(G_OBJECT(w), "draw", G_CALLBACK(common->drawing_area_expose), common->gport); + + return w; +} + + +void ghid_cairo_install(pcb_gtk_impl_t *impl, pcb_hid_t *hid) +{ + if (impl != NULL) { + impl->new_drawing_widget = ghid_cairo_new_drawing_widget; + impl->init_drawing_widget = ghid_cairo_init_drawing_widget; + impl->drawing_realize = ghid_cairo_port_drawing_realize_cb; + impl->drawing_area_expose = ghid_cairo_drawing_area_expose_cb; + impl->preview_expose = ghid_cairo_preview_expose; + //impl->invalidate_all = ghid_cairo_invalidate_all; + impl->set_special_colors = ghid_cairo_set_special_colors; + impl->init_renderer = ghid_cairo_init_renderer; + impl->screen_update = ghid_cairo_screen_update; + impl->draw_grid_local = ghid_cairo_draw_grid_local; + impl->drawing_area_configure_hook = ghid_cairo_drawing_area_configure_hook; + impl->shutdown_renderer = ghid_cairo_shutdown_renderer; + impl->get_color_name = get_color_name; + impl->map_color = map_color; + } + + if (hid != NULL) { + hid->invalidate_lr = ghid_cairo_invalidate_lr; + hid->invalidate_all = ghid_cairo_invalidate_all; + hid->notify_crosshair_change = ghid_cairo_notify_crosshair_change; + hid->notify_mark_change = ghid_cairo_notify_mark_change; + hid->set_layer_group = ghid_cairo_set_layer_group; + hid->end_layer = ghid_cairo_end_layer_group; + hid->make_gc = ghid_cairo_make_gc; + hid->destroy_gc = ghid_cairo_destroy_gc; + hid->render_burst = ghid_cairo_render_burst; + hid->set_drawing_mode = ghid_cairo_set_drawing_mode; + hid->set_color = ghid_cairo_set_color; + hid->set_line_cap = ghid_cairo_set_line_cap; + hid->set_line_width = ghid_cairo_set_line_width; + hid->set_draw_xor = ghid_cairo_set_draw_xor; + hid->draw_line = ghid_cairo_draw_line; + hid->draw_arc = ghid_cairo_draw_arc; + hid->draw_rect = ghid_cairo_draw_rect; + hid->fill_circle = ghid_cairo_fill_circle; + hid->fill_polygon = ghid_cairo_fill_polygon; + hid->fill_polygon_offs = ghid_cairo_fill_polygon_offs; + hid->fill_rect = ghid_cairo_fill_rect; + } +} Index: hid_gtk3_cairo/gtkhid-main.c =================================================================== --- hid_gtk3_cairo/gtkhid-main.c (nonexistent) +++ hid_gtk3_cairo/gtkhid-main.c (revision 27398) @@ -0,0 +1,46 @@ +#include "config.h" + +#include + +#include "plugins.h" +#include "hid_init.h" + +#include "../src_plugins/lib_gtk_common/glue_common.h" +#include "../src_plugins/lib_gtk_common/glue_hid.h" + +const char *ghid_cairo_cookie = "gtk3 hid, cairo"; + +pcb_hid_t gtk3_cairo_hid; + +extern void ghid_cairo_install(pcb_gtk_impl_t *impl, pcb_hid_t *hid); + +int gtk3_cairo_parse_arguments(pcb_hid_t *hid, int *argc, char ***argv) +{ + ghid_glue_common_init(ghid_cairo_cookie); + ghid_cairo_install(&ghidgui->impl, hid); + return gtkhid_parse_arguments(hid, argc, argv); +} + +int pplg_check_ver_hid_gtk3_cairo(int ver_needed) { return 0; } + +void pplg_uninit_hid_gtk3_cairo(void) +{ + ghid_glue_common_uninit(ghid_cairo_cookie); +} + +int pplg_init_hid_gtk3_cairo(void) +{ + PCB_API_CHK_VER; + + ghid_glue_hid_init(>k3_cairo_hid); + + gtk3_cairo_hid.parse_arguments = gtk3_cairo_parse_arguments; + ghid_cairo_install(NULL, >k3_cairo_hid); + + gtk3_cairo_hid.name = "gtk3_cairo"; + gtk3_cairo_hid.description = "Gtk3 - The Gimp Toolkit, with cairo rendering"; + + pcb_hid_register_hid(>k3_cairo_hid); + + return 0; +} Index: hid_gtk3_cairo/hid_gtk3_cairo.pup =================================================================== --- hid_gtk3_cairo/hid_gtk3_cairo.pup (nonexistent) +++ hid_gtk3_cairo/hid_gtk3_cairo.pup (revision 27398) @@ -0,0 +1,7 @@ +$class hid +$short #GTK3 GUI, cairo render +$long GUI: the GTK3 HID, using cairo for rendering +$state WIP +default disable-all +dep lib_gtk_common +autoload 1