Shiny 3D eye-candy
[geda-pcb/pcjc2.git] / src / hid / gtk / gtkhid-gl.c
blobcdec217488e480dd97b4c741b91d56f59dc62b08
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
7 #include "crosshair.h"
8 #include "clip.h"
9 #include "../hidint.h"
10 #include "gui.h"
11 #include "gui-pinout-preview.h"
13 /* The Linux OpenGL ABI 1.0 spec requires that we define
14 * GL_GLEXT_PROTOTYPES before including gl.h or glx.h for extensions
15 * in order to get prototypes:
16 * http://www.opengl.org/registry/ABI/
19 #define GL_GLEXT_PROTOTYPES 1
20 #ifdef HAVE_OPENGL_GL_H
21 # include <OpenGL/gl.h>
22 #else
23 # include <GL/gl.h>
24 #endif
26 #include <gtk/gtkgl.h>
27 #include "hid/common/hidgl.h"
29 #include "hid/common/draw_helpers.h"
30 #include "hid/common/trackball.h"
32 #ifdef HAVE_LIBDMALLOC
33 #include <dmalloc.h>
34 #endif
36 extern HID ghid_hid;
37 extern HID_DRAW ghid_graphics;
38 extern HID_DRAW_CLASS ghid_graphics_class;
40 static hidGC current_gc = NULL;
42 /* Sets gport->u_gc to the "right" GC to use (wrt mask or window)
44 #define USE_GC(gc) if (!use_gc(gc)) return
46 static enum mask_mode cur_mask = HID_MASK_OFF;
47 static GLfloat view_matrix[4][4] = {{1.0, 0.0, 0.0, 0.0},
48 {0.0, 1.0, 0.0, 0.0},
49 {0.0, 0.0, 1.0, 0.0},
50 {0.0, 0.0, 0.0, 1.0}};
51 static GLfloat last_modelview_matrix[4][4] = {{1.0, 0.0, 0.0, 0.0},
52 {0.0, 1.0, 0.0, 0.0},
53 {0.0, 0.0, 1.0, 0.0},
54 {0.0, 0.0, 0.0, 1.0}};
55 static int global_view_2d = 1;
57 typedef struct render_priv {
58 GdkGLConfig *glconfig;
59 bool trans_lines;
60 bool in_context;
61 int subcomposite_stencil_bit;
62 char *current_colorname;
63 double current_alpha_mult;
64 GTimer *time_since_expose;
66 /* Feature for leading the user to a particular location */
67 guint lead_user_timeout;
68 GTimer *lead_user_timer;
69 bool lead_user;
70 Coord lead_user_radius;
71 Coord lead_user_x;
72 Coord lead_user_y;
74 hidGC crosshair_gc;
75 hidgl_instance *hidgl;
77 } render_priv;
79 typedef struct gtk_gc_struct
81 struct hidgl_gc_struct hidgl_gc; /* Parent */
83 const char *colorname;
84 double alpha_mult;
85 Coord width;
86 gint cap, join;
87 } *gtkGC;
89 static void draw_lead_user (hidGC gc, render_priv *priv);
90 static void ghid_unproject_to_z_plane (int ex, int ey, Coord pcb_z, Coord *pcb_x, Coord *pcb_y);
93 #define BOARD_THICKNESS MM_TO_COORD(1.60)
94 #define MASK_COPPER_SPACING MM_TO_COORD(0.05)
95 #define SILK_MASK_SPACING MM_TO_COORD(0.01)
96 static int
97 compute_depth (int group)
99 static int last_depth_computed = 0;
101 int top_group;
102 int bottom_group;
103 int min_copper_group;
104 int max_copper_group;
105 int num_copper_groups;
106 int middle_copper_group;
107 int depth;
109 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
110 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
112 min_copper_group = MIN (bottom_group, top_group);
113 max_copper_group = MAX (bottom_group, top_group);
114 num_copper_groups = max_copper_group - min_copper_group + 1;
115 middle_copper_group = min_copper_group + num_copper_groups / 2;
117 if (group >= 0 && group < max_group) {
118 if (group >= min_copper_group && group <= max_copper_group) {
119 /* XXX: IS THIS INCORRECT FOR REVERSED GROUP ORDERINGS? */
120 depth = -(group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups;
121 } else {
122 depth = 0;
125 } else if (SL_TYPE (group) == SL_MASK) {
126 if (SL_SIDE (group) == SL_TOP_SIDE) {
127 depth = -((min_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups - MASK_COPPER_SPACING);
128 } else {
129 depth = -((max_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups + MASK_COPPER_SPACING);
131 } else if (SL_TYPE (group) == SL_SILK) {
132 if (SL_SIDE (group) == SL_TOP_SIDE) {
133 depth = -((min_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups - MASK_COPPER_SPACING - SILK_MASK_SPACING);
134 } else {
135 depth = -((max_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups + MASK_COPPER_SPACING + SILK_MASK_SPACING);
138 } else if (SL_TYPE (group) == SL_INVISIBLE) {
139 /* Same as silk, but for the back-side layer */
140 if (Settings.ShowBottomSide) {
141 depth = -((min_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups - MASK_COPPER_SPACING - SILK_MASK_SPACING);
142 } else {
143 depth = -((max_copper_group - middle_copper_group) * BOARD_THICKNESS / num_copper_groups + MASK_COPPER_SPACING + SILK_MASK_SPACING);
145 } else if (SL_TYPE (group) == SL_RATS ||
146 SL_TYPE (group) == SL_PDRILL ||
147 SL_TYPE (group) == SL_UDRILL) {
148 /* Draw these at the depth we last rendered at */
149 depth = last_depth_computed;
150 } else if (SL_TYPE (group) == SL_PASTE ||
151 SL_TYPE (group) == SL_FAB ||
152 SL_TYPE (group) == SL_ASSY) {
153 /* Layer types we don't use, which draw.c asks us about, so
154 * we just return _something_ to avoid the warnign below. */
155 depth = last_depth_computed;
156 } else {
157 /* DEFAULT CASE */
158 printf ("Unknown layer group to set depth for: %i\n", group);
159 depth = last_depth_computed;
162 last_depth_computed = depth;
163 return depth;
166 static void
167 start_subcomposite (hidgl_instance *hidgl)
169 render_priv *priv = gport->render_priv;
170 int stencil_bit;
172 /* Flush out any existing geoemtry to be rendered */
173 hidgl_flush_triangles (hidgl);
175 glEnable (GL_STENCIL_TEST); /* Enable Stencil test */
176 glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); /* Stencil pass => replace stencil value (with 1) */
178 stencil_bit = hidgl_assign_clear_stencil_bit (hidgl); /* Get a new (clean) bitplane to stencil with */
179 glStencilMask (stencil_bit); /* Only write to our subcompositing stencil bitplane */
180 glStencilFunc (GL_GREATER, stencil_bit, stencil_bit); /* Pass stencil test if our assigned bit is clear */
182 priv->subcomposite_stencil_bit = stencil_bit;
185 static void
186 end_subcomposite (hidgl_instance *hidgl)
188 render_priv *priv = gport->render_priv;
190 /* Flush out any existing geoemtry to be rendered */
191 hidgl_flush_triangles (hidgl);
193 hidgl_return_stencil_bit (hidgl, priv->subcomposite_stencil_bit); /* Relinquish any bitplane we previously used */
195 glStencilMask (0);
196 glStencilFunc (GL_ALWAYS, 0, 0); /* Always pass stencil test */
197 glDisable (GL_STENCIL_TEST); /* Disable Stencil test */
199 priv->subcomposite_stencil_bit = 0;
204 ghid_set_layer (const char *name, int group, int empty)
206 render_priv *priv = gport->render_priv;
207 hidgl_instance *hidgl = priv->hidgl;
208 int idx = group;
210 if (idx >= 0 && idx < max_group)
212 int n = PCB->LayerGroups.Number[group];
213 for (idx = 0; idx < n-1; idx ++)
215 int ni = PCB->LayerGroups.Entries[group][idx];
216 if (ni >= 0 && ni < max_copper_layer + EXTRA_LAYERS
217 && PCB->Data->Layer[ni].On)
218 break;
220 idx = PCB->LayerGroups.Entries[group][idx];
223 end_subcomposite (hidgl);
224 start_subcomposite (hidgl);
226 /* Drawing is already flushed by {start,end}_subcomposite */
227 hidgl_set_depth (compute_depth (group));
229 if (idx >= 0 && idx < max_copper_layer + EXTRA_LAYERS)
231 priv->trans_lines = true;
232 return PCB->Data->Layer[idx].On;
235 if (idx < 0)
237 switch (SL_TYPE (idx))
239 case SL_INVISIBLE:
240 return PCB->InvisibleObjectsOn;
241 case SL_MASK:
242 if (SL_MYSIDE (idx))
243 return TEST_FLAG (SHOWMASKFLAG, PCB);
244 return 0;
245 case SL_SILK:
246 priv->trans_lines = true;
247 if (SL_MYSIDE (idx))
248 return PCB->ElementOn;
249 return 0;
250 case SL_ASSY:
251 return 0;
252 case SL_PDRILL:
253 case SL_UDRILL:
254 return 1;
255 case SL_RATS:
256 if (PCB->RatOn)
257 priv->trans_lines = true;
258 return PCB->RatOn;
261 return 0;
264 static void
265 ghid_end_layer ()
267 render_priv *priv = gport->render_priv;
268 hidgl_instance *hidgl = priv->hidgl;
270 end_subcomposite (hidgl);
273 void
274 ghid_destroy_gc (hidGC gc)
276 hidgl_finish_gc (gc);
277 g_free (gc);
280 hidGC
281 ghid_make_gc (void)
283 render_priv *priv = gport->render_priv;
284 hidGC gc = (hidGC) g_new0 (struct gtk_gc_struct, 1);
285 gtkGC gtk_gc = (gtkGC)gc;
287 gc->hid = &ghid_hid;
288 gc->hid_draw = &ghid_graphics;
290 hidgl_init_gc (priv->hidgl, gc);
292 gtk_gc->colorname = Settings.BackgroundColor;
293 gtk_gc->alpha_mult = 1.0;
295 return gc;
298 static void
299 ghid_draw_grid (hidGC gc, BoxType *drawn_area)
301 if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
302 return;
304 if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
306 gport->grid_color.red ^= gport->bg_color.red;
307 gport->grid_color.green ^= gport->bg_color.green;
308 gport->grid_color.blue ^= gport->bg_color.blue;
311 glEnable (GL_COLOR_LOGIC_OP);
312 glLogicOp (GL_XOR);
314 glColor3f (gport->grid_color.red / 65535.,
315 gport->grid_color.green / 65535.,
316 gport->grid_color.blue / 65535.);
318 hidgl_draw_grid (gc, drawn_area);
320 glDisable (GL_COLOR_LOGIC_OP);
323 static void
324 ghid_draw_bg_image (void)
326 static GLuint texture_handle = 0;
328 if (!ghidgui->bg_pixbuf)
329 return;
331 if (texture_handle == 0)
333 int width = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
334 int height = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
335 int rowstride = gdk_pixbuf_get_rowstride (ghidgui->bg_pixbuf);
336 int bits_per_sample = gdk_pixbuf_get_bits_per_sample (ghidgui->bg_pixbuf);
337 int n_channels = gdk_pixbuf_get_n_channels (ghidgui->bg_pixbuf);
338 unsigned char *pixels = gdk_pixbuf_get_pixels (ghidgui->bg_pixbuf);
340 g_warn_if_fail (bits_per_sample == 8);
341 g_warn_if_fail (rowstride == width * n_channels);
343 glGenTextures (1, &texture_handle);
344 glBindTexture (GL_TEXTURE_2D, texture_handle);
346 /* XXX: We should proabbly determine what the maxmimum texture supported is,
347 * and if our image is larger, shrink it down using GDK pixbuf routines
348 * rather than having it fail below.
351 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
352 (n_channels == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, pixels);
355 glBindTexture (GL_TEXTURE_2D, texture_handle);
357 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
358 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
359 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
360 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
361 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
362 glEnable (GL_TEXTURE_2D);
364 /* Render a quad with the background as a texture */
366 glBegin (GL_QUADS);
367 glTexCoord2d (0., 0.);
368 glVertex3i (0, 0, 0);
369 glTexCoord2d (1., 0.);
370 glVertex3i (PCB->MaxWidth, 0, 0);
371 glTexCoord2d (1., 1.);
372 glVertex3i (PCB->MaxWidth, PCB->MaxHeight, 0);
373 glTexCoord2d (0., 1.);
374 glVertex3i (0, PCB->MaxHeight, 0);
375 glEnd ();
377 glDisable (GL_TEXTURE_2D);
380 void
381 ghid_use_mask (enum mask_mode mode)
383 render_priv *priv = gport->render_priv;
384 hidgl_instance *hidgl = priv->hidgl;
385 static int stencil_bit = 0;
387 if (mode == cur_mask)
388 return;
390 /* Flush out any existing geoemtry to be rendered */
391 hidgl_flush_triangles (hidgl);
393 switch (mode)
395 case HID_MASK_BEFORE:
396 /* The HID asks not to receive this mask type, so warn if we get it */
397 g_return_if_reached ();
399 case HID_MASK_CLEAR:
400 /* Write '1' to the stencil buffer where the solder-mask should not be drawn. */
401 glColorMask (0, 0, 0, 0); /* Disable writting in color buffer */
402 glEnable (GL_STENCIL_TEST); /* Enable Stencil test */
403 stencil_bit = hidgl_assign_clear_stencil_bit (hidgl); /* Get a new (clean) bitplane to stencil with */
404 glStencilFunc (GL_ALWAYS, stencil_bit, stencil_bit); /* Always pass stencil test, write stencil_bit */
405 glStencilMask (stencil_bit); /* Only write to our subcompositing stencil bitplane */
406 glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); /* Stencil pass => replace stencil value (with 1) */
407 break;
409 case HID_MASK_AFTER:
410 /* Drawing operations as masked to areas where the stencil buffer is '0' */
411 glColorMask (1, 1, 1, 1); /* Enable drawing of r, g, b & a */
412 glStencilFunc (GL_GEQUAL, 0, stencil_bit); /* Draw only where our bit of the stencil buffer is clear */
413 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); /* Stencil buffer read only */
414 break;
416 case HID_MASK_OFF:
417 /* Disable stenciling */
418 hidgl_return_stencil_bit (hidgl, stencil_bit); /* Relinquish any bitplane we previously used */
419 glDisable (GL_STENCIL_TEST); /* Disable Stencil test */
420 break;
422 cur_mask = mode;
426 /* Config helper functions for when the user changes color preferences.
427 | set_special colors used in the gtkhid.
429 static void
430 set_special_grid_color (void)
432 if (!gport->colormap)
433 return;
434 gport->grid_color.red ^= gport->bg_color.red;
435 gport->grid_color.green ^= gport->bg_color.green;
436 gport->grid_color.blue ^= gport->bg_color.blue;
439 void
440 ghid_set_special_colors (HID_Attribute * ha)
442 if (!ha->name || !ha->value)
443 return;
444 if (!strcmp (ha->name, "background-color"))
446 ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
447 set_special_grid_color ();
449 else if (!strcmp (ha->name, "off-limit-color"))
451 ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
453 else if (!strcmp (ha->name, "grid-color"))
455 ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
456 set_special_grid_color ();
460 typedef struct
462 int color_set;
463 GdkColor color;
464 double red;
465 double green;
466 double blue;
467 } ColorCache;
469 static void
470 set_gl_color_for_gc (hidGC gc)
472 gtkGC gtk_gc = (gtkGC)gc;
473 render_priv *priv = gport->render_priv;
474 static void *cache = NULL;
475 hidval cval;
476 ColorCache *cc;
477 double r, g, b, a;
479 if (priv->current_colorname != NULL &&
480 strcmp (priv->current_colorname, gtk_gc->colorname) == 0 &&
481 priv->current_alpha_mult == gtk_gc->alpha_mult)
482 return;
484 free (priv->current_colorname);
485 priv->current_colorname = strdup (gtk_gc->colorname);
486 priv->current_alpha_mult = gtk_gc->alpha_mult;
488 if (gport->colormap == NULL)
489 gport->colormap = gtk_widget_get_colormap (gport->top_window);
490 if (strcmp (gtk_gc->colorname, "erase") == 0)
492 r = gport->bg_color.red / 65535.;
493 g = gport->bg_color.green / 65535.;
494 b = gport->bg_color.blue / 65535.;
495 a = 1.0;
497 else if (strcmp (gtk_gc->colorname, "drill") == 0)
499 r = gport->offlimits_color.red / 65535.;
500 g = gport->offlimits_color.green / 65535.;
501 b = gport->offlimits_color.blue / 65535.;
502 a = 0.85;
504 else
506 if (hid_cache_color (0, gtk_gc->colorname, &cval, &cache))
507 cc = (ColorCache *) cval.ptr;
508 else
510 cc = (ColorCache *) malloc (sizeof (ColorCache));
511 memset (cc, 0, sizeof (*cc));
512 cval.ptr = cc;
513 hid_cache_color (1, gtk_gc->colorname, &cval, &cache);
516 if (!cc->color_set)
518 if (gdk_color_parse (gtk_gc->colorname, &cc->color))
519 gdk_color_alloc (gport->colormap, &cc->color);
520 else
521 gdk_color_white (gport->colormap, &cc->color);
522 cc->red = cc->color.red / 65535.;
523 cc->green = cc->color.green / 65535.;
524 cc->blue = cc->color.blue / 65535.;
525 cc->color_set = 1;
527 r = cc->red;
528 g = cc->green;
529 b = cc->blue;
530 a = 0.7;
532 if (1) {
533 double maxi, mult;
534 a *= gtk_gc->alpha_mult;
535 if (!priv->trans_lines)
536 a = 1.0;
537 maxi = r;
538 if (g > maxi) maxi = g;
539 if (b > maxi) maxi = b;
540 mult = MIN (1 / a, 1 / maxi);
541 #if 1
542 r = r * mult;
543 g = g * mult;
544 b = b * mult;
545 #endif
548 if(!priv->in_context)
549 return;
551 hidgl_flush_triangles (gtk_gc->hidgl_gc.hidgl);
552 glColor4d (r, g, b, a);
555 void
556 ghid_set_color (hidGC gc, const char *name)
558 gtkGC gtk_gc = (gtkGC)gc;
560 gtk_gc->colorname = name;
561 set_gl_color_for_gc (gc);
564 void
565 ghid_set_alpha_mult (hidGC gc, double alpha_mult)
567 gtkGC gtk_gc = (gtkGC)gc;
569 gtk_gc->alpha_mult = alpha_mult;
570 set_gl_color_for_gc (gc);
573 void
574 ghid_set_line_cap (hidGC gc, EndCapStyle style)
576 gtkGC gtk_gc = (gtkGC)gc;
578 gtk_gc->cap = style;
581 void
582 ghid_set_line_width (hidGC gc, Coord width)
584 gtkGC gtk_gc = (gtkGC)gc;
586 gtk_gc->width = width;
590 void
591 ghid_set_draw_xor (hidGC gc, int xor)
593 /* NOT IMPLEMENTED */
595 /* Only presently called when setting up a crosshair GC.
596 * We manage our own drawing model for that anyway. */
599 void
600 ghid_set_draw_faded (hidGC gc, int faded)
602 printf ("ghid_set_draw_faded(%p,%d) -- not implemented\n", gc, faded);
605 void
606 ghid_set_line_cap_angle (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
608 printf ("ghid_set_line_cap_angle() -- not implemented\n");
611 static void
612 ghid_invalidate_current_gc (void)
614 current_gc = NULL;
617 static int
618 use_gc (hidGC gc)
620 if (gc->hid != &ghid_hid)
622 fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
623 abort ();
626 if (current_gc == gc)
627 return 1;
629 current_gc = gc;
631 set_gl_color_for_gc (gc);
632 return 1;
635 void
636 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
638 gtkGC gtk_gc = (gtkGC)gc;
639 USE_GC (gc);
641 hidgl_draw_line (gc, gtk_gc->cap, gtk_gc->width, x1, y1, x2, y2, gport->view.coord_per_px);
644 void
645 ghid_draw_arc (hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius,
646 Angle start_angle, Angle delta_angle)
648 gtkGC gtk_gc = (gtkGC)gc;
649 USE_GC (gc);
651 hidgl_draw_arc (gc, gtk_gc->width, cx, cy, xradius, yradius,
652 start_angle, delta_angle, gport->view.coord_per_px);
655 void
656 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
658 USE_GC (gc);
660 hidgl_draw_rect (gc, x1, y1, x2, y2);
664 void
665 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
667 USE_GC (gc);
669 hidgl_fill_circle (gc, cx, cy, radius, gport->view.coord_per_px);
673 void
674 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
676 USE_GC (gc);
678 hidgl_fill_polygon (gc, n_coords, x, y);
681 void
682 ghid_fill_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
684 USE_GC (gc);
686 hidgl_fill_pcb_polygon (gc, poly, clip_box, gport->view.coord_per_px);
689 void
690 ghid_thindraw_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
692 common_thindraw_pcb_polygon (gc, poly, clip_box);
693 ghid_set_alpha_mult (gc, 0.25);
694 hid_draw_fill_pcb_polygon (gc, poly, clip_box);
695 ghid_set_alpha_mult (gc, 1.0);
698 void
699 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
701 USE_GC (gc);
703 hidgl_fill_rect (gc, x1, y1, x2, y2);
706 void
707 ghid_invalidate_lr (int left, int right, int top, int bottom)
709 ghid_invalidate_all ();
712 #define MAX_ELAPSED (50. / 1000.) /* 50ms */
713 void
714 ghid_invalidate_all ()
716 render_priv *priv = gport->render_priv;
717 double elapsed = g_timer_elapsed (priv->time_since_expose, NULL);
719 ghid_draw_area_update (gport, NULL);
721 if (elapsed > MAX_ELAPSED)
722 gdk_window_process_all_updates ();
725 void
726 ghid_notify_crosshair_change (bool changes_complete)
728 /* We sometimes get called before the GUI is up */
729 if (gport->drawing_area == NULL)
730 return;
732 /* FIXME: We could just invalidate the bounds of the crosshair attached objects? */
733 ghid_invalidate_all ();
736 void
737 ghid_notify_mark_change (bool changes_complete)
739 /* We sometimes get called before the GUI is up */
740 if (gport->drawing_area == NULL)
741 return;
743 /* FIXME: We could just invalidate the bounds of the mark? */
744 ghid_invalidate_all ();
747 static void
748 draw_right_cross (gint x, gint y, gint z)
750 glVertex3i (x, 0, z);
751 glVertex3i (x, PCB->MaxHeight, z);
752 glVertex3i (0, y, z);
753 glVertex3i (PCB->MaxWidth, y, z);
756 static void
757 draw_slanted_cross (gint x, gint y, gint z)
759 gint x0, y0, x1, y1;
761 x0 = x + (PCB->MaxHeight - y);
762 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
763 x1 = x - y;
764 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
765 y0 = y + (PCB->MaxWidth - x);
766 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
767 y1 = y - x;
768 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
769 glVertex3i (x0, y0, z);
770 glVertex3i (x1, y1, z);
772 x0 = x - (PCB->MaxHeight - y);
773 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
774 x1 = x + y;
775 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
776 y0 = y + x;
777 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
778 y1 = y - (PCB->MaxWidth - x);
779 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
780 glVertex3i (x0, y0, z);
781 glVertex3i (x1, y1, z);
784 static void
785 draw_dozen_cross (gint x, gint y, gint z)
787 gint x0, y0, x1, y1;
788 gdouble tan60 = sqrt (3);
790 x0 = x + (PCB->MaxHeight - y) / tan60;
791 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
792 x1 = x - y / tan60;
793 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
794 y0 = y + (PCB->MaxWidth - x) * tan60;
795 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
796 y1 = y - x * tan60;
797 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
798 glVertex3i (x0, y0, z);
799 glVertex3i (x1, y1, z);
801 x0 = x + (PCB->MaxHeight - y) * tan60;
802 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
803 x1 = x - y * tan60;
804 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
805 y0 = y + (PCB->MaxWidth - x) / tan60;
806 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
807 y1 = y - x / tan60;
808 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
809 glVertex3i (x0, y0, z);
810 glVertex3i (x1, y1, z);
812 x0 = x - (PCB->MaxHeight - y) / tan60;
813 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
814 x1 = x + y / tan60;
815 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
816 y0 = y + x * tan60;
817 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
818 y1 = y - (PCB->MaxWidth - x) * tan60;
819 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
820 glVertex3i (x0, y0, z);
821 glVertex3i (x1, y1, z);
823 x0 = x - (PCB->MaxHeight - y) * tan60;
824 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
825 x1 = x + y * tan60;
826 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
827 y0 = y + x / tan60;
828 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
829 y1 = y - (PCB->MaxWidth - x) / tan60;
830 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
831 glVertex3i (x0, y0, z);
832 glVertex3i (x1, y1, z);
835 static void
836 draw_crosshair (render_priv *priv)
838 gint x, y, z;
839 static int done_once = 0;
840 static GdkColor cross_color;
842 if (!done_once)
844 done_once = 1;
845 /* FIXME: when CrossColor changed from config */
846 ghid_map_color_string (Settings.CrossColor, &cross_color);
849 x = gport->crosshair_x;
850 y = gport->crosshair_y;
851 z = global_depth;
853 glEnable (GL_COLOR_LOGIC_OP);
854 glLogicOp (GL_XOR);
856 glColor3f (cross_color.red / 65535.,
857 cross_color.green / 65535.,
858 cross_color.blue / 65535.);
860 glBegin (GL_LINES);
862 draw_right_cross (x, y, z);
863 if (Crosshair.shape == Union_Jack_Crosshair_Shape)
864 draw_slanted_cross (x, y, z);
865 if (Crosshair.shape == Dozen_Crosshair_Shape)
866 draw_dozen_cross (x, y, z);
868 glEnd ();
870 glDisable (GL_COLOR_LOGIC_OP);
873 void
874 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
876 render_priv *priv;
878 port->render_priv = priv = g_new0 (render_priv, 1);
880 priv->time_since_expose = g_timer_new ();
882 gtk_gl_init(argc, argv);
884 /* setup GL-context */
885 priv->glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGBA |
886 GDK_GL_MODE_STENCIL |
887 GDK_GL_MODE_DOUBLE);
888 if (!priv->glconfig)
890 printf ("Could not setup GL-context!\n");
891 return; /* Should we abort? */
894 hidgl_init ();
895 priv->hidgl = hidgl_new_instance ();
896 port->render_priv->crosshair_gc = hid_draw_make_gc (&ghid_graphics);
898 /* Setup HID function pointers specific to the GL renderer*/
899 ghid_graphics_class.end_layer = ghid_end_layer;
900 ghid_graphics_class.fill_pcb_polygon = ghid_fill_pcb_polygon;
901 ghid_graphics_class.thindraw_pcb_polygon = ghid_thindraw_pcb_polygon;
904 void
905 ghid_shutdown_renderer (GHidPort *port)
907 render_priv *priv = port->render_priv;
909 hid_draw_destroy_gc (priv->crosshair_gc);
910 hidgl_free_instance (priv->hidgl);
912 ghid_cancel_lead_user ();
913 g_free (port->render_priv);
914 port->render_priv = NULL;
917 void
918 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
920 render_priv *priv = port->render_priv;
922 gtk_widget_set_gl_capability (widget,
923 priv->glconfig,
924 NULL,
925 TRUE,
926 GDK_GL_RGBA_TYPE);
929 void
930 ghid_drawing_area_configure_hook (GHidPort *port)
934 gboolean
935 ghid_start_drawing (GHidPort *port, GtkWidget *widget)
937 GdkGLContext *pGlContext = gtk_widget_get_gl_context (widget);
938 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
940 /* make GL-context "current" */
941 if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext))
942 return FALSE;
944 port->render_priv->in_context = true;
946 hidgl_start_render (port->render_priv->hidgl);
948 return TRUE;
951 void
952 ghid_end_drawing (GHidPort *port, GtkWidget *widget)
954 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
956 hidgl_finish_render (port->render_priv->hidgl);
958 if (gdk_gl_drawable_is_double_buffered (pGlDrawable))
959 gdk_gl_drawable_swap_buffers (pGlDrawable);
960 else
961 glFlush ();
963 port->render_priv->in_context = false;
965 /* end drawing to current GL-context */
966 gdk_gl_drawable_gl_end (pGlDrawable);
969 void
970 ghid_screen_update (void)
974 #define Z_NEAR 3.0
975 gboolean
976 ghid_drawing_area_expose_cb (GtkWidget *widget,
977 GdkEventExpose *ev,
978 GHidPort *port)
980 render_priv *priv = port->render_priv;
981 GtkAllocation allocation;
982 BoxType region;
983 Coord min_x, min_y;
984 Coord max_x, max_y;
985 Coord new_x, new_y;
986 Coord min_depth;
987 Coord max_depth;
989 gtk_widget_get_allocation (widget, &allocation);
991 ghid_start_drawing (port, widget);
993 /* If we don't have any stencil bits available,
994 we can't use the hidgl polygon drawing routine */
995 /* TODO: We could use the GLU tessellator though */
996 if (hidgl_stencil_bits (priv->hidgl) == 0)
997 ghid_graphics_class.fill_pcb_polygon = common_fill_pcb_polygon;
999 glEnable (GL_BLEND);
1000 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1002 glViewport (0, 0, allocation.width, allocation.height);
1004 glEnable (GL_SCISSOR_TEST);
1005 glScissor (ev->area.x,
1006 allocation.height - ev->area.height - ev->area.y,
1007 ev->area.width, ev->area.height);
1009 glMatrixMode (GL_PROJECTION);
1010 glLoadIdentity ();
1011 glOrtho (0, allocation.width, allocation.height, 0, -100000, 100000);
1012 glMatrixMode (GL_MODELVIEW);
1013 glLoadIdentity ();
1014 glTranslatef (widget->allocation.width / 2., widget->allocation.height / 2., 0);
1015 glMultMatrixf ((GLfloat *)view_matrix);
1016 glTranslatef (-widget->allocation.width / 2., -widget->allocation.height / 2., 0);
1017 glScalef ((port->view.flip_x ? -1. : 1.) / port->view.coord_per_px,
1018 (port->view.flip_y ? -1. : 1.) / port->view.coord_per_px,
1019 ((port->view.flip_x == port->view.flip_y) ? 1. : -1.) / port->view.coord_per_px);
1020 glTranslatef (port->view.flip_x ? port->view.x0 - PCB->MaxWidth :
1021 -port->view.x0,
1022 port->view.flip_y ? port->view.y0 - PCB->MaxHeight :
1023 -port->view.y0, 0);
1024 glGetFloatv (GL_MODELVIEW_MATRIX, (GLfloat *)last_modelview_matrix);
1026 glEnable (GL_STENCIL_TEST);
1027 glClearColor (port->offlimits_color.red / 65535.,
1028 port->offlimits_color.green / 65535.,
1029 port->offlimits_color.blue / 65535.,
1030 1.);
1031 glStencilMask (~0);
1032 glClearStencil (0);
1033 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1034 hidgl_reset_stencil_usage (priv->hidgl);
1036 /* Disable the stencil test until we need it - otherwise it gets dirty */
1037 glDisable (GL_STENCIL_TEST);
1038 glStencilMask (0);
1039 glStencilFunc (GL_ALWAYS, 0, 0);
1041 /* Test the 8 corners of a cube spanning the event */
1042 min_depth = -50; /* FIXME */
1043 max_depth = 0; /* FIXME */
1045 ghid_unproject_to_z_plane (ev->area.x,
1046 ev->area.y,
1047 min_depth, &new_x, &new_y);
1048 max_x = min_x = new_x;
1049 max_y = min_y = new_y;
1051 ghid_unproject_to_z_plane (ev->area.x,
1052 ev->area.y,
1053 max_depth, &new_x, &new_y);
1054 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1055 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1057 /* */
1058 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
1059 ev->area.y,
1060 min_depth, &new_x, &new_y);
1061 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1062 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1064 ghid_unproject_to_z_plane (ev->area.x + ev->area.width, ev->area.y,
1065 max_depth, &new_x, &new_y);
1066 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1067 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1069 /* */
1070 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
1071 ev->area.y + ev->area.height,
1072 min_depth, &new_x, &new_y);
1073 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1074 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1076 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
1077 ev->area.y + ev->area.height,
1078 max_depth, &new_x, &new_y);
1079 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1080 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1082 /* */
1083 ghid_unproject_to_z_plane (ev->area.x,
1084 ev->area.y + ev->area.height,
1085 min_depth,
1086 &new_x, &new_y);
1087 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1088 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1090 ghid_unproject_to_z_plane (ev->area.x,
1091 ev->area.y + ev->area.height,
1092 max_depth,
1093 &new_x, &new_y);
1094 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1095 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1097 region.X1 = min_x; region.X2 = max_x + 1;
1098 region.Y1 = min_y; region.Y2 = max_y + 1;
1100 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1101 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1102 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1103 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1105 glColor3f (port->bg_color.red / 65535.,
1106 port->bg_color.green / 65535.,
1107 port->bg_color.blue / 65535.);
1109 glBegin (GL_QUADS);
1110 glVertex3i (0, 0, -50);
1111 glVertex3i (PCB->MaxWidth, 0, -50);
1112 glVertex3i (PCB->MaxWidth, PCB->MaxHeight, -50);
1113 glVertex3i (0, PCB->MaxHeight, -50);
1114 glEnd ();
1116 ghid_draw_bg_image ();
1118 ghid_invalidate_current_gc ();
1119 hid_expose_callback (&ghid_graphics, &region, 0);
1120 hidgl_flush_triangles (priv->hidgl);
1122 /* Set the current depth to the right value for the layer we are editing */
1123 hidgl_set_depth (compute_depth (GetLayerGroupNumberByNumber (INDEXOFCURRENT)));
1125 ghid_draw_grid (priv->crosshair_gc, &region);
1127 ghid_invalidate_current_gc ();
1129 DrawAttached (priv->crosshair_gc);
1130 DrawMark (priv->crosshair_gc);
1131 hidgl_flush_triangles (priv->hidgl);
1133 draw_crosshair (priv);
1134 hidgl_flush_triangles (priv->hidgl);
1136 draw_lead_user (priv->crosshair_gc, priv);
1138 ghid_end_drawing (port, widget);
1140 g_timer_start (priv->time_since_expose);
1142 return FALSE;
1145 /* This realize callback is used to work around a crash bug in some mesa
1146 * versions (observed on a machine running the intel i965 driver. It isn't
1147 * obvious why it helps, but somehow fiddling with the GL context here solves
1148 * the issue. The problem appears to have been fixed in recent mesa versions.
1150 void
1151 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
1153 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
1154 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
1156 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
1157 return;
1159 gdk_gl_drawable_gl_end (gldrawable);
1160 return;
1163 gboolean
1164 ghid_pinout_preview_expose (GtkWidget *widget,
1165 GdkEventExpose *ev)
1167 GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
1168 render_priv *priv = gport->render_priv;
1169 GtkAllocation allocation;
1170 view_data save_view;
1171 int save_width, save_height;
1172 Coord save_max_width;
1173 Coord save_max_height;
1174 double xz, yz;
1176 save_view = gport->view;
1177 save_width = gport->width;
1178 save_height = gport->height;
1179 save_max_width = PCB->MaxWidth;
1180 save_max_height = PCB->MaxHeight;
1182 /* Setup zoom factor for drawing routines */
1184 gtk_widget_get_allocation (widget, &allocation);
1185 xz = (double) pinout->x_max / allocation.width;
1186 yz = (double) pinout->y_max / allocation.height;
1187 if (xz > yz)
1188 gport->view.coord_per_px = xz;
1189 else
1190 gport->view.coord_per_px = yz;
1192 gport->width = allocation.width;
1193 gport->height = allocation.height;
1194 gport->view.width = allocation.width * gport->view.coord_per_px;
1195 gport->view.height = allocation.height * gport->view.coord_per_px;
1196 gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
1197 gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
1198 PCB->MaxWidth = pinout->x_max;
1199 PCB->MaxHeight = pinout->y_max;
1201 ghid_start_drawing (gport, widget);
1203 #if 0 /* We disable alpha blending here, as hid_expose_callback() does not
1204 * call set_layer() as appropriate for us to sub-composite rendering
1205 * from each layer when drawing a single element. If we leave alpha-
1206 * blending on, it means text and overlapping pads are rendered ugly.
1209 glEnable (GL_BLEND);
1210 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1211 #endif
1213 glViewport (0, 0, allocation.width, allocation.height);
1215 #if 0 /* We disable the scissor test here, as it is interacting badly with
1216 * being handed expose events which don't cover the whole window.
1217 * As we have a double-buffered GL window, we end up with unintialised
1218 * contents remaining in the unpainted areas (outside the scissor
1219 * region), and these are being flipped onto the screen.
1221 * The debugging code below shows multiple expose events when the
1222 * window is shown the first time, some of which are very small.
1224 * XXX: There is clearly a perforamnce issue here, in that we may
1225 * be rendering the preview more times, and over a larger area
1226 * than is really required.
1229 glEnable (GL_SCISSOR_TEST);
1230 glScissor (ev->area.x,
1231 allocation.height - ev->area.height - ev->area.y,
1232 ev->area.width, ev->area.height);
1233 #endif
1235 #ifdef DEBUG
1236 printf ("EVT: %i, %i, w=%i, h=%i, Scissor setup: glScissor (%f, %f, %f, %f);\n",
1237 ev->area.x, ev->area.y, ev->area.width, ev->area.height,
1238 (double)ev->area.x,
1239 (double)(allocation.height - ev->area.height - ev->area.y),
1240 (double)ev->area.width,
1241 (double)ev->area.height);
1242 #endif
1244 glMatrixMode (GL_PROJECTION);
1245 glLoadIdentity ();
1246 glOrtho (0, allocation.width, allocation.height, 0, -100000, 100000);
1247 glMatrixMode (GL_MODELVIEW);
1248 glLoadIdentity ();
1249 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1251 glClearColor (gport->bg_color.red / 65535.,
1252 gport->bg_color.green / 65535.,
1253 gport->bg_color.blue / 65535.,
1254 1.);
1255 glStencilMask (~0);
1256 glClearStencil (0);
1257 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1258 hidgl_reset_stencil_usage (priv->hidgl);
1260 /* Disable the stencil test until we need it - otherwise it gets dirty */
1261 glDisable (GL_STENCIL_TEST);
1262 glStencilMask (0);
1263 glStencilFunc (GL_ALWAYS, 0, 0);
1265 /* call the drawing routine */
1266 ghid_invalidate_current_gc ();
1267 glPushMatrix ();
1268 glScalef ((gport->view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
1269 (gport->view.flip_y ? -1. : 1.) / gport->view.coord_per_px,
1270 ((gport->view.flip_x == gport->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1271 glTranslatef (gport->view.flip_x ? gport->view.x0 - PCB->MaxWidth :
1272 -gport->view.x0,
1273 gport->view.flip_y ? gport->view.y0 - PCB->MaxHeight :
1274 -gport->view.y0, 0);
1276 hidgl_set_depth (0.);
1277 hid_expose_callback (&ghid_graphics, NULL, pinout->element);
1278 hidgl_flush_triangles (priv->hidgl);
1279 glPopMatrix ();
1281 ghid_end_drawing (gport, widget);
1283 gport->view = save_view;
1284 gport->width = save_width;
1285 gport->height = save_height;
1286 PCB->MaxWidth = save_max_width;
1287 PCB->MaxHeight = save_max_height;
1289 return FALSE;
1293 GdkPixmap *
1294 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
1296 render_priv *priv = gport->render_priv;
1297 GdkGLConfig *glconfig;
1298 GdkPixmap *pixmap;
1299 GdkGLPixmap *glpixmap;
1300 GdkGLContext* glcontext;
1301 GdkGLDrawable* gldrawable;
1302 view_data save_view;
1303 int save_width, save_height;
1304 BoxType region;
1306 save_view = gport->view;
1307 save_width = gport->width;
1308 save_height = gport->height;
1310 /* Setup rendering context for drawing routines
1313 glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
1314 GDK_GL_MODE_STENCIL |
1315 GDK_GL_MODE_SINGLE);
1317 pixmap = gdk_pixmap_new (NULL, width, height, depth);
1318 glpixmap = gdk_pixmap_set_gl_capability (pixmap, glconfig, NULL);
1319 gldrawable = GDK_GL_DRAWABLE (glpixmap);
1320 glcontext = gdk_gl_context_new (gldrawable, NULL, TRUE, GDK_GL_RGBA_TYPE);
1322 /* Setup zoom factor for drawing routines */
1324 gport->view.coord_per_px = zoom;
1325 gport->width = width;
1326 gport->height = height;
1327 gport->view.width = width * gport->view.coord_per_px;
1328 gport->view.height = height * gport->view.coord_per_px;
1329 gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
1330 gport->view.x0 -= gport->view.height / 2;
1331 gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
1332 gport->view.y0 -= gport->view.width / 2;
1334 /* make GL-context "current" */
1335 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) {
1336 return NULL;
1338 hidgl_start_render (priv->hidgl);
1339 gport->render_priv->in_context = true;
1341 glEnable (GL_BLEND);
1342 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1344 glViewport (0, 0, width, height);
1346 glEnable (GL_SCISSOR_TEST);
1347 glScissor (0, 0, width, height);
1349 glMatrixMode (GL_PROJECTION);
1350 glLoadIdentity ();
1351 glOrtho (0, width, height, 0, -100000, 100000);
1352 glMatrixMode (GL_MODELVIEW);
1353 glLoadIdentity ();
1354 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1356 glClearColor (gport->bg_color.red / 65535.,
1357 gport->bg_color.green / 65535.,
1358 gport->bg_color.blue / 65535.,
1359 1.);
1360 glStencilMask (~0);
1361 glClearStencil (0);
1362 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1363 hidgl_reset_stencil_usage (priv->hidgl);
1365 /* Disable the stencil test until we need it - otherwise it gets dirty */
1366 glDisable (GL_STENCIL_TEST);
1367 glStencilMask (0);
1368 glStencilFunc (GL_ALWAYS, 0, 0);
1370 /* call the drawing routine */
1371 ghid_invalidate_current_gc ();
1372 glPushMatrix ();
1373 glScalef ((gport->view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
1374 (gport->view.flip_y ? -1. : 1.) / gport->view.coord_per_px,
1375 ((gport->view.flip_x == gport->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1376 glTranslatef (gport->view.flip_x ? gport->view.x0 - PCB->MaxWidth :
1377 -gport->view.x0,
1378 gport->view.flip_y ? gport->view.y0 - PCB->MaxHeight :
1379 -gport->view.y0, 0);
1381 region.X1 = MIN(Px(0), Px(gport->width + 1));
1382 region.Y1 = MIN(Py(0), Py(gport->height + 1));
1383 region.X2 = MAX(Px(0), Px(gport->width + 1));
1384 region.Y2 = MAX(Py(0), Py(gport->height + 1));
1386 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1387 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1388 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1389 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1391 hid_expose_callback (&ghid_graphics, &region, NULL);
1392 hidgl_flush_triangles (priv->hidgl);
1393 glPopMatrix ();
1395 glFlush ();
1397 hidgl_finish_render (priv->hidgl);
1399 /* end drawing to current GL-context */
1400 gport->render_priv->in_context = false;
1401 gdk_gl_drawable_gl_end (gldrawable);
1403 gdk_pixmap_unset_gl_capability (pixmap);
1405 g_object_unref (glconfig);
1406 g_object_unref (glcontext);
1408 gport->view = save_view;
1409 gport->width = save_width;
1410 gport->height = save_height;
1412 return pixmap;
1415 HID_DRAW *
1416 ghid_request_debug_draw (void)
1418 GHidPort *port = gport;
1419 GtkWidget *widget = port->drawing_area;
1420 GtkAllocation allocation;
1422 gtk_widget_get_allocation (widget, &allocation);
1424 ghid_start_drawing (port, widget);
1426 glViewport (0, 0, allocation.width, allocation.height);
1428 glMatrixMode (GL_PROJECTION);
1429 glLoadIdentity ();
1430 glOrtho (0, allocation.width, allocation.height, 0, 0, 100);
1431 glMatrixMode (GL_MODELVIEW);
1432 glLoadIdentity ();
1433 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1435 ghid_invalidate_current_gc ();
1437 /* Setup stenciling */
1438 glDisable (GL_STENCIL_TEST);
1440 glPushMatrix ();
1441 glScalef ((port->view.flip_x ? -1. : 1.) / port->view.coord_per_px,
1442 (port->view.flip_y ? -1. : 1.) / port->view.coord_per_px,
1443 ((gport->view.flip_x == port->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1444 glTranslatef (port->view.flip_x ? port->view.x0 - PCB->MaxWidth :
1445 -port->view.x0,
1446 port->view.flip_y ? port->view.y0 - PCB->MaxHeight :
1447 -port->view.y0, 0);
1449 return &ghid_graphics;
1452 void
1453 ghid_flush_debug_draw (void)
1455 render_priv *priv = gport->render_priv;
1456 GtkWidget *widget = gport->drawing_area;
1457 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
1459 hidgl_flush_triangles (priv->hidgl);
1461 if (gdk_gl_drawable_is_double_buffered (pGlDrawable))
1462 gdk_gl_drawable_swap_buffers (pGlDrawable);
1463 else
1464 glFlush ();
1467 void
1468 ghid_finish_debug_draw (void)
1470 render_priv *priv = gport->render_priv;
1472 hidgl_flush_triangles (priv->hidgl);
1473 glPopMatrix ();
1475 ghid_end_drawing (gport, gport->drawing_area);
1478 static float
1479 determinant_2x2 (float m[2][2])
1481 float det;
1482 det = m[0][0] * m[1][1] -
1483 m[0][1] * m[1][0];
1484 return det;
1487 #if 0
1488 static float
1489 determinant_4x4 (float m[4][4])
1491 float det;
1492 det = m[0][3] * m[1][2] * m[2][1] * m[3][0]-m[0][2] * m[1][3] * m[2][1] * m[3][0] -
1493 m[0][3] * m[1][1] * m[2][2] * m[3][0]+m[0][1] * m[1][3] * m[2][2] * m[3][0] +
1494 m[0][2] * m[1][1] * m[2][3] * m[3][0]-m[0][1] * m[1][2] * m[2][3] * m[3][0] -
1495 m[0][3] * m[1][2] * m[2][0] * m[3][1]+m[0][2] * m[1][3] * m[2][0] * m[3][1] +
1496 m[0][3] * m[1][0] * m[2][2] * m[3][1]-m[0][0] * m[1][3] * m[2][2] * m[3][1] -
1497 m[0][2] * m[1][0] * m[2][3] * m[3][1]+m[0][0] * m[1][2] * m[2][3] * m[3][1] +
1498 m[0][3] * m[1][1] * m[2][0] * m[3][2]-m[0][1] * m[1][3] * m[2][0] * m[3][2] -
1499 m[0][3] * m[1][0] * m[2][1] * m[3][2]+m[0][0] * m[1][3] * m[2][1] * m[3][2] +
1500 m[0][1] * m[1][0] * m[2][3] * m[3][2]-m[0][0] * m[1][1] * m[2][3] * m[3][2] -
1501 m[0][2] * m[1][1] * m[2][0] * m[3][3]+m[0][1] * m[1][2] * m[2][0] * m[3][3] +
1502 m[0][2] * m[1][0] * m[2][1] * m[3][3]-m[0][0] * m[1][2] * m[2][1] * m[3][3] -
1503 m[0][1] * m[1][0] * m[2][2] * m[3][3]+m[0][0] * m[1][1] * m[2][2] * m[3][3];
1504 return det;
1506 #endif
1508 static void
1509 invert_2x2 (float m[2][2], float out[2][2])
1511 float scale = 1 / determinant_2x2 (m);
1512 out[0][0] = m[1][1] * scale;
1513 out[0][1] = -m[0][1] * scale;
1514 out[1][0] = -m[1][0] * scale;
1515 out[1][1] = m[0][0] * scale;
1518 #if 0
1519 static void
1520 invert_4x4 (float m[4][4], float out[4][4])
1522 float scale = 1 / determinant_4x4 (m);
1524 out[0][0] = (m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] +
1525 m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] -
1526 m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * scale;
1527 out[0][1] = (m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] -
1528 m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] +
1529 m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * scale;
1530 out[0][2] = (m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] +
1531 m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] -
1532 m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * scale;
1533 out[0][3] = (m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] -
1534 m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] +
1535 m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * scale;
1536 out[1][0] = (m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] -
1537 m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] +
1538 m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * scale;
1539 out[1][1] = (m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] +
1540 m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] -
1541 m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * scale;
1542 out[1][2] = (m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] -
1543 m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] +
1544 m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * scale;
1545 out[1][3] = (m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] +
1546 m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] -
1547 m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * scale;
1548 out[2][0] = (m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] +
1549 m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] -
1550 m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * scale;
1551 out[2][1] = (m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] -
1552 m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] +
1553 m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * scale;
1554 out[2][2] = (m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] +
1555 m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] -
1556 m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * scale;
1557 out[2][3] = (m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] -
1558 m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] +
1559 m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * scale;
1560 out[3][0] = (m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] -
1561 m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] +
1562 m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * scale;
1563 out[3][1] = (m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] +
1564 m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] -
1565 m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * scale;
1566 out[3][2] = (m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] -
1567 m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] +
1568 m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * scale;
1569 out[3][3] = (m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] +
1570 m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] -
1571 m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * scale;
1573 #endif
1576 static void
1577 ghid_unproject_to_z_plane (int ex, int ey, Coord pcb_z, Coord *pcb_x, Coord *pcb_y)
1579 float mat[2][2];
1580 float inv_mat[2][2];
1581 float x, y;
1584 ex = view_matrix[0][0] * vx +
1585 view_matrix[0][1] * vy +
1586 view_matrix[0][2] * vz +
1587 view_matrix[0][3] * 1;
1588 ey = view_matrix[1][0] * vx +
1589 view_matrix[1][1] * vy +
1590 view_matrix[1][2] * vz +
1591 view_matrix[1][3] * 1;
1592 UNKNOWN ez = view_matrix[2][0] * vx +
1593 view_matrix[2][1] * vy +
1594 view_matrix[2][2] * vz +
1595 view_matrix[2][3] * 1;
1597 ex - view_matrix[0][3] * 1
1598 - view_matrix[0][2] * vz
1599 = view_matrix[0][0] * vx +
1600 view_matrix[0][1] * vy;
1602 ey - view_matrix[1][3] * 1
1603 - view_matrix[1][2] * vz
1604 = view_matrix[1][0] * vx +
1605 view_matrix[1][1] * vy;
1608 /* NB: last_modelview_matrix is transposed in memory! */
1609 x = (float)ex - last_modelview_matrix[3][0] * 1
1610 - last_modelview_matrix[2][0] * pcb_z;
1612 y = (float)ey - last_modelview_matrix[3][1] * 1
1613 - last_modelview_matrix[2][1] * pcb_z;
1616 x = view_matrix[0][0] * vx +
1617 view_matrix[0][1] * vy;
1619 y = view_matrix[1][0] * vx +
1620 view_matrix[1][1] * vy;
1622 [view_matrix[0][0] view_matrix[0][1]] [vx] = [x]
1623 [view_matrix[1][0] view_matrix[1][1]] [vy] [y]
1626 mat[0][0] = last_modelview_matrix[0][0];
1627 mat[0][1] = last_modelview_matrix[1][0];
1628 mat[1][0] = last_modelview_matrix[0][1];
1629 mat[1][1] = last_modelview_matrix[1][1];
1631 /* if (determinant_2x2 (mat) < 0.00001) */
1632 /* printf ("Determinant is quite small\n"); */
1634 invert_2x2 (mat, inv_mat);
1636 *pcb_x = (int)(inv_mat[0][0] * x + inv_mat[0][1] * y);
1637 *pcb_y = (int)(inv_mat[1][0] * x + inv_mat[1][1] * y);
1641 bool
1642 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
1644 ghid_unproject_to_z_plane (event_x, event_y, global_depth, pcb_x, pcb_y);
1646 return true;
1649 bool
1650 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
1652 /* NB: last_modelview_matrix is transposed in memory */
1653 float w = last_modelview_matrix[0][3] * (float)pcb_x +
1654 last_modelview_matrix[1][3] * (float)pcb_y +
1655 last_modelview_matrix[2][3] * 0. +
1656 last_modelview_matrix[3][3] * 1.;
1658 *event_x = (last_modelview_matrix[0][0] * (float)pcb_x +
1659 last_modelview_matrix[1][0] * (float)pcb_y +
1660 last_modelview_matrix[2][0] * global_depth +
1661 last_modelview_matrix[3][0] * 1.) / w;
1662 *event_y = (last_modelview_matrix[0][1] * (float)pcb_x +
1663 last_modelview_matrix[1][1] * (float)pcb_y +
1664 last_modelview_matrix[2][1] * global_depth +
1665 last_modelview_matrix[3][1] * 1.) / w;
1667 return true;
1670 void
1671 ghid_view_2d (void *ball, gboolean view_2d, gpointer userdata)
1673 global_view_2d = view_2d;
1674 ghid_invalidate_all ();
1677 void
1678 ghid_port_rotate (void *ball, float *quarternion, gpointer userdata)
1680 #ifdef DEBUG_ROTATE
1681 int row, column;
1682 #endif
1684 build_rotmatrix (view_matrix, quarternion);
1686 #ifdef DEBUG_ROTATE
1687 for (row = 0; row < 4; row++) {
1688 printf ("[ %f", view_matrix[row][0]);
1689 for (column = 1; column < 4; column++) {
1690 printf (",\t%f", view_matrix[row][column]);
1692 printf ("\t]\n");
1694 printf ("\n");
1695 #endif
1697 ghid_invalidate_all ();
1701 #define LEAD_USER_WIDTH 0.2 /* millimeters */
1702 #define LEAD_USER_PERIOD (1000 / 20) /* 20fps (in ms) */
1703 #define LEAD_USER_VELOCITY 3. /* millimeters per second */
1704 #define LEAD_USER_ARC_COUNT 3
1705 #define LEAD_USER_ARC_SEPARATION 3. /* millimeters */
1706 #define LEAD_USER_INITIAL_RADIUS 10. /* millimetres */
1707 #define LEAD_USER_COLOR_R 1.
1708 #define LEAD_USER_COLOR_G 1.
1709 #define LEAD_USER_COLOR_B 0.
1711 static void
1712 draw_lead_user (hidGC gc, render_priv *priv)
1714 gtkGC gtk_gc = (gtkGC)gc;
1715 int i;
1716 double radius = priv->lead_user_radius;
1717 double width = MM_TO_COORD (LEAD_USER_WIDTH);
1718 double separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
1720 if (!priv->lead_user)
1721 return;
1723 glPushAttrib (GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT);
1724 glEnable (GL_COLOR_LOGIC_OP);
1725 glLogicOp (GL_XOR);
1726 glColor3f (LEAD_USER_COLOR_R, LEAD_USER_COLOR_G,LEAD_USER_COLOR_B);
1729 /* arcs at the approrpriate radii */
1731 for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
1733 if (radius < width)
1734 radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1736 /* Draw an arc at radius */
1737 hidgl_draw_arc (gc, width, priv->lead_user_x, priv->lead_user_y,
1738 radius, radius, 0, 360, gport->view.coord_per_px);
1741 hidgl_flush_triangles (gtk_gc->hidgl_gc.hidgl);
1742 glPopAttrib ();
1745 gboolean
1746 lead_user_cb (gpointer data)
1748 render_priv *priv = data;
1749 Coord step;
1750 double elapsed_time;
1752 /* Queue a redraw */
1753 ghid_invalidate_all ();
1755 /* Update radius */
1756 elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
1757 g_timer_start (priv->lead_user_timer);
1759 step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
1760 if (priv->lead_user_radius > step)
1761 priv->lead_user_radius -= step;
1762 else
1763 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1765 return TRUE;
1768 void
1769 ghid_lead_user_to_location (Coord x, Coord y)
1771 render_priv *priv = gport->render_priv;
1773 ghid_cancel_lead_user ();
1775 priv->lead_user = true;
1776 priv->lead_user_x = x;
1777 priv->lead_user_y = y;
1778 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1779 priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
1780 priv->lead_user_timer = g_timer_new ();
1783 void
1784 ghid_cancel_lead_user (void)
1786 render_priv *priv = gport->render_priv;
1788 if (priv->lead_user_timeout)
1789 g_source_remove (priv->lead_user_timeout);
1791 if (priv->lead_user_timer)
1792 g_timer_destroy (priv->lead_user_timer);
1794 if (priv->lead_user)
1795 ghid_invalidate_all ();
1797 priv->lead_user_timeout = 0;
1798 priv->lead_user_timer = NULL;
1799 priv->lead_user = false;