From c4962116760783afa572f01ee532addecb359e98 Mon Sep 17 00:00:00 2001 From: Elliot Lee Date: Wed, 31 May 2000 21:50:38 +0000 Subject: [PATCH] Put it into CVS. Put it into CVS. --- gdk/linux-fb/.cvsignore | 8 + gdk/linux-fb/Makefile.am | 61 + gdk/linux-fb/TODO | 3 + gdk/linux-fb/gdkcolor-fb.c | 759 ++++++ gdk/linux-fb/gdkcursor-fb.c | 81 + gdk/linux-fb/gdkdnd-fb.c | 264 +++ gdk/linux-fb/gdkdrawable-fb2.c | 959 ++++++++ gdk/linux-fb/gdkevents-fb.c | 209 ++ gdk/linux-fb/gdkfb.h | 18 + gdk/linux-fb/gdkfont-fb.c | 328 +++ gdk/linux-fb/gdkgc-fb.c | 308 +++ gdk/linux-fb/gdkgeometry-fb.c | 0 gdk/linux-fb/gdkglobals-fb.c | 41 + gdk/linux-fb/gdkim-fb.c | 242 ++ gdk/linux-fb/gdkimage-fb.c | 268 +++ gdk/linux-fb/gdkinput-none.c | 79 + gdk/linux-fb/gdkinput-ps2.c | 1096 +++++++++ gdk/linux-fb/gdkinput.c | 290 +++ gdk/linux-fb/gdkinputprivate.h | 222 ++ gdk/linux-fb/gdkmain-fb.c | 575 +++++ gdk/linux-fb/gdkpixmap-fb.c | 768 +++++++ gdk/linux-fb/gdkpoly-generic.h | 291 +++ gdk/linux-fb/gdkpolyreg-generic.c | 616 +++++ gdk/linux-fb/gdkprivate-fb.h | 194 ++ gdk/linux-fb/gdkproperty-fb.c | 204 ++ gdk/linux-fb/gdkregion-generic.c | 1505 ++++++++++++ gdk/linux-fb/gdkregion-generic.h | 162 ++ gdk/linux-fb/gdkselection-fb.c | 104 + gdk/linux-fb/gdkvisual-fb.c | 180 ++ gdk/linux-fb/gdkwindow-fb.c | 1372 +++++++++++ gdk/linux-fb/mi.h | 21 + gdk/linux-fb/miarc.c | 3569 +++++++++++++++++++++++++++++ gdk/linux-fb/midash.c | 309 +++ gdk/linux-fb/mifillarc.c | 743 ++++++ gdk/linux-fb/mifillarc.h | 224 ++ gdk/linux-fb/mifpoly.h | 112 + gdk/linux-fb/mifpolycon.c | 261 +++ gdk/linux-fb/miline.h | 177 ++ gdk/linux-fb/mipoly.c | 77 + gdk/linux-fb/mipoly.h | 230 ++ gdk/linux-fb/mipolygen.c | 214 ++ gdk/linux-fb/mipolyutil.c | 392 ++++ gdk/linux-fb/miscanfill.h | 139 ++ gdk/linux-fb/mispans.c | 507 ++++ gdk/linux-fb/mispans.h | 127 + gdk/linux-fb/mistruct.h | 58 + gdk/linux-fb/mitypes.h | 486 ++++ gdk/linux-fb/miwideline.c | 2108 +++++++++++++++++ gdk/linux-fb/miwideline.h | 254 ++ gdk/linux-fb/mizerclip.c | 622 +++++ gdk/linux-fb/mizerline.c | 332 +++ 51 files changed, 22169 insertions(+) create mode 100644 gdk/linux-fb/.cvsignore create mode 100644 gdk/linux-fb/Makefile.am create mode 100644 gdk/linux-fb/TODO create mode 100644 gdk/linux-fb/gdkcolor-fb.c create mode 100644 gdk/linux-fb/gdkcursor-fb.c create mode 100644 gdk/linux-fb/gdkdnd-fb.c create mode 100644 gdk/linux-fb/gdkdrawable-fb2.c create mode 100644 gdk/linux-fb/gdkevents-fb.c create mode 100644 gdk/linux-fb/gdkfb.h create mode 100644 gdk/linux-fb/gdkfont-fb.c create mode 100644 gdk/linux-fb/gdkgc-fb.c create mode 100644 gdk/linux-fb/gdkgeometry-fb.c create mode 100644 gdk/linux-fb/gdkglobals-fb.c create mode 100644 gdk/linux-fb/gdkim-fb.c create mode 100644 gdk/linux-fb/gdkimage-fb.c create mode 100644 gdk/linux-fb/gdkinput-none.c create mode 100644 gdk/linux-fb/gdkinput-ps2.c create mode 100644 gdk/linux-fb/gdkinput.c create mode 100644 gdk/linux-fb/gdkinputprivate.h create mode 100644 gdk/linux-fb/gdkmain-fb.c create mode 100644 gdk/linux-fb/gdkpixmap-fb.c create mode 100644 gdk/linux-fb/gdkpoly-generic.h create mode 100644 gdk/linux-fb/gdkpolyreg-generic.c create mode 100644 gdk/linux-fb/gdkprivate-fb.h create mode 100644 gdk/linux-fb/gdkproperty-fb.c create mode 100644 gdk/linux-fb/gdkregion-generic.c create mode 100644 gdk/linux-fb/gdkregion-generic.h create mode 100644 gdk/linux-fb/gdkselection-fb.c create mode 100644 gdk/linux-fb/gdkvisual-fb.c create mode 100644 gdk/linux-fb/gdkwindow-fb.c create mode 100644 gdk/linux-fb/mi.h create mode 100644 gdk/linux-fb/miarc.c create mode 100644 gdk/linux-fb/midash.c create mode 100644 gdk/linux-fb/mifillarc.c create mode 100644 gdk/linux-fb/mifillarc.h create mode 100644 gdk/linux-fb/mifpoly.h create mode 100644 gdk/linux-fb/mifpolycon.c create mode 100644 gdk/linux-fb/miline.h create mode 100644 gdk/linux-fb/mipoly.c create mode 100644 gdk/linux-fb/mipoly.h create mode 100644 gdk/linux-fb/mipolygen.c create mode 100644 gdk/linux-fb/mipolyutil.c create mode 100644 gdk/linux-fb/miscanfill.h create mode 100644 gdk/linux-fb/mispans.c create mode 100644 gdk/linux-fb/mispans.h create mode 100644 gdk/linux-fb/mistruct.h create mode 100644 gdk/linux-fb/mitypes.h create mode 100644 gdk/linux-fb/miwideline.c create mode 100644 gdk/linux-fb/miwideline.h create mode 100644 gdk/linux-fb/mizerclip.c create mode 100644 gdk/linux-fb/mizerline.c diff --git a/gdk/linux-fb/.cvsignore b/gdk/linux-fb/.cvsignore new file mode 100644 index 0000000000..10f8f3eec3 --- /dev/null +++ b/gdk/linux-fb/.cvsignore @@ -0,0 +1,8 @@ +*.lo +Makefile +Makefile.in +.deps +_libs +.libs +libgdk-x11.la +gxid diff --git a/gdk/linux-fb/Makefile.am b/gdk/linux-fb/Makefile.am new file mode 100644 index 0000000000..bdaeb24a5b --- /dev/null +++ b/gdk/linux-fb/Makefile.am @@ -0,0 +1,61 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = @STRIP_BEGIN@ \ + -DG_LOG_DOMAIN=\"Gdk\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/gdk \ + -I$(top_builddir)/gdk \ + @GTK_DEBUG_FLAGS@ \ + @GTK_XIM_FLAGS@ \ + @GTK_LOCALE_FLAGS@ \ + @GLIB_CFLAGS@ \ +@STRIP_END@ + +LDFLAGS = @STRIP_BEGIN@ \ + @GLIB_LIBS@ \ + -L/gnome2/lib \ + -lt1 \ + -lm \ +@STRIP_END@ + +lib_LTLIBRARIES = libgdk-fb.la +noinst_PROGRAMS=test-fb + +test_fb_LDFLAGS=../libgdk.la libgdk-fb.la + +libgdk_fb_la_SOURCES = \ + gdkcolor-fb.c \ + gdkcursor-fb.c \ + gdkdnd-fb.c \ + gdkdrawable-fb2.c \ + gdkfont-fb.c \ + gdkgc-fb.c \ + gdkgeometry-fb.c \ + gdkglobals-fb.c \ + gdkim-fb.c \ + gdkimage-fb.c \ + gdkinput.c \ + gdkmain-fb.c \ + gdkpixmap-fb.c \ + gdkproperty-fb.c \ + gdkpolyreg-generic.c \ + gdkregion-generic.c \ + gdkselection-fb.c \ + gdkvisual-fb.c \ + gdkwindow-fb.c \ + gdkx.h \ + gdkprivate-fb.h \ + gdkinputprivate.h \ + gdkinput-ps2.c \ + gdkevents-fb.c \ + miarc.c \ + midash.c \ + mifillarc.c \ + mifpolycon.c \ + mipoly.c \ + mipolygen.c \ + mipolyutil.c \ + miwideline.c \ + mizerclip.c \ + mizerline.c \ + mispans.c diff --git a/gdk/linux-fb/TODO b/gdk/linux-fb/TODO new file mode 100644 index 0000000000..fd308a5b33 --- /dev/null +++ b/gdk/linux-fb/TODO @@ -0,0 +1,3 @@ +. Fix CTree +. DnD? +. All the standard X cursors diff --git a/gdk/linux-fb/gdkcolor-fb.c b/gdk/linux-fb/gdkcolor-fb.c new file mode 100644 index 0000000000..684dec443b --- /dev/null +++ b/gdk/linux-fb/gdkcolor-fb.c @@ -0,0 +1,759 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include +#include +#include +#include + +#include "gdkcolor.h" +#include "gdkprivate-fb.h" + +static gint gdk_colormap_match_color (GdkColormap *cmap, + GdkColor *color, + const gchar *available); +GdkColormap* +gdk_colormap_new (GdkVisual *visual, + gint private_cmap) +{ + GdkColormap *colormap; + GdkColormapPrivateFB *private; + GdkFBDisplay *fbd; + int i; + + g_return_val_if_fail (visual != NULL, NULL); + + private = g_new (GdkColormapPrivateFB, 1); + colormap = (GdkColormap*) private; + + private->base.visual = visual; + private->base.ref_count = 1; + fbd = gdk_display; + + private->hash = NULL; + + colormap->size = visual->colormap_size; + colormap->colors = NULL; + + switch (visual->type) + { + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_STATIC_COLOR: + case GDK_VISUAL_GRAYSCALE: + case GDK_VISUAL_PSEUDO_COLOR: + private->info = g_new0 (GdkColorInfo, colormap->size); + colormap->colors = g_new (GdkColor, colormap->size); + + private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash, + (GCompareFunc) gdk_color_equal); + + if (private_cmap) + { + guint16 red[256], green[256], blue[256]; + struct fb_cmap fbc = {0, 256}; + + fbc.red = red; + fbc.green = green; + fbc.blue = blue; + + if(ioctl(fbd->fd, FBIOGETCMAP, &fbc)) + g_error("ioctl(FBIOGETCMAP) failed"); + + for (i = 0; i < colormap->size; i++) + { + colormap->colors[i].pixel = i; + colormap->colors[i].red = red[i]; + colormap->colors[i].green = green[i]; + colormap->colors[i].blue = blue[i]; + } + + gdk_colormap_change (colormap, colormap->size); + } + break; + + case GDK_VISUAL_DIRECT_COLOR: + g_error("NYI"); +#if 0 + colormap->colors = g_new (GdkColor, colormap->size); + + size = 1 << visual->red_prec; + for (i = 0; i < size; i++) + colormap->colors[i].red = i * 65535 / (size - 1); + + size = 1 << visual->green_prec; + for (i = 0; i < size; i++) + colormap->colors[i].green = i * 65535 / (size - 1); + + size = 1 << visual->blue_prec; + for (i = 0; i < size; i++) + colormap->colors[i].blue = i * 65535 / (size - 1); + + gdk_colormap_change (colormap, colormap->size); +#endif + break; + + default: + g_assert_not_reached(); + + case GDK_VISUAL_TRUE_COLOR: + break; + } + + return colormap; +} + +void +_gdk_colormap_real_destroy (GdkColormap *colormap) +{ + GdkColormapPrivateFB *private = (GdkColormapPrivateFB*) colormap; + + if (private->hash) + g_hash_table_destroy (private->hash); + + g_free (private->info); + g_free (colormap->colors); + g_free (colormap); +} + +#define MIN_SYNC_TIME 2 + +void +gdk_colormap_sync (GdkColormap *colormap, + gboolean force) +{ + +} + +GdkColormap* +gdk_colormap_get_system (void) +{ + static GdkColormap *colormap = NULL; + + if (!colormap) + { + guint16 red[256], green[256], blue[256]; + struct fb_cmap fbc = {0, 256}; + int i, r, g, b; + GdkVisual *visual = gdk_visual_get_system(); + + if(visual->type == GDK_VISUAL_GRAYSCALE + || visual->type == GDK_VISUAL_PSEUDO_COLOR) + { + fbc.red = red; + fbc.green = green; + fbc.blue = blue; + switch(visual->type) + { + case GDK_VISUAL_GRAYSCALE: + for(i = 0; i < 256; i++) + red[i] = green[i] = blue[i] = i << 8; + i--; + red[i] = green[i] = blue[i] = 65535; /* Make it a true white */ + break; + case GDK_VISUAL_PSEUDO_COLOR: + /* Color cube stolen from gdkrgb upon advice from Owen */ + for(i = r = 0; r < 6; r++) + for(g = 0; g < 6; g++) + for(b = 0; b < 6; b++) + { + red[i] = r * 65535 / 5; + green[i] = g * 65535 / 5; + blue[i] = b * 65535 / 5; + i++; + } + g_assert(i == 216); + + /* Fill in remaining space with grays */ + for(i = 216; i < 256; i++) + { + red[i] = green[i] = blue[i] = + (i - 216) * 40; + } + /* Real white */ + red[255] = green[255] = blue[255] = 65535; + break; + default: + break; + } + + ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc); + } + + colormap = gdk_colormap_new(visual, TRUE); + } + + return colormap; +} + +gint +gdk_colormap_get_system_size (void) +{ + return 1 << (gdk_display->modeinfo.bits_per_pixel); +} + +void +gdk_colormap_change (GdkColormap *colormap, + gint ncolors) +{ + guint16 red[256], green[256], blue[256]; + struct fb_cmap fbc = {0,256}; + GdkColormapPrivateFB *private; + int i; + + g_return_if_fail (colormap != NULL); + + fbc.red = red; + fbc.green = green; + fbc.blue = blue; + + private = (GdkColormapPrivateFB*) colormap; + switch (private->base.visual->type) + { + case GDK_VISUAL_GRAYSCALE: + for(i = 0; i < ncolors; i++) + { + red[i] = green[i] = blue[i] = + (colormap->colors[i].red + + colormap->colors[i].green + + colormap->colors[i].blue)/3; + } + ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc); + break; + + case GDK_VISUAL_PSEUDO_COLOR: + for (i = 0; i < ncolors; i++) + { + red[i] = colormap->colors[i].red; + green[i] = colormap->colors[i].green; + blue[i] = colormap->colors[i].blue; + } + ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc); + break; + + default: + break; + } +} + +gboolean +gdk_color_parse (const gchar *spec, + GdkColor *color) +{ + char aline[512]; + FILE *fh; + + g_return_val_if_fail(spec, FALSE); + g_return_val_if_fail(color, FALSE); + + if(spec[0] == '#') + { + if(strlen(spec) == 7) + { + guint num; + + sscanf(spec + 1, "%x", &num); + color->red = (num & 0xFF0000) >> 8; + color->green = (num & 0xFF00); + color->blue = (num & 0xFF) << 8; + } + else if(strlen(spec) == 13) + { + char s1[5], s2[5], s3[5]; + g_snprintf(s1, sizeof(s1), spec + 1); + g_snprintf(s2, sizeof(s2), spec + 5); + g_snprintf(s3, sizeof(s3), spec + 9); + + if(!sscanf(s1, "%hx", &color->red)) + g_error("sscanf failed"); + if(!sscanf(s2, "%hx", &color->green)) + g_error("sscanf failed"); + if(!sscanf(s3, "%hx", &color->blue)) + g_error("sscanf failed"); + } + else + { + g_warning("Couldn't parse color specifier `%s'", spec); + return FALSE; + } + + return TRUE; + } + else + { + fh = fopen("/usr/lib/X11/rgb.txt", "r"); + if(!fh) + return FALSE; + + while(fgets(aline, sizeof(aline), fh)) + { + int red, green, blue; + char *ctmp; + + g_strstrip(aline); + if(!aline[0] || aline[0] == '#' || aline[0] == '!') + continue; + + ctmp = strtok(aline, " \t"); + if(!ctmp) + continue; + red = atoi(ctmp); + + ctmp = strtok(NULL, " \t"); + if(!ctmp) + continue; + green = atoi(ctmp); + + ctmp = strtok(NULL, " \t"); + if(!ctmp) + continue; + blue = atoi(ctmp); + + ctmp = strtok(NULL, " \t"); + if(!ctmp || strcmp(ctmp, spec)) + continue; + + color->red = red << 8; + color->green = green << 8; + color->blue = blue << 8; + return TRUE; + } + fclose(fh); + } + + return FALSE; +} + +void +gdk_colormap_free_colors (GdkColormap *colormap, + GdkColor *colors, + gint ncolors) +{ + GdkColormapPrivateFB *private; + gint i; + + g_return_if_fail (colormap != NULL); + g_return_if_fail (colors != NULL); + + private = (GdkColormapPrivateFB*) colormap; + + if ((private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR) && + (private->base.visual->type != GDK_VISUAL_GRAYSCALE)) + return; + + for (i=0; iinfo[pixel].ref_count) + { + private->info[pixel].ref_count--; + + if (private->info[pixel].ref_count == 0) + { + if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE)) + g_hash_table_remove (private->hash, &colormap->colors[pixel]); + private->info[pixel].flags = 0; + } + } + } +} + +/******************** + * Color allocation * + ********************/ + +/* Try to allocate a single color using XAllocColor. If it succeeds, + * cache the result in our colormap, and store in ret. + */ +static gboolean +gdk_colormap_alloc1 (GdkColormap *colormap, + GdkColor *color, + GdkColor *ret) +{ + GdkColormapPrivateFB *private; + int i; + + private = (GdkColormapPrivateFB*) colormap; + + if(private->base.visual->type != GDK_VISUAL_GRAYSCALE + && private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR) + return FALSE; + + *ret = *color; + if(!color->red && !color->green && !color->blue) /* black */ + { + ret->pixel = 0; + private->info[ret->pixel].ref_count++; + return TRUE; + } + + if(color->red == 65535 && color->green == 65535 && color->blue == 65535) /* white */ + { + ret->pixel = 255; + private->info[ret->pixel].ref_count++; + return TRUE; + } + + for(i = 1; i < (colormap->size - 1); i++) + { + if(!private->info[i].ref_count) + { + guint16 red = color->red, green = color->green, blue = color->blue; + struct fb_cmap fbc; + fbc.len = 1; + fbc.start = i; + fbc.red = &red; + fbc.green = &green; + fbc.blue = &blue; + + ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc); + + ret->pixel = i; + colormap->colors[ret->pixel] = *ret; + private->info[ret->pixel].ref_count = 1; + g_hash_table_insert (private->hash, + &colormap->colors[ret->pixel], + &colormap->colors[ret->pixel]); + return TRUE; + } + } + + return FALSE; +} + +static gint +gdk_colormap_alloc_colors_shared (GdkColormap *colormap, + GdkColor *colors, + gint ncolors, + gboolean writeable, + gboolean best_match, + gboolean *success) +{ + GdkColormapPrivateFB *private; + gint i, index; + gint nremaining = 0; + gint nfailed = 0; + + private = (GdkColormapPrivateFB*) colormap; + index = -1; + + for (i=0; i 0 && best_match) + { + gchar *available = g_new (gchar, colormap->size); + + for (i = 0; i < colormap->size; i++) + available[i] = ((private->info[i].ref_count == 0) || + !(private->info[i].flags && GDK_COLOR_WRITEABLE)); + + while (nremaining > 0) + { + for (i=0; iinfo[index].ref_count) + { + private->info[index].ref_count++; + colors[i] = colormap->colors[index]; + success[i] = TRUE; + nremaining--; + } + else + { + if (gdk_colormap_alloc1 (colormap, + &colormap->colors[index], + &colors[i])) + { + success[i] = TRUE; + nremaining--; + break; + } + else + { + available[index] = FALSE; + } + } + } + else + { + nfailed++; + nremaining--; + success[i] = 2; /* flag as permanent failure */ + } + } + } + } + g_free (available); + } + + /* Change back the values we flagged as permanent failures */ + if (nfailed > 0) + { + for (i=0; ihash, &colors[i]); + if (lookup_color) + { + private->info[lookup_color->pixel].ref_count++; + colors[i].pixel = lookup_color->pixel; + success[i] = TRUE; + } + else + nremaining++; + } + } + + /* If that failed, we try to allocate a new color, or approxmiate + * with what we can get if best_match is TRUE. + */ + if (nremaining > 0) + return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success); + else + return 0; +} + +gint +gdk_colormap_alloc_colors (GdkColormap *colormap, + GdkColor *colors, + gint ncolors, + gboolean writeable, + gboolean best_match, + gboolean *success) +{ + GdkColormapPrivateFB *private; + GdkVisual *visual; + gint i; + gint nremaining = 0; + + g_return_val_if_fail (colormap != NULL, FALSE); + g_return_val_if_fail (colors != NULL, FALSE); + + private = (GdkColormapPrivateFB*) colormap; + + for (i=0; ibase.visual->type) + { + case GDK_VISUAL_PSEUDO_COLOR: + case GDK_VISUAL_GRAYSCALE: + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_STATIC_COLOR: + return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors, + writeable, best_match, success); + break; + + case GDK_VISUAL_DIRECT_COLOR: + case GDK_VISUAL_TRUE_COLOR: + visual = private->base.visual; + + for (i=0; i> (16 - visual->red_prec)) << visual->red_shift) + + ((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) + + ((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift)); + success[i] = TRUE; + } + break; + } + return nremaining; +} + +gboolean +gdk_color_change (GdkColormap *colormap, + GdkColor *color) +{ + GdkColormapPrivateFB *private; + struct fb_cmap fbc = {0, 1}; + + g_return_val_if_fail (colormap != NULL, FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + private = (GdkColormapPrivateFB*) colormap; + + switch(private->base.visual->type) + { + case GDK_VISUAL_GRAYSCALE: + color->red = color->green = color->blue = (color->red + color->green + color->blue)/3; + + case GDK_VISUAL_PSEUDO_COLOR: + fbc.start = color->pixel; + fbc.red = &color->red; + fbc.green = &color->green; + fbc.blue = &color->blue; + ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc); + break; + + default: + break; + } + + return TRUE; +} + +static gint +gdk_colormap_match_color (GdkColormap *cmap, + GdkColor *color, + const gchar *available) +{ + GdkColor *colors; + guint sum, max; + gint rdiff, gdiff, bdiff; + gint i, index; + + g_return_val_if_fail (cmap != NULL, 0); + g_return_val_if_fail (color != NULL, 0); + + colors = cmap->colors; + max = 3 * (65536); + index = -1; + + for (i = 0; i < cmap->size; i++) + { + if ((!available) || (available && available[i])) + { + rdiff = (color->red - colors[i].red); + gdiff = (color->green - colors[i].green); + bdiff = (color->blue - colors[i].blue); + + sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff); + + if (sum < max) + { + index = i; + max = sum; + } + } + } + + return index; +} + +gint gdk_colors_alloc (GdkColormap *colormap, + gboolean contiguous, + gulong *planes, + gint nplanes, + gulong *pixels, + gint npixels) +{ + return 0; +} + +void +gdk_colors_free (GdkColormap *colormap, + gulong *pixels, + gint npixels, + gulong planes) +{ +} + +gulong +gdk_color_context_get_pixel(GdkColorContext *cc, + gushort red, + gushort green, + gushort blue, + gint *failed) +{ + g_error("NYI"); + + return 0; +} + +GdkColorContext * +gdk_color_context_new(GdkVisual *visual, + GdkColormap *colormap) +{ + g_error("NYI"); + + return NULL; +} + +GdkColorContext * +gdk_color_context_new_mono(GdkVisual *visual, + GdkColormap *colormap) +{ + g_error("NYI"); + + return NULL; +} + +void +gdk_color_context_free(GdkColorContext *cc) +{ + g_error("NYI"); +} + +gint +gdk_color_context_query_color(GdkColorContext *cc, + GdkColor *color) +{ + g_error("NYI"); + + return 0; +} diff --git a/gdk/linux-fb/gdkcursor-fb.c b/gdk/linux-fb/gdkcursor-fb.c new file mode 100644 index 0000000000..8a883f9e87 --- /dev/null +++ b/gdk/linux-fb/gdkcursor-fb.c @@ -0,0 +1,81 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gdkfb.h" +#include "gdkprivate-fb.h" +#include "gdkcursor.h" + +GdkCursor* +gdk_cursor_new (GdkCursorType cursor_type) +{ + GdkCursorPrivateFB *private; + GdkCursor *cursor; + + return NULL; + + private = g_new0(GdkCursorPrivateFB, 1); + cursor = (GdkCursor*) private; + cursor->type = cursor_type; + cursor->ref_count = 1; + + return cursor; +} + +GdkCursor* +gdk_cursor_new_from_pixmap (GdkPixmap *source, + GdkPixmap *mask, + GdkColor *fg, + GdkColor *bg, + gint x, + gint y) +{ + GdkCursorPrivateFB *private; + GdkCursor *cursor; + + g_return_val_if_fail (source != NULL, NULL); + + private = g_new (GdkCursorPrivateFB, 1); + cursor = (GdkCursor *) private; + cursor->type = GDK_CURSOR_IS_PIXMAP; + cursor->ref_count = 1; + private->cursor = gdk_pixmap_ref(source); + private->mask = gdk_pixmap_ref(mask); + + return cursor; +} + +void +_gdk_cursor_destroy (GdkCursor *cursor) +{ + GdkCursorPrivateFB *private; + + g_return_if_fail (cursor != NULL); + g_return_if_fail (cursor->ref_count == 0); + + private = (GdkCursorPrivateFB *) cursor; + + g_free (private); +} diff --git a/gdk/linux-fb/gdkdnd-fb.c b/gdk/linux-fb/gdkdnd-fb.c new file mode 100644 index 0000000000..b65c55a8e5 --- /dev/null +++ b/gdk/linux-fb/gdkdnd-fb.c @@ -0,0 +1,264 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include + +#include "gdk.h" /* For gdk_flush() */ +#include "gdkdnd.h" +#include "gdkproperty.h" +#include "gdkinternals.h" +#include "gdkprivate-fb.h" + +typedef struct _GdkDragContextPrivate GdkDragContextPrivate; + +typedef enum { + GDK_DRAG_STATUS_DRAG, + GDK_DRAG_STATUS_MOTION_WAIT, + GDK_DRAG_STATUS_ACTION_WAIT, + GDK_DRAG_STATUS_DROP +} GtkDragStatus; + +/* Structure that holds information about a drag in progress. + * this is used on both source and destination sides. + */ +struct _GdkDragContextPrivate { + GdkDragContext context; + + guint ref_count; +}; + +/* Drag Contexts */ + +static GList *contexts; + +GdkDragContext * +gdk_drag_context_new (void) +{ + GdkDragContextPrivate *result; + + result = g_new0 (GdkDragContextPrivate, 1); + + result->ref_count = 1; + + contexts = g_list_prepend (contexts, result); + + return (GdkDragContext *)result; +} + +void +gdk_drag_context_ref (GdkDragContext *context) +{ + g_return_if_fail (context != NULL); + + ((GdkDragContextPrivate *)context)->ref_count++; +} + +void +gdk_drag_context_unref (GdkDragContext *context) +{ + GdkDragContextPrivate *private = (GdkDragContextPrivate *)context; + + g_return_if_fail (context != NULL); + g_return_if_fail (private->ref_count > 0); + + private->ref_count--; + + if (private->ref_count == 0) + { + g_dataset_destroy (private); + + g_list_free (context->targets); + + if (context->source_window) + { +#if 0 + if ((context->protocol == GDK_DRAG_PROTO_XDND) && + !context->is_source) + xdnd_manage_source_filter (context, context->source_window, FALSE); +#endif + + gdk_window_unref (context->source_window); + } + + if (context->dest_window) + gdk_window_unref (context->dest_window); + +#if 0 + if (private->window_cache) + gdk_window_cache_destroy (private->window_cache); +#endif + + contexts = g_list_remove (contexts, private); + g_free (private); + } +} + +/************************************************************* + ************************** Public API *********************** + *************************************************************/ + +void +gdk_dnd_init (void) +{ +} + +/* Source side */ + +static void +gdk_drag_do_leave (GdkDragContext *context, guint32 time) G_GNUC_UNUSED; + +static void +gdk_drag_do_leave (GdkDragContext *context, guint32 time) +{ +} + +GdkDragContext * +gdk_drag_begin (GdkWindow *window, + GList *targets) +{ + GList *tmp_list; + GdkDragContext *new_context; + + g_return_val_if_fail (window != NULL, NULL); + + new_context = gdk_drag_context_new (); + new_context->is_source = TRUE; + new_context->source_window = window; + gdk_window_ref (window); + + tmp_list = g_list_last (targets); + new_context->targets = NULL; + while (tmp_list) + { + new_context->targets = g_list_prepend (new_context->targets, + tmp_list->data); + tmp_list = tmp_list->prev; + } + + new_context->actions = 0; + + return new_context; +} + +guint32 +gdk_drag_get_protocol (guint32 xid, + GdkDragProtocol *protocol) +{ + *protocol = GDK_DRAG_PROTO_NONE; + return GDK_NONE; +} + +void +gdk_drag_find_window (GdkDragContext *context, + GdkWindow *drag_window, + gint x_root, + gint y_root, + GdkWindow **dest_window, + GdkDragProtocol *protocol) +{ + g_return_if_fail (context != NULL); + + *dest_window = gdk_window_get_pointer(NULL, &x_root, &y_root, NULL); +} + +gboolean +gdk_drag_motion (GdkDragContext *context, + GdkWindow *dest_window, + GdkDragProtocol protocol, + gint x_root, + gint y_root, + GdkDragAction suggested_action, + GdkDragAction possible_actions, + guint32 time) +{ + g_return_val_if_fail (context != NULL, FALSE); + + return FALSE; +} + +void +gdk_drag_drop (GdkDragContext *context, + guint32 time) +{ + g_return_if_fail (context != NULL); +} + +void +gdk_drag_abort (GdkDragContext *context, + guint32 time) +{ + g_return_if_fail (context != NULL); +} + +/* Destination side */ + +void +gdk_drag_status (GdkDragContext *context, + GdkDragAction action, + guint32 time) +{ + g_return_if_fail (context != NULL); +} + +void +gdk_drop_reply (GdkDragContext *context, + gboolean ok, + guint32 time) +{ + g_return_if_fail (context != NULL); +} + +void +gdk_drop_finish (GdkDragContext *context, + gboolean success, + guint32 time) +{ + g_return_if_fail (context != NULL); +} + + +void +gdk_window_register_dnd (GdkWindow *window) +{ + g_return_if_fail (window != NULL); +} + +/************************************************************* + * gdk_drag_get_selection: + * Returns the selection atom for the current source window + * arguments: + * + * results: + *************************************************************/ + +GdkAtom +gdk_drag_get_selection (GdkDragContext *context) +{ + g_return_val_if_fail (context != NULL, GDK_NONE); + + return GDK_NONE; +} + diff --git a/gdk/linux-fb/gdkdrawable-fb2.c b/gdk/linux-fb/gdkdrawable-fb2.c new file mode 100644 index 0000000000..f5c7b8c122 --- /dev/null +++ b/gdk/linux-fb/gdkdrawable-fb2.c @@ -0,0 +1,959 @@ +#include "gdkprivate-fb.h" +#include "mi.h" +#include + +#ifndef g_alloca +#define g_alloca alloca +#endif + +static void gdk_fb_drawable_destroy (GdkDrawable *drawable); +void gdk_fb_draw_rectangle (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + gint x, + gint y, + gint width, + gint height); +static void gdk_fb_draw_arc (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + gint x, + gint y, + gint width, + gint height, + gint angle1, + gint angle2); +static void gdk_fb_draw_polygon (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + GdkPoint *points, + gint npoints); +static void gdk_fb_draw_text (GdkDrawable *drawable, + GdkFont *font, + GdkGC *gc, + gint x, + gint y, + const gchar *text, + gint text_length); +static void gdk_fb_draw_text_wc (GdkDrawable *drawable, + GdkFont *font, + GdkGC *gc, + gint x, + gint y, + const GdkWChar *text, + gint text_length); +void gdk_fb_draw_drawable (GdkDrawable *drawable, + GdkGC *gc, + GdkPixmap *src, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height); +static void gdk_fb_draw_points (GdkDrawable *drawable, + GdkGC *gc, + GdkPoint *points, + gint npoints); +static void gdk_fb_draw_segments (GdkDrawable *drawable, + GdkGC *gc, + GdkSegment *segs, + gint nsegs); +static void gdk_fb_draw_lines (GdkDrawable *drawable, + GdkGC *gc, + GdkPoint *points, + gint npoints); + +GdkDrawableClass _gdk_fb_drawable_class = { + gdk_fb_drawable_destroy, + (gpointer)_gdk_fb_gc_new, + gdk_fb_draw_rectangle, + gdk_fb_draw_arc, + gdk_fb_draw_polygon, + gdk_fb_draw_text, + gdk_fb_draw_text_wc, + gdk_fb_draw_drawable, + gdk_fb_draw_points, + gdk_fb_draw_segments, + gdk_fb_draw_lines +}; + +/***************************************************** + * FB specific implementations of generic functions * + *****************************************************/ + +GdkColormap* +gdk_drawable_get_colormap (GdkDrawable *drawable) +{ + GdkColormap *retval = GDK_DRAWABLE_P(drawable)->colormap; + + if(!retval) + retval = gdk_colormap_get_system(); + + return retval; +} + +void +gdk_drawable_set_colormap (GdkDrawable *drawable, + GdkColormap *colormap) +{ + GdkColormap *old_cmap; + old_cmap = GDK_DRAWABLE_P(drawable)->colormap; + GDK_DRAWABLE_P(drawable)->colormap = gdk_colormap_ref(colormap); + gdk_colormap_unref(old_cmap); +} + +/* Drawing + */ +static void +gdk_fb_drawable_destroy (GdkDrawable *drawable) +{ +} + +static GdkRegion * +gdk_fb_clip_region(GdkDrawable *drawable, GdkGC *gc, gboolean do_clipping) +{ + GdkRectangle draw_rect; + GdkRegion *real_clip_region, *tmpreg; + + g_assert(!GDK_IS_WINDOW(drawable) || !GDK_WINDOW_P(drawable)->input_only); + + draw_rect.x = GDK_DRAWABLE_FBDATA(drawable)->llim_x; + draw_rect.y = GDK_DRAWABLE_FBDATA(drawable)->llim_y; + draw_rect.width = GDK_DRAWABLE_FBDATA(drawable)->lim_x - draw_rect.x; + draw_rect.height = GDK_DRAWABLE_FBDATA(drawable)->lim_y - draw_rect.y; + real_clip_region = gdk_region_rectangle(&draw_rect); + + if(do_clipping && GDK_IS_WINDOW(drawable) && GDK_WINDOW_P(drawable)->mapped && !GDK_WINDOW_P(drawable)->input_only) + { + GdkWindow *parentwin, *lastwin; + + for(parentwin = lastwin = ((GdkWindow *)drawable); + parentwin; lastwin = parentwin, parentwin = GDK_WINDOW_P(parentwin)->parent) + { + GList *cur; + + for(cur = GDK_WINDOW_P(parentwin)->children; cur && cur->data != lastwin; cur = cur->next) + { + if(!GDK_WINDOW_P(cur->data)->mapped || GDK_WINDOW_P(cur->data)->input_only) + continue; + + draw_rect.x = GDK_DRAWABLE_FBDATA(cur->data)->llim_x; + draw_rect.y = GDK_DRAWABLE_FBDATA(cur->data)->llim_y; + draw_rect.width = GDK_DRAWABLE_FBDATA(cur->data)->lim_x - draw_rect.x; + draw_rect.height = GDK_DRAWABLE_FBDATA(cur->data)->lim_y - draw_rect.y; + + tmpreg = gdk_region_rectangle(&draw_rect); + gdk_region_subtract(real_clip_region, tmpreg); + gdk_region_destroy(tmpreg); + } + } + } + + if(gc) + { + if(GDK_GC_FBDATA(gc)->clip_region) + { + tmpreg = gdk_region_copy(GDK_GC_FBDATA(gc)->clip_region); + gdk_region_offset(tmpreg, GDK_DRAWABLE_FBDATA(drawable)->abs_x + GDK_GC_P(gc)->clip_x_origin, + GDK_DRAWABLE_FBDATA(drawable)->abs_y + GDK_GC_P(gc)->clip_y_origin); + gdk_region_intersect(real_clip_region, tmpreg); + gdk_region_destroy(tmpreg); + } + + if(GDK_GC_FBDATA(gc)->values.clip_mask) + { + GdkDrawable *cmask = GDK_GC_FBDATA(gc)->values.clip_mask; + + g_assert(GDK_DRAWABLE_P(cmask)->depth == 1); + g_assert(GDK_DRAWABLE_FBDATA(cmask)->abs_x == 0 + && GDK_DRAWABLE_FBDATA(cmask)->abs_y == 0); + + draw_rect.x = GDK_DRAWABLE_FBDATA(drawable)->abs_x + GDK_DRAWABLE_FBDATA(cmask)->llim_x + GDK_GC_FBDATA(gc)->values.clip_x_origin; + draw_rect.y = GDK_DRAWABLE_FBDATA(drawable)->abs_y + GDK_DRAWABLE_FBDATA(cmask)->llim_y + GDK_GC_FBDATA(gc)->values.clip_y_origin; + draw_rect.width = GDK_DRAWABLE_P(cmask)->width; + draw_rect.height = GDK_DRAWABLE_P(cmask)->height; + + tmpreg = gdk_region_rectangle(&draw_rect); + gdk_region_intersect(real_clip_region, tmpreg); + gdk_region_destroy(tmpreg); + } + } + + return real_clip_region; +} + +static void +gdk_fb_fill_span(GdkDrawable *drawable, GdkGC *gc, GdkSegment *cur, guint pixel, GdkVisual *visual) +{ + int curx, cury; + guchar *mem = GDK_DRAWABLE_FBDATA(drawable)->mem; + guint rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride; + guint depth = GDK_DRAWABLE_P(drawable)->depth; + + if(gc + && (GDK_GC_FBDATA(gc)->values.clip_mask + || GDK_GC_FBDATA(gc)->values.tile + || GDK_GC_FBDATA(gc)->values.stipple)) + { + int clipxoff, clipyoff; /* Amounts to add to curx & cury to get x & y in clip mask */ + int tsxoff, tsyoff; + GdkDrawable *cmask; + guchar *clipmem; + guint mask_rowstride; + GdkPixmap *ts = NULL; + gboolean solid_stipple; + + cmask = GDK_GC_FBDATA(gc)->values.clip_mask; + if(cmask) + { + clipmem = GDK_DRAWABLE_FBDATA(cmask)->mem; + clipxoff = GDK_DRAWABLE_FBDATA(cmask)->abs_x - GDK_GC_FBDATA(gc)->values.clip_x_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_x; + clipyoff = GDK_DRAWABLE_FBDATA(cmask)->abs_y - GDK_GC_FBDATA(gc)->values.clip_y_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_y; + mask_rowstride = GDK_DRAWABLE_FBDATA(cmask)->rowstride; + } + + if(GDK_GC_FBDATA(gc)->values.fill == GDK_TILED + && GDK_GC_FBDATA(gc)->values.tile) + { + gint xstep, ystep; + gint relx, rely; + + ts = GDK_GC_FBDATA(gc)->values.tile; + for(cury = cur->y1; cury < cur->y2; cury += ystep) + { + int drawh; + + rely = cury - GDK_DRAWABLE_FBDATA(drawable)->abs_y; + drawh = (rely + GDK_GC_FBDATA(gc)->values.ts_y_origin) % GDK_DRAWABLE_P(ts)->height; + if(drawh < 0) + drawh += GDK_DRAWABLE_P(ts)->height; + + ystep = MIN(GDK_DRAWABLE_P(ts)->height - drawh, cur->y2 - rely); + + for(curx = cur->x1; curx < cur->x2; curx += xstep) + { + int draww; + + relx = curx - GDK_DRAWABLE_FBDATA(drawable)->abs_x; + + draww = (relx + GDK_GC_FBDATA(gc)->values.ts_x_origin) % GDK_DRAWABLE_P(ts)->width; + if(draww < 0) + draww += GDK_DRAWABLE_P(ts)->width; + + xstep = MIN(GDK_DRAWABLE_P(ts)->width - draww, cur->x2 - relx); + + gdk_fb_draw_drawable_2(drawable, gc, ts, + draww, drawh, + relx, rely, + xstep, ystep, FALSE, TRUE); + } + } + + return; + } + else if((GDK_GC_FBDATA(gc)->values.fill == GDK_STIPPLED + || GDK_GC_FBDATA(gc)->values.fill == GDK_OPAQUE_STIPPLED) + && GDK_GC_FBDATA(gc)->values.stipple) + { + ts = GDK_GC_FBDATA(gc)->values.stipple; + tsxoff = GDK_DRAWABLE_FBDATA(ts)->abs_x - GDK_GC_FBDATA(gc)->values.ts_x_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_x; + tsyoff = GDK_DRAWABLE_FBDATA(ts)->abs_y - GDK_GC_FBDATA(gc)->values.ts_y_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_y; + solid_stipple = (GDK_GC_FBDATA(gc)->values.fill == GDK_OPAQUE_STIPPLED); + } + + switch(depth) + { + case 1: + g_assert(!ts); + for(cury = cur->y1; cury < cur->y2; cury++) + { + for(curx = cur->x1; curx < cur->x2; curx++) + { + guchar *ptr = mem + (cury * rowstride) + (curx >> 3); + int maskx = curx+clipxoff, masky = cury + clipyoff; + guchar foo; + + if(cmask) + { + foo = clipmem[masky*mask_rowstride + (maskx >> 3)]; + + if(!(foo & (1 << (maskx % 8)))) + continue; + } + + *ptr |= (1 << (curx % 8)); + } + } + break; + case 8: + for(cury = cur->y1; cury < cur->y2; cury++) + { + for(curx = cur->x1; curx < cur->x2; curx++) + { + guchar *ptr = mem + (cury * rowstride) + curx; + int maskx = curx+clipxoff, masky = cury + clipyoff; + guchar foo; + + if(cmask) + { + foo = clipmem[masky*mask_rowstride + (maskx >> 3)]; + + if(!(foo & (1 << (maskx % 8)))) + continue; + } + + if(ts) + { + int wid = GDK_DRAWABLE_P(ts)->width, hih = GDK_DRAWABLE_P(ts)->height; + maskx = (curx+tsxoff)%wid; + masky = (cury+tsyoff)%hih; + if(maskx < 0) + maskx += wid; + if(masky < 0) + masky += hih; + + foo = GDK_DRAWABLE_FBDATA(ts)->mem[(maskx >> 3) + GDK_DRAWABLE_FBDATA(ts)->rowstride*masky]; + if(foo & (1 << (maskx % 8))) + { + pixel = GDK_GC_FBDATA(gc)->values.foreground.pixel; + } + else if(solid_stipple) + { + pixel = GDK_GC_FBDATA(gc)->values.background.pixel; + } + else + continue; + } + + *ptr = pixel; + } + } + break; + + case 16: + case 24: + case 32: + for(cury = cur->y1; cury < cur->y2; cury++) + { + for(curx = cur->x1; curx < cur->x2; curx++) + { + guint *ptr2 = (guint *)(mem + (cury * rowstride) + (curx * (depth >> 3))); + int maskx = curx+clipxoff, masky = cury + clipyoff; + guchar foo; + + if(cmask) + { + foo = clipmem[masky*mask_rowstride + (maskx >> 3)]; + + if(!(foo & (1 << (maskx % 8)))) + continue; + } + + if(ts) + { + int wid = GDK_DRAWABLE_P(ts)->width, hih = GDK_DRAWABLE_P(ts)->height; + + maskx = (curx+tsxoff)%wid; + masky = (cury+tsyoff)%hih; + if(maskx < 0) + maskx += wid; + if(masky < 0) + masky += hih; + + foo = GDK_DRAWABLE_FBDATA(ts)->mem[(maskx >> 3) + GDK_DRAWABLE_FBDATA(ts)->rowstride*masky]; + if(foo & (1 << (maskx % 8))) + { + pixel = GDK_GC_FBDATA(gc)->values.foreground.pixel; + } + else if(solid_stipple) + { + pixel = GDK_GC_FBDATA(gc)->values.background.pixel; + } + else + continue; + } + + *ptr2 = (*ptr2 & ~(visual->red_mask|visual->green_mask|visual->blue_mask)) | pixel; + } + } + break; + } + } + else + { + switch(depth) + { + case 1: + for(cury = cur->y1; cury < cur->y2; cury++) + { + for(curx = cur->x1; curx < cur->x2; curx++) + { + guchar *ptr = mem + (cury * rowstride) + (curx >> 3); + + if(pixel) + *ptr |= (1 << (curx % 8)); + else + *ptr &= ~(1 << (curx % 8)); + } + } + break; + + case 8: + for(cury = cur->y1; cury < cur->y2; cury++) + { + guchar *ptr = mem + (cury * rowstride) + cur->x1; + memset(ptr, pixel, cur->x2 - cur->x1); + } + break; + + case 16: + case 24: + case 32: + for(cury = cur->y1; cury < cur->y2; cury++) + { + for(curx = cur->x1; curx < cur->x2; curx++) + { + guint *ptr2 = (guint *)(mem + (cury * rowstride) + (curx * (depth >> 3))); + + *ptr2 = (*ptr2 & ~(visual->red_mask|visual->green_mask|visual->blue_mask)) | pixel; + } + } + break; + } + } +} + +void +gdk_fb_fill_spans(GdkDrawable *drawable, + GdkGC *gc, + GdkRectangle *rects, int nrects) +{ + int i; + guint pixel; + GdkRegion *real_clip_region, *tmpreg; + GdkRectangle draw_rect, cursor_rect; + GdkVisual *visual = gdk_visual_get_system(); + gboolean handle_cursor = FALSE; + + if(GDK_IS_WINDOW(drawable) && !GDK_WINDOW_P(drawable)->mapped) + return; + if(GDK_IS_WINDOW(drawable) && GDK_WINDOW_P(drawable)->input_only) + g_error("Drawing on the evil input-only!"); + + if(gc) + pixel = GDK_GC_FBDATA(gc)->values.foreground.pixel; + else if(GDK_IS_WINDOW(drawable)) + pixel = GDK_WINDOW_P(drawable)->bg_color.pixel; + else + pixel = 0; + + real_clip_region = gdk_fb_clip_region(drawable, gc, TRUE); + + gdk_fb_get_cursor_rect(&cursor_rect); + if(GDK_DRAWABLE_FBDATA(drawable)->mem == GDK_DRAWABLE_FBDATA(gdk_parent_root)->mem + && cursor_rect.x >= 0 + && gdk_region_rect_in(real_clip_region, &cursor_rect) != GDK_OVERLAP_RECTANGLE_OUT) + { + handle_cursor = TRUE; + gdk_fb_cursor_hide(); + } + + for(i = 0; i < nrects; i++) + { + GdkSegment cur; + int j; + + cur.x1 = rects[i].x; + cur.y1 = rects[i].y; + cur.x2 = cur.x1 + rects[i].width; + cur.y2 = cur.y1 + rects[i].height; + g_assert(cur.x2 >= cur.x1); + g_assert(cur.y2 >= cur.y1); + + cur.x1 += GDK_DRAWABLE_FBDATA(drawable)->abs_x; + cur.x1 = MAX(cur.x1, GDK_DRAWABLE_FBDATA(drawable)->llim_x); + + cur.x2 += GDK_DRAWABLE_FBDATA(drawable)->abs_x; + cur.x2 = MIN(cur.x2, GDK_DRAWABLE_FBDATA(drawable)->lim_x); + cur.x1 = MIN(cur.x1, cur.x2); + + cur.y1 += GDK_DRAWABLE_FBDATA(drawable)->abs_y; + cur.y1 = MAX(cur.y1, GDK_DRAWABLE_FBDATA(drawable)->llim_y); + + cur.y2 += GDK_DRAWABLE_FBDATA(drawable)->abs_y; + cur.y2 = MIN(cur.y2, GDK_DRAWABLE_FBDATA(drawable)->lim_y); + cur.y1 = MIN(cur.y1, cur.y2); + + draw_rect.x = cur.x1; + draw_rect.y = cur.y1; + draw_rect.width = cur.x2 - cur.x1; + draw_rect.height = cur.y2 - cur.y1; + + switch(gdk_region_rect_in(real_clip_region, &draw_rect)) + { + case GDK_OVERLAP_RECTANGLE_PART: + tmpreg = gdk_region_rectangle(&draw_rect); + gdk_region_intersect(tmpreg, real_clip_region); + for(j = 0; j < tmpreg->numRects; j++) + gdk_fb_fill_span(drawable, gc, &tmpreg->rects[j], pixel, visual); + gdk_region_destroy(tmpreg); + break; + case GDK_OVERLAP_RECTANGLE_IN: + gdk_fb_fill_span(drawable, gc, &cur, pixel, visual); + break; + default: + break; + } + } + + gdk_region_destroy(real_clip_region); + if(handle_cursor) + gdk_fb_cursor_unhide(); +} + +void +gdk_fb_draw_drawable_2 (GdkDrawable *drawable, + GdkGC *gc, + GdkPixmap *src, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height, + gboolean draw_bg, + gboolean do_clipping) +{ + GdkRegion *real_clip_region, *tmpreg; + GdkRectangle rect, cursor_rect; + int i; + int src_x_off, src_y_off; + int clipxoff, clipyoff; + guchar *mem = GDK_DRAWABLE_FBDATA(drawable)->mem, *srcmem = GDK_DRAWABLE_FBDATA(src)->mem; + guchar *clipmem; + guint rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride, src_rowstride = GDK_DRAWABLE_FBDATA(src)->rowstride; + guint clip_rowstride; + GdkDrawableFBData *fbd; + gboolean handle_cursor = FALSE; + GdkPixmap *bgpm = NULL; + GdkWindow *bg_relto = drawable; + + if(GDK_IS_WINDOW(drawable) && !GDK_WINDOW_P(drawable)->mapped) + return; + if(GDK_IS_WINDOW(drawable) && GDK_WINDOW_P(drawable)->input_only) + g_error("Drawing on the evil input-only!"); + + if(GDK_IS_WINDOW(drawable)) + { + bgpm = GDK_WINDOW_P(drawable)->bg_pixmap; + if(bgpm == GDK_PARENT_RELATIVE_BG) + { + for(; bgpm == GDK_PARENT_RELATIVE_BG && bg_relto; bg_relto = GDK_WINDOW_P(bg_relto)->parent) + bgpm = GDK_WINDOW_P(bg_relto)->bg_pixmap; + } + + if(bgpm == GDK_NO_BG) + bgpm = NULL; + + if(bgpm) + g_assert(GDK_DRAWABLE_P(bgpm)->depth == 8); + } + + if(drawable == src) + { + GdkDrawableFBData *fbd = GDK_DRAWABLE_FBDATA(src); + /* One lame hack deserves another ;-) */ + srcmem = g_alloca(fbd->rowstride * fbd->lim_y); + memcpy(srcmem, mem, fbd->rowstride * fbd->lim_y); + } + + real_clip_region = gdk_fb_clip_region(drawable, gc, do_clipping); + rect.x = xdest + GDK_DRAWABLE_FBDATA(drawable)->abs_x; + rect.y = ydest + GDK_DRAWABLE_FBDATA(drawable)->abs_y; + rect.width = width; + rect.height = height; + tmpreg = gdk_region_rectangle(&rect); + gdk_region_intersect(real_clip_region, tmpreg); + gdk_region_destroy(tmpreg); + + rect.x = xdest + GDK_DRAWABLE_FBDATA(drawable)->abs_x; + rect.y = ydest + GDK_DRAWABLE_FBDATA(drawable)->abs_y; + rect.width = MAX(GDK_DRAWABLE_P(src)->width - xsrc, 0); + rect.height = MAX(GDK_DRAWABLE_P(src)->height - ysrc, 0); + if(!rect.width || !rect.height) + goto out; + tmpreg = gdk_region_rectangle(&rect); + gdk_region_intersect(real_clip_region, tmpreg); + gdk_region_destroy(tmpreg); + + src_x_off = (GDK_DRAWABLE_FBDATA(src)->abs_x + xsrc) - (GDK_DRAWABLE_FBDATA(drawable)->abs_x + xdest); + src_y_off = (GDK_DRAWABLE_FBDATA(src)->abs_y + ysrc) - (GDK_DRAWABLE_FBDATA(drawable)->abs_y + ydest); + clipxoff = - GDK_DRAWABLE_FBDATA(drawable)->abs_x; + clipyoff = - GDK_DRAWABLE_FBDATA(drawable)->abs_y; + if(gc) + { + clipxoff -= GDK_GC_FBDATA(gc)->values.clip_x_origin; + clipyoff -= GDK_GC_FBDATA(gc)->values.clip_y_origin; + + if(GDK_GC_FBDATA(gc)->values.clip_mask) + { + fbd = GDK_DRAWABLE_FBDATA(GDK_GC_FBDATA(gc)->values.clip_mask); + clipmem = GDK_DRAWABLE_FBDATA(GDK_GC_FBDATA(gc)->values.clip_mask)->mem; + clip_rowstride = GDK_DRAWABLE_FBDATA(GDK_GC_FBDATA(gc)->values.clip_mask)->rowstride; + } + } + + gdk_fb_get_cursor_rect(&cursor_rect); + if(do_clipping + && GDK_DRAWABLE_FBDATA(drawable)->mem == GDK_DRAWABLE_FBDATA(gdk_parent_root)->mem + && cursor_rect.x >= 0 + && gdk_region_rect_in(real_clip_region, &cursor_rect) != GDK_OVERLAP_RECTANGLE_OUT) + { + handle_cursor = TRUE; + gdk_fb_cursor_hide(); + } + + for(i = 0; i < real_clip_region->numRects; i++) + { + GdkRegionBox *cur = &real_clip_region->rects[i]; + int cur_y; + + if(GDK_DRAWABLE_P(src)->depth == GDK_DRAWABLE_P(drawable)->depth + && GDK_DRAWABLE_P(src)->depth > 1 + && (!gc || !GDK_GC_FBDATA(gc)->values.clip_mask)) + { + guint depth = GDK_DRAWABLE_P(src)->depth; + + for(cur_y = cur->y1; cur_y < cur->y2; cur_y++) + { + memcpy(mem + (cur_y * rowstride) + cur->x1*(depth>>3), + srcmem + ((cur_y + src_y_off)*src_rowstride) + (cur->x1 + src_x_off)*(depth>>3), + (cur->x2 - cur->x1)*(depth>>3)); + } + } + else + { + int cur_x; + + for(cur_y = cur->y1; cur_y < cur->y2; cur_y++) + { + for(cur_x = cur->x1; cur_x < cur->x2; cur_x++) + { + guint pixel; + guint16 *p16; + guint32 *p32; + + if(gc && GDK_GC_FBDATA(gc)->values.clip_mask) + { + int maskx = cur_x+clipxoff, masky = cur_y + clipyoff; + guchar foo; + + if(maskx < 0 || masky < 0) + continue; + + foo = clipmem[masky*clip_rowstride + (maskx >> 3)]; + + if(!(foo & (1 << (maskx % 8)))) + continue; + } + + switch(GDK_DRAWABLE_P(src)->depth) + { + case 1: + { + guchar foo = srcmem[((cur_x + src_x_off) >> 3) + ((cur_y + src_y_off)*src_rowstride)]; + + if(foo & (1 << ((cur_x + src_x_off) % 8))) { + pixel = GDK_GC_FBDATA(gc)->values.foreground.pixel; + } else if(draw_bg) { + if(bgpm) + { + int bgx, bgy; + + bgx = (cur_x - GDK_DRAWABLE_FBDATA(bg_relto)->abs_x) % GDK_DRAWABLE_P(bgpm)->width; + bgy = (cur_y - GDK_DRAWABLE_FBDATA(bg_relto)->abs_y) % GDK_DRAWABLE_P(bgpm)->height; + + pixel = GDK_DRAWABLE_FBDATA(bgpm)->mem[bgx + bgy * GDK_DRAWABLE_FBDATA(bgpm)->rowstride]; + } + else + pixel = GDK_GC_FBDATA(gc)->values.background.pixel; + } else + continue; + } + break; + case 8: + pixel = srcmem[(cur_x + src_x_off) + ((cur_y + src_y_off)*src_rowstride)]; + break; + case 16: + pixel = *(guint16 *)(srcmem + (cur_x + src_x_off)*2 + ((cur_y + src_y_off)*src_rowstride)); + break; + case 24: + pixel = 0x00FFFFFF & *(guint32 *)(srcmem + (cur_x + src_x_off)*3 + ((cur_y + src_y_off)*src_rowstride)); + break; + case 32: + pixel = *(guint32 *)(srcmem + (cur_x + src_x_off)*4 + ((cur_y + src_y_off)*src_rowstride)); + break; + default: + g_assert_not_reached(); + break; + } + + switch(GDK_DRAWABLE_P(drawable)->depth) + { + case 1: + { + guchar *foo = mem + (cur_y*src_rowstride) + (cur_x >> 3); + + if(pixel == GDK_GC_FBDATA(gc)->values.foreground.pixel) + *foo |= (1 << (cur_x % 8)); + else + *foo &= ~(1 << (cur_x % 8)); + } + break; + case 8: + mem[cur_x + cur_y*rowstride] = pixel; + break; + case 16: + p16 = (guint16 *)&mem[cur_x*2 + cur_y*rowstride]; + *p16 = pixel; + break; + case 24: + p32 = (guint32 *)&mem[cur_x*3 + cur_y*rowstride]; + *p32 = (*p32 & 0xFF000000) | pixel; + break; + case 32: + p32 = (guint32 *)&mem[cur_x*4 + cur_y*rowstride]; + *p32 = pixel; + break; + default: + g_assert_not_reached(); + break; + } + } + } + } + } + + out: + gdk_region_destroy(real_clip_region); + + if(handle_cursor) + gdk_fb_cursor_unhide(); +} + +void +gdk_fb_draw_drawable (GdkDrawable *drawable, + GdkGC *gc, + GdkPixmap *src, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height) +{ + gdk_fb_draw_drawable_2(drawable, gc, src, xsrc, ysrc, xdest, ydest, width, height, TRUE, TRUE); +} + +static void +gdk_fb_draw_text(GdkDrawable *drawable, + GdkFont *font, + GdkGC *gc, + gint x, + gint y, + const gchar *text, + gint text_length) +{ + GLYPH *g; + GdkDrawablePrivate tmp_pixmap; + GdkDrawableFBData fbd; + + if(GDK_IS_WINDOW(drawable) && !GDK_WINDOW_P(drawable)->mapped) + return; + + y -= font->ascent; /* y is relative to baseline, we want it relative to top left corner */ + + g = T1_SetString(GDK_FONT_FB(font)->t1_font_id, (char *)text, text_length, 0, T1_KERNING, GDK_FONT_FB(font)->size, + NULL); + g_assert(g); + + tmp_pixmap.window_type = GDK_DRAWABLE_PIXMAP; + tmp_pixmap.width = (g->metrics.rightSideBearing - g->metrics.leftSideBearing); + tmp_pixmap.height = (g->metrics.ascent - g->metrics.descent); + tmp_pixmap.depth = 1; + + fbd.mem = g->bits; + fbd.abs_x = fbd.abs_y = fbd.llim_x = fbd.llim_y = 0; + fbd.lim_x = tmp_pixmap.width; + fbd.lim_y = tmp_pixmap.height; + fbd.rowstride = (tmp_pixmap.width + 7) / 8; + tmp_pixmap.klass_data = &fbd; + tmp_pixmap.klass = &_gdk_fb_drawable_class; + + gdk_fb_draw_drawable_2(drawable, gc, (GdkPixmap *)&tmp_pixmap, 0, 0, x, y, tmp_pixmap.width, tmp_pixmap.height, FALSE, TRUE); +} + +static void +gdk_fb_draw_text_wc (GdkDrawable *drawable, + GdkFont *font, + GdkGC *gc, + gint x, + gint y, + const GdkWChar *text, + gint text_length) +{ + char *realbuf; + int i; + + if(GDK_IS_WINDOW(drawable) && (!GDK_WINDOW_P(drawable)->mapped || GDK_WINDOW_P(drawable)->input_only)) + return; + + /* A hack, a hack, a pretty little hack */ + realbuf = alloca(text_length + 1); + for(i = 0; i < text_length; i++) + realbuf[i] = text[i]; + realbuf[i] = 0; + + gdk_fb_draw_text(drawable, font, gc, x, y, realbuf, text_length); +} + +void +gdk_fb_draw_rectangle (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + gint x, + gint y, + gint width, + gint height) +{ + GdkRectangle rect; + + if(filled) + { + static volatile int print_rect = 0; + + if(print_rect) + { + fprintf(debug_out, "[%d, %d] +[%d, %d]\n", x, y, width, height); + if(y < 0) + G_BREAKPOINT(); + } + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + gdk_fb_fill_spans(drawable, gc, &rect, 1); + } + else + { + GdkPoint pts[5]; + pts[0].x = pts[4].x = x; + pts[0].y = pts[4].y = y; + pts[1].x = x + width; + pts[1].y = y; + pts[2].x = x + width; + pts[2].y = y + height; + pts[3].x = x; + pts[3].y = y + height; + gdk_fb_draw_lines(drawable, gc, pts, 5); + } +} + +static void gdk_fb_draw_points (GdkDrawable *drawable, + GdkGC *gc, + GdkPoint *points, + gint npoints) +{ + GdkRectangle *rects = alloca(npoints * sizeof(GdkRectangle)); + int i; + + for(i = 0; i < npoints; i++) + { + rects[i].x = points[i].x; + rects[i].y = points[i].y; + rects[i].width = rects[i].height = 1; + } + + gdk_fb_fill_spans(drawable, gc, rects, npoints); +} + +static void gdk_fb_draw_arc (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + gint x, + gint y, + gint width, + gint height, + gint angle1, + gint angle2) +{ + miArc arc; + + arc.x = x; + arc.y = y; + arc.width = width; + arc.height = height; + arc.angle1 = angle1; + arc.angle2 = angle2; + + if(filled) + miPolyFillArc(drawable, gc, 1, &arc); + else + miPolyArc(drawable, gc, 1, &arc); +} + +static void gdk_fb_draw_polygon (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + GdkPoint *points, + gint npoints) +{ + if(filled) + miFillPolygon(drawable, gc, 0, 0, npoints, points); + else + { + GdkPoint *realpts = alloca(sizeof(GdkPoint) * (npoints + 1)); + + memcpy(realpts, points, sizeof(GdkPoint) * npoints); + realpts[npoints] = points[0]; + gdk_fb_draw_lines(drawable, gc, points, npoints); + } +} + +static void gdk_fb_draw_lines (GdkDrawable *drawable, + GdkGC *gc, + GdkPoint *points, + gint npoints) +{ + if(GDK_GC_FBDATA(gc)->values.line_width > 0) + miWideLine(drawable, gc, 0, npoints, points); + else + miZeroLine(drawable, gc, 0, npoints, points); +} + +static void gdk_fb_draw_segments (GdkDrawable *drawable, + GdkGC *gc, + GdkSegment *segs, + gint nsegs) +{ + GdkPoint pts[2]; + int i; + + for(i = 0; i < nsegs; i++) + { + pts[0].x = segs[i].x1; + pts[0].y = segs[i].y1; + pts[1].x = segs[i].x2; + pts[1].y = segs[i].y2; + + gdk_fb_draw_lines(drawable, gc, pts, 2); + } +} + +void +gdk_fb_drawable_clear(GdkDrawable *d) +{ + _gdk_windowing_window_clear_area(d, 0, 0, GDK_DRAWABLE_P(d)->width, GDK_DRAWABLE_P(d)->height); +} diff --git a/gdk/linux-fb/gdkevents-fb.c b/gdk/linux-fb/gdkevents-fb.c new file mode 100644 index 0000000000..7b80aa364a --- /dev/null +++ b/gdk/linux-fb/gdkevents-fb.c @@ -0,0 +1,209 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gdk.h" +#include "gdkprivate-fb.h" +#include "gdkinternals.h" +#include "gdkfb.h" + +#include "gdkkeysyms.h" + +#if HAVE_CONFIG_H +# include +# if STDC_HEADERS +# include +# endif +#endif + +typedef struct _GdkIOClosure GdkIOClosure; +typedef struct _GdkEventPrivate GdkEventPrivate; + +#define DOUBLE_CLICK_TIME 250 +#define TRIPLE_CLICK_TIME 500 +#define DOUBLE_CLICK_DIST 5 +#define TRIPLE_CLICK_DIST 5 + +typedef enum +{ + /* Following flag is set for events on the event queue during + * translation and cleared afterwards. + */ + GDK_EVENT_PENDING = 1 << 0 +} GdkEventFlags; + +struct _GdkIOClosure +{ + GdkInputFunction function; + GdkInputCondition condition; + GdkDestroyNotify notify; + gpointer data; +}; + +struct _GdkEventPrivate +{ + GdkEvent event; + guint flags; +}; + +/* + * Private function declarations + */ + +/* Private variable declarations + */ + +#if 0 +static GList *client_filters; /* Filters for client messages */ + +static GSourceFuncs event_funcs = { + gdk_event_prepare, + gdk_event_check, + gdk_event_dispatch, + (GDestroyNotify)g_free +}; +#endif + +/********************************************* + * Functions for maintaining the event queue * + *********************************************/ + +void +gdk_events_init (void) +{ + +} + +/* + *-------------------------------------------------------------- + * gdk_events_pending + * + * Returns if events are pending on the queue. + * + * Arguments: + * + * Results: + * Returns TRUE if events are pending + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gboolean +gdk_events_pending (void) +{ + return gdk_event_queue_find_first()?TRUE:FALSE; +} + +GdkEvent* +gdk_event_get_graphics_expose (GdkWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + return NULL; +} + +void +gdk_events_queue (void) +{ + +} + +static gint handler_tag = 0; + +static gboolean +dispatch_events(gpointer data) +{ + GdkEvent *event; + + GDK_THREADS_ENTER(); + + while((event = gdk_event_unqueue())) + { + if(event->type == GDK_EXPOSE + && event->expose.window == gdk_parent_root) + gdk_fb_drawable_clear(event->expose.window); + else if(gdk_event_func) + (*gdk_event_func)(event, gdk_event_data); + gdk_event_free(event); + } + + GDK_THREADS_LEAVE(); + if(event && !handler_tag) + handler_tag = g_idle_add_full(G_PRIORITY_HIGH_IDLE, dispatch_events, NULL, NULL); + else if(!event && handler_tag) + { + g_source_remove(handler_tag); + handler_tag = 0; + } + + return handler_tag?TRUE:FALSE; +} + +void +_gdk_event_queue_changed(GList *queue) +{ + if(queue && !handler_tag) + handler_tag = g_idle_add_full(G_PRIORITY_HIGH_IDLE, dispatch_events, NULL, NULL); + else if(!queue && handler_tag) + { + g_source_remove(handler_tag); + handler_tag = 0; + } +} + +/* + *-------------------------------------------------------------- + * gdk_flush + * + * Flushes the Xlib output buffer and then waits + * until all requests have been received and processed + * by the X server. The only real use for this function + * is in dealing with XShm. + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +void +gdk_flush (void) +{ +} + +gboolean +gdk_event_send_client_message (GdkEvent *event, guint32 xid) +{ + return FALSE; +} + +void gdk_event_send_clientmessage_toall (GdkEvent *sev) +{ +} diff --git a/gdk/linux-fb/gdkfb.h b/gdk/linux-fb/gdkfb.h new file mode 100644 index 0000000000..7cb6f6d3ad --- /dev/null +++ b/gdk/linux-fb/gdkfb.h @@ -0,0 +1,18 @@ +#ifndef GDKFB_H +#define GDKFB_H 1 + +#include "gdk/gdkprivate.h" + +typedef struct _GdkFBDisplay GdkFBDisplay; +typedef struct _GdkFBWindow GdkFBWindow; + +extern GdkFBWindow *gdk_root_window; +extern GdkFBDisplay *gdk_display; + +#define GDK_ROOT_WINDOW() gdk_root_window +#define GDK_ROOT_PARENT() ((GdkWindow *)gdk_parent_root) +#define GDK_DISPLAY() gdk_display + +extern const char *gdk_progclass; + +#endif /* GDKFB_H */ diff --git a/gdk/linux-fb/gdkfont-fb.c b/gdk/linux-fb/gdkfont-fb.c new file mode 100644 index 0000000000..c22b840177 --- /dev/null +++ b/gdk/linux-fb/gdkfont-fb.c @@ -0,0 +1,328 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include +#include +#include +#include "gdkfont.h" +#include "gdkprivate-fb.h" + +static GHashTable *font_name_hash = NULL; +static GHashTable *fontset_name_hash = NULL; + +static void +gdk_font_hash_insert (GdkFontType type, GdkFont *font, const gchar *font_name) +{ + GdkFontPrivateFB *private = (GdkFontPrivateFB *)font; + GHashTable **hashp = (type == GDK_FONT_FONT) ? + &font_name_hash : &fontset_name_hash; + + if (!*hashp) + *hashp = g_hash_table_new (g_str_hash, g_str_equal); + + private->names = g_slist_prepend (private->names, g_strdup (font_name)); + g_hash_table_insert (*hashp, private->names->data, font); +} + +static void +gdk_font_hash_remove (GdkFontType type, GdkFont *font) +{ + GdkFontPrivateFB *private = (GdkFontPrivateFB *)font; + GSList *tmp_list; + GHashTable *hash = (type == GDK_FONT_FONT) ? + font_name_hash : fontset_name_hash; + + tmp_list = private->names; + while (tmp_list) + { + g_hash_table_remove (hash, tmp_list->data); + g_free (tmp_list->data); + + tmp_list = tmp_list->next; + } + + g_slist_free (private->names); + private->names = NULL; +} + +static GdkFont * +gdk_font_hash_lookup (GdkFontType type, const gchar *font_name) +{ + GdkFont *result; + GHashTable *hash = (type == GDK_FONT_FONT) ? + font_name_hash : fontset_name_hash; + + if (!hash) + return NULL; + else + { + result = g_hash_table_lookup (hash, font_name); + if (result) + gdk_font_ref (result); + + return result; + } +} + +GdkFont* +gdk_font_load (const gchar *font_name) +{ + GdkFont *font; + GdkFontPrivateFB *private; + + g_return_val_if_fail (font_name != NULL, NULL); + + font = gdk_font_hash_lookup (GDK_FONT_FONTSET, font_name); + if (font) + return font; + + { + char **pieces; + BBox bb; + + private = g_new0 (GdkFontPrivateFB, 1); + private->base.ref_count = 1; + private->names = NULL; + + pieces = g_strsplit(font_name, "-", 2); + if(pieces[1]) + { + private->size = atof(pieces[1]); + private->t1_font_id = T1_AddFont(pieces[0]); + } + else + private->t1_font_id = T1_AddFont((char *)font_name); + g_strfreev(pieces); + + T1_LoadFont(private->t1_font_id); + CreateNewFontSize(private->t1_font_id, private->size, FALSE); + + font = (GdkFont*) private; + font->type = GDK_FONT_FONTSET; + + bb = T1_GetFontBBox(private->t1_font_id); + + font->ascent = ((double)bb.ury) / 1000.0 * private->size; + font->descent = ((double)bb.lly) / -1000.0 * private->size; + } + + gdk_font_hash_insert (GDK_FONT_FONTSET, font, font_name); + + return font; +} + +GdkFont* +gdk_fontset_load (const gchar *fontset_name) +{ + return gdk_font_load(fontset_name); +} + +void +_gdk_font_destroy (GdkFont *font) +{ + gdk_font_hash_remove (font->type, font); + + switch (font->type) + { + case GDK_FONT_FONT: + break; + case GDK_FONT_FONTSET: + break; + default: + g_error ("unknown font type."); + break; + } + g_free (font); +} + +gint +_gdk_font_strlen (GdkFont *font, + const gchar *str) +{ + GdkFontPrivateFB *font_private; + gint length = 0; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (str != NULL, -1); + + font_private = (GdkFontPrivateFB*) font; + + if (font->type == GDK_FONT_FONT) + { + guint16 *string_2b = (guint16 *)str; + + while (*(string_2b++)) + length++; + } + else if (font->type == GDK_FONT_FONTSET) + { + length = strlen (str); + } + else + g_error("undefined font type\n"); + + return length; +} + +gint +gdk_font_id (const GdkFont *font) +{ + const GdkFontPrivateFB *font_private; + + g_return_val_if_fail (font != NULL, 0); + + font_private = (const GdkFontPrivateFB*) font; + + if (font->type == GDK_FONT_FONT) + { + return -1; + } + else + { + return 0; + } +} + +gint +gdk_font_equal (const GdkFont *fonta, + const GdkFont *fontb) +{ + const GdkFontPrivateFB *privatea; + const GdkFontPrivateFB *privateb; + + g_return_val_if_fail (fonta != NULL, FALSE); + g_return_val_if_fail (fontb != NULL, FALSE); + + privatea = (const GdkFontPrivateFB*) fonta; + privateb = (const GdkFontPrivateFB*) fontb; + + if(fonta == fontb) + return TRUE; + if(privatea->t1_font_id == privateb->t1_font_id + && privatea->size == privateb->size) + return TRUE; + + return FALSE; +} + +gint +gdk_text_width (GdkFont *font, + const gchar *text, + gint text_length) +{ + GdkFontPrivateFB *private; + gint width; + double n; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (text != NULL, -1); + + private = (GdkFontPrivateFB*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + case GDK_FONT_FONTSET: + n = private->size / 1000.0; + n *= T1_GetStringWidth(private->t1_font_id, (char *)text, text_length, 0, T1_KERNING); + width = ceil(n); + break; + default: + width = 0; + break; + } + + return width; +} + +gint +gdk_text_width_wc (GdkFont *font, + const GdkWChar *text, + gint text_length) +{ + char *realstr; + int i; + + realstr = alloca(text_length + 1); + for(i = 0; i < text_length; i++) + realstr[i] = text[i]; + realstr[i] = '\0'; + + return gdk_text_width(font, realstr, text_length); +} + +void +gdk_text_extents (GdkFont *font, + const gchar *text, + gint text_length, + gint *lbearing, + gint *rbearing, + gint *width, + gint *ascent, + gint *descent) +{ + GdkFontPrivateFB *private; + METRICSINFO mi; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + + private = (GdkFontPrivateFB*) font; + + mi = T1_GetMetricsInfo(private->t1_font_id, (char *)text, text_length, 0, T1_KERNING); + + if(ascent) + *ascent = ((double)mi.bbox.ury) / 1000.0 * private->size; + if(descent) + *descent = ((double)mi.bbox.lly) / -1000.0 * private->size; + if(width) + *width = ((double)mi.width) / 1000.0 * private->size; + if(lbearing) + *lbearing = ((double)mi.bbox.llx) / 1000.0 * private->size; + if(rbearing) + *rbearing = ((double)mi.bbox.urx) / 1000.0 * private->size; +} + +void +gdk_text_extents_wc (GdkFont *font, + const GdkWChar *text, + gint text_length, + gint *lbearing, + gint *rbearing, + gint *width, + gint *ascent, + gint *descent) +{ + char *realstr; + int i; + + realstr = alloca(text_length + 1); + for(i = 0; i < text_length; i++) + realstr[i] = text[i]; + realstr[i] = '\0'; + + return gdk_text_extents(font, realstr, text_length, lbearing, rbearing, width, ascent, descent); +} diff --git a/gdk/linux-fb/gdkgc-fb.c b/gdk/linux-fb/gdkgc-fb.c new file mode 100644 index 0000000000..02bf5d0591 --- /dev/null +++ b/gdk/linux-fb/gdkgc-fb.c @@ -0,0 +1,308 @@ +#include "gdkprivate-fb.h" +#include "gdkgc.h" +#include "gdkfb.h" +#include "gdkregion-generic.h" + +typedef enum { + GDK_GC_DIRTY_CLIP = 1 << 0, + GDK_GC_DIRTY_TS = 1 << 1 +} GdkGCDirtyValues; + +static void gdk_fb_gc_destroy (GdkGC *gc); +static void gdk_fb_gc_get_values (GdkGC *gc, + GdkGCValues *values); +static void gdk_fb_gc_set_values (GdkGC *gc, + GdkGCValues *values, + GdkGCValuesMask values_mask); +static void gdk_fb_gc_set_dashes (GdkGC *gc, + gint dash_offset, + gchar dash_list[], + gint n); + +static GdkGCClass gdk_fb_gc_class = { + gdk_fb_gc_destroy, + gdk_fb_gc_get_values, + gdk_fb_gc_set_values, + gdk_fb_gc_set_dashes +}; + +GdkGC * +_gdk_fb_gc_new (GdkDrawable *drawable, + GdkGCValues *values, + GdkGCValuesMask values_mask) +{ + GdkGC *gc; + GdkGCPrivate *private; + GdkGCFBData *data; + + gc = gdk_gc_alloc (); + private = (GdkGCPrivate *)gc; + + private->klass = &gdk_fb_gc_class; + private->klass_data = data = g_new0 (GdkGCFBData, 1); + data->values.foreground.pixel = 255; + data->values.foreground.red = data->values.foreground.green = data->values.foreground.blue = 65535; + + gdk_fb_gc_set_values(gc, values, values_mask); + + return gc; +} + +static void +gdk_fb_gc_destroy (GdkGC *gc) +{ + if (GDK_GC_FBDATA (gc)->clip_region) + gdk_region_destroy (GDK_GC_FBDATA (gc)->clip_region); + + g_free (GDK_GC_FBDATA (gc)); +} + +static void +gdk_fb_gc_get_values (GdkGC *gc, + GdkGCValues *values) +{ + *values = GDK_GC_FBDATA(gc)->values; +} + + +static void +gdk_fb_gc_set_values (GdkGC *gc, + GdkGCValues *values, + GdkGCValuesMask values_mask) +{ + GdkPixmap *oldpm; + GdkFont *oldf; + + if(values_mask & GDK_GC_FOREGROUND) + { + GDK_GC_FBDATA(gc)->values.foreground = values->foreground; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_FOREGROUND; + } + + if(values_mask & GDK_GC_BACKGROUND) + { + GDK_GC_FBDATA(gc)->values.background = values->background; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_BACKGROUND; + } + + if(values_mask & GDK_GC_FONT) + { + oldf = GDK_GC_FBDATA(gc)->values.font; + GDK_GC_FBDATA(gc)->values.font = gdk_font_ref(values->font); + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_FONT; + if(oldf) + gdk_font_unref(oldf); + } + + if(values_mask & GDK_GC_FUNCTION) + { + GDK_GC_FBDATA(gc)->values.function = values->function; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_FUNCTION; + } + + if(values_mask & GDK_GC_FILL) + { + GDK_GC_FBDATA(gc)->values.fill = values->fill; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_FILL; + } + + if(values_mask & GDK_GC_TILE) + { + oldpm = GDK_GC_FBDATA(gc)->values.tile; + if(values->tile) + g_assert(GDK_DRAWABLE_P(values->tile)->depth == 8); + + GDK_GC_FBDATA(gc)->values.tile = values->tile?gdk_pixmap_ref(values->tile):NULL; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_TILE; + if(oldpm) + gdk_pixmap_unref(oldpm); + } + + if(values_mask & GDK_GC_STIPPLE) + { + oldpm = GDK_GC_FBDATA(gc)->values.stipple; + if(values->stipple) + g_assert(GDK_DRAWABLE_P(values->stipple)->depth == 1); + GDK_GC_FBDATA(gc)->values.stipple = values->stipple?gdk_pixmap_ref(values->stipple):NULL; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_STIPPLE; + if(oldpm) + gdk_pixmap_unref(oldpm); + } + + if(values_mask & GDK_GC_CLIP_MASK) + { + oldpm = GDK_GC_FBDATA(gc)->values.clip_mask; + + GDK_GC_FBDATA(gc)->values.clip_mask = values->clip_mask?gdk_pixmap_ref(values->clip_mask):NULL; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_CLIP_MASK; + if(oldpm) + gdk_pixmap_unref(oldpm); + } + + if(values_mask & GDK_GC_SUBWINDOW) + { + GDK_GC_FBDATA(gc)->values.subwindow_mode = values->subwindow_mode; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_SUBWINDOW; + } + + if(values_mask & GDK_GC_TS_X_ORIGIN) + { + GDK_GC_FBDATA(gc)->values.ts_x_origin = values->ts_x_origin; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_TS_X_ORIGIN; + } + + if(values_mask & GDK_GC_TS_Y_ORIGIN) + { + GDK_GC_FBDATA(gc)->values.ts_y_origin = values->ts_y_origin; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_TS_Y_ORIGIN; + } + + if(values_mask & GDK_GC_CLIP_X_ORIGIN) + { + GDK_GC_FBDATA(gc)->values.clip_x_origin = GDK_GC_P(gc)->clip_x_origin = values->clip_x_origin; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_CLIP_X_ORIGIN; + } + + if(values_mask & GDK_GC_CLIP_Y_ORIGIN) + { + GDK_GC_FBDATA(gc)->values.clip_y_origin = GDK_GC_P(gc)->clip_y_origin = values->clip_y_origin; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_CLIP_Y_ORIGIN; + } + + if(values_mask & GDK_GC_EXPOSURES) + { + GDK_GC_FBDATA(gc)->values.graphics_exposures = values->graphics_exposures; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_EXPOSURES; + } + + if(values_mask & GDK_GC_LINE_WIDTH) + { + GDK_GC_FBDATA(gc)->values.line_width = values->line_width; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_LINE_WIDTH; + } + + if(values_mask & GDK_GC_LINE_STYLE) + { + GDK_GC_FBDATA(gc)->values.line_style = values->line_style; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_LINE_STYLE; + } + + if(values_mask & GDK_GC_CAP_STYLE) + { + GDK_GC_FBDATA(gc)->values.cap_style = values->cap_style; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_CAP_STYLE; + } + + if(values_mask & GDK_GC_JOIN_STYLE) + { + GDK_GC_FBDATA(gc)->values.join_style = values->join_style; + GDK_GC_FBDATA(gc)->values_mask |= GDK_GC_JOIN_STYLE; + } +} + +static void +gdk_fb_gc_set_dashes (GdkGC *gc, + gint dash_offset, + gchar dash_list[], + gint n) +{ + GDK_GC_FBDATA(gc)->dash_offset = dash_offset; + GDK_GC_FBDATA(gc)->dash_list_len = n; + if(n) + { + GDK_GC_FBDATA(gc)->dash_list = g_realloc(GDK_GC_FBDATA(gc)->dash_list, n); + memcpy(GDK_GC_FBDATA(gc)->dash_list, dash_list, n); + } + else + { + g_free(GDK_GC_FBDATA(gc)->dash_list); + GDK_GC_FBDATA(gc)->dash_list = NULL; + } +} + +void +gdk_gc_set_clip_rectangle (GdkGC *gc, + GdkRectangle *rectangle) +{ + GdkGCPrivate *private = (GdkGCPrivate *)gc; + GdkGCFBData *data; + + g_return_if_fail (gc != NULL); + + data = GDK_GC_FBDATA (gc); + + if (data->clip_region) + { + gdk_region_destroy (data->clip_region); + data->clip_region = NULL; + } + + if (rectangle) + data->clip_region = gdk_region_rectangle (rectangle); + + private->clip_x_origin = 0; + private->clip_y_origin = 0; + data->values.clip_x_origin = 0; + data->values.clip_y_origin = 0; +} + +void +gdk_gc_set_clip_region (GdkGC *gc, + GdkRegion *region) +{ + GdkGCPrivate *private = (GdkGCPrivate *)gc; + GdkGCFBData *data; + + g_return_if_fail (gc != NULL); + + data = GDK_GC_FBDATA (gc); + + if(region == data->clip_region) + return; + + if (data->clip_region) + { + gdk_region_destroy (data->clip_region); + data->clip_region = NULL; + } + + if (region) + data->clip_region = gdk_region_copy (region); + + private->clip_x_origin = 0; + private->clip_y_origin = 0; + data->values.clip_x_origin = 0; + data->values.clip_y_origin = 0; +} + + +void +gdk_gc_copy (GdkGC *dst_gc, GdkGC *src_gc) +{ + g_return_if_fail (dst_gc != NULL); + g_return_if_fail (src_gc != NULL); + + if(GDK_GC_FBDATA(dst_gc)->clip_region) + gdk_region_destroy(GDK_GC_FBDATA(dst_gc)->clip_region); + + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_FONT) + gdk_font_unref(GDK_GC_FBDATA(dst_gc)->values.font); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_TILE) + gdk_pixmap_unref(GDK_GC_FBDATA(dst_gc)->values.tile); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_STIPPLE) + gdk_pixmap_unref(GDK_GC_FBDATA(dst_gc)->values.stipple); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_CLIP_MASK) + gdk_pixmap_unref(GDK_GC_FBDATA(dst_gc)->values.clip_mask); + + *dst_gc = *src_gc; + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_FONT) + gdk_font_ref(GDK_GC_FBDATA(dst_gc)->values.font); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_TILE) + gdk_pixmap_ref(GDK_GC_FBDATA(dst_gc)->values.tile); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_STIPPLE) + gdk_pixmap_ref(GDK_GC_FBDATA(dst_gc)->values.stipple); + if(GDK_GC_FBDATA(dst_gc)->values_mask & GDK_GC_CLIP_MASK) + gdk_pixmap_ref(GDK_GC_FBDATA(dst_gc)->values.clip_mask); + if(GDK_GC_FBDATA(dst_gc)->clip_region) + GDK_GC_FBDATA(dst_gc)->clip_region = gdk_region_copy(GDK_GC_FBDATA(dst_gc)->clip_region); +} diff --git a/gdk/linux-fb/gdkgeometry-fb.c b/gdk/linux-fb/gdkgeometry-fb.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gdk/linux-fb/gdkglobals-fb.c b/gdk/linux-fb/gdkglobals-fb.c new file mode 100644 index 0000000000..220a4f6ab5 --- /dev/null +++ b/gdk/linux-fb/gdkglobals-fb.c @@ -0,0 +1,41 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include + +#include "gdktypes.h" +#include "gdkprivate-fb.h" +#include "config.h" + +const gchar *gdk_progclass = "none"; +gboolean gdk_null_window_warnings = TRUE; + +GdkWindow *_gdk_fb_pointer_grab_window, *_gdk_fb_keyboard_grab_window, *_gdk_fb_pointer_grab_confine = NULL; +GdkEventMask _gdk_fb_pointer_grab_events, _gdk_fb_keyboard_grab_events; + +GdkFBWindow *gdk_root_window = NULL; +GdkFBDisplay *gdk_display = NULL; +GdkCursor *_gdk_fb_pointer_grab_cursor; diff --git a/gdk/linux-fb/gdkim-fb.c b/gdk/linux-fb/gdkim-fb.c new file mode 100644 index 0000000000..05da53fc48 --- /dev/null +++ b/gdk/linux-fb/gdkim-fb.c @@ -0,0 +1,242 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gdki18n.h" +#include "gdkinternals.h" +#include "gdkprivate-fb.h" + +#if HAVE_CONFIG_H +# include +# if STDC_HEADERS +# include +# endif +#endif + +#include + +/* If this variable is FALSE, it indicates that we should + * avoid trying to use multibyte conversion functions and + * assume everything is 1-byte per character + */ +static gboolean gdk_use_mb; + +/* + *-------------------------------------------------------------- + * gdk_set_locale + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gchar* +gdk_set_locale (void) +{ + wchar_t result; + gchar *current_locale; + + gdk_use_mb = FALSE; + + if (!setlocale (LC_ALL,"")) + g_warning ("locale not supported by C library"); + + current_locale = setlocale (LC_ALL, NULL); + + if ((strcmp (current_locale, "C")) && (strcmp (current_locale, "POSIX"))) + { + gdk_use_mb = TRUE; + +#ifndef X_LOCALE + /* Detect GNU libc, where mb == UTF8. Not useful unless it's + * really a UTF8 locale. The below still probably will + * screw up on Greek, Cyrillic, etc, encoded as UTF8. + */ + + if ((MB_CUR_MAX == 2) && + (mbstowcs (&result, "\xdd\xa5", 1) > 0) && + result == 0x765) + { + if ((strlen (current_locale) < 4) || + g_strcasecmp (current_locale + strlen(current_locale) - 4, "utf8")) + gdk_use_mb = FALSE; + } +#endif /* X_LOCALE */ + } + + GDK_NOTE (MISC, + g_message ("%s multi-byte string functions.", + gdk_use_mb ? "Using" : "Not using")); + + return current_locale; +} + +void +gdk_im_begin (GdkIC *ic, GdkWindow* window) +{ +} + +void +gdk_im_end (void) +{ +} + +GdkIMStyle +gdk_im_decide_style (GdkIMStyle supported_style) +{ + return GDK_IM_PREEDIT_NONE | GDK_IM_STATUS_NONE; +} + +GdkIMStyle +gdk_im_set_best_style (GdkIMStyle style) +{ + return GDK_IM_PREEDIT_NONE | GDK_IM_STATUS_NONE; +} + +gint +gdk_im_ready (void) +{ + return FALSE; +} + +gint +gdk_im_open(void) +{ + return TRUE; +} + +void +gdk_im_close(void) +{ +} + +GdkIC * +gdk_ic_new (GdkICAttr *attr, GdkICAttributesType mask) +{ + return NULL; +} + +void +gdk_ic_destroy (GdkIC *ic) +{ +} + +GdkIMStyle +gdk_ic_get_style (GdkIC *ic) +{ + return GDK_IM_PREEDIT_NONE | GDK_IM_STATUS_NONE; +} + +void +gdk_ic_set_values (GdkIC *ic, ...) +{ +} + +void +gdk_ic_get_values (GdkIC *ic, ...) +{ +} + +GdkICAttributesType +gdk_ic_set_attr (GdkIC *ic, GdkICAttr *attr, GdkICAttributesType mask) +{ + return 0; +} + +GdkICAttributesType +gdk_ic_get_attr (GdkIC *ic, GdkICAttr *attr, GdkICAttributesType mask) +{ + return 0; +} + +GdkEventMask +gdk_ic_get_events (GdkIC *ic) +{ + return 0; +} + +/* + * gdk_wcstombs + * + * Returns a multi-byte string converted from the specified array + * of wide characters. The string is newly allocated. The array of + * wide characters must be null-terminated. If the conversion is + * failed, it returns NULL. + */ +gchar * +gdk_wcstombs (const GdkWChar *src) +{ + gchar *mbstr; + + gint length = 0; + gint i; + + while (src[length] != 0) + length++; + + mbstr = g_new (gchar, length + 1); + + for (i=0; i + + +#include +#include + +#include "gdk.h" +#include "gdkimage.h" +#include "gdkprivate.h" +#include "gdkprivate-fb.h" + +struct _GdkImagePrivateFB +{ + GdkImagePrivate base; +}; + +static void gdk_fb_image_destroy (GdkImage *image); +static void gdk_image_put_normal (GdkImage *image, + GdkDrawable *drawable, + GdkGC *gc, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height); + +static GdkImageClass image_class_normal = { + gdk_fb_image_destroy, + gdk_image_put_normal +}; + +GdkImage * +gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h) +{ + GdkImage *image; + GdkImagePrivateFB *private; + + private = g_new(GdkImagePrivateFB, 1); + image = (GdkImage *) private; + private->base.ref_count = 1; + private->base.klass = &image_class_normal; + image->type = GDK_IMAGE_NORMAL; + image->visual = visual; + image->width = w; + image->height = h; + image->depth = 1; + + image->byte_order = 1 /* MSBFirst */; + image->mem = g_malloc(w * h / 8); + image->bpp = 1; + image->bpl = (w+7)/8; + + return image; +} + +void +gdk_image_init (void) +{ +} + +GdkImage* +gdk_image_new (GdkImageType type, + GdkVisual *visual, + gint width, + gint height) +{ + GdkImage *image; + GdkImagePrivateFB *private; + + private = g_new (GdkImagePrivateFB, 1); + image = (GdkImage*) private; + + private->base.ref_count = 1; + + image->type = 0; + image->visual = visual; + image->width = width; + image->height = height; + image->depth = visual->depth; + + private->base.klass = &image_class_normal; + + if (image) + { + image->byte_order = 0; + image->mem = g_malloc(width * height * (image->depth >> 3)); + image->bpp = image->depth; + image->bpl = (width * image->depth + 7)/8; + } + + return image; +} + +GdkImage* +gdk_image_get (GdkWindow *window, + gint x, + gint y, + gint width, + gint height) +{ + GdkImage *image; + GdkImagePrivateFB *private; + gint bits_per_pixel = GDK_DRAWABLE_P(gdk_parent_root)->depth; + GdkDrawableFBData fbd; + GdkDrawablePrivate tmp_foo; + + g_return_val_if_fail (window != NULL, NULL); + + if (GDK_DRAWABLE_DESTROYED (window)) + return NULL; + + private = g_new (GdkImagePrivateFB, 1); + image = (GdkImage*) private; + + private->base.ref_count = 1; + private->base.klass = &image_class_normal; + + image->type = GDK_IMAGE_NORMAL; + image->visual = gdk_window_get_visual (window); + image->width = width; + image->height = height; + image->depth = bits_per_pixel; + + if (bits_per_pixel <= 8) + image->bpp = 1; + else if (bits_per_pixel <= 16) + image->bpp = 2; + else if (bits_per_pixel <= 24) + image->bpp = 3; + else + image->bpp = 4; + image->byte_order = 1; + image->bpl = (image->width * image->bpp + 7)/8; + image->mem = g_malloc(image->bpl * image->height); + + /* Fake its existence as a pixmap */ + memset(&tmp_foo, 0, sizeof(tmp_foo)); + memset(&fbd, 0, sizeof(fbd)); + tmp_foo.klass = &_gdk_fb_drawable_class; + tmp_foo.klass_data = &fbd; + fbd.mem = image->mem; + fbd.rowstride = image->bpl; + tmp_foo.width = fbd.lim_x = image->width; + tmp_foo.height = fbd.lim_y = image->height; + tmp_foo.depth = image->depth; + + gdk_fb_draw_drawable((GdkPixmap *)&tmp_foo, NULL, window, x, y, 0, 0, width, height); + + return image; +} + +guint32 +gdk_image_get_pixel (GdkImage *image, + gint x, + gint y) +{ + GdkImagePrivateFB *private; + + g_return_val_if_fail (image != NULL, 0); + + private = (GdkImagePrivateFB *) image; + + g_assert(image->depth == 8); + + return ((guchar *)image->mem)[x + y * image->bpl]; +} + +void +gdk_image_put_pixel (GdkImage *image, + gint x, + gint y, + guint32 pixel) +{ + GdkImagePrivateFB *private; + + g_return_if_fail (image != NULL); + + private = (GdkImagePrivateFB *) image; + g_assert(image->depth == 8); + + ((guchar *)image->mem)[x + y * image->bpl] = pixel; +} + +static void +gdk_fb_image_destroy (GdkImage *image) +{ + GdkImagePrivateFB *private; + + g_return_if_fail (image != NULL); + + private = (GdkImagePrivateFB*) image; + + g_free(image->mem); image->mem = NULL; + + g_free (image); +} + +static void +gdk_image_put_normal (GdkImage *image, + GdkDrawable *drawable, + GdkGC *gc, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height) +{ + GdkImagePrivateFB *image_private; + GdkDrawableFBData fbd; + GdkDrawablePrivate tmp_foo; + + g_return_if_fail (drawable != NULL); + g_return_if_fail (image != NULL); + g_return_if_fail (gc != NULL); + + if (GDK_DRAWABLE_DESTROYED (drawable)) + return; + + image_private = (GdkImagePrivateFB*) image; + + g_return_if_fail (image->type == GDK_IMAGE_NORMAL); + + /* Fake its existence as a pixmap */ + memset(&tmp_foo, 0, sizeof(tmp_foo)); + memset(&fbd, 0, sizeof(fbd)); + tmp_foo.klass = &_gdk_fb_drawable_class; + tmp_foo.klass_data = &fbd; + fbd.mem = image->mem; + fbd.rowstride = image->bpl; + tmp_foo.width = fbd.lim_x = image->width; + tmp_foo.height = fbd.lim_y = image->height; + tmp_foo.depth = image->depth; + + gdk_fb_draw_drawable(drawable, gc, (GdkPixmap *)&tmp_foo, xsrc, ysrc, xdest, ydest, width, height); +} + +void +gdk_image_exit(void) +{ +} diff --git a/gdk/linux-fb/gdkinput-none.c b/gdk/linux-fb/gdkinput-none.c new file mode 100644 index 0000000000..d3aba3dd93 --- /dev/null +++ b/gdk/linux-fb/gdkinput-none.c @@ -0,0 +1,79 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gdkinputprivate.h" + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +static void gdk_input_none_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask); + +void +gdk_input_init (void) +{ + gdk_input_vtable.set_mode = NULL; + gdk_input_vtable.set_axes = NULL; + gdk_input_vtable.set_key = NULL; + gdk_input_vtable.motion_events = NULL; + gdk_input_vtable.get_pointer = gdk_input_none_get_pointer; + gdk_input_vtable.grab_pointer = NULL; + gdk_input_vtable.ungrab_pointer = NULL; + gdk_input_vtable.configure_event = NULL; + gdk_input_vtable.enter_event = NULL; + gdk_input_vtable.other_event = NULL; + gdk_input_vtable.window_none_event = NULL; + gdk_input_vtable.enable_window = NULL; + gdk_input_vtable.disable_window = NULL; + + gdk_input_devices = g_list_append (NULL, (GdkDeviceInfo *) &gdk_input_core_info); + + gdk_input_ignore_core = FALSE; +} + +static void +gdk_input_none_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask) +{ + gint x_int, y_int; + + gdk_window_get_pointer (window, &x_int, &y_int, mask); + + if (x) *x = x_int; + if (y) *y = y_int; + if (pressure) *pressure = 0.5; + if (xtilt) *xtilt = 0; + if (ytilt) *ytilt = 0; +} diff --git a/gdk/linux-fb/gdkinput-ps2.c b/gdk/linux-fb/gdkinput-ps2.c new file mode 100644 index 0000000000..0db7456728 --- /dev/null +++ b/gdk/linux-fb/gdkinput-ps2.c @@ -0,0 +1,1096 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gdkinputprivate.h" +#include "gdkkeysyms.h" +#include "gdkprivate-fb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +typedef struct { + gint fd, fd_tag; + + gint x, y; + GdkWindow *prev_window; + gboolean button1_pressed, button2_pressed, button3_pressed; + gboolean click_grab; +} PS2Mouse; + +typedef struct { + gint fd, fd_tag, consfd; + + int vtnum, prev_vtnum; + guchar states[256]; + gboolean is_ext : 1; + gboolean caps_lock : 1; +} Keyboard; + +static void gdk_input_ps2_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask); +static Keyboard * tty_keyboard_open(void); +static guint keyboard_get_state(Keyboard *k); + +static PS2Mouse *ps2mouse = NULL; +static Keyboard *keyboard = NULL; +FILE *debug_out; + +static guint multiclick_tag; +static GdkEvent *multiclick_event = NULL; + +static gboolean +click_event_timeout(gpointer x) +{ + switch(multiclick_event->type) + { + case GDK_BUTTON_RELEASE: + gdk_event_free(multiclick_event); + break; + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + gdk_event_queue_append(multiclick_event); + break; + default: + break; + } + + multiclick_event = NULL; + multiclick_tag = 0; + + return FALSE; +} + +static void +send_button_event(PS2Mouse *mouse, guint button, gboolean press_event, time_t the_time) +{ + GdkEvent *event; + gint x, y; + GdkWindow *window; + int nbuttons = 0; + + if(_gdk_fb_pointer_grab_window) + window = _gdk_fb_pointer_grab_window; + else + window = gdk_window_get_pointer(NULL, NULL, NULL, NULL); + + gdk_window_get_origin(window, &x, &y); + x = mouse->x - x; + y = mouse->y - y; + + if(!press_event + && multiclick_event + && multiclick_event->button.button == button + && multiclick_event->button.window == window + && ABS(multiclick_event->button.x - x) < 3 + && ABS(multiclick_event->button.y - y) < 3) + { + multiclick_event->button.time = the_time; + + /* Just change multiclick_event into a different event */ + switch(multiclick_event->button.type) + { + default: + g_assert_not_reached(); + + case GDK_BUTTON_RELEASE: + multiclick_event->button.type = GDK_2BUTTON_PRESS; + return; + + case GDK_2BUTTON_PRESS: + multiclick_event->button.type = GDK_3BUTTON_PRESS; + return; + + case GDK_3BUTTON_PRESS: + gdk_event_queue_append(multiclick_event); multiclick_event = NULL; + g_source_remove(multiclick_tag); multiclick_tag = 0; + } + } + + event = gdk_event_make(window, press_event?GDK_BUTTON_PRESS:GDK_BUTTON_RELEASE, FALSE); + + if(!event) + return; + + event->button.x = x; + event->button.y = y; + event->button.button = button; + event->button.pressure = 0.5; + event->button.xtilt = event->button.ytilt = 0; + event->button.state = (mouse->button1_pressed?GDK_BUTTON1_MASK:0) + | (mouse->button2_pressed?GDK_BUTTON2_MASK:0) + | (mouse->button3_pressed?GDK_BUTTON3_MASK:0) + | (1 << (button + 8)) /* badhack */ + | keyboard_get_state(keyboard); + event->button.source = GDK_SOURCE_MOUSE; + event->button.deviceid = 0; + event->button.x_root = mouse->x; + event->button.y_root = mouse->y; + + if(mouse->button1_pressed) + nbuttons++; + if(mouse->button2_pressed) + nbuttons++; + if(mouse->button3_pressed) + nbuttons++; + + if(press_event && nbuttons == 1 && !_gdk_fb_pointer_grab_window) + { + gdk_pointer_grab(window, FALSE, gdk_window_get_events(window), NULL, NULL, GDK_CURRENT_TIME); + mouse->click_grab = TRUE; + } + else if(!press_event && nbuttons == 0 && mouse->click_grab) + { + gdk_pointer_ungrab(GDK_CURRENT_TIME); + mouse->click_grab = FALSE; + } + +#if 0 + g_message("Button #%d %s [%d, %d] in %p", button, press_event?"pressed":"released", + x, y, window); + + /* Debugging aid */ + if(window && window != gdk_parent_root) + { + GdkGC *tmp_gc; + + tmp_gc = gdk_gc_new(window); + GDK_GC_FBDATA(tmp_gc)->values.foreground.pixel = 0; + gdk_fb_draw_rectangle(window, tmp_gc, TRUE, 0, 0, + GDK_DRAWABLE_P(window)->width, GDK_DRAWABLE_P(window)->height); + gdk_gc_unref(tmp_gc); + } +#endif + + if(!press_event && !multiclick_tag) + { + multiclick_tag = g_timeout_add(250, click_event_timeout, NULL); + multiclick_event = gdk_event_copy(event); + } + + gdk_event_queue_append(event); +} + +static GdkPixmap *last_contents = NULL; +static GdkPoint last_location; +static GdkCursor *last_cursor = NULL; +static GdkGC *cursor_gc; + +void +gdk_fb_cursor_hide(void) +{ + if(last_contents) + { + gdk_gc_set_clip_mask(cursor_gc, NULL); + /* Restore old picture */ + gdk_fb_draw_drawable_2(gdk_parent_root, cursor_gc, last_contents, 0, 0, last_location.x, last_location.y, + GDK_DRAWABLE_P(last_contents)->width, + GDK_DRAWABLE_P(last_contents)->height, TRUE, FALSE); + } +} + +void +gdk_fb_cursor_invalidate(void) +{ + gdk_pixmap_unref(last_contents); + last_contents = NULL; +} + +void +gdk_fb_cursor_unhide(void) +{ + if(last_cursor) + { + if(!last_contents + || GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->width != GDK_DRAWABLE_P(last_contents)->width + || GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->height != GDK_DRAWABLE_P(last_contents)->height) + { + if(last_contents) + gdk_pixmap_unref(last_contents); + + last_contents = gdk_pixmap_new(gdk_parent_root, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->width, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->height, + GDK_DRAWABLE_P(gdk_parent_root)->depth); + } + + gdk_gc_set_clip_mask(cursor_gc, NULL); + gdk_fb_draw_drawable_2(last_contents, cursor_gc, gdk_parent_root, last_location.x, last_location.y, 0, 0, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->width, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->height, TRUE, FALSE); + gdk_gc_set_clip_mask(cursor_gc, GDK_CURSOR_FB(last_cursor)->mask); + gdk_gc_set_clip_origin(cursor_gc, last_location.x, last_location.y); + gdk_fb_draw_drawable_2(gdk_parent_root, cursor_gc, GDK_CURSOR_FB(last_cursor)->cursor, + 0, 0, last_location.x, last_location.y, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->width, + GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->height, TRUE, FALSE); + } + else + gdk_fb_cursor_invalidate(); +} + +void +gdk_fb_get_cursor_rect(GdkRectangle *rect) +{ + if(last_cursor) + { + rect->x = last_location.x; + rect->y = last_location.y; + rect->width = GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->width; + rect->height = GDK_DRAWABLE_P(GDK_CURSOR_FB(last_cursor)->cursor)->height; + } + else + { + rect->x = rect->y = -1; + rect->width = rect->height = 0; + } +} + +static void +move_pointer(PS2Mouse *mouse, GdkWindow *in_window) +{ + GdkCursor *the_cursor; + + if(!cursor_gc) + { + GdkColor white, black; + cursor_gc = gdk_gc_new(gdk_parent_root); + gdk_color_black(gdk_colormap_get_system(), &black); + gdk_color_white(gdk_colormap_get_system(), &white); + gdk_gc_set_foreground(cursor_gc, &black); + gdk_gc_set_background(cursor_gc, &white); + } + + gdk_fb_cursor_hide(); + + last_location.x = mouse->x; + last_location.y = mouse->y; + + if(_gdk_fb_pointer_grab_cursor) + the_cursor = _gdk_fb_pointer_grab_cursor; + else + { + while(!GDK_WINDOW_FBDATA(in_window)->cursor && GDK_WINDOW_P(in_window)->parent) + in_window = GDK_WINDOW_P(in_window)->parent; + the_cursor = GDK_WINDOW_FBDATA(in_window)->cursor; + } + + if(the_cursor) + gdk_cursor_ref(the_cursor); + if(last_cursor) + gdk_cursor_unref(last_cursor); + last_cursor = the_cursor; + + gdk_fb_cursor_unhide(); +} + +static gboolean +handle_input(GIOChannel *gioc, GIOCondition cond, gpointer data) +{ + guchar buf[3]; + int n, left, dx=0, dy=0; + PS2Mouse *mouse = data; + gboolean new_button1, new_button2, new_button3; + time_t the_time = g_latest_time.tv_sec; + GdkWindow *mousewin; + + for(left = sizeof(buf); left > 0; ) + { + n = read(mouse->fd, buf+sizeof(buf)-left, left); + g_assert(n > 0); + left -= n; + } + + new_button1 = (buf[0] & 1) && 1; + new_button3 = (buf[0] & 2) && 1; + new_button2 = (buf[0] & 4) && 1; + + if(new_button1 != mouse->button1_pressed) + { + mouse->button1_pressed = new_button1; + send_button_event(mouse, 1, new_button1, the_time); + } + + if(new_button2 != mouse->button2_pressed) + { + mouse->button2_pressed = new_button2; + send_button_event(mouse, 2, new_button2, the_time); + } + + if(new_button3 != mouse->button3_pressed) + { + mouse->button3_pressed = new_button3; + send_button_event(mouse, 3, new_button3, the_time); + } + + if(buf[1] != 0) + dx = ((buf[0] & 0x10) ? ((gint)buf[1])-256 : buf[1]); + else + dx = 0; + if(buf[2] != 0) + dy = -((buf[0] & 0x20) ? ((gint)buf[2])-256 : buf[2]); + else + dy = 0; + + mouse->x += dx; + mouse->y += dy; + if(_gdk_fb_pointer_grab_confine) + mousewin = _gdk_fb_pointer_grab_confine; + else + mousewin = gdk_parent_root; + + if(mouse->x < 0) + mouse->x = 0; + else if(mouse->x > (GDK_DRAWABLE_FBDATA(mousewin)->lim_x - 1)) + mouse->x = GDK_DRAWABLE_FBDATA(mousewin)->lim_x - 1; + if(mouse->y < 0) + mouse->y = 0; + else if(mouse->y > (GDK_DRAWABLE_FBDATA(mousewin)->lim_y - 1)) + mouse->y = GDK_DRAWABLE_FBDATA(mousewin)->lim_y - 1; + + if(dx || dy) { + GdkEvent *event; + gint x, y, rx, ry; + GdkWindow *win; + guint state; + + win = gdk_window_get_pointer(NULL, NULL, NULL, NULL); + move_pointer(mouse, win); + if(_gdk_fb_pointer_grab_window) + win = _gdk_fb_pointer_grab_window; + + gdk_window_get_origin(win, &x, &y); + x = mouse->x - x; + y = mouse->y - y; + + + state = (mouse->button1_pressed?GDK_BUTTON1_MASK:0) + | (mouse->button2_pressed?GDK_BUTTON2_MASK:0) + | (mouse->button3_pressed?GDK_BUTTON3_MASK:0) + | keyboard_get_state(keyboard); + + event = gdk_event_make (win, GDK_MOTION_NOTIFY, TRUE); + if(event) + { + event->motion.x = x; + event->motion.y = y; + event->motion.pressure = 0.5; + event->motion.xtilt = event->motion.ytilt = 0; + event->motion.state = state; + event->motion.is_hint = FALSE; + event->motion.source = GDK_SOURCE_MOUSE; + event->motion.deviceid = 0; + event->motion.x_root = mouse->x; + event->motion.y_root = mouse->y; + } + + if(win != mouse->prev_window) + { + GdkEvent *evel; + + if(mouse->prev_window && (evel = gdk_event_make(mouse->prev_window, GDK_LEAVE_NOTIFY, TRUE))) + { + evel->crossing.subwindow = gdk_window_ref(win); + evel->crossing.x = x; + evel->crossing.y = y; + evel->crossing.x_root = mouse->x; + evel->crossing.y_root = mouse->y; + evel->crossing.mode = GDK_CROSSING_NORMAL; + evel->crossing.detail = GDK_NOTIFY_UNKNOWN; + evel->crossing.focus = FALSE; + evel->crossing.state = state; + } + + evel = gdk_event_make(win, GDK_ENTER_NOTIFY, TRUE); + if(evel) + { + evel->crossing.subwindow = gdk_window_ref(mouse->prev_window?mouse->prev_window:gdk_parent_root); + evel->crossing.x = x; + evel->crossing.y = y; + evel->crossing.x_root = mouse->x; + evel->crossing.y_root = mouse->y; + evel->crossing.mode = GDK_CROSSING_NORMAL; + evel->crossing.detail = GDK_NOTIFY_UNKNOWN; + evel->crossing.focus = FALSE; + evel->crossing.state = state; + } + + if(mouse->prev_window) + gdk_window_unref(mouse->prev_window); + mouse->prev_window = gdk_window_ref(win); + } + } + + return TRUE; +} + +static PS2Mouse * +mouse_open(void) +{ + PS2Mouse *retval = g_new0(PS2Mouse, 1); + guchar buf[7]; + int i = 0; + GIOChannel *gioc; + + retval->fd = open("/dev/psaux", O_RDWR); + if(retval->fd < 0) + { + g_free(retval); + return NULL; + } + + /* From xf86_Mouse.c */ + buf[i++] = 230; /* 1:1 scaling */ + buf[i++] = 244; /* enable mouse */ + buf[i++] = 243; /* Sample rate */ + buf[i++] = 200; + buf[i++] = 232; /* device resolution */ + buf[i++] = 1; + write(retval->fd, buf, i); + read(retval->fd, buf, 3); /* Get rid of misc garbage whatever stuff from mouse */ + + gioc = g_io_channel_unix_new(retval->fd); + retval->fd_tag = g_io_add_watch(gioc, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, handle_input, retval); + + retval->x = gdk_display->modeinfo.xres >> 1; + retval->y = gdk_display->modeinfo.yres >> 1; + + return retval; +} + +void +gdk_input_init (void) +{ + gdk_input_vtable.set_mode = NULL; + gdk_input_vtable.set_axes = NULL; + gdk_input_vtable.set_key = NULL; + gdk_input_vtable.motion_events = NULL; + gdk_input_vtable.get_pointer = gdk_input_ps2_get_pointer; + gdk_input_vtable.grab_pointer = NULL; + gdk_input_vtable.ungrab_pointer = NULL; + gdk_input_vtable.configure_event = NULL; + gdk_input_vtable.enter_event = NULL; + gdk_input_vtable.other_event = NULL; + gdk_input_vtable.window_none_event = NULL; + gdk_input_vtable.enable_window = NULL; + gdk_input_vtable.disable_window = NULL; + + gdk_input_devices = g_list_append (NULL, (GdkDeviceInfo *) &gdk_input_core_info); + + gdk_input_ignore_core = FALSE; + + ps2mouse = mouse_open(); +} + +void +gdk_input_ps2_get_mouseinfo(gint *x, gint *y, GdkModifierType *mask) +{ + *x = ps2mouse->x; + *y = ps2mouse->y; + *mask = + (ps2mouse->button1_pressed?GDK_BUTTON1_MASK:0) + | (ps2mouse->button2_pressed?GDK_BUTTON2_MASK:0) + | (ps2mouse->button3_pressed?GDK_BUTTON3_MASK:0) + | keyboard_get_state(keyboard); +} + +static void +gdk_input_ps2_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask) +{ + gint x_int, y_int; + + gdk_window_get_root_origin(window, &x_int, &y_int); + + if (x) *x = ps2mouse->x - x_int; + if (y) *y = ps2mouse->y - y_int; + if (pressure) *pressure = 0.5; + if (xtilt) *xtilt = 0; + if (ytilt) *ytilt = 0; +} + +/* Returns the modifier mask for the keyboard */ +static guint +keyboard_get_state(Keyboard *k) +{ + guint retval = 0; + struct { + guchar from; + guint to; + } statetrans[] = { + {0x1D, GDK_CONTROL_MASK}, + {0x9D, GDK_CONTROL_MASK}, + {0x38, GDK_MOD1_MASK}, + {0xB8, GDK_MOD1_MASK}, + {0x2A, GDK_SHIFT_MASK}, + {0x36, GDK_SHIFT_MASK} + }; + int i; + + for(i = 0; i < sizeof(statetrans)/sizeof(statetrans[0]); i++) + if(k->states[statetrans[i].from]) + retval |= statetrans[i].to; + + return retval; +} + +static GdkWindow * +gdk_window_find_focus(void) +{ + if(_gdk_fb_keyboard_grab_window) + return _gdk_fb_keyboard_grab_window; + else if(GDK_WINDOW_P(gdk_parent_root)->children) + return GDK_WINDOW_P(gdk_parent_root)->children->data; + else + return gdk_parent_root; +} + +static gboolean +handle_keyboard_input(GIOChannel *gioc, GIOCondition cond, gpointer data) +{ + guchar buf[128]; + int i, n; + Keyboard *k = data; + time_t now; + + n = read(k->fd, buf, sizeof(buf)); + if(n <= 0) + g_error("Nothing from keyboard!"); + + /* Now turn this into a keyboard event */ + now = g_latest_time.tv_sec; + + for(i = 0; i < n; i++) + { + guchar base_char; + static const guint trans_table[256][3] = { + /* 0x00 */ + {0, 0, 0}, + {GDK_Escape, 0, 0}, + {'1', '!', 0}, + {'2', '@', 0}, + {'3', '#', 0}, + {'4', '$', 0}, + {'5', '%', 0}, + {'6', '^', 0}, + {'7', '&', 0}, + {'8', '*', 0}, + {'9', '(', 0}, + {'0', ')', 0}, + {'-', '_', 0}, + {'=', '+', 0}, + {GDK_BackSpace, 0, 0}, + {GDK_Tab, 0, 0}, + /* 0x10 */ + {'q', 'Q', 0}, + {'w', 'W', 0}, + {'e', 'E', 0}, + {'r', 'R', 0}, + {'t', 'T', 0}, + {'y', 'Y', 0}, + {'u', 'U', 0}, + {'i', 'I', 0}, + {'o', 'O', 0}, + {'p', 'P', 0}, + {'[', '{', 0}, + {']', '}', 0}, + {GDK_Return, 0, 0}, + {GDK_Control_L, 0, 0}, /* mod */ + {'a', 'A', 0}, + {'s', 'S', 0}, + + /* 0x20 */ + {'d', 'D', 0}, + {'f', 'F', 0}, + {'g', 'G', 0}, + {'h', 'H', 0}, + {'j', 'J', 0}, + {'k', 'K', 0}, + {'l', 'L', 0}, + {';', ':', 0}, + {'\'', '"', 0}, + {'`', '~', 0}, + {0, 0, 0}, + {GDK_Shift_L, 0, 0}, /* mod */ + {'\\', 0, 0}, + {'z', 0, 0}, + {'x', 0, 0}, + {'c', 0, 0}, + + /* 0x30 */ + {'v', 'V', 0}, + {'b', 'B', 0}, + {'n', 'N', 0}, + {'m', 'M', 0}, + {',', 0, 0}, + {'.', 0, 0}, + {'/', 0, 0}, + {GDK_Shift_R, 0, 0}, /* mod */ + {GDK_KP_Multiply, 0, 0}, + {' ', 0, 0}, + {0, 0, 0}, + {GDK_F1, 0, 0}, + {GDK_F2, 0, 0}, + {GDK_F3, 0, 0}, + {GDK_F4, 0, 0}, + {GDK_F5, 0, 0}, + + /* 0x40 */ + {GDK_F6, 0, 0}, + {GDK_F7, 0, 0}, + {GDK_F8, 0, 0}, + {GDK_F9, 0, 0}, + {GDK_F10, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0x50 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {GDK_F11, 0, 0}, + {GDK_F12, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0x60 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0x70 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0x80 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0x90 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xA0 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xB0 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xC0 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {GDK_Up, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {GDK_Left, 0, 0}, + {GDK_Right, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xD0 */ + {GDK_Down, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xE0 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + + /* 0xF0 */ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + GdkEvent *event; + GdkWindow *win; + char dummy[2]; + int mod; + guint keyval, state; + + if(buf[i] == 0xE0 || k->is_ext) /* extended char */ + { + int l; + + l = k->is_ext?0:1; + k->is_ext = TRUE; + + if((i+l) >= n) + continue; + + if(buf[i+l] == 0x2A + || buf[i+l] == 0xAA) + { + i++; + continue; + } + + base_char = 0x80 + (buf[i+l] & 0x7F); + k->is_ext = FALSE; + i += l; + } + else + base_char = buf[i] & 0x7F; + + if(base_char > sizeof(trans_table)/sizeof(trans_table[0])) + continue; + + { + gboolean new_state = (buf[i] & 0x80)?FALSE:TRUE; + + k->states[base_char] = new_state; + } + + if((base_char == 0x1D) /* left Ctrl */ + || (base_char == 0x9D) /* right Ctrl */ + || (base_char == 0x38) /* left Alt */ + || (base_char == 0xB8) /* right Alt */ + || (base_char == 0x2A) /* left Shift */ + || (base_char == 0x36) /* right Shift */) + { + continue; /* Don't generate events for modifiers */ + } + + if(base_char == 0x3A /* Caps lock */) + { + if(k->states[base_char]) + k->caps_lock = !k->caps_lock; + ioctl(k->fd, KDSETLED, k->caps_lock?LED_CAP:0); + + continue; + } + + if(trans_table[base_char][0] >= GDK_F1 + && trans_table[base_char][0] <= GDK_F35 + && (keyboard_get_state(k) & GDK_MOD1_MASK)) + { + if(!k->states[base_char]) /* Only switch on release */ + { + gint vtnum = trans_table[base_char][0] - GDK_F1 + 1; + + fprintf(debug_out, "Switching VTs\n"); + + /* Do the whole funky VT switch thing */ + ioctl(k->consfd, VT_ACTIVATE, vtnum); + ioctl(k->consfd, VT_WAITACTIVE, k->vtnum); + gdk_fb_redraw_all(); + } + + continue; + } + + keyval = 0; + state = keyboard_get_state(k); + mod = 0; + if(state & GDK_CONTROL_MASK) + mod = 2; + else if(state & GDK_SHIFT_MASK) + mod = 1; + do { + keyval = trans_table[base_char][mod--]; + } while(!keyval && (mod > 0)); + + if(k->caps_lock && (keyval >= 'a') + && (keyval <= 'z')) + keyval = toupper(keyval); + + win = gdk_window_find_focus(); + event = gdk_event_make(win, k->states[base_char]?GDK_KEY_PRESS:GDK_KEY_RELEASE, TRUE); + if(event) + { + /* Find focused window */ + event->key.time = now; + event->key.state = state; + event->key.keyval = keyval; + event->key.length = isprint(event->key.keyval)?1:0; + dummy[0] = event->key.keyval; + dummy[1] = 0; + event->key.string = event->key.length?g_strdup(dummy):NULL; + } + } + + return TRUE; +} + +static Keyboard * +tty_keyboard_open(void) +{ + Keyboard *retval = g_new0(Keyboard, 1); + GIOChannel *gioc; + const char cursoroff_str[] = "\033[?1;0;0c"; + int n; + struct vt_stat vs; + char buf[32]; + struct termios ts; + + setsid(); + retval->consfd = open("/dev/console", O_RDWR); + ioctl(retval->consfd, VT_GETSTATE, &vs); + retval->prev_vtnum = vs.v_active; + g_snprintf(buf, sizeof(buf), "/dev/tty%d", retval->prev_vtnum); + ioctl(retval->consfd, KDSKBMODE, K_XLATE); + + n = ioctl(retval->consfd, VT_OPENQRY, &retval->vtnum); + if(n < 0 || retval->vtnum == -1) + g_error("Cannot allocate VT"); + + ioctl(retval->consfd, VT_ACTIVATE, retval->vtnum); + ioctl(retval->consfd, VT_WAITACTIVE, retval->vtnum); + + debug_out = fdopen(dup(2), "w"); + +#if 0 + close(0); + close(1); + close(2); +#endif + g_snprintf(buf, sizeof(buf), "/dev/tty%d", retval->vtnum); + retval->fd = open(buf, O_RDWR|O_NONBLOCK); + if(retval->fd < 0) + return NULL; + if(ioctl(retval->fd, KDSKBMODE, K_RAW) < 0) + g_warning("K_RAW failed"); + + ioctl(0, TIOCNOTTY, 0); + ioctl(retval->fd, TIOCSCTTY, 0); + tcgetattr(retval->fd, &ts); + ts.c_cc[VTIME] = 0; + ts.c_cc[VMIN] = 1; + ts.c_lflag &= ~(ICANON|ECHO|ISIG); + ts.c_iflag = 0; + tcsetattr(retval->fd, TCSAFLUSH, &ts); + + tcsetpgrp(retval->fd, getpgrp()); + + write(retval->fd, cursoroff_str, strlen(cursoroff_str)); + +#if 0 + if(retval->fd != 0) + dup2(retval->fd, 0); + if(retval->fd != 1) + dup2(retval->fd, 1); + if(retval->fd != 2) + dup2(retval->fd, 2); +#endif + + gioc = g_io_channel_unix_new(retval->fd); + retval->fd_tag = g_io_add_watch(gioc, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, handle_keyboard_input, retval); + + return retval; +} + +void +keyboard_init(void) +{ + keyboard = tty_keyboard_open(); +} + +void +keyboard_shutdown(void) +{ + int tmpfd; + + ioctl(keyboard->fd, KDSKBMODE, K_XLATE); + close(keyboard->fd); + g_source_remove(keyboard->fd_tag); + + tmpfd = keyboard->consfd; + ioctl(tmpfd, VT_ACTIVATE, keyboard->prev_vtnum); + ioctl(tmpfd, VT_WAITACTIVE, keyboard->prev_vtnum); + ioctl(tmpfd, VT_DISALLOCATE, keyboard->vtnum); + close(tmpfd); + + g_free(keyboard); + keyboard = NULL; +} diff --git a/gdk/linux-fb/gdkinput.c b/gdk/linux-fb/gdkinput.c new file mode 100644 index 0000000000..2013ea07c8 --- /dev/null +++ b/gdk/linux-fb/gdkinput.c @@ -0,0 +1,290 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include +#include "config.h" + +#include "gdkfb.h" +#include "gdkinput.h" +#include "gdkprivate.h" +#include "gdkinputprivate.h" + +static const GdkAxisUse gdk_input_core_axes[] = { GDK_AXIS_X, GDK_AXIS_Y }; + +const GdkDeviceInfo gdk_input_core_info = +{ + GDK_CORE_POINTER, + "Core Pointer", + GDK_SOURCE_MOUSE, + GDK_MODE_SCREEN, + TRUE, + 2, + gdk_input_core_axes +}; + +/* Global variables */ + +GdkInputVTable gdk_input_vtable; +/* information about network port and host for gxid daemon */ +gchar *gdk_input_gxid_host; +gint gdk_input_gxid_port; +gint gdk_input_ignore_core; + +GList *gdk_input_devices; +GList *gdk_input_windows; + +GList * +gdk_input_list_devices (void) +{ + return gdk_input_devices; +} + +void +gdk_input_set_source (guint32 deviceid, GdkInputSource source) +{ + GdkDevicePrivate *gdkdev = gdk_input_find_device(deviceid); + g_return_if_fail (gdkdev != NULL); + + gdkdev->info.source = source; +} + +gint +gdk_input_set_mode (guint32 deviceid, GdkInputMode mode) +{ + if (deviceid == GDK_CORE_POINTER) + return FALSE; + + if (gdk_input_vtable.set_mode) + return gdk_input_vtable.set_mode(deviceid,mode); + else + return FALSE; +} + +void +gdk_input_set_axes (guint32 deviceid, GdkAxisUse *axes) +{ + if (deviceid != GDK_CORE_POINTER && gdk_input_vtable.set_axes) + gdk_input_vtable.set_axes (deviceid, axes); +} + +void gdk_input_set_key (guint32 deviceid, + guint index, + guint keyval, + GdkModifierType modifiers) +{ + if (deviceid != GDK_CORE_POINTER && gdk_input_vtable.set_key) + gdk_input_vtable.set_key (deviceid, index, keyval, modifiers); +} + +GdkTimeCoord * +gdk_input_motion_events (GdkWindow *window, + guint32 deviceid, + guint32 start, + guint32 stop, + gint *nevents_return) +{ + g_return_val_if_fail (window != NULL, NULL); + g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + + *nevents_return = 0; + return NULL; +} + +gint +gdk_input_enable_window (GdkWindow *window, GdkDevicePrivate *gdkdev) +{ + if (gdk_input_vtable.enable_window) + return gdk_input_vtable.enable_window (window, gdkdev); + else + return TRUE; +} + +gint +gdk_input_disable_window (GdkWindow *window, GdkDevicePrivate *gdkdev) +{ + if (gdk_input_vtable.disable_window) + return gdk_input_vtable.disable_window(window,gdkdev); + else + return TRUE; +} + + +GdkInputWindow * +gdk_input_window_find(GdkWindow *window) +{ + GList *tmp_list; + + for (tmp_list=gdk_input_windows; tmp_list; tmp_list=tmp_list->next) + if (((GdkInputWindow *)(tmp_list->data))->window == window) + return (GdkInputWindow *)(tmp_list->data); + + return NULL; /* Not found */ +} + +/* FIXME: this routine currently needs to be called between creation + and the corresponding configure event (because it doesn't get the + root_relative_geometry). This should work with + gtk_window_set_extension_events, but will likely fail in other + cases */ + +void +gdk_input_set_extension_events (GdkWindow *window, gint mask, + GdkExtensionMode mode) +{ + GdkWindowPrivate *window_private; + GList *tmp_list; + GdkInputWindow *iw; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + window_private = (GdkWindowPrivate*) window; + if (GDK_DRAWABLE_DESTROYED (window)) + return; + + if (mode == GDK_EXTENSION_EVENTS_NONE) + mask = 0; + + if (mask != 0) + { + iw = g_new(GdkInputWindow,1); + + iw->window = window; + iw->mode = mode; + + iw->obscuring = NULL; + iw->num_obscuring = 0; + iw->grabbed = FALSE; + + gdk_input_windows = g_list_append(gdk_input_windows,iw); + window_private->extension_events = mask; + + /* Add enter window events to the event mask */ + /* FIXME, this is not needed for XINPUT_NONE */ + gdk_window_set_events (window, + gdk_window_get_events (window) | + GDK_ENTER_NOTIFY_MASK); + } + else + { + iw = gdk_input_window_find (window); + if (iw) + { + gdk_input_windows = g_list_remove(gdk_input_windows,iw); + g_free(iw); + } + + window_private->extension_events = 0; + } + + for (tmp_list = gdk_input_devices; tmp_list; tmp_list = tmp_list->next) + { + GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)(tmp_list->data); + + if (gdkdev->info.deviceid != GDK_CORE_POINTER) + { + if (mask != 0 && gdkdev->info.mode != GDK_MODE_DISABLED + && (gdkdev->info.has_cursor || mode == GDK_EXTENSION_EVENTS_ALL)) + gdk_input_enable_window(window,gdkdev); + else + gdk_input_disable_window(window,gdkdev); + } + } +} + +void +gdk_input_window_destroy (GdkWindow *window) +{ + GdkInputWindow *input_window; + + input_window = gdk_input_window_find (window); + g_return_if_fail (input_window != NULL); + + gdk_input_windows = g_list_remove (gdk_input_windows,input_window); + g_free(input_window); +} + +void +gdk_input_exit (void) +{ + GList *tmp_list; + GdkDevicePrivate *gdkdev; + + for (tmp_list = gdk_input_devices; tmp_list; tmp_list = tmp_list->next) + { + gdkdev = (GdkDevicePrivate *)(tmp_list->data); + if (gdkdev->info.deviceid != GDK_CORE_POINTER) + { + gdk_input_set_mode(gdkdev->info.deviceid,GDK_MODE_DISABLED); + + g_free(gdkdev->info.name); +#ifndef XINPUT_NONE + g_free(gdkdev->axes); +#endif + g_free(gdkdev->info.axes); + g_free(gdkdev->info.keys); + g_free(gdkdev); + } + } + + g_list_free(gdk_input_devices); + + for (tmp_list = gdk_input_windows; tmp_list; tmp_list = tmp_list->next) + { + g_free(tmp_list->data); + } + g_list_free(gdk_input_windows); +} + +GdkDevicePrivate * +gdk_input_find_device(guint32 id) +{ + GList *tmp_list = gdk_input_devices; + GdkDevicePrivate *gdkdev; + while (tmp_list) + { + gdkdev = (GdkDevicePrivate *)(tmp_list->data); + if (gdkdev->info.deviceid == id) + return gdkdev; + tmp_list = tmp_list->next; + } + return NULL; +} + +void +gdk_input_window_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask) +{ + if (gdk_input_vtable.get_pointer) + gdk_input_vtable.get_pointer (window, deviceid, x, y, pressure, + xtilt, ytilt, mask); +} diff --git a/gdk/linux-fb/gdkinputprivate.h b/gdk/linux-fb/gdkinputprivate.h new file mode 100644 index 0000000000..d9ce74fdf9 --- /dev/null +++ b/gdk/linux-fb/gdkinputprivate.h @@ -0,0 +1,222 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_INPUTPRIVATE_H__ +#define __GDK_INPUTPRIVATE_H__ + +#include "config.h" +#include "gdkinput.h" +#include "gdkevents.h" +#include "gdkfb.h" + +typedef struct _GdkAxisInfo GdkAxisInfo; +typedef struct _GdkInputVTable GdkInputVTable; +typedef struct _GdkDevicePrivate GdkDevicePrivate; +typedef struct _GdkInputWindow GdkInputWindow; + +struct _GdkInputVTable { + gint (*set_mode) (guint32 deviceid, GdkInputMode mode); + void (*set_axes) (guint32 deviceid, GdkAxisUse *axes); + void (*set_key) (guint32 deviceid, + guint index, + guint keyval, + GdkModifierType modifiers); + + GdkTimeCoord* (*motion_events) (GdkWindow *window, + guint32 deviceid, + guint32 start, + guint32 stop, + gint *nevents_return); + void (*get_pointer) (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask); + gint (*grab_pointer) (GdkWindow * window, + gint owner_events, + GdkEventMask event_mask, + GdkWindow * confine_to, + guint32 time); + void (*ungrab_pointer) (guint32 time); + + void (*configure_event) (GdkEventConfigure *event, GdkWindow *window); + void (*enter_event) (GdkEventCrossing *event, GdkWindow *window); + gint (*other_event) (GdkEvent *event, GdkWindow *window); + /* Handle an unidentified event. Returns TRUE if handled, FALSE + otherwise */ + gint (*window_none_event) (GdkEvent *event); + gint (*enable_window) (GdkWindow *window, GdkDevicePrivate *gdkdev); + gint (*disable_window) (GdkWindow *window, GdkDevicePrivate *gdkdev); +}; + +/* information about a device axis */ +struct _GdkAxisInfo +{ + /* reported x resolution */ + gint xresolution; + + /* reported x minimum/maximum values */ + gint xmin_value, xmax_value; + + /* calibrated resolution (for aspect ration) - only relative values + between axes used */ + gint resolution; + + /* calibrated minimum/maximum values */ + gint min_value, max_value; +}; + +#define GDK_INPUT_NUM_EVENTC 6 + +struct _GdkDevicePrivate { + GdkDeviceInfo info; + +#ifndef XINPUT_NONE + /* information about the axes */ + GdkAxisInfo *axes; + + /* reverse lookup on axis use type */ + gint axis_for_use[GDK_AXIS_LAST]; + + /* Information about XInput device */ + XDevice *xdevice; + + /* minimum key code for device */ + gint min_keycode; + + int buttonpress_type, buttonrelease_type, keypress_type, + keyrelease_type, motionnotify_type, proximityin_type, + proximityout_type, changenotify_type; + + /* true if we need to select a different set of events, but + can't because this is the core pointer */ + gint needs_update; + + /* Mask of buttons (used for button grabs) */ + gint button_state; + + /* true if we've claimed the device as active. (used only for XINPUT_GXI) */ + gint claimed; +#endif /* !XINPUT_NONE */ +}; + +struct _GdkInputWindow +{ + /* gdk window */ + GdkWindow *window; + + /* Extension mode (GDK_EXTENSION_EVENTS_ALL/CURSOR) */ + GdkExtensionMode mode; + + /* position relative to root window */ + gint root_x; + gint root_y; + + /* rectangles relative to window of windows obscuring this one */ + GdkRectangle *obscuring; + gint num_obscuring; + + /* Is there a pointer grab for this window ? */ + gint grabbed; +}; + +/* Global data */ + +extern const GdkDeviceInfo gdk_input_core_info; +extern GList *gdk_input_devices; +extern GList *gdk_input_windows; + +extern GdkInputVTable gdk_input_vtable; +/* information about network port and host for gxid daemon */ +extern gchar *gdk_input_gxid_host; +extern gint gdk_input_gxid_port; +extern gint gdk_input_ignore_core; + +/* Function declarations */ + +GdkDevicePrivate * gdk_input_find_device (guint32 id); +GdkInputWindow * gdk_input_window_find (GdkWindow *window); +void gdk_input_window_destroy (GdkWindow *window); +void gdk_input_init (void); +void gdk_input_exit (void); +gint gdk_input_enable_window (GdkWindow *window, + GdkDevicePrivate *gdkdev); +gint gdk_input_disable_window (GdkWindow *window, + GdkDevicePrivate *gdkdev); + +#ifndef XINPUT_NONE + +#define GDK_MAX_DEVICE_CLASSES 13 + +gint gdk_input_common_init (gint include_core); +void gdk_input_get_root_relative_geometry (Display *dpy, + Window w, + int *x_ret, + int *y_ret, + int *width_ret, + int *height_ret); +void gdk_input_common_find_events (GdkWindow *window, + GdkDevicePrivate *gdkdev, + gint mask, + XEventClass *classes, + int *num_classes); +void gdk_input_common_select_events (GdkWindow *window, + GdkDevicePrivate *gdkdev); +gint gdk_input_common_other_event (GdkEvent *event, + XEvent *xevent, + GdkInputWindow *input_window, + GdkDevicePrivate *gdkdev); +void gdk_input_common_get_pointer (GdkWindow *window, + guint32 deviceid, + gdouble *x, + gdouble *y, + gdouble *pressure, + gdouble *xtilt, + gdouble *ytilt, + GdkModifierType *mask); +void gdk_input_common_set_key (guint32 deviceid, + guint index, + guint keyval, + GdkModifierType modifiers); +void gdk_input_common_set_axes (guint32 deviceid, + GdkAxisUse *axes); +GdkTimeCoord * gdk_input_common_motion_events (GdkWindow *window, + guint32 deviceid, + guint32 start, + guint32 stop, + gint *nevents_return); + +#endif /* !XINPUT_NONE */ + +GdkDevicePrivate *gdk_input_find_device (guint32 id); +GdkInputWindow *gdk_input_window_find (GdkWindow *window); +void gdk_input_window_destroy (GdkWindow *window); +void gdk_input_exit (void); + +#endif /* __GDK_INPUTPRIVATE_H__ */ diff --git a/gdk/linux-fb/gdkmain-fb.c b/gdk/linux-fb/gdkmain-fb.c new file mode 100644 index 0000000000..5fd7bb8800 --- /dev/null +++ b/gdk/linux-fb/gdkmain-fb.c @@ -0,0 +1,575 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gdk.h" + +#include "gdkprivate-fb.h" +#include "gdkinternals.h" + +/* + * Private function declarations + */ + +#ifndef HAVE_XCONVERTCASE +static void gdkx_XConvertCase (KeySym symbol, + KeySym *lower, + KeySym *upper); +#define XConvertCase gdkx_XConvertCase +#endif + +/* Private variable declarations + */ +static int gdk_initialized = 0; /* 1 if the library is initialized, + * 0 otherwise. + */ + +#ifdef G_ENABLE_DEBUG +static const GDebugKey gdk_debug_keys[] = { + {"misc", GDK_DEBUG_MISC}, + {"events", GDK_DEBUG_EVENTS}, +}; + +static const int gdk_ndebug_keys = sizeof(gdk_debug_keys)/sizeof(GDebugKey); + +#endif /* G_ENABLE_DEBUG */ + +GdkArgDesc _gdk_windowing_args[] = { + { NULL } +}; + +static GdkFBDisplay * +gdk_fb_display_new(const char *filename) +{ + int fd, n; + GdkFBDisplay *retval; + guint16 red[256], green[256], blue[256]; + struct fb_cmap cmap; + + fd = open(filename, O_RDWR); + if(fd < 0) + return NULL; + + retval = g_new0(GdkFBDisplay, 1); + retval->fd = fd; + n = ioctl(fd, FBIOGET_FSCREENINFO, &retval->sinfo); + n |= ioctl(fd, FBIOGET_VSCREENINFO, &retval->modeinfo); + g_assert(!n); + + retval->fbmem = mmap(NULL, retval->sinfo.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + g_assert(retval->fbmem != MAP_FAILED); + + for(n = 0; n < 16; n++) + red[n] = green[n] = blue[n] = n << 12; + for(n = 16; n < 256; n++) + red[n] = green[n] = blue[n] = n << 8; + cmap.red = red; cmap.green = green; cmap.blue = blue; cmap.len = 256; cmap.start = 0; + ioctl(fd, FBIOPUTCMAP, &cmap); + + return retval; +} + +static void +gdk_fb_display_destroy(GdkFBDisplay *fbd) +{ + munmap(fbd->fbmem, fbd->sinfo.smem_len); + g_free(fbd); +} + +extern void keyboard_init(void); + +gboolean +_gdk_windowing_init_check (int argc, char **argv) +{ + if(gdk_initialized) + return TRUE; + + keyboard_init(); + gdk_display = gdk_fb_display_new("/dev/fb"); + + if(!gdk_display) + return FALSE; + + T1_InitLib(NO_LOGFILE|IGNORE_FONTDATABASE); + T1_AASetBitsPerPixel(gdk_display->modeinfo.bits_per_pixel); + + gdk_initialized = TRUE; + + return TRUE; +} + +/* + *-------------------------------------------------------------- + * gdk_pointer_grab + * + * Grabs the pointer to a specific window + * + * Arguments: + * "window" is the window which will receive the grab + * "owner_events" specifies whether events will be reported as is, + * or relative to "window" + * "event_mask" masks only interesting events + * "confine_to" limits the cursor movement to the specified window + * "cursor" changes the cursor for the duration of the grab + * "time" specifies the time + * + * Results: + * + * Side effects: + * requires a corresponding call to gdk_pointer_ungrab + * + *-------------------------------------------------------------- + */ + +gint +gdk_pointer_grab (GdkWindow * window, + gint owner_events, + GdkEventMask event_mask, + GdkWindow * confine_to, + GdkCursor * cursor, + guint32 time) +{ + g_return_val_if_fail (window != NULL, 0); + g_return_val_if_fail (GDK_IS_WINDOW (window), 0); + g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0); + + if(_gdk_fb_pointer_grab_window) + return 1; + + if(!owner_events) + _gdk_fb_pointer_grab_window = gdk_window_ref(window); + + _gdk_fb_pointer_grab_confine = confine_to?gdk_window_ref(confine_to):NULL; + _gdk_fb_pointer_grab_events = event_mask; + _gdk_fb_pointer_grab_cursor = cursor?gdk_cursor_ref(cursor):NULL; + + return 0; +} + +/* + *-------------------------------------------------------------- + * gdk_pointer_ungrab + * + * Releases any pointer grab + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +void +gdk_pointer_ungrab (guint32 time) +{ + if(_gdk_fb_pointer_grab_window) + gdk_window_unref(_gdk_fb_pointer_grab_window); + _gdk_fb_pointer_grab_window = NULL; + + if(_gdk_fb_pointer_grab_confine) + gdk_window_unref(_gdk_fb_pointer_grab_confine); + _gdk_fb_pointer_grab_confine = NULL; + + if(_gdk_fb_pointer_grab_cursor) + gdk_cursor_unref(_gdk_fb_pointer_grab_cursor); + _gdk_fb_pointer_grab_cursor = NULL; +} + +/* + *-------------------------------------------------------------- + * gdk_pointer_is_grabbed + * + * Tell wether there is an active x pointer grab in effect + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gint +gdk_pointer_is_grabbed (void) +{ + return _gdk_fb_pointer_grab_window != NULL; +} + +/* + *-------------------------------------------------------------- + * gdk_keyboard_grab + * + * Grabs the keyboard to a specific window + * + * Arguments: + * "window" is the window which will receive the grab + * "owner_events" specifies whether events will be reported as is, + * or relative to "window" + * "time" specifies the time + * + * Results: + * + * Side effects: + * requires a corresponding call to gdk_keyboard_ungrab + * + *-------------------------------------------------------------- + */ + +gint +gdk_keyboard_grab (GdkWindow * window, + gint owner_events, + guint32 time) +{ + g_return_val_if_fail (window != NULL, 0); + g_return_val_if_fail (GDK_IS_WINDOW (window), 0); + + if(_gdk_fb_pointer_grab_window) + gdk_keyboard_ungrab(time); + + if(!owner_events) + _gdk_fb_keyboard_grab_window = gdk_window_ref(window); + + return 0; +} + +/* + *-------------------------------------------------------------- + * gdk_keyboard_ungrab + * + * Releases any keyboard grab + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +void +gdk_keyboard_ungrab (guint32 time) +{ + if(_gdk_fb_keyboard_grab_window) + gdk_window_unref(_gdk_fb_keyboard_grab_window); + _gdk_fb_keyboard_grab_window = NULL; +} + +/* + *-------------------------------------------------------------- + * gdk_screen_width + * + * Return the width of the screen. + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gint +gdk_screen_width (void) +{ + return gdk_display->modeinfo.xres; +} + +/* + *-------------------------------------------------------------- + * gdk_screen_height + * + * Return the height of the screen. + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gint +gdk_screen_height (void) +{ + return gdk_display->modeinfo.yres; +} + +/* + *-------------------------------------------------------------- + * gdk_screen_width_mm + * + * Return the width of the screen in millimeters. + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gint +gdk_screen_width_mm (void) +{ + return 0.5 + gdk_screen_width () * (25.4 / 72.); +} + +/* + *-------------------------------------------------------------- + * gdk_screen_height + * + * Return the height of the screen in millimeters. + * + * Arguments: + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +gint +gdk_screen_height_mm (void) +{ + return 0.5 + gdk_screen_height () * (25.4 / 72.); +} + +/* + *-------------------------------------------------------------- + * gdk_set_sm_client_id + * + * Set the SM_CLIENT_ID property on the WM_CLIENT_LEADER window + * so that the window manager can save our state using the + * X11R6 ICCCM session management protocol. A NULL value should + * be set following disconnection from the session manager to + * remove the SM_CLIENT_ID property. + * + * Arguments: + * + * "sm_client_id" specifies the client id assigned to us by the + * session manager or NULL to remove the property. + * + * Results: + * + * Side effects: + * + *-------------------------------------------------------------- + */ + +void +gdk_set_sm_client_id (const gchar* sm_client_id) +{ +} + +void +gdk_key_repeat_disable (void) +{ +} + +void +gdk_key_repeat_restore (void) +{ +} + + +void +gdk_beep (void) +{ +} + +extern void keyboard_shutdown(void); + +void +gdk_windowing_exit (void) +{ + gdk_fb_display_destroy(gdk_display); gdk_display = NULL; + + T1_CloseLib(); + + keyboard_shutdown(); +} + +gchar* +gdk_keyval_name (guint keyval) +{ + return NULL; +} + +guint +gdk_keyval_from_name (const gchar *keyval_name) +{ + return 0; +} + +void gdk_keyval_convert_case (guint symbol, + guint *lower, + guint *upper) +{ + if(symbol >= 'a' && symbol <= 'z') + symbol = toupper(symbol); + + if(upper) + *upper = symbol; + + if(lower) + *lower = symbol; +} + +gchar * +gdk_get_display(void) +{ + return g_strdup("/dev/fb0"); +} + +/* utils */ +GdkEvent * +gdk_event_make(GdkWindow *window, GdkEventType type, gboolean append_to_queue) +{ + static const guint type_masks[] = { + GDK_SUBSTRUCTURE_MASK, /* GDK_DELETE = 0, */ + GDK_STRUCTURE_MASK, /* GDK_DESTROY = 1, */ + GDK_EXPOSURE_MASK, /* GDK_EXPOSE = 2, */ + GDK_POINTER_MOTION_MASK|GDK_BUTTON_MOTION_MASK, /* GDK_MOTION_NOTIFY = 3, */ + GDK_BUTTON_PRESS_MASK, /* GDK_BUTTON_PRESS = 4, */ + GDK_BUTTON_PRESS_MASK, /* GDK_2BUTTON_PRESS = 5, */ + GDK_BUTTON_PRESS_MASK, /* GDK_3BUTTON_PRESS = 6, */ + GDK_BUTTON_RELEASE_MASK, /* GDK_BUTTON_RELEASE = 7, */ + GDK_KEY_PRESS_MASK, /* GDK_KEY_PRESS = 8, */ + GDK_KEY_RELEASE_MASK, /* GDK_KEY_RELEASE = 9, */ + GDK_ENTER_NOTIFY_MASK, /* GDK_ENTER_NOTIFY = 10, */ + GDK_LEAVE_NOTIFY_MASK, /* GDK_LEAVE_NOTIFY = 11, */ + GDK_FOCUS_CHANGE_MASK, /* GDK_FOCUS_CHANGE = 12, */ + GDK_STRUCTURE_MASK, /* GDK_CONFIGURE = 13, */ + GDK_VISIBILITY_NOTIFY_MASK, /* GDK_MAP = 14, */ + GDK_VISIBILITY_NOTIFY_MASK, /* GDK_UNMAP = 15, */ + GDK_PROPERTY_CHANGE_MASK, /* GDK_PROPERTY_NOTIFY = 16, */ + GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_CLEAR = 17, */ + GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_REQUEST = 18, */ + GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_NOTIFY = 19, */ + GDK_PROXIMITY_IN_MASK, /* GDK_PROXIMITY_IN = 20, */ + GDK_PROXIMITY_OUT_MASK, /* GDK_PROXIMITY_OUT = 21, */ + GDK_ALL_EVENTS_MASK, /* GDK_DRAG_ENTER = 22, */ + GDK_ALL_EVENTS_MASK, /* GDK_DRAG_LEAVE = 23, */ + GDK_ALL_EVENTS_MASK, /* GDK_DRAG_MOTION = 24, */ + GDK_ALL_EVENTS_MASK, /* GDK_DRAG_STATUS = 25, */ + GDK_ALL_EVENTS_MASK, /* GDK_DROP_START = 26, */ + GDK_ALL_EVENTS_MASK, /* GDK_DROP_FINISHED = 27, */ + GDK_ALL_EVENTS_MASK, /* GDK_CLIENT_EVENT = 28, */ + GDK_VISIBILITY_NOTIFY_MASK, /* GDK_VISIBILITY_NOTIFY = 29, */ + GDK_EXPOSURE_MASK, /* GDK_NO_EXPOSE = 30, */ + GDK_SCROLL_MASK /* GDK_SCROLL = 31 */ + }; + + if(GDK_WINDOW_FBDATA(window)->event_mask & type_masks[type]) + { + GdkEvent *event = gdk_event_new(); + guint32 the_time = g_latest_time.tv_sec; + + event->any.type = type; + event->any.window = gdk_window_ref(window); + switch(type) + { + case GDK_MOTION_NOTIFY: + event->motion.time = the_time; + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + event->button.time = the_time; + break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + event->key.time = the_time; + break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + event->crossing.time = the_time; + break; + + case GDK_PROPERTY_NOTIFY: + event->property.time = the_time; + break; + + case GDK_SELECTION_CLEAR: + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + event->selection.time = the_time; + break; + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + event->proximity.time = the_time; + break; + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DRAG_STATUS: + case GDK_DROP_START: + case GDK_DROP_FINISHED: + event->dnd.time = the_time; + break; + + case GDK_FOCUS_CHANGE: + case GDK_CONFIGURE: + case GDK_MAP: + case GDK_UNMAP: + case GDK_CLIENT_EVENT: + case GDK_VISIBILITY_NOTIFY: + case GDK_NO_EXPOSE: + case GDK_SCROLL: + case GDK_DELETE: + case GDK_DESTROY: + case GDK_EXPOSE: + default: + break; + } + + if(append_to_queue) + gdk_event_queue_append(event); + + return event; + } + + return NULL; +} diff --git a/gdk/linux-fb/gdkpixmap-fb.c b/gdk/linux-fb/gdkpixmap-fb.c new file mode 100644 index 0000000000..30c6d2bdcb --- /dev/null +++ b/gdk/linux-fb/gdkpixmap-fb.c @@ -0,0 +1,768 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include +#include +#include +/* Needed for SEEK_END in SunOS */ +#include + +#include "gdkpixmap.h" +#include "gdkfb.h" +#include "gdkprivate-fb.h" + +typedef struct +{ + gchar *color_string; + GdkColor color; + gint transparent; +} _GdkPixmapColor; + +typedef struct +{ + guint ncolors; + GdkColormap *colormap; + gulong pixels[1]; +} _GdkPixmapInfo; + +static void +gdk_fb_pixmap_destroy (GdkPixmap *pixmap) +{ + g_free (GDK_DRAWABLE_FBDATA(pixmap)->mem); + g_free (GDK_DRAWABLE_FBDATA (pixmap)); +} + +static GdkDrawable * +gdk_fb_pixmap_alloc (void) +{ + GdkDrawable *drawable; + GdkDrawablePrivate *private; + + static GdkDrawableClass klass; + static gboolean initialized = FALSE; + + if (!initialized) + { + initialized = TRUE; + + klass = _gdk_fb_drawable_class; + klass.destroy = gdk_fb_pixmap_destroy; + } + + drawable = gdk_drawable_alloc (); + private = (GdkDrawablePrivate *)drawable; + + private->klass = &klass; + private->klass_data = g_new0 (GdkDrawableFBData, 1); + private->window_type = GDK_DRAWABLE_PIXMAP; + private->colormap = gdk_colormap_ref(gdk_colormap_get_system()); + + return drawable; +} + +GdkPixmap* +gdk_pixmap_new (GdkWindow *window, + gint width, + gint height, + gint depth) +{ + GdkPixmap *pixmap; + GdkDrawablePrivate *private; + + g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL); + g_return_val_if_fail ((window != NULL) || (depth != -1), NULL); + g_return_val_if_fail ((width != 0) && (height != 0), NULL); + + if (!window) + window = gdk_parent_root; + + if (GDK_DRAWABLE_DESTROYED (window)) + return NULL; + + if (depth == -1) + depth = gdk_drawable_get_visual (window)->depth; + + pixmap = gdk_fb_pixmap_alloc (); + private = (GdkDrawablePrivate *)pixmap; + + GDK_DRAWABLE_FBDATA(pixmap)->mem = g_malloc(((width * depth + 7) / 8) * height); + GDK_DRAWABLE_FBDATA(pixmap)->rowstride = (width * depth + 7) / 8; /* Round up to nearest whole byte */ + GDK_DRAWABLE_FBDATA(pixmap)->lim_x = width; + GDK_DRAWABLE_FBDATA(pixmap)->lim_y = height; + private->width = width; + private->height = height; + private->depth = depth; + + return pixmap; +} + +GdkPixmap * +gdk_bitmap_create_from_data (GdkWindow *window, + const gchar *data, + gint width, + gint height) +{ + GdkPixmap *pixmap; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail ((width != 0) && (height != 0), NULL); + g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL); + + if (!window) + window = gdk_parent_root; + + if (GDK_DRAWABLE_DESTROYED (window)) + return NULL; + + pixmap = gdk_pixmap_new(window, width, height, 1); + + memcpy(GDK_DRAWABLE_FBDATA(pixmap)->mem, data, ((width + 7) / 8) * height); + + return pixmap; +} + +GdkPixmap* +gdk_pixmap_create_from_data (GdkWindow *window, + const gchar *data, + gint width, + gint height, + gint depth, + GdkColor *fg, + GdkColor *bg) +{ + GdkPixmap *pixmap; + GdkDrawablePrivate *private; + + g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (fg != NULL, NULL); + g_return_val_if_fail (bg != NULL, NULL); + g_return_val_if_fail ((window != NULL) || (depth != -1), NULL); + g_return_val_if_fail ((width != 0) && (height != 0), NULL); + + if (!window) + window = gdk_parent_root; + + if (GDK_DRAWABLE_DESTROYED (window)) + return NULL; + + if (depth == -1) + depth = gdk_drawable_get_visual (window)->depth; + + pixmap = gdk_pixmap_new(window, width, height, depth); + + private = (GdkDrawablePrivate *)pixmap; + + + return pixmap; +} + +static gint +gdk_pixmap_seek_string (FILE *infile, + const gchar *str, + gint skip_comments) +{ + char instr[1024]; + + while (!feof (infile)) + { + fscanf (infile, "%1023s", instr); + if (skip_comments == TRUE && strcmp (instr, "/*") == 0) + { + fscanf (infile, "%1023s", instr); + while (!feof (infile) && strcmp (instr, "*/") != 0) + fscanf (infile, "%1023s", instr); + fscanf(infile, "%1023s", instr); + } + if (strcmp (instr, str)==0) + return TRUE; + } + + return FALSE; +} + +static gint +gdk_pixmap_seek_char (FILE *infile, + gchar c) +{ + gint b, oldb; + + while ((b = getc(infile)) != EOF) + { + if (c != b && b == '/') + { + b = getc (infile); + if (b == EOF) + return FALSE; + else if (b == '*') /* we have a comment */ + { + b = -1; + do + { + oldb = b; + b = getc (infile); + if (b == EOF) + return FALSE; + } + while (!(oldb == '*' && b == '/')); + } + } + else if (c == b) + return TRUE; + } + return FALSE; +} + +static gint +gdk_pixmap_read_string (FILE *infile, + gchar **buffer, + guint *buffer_size) +{ + gint c; + guint cnt = 0, bufsiz, ret = FALSE; + gchar *buf; + + buf = *buffer; + bufsiz = *buffer_size; + if (buf == NULL) + { + bufsiz = 10 * sizeof (gchar); + buf = g_new(gchar, bufsiz); + } + + do + c = getc (infile); + while (c != EOF && c != '"'); + + if (c != '"') + goto out; + + while ((c = getc(infile)) != EOF) + { + if (cnt == bufsiz) + { + guint new_size = bufsiz * 2; + if (new_size > bufsiz) + bufsiz = new_size; + else + goto out; + + buf = (gchar *) g_realloc (buf, bufsiz); + buf[bufsiz-1] = '\0'; + } + + if (c != '"') + buf[cnt++] = c; + else + { + buf[cnt] = 0; + ret = TRUE; + break; + } + } + + out: + buf[bufsiz-1] = '\0'; /* ensure null termination for errors */ + *buffer = buf; + *buffer_size = bufsiz; + return ret; +} + +static gchar* +gdk_pixmap_skip_whitespaces (gchar *buffer) +{ + gint32 index = 0; + + while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09)) + index++; + + return &buffer[index]; +} + +static gchar* +gdk_pixmap_skip_string (gchar *buffer) +{ + gint32 index = 0; + + while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09) + index++; + + return &buffer[index]; +} + +/* Xlib crashed once at a color name lengths around 125 */ +#define MAX_COLOR_LEN 120 + +static gchar* +gdk_pixmap_extract_color (gchar *buffer) +{ + gint counter, numnames; + gchar *ptr = NULL, ch, temp[128]; + gchar color[MAX_COLOR_LEN], *retcol; + gint space; + + counter = 0; + while (ptr == NULL) + { + if (buffer[counter] == 'c') + { + ch = buffer[counter + 1]; + if (ch == 0x20 || ch == 0x09) + ptr = &buffer[counter + 1]; + } + else if (buffer[counter] == 0) + return NULL; + + counter++; + } + + ptr = gdk_pixmap_skip_whitespaces (ptr); + + if (ptr[0] == 0) + return NULL; + else if (ptr[0] == '#') + { + counter = 1; + while (ptr[counter] != 0 && + ((ptr[counter] >= '0' && ptr[counter] <= '9') || + (ptr[counter] >= 'a' && ptr[counter] <= 'f') || + (ptr[counter] >= 'A' && ptr[counter] <= 'F'))) + counter++; + + retcol = g_new (gchar, counter+1); + strncpy (retcol, ptr, counter); + + retcol[counter] = 0; + + return retcol; + } + + color[0] = 0; + numnames = 0; + + space = MAX_COLOR_LEN - 1; + while (space > 0) + { + sscanf (ptr, "%127s", temp); + + if (((gint)ptr[0] == 0) || + (strcmp ("s", temp) == 0) || (strcmp ("m", temp) == 0) || + (strcmp ("g", temp) == 0) || (strcmp ("g4", temp) == 0)) + { + break; + } + else + { + if (numnames > 0) + { + space -= 1; + strcat (color, " "); + } + strncat (color, temp, space); + space -= MIN (space, strlen (temp)); + ptr = gdk_pixmap_skip_string (ptr); + ptr = gdk_pixmap_skip_whitespaces (ptr); + numnames++; + } + } + + retcol = g_strdup (color); + return retcol; +} + + +enum buffer_op +{ + op_header, + op_cmap, + op_body +}; + + +static void +gdk_xpm_destroy_notify (gpointer data) +{ + _GdkPixmapInfo *info = (_GdkPixmapInfo *)data; + GdkColor color; + int i; + + for (i=0; incolors; i++) + { + color.pixel = info->pixels[i]; + gdk_colormap_free_colors (info->colormap, &color, 1); + } + + gdk_colormap_unref (info->colormap); + g_free (info); +} + +static GdkPixmap * +_gdk_pixmap_create_from_xpm (GdkWindow *window, + GdkColormap *colormap, + GdkBitmap **mask, + GdkColor *transparent_color, + gchar * (*get_buf) (enum buffer_op op, + gpointer handle), + gpointer handle) +{ + GdkPixmap *pixmap = NULL; + GdkImage *image = NULL; + GdkVisual *visual; + GdkGC *gc = NULL; + GdkColor tmp_color; + gint width, height, num_cols, cpp, n, ns, cnt, xcnt, ycnt, wbytes; + gchar *buffer, pixel_str[32]; + gchar *name_buf; + _GdkPixmapColor *color = NULL, *fallbackcolor = NULL; + _GdkPixmapColor *colors = NULL; + gulong index; + GHashTable *color_hash = NULL; + _GdkPixmapInfo *color_info = NULL; + + if ((window == NULL) && (colormap == NULL)) + g_warning ("Creating pixmap from xpm with NULL window and colormap"); + + if (window == NULL) + window = gdk_parent_root; + + if (colormap == NULL) + { + colormap = gdk_drawable_get_colormap (window); + visual = gdk_drawable_get_visual (window); + } + else + visual = ((GdkColormapPrivate *)colormap)->visual; + + buffer = (*get_buf) (op_header, handle); + if (buffer == NULL) + return NULL; + + sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp); + if (cpp >= 32) + { + g_warning ("Pixmap has more than 31 characters per color\n"); + return NULL; + } + + color_hash = g_hash_table_new (g_str_hash, g_str_equal); + + if (transparent_color == NULL) + { + gdk_color_white (colormap, &tmp_color); + transparent_color = &tmp_color; + } + + /* For pseudo-color and grayscale visuals, we have to remember + * the colors we allocated, so we can free them later. + */ + if ((visual->type == GDK_VISUAL_PSEUDO_COLOR) || + (visual->type == GDK_VISUAL_GRAYSCALE)) + { + color_info = g_malloc (sizeof (_GdkPixmapInfo) + + sizeof(gulong) * (num_cols - 1)); + color_info->ncolors = num_cols; + color_info->colormap = colormap; + gdk_colormap_ref (colormap); + } + + name_buf = g_new (gchar, num_cols * (cpp+1)); + colors = g_new (_GdkPixmapColor, num_cols); + + for (cnt = 0; cnt < num_cols; cnt++) + { + gchar *color_name; + + buffer = (*get_buf) (op_cmap, handle); + if (buffer == NULL) + goto error; + + color = &colors[cnt]; + color->color_string = &name_buf [cnt * (cpp + 1)]; + strncpy (color->color_string, buffer, cpp); + color->color_string[cpp] = 0; + buffer += strlen (color->color_string); + color->transparent = FALSE; + + color_name = gdk_pixmap_extract_color (buffer); + + if (color_name == NULL || g_strcasecmp (color_name, "None") == 0 || + gdk_color_parse (color_name, &color->color) == FALSE) + { + color->color = *transparent_color; + color->transparent = TRUE; + } + + g_free (color_name); + + /* FIXME: The remaining slowness appears to happen in this + function. */ + gdk_color_alloc (colormap, &color->color); + + if (color_info) + color_info->pixels[cnt] = color->color.pixel; + + g_hash_table_insert (color_hash, color->color_string, color); + if (cnt == 0) + fallbackcolor = color; + } + + index = 0; + image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height); + + if (mask) + { + /* The pixmap mask is just a bits pattern. + * Color 0 is used for background and 1 for foreground. + * We don't care about the colormap, we just need 0 and 1. + */ + GdkColor mask_pattern; + + *mask = gdk_pixmap_new (window, width, height, 1); + gc = gdk_gc_new (*mask); + + mask_pattern.pixel = 0; + gdk_gc_set_foreground (gc, &mask_pattern); + gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, width, height); + + mask_pattern.pixel = 255; + gdk_gc_set_foreground (gc, &mask_pattern); + } + + wbytes = width * cpp; + for (ycnt = 0; ycnt < height; ycnt++) + { + buffer = (*get_buf) (op_body, handle); + + /* FIXME: this slows things down a little - it could be + * integrated into the strncpy below, perhaps. OTOH, strlen + * is fast. + */ + if ((buffer == NULL) || strlen (buffer) < wbytes) + continue; + + for (n = 0, cnt = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) + { + strncpy (pixel_str, &buffer[n], cpp); + pixel_str[cpp] = 0; + ns = 0; + + color = g_hash_table_lookup (color_hash, pixel_str); + + if (!color) /* screwed up XPM file */ + color = fallbackcolor; + + gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel); + + if (mask && color->transparent) + { + if (cnt < xcnt) + gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt); + cnt = xcnt + 1; + } + } + + if (mask && (cnt < xcnt)) + gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt); + } + + error: + + if (mask) + gdk_gc_unref (gc); + + if (image != NULL) + { + pixmap = gdk_pixmap_new (window, width, height, visual->depth); + + if (color_info) + gdk_drawable_set_data (pixmap, "gdk-xpm", color_info, + gdk_xpm_destroy_notify); + + gc = gdk_gc_new (pixmap); + gdk_gc_set_foreground (gc, transparent_color); + gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height); + gdk_gc_unref (gc); + gdk_image_unref (image); + +#if 0 + g_print("%dx%d\n", width, height); + for(y = 0; y < height; y++) + { + for(x = 0; x < width; x++) + { + guchar foo = GDK_DRAWABLE_FBDATA(pixmap)->mem[(x + GDK_DRAWABLE_FBDATA(pixmap)->rowstride * y]; + if(foo == 0) + g_print("o"); + else if(foo == 255) + g_print("w"); + else if(foo == transparent_color->pixel) + g_print(" "); + else + g_print("."); + } + g_print("\n"); + } +#endif + } + else if (color_info) + gdk_xpm_destroy_notify (color_info); + + if (color_hash != NULL) + g_hash_table_destroy (color_hash); + + if (colors != NULL) + g_free (colors); + + if (name_buf != NULL) + g_free (name_buf); + + return pixmap; +} + + +struct file_handle +{ + FILE *infile; + gchar *buffer; + guint buffer_size; +}; + + +static gchar * +file_buffer (enum buffer_op op, gpointer handle) +{ + struct file_handle *h = handle; + + switch (op) + { + case op_header: + if (gdk_pixmap_seek_string (h->infile, "XPM", FALSE) != TRUE) + break; + + if (gdk_pixmap_seek_char (h->infile,'{') != TRUE) + break; + /* Fall through to the next gdk_pixmap_seek_char. */ + + case op_cmap: + gdk_pixmap_seek_char (h->infile, '"'); + fseek (h->infile, -1, SEEK_CUR); + /* Fall through to the gdk_pixmap_read_string. */ + + case op_body: + gdk_pixmap_read_string (h->infile, &h->buffer, &h->buffer_size); + return h->buffer; + } + return 0; +} + + +GdkPixmap* +gdk_pixmap_colormap_create_from_xpm (GdkWindow *window, + GdkColormap *colormap, + GdkBitmap **mask, + GdkColor *transparent_color, + const gchar *filename) +{ + struct file_handle h; + GdkPixmap *pixmap = NULL; + + memset (&h, 0, sizeof (h)); + h.infile = fopen (filename, "rb"); + if (h.infile != NULL) + { + pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask, + transparent_color, + file_buffer, &h); + fclose (h.infile); + g_free (h.buffer); + } + + return pixmap; +} + +GdkPixmap* +gdk_pixmap_create_from_xpm (GdkWindow *window, + GdkBitmap **mask, + GdkColor *transparent_color, + const gchar *filename) +{ + return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask, + transparent_color, filename); +} + + +struct mem_handle +{ + gchar **data; + int offset; +}; + + +static gchar * +mem_buffer (enum buffer_op op, gpointer handle) +{ + struct mem_handle *h = handle; + switch (op) + { + case op_header: + case op_cmap: + case op_body: + if (h->data[h->offset]) + return h->data[h->offset ++]; + } + return 0; +} + + +GdkPixmap* +gdk_pixmap_colormap_create_from_xpm_d (GdkWindow *window, + GdkColormap *colormap, + GdkBitmap **mask, + GdkColor *transparent_color, + gchar **data) +{ + struct mem_handle h; + GdkPixmap *pixmap = NULL; + + memset (&h, 0, sizeof (h)); + h.data = data; + pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask, + transparent_color, + mem_buffer, &h); + return pixmap; +} + + +GdkPixmap* +gdk_pixmap_create_from_xpm_d (GdkWindow *window, + GdkBitmap **mask, + GdkColor *transparent_color, + gchar **data) +{ + return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask, + transparent_color, data); +} diff --git a/gdk/linux-fb/gdkpoly-generic.h b/gdk/linux-fb/gdkpoly-generic.h new file mode 100644 index 0000000000..660c689adb --- /dev/null +++ b/gdk/linux-fb/gdkpoly-generic.h @@ -0,0 +1,291 @@ +/* $TOG: poly.h /main/5 1998/02/06 17:47:27 kaleb $ */ +/************************************************************************ + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +/* + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor_axis; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2) + + + +/* + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counter-clockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* + * for the winding number rule + */ +#define CLOCKWISE 1 +#define COUNTERCLOCKWISE -1 + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + + + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} diff --git a/gdk/linux-fb/gdkpolyreg-generic.c b/gdk/linux-fb/gdkpolyreg-generic.c new file mode 100644 index 0000000000..b98bd5641e --- /dev/null +++ b/gdk/linux-fb/gdkpolyreg-generic.c @@ -0,0 +1,616 @@ +/* $TOG: PolyReg.c /main/15 1998/02/06 17:47:08 kaleb $ */ +/************************************************************************ + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* $XFree86: xc/lib/X11/PolyReg.c,v 1.4 1998/10/03 08:41:21 dawes Exp $ */ + +#define LARGE_COORDINATE 1000000 +#define SMALL_COORDINATE -LARGE_COORDINATE + +#include +#include "gdkregion-generic.h" +#include "gdkpoly-generic.h" + +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +static void +InsertEdgeInET(ET, ETE, scanline, SLLBlock, iSLLBlock) + EdgeTable *ET; + EdgeTableEntry *ETE; + int scanline; + ScanLineListBlock **SLLBlock; + int *iSLLBlock; +{ + EdgeTableEntry *start, *prev; + ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)g_malloc(sizeof(ScanLineListBlock)); + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = (ScanLineListBlock *)NULL; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = (EdgeTableEntry *)NULL; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = (EdgeTableEntry *)NULL; + start = pSLL->edgelist; + while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +static void +CreateETandAET(count, pts, ET, AET, pETEs, pSLLBlock) + int count; + GdkPoint *pts; + EdgeTable *ET; + EdgeTableEntry *AET; + EdgeTableEntry *pETEs; + ScanLineListBlock *pSLLBlock; +{ + GdkPoint *top, *bottom; + GdkPoint *PrevPt, *CurrPt; + int iSLLBlock = 0; + int dy; + + if (count < 2) return; + + /* + * initialize the Active Edge Table + */ + AET->next = (EdgeTableEntry *)NULL; + AET->back = (EdgeTableEntry *)NULL; + AET->nextWETE = (EdgeTableEntry *)NULL; + AET->bres.minor_axis = SMALL_COORDINATE; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = (ScanLineList *)NULL; + ET->ymax = SMALL_COORDINATE; + ET->ymin = LARGE_COORDINATE; + pSLLBlock->next = (ScanLineListBlock *)NULL; + + PrevPt = &pts[count-1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); + + InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock); + + if (PrevPt->y > ET->ymax) + ET->ymax = PrevPt->y; + if (PrevPt->y < ET->ymin) + ET->ymin = PrevPt->y; + pETEs++; + } + + PrevPt = CurrPt; + } +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +static void +loadAET(AET, ETEs) + EdgeTableEntry *AET, *ETEs; +{ + EdgeTableEntry *pPrevAET; + EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +static void +computeWAET(AET) + EdgeTableEntry *AET; +{ + EdgeTableEntry *pWETE; + int inside = 1; + int isInside = 0; + + AET->nextWETE = (EdgeTableEntry *)NULL; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + ( inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = (EdgeTableEntry *)NULL; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +static int +InsertionSort(AET) + EdgeTableEntry *AET; +{ + EdgeTableEntry *pETEchase; + EdgeTableEntry *pETEinsert; + EdgeTableEntry *pETEchaseBackTMP; + int changed = 0; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return(changed); +} + +/* + * Clean up our act. + */ +static void +FreeStorage(pSLLBlock) + ScanLineListBlock *pSLLBlock; +{ + ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + g_free (pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} + +/* + * Create an array of rectangles from a list of points. + * If indeed these things (POINTS, RECTS) are the same, + * then this proc is still needed, because it allocates + * storage for the array, which was allocated on the + * stack by the calling procedure. + * + */ +static int PtsToRegion(numFullPtBlocks, iCurPtBlock, FirstPtBlock, reg) + int numFullPtBlocks, iCurPtBlock; + POINTBLOCK *FirstPtBlock; + GdkRegion *reg; +{ + GdkRegionBox *rects; + GdkPoint *pts; + POINTBLOCK *CurPtBlock; + int i; + GdkRegionBox *extents; + int numRects; + + extents = ®->extents; + + numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1; + + reg->rects = g_renew (GdkRegionBox, reg->rects, numRects); + + reg->size = numRects; + CurPtBlock = FirstPtBlock; + rects = reg->rects - 1; + numRects = 0; + extents->x1 = G_MAXSHORT, extents->x2 = G_MINSHORT; + + for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) { + /* the loop uses 2 points per iteration */ + i = NUMPTSTOBUFFER >> 1; + if (!numFullPtBlocks) + i = iCurPtBlock >> 1; + for (pts = CurPtBlock->pts; i--; pts += 2) { + if (pts->x == pts[1].x) + continue; + if (numRects && pts->x == rects->x1 && pts->y == rects->y2 && + pts[1].x == rects->x2 && + (numRects == 1 || rects[-1].y1 != rects->y1) && + (i && pts[2].y > pts[1].y)) { + rects->y2 = pts[1].y + 1; + continue; + } + numRects++; + rects++; + rects->x1 = pts->x; rects->y1 = pts->y; + rects->x2 = pts[1].x; rects->y2 = pts[1].y + 1; + if (rects->x1 < extents->x1) + extents->x1 = rects->x1; + if (rects->x2 > extents->x2) + extents->x2 = rects->x2; + } + CurPtBlock = CurPtBlock->next; + } + + if (numRects) { + extents->y1 = reg->rects->y1; + extents->y2 = rects->y2; + } else { + extents->x1 = 0; + extents->y1 = 0; + extents->x2 = 0; + extents->y2 = 0; + } + reg->numRects = numRects; + + return(TRUE); +} + +/* + * polytoregion + * + * Scan converts a polygon by returning a run-length + * encoding of the resultant bitmap -- the run-length + * encoding is in the form of an array of rectangles. + */ +GdkRegion * +gdk_region_polygon(GdkPoint *Pts, gint Count, GdkFillRule rule) +{ + GdkRegion *region; + EdgeTableEntry *pAET; /* Active Edge Table */ + int y; /* current scanline */ + int iPts = 0; /* number of pts in buffer */ + EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/ + ScanLineList *pSLL; /* current scanLineList */ + GdkPoint *pts; /* output buffer */ + EdgeTableEntry *pPrevAET; /* ptr to previous AET */ + EdgeTable ET; /* header node for ET */ + EdgeTableEntry AET; /* header node for AET */ + EdgeTableEntry *pETEs; /* EdgeTableEntries pool */ + ScanLineListBlock SLLBlock; /* header for scanlinelist */ + int fixWAET = FALSE; + POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */ + POINTBLOCK *tmpPtBlock; + int numFullPtBlocks = 0; + + region = gdk_region_new (); + + /* special case a rectangle */ + pts = Pts; + if (((Count == 4) || + ((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) && + (((pts[0].y == pts[1].y) && + (pts[1].x == pts[2].x) && + (pts[2].y == pts[3].y) && + (pts[3].x == pts[0].x)) || + ((pts[0].x == pts[1].x) && + (pts[1].y == pts[2].y) && + (pts[2].x == pts[3].x) && + (pts[3].y == pts[0].y)))) { + region->extents.x1 = MIN(pts[0].x, pts[2].x); + region->extents.y1 = MIN(pts[0].y, pts[2].y); + region->extents.x2 = MAX(pts[0].x, pts[2].x); + region->extents.y2 = MAX(pts[0].y, pts[2].y); + if ((region->extents.x1 != region->extents.x2) && + (region->extents.y1 != region->extents.y2)) { + region->numRects = 1; + *(region->rects) = region->extents; + } + return(region); + } + + pETEs = g_new (EdgeTableEntry, Count); + + pts = FirstPtBlock.pts; + CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock); + pSLL = ET.scanlines.next; + curPtBlock = &FirstPtBlock; + + if (rule == GDK_EVEN_ODD_RULE) { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL != NULL && y == pSLL->scanline) { + loadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) { + pts->x = pAET->bres.minor_axis, pts->y = y; + pts++, iPts++; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK)); + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + numFullPtBlocks++; + iPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y); + } + (void) InsertionSort(&AET); + } + } + else { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL != NULL && y == pSLL->scanline) { + loadAET(&AET, pSLL->edgelist); + computeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) { + /* + * add to the buffer only those edges that + * are in the Winding active edge table. + */ + if (pWETE == pAET) { + pts->x = pAET->bres.minor_axis, pts->y = y; + pts++, iPts++; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK)); + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + numFullPtBlocks++; iPts = 0; + } + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + } + + /* + * recompute the winding active edge table if + * we just resorted or have exited an edge. + */ + if (InsertionSort(&AET) || fixWAET) { + computeWAET(&AET); + fixWAET = FALSE; + } + } + } + FreeStorage(SLLBlock.next); + (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region); + for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) { + tmpPtBlock = curPtBlock->next; + g_free (curPtBlock); + curPtBlock = tmpPtBlock; + } + g_free (pETEs); + return(region); +} diff --git a/gdk/linux-fb/gdkprivate-fb.h b/gdk/linux-fb/gdkprivate-fb.h new file mode 100644 index 0000000000..2ad3a04896 --- /dev/null +++ b/gdk/linux-fb/gdkprivate-fb.h @@ -0,0 +1,194 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * Private uninstalled header defining things local to X windowing code + */ + +#ifndef __GDK_PRIVATE_FB_H__ +#define __GDK_PRIVATE_FB_H__ + +#include +#include +#include "gdkfb.h" +#include "gdkregion-generic.h" +#include +#include + +#define GDK_DRAWABLE_FBDATA(win) ((GdkDrawableFBData *)(((GdkDrawablePrivate*)(win))->klass_data)) +#define GDK_PIXMAP_FBDATA(win) ((GdkPixmapFBData *)(((GdkDrawablePrivate*)(win))->klass_data)) +#define GDK_WINDOW_FBDATA(win) ((GdkWindowFBData *)(((GdkDrawablePrivate*)(win))->klass_data)) +#define GDK_FONT_FB(f) ((GdkFontPrivateFB *)(f)) +#define GDK_CURSOR_FB(c) ((GdkCursorPrivateFB *)(c)) + +typedef struct _GdkDrawableFBData GdkDrawableFBData; +typedef struct _GdkWindowFBData GdkWindowFBData; + +struct _GdkDrawableFBData +{ + guchar *mem; + + gint abs_x, abs_y, lim_x, lim_y, llim_x, llim_y; /* computed values */ + + guint rowstride; +}; + +struct _GdkPixmapFBData +{ + GdkDrawableFBData drawable_data; +}; + +typedef struct { + gulong length; + GdkAtom type; + gint format; + guchar data[1]; +} GdkWindowProperty; + +struct _GdkWindowFBData +{ + GdkDrawableFBData drawable_data; + GdkCursor *cursor; + GHashTable *properties; + + GdkEventMask event_mask; + gint level; + gboolean realized : 1; +}; + +struct _GdkFBDisplay +{ + int fd; + guchar *fbmem; + gpointer active_cmap; + gulong mem_len; + struct fb_fix_screeninfo sinfo; + struct fb_var_screeninfo modeinfo; +}; + +typedef struct { + GdkVisual base; +} GdkVisualPrivateFB; + +typedef struct { + GdkColormapPrivate base; + + GHashTable *hash; + GdkColorInfo *info; + guint sync_tag; +} GdkColormapPrivateFB; + +typedef struct { + GdkCursor base; + GdkPixmap *cursor, *mask; +} GdkCursorPrivateFB; + +typedef struct { + GdkFontPrivate base; + + int t1_font_id; + double size; + GSList *names; +} GdkFontPrivateFB; + +typedef struct { + GdkImagePrivate base; +} GdkImagePrivateFB; + +#define GDK_GC_FBDATA(x) ((GdkGCFBData *)((GdkGCPrivate *)x)->klass_data) +typedef struct { + GdkRegion *clip_region; + gchar *dash_list; + GdkGCValuesMask values_mask; + GdkGCValues values; + gint dash_offset; + gushort dash_list_len; + guchar depth, alu; +} GdkGCFBData; + +GdkGC * _gdk_fb_gc_new (GdkDrawable *drawable, + GdkGCValues *values, + GdkGCValuesMask values_mask); + +/* Routines from gdkgeometry-fb.c */ + +void _gdk_window_init_position (GdkWindow *window); +void _gdk_window_move_resize_child (GdkWindow *window, + gint x, + gint y, + gint width, + gint height); +void _gdk_window_process_expose (GdkWindow *window, + gulong serial, + GdkRectangle *area); +GdkGC *_gdk_fb_gc_new(GdkDrawable *drawable, GdkGCValues *values, GdkGCValuesMask values_mask); + +void gdk_fb_drawable_clear(GdkDrawable *drawable); +void gdk_fb_draw_drawable (GdkDrawable *drawable, + GdkGC *gc, + GdkPixmap *src, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height); +void gdk_fb_draw_drawable_2 (GdkDrawable *drawable, + GdkGC *gc, + GdkPixmap *src, + gint xsrc, + gint ysrc, + gint xdest, + gint ydest, + gint width, + gint height, + gboolean draw_bg, + gboolean do_clipping); +void gdk_fb_draw_rectangle (GdkDrawable *drawable, + GdkGC *gc, + gint filled, + gint x, + gint y, + gint width, + gint height); +void gdk_fb_fill_spans(GdkDrawable *drawable, GdkGC *gc, GdkRectangle *rects, int nrects); + +extern GdkWindow *_gdk_fb_pointer_grab_window, *_gdk_fb_keyboard_grab_window, *_gdk_fb_pointer_grab_confine; +extern GdkEventMask _gdk_fb_pointer_grab_events, _gdk_fb_keyboard_grab_events; +extern GdkCursor *_gdk_fb_pointer_grab_cursor; +extern GdkFBDisplay *gdk_display; +extern GdkDrawableClass _gdk_fb_drawable_class; +extern FILE *debug_out; +GdkEvent *gdk_event_make(GdkWindow *window, GdkEventType type, gboolean append_to_queue); + +void gdk_fb_get_cursor_rect(GdkRectangle *rect); +void gdk_fb_cursor_unhide(void); +void gdk_fb_cursor_hide(void); +void gdk_fb_redraw_all(void); + +void gdk_input_ps2_get_mouseinfo(gint *x, gint *y, GdkModifierType *mask); + +#endif /* __GDK_PRIVATE_FB_H__ */ diff --git a/gdk/linux-fb/gdkproperty-fb.c b/gdk/linux-fb/gdkproperty-fb.c new file mode 100644 index 0000000000..e251e71d3b --- /dev/null +++ b/gdk/linux-fb/gdkproperty-fb.c @@ -0,0 +1,204 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include +#include + +#include "gdkfb.h" +#include "gdkproperty.h" +#include "gdkprivate.h" +#include "gdkprivate-fb.h" + +GdkAtom +gdk_atom_intern (const gchar *atom_name, + gboolean only_if_exists) +{ + g_return_val_if_fail (atom_name != NULL, GDK_NONE); + + return g_quark_from_string(atom_name); +} + +gchar* +gdk_atom_name (GdkAtom atom) +{ + return g_quark_to_string(atom); +} + +static void +gdk_property_delete_2 (GdkWindow *window, + GdkAtom property, + GdkWindowProperty *prop) +{ + GdkWindowFBData *fbd = GDK_WINDOW_FBDATA(window); + GdkEvent *event; + + g_hash_table_remove(fbd->properties, GUINT_TO_POINTER(property)); + g_free(prop); + + event = gdk_event_make(window, GDK_PROPERTY_NOTIFY, TRUE); + if(event) + { + event->property.atom = property; + event->property.state = GDK_PROPERTY_DELETE; + } +} + +void +gdk_property_delete (GdkWindow *window, + GdkAtom property) +{ + GdkWindowFBData *fbd = GDK_WINDOW_FBDATA(window); + GdkWindowProperty *prop; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if(!fbd->properties) + return; + + prop = g_hash_table_lookup(fbd->properties, GUINT_TO_POINTER(property)); + if(!prop) + return; + + gdk_property_delete_2(window, property, prop); +} + +gint +gdk_property_get (GdkWindow *window, + GdkAtom property, + GdkAtom type, + gulong offset, + gulong length, + gint pdelete, + GdkAtom *actual_property_type, + gint *actual_format_type, + gint *actual_length, + guchar **data) +{ + GdkWindowFBData *fbd = GDK_WINDOW_FBDATA(window); + GdkWindowProperty *prop; + int nbytes; + + g_return_val_if_fail (window != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (actual_length != NULL, FALSE); + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + if(!fbd->properties) + return FALSE; + + prop = g_hash_table_lookup(fbd->properties, GUINT_TO_POINTER(property)); + if(!prop) + return FALSE; + + nbytes = (offset + length * (prop->format >> 3)) - prop->length; + nbytes = MAX(nbytes, 0); + if(nbytes > 0) + { + *data = g_malloc(nbytes+1); + memcpy(data, prop->data + offset, nbytes); + (*data)[nbytes] = 0; + } + else + *data = NULL; + *actual_length = nbytes / (prop->format >> 3); + *actual_property_type = prop->type; + *actual_format_type = prop->format; + + if(pdelete) + gdk_property_delete_2(window, property, prop); + + return TRUE; +} + +void +gdk_property_change (GdkWindow *window, + GdkAtom property, + GdkAtom type, + gint format, + GdkPropMode mode, + const guchar *data, + gint nelements) +{ + GdkWindowFBData *fbd = GDK_WINDOW_FBDATA(window); + GdkWindowProperty *prop, *new_prop; + int new_size; + GdkEvent *event; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if(!fbd->properties) + fbd->properties = g_hash_table_new(NULL, NULL); + + prop = g_hash_table_lookup(fbd->properties, GUINT_TO_POINTER(property)); + + switch(mode) + { + case GDK_PROP_MODE_REPLACE: + new_size = nelements * (format >> 3); + break; + case GDK_PROP_MODE_PREPEND: + case GDK_PROP_MODE_APPEND: + new_size = nelements * (format >> 3); + if(prop) + new_size += prop->length; + default: + break; + } + + new_prop = g_malloc(G_STRUCT_OFFSET(GdkWindowProperty, data) + new_size); + new_prop->length = new_size; + new_prop->type = type; + new_prop->format = format; + + switch(mode) + { + case GDK_PROP_MODE_REPLACE: + memcpy(new_prop->data, data, new_size); + break; + case GDK_PROP_MODE_APPEND: + if(prop) + memcpy(new_prop->data, prop->data, prop->length); + memcpy(new_prop->data + prop->length, data, (nelements * (format >> 3))); + break; + case GDK_PROP_MODE_PREPEND: + memcpy(new_prop->data, data, (nelements * (format >> 3))); + if(prop) + memcpy(new_prop->data + (nelements * (format >> 3)), prop->data, prop->length); + break; + } + + g_hash_table_insert(fbd->properties, GUINT_TO_POINTER(property), new_prop); + g_free(prop); + + event = gdk_event_make(window, GDK_PROPERTY_NOTIFY, TRUE); + if(event) + { + event->property.atom = property; + event->property.state = GDK_PROPERTY_NEW_VALUE; + } +} diff --git a/gdk/linux-fb/gdkregion-generic.c b/gdk/linux-fb/gdkregion-generic.c new file mode 100644 index 0000000000..1140ce0421 --- /dev/null +++ b/gdk/linux-fb/gdkregion-generic.c @@ -0,0 +1,1505 @@ +/* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */ +/************************************************************************ + +Copyright 1987, 1988, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */ +/* + * The functions in this file implement the Region abstraction, similar to one + * used in the X11 sample server. A Region is simply an area, as the name + * implies, and is implemented as a "y-x-banded" array of rectangles. To + * explain: Each Region is made up of a certain number of rectangles sorted + * by y coordinate first, and then by x coordinate. + * + * Furthermore, the rectangles are banded such that every rectangle with a + * given upper-left y coordinate (y1) will have the same lower-right y + * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it + * will span the entire vertical distance of the band. This means that some + * areas that could be merged into a taller rectangle will be represented as + * several shorter rectangles to account for shorter rectangles to its left + * or right but within its "vertical scope". + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible. E.g. no two rectangles in a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). This maintains + * the y-x-banding that's so nice to have... + */ + +#include +#include "gdkregion-generic.h" + +#ifdef DEBUG +#include +#define assert(expr) {if (!(expr)) fprintf(stderr,\ +"Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); } +#else +#define assert(expr) +#endif + +typedef void (*overlapFunc) (GdkRegion *pReg, + GdkRegionBox *r1, + GdkRegionBox *r1End, + GdkRegionBox *r2, + GdkRegionBox *r2End, + gint y1, + gint y2); +typedef void (*nonOverlapFunc) (GdkRegion *pReg, + GdkRegionBox *r, + GdkRegionBox *rEnd, + gint y1, + gint y2); + +static void miRegionCopy (GdkRegion *dstrgn, + GdkRegion *rgn); +static void miRegionOp (GdkRegion *newReg, + GdkRegion *reg1, + GdkRegion *reg2, + overlapFunc overlapFn, + nonOverlapFunc nonOverlap1Fn, + nonOverlapFunc nonOverlap2Fn); + +/* Create a new empty region */ + +GdkRegion * +gdk_region_new () +{ + GdkRegion *temp; + + temp = g_new (GdkRegion, 1); + temp->rects = g_new (GdkRegionBox, 1); + + temp->numRects = 0; + temp->extents.x1 = 0; + temp->extents.y1 = 0; + temp->extents.x2 = 0; + temp->extents.y2 = 0; + temp->size = 1; + + return temp; +} + +GdkRegion * +gdk_region_rectangle (GdkRectangle *rectangle) +{ + GdkRegion *temp; + + if (rectangle->width <= 0 || rectangle->height <= 0) + return gdk_region_new(); + + temp = g_new (GdkRegion, 1); + temp->rects = g_new (GdkRegionBox, 1); + + temp->numRects = 1; + temp->extents.x1 = temp->rects[0].x1 = rectangle->x; + temp->extents.y1 = temp->rects[0].y1 = rectangle->y; + temp->extents.x2 = temp->rects[0].x2 = rectangle->x + rectangle->width; + temp->extents.y2 = temp->rects[0].y2 = rectangle->y + rectangle->height; + temp->size = 1; + + return temp; +} + +GdkRegion * +gdk_region_copy (GdkRegion *region) +{ + GdkRegion *temp; + + temp = g_new (GdkRegion, 1); + temp->rects = g_new (GdkRegionBox, region->numRects); + + temp->numRects = region->numRects; + temp->extents = region->extents; + temp->size = region->numRects; + + memcpy (temp->rects, region->rects, region->numRects * sizeof (GdkRegionBox)); + + return temp; +} + +void +gdk_region_get_clipbox (GdkRegion *r, GdkRectangle *rect) +{ + rect->x = r->extents.x1; + rect->y = r->extents.y1; + rect->width = r->extents.x2 - r->extents.x1; + rect->height = r->extents.y2 - r->extents.y1; +} + +void +gdk_region_union_with_rect (GdkRegion *region, + GdkRectangle *rect) +{ + GdkRegion tmp_region; + + if (!rect->width || !rect->height) + return; + + tmp_region.rects = &tmp_region.extents; + tmp_region.numRects = 1; + tmp_region.extents.x1 = rect->x; + tmp_region.extents.y1 = rect->y; + tmp_region.extents.x2 = rect->x + rect->width; + tmp_region.extents.y2 = rect->y + rect->height; + tmp_region.size = 1; + + gdk_region_union (region, &tmp_region); +} + +/*- + *----------------------------------------------------------------------- + * miSetExtents -- + * Reset the extents of a region to what they should be. Called by + * miSubtract and miIntersect b/c they can't figure it out along the + * way or do so easily, as miUnion can. + * + * Results: + * None. + * + * Side Effects: + * The region's 'extents' structure is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +miSetExtents (GdkRegion *pReg) +{ + GdkRegionBox *pBox, *pBoxEnd, *pExtents; + + if (pReg->numRects == 0) + { + pReg->extents.x1 = 0; + pReg->extents.y1 = 0; + pReg->extents.x2 = 0; + pReg->extents.y2 = 0; + return; + } + + pExtents = &pReg->extents; + pBox = pReg->rects; + pBoxEnd = &pBox[pReg->numRects - 1]; + + /* + * Since pBox is the first rectangle in the region, it must have the + * smallest y1 and since pBoxEnd is the last rectangle in the region, + * it must have the largest y2, because of banding. Initialize x1 and + * x2 from pBox and pBoxEnd, resp., as good things to initialize them + * to... + */ + pExtents->x1 = pBox->x1; + pExtents->y1 = pBox->y1; + pExtents->x2 = pBoxEnd->x2; + pExtents->y2 = pBoxEnd->y2; + + assert(pExtents->y1 < pExtents->y2); + while (pBox <= pBoxEnd) + { + if (pBox->x1 < pExtents->x1) + { + pExtents->x1 = pBox->x1; + } + if (pBox->x2 > pExtents->x2) + { + pExtents->x2 = pBox->x2; + } + pBox++; + } + assert(pExtents->x1 < pExtents->x2); +} + +void +gdk_region_destroy (GdkRegion *r) +{ + g_free (r->rects); + g_free (r); +} + + +/* TranslateRegion(pRegion, x, y) + translates in place + added by raymond +*/ + +void +gdk_region_offset (GdkRegion *region, + gint x, + gint y) +{ + int nbox; + GdkRegionBox *pbox; + + pbox = region->rects; + nbox = region->numRects; + + while(nbox--) + { + pbox->x1 += x; + pbox->x2 += x; + pbox->y1 += y; + pbox->y2 += y; + pbox++; + } + region->extents.x1 += x; + region->extents.x2 += x; + region->extents.y1 += y; + region->extents.y2 += y; +} + +/* + Utility procedure Compress: + Replace r by the region r', where + p in r' iff (Quantifer m <= dx) (p + m in r), and + Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and + (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE. + + Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region + of all points p such that p and the next dx points on the same + horizontal scan line are all in r. We do this using by noting + that p is the head of a run of length 2^i + k iff p is the head + of a run of length 2^i and p+2^i is the head of a run of length + k. Thus, the loop invariant: s contains the region corresponding + to the runs of length shift. r contains the region corresponding + to the runs of length 1 + dxo & (shift-1), where dxo is the original + value of dx. dx = dxo & ~(shift-1). As parameters, s and t are + scratch regions, so that we don't have to allocate them on every + call. +*/ + +#define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \ + else gdk_region_intersect (a,b) +#define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \ + else gdk_region_offset (a,0,b) + +static void +Compress(GdkRegion *r, + GdkRegion *s, + GdkRegion *t, + guint dx, + int xdir, + int grow) +{ + guint shift = 1; + + miRegionCopy (s, r); + while (dx) + { + if (dx & shift) + { + ZShiftRegion(r, -(int)shift); + ZOpRegion(r, s); + dx -= shift; + if (!dx) break; + } + miRegionCopy (t, s); + ZShiftRegion(s, -(int)shift); + ZOpRegion(s, t); + shift <<= 1; + } +} + +#undef ZOpRegion +#undef ZShiftRegion +#undef ZCopyRegion + +void +gdk_region_shrink (GdkRegion *r, + int dx, + int dy) +{ + GdkRegion *s, *t; + int grow; + + if (!dx && !dy) + return; + + s = gdk_region_new (); + t = gdk_region_new (); + + grow = (dx < 0); + if (grow) + dx = -dx; + if (dx) + Compress(r, s, t, (unsigned) 2*dx, TRUE, grow); + + grow = (dy < 0); + if (grow) + dy = -dy; + if (dy) + Compress(r, s, t, (unsigned) 2*dy, FALSE, grow); + + gdk_region_offset (r, dx, dy); + gdk_region_destroy (s); + gdk_region_destroy (t); +} + + +/*====================================================================== + * Region Intersection + *====================================================================*/ +/*- + *----------------------------------------------------------------------- + * miIntersectO -- + * Handle an overlapping band for miIntersect. + * + * Results: + * None. + * + * Side Effects: + * Rectangles may be added to the region. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static void +miIntersectO (GdkRegion *pReg, + GdkRegionBox *r1, + GdkRegionBox *r1End, + GdkRegionBox *r2, + GdkRegionBox *r2End, + gint y1, + gint y2) +{ + int x1; + int x2; + GdkRegionBox *pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + while ((r1 != r1End) && (r2 != r2End)) + { + x1 = MAX (r1->x1,r2->x1); + x2 = MIN (r1->x2,r2->x2); + + /* + * If there's any overlap between the two rectangles, add that + * overlap to the new region. + * There's no need to check for subsumption because the only way + * such a need could arise is if some region has two rectangles + * right next to each other. Since that should never happen... + */ + if (x1 < x2) + { + assert (y1rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + assert (pReg->numRects <= pReg->size); + } + + /* + * Need to advance the pointers. Shift the one that extends + * to the right the least, since the other still has a chance to + * overlap with that region's next rectangle, if you see what I mean. + */ + if (r1->x2 < r2->x2) + { + r1++; + } + else if (r2->x2 < r1->x2) + { + r2++; + } + else + { + r1++; + r2++; + } + } +} + +void +gdk_region_intersect (GdkRegion *region, + GdkRegion *other) +{ + /* check for trivial reject */ + if ((!(region->numRects)) || (!(other->numRects)) || + (!EXTENTCHECK(®ion->extents, &other->extents))) + region->numRects = 0; + else + miRegionOp (region, region, other, + miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL); + + /* + * Can't alter region's extents before miRegionOp depends on the + * extents of the regions being unchanged. Besides, this way there's + * no checking against rectangles that will be nuked due to + * coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(region); +} + +static void +miRegionCopy(GdkRegion *dstrgn, GdkRegion *rgn) +{ + if (dstrgn != rgn) /* don't want to copy to itself */ + { + if (dstrgn->size < rgn->numRects) + { + dstrgn->rects = g_renew (GdkRegionBox, dstrgn->rects, rgn->numRects); + dstrgn->size = rgn->numRects; + } + dstrgn->numRects = rgn->numRects; + dstrgn->extents.x1 = rgn->extents.x1; + dstrgn->extents.y1 = rgn->extents.y1; + dstrgn->extents.x2 = rgn->extents.x2; + dstrgn->extents.y2 = rgn->extents.y2; + + memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox)); + } +} + + +/*====================================================================== + * Generic Region Operator + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miCoalesce -- + * Attempt to merge the boxes in the current band with those in the + * previous one. Used only by miRegionOp. + * + * Results: + * The new index for the previous band. + * + * Side Effects: + * If coalescing takes place: + * - rectangles in the previous band will have their y2 fields + * altered. + * - pReg->numRects will be decreased. + * + *----------------------------------------------------------------------- + */ +/* static int*/ +static int +miCoalesce (GdkRegion *pReg, /* Region to coalesce */ + gint prevStart, /* Index of start of previous band */ + gint curStart) /* Index of start of current band */ +{ + GdkRegionBox *pPrevBox; /* Current box in previous band */ + GdkRegionBox *pCurBox; /* Current box in current band */ + GdkRegionBox *pRegEnd; /* End of region */ + int curNumRects; /* Number of rectangles in current + * band */ + int prevNumRects; /* Number of rectangles in previous + * band */ + int bandY1; /* Y1 coordinate for current band */ + + pRegEnd = &pReg->rects[pReg->numRects]; + + pPrevBox = &pReg->rects[prevStart]; + prevNumRects = curStart - prevStart; + + /* + * Figure out how many rectangles are in the current band. Have to do + * this because multiple bands could have been added in miRegionOp + * at the end when one region has been exhausted. + */ + pCurBox = &pReg->rects[curStart]; + bandY1 = pCurBox->y1; + for (curNumRects = 0; + (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1); + curNumRects++) + { + pCurBox++; + } + + if (pCurBox != pRegEnd) + { + /* + * If more than one band was added, we have to find the start + * of the last band added so the next coalescing job can start + * at the right place... (given when multiple bands are added, + * this may be pointless -- see above). + */ + pRegEnd--; + while (pRegEnd[-1].y1 == pRegEnd->y1) + { + pRegEnd--; + } + curStart = pRegEnd - pReg->rects; + pRegEnd = pReg->rects + pReg->numRects; + } + + if ((curNumRects == prevNumRects) && (curNumRects != 0)) { + pCurBox -= curNumRects; + /* + * The bands may only be coalesced if the bottom of the previous + * matches the top scanline of the current. + */ + if (pPrevBox->y2 == pCurBox->y1) + { + /* + * Make sure the bands have boxes in the same places. This + * assumes that boxes have been added in such a way that they + * cover the most area possible. I.e. two boxes in a band must + * have some horizontal space between them. + */ + do + { + if ((pPrevBox->x1 != pCurBox->x1) || + (pPrevBox->x2 != pCurBox->x2)) + { + /* + * The bands don't line up so they can't be coalesced. + */ + return (curStart); + } + pPrevBox++; + pCurBox++; + prevNumRects -= 1; + } while (prevNumRects != 0); + + pReg->numRects -= curNumRects; + pCurBox -= curNumRects; + pPrevBox -= curNumRects; + + /* + * The bands may be merged, so set the bottom y of each box + * in the previous band to that of the corresponding box in + * the current band. + */ + do + { + pPrevBox->y2 = pCurBox->y2; + pPrevBox++; + pCurBox++; + curNumRects -= 1; + } + while (curNumRects != 0); + + /* + * If only one band was added to the region, we have to backup + * curStart to the start of the previous band. + * + * If more than one band was added to the region, copy the + * other bands down. The assumption here is that the other bands + * came from the same region as the current one and no further + * coalescing can be done on them since it's all been done + * already... curStart is already in the right place. + */ + if (pCurBox == pRegEnd) + { + curStart = prevStart; + } + else + { + do + { + *pPrevBox++ = *pCurBox++; + } + while (pCurBox != pRegEnd); + } + + } + } + return curStart; +} + +/*- + *----------------------------------------------------------------------- + * miRegionOp -- + * Apply an operation to two regions. Called by miUnion, miInverse, + * miSubtract, miIntersect... + * + * Results: + * None. + * + * Side Effects: + * The new region is overwritten. + * + * Notes: + * The idea behind this function is to view the two regions as sets. + * Together they cover a rectangle of area that this function divides + * into horizontal bands where points are covered only by one region + * or by both. For the first case, the nonOverlapFunc is called with + * each the band and the band's upper and lower extents. For the + * second, the overlapFunc is called to process the entire band. It + * is responsible for clipping the rectangles in the band, though + * this function provides the boundaries. + * At the end of each band, the new region is coalesced, if possible, + * to reduce the number of rectangles in the region. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static void +miRegionOp(GdkRegion *newReg, + GdkRegion *reg1, + GdkRegion *reg2, + overlapFunc overlapFn, /* Function to call for over- + * lapping bands */ + nonOverlapFunc nonOverlap1Fn, /* Function to call for non- + * overlapping bands in region + * 1 */ + nonOverlapFunc nonOverlap2Fn) /* Function to call for non- + * overlapping bands in region + * 2 */ +{ + GdkRegionBox *r1; /* Pointer into first region */ + GdkRegionBox *r2; /* Pointer into 2d region */ + GdkRegionBox *r1End; /* End of 1st region */ + GdkRegionBox *r2End; /* End of 2d region */ + int ybot; /* Bottom of intersection */ + int ytop; /* Top of intersection */ + GdkRegionBox *oldRects; /* Old rects for newReg */ + int prevBand; /* Index of start of + * previous band in newReg */ + int curBand; /* Index of start of current + * band in newReg */ + GdkRegionBox *r1BandEnd; /* End of current band in r1 */ + GdkRegionBox *r2BandEnd; /* End of current band in r2 */ + int top; /* Top of non-overlapping + * band */ + int bot; /* Bottom of non-overlapping + * band */ + + /* + * Initialization: + * set r1, r2, r1End and r2End appropriately, preserve the important + * parts of the destination region until the end in case it's one of + * the two source regions, then mark the "new" region empty, allocating + * another array of rectangles for it to use. + */ + r1 = reg1->rects; + r2 = reg2->rects; + r1End = r1 + reg1->numRects; + r2End = r2 + reg2->numRects; + + oldRects = newReg->rects; + + EMPTY_REGION(newReg); + + /* + * Allocate a reasonable number of rectangles for the new region. The idea + * is to allocate enough so the individual functions don't need to + * reallocate and copy the array, which is time consuming, yet we don't + * have to worry about using too much memory. I hope to be able to + * nuke the Xrealloc() at the end of this function eventually. + */ + newReg->size = MAX (reg1->numRects, reg2->numRects) * 2; + newReg->rects = g_new (GdkRegionBox, newReg->size); + + /* + * Initialize ybot and ytop. + * In the upcoming loop, ybot and ytop serve different functions depending + * on whether the band being handled is an overlapping or non-overlapping + * band. + * In the case of a non-overlapping band (only one of the regions + * has points in the band), ybot is the bottom of the most recent + * intersection and thus clips the top of the rectangles in that band. + * ytop is the top of the next intersection between the two regions and + * serves to clip the bottom of the rectangles in the current band. + * For an overlapping band (where the two regions intersect), ytop clips + * the top of the rectangles of both regions and ybot clips the bottoms. + */ + if (reg1->extents.y1 < reg2->extents.y1) + ybot = reg1->extents.y1; + else + ybot = reg2->extents.y1; + + /* + * prevBand serves to mark the start of the previous band so rectangles + * can be coalesced into larger rectangles. qv. miCoalesce, above. + * In the beginning, there is no previous band, so prevBand == curBand + * (curBand is set later on, of course, but the first band will always + * start at index 0). prevBand and curBand must be indices because of + * the possible expansion, and resultant moving, of the new region's + * array of rectangles. + */ + prevBand = 0; + + do + { + curBand = newReg->numRects; + + /* + * This algorithm proceeds one source-band (as opposed to a + * destination band, which is determined by where the two regions + * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the + * rectangle after the last one in the current band for their + * respective regions. + */ + r1BandEnd = r1; + while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1)) + { + r1BandEnd++; + } + + r2BandEnd = r2; + while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1)) + { + r2BandEnd++; + } + + /* + * First handle the band that doesn't intersect, if any. + * + * Note that attention is restricted to one band in the + * non-intersecting region at once, so if a region has n + * bands between the current position and the next place it overlaps + * the other, this entire loop will be passed through n times. + */ + if (r1->y1 < r2->y1) + { + top = MAX (r1->y1,ybot); + bot = MIN (r1->y2,r2->y1); + + if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL)) + { + (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot); + } + + ytop = r2->y1; + } + else if (r2->y1 < r1->y1) + { + top = MAX (r2->y1,ybot); + bot = MIN (r2->y2,r1->y1); + + if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL)) + { + (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot); + } + + ytop = r1->y1; + } + else + { + ytop = r1->y1; + } + + /* + * If any rectangles got added to the region, try and coalesce them + * with rectangles from the previous band. Note we could just do + * this test in miCoalesce, but some machines incur a not + * inconsiderable cost for function calls, so... + */ + if (newReg->numRects != curBand) + { + prevBand = miCoalesce (newReg, prevBand, curBand); + } + + /* + * Now see if we've hit an intersecting band. The two bands only + * intersect if ybot > ytop + */ + ybot = MIN (r1->y2, r2->y2); + curBand = newReg->numRects; + if (ybot > ytop) + { + (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot); + + } + + if (newReg->numRects != curBand) + { + prevBand = miCoalesce (newReg, prevBand, curBand); + } + + /* + * If we've finished with a band (y2 == ybot) we skip forward + * in the region to the next band. + */ + if (r1->y2 == ybot) + { + r1 = r1BandEnd; + } + if (r2->y2 == ybot) + { + r2 = r2BandEnd; + } + } while ((r1 != r1End) && (r2 != r2End)); + + /* + * Deal with whichever region still has rectangles left. + */ + curBand = newReg->numRects; + if (r1 != r1End) + { + if (nonOverlap1Fn != (nonOverlapFunc )NULL) + { + do + { + r1BandEnd = r1; + while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1)) + { + r1BandEnd++; + } + (* nonOverlap1Fn) (newReg, r1, r1BandEnd, + MAX (r1->y1,ybot), r1->y2); + r1 = r1BandEnd; + } while (r1 != r1End); + } + } + else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL)) + { + do + { + r2BandEnd = r2; + while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1)) + { + r2BandEnd++; + } + (* nonOverlap2Fn) (newReg, r2, r2BandEnd, + MAX (r2->y1,ybot), r2->y2); + r2 = r2BandEnd; + } while (r2 != r2End); + } + + if (newReg->numRects != curBand) + { + (void) miCoalesce (newReg, prevBand, curBand); + } + + /* + * A bit of cleanup. To keep regions from growing without bound, + * we shrink the array of rectangles to match the new number of + * rectangles in the region. This never goes to 0, however... + * + * Only do this stuff if the number of rectangles allocated is more than + * twice the number of rectangles in the region (a simple optimization...). + */ + if (newReg->numRects < (newReg->size >> 1)) + { + if (REGION_NOT_EMPTY (newReg)) + { + newReg->size = newReg->numRects; + newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size); + } + else + { + /* + * No point in doing the extra work involved in an Xrealloc if + * the region is empty + */ + newReg->size = 1; + g_free (newReg->rects); + newReg->rects = g_new (GdkRegionBox, 1); + } + } + g_free (oldRects); +} + + +/*====================================================================== + * Region Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miUnionNonO -- + * Handle a non-overlapping band for the union operation. Just + * Adds the rectangles into the region. Doesn't have to check for + * subsumption or anything. + * + * Results: + * None. + * + * Side Effects: + * pReg->numRects is incremented and the final rectangles overwritten + * with the rectangles we're passed. + * + *----------------------------------------------------------------------- + */ +static void +miUnionNonO (GdkRegion *pReg, + GdkRegionBox *r, + GdkRegionBox *rEnd, + gint y1, + gint y2) +{ + GdkRegionBox *pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + assert(y1 < y2); + + while (r != rEnd) + { + assert(r->x1 < r->x2); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = r->x1; + pNextRect->y1 = y1; + pNextRect->x2 = r->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + r++; + } +} + + +/*- + *----------------------------------------------------------------------- + * miUnionO -- + * Handle an overlapping band for the union operation. Picks the + * left-most rectangle each time and merges it into the region. + * + * Results: + * None. + * + * Side Effects: + * Rectangles are overwritten in pReg->rects and pReg->numRects will + * be changed. + * + *----------------------------------------------------------------------- + */ + +/* static void*/ +static void +miUnionO (GdkRegion *pReg, + GdkRegionBox *r1, + GdkRegionBox *r1End, + GdkRegionBox *r2, + GdkRegionBox *r2End, + gint y1, + gint y2) +{ + GdkRegionBox * pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + +#define MERGERECT(r) \ + if ((pReg->numRects != 0) && \ + (pNextRect[-1].y1 == y1) && \ + (pNextRect[-1].y2 == y2) && \ + (pNextRect[-1].x2 >= r->x1)) \ + { \ + if (pNextRect[-1].x2 < r->x2) \ + { \ + pNextRect[-1].x2 = r->x2; \ + assert(pNextRect[-1].x1rects); \ + pNextRect->y1 = y1; \ + pNextRect->y2 = y2; \ + pNextRect->x1 = r->x1; \ + pNextRect->x2 = r->x2; \ + pReg->numRects += 1; \ + pNextRect += 1; \ + } \ + assert(pReg->numRects<=pReg->size); \ + r++; + + assert (y1x1 < r2->x1) + { + MERGERECT(r1); + } + else + { + MERGERECT(r2); + } + } + + if (r1 != r1End) + { + do + { + MERGERECT(r1); + } while (r1 != r1End); + } + else while (r2 != r2End) + { + MERGERECT(r2); + } +} + +void +gdk_region_union (GdkRegion *region, + GdkRegion *other) +{ + /* checks all the simple cases */ + + /* + * region and other are the same or other is empty + */ + if ((region == other) || (!(other->numRects))) + return; + + /* + * region is empty + */ + if (!(region->numRects)) + { + miRegionCopy (region, other); + return; + } + + /* + * region completely subsumes otehr + */ + if ((region->numRects == 1) && + (region->extents.x1 <= other->extents.x1) && + (region->extents.y1 <= other->extents.y1) && + (region->extents.x2 >= other->extents.x2) && + (region->extents.y2 >= other->extents.y2)) + return; + + /* + * other completely subsumes region + */ + if ((other->numRects == 1) && + (other->extents.x1 <= region->extents.x1) && + (other->extents.y1 <= region->extents.y1) && + (other->extents.x2 >= region->extents.x2) && + (other->extents.y2 >= region->extents.y2)) + { + miRegionCopy(region, other); + return; + } + + miRegionOp (region, region, other, miUnionO, + miUnionNonO, miUnionNonO); + + region->extents.x1 = MIN (region->extents.x1, other->extents.x1); + region->extents.y1 = MIN (region->extents.y1, other->extents.y1); + region->extents.x2 = MAX (region->extents.x2, other->extents.x2); + region->extents.y2 = MAX (region->extents.y2, other->extents.y2); +} + + +/*====================================================================== + * Region Subtraction + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miSubtractNonO -- + * Deal with non-overlapping band for subtraction. Any parts from + * region 2 we discard. Anything from region 1 we add to the region. + * + * Results: + * None. + * + * Side Effects: + * pReg may be affected. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static void +miSubtractNonO1 (GdkRegion *pReg, + GdkRegionBox *r, + GdkRegionBox *rEnd, + gint y1, + gint y2) +{ + GdkRegionBox * pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + assert(y1x1x2); + MEMCHECK (pReg, pNextRect, pReg->rects); + pNextRect->x1 = r->x1; + pNextRect->y1 = y1; + pNextRect->x2 = r->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert (pReg->numRects <= pReg->size); + + r++; + } +} + +/*- + *----------------------------------------------------------------------- + * miSubtractO -- + * Overlapping band subtraction. x1 is the left-most point not yet + * checked. + * + * Results: + * None. + * + * Side Effects: + * pReg may have rectangles added to it. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static void +miSubtractO (GdkRegion *pReg, + GdkRegionBox *r1, + GdkRegionBox *r1End, + GdkRegionBox *r2, + GdkRegionBox *r2End, + gint y1, + gint y2) +{ + GdkRegionBox * pNextRect; + int x1; + + x1 = r1->x1; + + assert(y1rects[pReg->numRects]; + + while ((r1 != r1End) && (r2 != r2End)) + { + if (r2->x2 <= x1) + { + /* + * Subtrahend missed the boat: go to next subtrahend. + */ + r2++; + } + else if (r2->x1 <= x1) + { + /* + * Subtrahend preceeds minuend: nuke left edge of minuend. + */ + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend completely covered: advance to next minuend and + * reset left fence to edge of new minuend. + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend now used up since it doesn't extend beyond + * minuend + */ + r2++; + } + } + else if (r2->x1 < r1->x2) + { + /* + * Left part of subtrahend covers part of minuend: add uncovered + * part of minuend to region and skip to next subtrahend. + */ + assert(x1x1); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r2->x1; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend used up: advance to new... + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend used up + */ + r2++; + } + } + else + { + /* + * Minuend used up: add any remaining piece before advancing. + */ + if (r1->x2 > x1) + { + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r1->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + assert(pReg->numRects<=pReg->size); + } + r1++; + x1 = r1->x1; + } + } + + /* + * Add remaining minuend rectangles to region. + */ + while (r1 != r1End) + { + assert(x1x2); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r1->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + + r1++; + if (r1 != r1End) + { + x1 = r1->x1; + } + } +} + +/*- + *----------------------------------------------------------------------- + * gdk_region_subtract -- + * Subtract other from region and leave the result in region. + * + * Results: + * TRUE. + * + * Side Effects: + * region is overwritten. + * + *----------------------------------------------------------------------- + */ + +void +gdk_region_subtract (GdkRegion *region, + GdkRegion *other) +{ + /* check for trivial reject */ + if ((!(region->numRects)) || (!(other->numRects)) || + (!EXTENTCHECK(®ion->extents, &other->extents))) + return; + + miRegionOp (region, region, other, miSubtractO, + miSubtractNonO1, (nonOverlapFunc) NULL); + + /* + * Can't alter region's extents before we call miRegionOp because miRegionOp + * depends on the extents of those regions being the unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents (region); +} + +void +gdk_region_xor (GdkRegion *sra, + GdkRegion *srb) +{ + GdkRegion *trb; + + trb = gdk_region_copy (srb); + + gdk_region_subtract (trb, sra); + gdk_region_subtract (sra, srb); + + gdk_region_union (sra,trb); + + gdk_region_destroy (trb); +} + +/* + * Check to see if the region is empty. Assumes a region is passed + * as a parameter + */ +gboolean +gdk_region_empty (GdkRegion *r) +{ + if (r->numRects == 0) + return TRUE; + else + return FALSE; +} + +/* + * Check to see if two regions are equal + */ +gboolean +gdk_region_equal (GdkRegion *r1, + GdkRegion *r2) +{ + int i; + + if (r1->numRects != r2->numRects) return FALSE; + else if (r1->numRects == 0) return TRUE; + else if (r1->extents.x1 != r2->extents.x1) return FALSE; + else if (r1->extents.x2 != r2->extents.x2) return FALSE; + else if (r1->extents.y1 != r2->extents.y1) return FALSE; + else if (r1->extents.y2 != r2->extents.y2) return FALSE; + else + for(i=0; i < r1->numRects; i++ ) + { + if (r1->rects[i].x1 != r2->rects[i].x1) return FALSE; + else if (r1->rects[i].x2 != r2->rects[i].x2) return FALSE; + else if (r1->rects[i].y1 != r2->rects[i].y1) return FALSE; + else if (r1->rects[i].y2 != r2->rects[i].y2) return FALSE; + } + return TRUE; +} + +gboolean +gdk_region_point_in (GdkRegion *region, + int x, + int y) +{ + int i; + + if (region->numRects == 0) + return FALSE; + if (!INBOX(region->extents, x, y)) + return FALSE; + for (i=0; inumRects; i++) + { + if (INBOX (region->rects[i], x, y)) + return TRUE; + } + return FALSE; +} + +GdkOverlapType +gdk_region_rect_in (GdkRegion *region, + GdkRectangle *rectangle) +{ + GdkRegionBox *pbox; + GdkRegionBox *pboxEnd; + GdkRegionBox rect; + GdkRegionBox *prect = ▭ + gboolean partIn, partOut; + + gint rx = rectangle->x; + gint ry = rectangle->y; + + prect->x1 = rx; + prect->y1 = ry; + prect->x2 = rx + rectangle->width; + prect->y2 = ry + rectangle->height; + + /* this is (just) a useful optimization */ + if ((region->numRects == 0) || !EXTENTCHECK (®ion->extents, prect)) + return GDK_OVERLAP_RECTANGLE_OUT; + + partOut = FALSE; + partIn = FALSE; + + /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */ + for (pbox = region->rects, pboxEnd = pbox + region->numRects; + pbox < pboxEnd; + pbox++) + { + + if (pbox->y2 <= ry) + continue; /* getting up to speed or skipping remainder of band */ + + if (pbox->y1 > ry) + { + partOut = TRUE; /* missed part of rectangle above */ + if (partIn || (pbox->y1 >= prect->y2)) + break; + ry = pbox->y1; /* x guaranteed to be == prect->x1 */ + } + + if (pbox->x2 <= rx) + continue; /* not far enough over yet */ + + if (pbox->x1 > rx) + { + partOut = TRUE; /* missed part of rectangle to left */ + if (partIn) + break; + } + + if (pbox->x1 < prect->x2) + { + partIn = TRUE; /* definitely overlap */ + if (partOut) + break; + } + + if (pbox->x2 >= prect->x2) + { + ry = pbox->y2; /* finished with this band */ + if (ry >= prect->y2) + break; + rx = prect->x1; /* reset x out to left again */ + } + else + { + /* + * Because boxes in a band are maximal width, if the first box + * to overlap the rectangle doesn't completely cover it in that + * band, the rectangle must be partially out, since some of it + * will be uncovered in that band. partIn will have been set true + * by now... + */ + break; + } + + } + + return (partIn ? + ((ry < prect->y2) ? + GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) : + GDK_OVERLAP_RECTANGLE_OUT); +} diff --git a/gdk/linux-fb/gdkregion-generic.h b/gdk/linux-fb/gdkregion-generic.h new file mode 100644 index 0000000000..33d0683dfb --- /dev/null +++ b/gdk/linux-fb/gdkregion-generic.h @@ -0,0 +1,162 @@ +/* $TOG: region.h /main/9 1998/02/06 17:50:30 kaleb $ */ +/************************************************************************ + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +#ifndef __GDK_REGION_GENERIC_H__ +#define __GDK_REGION_GENERIC_H__ + +typedef GdkSegment GdkRegionBox; + +/* + * clip region + */ + +struct _GdkRegion +{ + long size; + long numRects; + GdkRegionBox *rects; + GdkRegionBox extents; +}; + +/* 1 if two BOXs overlap. + * 0 if two BOXs do not overlap. + * Remember, x2 and y2 are not in the region + */ +#define EXTENTCHECK(r1, r2) \ + ((r1)->x2 > (r2)->x1 && \ + (r1)->x1 < (r2)->x2 && \ + (r1)->y2 > (r2)->y1 && \ + (r1)->y1 < (r2)->y2) + +/* + * update region extents + */ +#define EXTENTS(r,idRect){\ + if((r)->x1 < (idRect)->extents.x1)\ + (idRect)->extents.x1 = (r)->x1;\ + if((r)->y1 < (idRect)->extents.y1)\ + (idRect)->extents.y1 = (r)->y1;\ + if((r)->x2 > (idRect)->extents.x2)\ + (idRect)->extents.x2 = (r)->x2;\ + if((r)->y2 > (idRect)->extents.y2)\ + (idRect)->extents.y2 = (r)->y2;\ + } + +/* + * Check to see if there is enough memory in the present region. + */ +#define MEMCHECK(reg, rect, firstrect){ \ + if ((reg)->numRects >= ((reg)->size - 1)) { \ + (firstrect) = g_renew (GdkRegionBox, (firstrect), 2 * (reg)->size); \ + (reg)->size *= 2; \ + (rect) = &(firstrect)[(reg)->numRects]; \ + } \ + } + +/* this routine checks to see if the previous rectangle is the same + * or subsumes the new rectangle to add. + */ + +#define CHECK_PREVIOUS(Reg, R, Rx1, Ry1, Rx2, Ry2)\ + (!(((Reg)->numRects > 0)&&\ + ((R-1)->y1 == (Ry1)) &&\ + ((R-1)->y2 == (Ry2)) &&\ + ((R-1)->x1 <= (Rx1)) &&\ + ((R-1)->x2 >= (Rx2)))) + +/* add a rectangle to the given Region */ +#define ADDRECT(reg, r, rx1, ry1, rx2, ry2){\ + if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + EXTENTS((r), (reg));\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + + + +/* add a rectangle to the given Region */ +#define ADDRECTNOX(reg, r, rx1, ry1, rx2, ry2){\ + if ((rx1 < rx2) && (ry1 < ry2) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + +#define EMPTY_REGION(pReg) pReg->numRects = 0 + +#define REGION_NOT_EMPTY(pReg) pReg->numRects + +#define INBOX(r, x, y) \ + ( ( ((r).x2 > x)) && \ + ( ((r).x1 <= x)) && \ + ( ((r).y2 > y)) && \ + ( ((r).y1 <= y)) ) + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * used to allocate buffers for points and link + * the buffers together + */ +typedef struct _POINTBLOCK { + GdkPoint pts[NUMPTSTOBUFFER]; + struct _POINTBLOCK *next; +} POINTBLOCK; + +#endif /* __GDK_REGION_GENERIC_H__ */ diff --git a/gdk/linux-fb/gdkselection-fb.c b/gdk/linux-fb/gdkselection-fb.c new file mode 100644 index 0000000000..1b154b0045 --- /dev/null +++ b/gdk/linux-fb/gdkselection-fb.c @@ -0,0 +1,104 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include + +#include "gdkproperty.h" +#include "gdkselection.h" +#include "gdkprivate.h" +#include "gdkprivate-fb.h" + + +gint +gdk_selection_owner_set (GdkWindow *owner, + GdkAtom selection, + guint32 time, + gint send_event) +{ + return FALSE; +} + +GdkWindow* +gdk_selection_owner_get (GdkAtom selection) +{ + return NULL; +} + +void +gdk_selection_convert (GdkWindow *requestor, + GdkAtom selection, + GdkAtom target, + guint32 time) +{ +} + +gint +gdk_selection_property_get (GdkWindow *requestor, + guchar **data, + GdkAtom *ret_type, + gint *ret_format) +{ + g_return_val_if_fail (requestor != NULL, 0); + g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0); + + return 0; +} + + +void +gdk_selection_send_notify (guint32 requestor, + GdkAtom selection, + GdkAtom target, + GdkAtom property, + guint32 time) +{ +} + +gint +gdk_text_property_to_text_list (GdkAtom encoding, gint format, + const guchar *text, gint length, + gchar ***list) +{ + return 0; +} + +void +gdk_free_text_list (gchar **list) +{ + g_return_if_fail (list != NULL); +} + +gint +gdk_string_to_compound_text (const gchar *str, + GdkAtom *encoding, gint *format, + guchar **ctext, gint *length) +{ + return 0; +} + +void gdk_free_compound_text (guchar *ctext) +{ +} diff --git a/gdk/linux-fb/gdkvisual-fb.c b/gdk/linux-fb/gdkvisual-fb.c new file mode 100644 index 0000000000..24468aafdc --- /dev/null +++ b/gdk/linux-fb/gdkvisual-fb.c @@ -0,0 +1,180 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gdkvisual.h" +#include "gdkprivate-fb.h" +#include "gdkinternals.h" +#include + +static GdkVisual *system_visual = NULL; + +#ifdef G_ENABLE_DEBUG + +#if 0 +static const gchar* visual_names[] = +{ + "static gray", + "grayscale", + "static color", + "pseudo color", + "true color", + "direct color", +}; +#endif + +#endif /* G_ENABLE_DEBUG */ + +void +gdk_visual_init (void) +{ + system_visual = g_new0(GdkVisual, 1); + + system_visual->depth = system_visual->bits_per_rgb = gdk_display->modeinfo.bits_per_pixel; + system_visual->byte_order = GDK_LSB_FIRST; + system_visual->colormap_size = 0; + + switch(gdk_display->sinfo.visual) + { + case FB_VISUAL_PSEUDOCOLOR: + system_visual->colormap_size = 1 << gdk_display->modeinfo.bits_per_pixel; + system_visual->type = GDK_VISUAL_PSEUDO_COLOR; + break; + case FB_VISUAL_DIRECTCOLOR: + system_visual->colormap_size = 1 << gdk_display->modeinfo.bits_per_pixel; + system_visual->type = GDK_VISUAL_DIRECT_COLOR; + case FB_VISUAL_TRUECOLOR: + if(gdk_display->sinfo.visual == GDK_VISUAL_TRUE_COLOR) + system_visual->type = GDK_VISUAL_TRUE_COLOR; + + system_visual->red_prec = MIN(system_visual->depth / 3, 8); + system_visual->red_shift = 0; + system_visual->red_mask = ((1 << (system_visual->red_prec + 1)) - 1) << system_visual->red_shift; + + system_visual->green_shift = system_visual->red_prec; + system_visual->green_prec = MIN(system_visual->depth / 3, 8); + system_visual->green_mask = ((1 << (system_visual->green_prec + 1)) - 1) << system_visual->green_shift; + + system_visual->blue_shift = system_visual->green_prec + system_visual->green_shift; + system_visual->blue_prec = MIN(system_visual->depth / 3, 8); + system_visual->blue_mask = ((1 << (system_visual->blue_prec + 1)) - 1) << system_visual->blue_shift; + break; + case FB_VISUAL_STATIC_PSEUDOCOLOR: + system_visual->type = GDK_VISUAL_STATIC_COLOR; + system_visual->colormap_size = 1 << gdk_display->modeinfo.bits_per_pixel; + break; + default: + g_assert_not_reached(); + break; + } +} + +GdkVisual* +gdk_visual_ref (GdkVisual *visual) +{ + return visual; +} + +void +gdk_visual_unref (GdkVisual *visual) +{ +} + +gint +gdk_visual_get_best_depth (void) +{ + return system_visual->depth; +} + +GdkVisualType +gdk_visual_get_best_type (void) +{ + return system_visual->type; +} + +GdkVisual* +gdk_visual_get_system (void) +{ + return system_visual; +} + +GdkVisual* +gdk_visual_get_best (void) +{ + return system_visual; +} + +GdkVisual* +gdk_visual_get_best_with_depth (gint depth) +{ + if(system_visual->depth != depth) + return NULL; + + return system_visual; +} + +GdkVisual* +gdk_visual_get_best_with_type (GdkVisualType visual_type) +{ + if(system_visual->type != visual_type) + return NULL; + + return system_visual; +} + +GdkVisual* +gdk_visual_get_best_with_both (gint depth, + GdkVisualType visual_type) +{ + if(system_visual->depth != depth) + return NULL; + + if(system_visual->type != visual_type) + return NULL; + + return system_visual; +} + +void +gdk_query_depths (gint **depths, + gint *count) +{ + *count = 1; + *depths = &system_visual->depth; +} + +void +gdk_query_visual_types (GdkVisualType **visual_types, + gint *count) +{ + *count = 1; + *visual_types = &system_visual->type; +} + +GList* +gdk_list_visuals (void) +{ + return g_list_append(NULL, gdk_visual_get_system()); +} diff --git a/gdk/linux-fb/gdkwindow-fb.c b/gdk/linux-fb/gdkwindow-fb.c new file mode 100644 index 0000000000..dad9ff3f35 --- /dev/null +++ b/gdk/linux-fb/gdkwindow-fb.c @@ -0,0 +1,1372 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gdk.h" +#include "config.h" + +#include "gdkwindow.h" +#include "gdkinputprivate.h" +#include "gdkprivate-fb.h" +#include "gdkinternals.h" + +#include + +GdkDrawableClass _gdk_windowing_window_class; + +static void recompute_drawable(GdkDrawable *drawable); + +static void +g_free_2nd(gpointer a, gpointer b, gpointer data) +{ + g_free(b); +} + +static void +gdk_fb_window_destroy (GdkDrawable *drawable) +{ + GdkWindowFBData *fbd = GDK_WINDOW_FBDATA(drawable); + + if (!GDK_DRAWABLE_DESTROYED (drawable)) + g_warning ("losing last reference to undestroyed window\n"); + + if(GDK_WINDOW_P(drawable)->mapped) + gdk_window_hide(drawable); + + if(fbd->cursor) + gdk_cursor_unref(fbd->cursor); + + if(fbd->properties) + { + g_hash_table_foreach(fbd->properties, g_free_2nd, NULL); + g_hash_table_destroy(fbd->properties); + } + + g_free (GDK_DRAWABLE_FBDATA (drawable)); +} + +static GdkWindow * +gdk_fb_window_alloc (void) +{ + GdkWindow *window; + GdkWindowPrivate *private; + + static gboolean initialized = FALSE; + + if (!initialized) + { + initialized = TRUE; + + _gdk_windowing_window_class = _gdk_fb_drawable_class; + _gdk_windowing_window_class.destroy = gdk_fb_window_destroy; + } + + window = _gdk_window_alloc (); + private = (GdkWindowPrivate *)window; + + private->drawable.klass = &_gdk_window_class; + private->drawable.klass_data = g_new0 (GdkWindowFBData, 1); + private->drawable.depth = gdk_display->modeinfo.bits_per_pixel; + private->drawable.colormap = gdk_colormap_get_system (); + GDK_WINDOW_FBDATA(private)->event_mask = GDK_STRUCTURE_MASK; + + return window; +} + +#include "/usr/include/X11/bitmaps/left_ptr" +#include "/usr/include/X11/bitmaps/left_ptrmsk" + +void +gdk_window_init (void) +{ + GdkBitmap *ptr, *mask; + GdkCursor *cursor; + GdkWindowPrivate *private; + + gdk_parent_root = gdk_fb_window_alloc (); + private = (GdkWindowPrivate *)gdk_parent_root; + + private->drawable.window_type = GDK_WINDOW_ROOT; + private->drawable.width = gdk_screen_width (); + private->drawable.height = gdk_screen_height (); + private->mapped = TRUE; + private->x = 0; + private->y = 0; + + GDK_DRAWABLE_FBDATA(private)->mem = gdk_display->fbmem; + GDK_DRAWABLE_FBDATA(private)->rowstride = gdk_display->modeinfo.xres * (gdk_display->modeinfo.bits_per_pixel >> 3); + GDK_DRAWABLE_FBDATA(private)->lim_x = gdk_display->modeinfo.xres; + GDK_DRAWABLE_FBDATA(private)->lim_y = gdk_display->modeinfo.yres; + GDK_WINDOW_FBDATA(private)->event_mask = GDK_EXPOSURE_MASK; + + gdk_fb_drawable_clear(gdk_parent_root); + + ptr = gdk_bitmap_create_from_data(gdk_parent_root, left_ptr_bits, left_ptr_width, left_ptr_height); + mask = gdk_bitmap_create_from_data(gdk_parent_root, left_ptrmsk_bits, left_ptrmsk_width, left_ptrmsk_height); + cursor = gdk_cursor_new_from_pixmap(ptr, mask, NULL, NULL, left_ptr_x_hot, left_ptr_y_hot); + + gdk_window_set_cursor(gdk_parent_root, cursor); +} + +GdkWindow* +gdk_window_new (GdkWindow *parent, + GdkWindowAttr *attributes, + gint attributes_mask) +{ + GdkWindow *window; + GdkWindowPrivate *private; + GdkWindowPrivate *parent_private; + GdkVisual *visual; + + int x, y, depth; + + g_return_val_if_fail (attributes != NULL, NULL); + + if (!parent || attributes->window_type != GDK_WINDOW_CHILD) + parent = gdk_parent_root; + + parent_private = (GdkWindowPrivate*) parent; + if (GDK_DRAWABLE_DESTROYED (parent)) + return NULL; + + window = gdk_fb_window_alloc (); + private = (GdkWindowPrivate *)window; + + private->parent = parent; + + if (attributes_mask & GDK_WA_X) + x = attributes->x; + else + x = 0; + + if (attributes_mask & GDK_WA_Y) + y = attributes->y; + else + y = 0; + + gdk_window_set_events(window, attributes->event_mask); + + if (attributes_mask & GDK_WA_VISUAL) + visual = attributes->visual; + + private->x = x; + private->y = y; + private->drawable.width = (attributes->width > 1) ? (attributes->width) : (1); + private->drawable.height = (attributes->height > 1) ? (attributes->height) : (1); + private->drawable.window_type = attributes->window_type; + GDK_DRAWABLE_FBDATA(private)->mem = gdk_display->fbmem; + GDK_DRAWABLE_FBDATA(private)->rowstride = gdk_display->modeinfo.xres * (gdk_display->modeinfo.bits_per_pixel >> 3); + gdk_window_move_resize (window, x, y, + private->drawable.width, private->drawable.height); + + if (attributes->wclass == GDK_INPUT_OUTPUT) + { + depth = visual->depth; + + private->input_only = FALSE; + private->drawable.depth = depth; + + if ((attributes_mask & GDK_WA_COLORMAP) + && attributes->colormap) + private->drawable.colormap = attributes->colormap; + + switch (private->drawable.window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_CHILD: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: + break; + + case GDK_WINDOW_ROOT: + g_error ("cannot make windows of type GDK_WINDOW_ROOT"); + break; + case GDK_WINDOW_PIXMAP: + g_error ("cannot make windows of type GDK_WINDOW_PIXMAP (use gdk_pixmap_new)"); + break; + } + } + else + { + depth = 0; + private->input_only = TRUE; + GDK_WINDOW_FBDATA(private)->level = 10000; + private->drawable.colormap = NULL; + } + + gdk_drawable_ref (window); + + if (private->drawable.colormap) + gdk_colormap_ref (private->drawable.colormap); + + gdk_window_set_cursor (window, ((attributes_mask & GDK_WA_CURSOR) ? + (attributes->cursor) : + NULL)); + + if (parent_private) + parent_private->children = g_list_prepend (parent_private->children, window); + if(parent_private && parent_private->children->next) + GDK_WINDOW_FBDATA(private)->level = GDK_WINDOW_FBDATA(parent_private->children->next->data)->level + 1; + + return window; +} + +/* Call this function when you want a window and all its children to + * disappear. When xdestroy is true, a request to destroy the XWindow + * is sent out. When it is false, it is assumed that the XWindow has + * been or will be destroyed by destroying some ancestor of this + * window. + */ +void +_gdk_windowing_window_destroy(GdkWindow *window, + gboolean recursing, + gboolean foreign_destroy) +{ + GdkWindowPrivate *private; + GdkWindowPrivate *temp_private; + GdkWindow *temp_window; + GList *children; + GList *tmp; + gboolean our_destroy = !foreign_destroy; + + g_return_if_fail (window != NULL); + + private = (GdkWindowPrivate*) window; + + switch (private->drawable.window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_CHILD: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: + case GDK_WINDOW_FOREIGN: + if (!private->drawable.destroyed) + { + if (private->parent) + { + GdkWindowPrivate *parent_private = (GdkWindowPrivate *)private->parent; + if (parent_private->children) + parent_private->children = g_list_remove (parent_private->children, window); + } + + if (private->bg_pixmap && private->bg_pixmap != GDK_PARENT_RELATIVE_BG) + { + gdk_pixmap_unref (private->bg_pixmap); + private->bg_pixmap = NULL; + } + + if (GDK_DRAWABLE_TYPE (window) != GDK_WINDOW_FOREIGN) + { + children = tmp = private->children; + private->children = NULL; + + while (tmp) + { + temp_window = tmp->data; + tmp = tmp->next; + + temp_private = (GdkWindowPrivate*) temp_window; + if (temp_private) + _gdk_windowing_window_destroy (temp_window, !FALSE, + !our_destroy); + } + + g_list_free (children); + } + + if (private->extension_events != 0) + gdk_input_window_destroy (window); + + if (private->filters) + { + tmp = private->filters; + + while (tmp) + { + g_free (tmp->data); + tmp = tmp->next; + } + + g_list_free (private->filters); + private->filters = NULL; + } + + if (private->drawable.window_type == GDK_WINDOW_FOREIGN) + { + if (our_destroy && (private->parent != NULL)) + { + /* It's somebody elses window, but in our heirarchy, + * so reparent it to the root window, and then send + * it a delete event, as if we were a WM + */ + gdk_error_trap_push (); + gdk_window_hide (window); + gdk_window_reparent (window, NULL, 0, 0); + + gdk_flush (); + gdk_error_trap_pop (); + } + } + + if (private->drawable.colormap) + gdk_colormap_unref (private->drawable.colormap); + + private->mapped = FALSE; + private->drawable.destroyed = TRUE; + } + break; + + case GDK_WINDOW_ROOT: + g_error ("attempted to destroy root window"); + break; + + case GDK_WINDOW_PIXMAP: + g_error ("called gdk_window_destroy on a pixmap (use gdk_pixmap_unref)"); + break; + } +} + +/* This function is called when the XWindow is really gone. */ + +void +gdk_window_destroy_notify (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + + if (!GDK_DRAWABLE_DESTROYED (window)) + { + if (GDK_DRAWABLE_TYPE(window) != GDK_WINDOW_FOREIGN) + g_warning ("GdkWindow %#x unexpectedly destroyed", GPOINTER_TO_UINT(window)); + + _gdk_windowing_window_destroy (window, TRUE, TRUE); + } + + gdk_drawable_unref (window); +} + +static gboolean all_parents_shown(GdkWindowPrivate *private) +{ + while(private->mapped) + { + if(private->parent) + private = (GdkWindowPrivate *)private->parent; + else + return TRUE; + } + + return FALSE; +} + +static void +send_map_events(GdkWindowPrivate *private, gboolean is_map) +{ + GdkEvent *event; + GList *l; + GdkWindow *parent = private->parent; + + if(!private->mapped || GDK_DRAWABLE_DESTROYED(private)) + return; + + if(is_map) + gdk_event_make((GdkWindow *)private, GDK_MAP, TRUE); + + if(private->input_only) + return; + + if(!parent) + parent = (GdkWindow *)private; + + if(((GDK_DRAWABLE_FBDATA(private)->abs_x > GDK_DRAWABLE_FBDATA(parent)->lim_x) + || (GDK_DRAWABLE_FBDATA(private)->abs_y > GDK_DRAWABLE_FBDATA(parent)->lim_y) + || (GDK_DRAWABLE_FBDATA(private)->lim_x < GDK_DRAWABLE_FBDATA(parent)->llim_x) + || (GDK_DRAWABLE_FBDATA(private)->lim_y < GDK_DRAWABLE_FBDATA(parent)->llim_y))) + return; + + if(is_map) + gdk_window_clear((GdkWindow *)private); + + event = gdk_event_new(); + event->expose.type = GDK_EXPOSE; + event->expose.window = gdk_window_ref((GdkWindow *)private); + if(GDK_DRAWABLE_FBDATA(private)->abs_x > GDK_DRAWABLE_FBDATA(parent)->llim_x) + event->expose.area.x = 0; + else + event->expose.area.x = GDK_DRAWABLE_FBDATA(parent)->llim_x - GDK_DRAWABLE_FBDATA(private)->abs_x; + + if(GDK_DRAWABLE_FBDATA(private)->abs_y > GDK_DRAWABLE_FBDATA(parent)->llim_y) + event->expose.area.y = 0; + else + event->expose.area.y = GDK_DRAWABLE_FBDATA(parent)->llim_y - GDK_DRAWABLE_FBDATA(private)->abs_y; + + event->expose.area.width = MIN(private->drawable.width, + GDK_DRAWABLE_FBDATA(private)->lim_x - GDK_DRAWABLE_FBDATA(private)->abs_x); + event->expose.area.height = MIN(private->drawable.height, + GDK_DRAWABLE_FBDATA(private)->lim_y - GDK_DRAWABLE_FBDATA(private)->abs_y); + if(event->expose.area.width > 0 + && event->expose.area.height > 0) + { + gdk_event_queue_append(event); + for(l = private->children; l; l = l->next) + send_map_events(l->data, is_map); + } + else + gdk_event_free(event); + +} + +void +gdk_fb_redraw_all(void) +{ + send_map_events((GdkWindowPrivate *)gdk_parent_root, FALSE); +} + +static +void gdk_fb_window_visibility_crossing(GdkWindow *window, gboolean is_show) +{ + gint winx, winy; + GdkModifierType my_mask; + + gdk_input_ps2_get_mouseinfo(&winx, &winy, &my_mask); + + if(winx >= GDK_DRAWABLE_FBDATA(window)->llim_x + && winx < GDK_DRAWABLE_FBDATA(window)->lim_x + && winy >= GDK_DRAWABLE_FBDATA(window)->llim_y + && winy < GDK_DRAWABLE_FBDATA(window)->lim_y && 0) + { + GdkWindow *oldwin, *newwin, *curwin; + GdkEvent *event; + + curwin = gdk_window_get_pointer(NULL, NULL, NULL, NULL); + + if(is_show) + { + /* Window is about to be shown */ + oldwin = curwin; + newwin = window; + } + else + { + /* Window is about to be hidden */ + oldwin = window; + newwin = curwin; + } + event = gdk_event_make(oldwin, GDK_LEAVE_NOTIFY, TRUE); + if(event) + { + guint x_int, y_int; + event->crossing.subwindow = gdk_window_ref(newwin); + gdk_window_get_root_origin(oldwin, &x_int, &y_int); + event->crossing.x = winx - x_int; + event->crossing.y = winy - y_int; + event->crossing.x_root = winx; + event->crossing.y_root = winy; + event->crossing.mode = GDK_CROSSING_NORMAL; + event->crossing.detail = GDK_NOTIFY_UNKNOWN; + event->crossing.focus = FALSE; + event->crossing.state = my_mask; + } + + event = gdk_event_make(newwin, GDK_ENTER_NOTIFY, TRUE); + if(event) + { + guint x_int, y_int; + event->crossing.subwindow = gdk_window_ref(oldwin); + gdk_window_get_root_origin(newwin, &x_int, &y_int); + event->crossing.x = winx - x_int; + event->crossing.y = winy - y_int; + event->crossing.x_root = winx; + event->crossing.y_root = winy; + event->crossing.mode = GDK_CROSSING_NORMAL; + event->crossing.detail = GDK_NOTIFY_UNKNOWN; + event->crossing.focus = FALSE; + event->crossing.state = my_mask; + } + } +} + +void +gdk_window_show (GdkWindow *window) +{ + GdkWindowPrivate *private; + + g_return_if_fail (window != NULL); + + private = (GdkWindowPrivate*) window; + + if (!private->drawable.destroyed && !private->mapped) + { + private->mapped = TRUE; + + if(all_parents_shown((GdkWindowPrivate *)private->parent)) + { + recompute_drawable((GdkDrawable *)window); + send_map_events(private, TRUE); + + private->mapped = FALSE; /* a hack, ayup, to make gdk_window_get_pointer get the other window */ + gdk_fb_window_visibility_crossing(window, TRUE); + private->mapped = TRUE; + } + } +} + +static gboolean +rects_overlap(GdkRectangle *r1, GdkRectangle *r2) +{ + if(r1->x > (r2->x + r2->width)) + return FALSE; + if(r2->x > (r1->x + r1->width)) + return FALSE; + if(r1->y > (r2->y + r2->height)) + return FALSE; + if(r2->y > (r1->y + r1->height)) + return FALSE; + + return TRUE; +} + +static void +gdk_window_on_hide(GdkWindow *window, GdkRectangle *in_rect, GdkWindow *ignore) +{ + GdkEvent *event; + GdkRectangle this_rect, test_rect; + + if(!GDK_WINDOW_P(window)->mapped || window == ignore || GDK_WINDOW_P(window)->input_only) + return; + + this_rect.x = MAX(GDK_DRAWABLE_FBDATA(window)->llim_x - GDK_DRAWABLE_FBDATA(window)->abs_x, 0); + + this_rect.y = MAX(GDK_DRAWABLE_FBDATA(window)->llim_y - GDK_DRAWABLE_FBDATA(window)->abs_y, 0); + + this_rect.width = MIN(GDK_DRAWABLE_P(window)->width, + GDK_DRAWABLE_FBDATA(window)->lim_x - GDK_DRAWABLE_FBDATA(window)->abs_x); + this_rect.height = MIN(GDK_DRAWABLE_P(window)->height, + GDK_DRAWABLE_FBDATA(window)->lim_y - GDK_DRAWABLE_FBDATA(window)->abs_y); + + test_rect = this_rect; + test_rect.x += GDK_DRAWABLE_FBDATA(window)->abs_x; + test_rect.y += GDK_DRAWABLE_FBDATA(window)->abs_y; + if(this_rect.width > 0 + && this_rect.height > 0 + && rects_overlap(&test_rect, in_rect)) + { + GList *l; + + event = gdk_event_make(window, GDK_EXPOSE, TRUE); + if(event) + event->expose.area = this_rect; + + for(l = GDK_WINDOW_P(window)->children; l; l = l->next) + gdk_window_on_hide(l->data, in_rect, ignore); + } +} + +void +gdk_window_hide (GdkWindow *window) +{ + GdkWindowPrivate *private; + + g_return_if_fail (window != NULL); + + private = (GdkWindowPrivate*) window; + + if (!private->drawable.destroyed && private->mapped) + { + GdkEvent *event; + GdkRectangle r; + + event = gdk_event_make(window, GDK_UNMAP, TRUE); + + r.x = GDK_DRAWABLE_FBDATA(window)->llim_x; + r.y = GDK_DRAWABLE_FBDATA(window)->llim_y; + r.width = GDK_DRAWABLE_FBDATA(window)->lim_x - r.x; + r.height = GDK_DRAWABLE_FBDATA(window)->lim_y - r.y; + + private->mapped = FALSE; + gdk_window_on_hide(private->parent, &r, NULL); + + if(private->parent == gdk_parent_root) + gdk_fb_drawable_clear((GdkDrawable *)gdk_parent_root); + + if(all_parents_shown((GdkWindowPrivate *)private->parent)) + gdk_fb_window_visibility_crossing(window, FALSE); + } +} + +void +gdk_window_withdraw (GdkWindow *window) +{ + gdk_window_hide(window); +} + +void +gdk_window_move (GdkWindow *window, + gint x, + gint y) +{ + GdkWindowPrivate *private = (GdkWindowPrivate *)window; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + gdk_window_move_resize (window, x, y, + private->drawable.width, private->drawable.height); +} + +void +gdk_window_resize (GdkWindow *window, + gint width, + gint height) +{ + GdkWindowPrivate *private; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + private = (GdkWindowPrivate*) window; + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + gdk_window_move_resize(window, private->x, private->y, width, height); +} + +static void +recompute_abs_positions(GdkDrawable *drawable, gint parent_x, gint parent_y, + gint parent_llim_x, gint parent_llim_y, + gint parent_lim_x, gint parent_lim_y) +{ + GList *l; + + if(GDK_IS_WINDOW(drawable)) + { + GdkWindowPrivate *private = GDK_WINDOW_P(drawable); + + if(!private->mapped) + return; + + GDK_DRAWABLE_FBDATA(private)->abs_x = parent_x + private->x; + GDK_DRAWABLE_FBDATA(private)->abs_y = parent_y + private->y; + GDK_DRAWABLE_FBDATA(private)->llim_x = MIN(MAX(parent_llim_x, GDK_DRAWABLE_FBDATA(private)->abs_x), + parent_lim_x); + GDK_DRAWABLE_FBDATA(private)->llim_y = MIN(MAX(parent_llim_y, GDK_DRAWABLE_FBDATA(private)->abs_y), + parent_lim_y); + GDK_DRAWABLE_FBDATA(private)->lim_x = MAX(MIN(parent_lim_x, GDK_DRAWABLE_FBDATA(private)->abs_x + GDK_DRAWABLE_P(private)->width), + GDK_DRAWABLE_FBDATA(private)->llim_x); + GDK_DRAWABLE_FBDATA(private)->lim_y = MAX(MIN(parent_lim_y, + GDK_DRAWABLE_FBDATA(private)->abs_y + GDK_DRAWABLE_P(private)->height), + GDK_DRAWABLE_FBDATA(private)->llim_y); + + g_assert(GDK_DRAWABLE_FBDATA(private)->llim_x <= GDK_DRAWABLE_FBDATA(private)->lim_x); + g_assert(GDK_DRAWABLE_FBDATA(private)->llim_y <= GDK_DRAWABLE_FBDATA(private)->lim_y); + + for(l = private->children; l; l = l->next) + recompute_abs_positions(l->data, GDK_DRAWABLE_FBDATA(private)->abs_x, GDK_DRAWABLE_FBDATA(private)->abs_y, + GDK_DRAWABLE_FBDATA(private)->llim_x, GDK_DRAWABLE_FBDATA(private)->llim_y, + GDK_DRAWABLE_FBDATA(private)->lim_x, GDK_DRAWABLE_FBDATA(private)->lim_y); + } + else + { + GDK_DRAWABLE_FBDATA(drawable)->abs_x = 0; + GDK_DRAWABLE_FBDATA(drawable)->abs_y = 0; + GDK_DRAWABLE_FBDATA(drawable)->llim_x = 0; + GDK_DRAWABLE_FBDATA(drawable)->llim_y = 0; + GDK_DRAWABLE_FBDATA(drawable)->lim_x = GDK_DRAWABLE_P(drawable)->width; + GDK_DRAWABLE_FBDATA(drawable)->lim_y = GDK_DRAWABLE_P(drawable)->height; + } +} + +static void +recompute_drawable(GdkDrawable *drawable) +{ + if(GDK_IS_WINDOW(drawable)) + { + GdkWindowPrivate *private = GDK_WINDOW_P(drawable); + GdkWindow *parent; + + parent = private->parent; + if(!parent) + parent = gdk_parent_root; + + recompute_abs_positions(drawable, GDK_DRAWABLE_FBDATA(parent)->abs_x, + GDK_DRAWABLE_FBDATA(parent)->abs_y, + GDK_DRAWABLE_FBDATA(parent)->llim_x, + GDK_DRAWABLE_FBDATA(parent)->llim_y, + GDK_DRAWABLE_FBDATA(parent)->lim_x, + GDK_DRAWABLE_FBDATA(parent)->lim_y); + } + else + recompute_abs_positions(drawable, 0, 0, 0, 0, INT_MAX, INT_MAX); +} + +void +gdk_window_move_resize (GdkWindow *window, + gint x, + gint y, + gint width, + gint height) +{ + GdkWindowPrivate *private; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + private = (GdkWindowPrivate*) window; + + if (!GDK_DRAWABLE_DESTROYED (window)) + { + private->x = x; + private->y = y; + GDK_DRAWABLE_P(private)->width = width; + GDK_DRAWABLE_P(private)->height = height; + + if(private->mapped) + { + GdkRectangle r; + + r.x = GDK_DRAWABLE_FBDATA(window)->llim_x; + r.y = GDK_DRAWABLE_FBDATA(window)->llim_y; + r.width = GDK_DRAWABLE_FBDATA(window)->lim_x - r.x; + r.height = GDK_DRAWABLE_FBDATA(window)->lim_y - r.y; + + recompute_drawable((GdkDrawable *)window); + send_map_events(private, FALSE); + + gdk_window_on_hide(private->parent, &r, window); + } + } +} + +void +gdk_window_reparent (GdkWindow *window, + GdkWindow *new_parent, + gint x, + gint y) +{ + GdkWindowPrivate *window_private; + GdkWindowPrivate *parent_private; + GdkWindowPrivate *old_parent_private; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (new_parent != NULL); + g_return_if_fail (GDK_IS_WINDOW (new_parent)); + + if (!new_parent) + new_parent = gdk_parent_root; + + window_private = (GdkWindowPrivate*) window; + old_parent_private = (GdkWindowPrivate*)window_private->parent; + parent_private = (GdkWindowPrivate*) new_parent; + + g_assert(window_private->drawable.colormap); + + window_private->parent = new_parent; + + if (old_parent_private) + old_parent_private->children = g_list_remove (old_parent_private->children, window); + + parent_private->children = g_list_prepend (parent_private->children, window); + + if(window_private->mapped) + recompute_drawable((GdkDrawable *)window); +} + + +void +_gdk_windowing_window_clear_area (GdkWindow *window, + gint x, + gint y, + gint width, + gint height) +{ + GdkPixmap *bgpm; + GdkWindow *relto; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if(GDK_WINDOW_P(window)->input_only) + return; + + bgpm = GDK_WINDOW_P(window)->bg_pixmap; + for(relto = window; bgpm == GDK_PARENT_RELATIVE_BG && relto; relto = GDK_WINDOW_P(relto)->parent) + bgpm = GDK_WINDOW_P(relto)->bg_pixmap; + + if(bgpm && bgpm != GDK_NO_BG) + { + int curx, cury; + int xtrans, ytrans; + int xstep, ystep; + + return; /* Don't bother doing this - gtk+ will do it itself using GC tiles */ + + xtrans = GDK_DRAWABLE_FBDATA(relto)->abs_x - GDK_DRAWABLE_FBDATA(window)->abs_x; + ytrans = GDK_DRAWABLE_FBDATA(relto)->abs_y - GDK_DRAWABLE_FBDATA(window)->abs_y; + + for(cury = y - ytrans; cury < (y - ytrans + height); cury += ystep) + { + int drawh = cury % GDK_DRAWABLE_P(bgpm)->height; + ystep = GDK_DRAWABLE_P(bgpm)->height - drawh; + + for(curx = x - xtrans; curx < (x - xtrans + width); curx += xstep) + { + int draww = curx % GDK_DRAWABLE_P(bgpm)->width; + xstep = GDK_DRAWABLE_P(bgpm)->width - draww; + + gdk_fb_draw_drawable_2(window, NULL, bgpm, + draww, drawh, curx + xtrans, cury + ytrans, + xstep, ystep, FALSE, TRUE); + } + } + } + else if(!bgpm) + gdk_fb_draw_rectangle(window, NULL, TRUE, x, y, width, height); +} + +/* What's the diff? */ +void +_gdk_windowing_window_clear_area_e (GdkWindow *window, + gint x, + gint y, + gint width, + gint height) +{ + _gdk_windowing_window_clear_area(window, x, y, width, height); +} + +static gint +compare_window_levels(gconstpointer a, gconstpointer b) +{ + return (GDK_WINDOW_FBDATA(b)->level - GDK_WINDOW_FBDATA(a)->level); +} + +/* Child list is sorted bottom-to-top */ +static void +gdk_window_resort_children(GdkWindow *win) +{ + GdkWindowPrivate *private = GDK_WINDOW_P(win); + + private->children = g_list_sort(private->children, compare_window_levels); + + /* Now the fun part - redraw */ + if(GDK_WINDOW_P(win)->parent) + send_map_events(private, FALSE); +} + +void +gdk_window_raise (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + GDK_WINDOW_FBDATA(window)->level++; + + if(GDK_WINDOW_P(window)->parent) + gdk_window_resort_children(GDK_WINDOW_P(window)->parent); +} + +void +gdk_window_lower (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + GDK_WINDOW_FBDATA(window)->level--; + + if(GDK_WINDOW_P(window)->parent) + gdk_window_resort_children(GDK_WINDOW_P(window)->parent); +} + +void +gdk_window_set_hints (GdkWindow *window, + gint x, + gint y, + gint min_width, + gint min_height, + gint max_width, + gint max_height, + gint flags) +{ +} + +void +gdk_window_set_geometry_hints (GdkWindow *window, + GdkGeometry *geometry, + GdkWindowHints geom_mask) +{ +} + +void +gdk_window_set_title (GdkWindow *window, + const gchar *title) +{ +} + +void +gdk_window_set_role (GdkWindow *window, + const gchar *role) +{ +} + +void +gdk_window_set_transient_for (GdkWindow *window, + GdkWindow *parent) +{ + GDK_WINDOW_FBDATA(window)->level = GDK_WINDOW_FBDATA(parent)->level + 1; +} + +void +gdk_window_set_background (GdkWindow *window, + GdkColor *color) +{ + GdkWindowPrivate *private = (GdkWindowPrivate *)window; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + private->bg_color = *color; + + if (private->bg_pixmap && + private->bg_pixmap != GDK_PARENT_RELATIVE_BG && + private->bg_pixmap != GDK_NO_BG) + { + gdk_pixmap_unref (private->bg_pixmap); + private->bg_pixmap = NULL; + } +} + +void +gdk_window_set_back_pixmap (GdkWindow *window, + GdkPixmap *pixmap, + gboolean parent_relative) +{ + GdkWindowPrivate *private = (GdkWindowPrivate *)window; + GdkPixmap *old_pixmap; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (pixmap == NULL || !parent_relative); + + old_pixmap = private->bg_pixmap; + + if (parent_relative) + { + private->bg_pixmap = GDK_PARENT_RELATIVE_BG; + } + else + { + if (pixmap) + { + gdk_pixmap_ref (pixmap); + private->bg_pixmap = pixmap; + } + else + { + private->bg_pixmap = GDK_NO_BG; + } + } + + if (old_pixmap && + old_pixmap != GDK_PARENT_RELATIVE_BG && + old_pixmap != GDK_NO_BG) + gdk_pixmap_unref (old_pixmap); +} + +void +gdk_window_set_cursor (GdkWindow *window, + GdkCursor *cursor) +{ + GdkCursor *old_cursor = GDK_WINDOW_FBDATA(window)->cursor; + + GDK_WINDOW_FBDATA(window)->cursor = cursor?gdk_cursor_ref(cursor):NULL; + + if(old_cursor) + gdk_cursor_unref(old_cursor); +} + +void +gdk_window_get_geometry (GdkWindow *window, + gint *x, + gint *y, + gint *width, + gint *height, + gint *depth) +{ + GdkWindowPrivate *private = (GdkWindowPrivate *)window; + + g_return_if_fail (window == NULL || GDK_IS_WINDOW (window)); + + if (!window) + window = gdk_parent_root; + + if (!GDK_DRAWABLE_DESTROYED (window)) + { + + if (x) + *x = private->x; + if (y) + *y = private->y; + if (width) + *width = GDK_DRAWABLE_P(window)->width; + if (height) + *height = GDK_DRAWABLE_P(window)->height; + if (depth) + *depth = gdk_display->modeinfo.bits_per_pixel; + } +} + +gboolean +gdk_window_get_origin (GdkWindow *window, + gint *x, + gint *y) +{ + g_return_val_if_fail (window != NULL, 0); + + if (x) + *x = GDK_DRAWABLE_FBDATA(window)->abs_x; + if (y) + *y = GDK_DRAWABLE_FBDATA(window)->abs_y; + + return TRUE; +} + +gboolean +gdk_window_get_deskrelative_origin (GdkWindow *window, + gint *x, + gint *y) +{ + gint tx = 0; + gint ty = 0; + gboolean return_val; + + g_return_val_if_fail (window != NULL, 0); + + if (!GDK_DRAWABLE_DESTROYED (window)) + { + tx = GDK_DRAWABLE_FBDATA(window)->abs_x; + ty = GDK_DRAWABLE_FBDATA(window)->abs_y; + + return_val = TRUE; + } + else + return_val = FALSE; + + if (x) + *x = tx; + if (y) + *y = ty; + + return return_val; +} + +void +gdk_window_get_root_origin (GdkWindow *window, + gint *x, + gint *y) +{ + gdk_window_get_deskrelative_origin(window, x, y); +} + +GdkWindow* +gdk_window_get_pointer (GdkWindow *window, + gint *x, + gint *y, + GdkModifierType *mask) +{ + GdkWindow *return_val; + int winx = 0; + int winy = 0; + int x_int, y_int; + GdkModifierType my_mask; + + g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL); + + if (!window) + window = gdk_parent_root; + + gdk_window_get_root_origin(window, &x_int, &y_int); + gdk_input_ps2_get_mouseinfo(&winx, &winy, &my_mask); + + winx -= x_int; + winy -= y_int; + + if (x) + *x = winx; + if (y) + *y = winy; + if (mask) + *mask = my_mask; + + return_val = NULL; + + if((winx >= 0) && (winx < GDK_DRAWABLE_P(window)->width) + && (winy >= 0) && (winy < GDK_DRAWABLE_P(window)->height)) + { + GdkWindowPrivate *private; + GdkWindowPrivate *sub; + int subx = winx, suby = winy; + + for(private = sub = (GdkWindowPrivate *)window; sub; private = sub) + { + GList *ltmp; + + for(ltmp = private->children; ltmp; ltmp = ltmp->next) + { + sub = ltmp->data; + + if(!sub->mapped) + continue; + + if(subx >= sub->x + && (subx < (GDK_DRAWABLE_P(sub)->width + sub->x)) + && (suby >= sub->y) + && (suby < (GDK_DRAWABLE_P(sub)->height + sub->y))) + { + subx -= sub->x; + suby -= sub->y; + break; + } + } + + if(!ltmp) + { + sub = NULL; + break; + } + } + + return_val = (GdkWindow *)private; + } + + if(!return_val) + return_val = gdk_parent_root; + + return return_val; +} + +GdkWindow* +gdk_window_at_pointer (gint *win_x, + gint *win_y) +{ + gint rx, ry; + GdkWindow *retval = gdk_window_get_pointer(NULL, win_x, win_y, NULL); + + if(retval) + { + gdk_window_get_origin(retval, &ry, &rx); + if(win_x) + (*win_x) -= rx; + if(win_y) + (*win_y) -= ry; + } + + return retval; +} + +GList* +gdk_window_get_children (GdkWindow *window) +{ + GList *children; + + g_return_val_if_fail (window != NULL, NULL); + g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + + if (GDK_DRAWABLE_DESTROYED (window)) + return NULL; + + children = NULL; + + return ((GdkWindowPrivate *)window)->children; /* Need to copy this list? */ +} + +GdkEventMask +gdk_window_get_events (GdkWindow *window) +{ + g_return_val_if_fail (window != NULL, 0); + g_return_val_if_fail (GDK_IS_WINDOW (window), 0); + + if (GDK_DRAWABLE_DESTROYED (window)) + return 0; + else + return GDK_WINDOW_FBDATA(window)->event_mask; +} + +void +gdk_window_set_events (GdkWindow *window, + GdkEventMask event_mask) +{ + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (!GDK_DRAWABLE_DESTROYED (window)) + GDK_WINDOW_FBDATA(window)->event_mask = event_mask; +} + +void +gdk_window_add_colormap_windows (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +/* + * This needs the X11 shape extension. + * If not available, shaped windows will look + * ugly, but programs still work. Stefan Wille + */ +void +gdk_window_shape_combine_mask (GdkWindow *window, + GdkBitmap *mask, + gint x, gint y) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); +} + +void +gdk_window_set_override_redirect (GdkWindow *window, + gboolean override_redirect) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +void +gdk_window_set_icon (GdkWindow *window, + GdkWindow *icon_window, + GdkPixmap *pixmap, + GdkBitmap *mask) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +void +gdk_window_set_icon_name (GdkWindow *window, + const gchar * name) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +void +gdk_window_set_group (GdkWindow *window, + GdkWindow *leader) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (leader != NULL); + g_return_if_fail (GDK_IS_WINDOW (leader)); + + /* N/A */ +} + +void +gdk_window_set_decorations (GdkWindow *window, + GdkWMDecoration decorations) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +void +gdk_window_set_functions (GdkWindow *window, + GdkWMFunction functions) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + /* N/A */ +} + +void +gdk_window_set_child_shapes (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + +} + +void +gdk_window_merge_child_shapes (GdkWindow *window) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + +} + +/************************************************************* + * gdk_window_set_static_gravities: + * Set the bit gravity of the given window to static, + * and flag it so all children get static subwindow + * gravity. + * arguments: + * window: window for which to set static gravity + * use_static: Whether to turn static gravity on or off. + * results: + * Does the XServer support static gravity? + *************************************************************/ + +gboolean +gdk_window_set_static_gravities (GdkWindow *window, + gboolean use_static) +{ + g_return_val_if_fail (window != NULL, FALSE); + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + return TRUE; +} + +void +_gdk_windowing_window_get_offsets (GdkWindow *window, + gint *x_offset, + gint *y_offset) +{ + *x_offset = *y_offset = 0; +} + +gboolean +_gdk_windowing_window_queue_antiexpose (GdkWindow *window, + GdkRegion *area) +{ + return FALSE; +} diff --git a/gdk/linux-fb/mi.h b/gdk/linux-fb/mi.h new file mode 100644 index 0000000000..b65179700b --- /dev/null +++ b/gdk/linux-fb/mi.h @@ -0,0 +1,21 @@ +#ifndef MI_H +#define MI_H 1 + +#include "mitypes.h" +#include "mistruct.h" +#include "mifpoly.h" +#include "mifillarc.h" +#include "mipoly.h" + +void miPolyArc(GdkDrawable *pDraw, GdkGC *pGC, int narcs, miArc *parcs); +void miPolyFillArc(GdkDrawable *pDraw, GdkGC *pGC, int narcs, miArc *parcs); +void miFillPolygon(GdkDrawable *dst, GdkGC *pgc, int shape, int mode, int count, GdkPoint *pPts); + +miDashPtr miDashLine(int npt, GdkPoint *ppt, unsigned int nDash, unsigned char *pDash, unsigned int offset, int *pnseg); +void miZeroLine(GdkDrawable *pDraw, GdkGC *pGC, int mode, int npt, GdkPoint *pptInit); +void miZeroDashLine(GdkDrawable *dst, GdkGC *pgc, int mode, int nptInit, GdkPoint *pptInit); +void miStepDash (int dist, int *pDashIndex, unsigned char *pDash, int numInDashList, int *pDashOffset); +void miWideDash (GdkDrawable *pDrawable, GdkGC *pGC, int mode, int npt, GdkPoint *pPts); +void miWideLine (GdkDrawable *pDrawable, GdkGC *pGC, int mode, int npt, GdkPoint *pPts); + +#endif diff --git a/gdk/linux-fb/miarc.c b/gdk/linux-fb/miarc.c new file mode 100644 index 0000000000..a3d739513f --- /dev/null +++ b/gdk/linux-fb/miarc.c @@ -0,0 +1,3569 @@ +/* $XFree86: xc/programs/Xserver/mi/miarc.c,v 3.7 1999/12/27 00:39:56 robin Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: miarc.c /main/91 1998/02/09 14:45:57 kaleb $ */ +/* Author: Keith Packard and Bob Scheifler */ +/* Warning: this code is toxic, do not dally very long here. */ + +#define _XOPEN_SOURCE_EXTENDED /* to get prototype for cbrt on some systems */ +#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */ + +#include /* memmove */ +#include + +#include + +#include "mi.h" +#include "gdkprivate-fb.h" + +static double miDsin(double a), miDcos(double a), miDasin(double a), miDatan2(double x, double y); + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +/* + * some interesting sematic interpretation of the protocol: + * + * Self intersecting arcs (i.e. those spanning 360 degrees) + * never join with other arcs, and are drawn without caps + * (unless on/off dashed, in which case each dash segment + * is capped, except when the last segment meets the + * first segment, when no caps are drawn) + * + * double dash arcs are drawn in two parts, first the + * odd dashes (drawn in background) then the even dashes + * (drawn in foreground). This means that overlapping + * sections of foreground/background are drawn twice, + * first in background then in foreground. The double-draw + * occurs even when the function uses the destination values + * (e.g. xor mode). This is the same way the wide-line + * code works and should be "fixed". + * + */ + +#undef max +#undef min + +#if defined (__GNUC__) && defined (__STDC__) && !defined (__STRICT_ANSI__) +#define USE_INLINE +#endif + +struct bound { + double min, max; +}; + +struct ibound { + int min, max; +}; + +#define boundedLe(value, bounds)\ + ((bounds).min <= (value) && (value) <= (bounds).max) + +struct line { + double m, b; + int valid; +}; + +#define intersectLine(y,line) (line.m * (y) + line.b) + +/* + * these are all y value bounds + */ + +struct arc_bound { + struct bound ellipse; + struct bound inner; + struct bound outer; + struct bound right; + struct bound left; + struct ibound inneri; + struct ibound outeri; +}; + +struct accelerators { + double tail_y; + double h2; + double w2; + double h4; + double w4; + double h2mw2; + double h2l; + double w2l; + double fromIntX; + double fromIntY; + struct line left, right; + int yorgu; + int yorgl; + int xorg; +}; + +struct arc_def { + double w, h, l; + double a0, a1; +}; + +# define todeg(xAngle) (((double) (xAngle)) / 64.0) + +# define RIGHT_END 0 +# define LEFT_END 1 + +typedef struct _miArcJoin { + int arcIndex0, arcIndex1; + int phase0, phase1; + int end0, end1; +} miArcJoinRec, *miArcJoinPtr; + +typedef struct _miArcCap { + int arcIndex; + int end; +} miArcCapRec, *miArcCapPtr; + +typedef struct _miArcFace { + SppPointRec clock; + SppPointRec center; + SppPointRec counterClock; +} miArcFaceRec, *miArcFacePtr; + +typedef struct _miArcData { + miArc arc; + int render; /* non-zero means render after drawing */ + int join; /* related join */ + int cap; /* related cap */ + int selfJoin; /* final dash meets first dash */ + miArcFaceRec bounds[2]; + double x0, y0, x1, y1; +} miArcDataRec, *miArcDataPtr; + +/* + * This is an entire sequence of arcs, computed and categorized according + * to operation. miDashArcs generates either one or two of these. + */ + +typedef struct _miPolyArc { + int narcs; + miArcDataPtr arcs; + int ncaps; + miArcCapPtr caps; + int njoins; + miArcJoinPtr joins; +} miPolyArcRec, *miPolyArcPtr; + +typedef struct { + short lx, lw, rx, rw; +} miArcSpan; + +typedef struct { + miArcSpan *spans; + int count1, count2, k; + char top, bot, hole; +} miArcSpanData; + +typedef struct { + unsigned long lrustamp; + unsigned short lw; + unsigned short width, height; + miArcSpanData *spdata; +} arcCacheRec; + +# define DASH_MAP_SIZE 91 + +typedef struct { + double map[DASH_MAP_SIZE]; +} dashMap; + +static void fillSpans(GdkDrawable *pDrawable, GdkGC *pGC); +static void newFinalSpan(int y, int xmin, int xmax); +static void drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left); +static void drawQuadrant(struct arc_def *def, struct accelerators *acc, + int a0, int a1, int mask, miArcFacePtr right, miArcFacePtr left, + miArcSpanData *spdata); +static void drawZeroArc(GdkDrawable *pDraw, GdkGC *pGC, miArc *tarc, int lw, miArcFacePtr right, miArcFacePtr left); +static void miArcJoin(GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pRight, miArcFacePtr pLeft, int xOrgRight, int yOrgRight, + double xFtransRight, double yFtransRight, + int xOrgLeft, int yOrgLeft, + double xFtransLeft, double yFtransLeft); +static void miArcCap(GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pFace, int end, int xOrg, int yOrg, + double xFtrans, double yFtrans); +static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner, + SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg, + double xFtrans, double yFtrans); +static void miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC); +static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards); +static miPolyArcPtr miComputeArcs (miArc *parcs, int narcs, GdkGC *gc); +static int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts); + +# define CUBED_ROOT_2 1.2599210498948732038115849718451499938964 +# define CUBED_ROOT_4 1.5874010519681993173435330390930175781250 + +/* + * draw one segment of the arc using the arc spans generation routines + */ + +static void +miArcSegment(GdkDrawable *pDraw, GdkGC *pGC, miArc tarc, miArcFacePtr right, miArcFacePtr left) +{ + int l = GDK_GC_FBDATA(pGC)->values.line_width; + int a0, a1, startAngle, endAngle; + miArcFacePtr temp; + + if (!l) + l = 1; + + if (tarc.width == 0 || tarc.height == 0) { + drawZeroArc (pDraw, pGC, &tarc, l, left, right); + return; + } + + a0 = tarc.angle1; + a1 = tarc.angle2; + if (a1 > FULLCIRCLE) + a1 = FULLCIRCLE; + else if (a1 < -FULLCIRCLE) + a1 = -FULLCIRCLE; + if (a1 < 0) { + startAngle = a0 + a1; + endAngle = a0; + temp = right; + right = left; + left = temp; + } else { + startAngle = a0; + endAngle = a0 + a1; + } + /* + * bounds check the two angles + */ + if (startAngle < 0) + startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE; + if (startAngle >= FULLCIRCLE) + startAngle = startAngle % FULLCIRCLE; + if (endAngle < 0) + endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE; + if (endAngle > FULLCIRCLE) + endAngle = (endAngle-1) % FULLCIRCLE + 1; + if ((startAngle == endAngle) && a1) { + startAngle = 0; + endAngle = FULLCIRCLE; + } + + drawArc (&tarc, l, startAngle, endAngle, right, left); +} + +/* + +Three equations combine to describe the boundaries of the arc + +x^2/w^2 + y^2/h^2 = 1 ellipse itself +(X-x)^2 + (Y-y)^2 = r^2 circle at (x, y) on the ellipse +(Y-y) = (X-x)*w^2*y/(h^2*x) normal at (x, y) on the ellipse + +These lead to a quartic relating Y and y + +y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2 + - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0 + +The reducible cubic obtained from this quartic is + +z^3 - (3N)z^2 - 2V = 0 + +where + +N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6 +V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2) + +Let + +t = z - N +p = -N^2 +q = -N^3 - V + +Then we get + +t^3 + 3pt + 2q = 0 + +The discriminant of this cubic is + +D = q^2 + p^3 + +When D > 0, a real root is obtained as + +z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D)) + +When D < 0, a real root is obtained as + +z = N - 2m*cos(acos(-q/m^3)/3) + +where + +m = sqrt(|p|) * sign(q) + +Given a real root Z of the cubic, the roots of the quartic are the roots +of the two quadratics + +y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0 + +where + +A = +/- sqrt(8Z + b^2 - 4c) +b, c, d are the cubic, quadratic, and linear coefficients of the quartic + +Some experimentation is then required to determine which solutions +correspond to the inner and outer boundaries. + +*/ + +#define CACHESIZE 25 + +static arcCacheRec arcCache[CACHESIZE]; +static unsigned long lrustamp; +static arcCacheRec *lastCacheHit = &arcCache[0]; + +#if 0 +static RESTYPE cacheType; + +/* + * External so it can be called when low on memory. + * Call with a zero ID in that case. + */ +/*ARGSUSED*/ +int +miFreeArcCache (data, id) + gpointer data; + guint id; +{ + int k; + arcCacheRec *cent; + + if (id) + cacheType = 0; + + for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++) + { + if (cent->spdata) + { + cent->lrustamp = 0; + cent->lw = 0; + g_free(cent->spdata); + cent->spdata = NULL; + } + } + lrustamp = 0; + return TRUE; +} +#endif + +static void +miComputeCircleSpans(int lw, miArc *parc, miArcSpanData *spdata) +{ + register miArcSpan *span; + int doinner; + register int x, y, e; + int xk, yk, xm, ym, dx, dy; + register int slw, inslw; + int inx, iny, ine; + int inxk, inyk, inxm, inym; + + doinner = -lw; + slw = parc->width - doinner; + y = parc->height >> 1; + dy = parc->height & 1; + dx = 1 - dy; + MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym); + inslw = parc->width + doinner; + if (inslw > 0) + { + spdata->hole = spdata->top; + MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym); + } + else + { + spdata->hole = FALSE; + doinner = -y; + } + spdata->count1 = -doinner - spdata->top; + spdata->count2 = y + doinner; + span = spdata->spans; + while (y) + { + MIFILLARCSTEP(slw); + span->lx = dy - x; + if (++doinner <= 0) + { + span->lw = slw; + span->rx = 0; + span->rw = span->lx + slw; + } + else + { + MIFILLINARCSTEP(inslw); + span->lw = x - inx; + span->rx = dy - inx + inslw; + span->rw = inx - x + slw - inslw; + } + span++; + } + if (spdata->bot) + { + if (spdata->count2) + spdata->count2--; + else + { + if (lw > (int)parc->height) + span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1); + else + span[-1].rw = 0; + spdata->count1--; + } + } +} + +static void +miComputeEllipseSpans(int lw, miArc *parc, miArcSpanData *spdata) +{ + register miArcSpan *span; + double w, h, r, xorg; + double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs; + double A, T, b, d, x, y, t, inx, outx, hepp, hepm; + int flip, solution; + + w = (double)parc->width / 2.0; + h = (double)parc->height / 2.0; + r = lw / 2.0; + rs = r * r; + Hs = h * h; + WH = w * w - Hs; + Nk = w * r; + Vk = (Nk * Hs) / (WH + WH); + Hf = Hs * Hs; + Nk = (Hf - Nk * Nk) / WH; + Fk = Hf / WH; + hepp = h + EPSILON; + hepm = h - EPSILON; + K = h + ((lw - 1) >> 1); + span = spdata->spans; + if (parc->width & 1) + xorg = .5; + else + xorg = 0.0; + if (spdata->top) + { + span->lx = 0; + span->lw = 1; + span++; + } + spdata->count1 = 0; + spdata->count2 = 0; + spdata->hole = (spdata->top && + (int)parc->height * lw <= (int)(parc->width * parc->width) && + lw < (int)parc->height); + for (; K > 0.0; K -= 1.0) + { + N = (K * K + Nk) / 6.0; + Nc = N * N * N; + Vr = Vk * K; + t = Nc + Vr * Vr; + d = Nc + t; + if (d < 0.0) { + d = Nc; + b = N; + if ( (b < 0.0) == (t < 0.0) ) + { + b = -b; + d = -d; + } + Z = N - 2.0 * b * cos(acos(-t / d) / 3.0); + if ( (Z < 0.0) == (Vr < 0.0) ) + flip = 2; + else + flip = 1; + } + else + { + d = Vr * sqrt(d); + Z = N + cbrt(t + d) + cbrt(t - d); + flip = 0; + } + A = sqrt((Z + Z) - Nk); + T = (Fk - Z) * K / A; + inx = 0.0; + solution = FALSE; + b = -A + K; + d = b * b - 4 * (Z + T); + if (d >= 0) + { + d = sqrt(d); + y = (b + d) / 2; + if ((y >= 0.0) && (y < hepp)) + { + solution = TRUE; + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + t = sqrt(rs - (t * t)); + else + t = 0; + if (flip == 2) + inx = x - t; + else + outx = x + t; + } + } + b = A + K; + d = b * b - 4 * (Z - T); + /* Because of the large magnitudes involved, we lose enough precision + * that sometimes we end up with a negative value near the axis, when + * it should be positive. This is a workaround. + */ + if (d < 0 && !solution) + d = 0.0; + if (d >= 0) { + d = sqrt(d); + y = (b + d) / 2; + if (y < hepp) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + inx = x - sqrt(rs - (t * t)); + else + inx = x; + } + y = (b - d) / 2; + if (y >= 0.0) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + t = sqrt(rs - (t * t)); + else + t = 0; + if (flip == 1) + inx = x - t; + else + outx = x + t; + } + } + span->lx = ICEIL(xorg - outx); + if (inx <= 0.0) + { + spdata->count1++; + span->lw = ICEIL(xorg + outx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = -ICEIL(xorg - inx); + } + else + { + spdata->count2++; + span->lw = ICEIL(xorg - inx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = ICEIL(xorg + outx) - span->rx; + } + span++; + } + if (spdata->bot) + { + outx = w + r; + if (r >= h && r <= w) + inx = 0.0; + else if (Nk < 0.0 && -Nk < Hs) + { + inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk); + if (inx > w - r) + inx = w - r; + } + else + inx = w - r; + span->lx = ICEIL(xorg - outx); + if (inx <= 0.0) + { + span->lw = ICEIL(xorg + outx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = -ICEIL(xorg - inx); + } + else + { + span->lw = ICEIL(xorg - inx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = ICEIL(xorg + outx) - span->rx; + } + } + if (spdata->hole) + { + span = &spdata->spans[spdata->count1]; + span->lw = -span->lx; + span->rx = 1; + span->rw = span->lw; + spdata->count1--; + spdata->count2++; + } +} + +static double +tailX(double K, struct arc_def *def, struct arc_bound *bounds, struct accelerators *acc) +{ + double w, h, r; + double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs; + double A, T, b, d, x, y, t, hepp, hepm; + int flip, solution; + double xs[2]; + double *xp; + + w = def->w; + h = def->h; + r = def->l; + rs = r * r; + Hs = acc->h2; + WH = -acc->h2mw2; + Nk = def->w * r; + Vk = (Nk * Hs) / (WH + WH); + Hf = acc->h4; + Nk = (Hf - Nk * Nk) / WH; + if (K == 0.0) { + if (Nk < 0.0 && -Nk < Hs) { + xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk); + xs[1] = w - r; + if (acc->left.valid && boundedLe(K, bounds->left) && + !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0) + return xs[1]; + if (acc->right.valid && boundedLe(K, bounds->right) && + !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0) + return xs[1]; + return xs[0]; + } + return w - r; + } + Fk = Hf / WH; + hepp = h + EPSILON; + hepm = h - EPSILON; + N = (K * K + Nk) / 6.0; + Nc = N * N * N; + Vr = Vk * K; + xp = xs; + xs[0] = 0.0; + t = Nc + Vr * Vr; + d = Nc + t; + if (d < 0.0) { + d = Nc; + b = N; + if ( (b < 0.0) == (t < 0.0) ) + { + b = -b; + d = -d; + } + Z = N - 2.0 * b * cos(acos(-t / d) / 3.0); + if ( (Z < 0.0) == (Vr < 0.0) ) + flip = 2; + else + flip = 1; + } + else + { + d = Vr * sqrt(d); + Z = N + cbrt(t + d) + cbrt(t - d); + flip = 0; + } + A = sqrt((Z + Z) - Nk); + T = (Fk - Z) * K / A; + solution = FALSE; + b = -A + K; + d = b * b - 4 * (Z + T); + if (d >= 0 && flip == 2) + { + d = sqrt(d); + y = (b + d) / 2; + if ((y >= 0.0) && (y < hepp)) + { + solution = TRUE; + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + t = sqrt(rs - (t * t)); + else + t = 0; + *xp++ = x - t; + } + } + b = A + K; + d = b * b - 4 * (Z - T); + /* Because of the large magnitudes involved, we lose enough precision + * that sometimes we end up with a negative value near the axis, when + * it should be positive. This is a workaround. + */ + if (d < 0 && !solution) + d = 0.0; + if (d >= 0) { + d = sqrt(d); + y = (b + d) / 2; + if (y < hepp) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + *xp++ = x - sqrt(rs - (t * t)); + else + *xp++ = x; + } + y = (b - d) / 2; + if (y >= 0.0 && flip == 1) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + if (rs - (t * t) >= 0) + t = sqrt(rs - (t * t)); + else + t = 0; + *xp++ = x - t; + } + } + if (xp > &xs[1]) { + if (acc->left.valid && boundedLe(K, bounds->left) && + !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0) + return xs[1]; + if (acc->right.valid && boundedLe(K, bounds->right) && + !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0) + return xs[1]; + } + return xs[0]; +} + +static miArcSpanData * +miComputeWideEllipse(int lw, miArc *parc, gboolean *mustFree) +{ + register miArcSpanData *spdata; + register arcCacheRec *cent, *lruent; + register int k; + arcCacheRec fakeent; + + if (!lw) + lw = 1; + if (parc->height <= 1500) + { + *mustFree = FALSE; + cent = lastCacheHit; + if (cent->lw == lw && + cent->width == parc->width && cent->height == parc->height) + { + cent->lrustamp = ++lrustamp; + return cent->spdata; + } + lruent = &arcCache[0]; + for (k = CACHESIZE, cent = lruent; --k >= 0; cent++) + { + if (cent->lw == lw && + cent->width == parc->width && cent->height == parc->height) + { + cent->lrustamp = ++lrustamp; + lastCacheHit = cent; + return cent->spdata; + } + if (cent->lrustamp < lruent->lrustamp) + lruent = cent; + } +#if 0 + if (!cacheType) + { + cacheType = CreateNewResourceType(miFreeArcCache); + (void) AddResource(FakeClientID(0), cacheType, NULL); + } +#endif + } else { + lruent = &fakeent; + lruent->spdata = NULL; + *mustFree = TRUE; + } + k = (parc->height >> 1) + ((lw - 1) >> 1); + spdata = lruent->spdata; + if (!spdata || spdata->k != k) + { + if (spdata) + g_free(spdata); + spdata = (miArcSpanData *)g_malloc(sizeof(miArcSpanData) + + sizeof(miArcSpan) * (k + 2)); + lruent->spdata = spdata; + if (!spdata) + { + lruent->lrustamp = 0; + lruent->lw = 0; + return spdata; + } + spdata->spans = (miArcSpan *)(spdata + 1); + spdata->k = k; + } + spdata->top = !(lw & 1) && !(parc->width & 1); + spdata->bot = !(parc->height & 1); + lruent->lrustamp = ++lrustamp; + lruent->lw = lw; + lruent->width = parc->width; + lruent->height = parc->height; + if (lruent != &fakeent) + lastCacheHit = lruent; + if (parc->width == parc->height) + miComputeCircleSpans(lw, parc, spdata); + else + miComputeEllipseSpans(lw, parc, spdata); + return spdata; +} + +static void +miFillWideEllipse(GdkDrawable *pDraw, GdkGC *pGC, miArc *parc) +{ + GdkRectangle* points; + register GdkRectangle* pts; + miArcSpanData *spdata; + gboolean mustFree; + register miArcSpan *span; + register int xorg, yorgu, yorgl; + register int n; + + yorgu = parc->height + GDK_GC_FBDATA(pGC)->values.line_width; + points = ALLOCATE_LOCAL(sizeof(GdkRectangle) * yorgu * 2); + spdata = miComputeWideEllipse(GDK_GC_FBDATA(pGC)->values.line_width, parc, &mustFree); + if (!spdata) + { + DEALLOCATE_LOCAL(points); + return; + } + pts = points; + span = spdata->spans; + xorg = parc->x + (parc->width >> 1); + yorgu = parc->y + (parc->height >> 1); + yorgl = yorgu + (parc->height & 1); + yorgu -= spdata->k; + yorgl += spdata->k; + if (spdata->top) + { + pts->x = xorg; + pts->y = yorgu - 1; + pts->width = pts->height = 1; + pts++; + span++; + } + for (n = spdata->count1; --n >= 0; ) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + pts[0].height = 1; + pts[0].width = span->lw; + pts[1] = pts[0]; + pts[1].y = yorgl; + yorgu++; + yorgl--; + pts += 2; + span++; + } + if (spdata->hole) + { + pts[0].x = xorg; + pts[0].y = yorgl; + pts[0].width = pts[0].height = 1; + pts++; + } + for (n = spdata->count2; --n >= 0; ) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + pts[0].width = span->lw; + pts[0].height = 1; + + pts[1].x = xorg + span->rx; + pts[1].y = pts[0].y; + pts[1].width = span->rw; + pts[1].height = 1; + + pts[2].x = pts[0].x; + pts[2].y = yorgl; + pts[2].height = 1; + pts[2].width = pts[0].width; + + pts[3].x = pts[1].x; + pts[3].y = pts[2].y; + pts[3].width = pts[1].width; + pts[3].height = 1; + + yorgu++; + yorgl--; + pts += 4; + span++; + } + if (spdata->bot) + { + if (span->rw <= 0) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + pts[0].width = span->lw; + pts[0].height = 1; + pts++; + } + else + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + pts[0].width = span->lw; + pts[0].height = 1; + pts[1].x = xorg + span->rx; + pts[1].y = pts[0].y; + pts[1].width = span->rw; + pts[1].height = 1; + pts += 2; + } + } + if (mustFree) + g_free(spdata); + + gdk_fb_fill_spans(pDraw, pGC, points, pts - points); + + DEALLOCATE_LOCAL(points); +} + +/* + * miPolyArc strategy: + * + * If arc is zero width and solid, we don't have to worry about the rasterop + * or join styles. For wide solid circles, we use a fast integer algorithm. + * For wide solid ellipses, we use special case floating point code. + * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then + * draw using pGCTo and pDrawTo. If the raster-op was "tricky," that is, + * if it involves the destination, then we use PushPixels to move the bits + * from the scratch drawable to pDraw. (See the wide line code for a + * fuller explanation of this.) + */ + +void +miPolyArc(GdkDrawable *pDraw, GdkGC *pGC, int narcs, miArc *parcs) +{ + register int i; + miArc *parc; + int xMin, xMax, yMin, yMax; + int pixmapWidth, pixmapHeight; + int xOrg, yOrg; + int width; + gboolean fTricky; + GdkDrawable* pDrawTo; + GdkColor fg, bg; + GdkGC* pGCTo; + miPolyArcPtr polyArcs; + int cap[2], join[2]; + int iphase; + int halfWidth; + GdkGCValues gcv; + + width = GDK_GC_FBDATA(pGC)->values.line_width; + if(width == 0 && GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID) + { + for(i = narcs, parc = parcs; --i >= 0; parc++) + miArcSegment( pDraw, pGC, *parc, + (miArcFacePtr) 0, (miArcFacePtr) 0 ); + fillSpans (pDraw, pGC); + } + else + { + if ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID) && narcs) + { + while (parcs->width && parcs->height && + (parcs->angle2 >= FULLCIRCLE || + parcs->angle2 <= -FULLCIRCLE)) + { + miFillWideEllipse(pDraw, pGC, parcs); + if (!--narcs) + return; + parcs++; + } + } + + /* Set up pDrawTo and pGCTo based on the rasterop */ + switch(GDK_GC_FBDATA(pGC)->alu) + { + case GDK_CLEAR: /* 0 */ + case GDK_COPY: /* src */ + case GDK_COPY_INVERT: /* NOT src */ + case GDK_SET: /* 1 */ + fTricky = FALSE; + pDrawTo = pDraw; + pGCTo = pGC; + break; + default: + fTricky = TRUE; + + /* find bounding box around arcs */ + xMin = yMin = SHRT_MAX; + xMax = yMax = SHRT_MIN; + + for(i = narcs, parc = parcs; --i >= 0; parc++) + { + xMin = MIN (xMin, parc->x); + yMin = MIN (yMin, parc->y); + xMax = MAX (xMax, (parc->x + (int) parc->width)); + yMax = MAX (yMax, (parc->y + (int) parc->height)); + } + + /* expand box to deal with line widths */ + halfWidth = (width + 1)/2; + xMin -= halfWidth; + yMin -= halfWidth; + xMax += halfWidth; + yMax += halfWidth; + + /* compute pixmap size; limit it to size of drawable */ + xOrg = MAX(xMin, 0); + yOrg = MAX(yMin, 0); + pixmapWidth = MIN(xMax, GDK_DRAWABLE_P(pDraw)->width) - xOrg; + pixmapHeight = MIN(yMax, GDK_DRAWABLE_P(pDraw)->height) - yOrg; + + /* if nothing left, return */ + if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return; + + for(i = narcs, parc = parcs; --i >= 0; parc++) + { + parc->x -= xOrg; + parc->y -= yOrg; + } + + /* set up scratch GC */ + /* allocate a 1 bit deep pixmap of the appropriate size, and + * validate it */ + pDrawTo = gdk_pixmap_new(NULL, pixmapWidth, pixmapHeight, 1); + if (!pDrawTo) + return; + + pGCTo = gdk_gc_new(pDrawTo); + if (!pGCTo) + { + gdk_pixmap_unref(pDrawTo); + return; + } + gdk_gc_set_function(pGCTo, GDK_COPY); + memset(&gcv.background, 0, sizeof(GdkColor)); + gcv.foreground.pixel = 1; + gcv.foreground.red = gcv.foreground.green = gcv.foreground.blue = 1; + gdk_gc_set_foreground(pGCTo, &gcv.foreground); + gdk_gc_set_background(pGCTo, &gcv.background); + gdk_gc_set_line_attributes(pGCTo, + GDK_GC_FBDATA(pGC)->values.line_width, + GDK_GC_FBDATA(pGC)->values.line_style, + GDK_GC_FBDATA(pGC)->values.cap_style, + GDK_GC_FBDATA(pGC)->values.join_style); + gdk_fb_drawable_clear(pDrawTo); + } + + fg = GDK_GC_FBDATA(pGC)->values.foreground; + bg = GDK_GC_FBDATA(pGC)->values.background; + if ((GDK_GC_FBDATA(pGC)->values.fill == GDK_TILED) || + (GDK_GC_FBDATA(pGC)->values.fill == GDK_OPAQUE_STIPPLED)) + bg = fg; /* the protocol sez these don't cause color changes */ + + polyArcs = miComputeArcs (parcs, narcs, pGC); + + if (!polyArcs) + { + if (fTricky) { + gdk_pixmap_unref(pDrawTo); + gdk_gc_unref(pGCTo); + } + return; + } + + cap[0] = cap[1] = 0; + join[0] = join[1] = 0; + for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0); + iphase >= 0; + iphase--) + { + if (iphase == 1) + gdk_gc_set_foreground(pGC, &bg); + else if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) + gdk_gc_set_foreground(pGC, &fg); + for (i = 0; i < polyArcs[iphase].narcs; i++) { + miArcDataPtr arcData; + + arcData = &polyArcs[iphase].arcs[i]; + miArcSegment(pDrawTo, pGCTo, arcData->arc, + &arcData->bounds[RIGHT_END], + &arcData->bounds[LEFT_END]); + if (polyArcs[iphase].arcs[i].render) { + fillSpans (pDrawTo, pGCTo); + /* + * don't cap self-joining arcs + */ + if (polyArcs[iphase].arcs[i].selfJoin && + cap[iphase] < polyArcs[iphase].arcs[i].cap) + cap[iphase]++; + while (cap[iphase] < polyArcs[iphase].arcs[i].cap) { + int arcIndex, end; + miArcDataPtr arcData0; + + arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex; + end = polyArcs[iphase].caps[cap[iphase]].end; + arcData0 = &polyArcs[iphase].arcs[arcIndex]; + miArcCap (pDrawTo, pGCTo, + &arcData0->bounds[end], end, + arcData0->arc.x, arcData0->arc.y, + (double) arcData0->arc.width / 2.0, + (double) arcData0->arc.height / 2.0); + ++cap[iphase]; + } + while (join[iphase] < polyArcs[iphase].arcs[i].join) { + int arcIndex0, arcIndex1, end0, end1; + int phase0, phase1; + miArcDataPtr arcData0, arcData1; + miArcJoinPtr joinp; + + joinp = &polyArcs[iphase].joins[join[iphase]]; + arcIndex0 = joinp->arcIndex0; + end0 = joinp->end0; + arcIndex1 = joinp->arcIndex1; + end1 = joinp->end1; + phase0 = joinp->phase0; + phase1 = joinp->phase1; + arcData0 = &polyArcs[phase0].arcs[arcIndex0]; + arcData1 = &polyArcs[phase1].arcs[arcIndex1]; + miArcJoin (pDrawTo, pGCTo, + &arcData0->bounds[end0], + &arcData1->bounds[end1], + arcData0->arc.x, arcData0->arc.y, + (double) arcData0->arc.width / 2.0, + (double) arcData0->arc.height / 2.0, + arcData1->arc.x, arcData1->arc.y, + (double) arcData1->arc.width / 2.0, + (double) arcData1->arc.height / 2.0); + ++join[iphase]; + } + if (fTricky) { + gdk_fb_draw_drawable(pDraw, pGC, pDrawTo, 0, 0, xOrg, yOrg, pixmapWidth, pixmapHeight); + gdk_fb_drawable_clear(pDrawTo); + } + } + } + } + miFreeArcs(polyArcs, pGC); + + if(fTricky) + { + gdk_pixmap_unref(pDrawTo); + gdk_gc_unref(pGCTo); + } + } +} + +static double +angleBetween (center, point1, point2) + SppPointRec center, point1, point2; +{ + double a1, a2, a; + + /* + * reflect from X coordinates back to ellipse + * coordinates -- y increasing upwards + */ + a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x); + a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x); + a = a2 - a1; + if (a <= -180.0) + a += 360.0; + else if (a > 180.0) + a -= 360.0; + return a; +} + +static void +translateBounds (b, x, y, fx, fy) +miArcFacePtr b; +int x, y; +double fx, fy; +{ + fx += x; + fy += y; + b->clock.x -= fx; + b->clock.y -= fy; + b->center.x -= fx; + b->center.y -= fy; + b->counterClock.x -= fx; + b->counterClock.y -= fy; +} + +static void +miArcJoin (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pLeft, miArcFacePtr pRight, + int xOrgLeft, int yOrgLeft, double xFtransLeft, double yFtransLeft, + int xOrgRight, int yOrgRight, double xFtransRight, double yFtransRight) +{ + SppPointRec center, corner, otherCorner; + SppPointRec poly[5], e; + SppPointPtr pArcPts; + int cpt; + SppArcRec arc; + miArcFaceRec Right, Left; + int polyLen; + int xOrg, yOrg; + double xFtrans, yFtrans; + double a; + double ae, ac2, ec2, bc2, de; + double width; + + xOrg = (xOrgRight + xOrgLeft) / 2; + yOrg = (yOrgRight + yOrgLeft) / 2; + xFtrans = (xFtransLeft + xFtransRight) / 2; + yFtrans = (yFtransLeft + yFtransRight) / 2; + Right = *pRight; + translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight, + xFtrans - xFtransRight, yFtrans - yFtransRight); + Left = *pLeft; + translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft, + xFtrans - xFtransLeft, yFtrans - yFtransLeft); + pRight = &Right; + pLeft = &Left; + + if (pRight->clock.x == pLeft->counterClock.x && + pRight->clock.y == pLeft->counterClock.y) + return; + center = pRight->center; + if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock)) + && a <= 180.0) + { + corner = pRight->clock; + otherCorner = pLeft->counterClock; + } else { + a = angleBetween (center, pLeft->clock, pRight->counterClock); + corner = pLeft->clock; + otherCorner = pRight->counterClock; + } + switch (GDK_GC_FBDATA(pGC)->values.join_style) { + case GDK_JOIN_ROUND: + width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1); + + arc.x = center.x - width/2; + arc.y = center.y - width/2; + arc.width = width; + arc.height = width; + arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x); + arc.angle2 = a; + pArcPts = (SppPointPtr) g_malloc (3 * sizeof (SppPointRec)); + if (!pArcPts) + return; + pArcPts[0].x = otherCorner.x; + pArcPts[0].y = otherCorner.y; + pArcPts[1].x = center.x; + pArcPts[1].y = center.y; + pArcPts[2].x = corner.x; + pArcPts[2].y = corner.y; + if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) ) + { + /* by drawing with miFillSppPoly and setting the endpoints of the arc + * to be the corners, we assure that the cap will meet up with the + * rest of the line */ + miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans); + } + g_free(pArcPts); + return; + case GDK_JOIN_MITER: + /* + * don't miter arcs with less than 11 degrees between them + */ + if (a < 169.0) { + poly[0] = corner; + poly[1] = center; + poly[2] = otherCorner; + bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) + + (corner.y - otherCorner.y) * (corner.y - otherCorner.y); + ec2 = bc2 / 4; + ac2 = (corner.x - center.x) * (corner.x - center.x) + + (corner.y - center.y) * (corner.y - center.y); + ae = sqrt (ac2 - ec2); + de = ec2 / ae; + e.x = (corner.x + otherCorner.x) / 2; + e.y = (corner.y + otherCorner.y) / 2; + poly[3].x = e.x + de * (e.x - center.x) / ae; + poly[3].y = e.y + de * (e.y - center.y) / ae; + poly[4] = corner; + polyLen = 5; + break; + } + case GDK_JOIN_BEVEL: + poly[0] = corner; + poly[1] = center; + poly[2] = otherCorner; + poly[3] = corner; + polyLen = 4; + break; + } + miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans); +} + +/*ARGSUSED*/ +static void +miArcCap (pDraw, pGC, pFace, end, xOrg, yOrg, xFtrans, yFtrans) + GdkDrawable* pDraw; + GdkGC* pGC; + miArcFacePtr pFace; + int end; + int xOrg, yOrg; + double xFtrans, yFtrans; +{ + SppPointRec corner, otherCorner, center, endPoint, poly[5]; + + corner = pFace->clock; + otherCorner = pFace->counterClock; + center = pFace->center; + switch (GDK_GC_FBDATA(pGC)->values.cap_style) { + case GDK_CAP_PROJECTING: + poly[0].x = otherCorner.x; + poly[0].y = otherCorner.y; + poly[1].x = corner.x; + poly[1].y = corner.y; + poly[2].x = corner.x - + (center.y - corner.y); + poly[2].y = corner.y + + (center.x - corner.x); + poly[3].x = otherCorner.x - + (otherCorner.y - center.y); + poly[3].y = otherCorner.y + + (otherCorner.x - center.x); + poly[4].x = otherCorner.x; + poly[4].y = otherCorner.y; + miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans); + break; + case GDK_CAP_ROUND: + /* + * miRoundCap just needs these to be unequal. + */ + endPoint = center; + endPoint.x = endPoint.x + 100; + miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0, + -xOrg, -yOrg, xFtrans, yFtrans); + break; + default: + break; + } +} + +/* MIROUNDCAP -- a private helper function + * Put Rounded cap on end. pCenter is the center of this end of the line + * pEnd is the center of the other end of the line. pCorner is one of the + * two corners at this end of the line. + * NOTE: pOtherCorner must be counter-clockwise from pCorner. + */ +/*ARGSUSED*/ +static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner, + SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg, + double xFtrans, double yFtrans) +{ + int cpt; + double width; + double miDatan2 (); + SppArcRec arc; + SppPointPtr pArcPts; + + width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1); + + arc.x = pCenter.x - width/2; + arc.y = pCenter.y - width/2; + arc.width = width; + arc.height = width; + arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x); + if(PTISEQUAL(pCenter, pEnd)) + arc.angle2 = - 180.0; + else { + arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1; + if (arc.angle2 < 0) + arc.angle2 += 360.0; + } + pArcPts = (SppPointPtr) NULL; + if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) ) + { + /* by drawing with miFillSppPoly and setting the endpoints of the arc + * to be the corners, we assure that the cap will meet up with the + * rest of the line */ + miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans); + } + g_free(pArcPts); +} + +/* + * To avoid inaccuracy at the cardinal points, use trig functions + * which are exact for those angles + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif + +# define Dsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0))) +# define Dcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0))) +# define mod(a,b) ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b)) + +static double +miDcos (a) +double a; +{ + int i; + + if (floor (a/90) == a/90) { + i = (int) (a/90.0); + switch (mod (i, 4)) { + case 0: return 1; + case 1: return 0; + case 2: return -1; + case 3: return 0; + } + } + return cos (a * M_PI / 180.0); +} + +static double +miDsin (a) +double a; +{ + int i; + + if (floor (a/90) == a/90) { + i = (int) (a/90.0); + switch (mod (i, 4)) { + case 0: return 0; + case 1: return 1; + case 2: return 0; + case 3: return -1; + } + } + return sin (a * M_PI / 180.0); +} + +static double +miDasin (v) +double v; +{ + if (v == 0) + return 0.0; + if (v == 1.0) + return 90.0; + if (v == -1.0) + return -90.0; + return asin(v) * (180.0 / M_PI); +} + +static double +miDatan2 (dy, dx) +double dy, dx; +{ + if (dy == 0) { + if (dx >= 0) + return 0.0; + return 180.0; + } else if (dx == 0) { + if (dy > 0) + return 90.0; + return -90.0; + } else if (fabs (dy) == fabs (dx)) { + if (dy > 0) { + if (dx > 0) + return 45.0; + return 135.0; + } else { + if (dx > 0) + return 315.0; + return 225.0; + } + } else { + return atan2 (dy, dx) * (180.0 / M_PI); + } +} + +/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper + * routine for filled arc and line (round cap) code. + * Returns the number of points in the arc. Note that it takes a pointer + * to a pointer to where it should put the points and an index (cpt). + * This procedure allocates the space necessary to fit the arc points. + * Sometimes it's convenient for those points to be at the end of an existing + * array. (For example, if we want to leave a spare point to make sectors + * instead of segments.) So we pass in the g_malloc()ed chunk that contains the + * array and an index saying where we should start stashing the points. + * If there isn't an array already, we just pass in a null pointer and + * count on g_realloc() to handle the null pointer correctly. + */ +static int +miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts) +#if 0 + SppArcPtr parc; /* points to an arc */ + int cpt; /* number of points already in arc list */ + SppPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */ +#endif +{ + double st, /* Start Theta, start angle */ + et, /* End Theta, offset from start theta */ + dt, /* Delta Theta, angle to sweep ellipse */ + cdt, /* Cos Delta Theta, actually 2 cos(dt) */ + x0, y0, /* the recurrence formula needs two points to start */ + x1, y1, + x2, y2, /* this will be the new point generated */ + xc, yc; /* the center point */ + int count, i; + SppPointPtr poly; + GdkPoint last; /* last point on integer boundaries */ + + /* The spec says that positive angles indicate counterclockwise motion. + * Given our coordinate system (with 0,0 in the upper left corner), + * the screen appears flipped in Y. The easiest fix is to negate the + * angles given */ + + st = - parc->angle1; + + et = - parc->angle2; + + /* Try to get a delta theta that is within 1/2 pixel. Then adjust it + * so that it divides evenly into the total. + * I'm just using cdt 'cause I'm lazy. + */ + cdt = parc->width; + if (parc->height > cdt) + cdt = parc->height; + cdt /= 2.0; + if(cdt <= 0) + return 0; + if (cdt < 1.0) + cdt = 1.0; + dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */ + count = et/dt; + count = abs(count) + 1; + dt = et/count; + count++; + + cdt = 2 * miDcos(dt); + if (!(poly = (SppPointPtr) g_realloc((gpointer)*ppPts, + (cpt + count) * sizeof(SppPointRec)))) + return(0); + *ppPts = poly; + + xc = parc->width/2.0; /* store half width and half height */ + yc = parc->height/2.0; + + x0 = xc * miDcos(st); + y0 = yc * miDsin(st); + x1 = xc * miDcos(st + dt); + y1 = yc * miDsin(st + dt); + xc += parc->x; /* by adding initial point, these become */ + yc += parc->y; /* the center point */ + + poly[cpt].x = (xc + x0); + poly[cpt].y = (yc + y0); + last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) ); + last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) ); + + for(i = 2; i < count; i++) + { + x2 = cdt * x1 - x0; + y2 = cdt * y1 - y0; + + poly[cpt + i].x = (xc + x2); + poly[cpt + i].y = (yc + y2); + + x0 = x1; y0 = y1; + x1 = x2; y1 = y2; + } + /* adjust the last point */ + if (abs(parc->angle2) >= 360.0) + poly[cpt +i -1] = poly[0]; + else { + poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc); + poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc); + } + + return(count); +} + +struct arcData { + double x0, y0, x1, y1; + int selfJoin; +}; + +# define ADD_REALLOC_STEP 20 + +static void +addCap (capsp, ncapsp, sizep, end, arcIndex) + miArcCapPtr *capsp; + int *ncapsp, *sizep; + int end, arcIndex; +{ + int newsize; + miArcCapPtr cap; + + if (*ncapsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + cap = (miArcCapPtr) g_realloc (*capsp, + newsize * sizeof (**capsp)); + if (!cap) + return; + *sizep = newsize; + *capsp = cap; + } + cap = &(*capsp)[*ncapsp]; + cap->end = end; + cap->arcIndex = arcIndex; + ++*ncapsp; +} + +static void +addJoin (joinsp, njoinsp, sizep, end0, index0, phase0, end1, index1, phase1) + miArcJoinPtr *joinsp; + int *njoinsp, *sizep; + int end0, index0, phase0, end1, index1, phase1; +{ + int newsize; + miArcJoinPtr join; + + if (*njoinsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + join = (miArcJoinPtr) g_realloc (*joinsp, + newsize * sizeof (**joinsp)); + if (!join) + return; + *sizep = newsize; + *joinsp = join; + } + join = &(*joinsp)[*njoinsp]; + join->end0 = end0; + join->arcIndex0 = index0; + join->phase0 = phase0; + join->end1 = end1; + join->arcIndex1 = index1; + join->phase1 = phase1; + ++*njoinsp; +} + +static miArcDataPtr +addArc (arcsp, narcsp, sizep, xarc) + miArcDataPtr *arcsp; + int *narcsp, *sizep; + miArc *xarc; +{ + int newsize; + miArcDataPtr arc; + + if (*narcsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + arc = (miArcDataPtr) g_realloc (*arcsp, + newsize * sizeof (**arcsp)); + if (!arc) + return (miArcDataPtr)NULL; + *sizep = newsize; + *arcsp = arc; + } + arc = &(*arcsp)[*narcsp]; + arc->arc = *xarc; + ++*narcsp; + return arc; +} + +static void +miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC) +{ + int iphase; + + for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0); + iphase >= 0; + iphase--) + { + if (arcs[iphase].narcs > 0) + g_free(arcs[iphase].arcs); + if (arcs[iphase].njoins > 0) + g_free(arcs[iphase].joins); + if (arcs[iphase].ncaps > 0) + g_free(arcs[iphase].caps); + } + g_free(arcs); +} + +/* + * map angles to radial distance. This only deals with the first quadrant + */ + +/* + * a polygonal approximation to the arc for computing arc lengths + */ + +# define dashIndexToAngle(di) ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1)) +# define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64)) +# define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1)) +# define dashXAngleStep (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1))) + +static void +computeDashMap (arcp, map) + miArc *arcp; + dashMap *map; +{ + int di; + double a, x, y, prevx, prevy, dist; + + for (di = 0; di < DASH_MAP_SIZE; di++) { + a = dashIndexToAngle (di); + x = ((double) arcp->width / 2.0) * miDcos (a); + y = ((double) arcp->height / 2.0) * miDsin (a); + if (di == 0) { + map->map[di] = 0.0; + } else { + dist = hypot (x - prevx, y - prevy); + map->map[di] = map->map[di - 1] + dist; + } + prevx = x; + prevy = y; + } +} + +typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes; + +/* this routine is a bit gory */ + +static miPolyArcPtr +miComputeArcs (miArc *parcs, int narcs, GdkGC *pGC) +{ + int isDashed, isDoubleDash; + int dashOffset; + miPolyArcPtr arcs; + int start, i, j, k, nexti, nextk; + int joinSize[2]; + int capSize[2]; + int arcSize[2]; + int angle2; + double a0, a1; + struct arcData *data; + miArcDataPtr arc; + miArc xarc; + int iphase, prevphase, joinphase; + int arcsJoin; + int selfJoin; + + int iDash, dashRemaining; + int iDashStart, dashRemainingStart, iphaseStart; + int startAngle, spanAngle, endAngle, backwards; + int prevDashAngle, dashAngle; + dashMap map; + + isDashed = !(GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID); + isDoubleDash = (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH); + dashOffset = GDK_GC_FBDATA(pGC)->dash_offset; + + data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData)); + if (!data) + return (miPolyArcPtr)NULL; + arcs = (miPolyArcPtr) g_malloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1)); + if (!arcs) + { + DEALLOCATE_LOCAL(data); + return (miPolyArcPtr)NULL; + } + for (i = 0; i < narcs; i++) { + a0 = todeg (parcs[i].angle1); + angle2 = parcs[i].angle2; + if (angle2 > FULLCIRCLE) + angle2 = FULLCIRCLE; + else if (angle2 < -FULLCIRCLE) + angle2 = -FULLCIRCLE; + data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE; + a1 = todeg (parcs[i].angle1 + angle2); + data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0)); + data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0)); + data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1)); + data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1)); + } + + for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) { + arcs[iphase].njoins = 0; + arcs[iphase].joins = 0; + joinSize[iphase] = 0; + + arcs[iphase].ncaps = 0; + arcs[iphase].caps = 0; + capSize[iphase] = 0; + + arcs[iphase].narcs = 0; + arcs[iphase].arcs = 0; + arcSize[iphase] = 0; + } + + iphase = 0; + if (isDashed) { + iDash = 0; + dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[0]; + while (dashOffset > 0) { + if (dashOffset >= dashRemaining) { + dashOffset -= dashRemaining; + iphase = iphase ? 0 : 1; + iDash++; + if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len) + iDash = 0; + dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash]; + } else { + dashRemaining -= dashOffset; + dashOffset = 0; + } + } + iDashStart = iDash; + dashRemainingStart = dashRemaining; + } + iphaseStart = iphase; + + for (i = narcs - 1; i >= 0; i--) { + j = i + 1; + if (j == narcs) + j = 0; + if (data[i].selfJoin || i == j || + (UNEQUAL (data[i].x1, data[j].x0) || + UNEQUAL (data[i].y1, data[j].y0))) + { + if (iphase == 0 || isDoubleDash) + addCap (&arcs[iphase].caps, &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, 0); + break; + } + } + start = i + 1; + if (start == narcs) + start = 0; + i = start; + for (;;) { + j = i + 1; + if (j == narcs) + j = 0; + nexti = i+1; + if (nexti == narcs) + nexti = 0; + if (isDashed) { + /* + ** deal with dashed arcs. Use special rules for certain 0 area arcs. + ** Presumably, the other 0 area arcs still aren't done right. + */ + arcTypes arcType = OTHER; + guint16 thisLength; + + if (parcs[i].height == 0 + && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00 + && parcs[i].angle2 == 0x2d00) + arcType = HORIZONTAL; + else if (parcs[i].width == 0 + && (parcs[i].angle1 % FULLCIRCLE) == 0x1680 + && parcs[i].angle2 == 0x2d00) + arcType = VERTICAL; + if (arcType == OTHER) { + /* + * precompute an approximation map + */ + computeDashMap (&parcs[i], &map); + /* + * compute each individual dash segment using the path + * length function + */ + startAngle = parcs[i].angle1; + spanAngle = parcs[i].angle2; + if (spanAngle > FULLCIRCLE) + spanAngle = FULLCIRCLE; + else if (spanAngle < -FULLCIRCLE) + spanAngle = -FULLCIRCLE; + if (startAngle < 0) + startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE; + if (startAngle >= FULLCIRCLE) + startAngle = startAngle % FULLCIRCLE; + endAngle = startAngle + spanAngle; + backwards = spanAngle < 0; + } else { + xarc = parcs[i]; + if (arcType == VERTICAL) { + xarc.angle1 = 0x1680; + startAngle = parcs[i].y; + endAngle = startAngle + parcs[i].height; + } else { + xarc.angle1 = 0x2d00; + startAngle = parcs[i].x; + endAngle = startAngle + parcs[i].width; + } + } + dashAngle = startAngle; + selfJoin = data[i].selfJoin && + (iphase == 0 || isDoubleDash); + /* + * add dashed arcs to each bucket + */ + arc = 0; + while (dashAngle != endAngle) { + prevDashAngle = dashAngle; + if (arcType == OTHER) { + dashAngle = computeAngleFromPath (prevDashAngle, endAngle, + &map, &dashRemaining, backwards); + /* avoid troubles with huge arcs and small dashes */ + if (dashAngle == prevDashAngle) { + if (backwards) + dashAngle--; + else + dashAngle++; + } + } else { + thisLength = (dashAngle + dashRemaining <= endAngle) ? + dashRemaining : endAngle - dashAngle; + if (arcType == VERTICAL) { + xarc.y = dashAngle; + xarc.height = thisLength; + } else { + xarc.x = dashAngle; + xarc.width = thisLength; + } + dashAngle += thisLength; + dashRemaining -= thisLength; + } + if (iphase == 0 || isDoubleDash) { + if (arcType == OTHER) { + xarc = parcs[i]; + spanAngle = prevDashAngle; + if (spanAngle < 0) + spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE; + if (spanAngle >= FULLCIRCLE) + spanAngle = spanAngle % FULLCIRCLE; + xarc.angle1 = spanAngle; + spanAngle = dashAngle - prevDashAngle; + if (backwards) { + if (dashAngle > prevDashAngle) + spanAngle = - FULLCIRCLE + spanAngle; + } else { + if (dashAngle < prevDashAngle) + spanAngle = FULLCIRCLE + spanAngle; + } + if (spanAngle > FULLCIRCLE) + spanAngle = FULLCIRCLE; + if (spanAngle < -FULLCIRCLE) + spanAngle = -FULLCIRCLE; + xarc.angle2 = spanAngle; + } + arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs, + &arcSize[iphase], &xarc); + if (!arc) + goto arcfail; + /* + * cap each end of an on/off dash + */ + if (!isDoubleDash) { + if (prevDashAngle != startAngle) { + addCap (&arcs[iphase].caps, + &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, + arc - arcs[iphase].arcs); + + } + if (dashAngle != endAngle) { + addCap (&arcs[iphase].caps, + &arcs[iphase].ncaps, + &capSize[iphase], LEFT_END, + arc - arcs[iphase].arcs); + } + } + arc->cap = arcs[iphase].ncaps; + arc->join = arcs[iphase].njoins; + arc->render = 0; + arc->selfJoin = 0; + if (dashAngle == endAngle) + arc->selfJoin = selfJoin; + } + prevphase = iphase; + if (dashRemaining <= 0) { + ++iDash; + if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len) + iDash = 0; + iphase = iphase ? 0:1; + dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash]; + } + } + /* + * make sure a place exists for the position data when + * drawing a zero-length arc + */ + if (startAngle == endAngle) { + prevphase = iphase; + if (!isDoubleDash && iphase == 1) + prevphase = 0; + arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs, + &arcSize[prevphase], &parcs[i]); + if (!arc) + goto arcfail; + arc->join = arcs[prevphase].njoins; + arc->cap = arcs[prevphase].ncaps; + arc->selfJoin = data[i].selfJoin; + } + } else { + arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs, + &arcSize[iphase], &parcs[i]); + if (!arc) + goto arcfail; + arc->join = arcs[iphase].njoins; + arc->cap = arcs[iphase].ncaps; + arc->selfJoin = data[i].selfJoin; + prevphase = iphase; + } + if (prevphase == 0 || isDoubleDash) + k = arcs[prevphase].narcs - 1; + if (iphase == 0 || isDoubleDash) + nextk = arcs[iphase].narcs; + if (nexti == start) { + nextk = 0; + if (isDashed) { + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + } + arcsJoin = narcs > 1 && i != j && + ISEQUAL (data[i].x1, data[j].x0) && + ISEQUAL (data[i].y1, data[j].y0) && + !data[i].selfJoin && !data[j].selfJoin; + if (arc) + { + if (arcsJoin) + arc->render = 0; + else + arc->render = 1; + } + if (arcsJoin && + (prevphase == 0 || isDoubleDash) && + (iphase == 0 || isDoubleDash)) + { + joinphase = iphase; + if (isDoubleDash) { + if (nexti == start) + joinphase = iphaseStart; + /* + * if the join is right at the dash, + * draw the join in foreground + * This is because the foreground + * arcs are computed second, the results + * of which are needed to draw the join + */ + if (joinphase != prevphase) + joinphase = 0; + } + if (joinphase == 0 || isDoubleDash) { + addJoin (&arcs[joinphase].joins, + &arcs[joinphase].njoins, + &joinSize[joinphase], + LEFT_END, k, prevphase, + RIGHT_END, nextk, iphase); + arc->join = arcs[prevphase].njoins; + } + } else { + /* + * cap the left end of this arc + * unless it joins itself + */ + if ((prevphase == 0 || isDoubleDash) && + !arc->selfJoin) + { + addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps, + &capSize[prevphase], LEFT_END, k); + arc->cap = arcs[prevphase].ncaps; + } + if (isDashed && !arcsJoin) { + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + nextk = arcs[iphase].narcs; + if (nexti == start) { + nextk = 0; + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + /* + * cap the right end of the next arc. If the + * next arc is actually the first arc, only + * cap it if it joins with this arc. This + * case will occur when the final dash segment + * of an on/off dash is off. Of course, this + * cap will be drawn at a strange time, but that + * hardly matters... + */ + if ((iphase == 0 || isDoubleDash) && + (nexti != start || (arcsJoin && isDashed))) + addCap (&arcs[iphase].caps, &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, nextk); + } + i = nexti; + if (i == start) + break; + } + /* + * make sure the last section is rendered + */ + for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) + if (arcs[iphase].narcs > 0) { + arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1; + arcs[iphase].arcs[arcs[iphase].narcs-1].join = + arcs[iphase].njoins; + arcs[iphase].arcs[arcs[iphase].narcs-1].cap = + arcs[iphase].ncaps; + } + DEALLOCATE_LOCAL(data); + return arcs; +arcfail: + miFreeArcs(arcs, pGC); + DEALLOCATE_LOCAL(data); + return (miPolyArcPtr)NULL; +} + +static double +angleToLength (angle, map) + int angle; + dashMap *map; +{ + double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen; + int di; + int excess; + gboolean oddSide = FALSE; + + totallen = 0; + if (angle >= 0) { + while (angle >= 90 * 64) { + angle -= 90 * 64; + totallen += sidelen; + oddSide = !oddSide; + } + } else { + while (angle < 0) { + angle += 90 * 64; + totallen -= sidelen; + oddSide = !oddSide; + } + } + if (oddSide) + angle = 90 * 64 - angle; + + di = xAngleToDashIndex (angle); + excess = angle - dashIndexToXAngle (di); + + len = map->map[di]; + /* + * linearly interpolate between this point and the next + */ + if (excess > 0) { + excesslen = (map->map[di + 1] - map->map[di]) * + ((double) excess) / dashXAngleStep; + len += excesslen; + } + if (oddSide) + totallen += (sidelen - len); + else + totallen += len; + return totallen; +} + +/* + * len is along the arc, but may be more than one rotation + */ + +static int +lengthToAngle (len, map) + double len; + dashMap *map; +{ + double sidelen = map->map[DASH_MAP_SIZE - 1]; + int angle, angleexcess; + gboolean oddSide = FALSE; + int a0, a1, a; + + angle = 0; + /* + * step around the ellipse, subtracting sidelens and + * adding 90 degrees. oddSide will tell if the + * map should be interpolated in reverse + */ + if (len >= 0) { + if (sidelen == 0) + return 2 * FULLCIRCLE; /* infinity */ + while (len >= sidelen) { + angle += 90 * 64; + len -= sidelen; + oddSide = !oddSide; + } + } else { + if (sidelen == 0) + return -2 * FULLCIRCLE; /* infinity */ + while (len < 0) { + angle -= 90 * 64; + len += sidelen; + oddSide = !oddSide; + } + } + if (oddSide) + len = sidelen - len; + a0 = 0; + a1 = DASH_MAP_SIZE - 1; + /* + * binary search for the closest pre-computed length + */ + while (a1 - a0 > 1) { + a = (a0 + a1) / 2; + if (len > map->map[a]) + a0 = a; + else + a1 = a; + } + angleexcess = dashIndexToXAngle (a0); + /* + * linearly interpolate to the next point + */ + angleexcess += (len - map->map[a0]) / + (map->map[a0+1] - map->map[a0]) * dashXAngleStep; + if (oddSide) + angle += (90 * 64) - angleexcess; + else + angle += angleexcess; + return angle; +} + +/* + * compute the angle of an ellipse which cooresponds to + * the given path length. Note that the correct solution + * to this problem is an eliptic integral, we'll punt and + * approximate (it's only for dashes anyway). This + * approximation uses a polygon. + * + * The remaining portion of len is stored in *lenp - + * this will be negative if the arc extends beyond + * len and positive if len extends beyond the arc. + */ + +static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards) +/* int startAngle, endAngle; *//* normalized absolute angles in *64 degrees */ +{ + int a0, a1, a; + double len0; + int len; + + a0 = startAngle; + a1 = endAngle; + len = *lenp; + if (backwards) { + /* + * flip the problem around to always be + * forwards + */ + a0 = FULLCIRCLE - a0; + a1 = FULLCIRCLE - a1; + } + if (a1 < a0) + a1 += FULLCIRCLE; + len0 = angleToLength (a0, map); + a = lengthToAngle (len0 + len, map); + if (a > a1) { + a = a1; + len -= angleToLength (a1, map) - len0; + } else + len = 0; + if (backwards) + a = FULLCIRCLE - a; + *lenp = len; + return a; +} + +/* + * scan convert wide arcs. + */ + +/* + * draw zero width/height arcs + */ + +static void +drawZeroArc (pDraw, pGC, tarc, lw, left, right) + GdkDrawable* pDraw; + GdkGC* pGC; + miArc *tarc; + int lw; + miArcFacePtr right, left; +{ + double x0, y0, x1, y1, w, h, x, y; + double xmax, ymax, xmin, ymin; + int a0, a1; + double a, startAngle, endAngle; + double l, lx, ly; + + l = lw / 2.0; + a0 = tarc->angle1; + a1 = tarc->angle2; + if (a1 > FULLCIRCLE) + a1 = FULLCIRCLE; + else if (a1 < -FULLCIRCLE) + a1 = -FULLCIRCLE; + w = (double)tarc->width / 2.0; + h = (double)tarc->height / 2.0; + /* + * play in X coordinates right away + */ + startAngle = - ((double) a0 / 64.0); + endAngle = - ((double) (a0 + a1) / 64.0); + + xmax = -w; + xmin = w; + ymax = -h; + ymin = h; + a = startAngle; + for (;;) + { + x = w * miDcos(a); + y = h * miDsin(a); + if (a == startAngle) + { + x0 = x; + y0 = y; + } + if (a == endAngle) + { + x1 = x; + y1 = y; + } + if (x > xmax) + xmax = x; + if (x < xmin) + xmin = x; + if (y > ymax) + ymax = y; + if (y < ymin) + ymin = y; + if (a == endAngle) + break; + if (a1 < 0) /* clockwise */ + { + if (floor (a / 90.0) == floor (endAngle / 90.0)) + a = endAngle; + else + a = 90 * (floor (a/90.0) + 1); + } + else + { + if (ceil (a / 90.0) == ceil (endAngle / 90.0)) + a = endAngle; + else + a = 90 * (ceil (a/90.0) - 1); + } + } + lx = ly = l; + if ((x1 - x0) + (y1 - y0) < 0) + lx = ly = -l; + if (h) + { + ly = 0.0; + lx = -lx; + } + else + lx = 0.0; + if (right) + { + right->center.x = x0; + right->center.y = y0; + right->clock.x = x0 - lx; + right->clock.y = y0 - ly; + right->counterClock.x = x0 + lx; + right->counterClock.y = y0 + ly; + } + if (left) + { + left->center.x = x1; + left->center.y = y1; + left->clock.x = x1 + lx; + left->clock.y = y1 + ly; + left->counterClock.x = x1 - lx; + left->counterClock.y = y1 - ly; + } + + x0 = xmin; + x1 = xmax; + y0 = ymin; + y1 = ymax; + if (ymin != y1) { + xmin = -l; + xmax = l; + } else { + ymin = -l; + ymax = l; + } + if (xmax != xmin && ymax != ymin) { + int minx, maxx, miny, maxy; + + minx = ICEIL (xmin + w) + tarc->x; + maxx = ICEIL (xmax + w) + tarc->x; + miny = ICEIL (ymin + h) + tarc->y; + maxy = ICEIL (ymax + h) + tarc->y; + + gdk_fb_draw_rectangle(pDraw, pGC, TRUE, minx, miny, maxx - minx, maxy - miny); + } +} + +/* + * this computes the ellipse y value associated with the + * bottom of the tail. + */ + +static void +tailEllipseY (def, acc) + struct arc_def *def; + struct accelerators *acc; +{ + double t; + + acc->tail_y = 0.0; + if (def->w == def->h) + return; + t = def->l * def->w; + if (def->w > def->h) { + if (t < acc->h2) + return; + } else { + if (t > acc->h2) + return; + } + t = 2.0 * def->h * t; + t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2; + if (t > 0.0) + acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t); +} + +/* + * inverse functions -- compute edge coordinates + * from the ellipse + */ + +static double +outerXfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +outerYfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerXfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerYfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerYfromY (y, def, acc) + double y; + struct arc_def *def; + struct accelerators *acc; +{ + double x; + + x = (def->w / def->h) * sqrt (acc->h2 - y*y); + + return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static void +computeLine (x1, y1, x2, y2, line) + double x1, y1, x2, y2; + struct line *line; +{ + if (y1 == y2) + line->valid = 0; + else { + line->m = (x1 - x2) / (y1 - y2); + line->b = x1 - y1 * line->m; + line->valid = 1; + } +} + +/* + * compute various accelerators for an ellipse. These + * are simply values that are used repeatedly in + * the computations + */ + +static void +computeAcc (tarc, lw, def, acc) + miArc *tarc; + int lw; + struct arc_def *def; + struct accelerators *acc; +{ + def->w = ((double) tarc->width) / 2.0; + def->h = ((double) tarc->height) / 2.0; + def->l = ((double) lw) / 2.0; + acc->h2 = def->h * def->h; + acc->w2 = def->w * def->w; + acc->h4 = acc->h2 * acc->h2; + acc->w4 = acc->w2 * acc->w2; + acc->h2l = acc->h2 * def->l; + acc->w2l = acc->w2 * def->l; + acc->h2mw2 = acc->h2 - acc->w2; + acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0; + acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0; + acc->xorg = tarc->x + (tarc->width >> 1); + acc->yorgu = tarc->y + (tarc->height >> 1); + acc->yorgl = acc->yorgu + (tarc->height & 1); + tailEllipseY (def, acc); +} + +/* + * compute y value bounds of various portions of the arc, + * the outer edge, the ellipse and the inner edge. + */ + +static void +computeBound (def, bound, acc, right, left) + struct arc_def *def; + struct arc_bound *bound; + struct accelerators *acc; + miArcFacePtr right, left; +{ + double t; + double innerTaily; + double tail_y; + struct bound innerx, outerx; + struct bound ellipsex; + + bound->ellipse.min = Dsin (def->a0) * def->h; + bound->ellipse.max = Dsin (def->a1) * def->h; + if (def->a0 == 45 && def->w == def->h) + ellipsex.min = bound->ellipse.min; + else + ellipsex.min = Dcos (def->a0) * def->w; + if (def->a1 == 45 && def->w == def->h) + ellipsex.max = bound->ellipse.max; + else + ellipsex.max = Dcos (def->a1) * def->w; + bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc); + bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc); + bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc); + bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc); + + outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc); + outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc); + innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc); + innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc); + + /* + * save the line end points for the + * cap code to use. Careful here, these are + * in cartesean coordinates (y increasing upwards) + * while the cap code uses inverted coordinates + * (y increasing downwards) + */ + + if (right) { + right->counterClock.y = bound->outer.min; + right->counterClock.x = outerx.min; + right->center.y = bound->ellipse.min; + right->center.x = ellipsex.min; + right->clock.y = bound->inner.min; + right->clock.x = innerx.min; + } + + if (left) { + left->clock.y = bound->outer.max; + left->clock.x = outerx.max; + left->center.y = bound->ellipse.max; + left->center.x = ellipsex.max; + left->counterClock.y = bound->inner.max; + left->counterClock.x = innerx.max; + } + + bound->left.min = bound->inner.max; + bound->left.max = bound->outer.max; + bound->right.min = bound->inner.min; + bound->right.max = bound->outer.min; + + computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min, + &acc->right); + computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max, + &acc->left); + + if (bound->inner.min > bound->inner.max) { + t = bound->inner.min; + bound->inner.min = bound->inner.max; + bound->inner.max = t; + } + tail_y = acc->tail_y; + if (tail_y > bound->ellipse.max) + tail_y = bound->ellipse.max; + else if (tail_y < bound->ellipse.min) + tail_y = bound->ellipse.min; + innerTaily = innerYfromY (tail_y, def, acc); + if (bound->inner.min > innerTaily) + bound->inner.min = innerTaily; + if (bound->inner.max < innerTaily) + bound->inner.max = innerTaily; + bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY); + bound->inneri.max = floor(bound->inner.max - acc->fromIntY); + bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY); + bound->outeri.max = floor(bound->outer.max - acc->fromIntY); +} + +/* + * this section computes the x value of the span at y + * intersected with the specified face of the ellipse. + * + * this is the min/max X value over the set of normal + * lines to the entire ellipse, the equation of the + * normal lines is: + * + * ellipse_x h^2 h^2 + * x = ------------ y + ellipse_x (1 - --- ) + * ellipse_y w^2 w^2 + * + * compute the derivative with-respect-to ellipse_y and solve + * for zero: + * + * (w^2 - h^2) ellipse_y^3 + h^4 y + * 0 = - ---------------------------------- + * h w ellipse_y^2 sqrt (h^2 - ellipse_y^2) + * + * ( h^4 y ) + * ellipse_y = ( ---------- ) ^ (1/3) + * ( (h^2 - w^2) ) + * + * The other two solutions to the equation are imaginary. + * + * This gives the position on the ellipse which generates + * the normal with the largest/smallest x intersection point. + * + * Now compute the second derivative to check whether + * the intersection is a minimum or maximum: + * + * h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2)) + * - ------------------------------------------- + * w y0^3 (sqrt (h^2 - y^2)) ^ 3 + * + * as we only care about the sign, + * + * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2)) + * + * or (to use accelerators), + * + * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2) + * + */ + +/* + * computes the position on the ellipse whose normal line + * intersects the given scan line maximally + */ + +static double +hookEllipseY (scan_y, bound, acc, left) + double scan_y; + struct arc_bound *bound; + struct accelerators *acc; + int left; +{ + double ret; + + if (acc->h2mw2 == 0) { + if ( (scan_y > 0 && !left) || (scan_y < 0 && left) ) + return bound->ellipse.min; + return bound->ellipse.max; + } + ret = (acc->h4 * scan_y) / (acc->h2mw2); + if (ret >= 0) + return cbrt (ret); + else + return -cbrt (-ret); +} + +/* + * computes the X value of the intersection of the + * given scan line with the right side of the lower hook + */ + +static double +hookX (scan_y, def, bound, acc, left) + double scan_y; + struct arc_def *def; + struct arc_bound *bound; + struct accelerators *acc; + int left; +{ + double ellipse_y, x; + double maxMin; + + if (def->w != def->h) { + ellipse_y = hookEllipseY (scan_y, bound, acc, left); + if (boundedLe (ellipse_y, bound->ellipse)) { + /* + * compute the value of the second + * derivative + */ + maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 - + acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2); + if ((left && maxMin > 0) || (!left && maxMin < 0)) { + if (ellipse_y == 0) + return def->w + left ? -def->l : def->l; + x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) * + sqrt (acc->h2 - ellipse_y * ellipse_y) / + (def->h * def->w * ellipse_y); + return x; + } + } + } + if (left) { + if (acc->left.valid && boundedLe (scan_y, bound->left)) { + x = intersectLine (scan_y, acc->left); + } else { + if (acc->right.valid) + x = intersectLine (scan_y, acc->right); + else + x = def->w - def->l; + } + } else { + if (acc->right.valid && boundedLe (scan_y, bound->right)) { + x = intersectLine (scan_y, acc->right); + } else { + if (acc->left.valid) + x = intersectLine (scan_y, acc->left); + else + x = def->w - def->l; + } + } + return x; +} + +/* + * generate the set of spans with + * the given y coordinate + */ + +static void +arcSpan (y, lx, lw, rx, rw, def, bounds, acc, mask) + int y; + int lx; + int lw; + int rx; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + int linx, loutx, rinx, routx; + double x, altx; + + if (boundedLe (y, bounds->inneri)) { + linx = -(lx + lw); + rinx = rx; + } else { + /* + * intersection with left face + */ + x = hookX (y + acc->fromIntY, def, bounds, acc, 1); + if (acc->right.valid && + boundedLe (y + acc->fromIntY, bounds->right)) + { + altx = intersectLine (y + acc->fromIntY, acc->right); + if (altx < x) + x = altx; + } + linx = -ICEIL(acc->fromIntX - x); + rinx = ICEIL(acc->fromIntX + x); + } + if (boundedLe (y, bounds->outeri)) { + loutx = -lx; + routx = rx + rw; + } else { + /* + * intersection with right face + */ + x = hookX (y + acc->fromIntY, def, bounds, acc, 0); + if (acc->left.valid && + boundedLe (y + acc->fromIntY, bounds->left)) + { + altx = x; + x = intersectLine (y + acc->fromIntY, acc->left); + if (x < altx) + x = altx; + } + loutx = -ICEIL(acc->fromIntX - x); + routx = ICEIL(acc->fromIntX + x); + } + if (routx > rinx) { + if (mask & 1) + newFinalSpan (acc->yorgu - y, + acc->xorg + rinx, acc->xorg + routx); + if (mask & 8) + newFinalSpan (acc->yorgl + y, + acc->xorg + rinx, acc->xorg + routx); + } + if (loutx > linx) { + if (mask & 2) + newFinalSpan (acc->yorgu - y, + acc->xorg - loutx, acc->xorg - linx); + if (mask & 4) + newFinalSpan (acc->yorgl + y, + acc->xorg - loutx, acc->xorg - linx); + } +} + +static void +arcSpan0 (lx, lw, rx, rw, def, bounds, acc, mask) + int lx; + int lw; + int rx; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + double x; + + if (boundedLe (0, bounds->inneri) && + acc->left.valid && boundedLe (0, bounds->left) && + acc->left.b > 0) + { + x = def->w - def->l; + if (acc->left.b < x) + x = acc->left.b; + lw = ICEIL(acc->fromIntX - x) - lx; + rw += rx; + rx = ICEIL(acc->fromIntX + x); + rw -= rx; + } + arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask); +} + +static void +tailSpan (y, lw, rw, def, bounds, acc, mask) + int y; + int lw; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + double yy, xalt, x, lx, rx; + int n; + + if (boundedLe(y, bounds->outeri)) + arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask); + else if (def->w != def->h) { + yy = y + acc->fromIntY; + x = tailX(yy, def, bounds, acc); + if (yy == 0.0 && x == -rw - acc->fromIntX) + return; + if (acc->right.valid && boundedLe (yy, bounds->right)) { + rx = x; + lx = -x; + xalt = intersectLine (yy, acc->right); + if (xalt >= -rw - acc->fromIntX && xalt <= rx) + rx = xalt; + n = ICEIL(acc->fromIntX + lx); + if (lw > n) { + if (mask & 2) + newFinalSpan (acc->yorgu - y, + acc->xorg + n, acc->xorg + lw); + if (mask & 4) + newFinalSpan (acc->yorgl + y, + acc->xorg + n, acc->xorg + lw); + } + n = ICEIL(acc->fromIntX + rx); + if (n > -rw) { + if (mask & 1) + newFinalSpan (acc->yorgu - y, + acc->xorg - rw, acc->xorg + n); + if (mask & 8) + newFinalSpan (acc->yorgl + y, + acc->xorg - rw, acc->xorg + n); + } + } + arcSpan (y, + ICEIL(acc->fromIntX - x), 0, + ICEIL(acc->fromIntX + x), 0, + def, bounds, acc, mask); + } +} + +/* + * create whole arcs out of pieces. This code is + * very bad. + */ + +static struct finalSpan **finalSpans = NULL; +static int finalMiny = 0, finalMaxy = -1; +static int finalSize = 0; + +static int nspans = 0; /* total spans, not just y coords */ + +struct finalSpan { + struct finalSpan *next; + int min, max; /* x values */ +}; + +static struct finalSpan *freeFinalSpans, *tmpFinalSpan; + +# define allocFinalSpan() (freeFinalSpans ?\ + ((tmpFinalSpan = freeFinalSpans), \ + (freeFinalSpans = freeFinalSpans->next), \ + (tmpFinalSpan->next = 0), \ + tmpFinalSpan) : \ + realAllocSpan ()) + +# define SPAN_CHUNK_SIZE 128 + +struct finalSpanChunk { + struct finalSpan data[SPAN_CHUNK_SIZE]; + struct finalSpanChunk *next; +}; + +static struct finalSpanChunk *chunks; + +struct finalSpan * +realAllocSpan () +{ + register struct finalSpanChunk *newChunk; + register struct finalSpan *span; + register int i; + + newChunk = (struct finalSpanChunk *) g_malloc (sizeof (struct finalSpanChunk)); + if (!newChunk) + return (struct finalSpan *) NULL; + newChunk->next = chunks; + chunks = newChunk; + freeFinalSpans = span = newChunk->data + 1; + for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) { + span->next = span+1; + span++; + } + span->next = 0; + span = newChunk->data; + span->next = 0; + return span; +} + +static void +disposeFinalSpans () +{ + struct finalSpanChunk *chunk, *next; + + for (chunk = chunks; chunk; chunk = next) { + next = chunk->next; + g_free (chunk); + } + chunks = 0; + freeFinalSpans = 0; + g_free(finalSpans); + finalSpans = 0; +} + +static void +fillSpans (pDrawable, pGC) + GdkDrawable* pDrawable; + GdkGC* pGC; +{ + register struct finalSpan *span; + register GdkRectangle* xSpan; + register int i; + register struct finalSpan **f; + register int spany; + GdkRectangle* xSpans; + + if (nspans == 0) + return; + xSpan = xSpans = (GdkRectangle*) ALLOCATE_LOCAL (nspans * sizeof (GdkRectangle)); + if (xSpans) + { + i = 0; + f = finalSpans; + for (spany = finalMiny; spany <= finalMaxy; spany++, f++) { + for (span = *f; span; span=span->next) { + if (span->max <= span->min) + continue; + xSpan->x = span->min; + xSpan->y = spany; + xSpan->width = span->max - span->min; + xSpan->height = 1; + ++xSpan; + ++i; + } + } + + gdk_fb_fill_spans(pDrawable, pGC, xSpans, i); + } + disposeFinalSpans (); + if (xSpans) + DEALLOCATE_LOCAL (xSpans); + finalMiny = 0; + finalMaxy = -1; + finalSize = 0; + nspans = 0; +} + +# define SPAN_REALLOC 100 + +# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \ + &finalSpans[(y) - finalMiny] : \ + realFindSpan (y)) + +static struct finalSpan ** +realFindSpan (y) + int y; +{ + struct finalSpan **newSpans; + int newSize, newMiny, newMaxy; + int change; + int i; + + if (y < finalMiny || y > finalMaxy) { + if (!finalSize) { + finalMiny = y; + finalMaxy = y - 1; + } + if (y < finalMiny) + change = finalMiny - y; + else + change = y - finalMaxy; + if (change >= SPAN_REALLOC) + change += SPAN_REALLOC; + else + change = SPAN_REALLOC; + newSize = finalSize + change; + newSpans = (struct finalSpan **) g_malloc + (newSize * sizeof (struct finalSpan *)); + if (!newSpans) + return (struct finalSpan **)NULL; + newMiny = finalMiny; + newMaxy = finalMaxy; + if (y < finalMiny) + newMiny = finalMiny - change; + else + newMaxy = finalMaxy + change; + if (finalSpans) { + g_memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *), + (char *) finalSpans, + finalSize * sizeof (struct finalSpan *)); + g_free (finalSpans); + } + if ((i = finalMiny - newMiny) > 0) + memset ((char *)newSpans, 0, i * sizeof (struct finalSpan *)); + if ((i = newMaxy - finalMaxy) > 0) + memset ((char *)(newSpans + newSize - i), 0, + i * sizeof (struct finalSpan *)); + finalSpans = newSpans; + finalMaxy = newMaxy; + finalMiny = newMiny; + finalSize = newSize; + } + return &finalSpans[y - finalMiny]; +} + +static void +newFinalSpan (y, xmin, xmax) + int y; + register int xmin, xmax; +{ + register struct finalSpan *x; + register struct finalSpan **f; + struct finalSpan *oldx; + struct finalSpan *prev; + + f = findSpan (y); + if (!f) + return; + oldx = 0; + for (;;) { + prev = 0; + for (x = *f; x; x=x->next) { + if (x == oldx) { + prev = x; + continue; + } + if (x->min <= xmax && xmin <= x->max) { + if (oldx) { + oldx->min = MIN (x->min, xmin); + oldx->max = MAX (x->max, xmax); + if (prev) + prev->next = x->next; + else + *f = x->next; + --nspans; + } else { + x->min = MIN (x->min, xmin); + x->max = MAX (x->max, xmax); + oldx = x; + } + xmin = oldx->min; + xmax = oldx->max; + break; + } + prev = x; + } + if (!x) + break; + } + if (!oldx) { + x = allocFinalSpan (); + if (x) + { + x->min = xmin; + x->max = xmax; + x->next = *f; + *f = x; + ++nspans; + } + } +} + +static void +mirrorSppPoint (quadrant, sppPoint) + int quadrant; + SppPointPtr sppPoint; +{ + switch (quadrant) { + case 0: + break; + case 1: + sppPoint->x = -sppPoint->x; + break; + case 2: + sppPoint->x = -sppPoint->x; + sppPoint->y = -sppPoint->y; + break; + case 3: + sppPoint->y = -sppPoint->y; + break; + } + /* + * and translate to X coordinate system + */ + sppPoint->y = -sppPoint->y; +} + +/* + * split an arc into pieces which are scan-converted + * in the first-quadrant and mirrored into position. + * This is necessary as the scan-conversion code can + * only deal with arcs completely contained in the + * first quadrant. + */ + +static void +drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left) + /* miArcFacePtr right, left; */ /* save end line points */ +{ + struct arc_def def; + struct accelerators acc; + int startq, endq, curq; + int rightq, leftq, righta, lefta; + miArcFacePtr passRight, passLeft; + int q0, q1, mask; + struct band { + int a0, a1; + int mask; + } band[5], sweep[20]; + int bandno, sweepno; + int i, j; + int flipRight = 0, flipLeft = 0; + int copyEnd = 0; + miArcSpanData *spdata; + gboolean mustFree; + + spdata = miComputeWideEllipse(l, tarc, &mustFree); + if (!spdata) + return; + + if (a1 < a0) + a1 += 360 * 64; + startq = a0 / (90 * 64); + if (a0 == a1) + endq = startq; + else + endq = (a1-1) / (90 * 64); + bandno = 0; + curq = startq; + rightq = -1; + for (;;) { + switch (curq) { + case 0: + if (a0 > 90 * 64) + q0 = 0; + else + q0 = a0; + if (a1 < 360 * 64) + q1 = MIN (a1, 90 * 64); + else + q1 = 90 * 64; + if (curq == startq && a0 == q0 && rightq < 0) { + righta = q0; + rightq = curq; + } + if (curq == endq && a1 == q1) { + lefta = q1; + leftq = curq; + } + break; + case 1: + if (a1 < 90 * 64) + q0 = 0; + else + q0 = 180 * 64 - MIN (a1, 180 * 64); + if (a0 > 180 * 64) + q1 = 90 * 64; + else + q1 = 180 * 64 - MAX (a0, 90 * 64); + if (curq == startq && 180 * 64 - a0 == q1) { + righta = q1; + rightq = curq; + } + if (curq == endq && 180 * 64 - a1 == q0) { + lefta = q0; + leftq = curq; + } + break; + case 2: + if (a0 > 270 * 64) + q0 = 0; + else + q0 = MAX (a0, 180 * 64) - 180 * 64; + if (a1 < 180 * 64) + q1 = 90 * 64; + else + q1 = MIN (a1, 270 * 64) - 180 * 64; + if (curq == startq && a0 - 180*64 == q0) { + righta = q0; + rightq = curq; + } + if (curq == endq && a1 - 180 * 64 == q1) { + lefta = q1; + leftq = curq; + } + break; + case 3: + if (a1 < 270 * 64) + q0 = 0; + else + q0 = 360 * 64 - MIN (a1, 360 * 64); + q1 = 360 * 64 - MAX (a0, 270 * 64); + if (curq == startq && 360 * 64 - a0 == q1) { + righta = q1; + rightq = curq; + } + if (curq == endq && 360 * 64 - a1 == q0) { + lefta = q0; + leftq = curq; + } + break; + } + band[bandno].a0 = q0; + band[bandno].a1 = q1; + band[bandno].mask = 1 << curq; + bandno++; + if (curq == endq) + break; + curq++; + if (curq == 4) { + a0 = 0; + a1 -= 360 * 64; + curq = 0; + endq -= 4; + } + } + sweepno = 0; + for (;;) { + q0 = 90 * 64; + mask = 0; + /* + * find left-most point + */ + for (i = 0; i < bandno; i++) + if (band[i].a0 <= q0) { + q0 = band[i].a0; + q1 = band[i].a1; + mask = band[i].mask; + } + if (!mask) + break; + /* + * locate next point of change + */ + for (i = 0; i < bandno; i++) + if (!(mask & band[i].mask)) { + if (band[i].a0 == q0) { + if (band[i].a1 < q1) + q1 = band[i].a1; + mask |= band[i].mask; + } else if (band[i].a0 < q1) + q1 = band[i].a0; + } + /* + * create a new sweep + */ + sweep[sweepno].a0 = q0; + sweep[sweepno].a1 = q1; + sweep[sweepno].mask = mask; + sweepno++; + /* + * subtract the sweep from the affected bands + */ + for (i = 0; i < bandno; i++) + if (band[i].a0 == q0) { + band[i].a0 = q1; + /* + * check if this band is empty + */ + if (band[i].a0 == band[i].a1) + band[i].a1 = band[i].a0 = 90 * 64 + 1; + } + } + computeAcc (tarc, l, &def, &acc); + for (j = 0; j < sweepno; j++) { + mask = sweep[j].mask; + passRight = passLeft = 0; + if (mask & (1 << rightq)) { + if (sweep[j].a0 == righta) + passRight = right; + else if (sweep[j].a1 == righta) { + passLeft = right; + flipRight = 1; + } + } + if (mask & (1 << leftq)) { + if (sweep[j].a1 == lefta) + { + if (passLeft) + copyEnd = 1; + passLeft = left; + } + else if (sweep[j].a0 == lefta) { + if (passRight) + copyEnd = 1; + passRight = left; + flipLeft = 1; + } + } + drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask, + passRight, passLeft, spdata); + } + /* + * when copyEnd is set, both ends of the arc were computed + * at the same time; drawQuadrant only takes one end though, + * so the left end will be the only one holding the data. Copy + * it from there. + */ + if (copyEnd) + *right = *left; + /* + * mirror the coordinates generated for the + * faces of the arc + */ + if (right) { + mirrorSppPoint (rightq, &right->clock); + mirrorSppPoint (rightq, &right->center); + mirrorSppPoint (rightq, &right->counterClock); + if (flipRight) { + SppPointRec temp; + + temp = right->clock; + right->clock = right->counterClock; + right->counterClock = temp; + } + } + if (left) { + mirrorSppPoint (leftq, &left->counterClock); + mirrorSppPoint (leftq, &left->center); + mirrorSppPoint (leftq, &left->clock); + if (flipLeft) { + SppPointRec temp; + + temp = left->clock; + left->clock = left->counterClock; + left->counterClock = temp; + } + } + if (mustFree) + g_free(spdata); +} + +static void +drawQuadrant (def, acc, a0, a1, mask, right, left, spdata) + struct arc_def *def; + struct accelerators *acc; + int a0, a1; + int mask; + miArcFacePtr right, left; + miArcSpanData *spdata; +{ + struct arc_bound bound; + double yy, x, xalt; + int y, miny, maxy; + int n; + miArcSpan *span; + + def->a0 = ((double) a0) / 64.0; + def->a1 = ((double) a1) / 64.0; + computeBound (def, &bound, acc, right, left); + yy = bound.inner.min; + if (bound.outer.min < yy) + yy = bound.outer.min; + miny = ICEIL(yy - acc->fromIntY); + yy = bound.inner.max; + if (bound.outer.max > yy) + yy = bound.outer.max; + maxy = floor(yy - acc->fromIntY); + y = spdata->k; + span = spdata->spans; + if (spdata->top) + { + if (a1 == 90 * 64 && (mask & 1)) + newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1); + span++; + } + for (n = spdata->count1; --n >= 0; ) + { + if (y < miny) + return; + if (y <= maxy) { + arcSpan (y, + span->lx, -span->lx, 0, span->lx + span->lw, + def, &bound, acc, mask); + if (span->rw + span->rx) + tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask); + } + y--; + span++; + } + if (y < miny) + return; + if (spdata->hole) + { + if (y <= maxy) + arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc); + } + for (n = spdata->count2; --n >= 0; ) + { + if (y < miny) + return; + if (y <= maxy) + arcSpan (y, span->lx, span->lw, span->rx, span->rw, + def, &bound, acc, mask); + y--; + span++; + } + if (spdata->bot && miny <= y && y <= maxy) + { + n = mask; + if (y == miny) + n &= 0xc; + if (span->rw <= 0) { + arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw, + def, &bound, acc, n); + if (span->rw + span->rx) + tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n); + } + else + arcSpan0 (span->lx, span->lw, span->rx, span->rw, + def, &bound, acc, n); + y--; + } + while (y >= miny) { + yy = y + acc->fromIntY; + if (def->w == def->h) { + xalt = def->w - def->l; + x = -sqrt(xalt * xalt - yy * yy); + } else { + x = tailX(yy, def, &bound, acc); + if (acc->left.valid && boundedLe (yy, bound.left)) { + xalt = intersectLine (yy, acc->left); + if (xalt < x) + x = xalt; + } + if (acc->right.valid && boundedLe (yy, bound.right)) { + xalt = intersectLine (yy, acc->right); + if (xalt < x) + x = xalt; + } + } + arcSpan (y, + ICEIL(acc->fromIntX - x), 0, + ICEIL(acc->fromIntX + x), 0, + def, &bound, acc, mask); + y--; + } +} diff --git a/gdk/linux-fb/midash.c b/gdk/linux-fb/midash.c new file mode 100644 index 0000000000..c939976e78 --- /dev/null +++ b/gdk/linux-fb/midash.c @@ -0,0 +1,309 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: midash.c /main/14 1998/02/09 14:46:34 kaleb $ */ + +#include "mi.h" + +static miDashPtr CheckDashStorage(); + +/* return a list of DashRec. there will be an extra +entry at the end holding the last point of the polyline. + this means that the code that actually draws dashes can +get a pair of points for every dash. only the point in the last +dash record is useful; the other fields are not used. + nseg is the number of segments, not the number of points. + +example: + + dash1.start + dash2.start + dash3.start + last-point + +defines a list of segments + (dash1.pt, dash2.pt) + (dash2.pt, dash3.pt) + (dash3.pt, last-point) +and nseg == 3. + +NOTE: + EVEN_DASH == ~ODD_DASH + +NOTE ALSO: + miDashLines may return 0 segments, going from pt[0] to pt[0] with one dash. +*/ + +enum { EVEN_DASH=0, ODD_DASH=1 }; + +#define sign(x) ((x)>0)?1:( ((x)<0)?-1:0 ) + +miDashPtr +miDashLine(npt, ppt, nDash, pDash, offset, pnseg) +int npt; +GdkPoint* ppt; +unsigned int nDash; +unsigned char *pDash; +unsigned int offset; +int *pnseg; +{ + GdkPoint pt1, pt2; + int lenCur; /* npt used from this dash */ + int lenMax; /* npt in this dash */ + int iDash = 0; /* index of current dash */ + int which; /* EVEN_DASH or ODD_DASH */ + miDashPtr pseg; /* list of dash segments */ + miDashPtr psegBase; /* start of list */ + int nseg = 0; /* number of dashes so far */ + int nsegMax = 0; /* num segs we can fit in this list */ + + int x, y, len; + int adx, ady, signdx, signdy; + int du, dv, e1, e2, e, base_e = 0; + + lenCur = offset; + which = EVEN_DASH; + while(lenCur >= pDash[iDash]) + { + lenCur -= pDash[iDash]; + iDash++; + if (iDash >= nDash) + iDash = 0; + which = ~which; + } + lenMax = pDash[iDash]; + + psegBase = (miDashPtr)NULL; + pt2 = ppt[0]; /* just in case there is only one point */ + + while(--npt) + { + if (PtEqual(ppt[0], ppt[1])) + { + ppt++; + continue; /* no duplicated points in polyline */ + } + pt1 = *ppt++; + pt2 = *ppt; + + adx = pt2.x - pt1.x; + ady = pt2.y - pt1.y; + signdx = sign(adx); + signdy = sign(ady); + adx = abs(adx); + ady = abs(ady); + + if (adx > ady) + { + du = adx; + dv = ady; + len = adx; + } + else + { + du = ady; + dv = adx; + len = ady; + } + + e1 = dv * 2; + e2 = e1 - 2*du; + e = e1 - du; + x = pt1.x; + y = pt1.y; + + nseg++; + pseg = CheckDashStorage(&psegBase, nseg, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt = pt1; + pseg->e1 = e1; + pseg->e2 = e2; + base_e = pseg->e = e; + pseg->which = which; + pseg->newLine = 1; + + while (len--) + { + if (adx > ady) + { + /* X_AXIS */ + if (((signdx > 0) && (e < 0)) || + ((signdx <=0) && (e <=0)) + ) + { + e += e1; + } + else + { + y += signdy; + e += e2; + } + x += signdx; + } + else + { + /* Y_AXIS */ + if (((signdx > 0) && (e < 0)) || + ((signdx <=0) && (e <=0)) + ) + { + e +=e1; + } + else + { + x += signdx; + e += e2; + } + y += signdy; + } + + lenCur++; + if (lenCur >= lenMax && (len || npt <= 1)) + { + nseg++; + pseg = CheckDashStorage(&psegBase, nseg, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt.x = x; + pseg->pt.y = y; + pseg->e1 = e1; + pseg->e2 = e2; + pseg->e = e; + which = ~which; + pseg->which = which; + pseg->newLine = 0; + + /* move on to next dash */ + iDash++; + if (iDash >= nDash) + iDash = 0; + lenMax = pDash[iDash]; + lenCur = 0; + } + } /* while len-- */ + } /* while --npt */ + + if (lenCur == 0 && nseg != 0) + { + nseg--; + which = ~which; + } + *pnseg = nseg; + pseg = CheckDashStorage(&psegBase, nseg+1, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt = pt2; + pseg->e = base_e; + pseg->which = which; + pseg->newLine = 0; + return psegBase; +} + + +#define NSEGDELTA 16 + +/* returns a pointer to the pseg[nseg-1], growing the storage as +necessary. this interface seems unnecessarily cumbersome. + +*/ + +static +miDashPtr +CheckDashStorage(ppseg, nseg, pnsegMax) +miDashPtr *ppseg; /* base pointer */ +int nseg; /* number of segment we want to write to */ +int *pnsegMax; /* size (in segments) of list so far */ +{ + if (nseg > *pnsegMax) + { + miDashPtr newppseg; + + *pnsegMax += NSEGDELTA; + newppseg = (miDashPtr)g_realloc(*ppseg, + (*pnsegMax)*sizeof(miDashRec)); + if (!newppseg) + { + g_free(*ppseg); + return (miDashPtr)NULL; + } + *ppseg = newppseg; + } + return(*ppseg+(nseg-1)); +} + +void +miStepDash (dist, pDashIndex, pDash, numInDashList, pDashOffset) + int dist; /* distance to step */ + int *pDashIndex; /* current dash */ + unsigned char *pDash; /* dash list */ + int numInDashList; /* total length of dash list */ + int *pDashOffset; /* offset into current dash */ +{ + int dashIndex, dashOffset; + int totallen; + int i; + + dashIndex = *pDashIndex; + dashOffset = *pDashOffset; + if (dist < pDash[dashIndex] - dashOffset) + { + *pDashOffset = dashOffset + dist; + return; + } + dist -= pDash[dashIndex] - dashOffset; + if (++dashIndex == numInDashList) + dashIndex = 0; + totallen = 0; + for (i = 0; i < numInDashList; i++) + totallen += pDash[i]; + if (totallen <= dist) + dist = dist % totallen; + while (dist >= pDash[dashIndex]) + { + dist -= pDash[dashIndex]; + if (++dashIndex == numInDashList) + dashIndex = 0; + } + *pDashIndex = dashIndex; + *pDashOffset = dist; +} diff --git a/gdk/linux-fb/mifillarc.c b/gdk/linux-fb/mifillarc.c new file mode 100644 index 0000000000..115be216c3 --- /dev/null +++ b/gdk/linux-fb/mifillarc.c @@ -0,0 +1,743 @@ +/* $XFree86: xc/programs/Xserver/mi/mifillarc.c,v 3.4 1999/04/11 13:11:20 dawes Exp $ */ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Bob Scheifler, MIT X Consortium + +********************************************************/ + +/* $TOG: mifillarc.c /main/20 1998/02/09 14:46:52 kaleb $ */ + +#include +#include "mi.h" +#include "mifillarc.h" +#include "gdkprivate-fb.h" + +#define QUADRANT (90 * 64) +#define HALFCIRCLE (180 * 64) +#define QUADRANT3 (270 * 64) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define Dsin(d) sin((double)d*(M_PI/11520.0)) +#define Dcos(d) cos((double)d*(M_PI/11520.0)) + +void +miFillArcSetup(arc, info) + register miArc *arc; + register miFillArcRec *info; +{ + info->y = arc->height >> 1; + info->dy = arc->height & 1; + info->yorg = arc->y + info->y; + info->dx = arc->width & 1; + info->xorg = arc->x + (arc->width >> 1) + info->dx; + info->dx = 1 - info->dx; + if (arc->width == arc->height) + { + /* (2x - 2xorg)^2 = d^2 - (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->ym = 8; + info->xm = 8; + info->yk = info->y << 3; + if (!info->dx) + { + info->xk = 0; + info->e = -1; + } + else + { + info->y++; + info->yk += 4; + info->xk = -4; + info->e = - (info->y << 3); + } + } + else + { + /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->ym = (arc->width * arc->width) << 3; + info->xm = (arc->height * arc->height) << 3; + info->yk = info->y * info->ym; + if (!info->dy) + info->yk -= info->ym >> 1; + if (!info->dx) + { + info->xk = 0; + info->e = - (info->xm >> 3); + } + else + { + info->y++; + info->yk += info->ym; + info->xk = -(info->xm >> 1); + info->e = info->xk - info->yk; + } + } +} + +void +miFillArcDSetup(arc, info) + register miArc *arc; + register miFillArcDRec *info; +{ + /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->y = arc->height >> 1; + info->dy = arc->height & 1; + info->yorg = arc->y + info->y; + info->dx = arc->width & 1; + info->xorg = arc->x + (arc->width >> 1) + info->dx; + info->dx = 1 - info->dx; + info->ym = ((double)arc->width) * (arc->width * 8); + info->xm = ((double)arc->height) * (arc->height * 8); + info->yk = info->y * info->ym; + if (!info->dy) + info->yk -= info->ym / 2.0; + if (!info->dx) + { + info->xk = 0; + info->e = - (info->xm / 8.0); + } + else + { + info->y++; + info->yk += info->ym; + info->xk = -info->xm / 2.0; + info->e = info->xk - info->yk; + } +} + +static void +miGetArcEdge(arc, edge, k, top, left) + register miArc *arc; + register miSliceEdgePtr edge; + int k; + gboolean top, left; +{ + register int xady, y; + + y = arc->height >> 1; + if (!(arc->width & 1)) + y++; + if (!top) + { + y = -y; + if (arc->height & 1) + y--; + } + xady = k + y * edge->dx; + if (xady <= 0) + edge->x = - ((-xady) / edge->dy + 1); + else + edge->x = (xady - 1) / edge->dy; + edge->e = xady - edge->x * edge->dy; + if ((top && (edge->dx < 0)) || (!top && (edge->dx > 0))) + edge->e = edge->dy - edge->e + 1; + if (left) + edge->x++; + edge->x += arc->x + (arc->width >> 1); + if (edge->dx > 0) + { + edge->deltax = 1; + edge->stepx = edge->dx / edge->dy; + edge->dx = edge->dx % edge->dy; + } + else + { + edge->deltax = -1; + edge->stepx = - ((-edge->dx) / edge->dy); + edge->dx = (-edge->dx) % edge->dy; + } + if (!top) + { + edge->deltax = -edge->deltax; + edge->stepx = -edge->stepx; + } +} + +void +miEllipseAngleToSlope (angle, width, height, dxp, dyp, d_dxp, d_dyp) + int angle; + int width; + int height; + int *dxp; + int *dyp; + double *d_dxp; + double *d_dyp; +{ + int dx, dy; + double d_dx, d_dy, scale; + gboolean negative_dx, negative_dy; + + switch (angle) { + case 0: + *dxp = -1; + *dyp = 0; + if (d_dxp) { + *d_dxp = width / 2.0; + *d_dyp = 0; + } + break; + case QUADRANT: + *dxp = 0; + *dyp = 1; + if (d_dxp) { + *d_dxp = 0; + *d_dyp = - height / 2.0; + } + break; + case HALFCIRCLE: + *dxp = 1; + *dyp = 0; + if (d_dxp) { + *d_dxp = - width / 2.0; + *d_dyp = 0; + } + break; + case QUADRANT3: + *dxp = 0; + *dyp = -1; + if (d_dxp) { + *d_dxp = 0; + *d_dyp = height / 2.0; + } + break; + default: + d_dx = Dcos(angle) * width; + d_dy = Dsin(angle) * height; + if (d_dxp) { + *d_dxp = d_dx / 2.0; + *d_dyp = - d_dy / 2.0; + } + negative_dx = FALSE; + if (d_dx < 0.0) + { + d_dx = -d_dx; + negative_dx = TRUE; + } + negative_dy = FALSE; + if (d_dy < 0.0) + { + d_dy = -d_dy; + negative_dy = TRUE; + } + scale = d_dx; + if (d_dy > d_dx) + scale = d_dy; + dx = floor ((d_dx * 32768) / scale + 0.5); + if (negative_dx) + dx = -dx; + *dxp = dx; + dy = floor ((d_dy * 32768) / scale + 0.5); + if (negative_dy) + dy = -dy; + *dyp = dy; + break; + } +} + +static void +miGetPieEdge(arc, angle, edge, top, left) + register miArc *arc; + register int angle; + register miSliceEdgePtr edge; + gboolean top, left; +{ + register int k; + int dx, dy; + + miEllipseAngleToSlope (angle, arc->width, arc->height, &dx, &dy, 0, 0); + + if (dy == 0) + { + edge->x = left ? -65536 : 65536; + edge->stepx = 0; + edge->e = 0; + edge->dx = -1; + return; + } + if (dx == 0) + { + edge->x = arc->x + (arc->width >> 1); + if (left && (arc->width & 1)) + edge->x++; + else if (!left && !(arc->width & 1)) + edge->x--; + edge->stepx = 0; + edge->e = 0; + edge->dx = -1; + return; + } + if (dy < 0) { + dx = -dx; + dy = -dy; + } + k = (arc->height & 1) ? dx : 0; + if (arc->width & 1) + k += dy; + edge->dx = dx << 1; + edge->dy = dy << 1; + miGetArcEdge(arc, edge, k, top, left); +} + +void +miFillArcSliceSetup(arc, slice, pGC) + register miArc *arc; + register miArcSliceRec *slice; + GdkGC* pGC; +{ + register int angle1, angle2; + + angle1 = arc->angle1; + if (arc->angle2 < 0) + { + angle2 = angle1; + angle1 += arc->angle2; + } + else + angle2 = angle1 + arc->angle2; + while (angle1 < 0) + angle1 += FULLCIRCLE; + while (angle1 >= FULLCIRCLE) + angle1 -= FULLCIRCLE; + while (angle2 < 0) + angle2 += FULLCIRCLE; + while (angle2 >= FULLCIRCLE) + angle2 -= FULLCIRCLE; + slice->min_top_y = 0; + slice->max_top_y = arc->height >> 1; + slice->min_bot_y = 1 - (arc->height & 1); + slice->max_bot_y = slice->max_top_y - 1; + slice->flip_top = FALSE; + slice->flip_bot = FALSE; + if (0 /* pGC->arcMode == ArcPieSlice */) + { + slice->edge1_top = (angle1 < HALFCIRCLE); + slice->edge2_top = (angle2 <= HALFCIRCLE); + if ((angle2 == 0) || (angle1 == HALFCIRCLE)) + { + if (angle2 ? slice->edge2_top : slice->edge1_top) + slice->min_top_y = slice->min_bot_y; + else + slice->min_top_y = arc->height; + slice->min_bot_y = 0; + } + else if ((angle1 == 0) || (angle2 == HALFCIRCLE)) + { + slice->min_top_y = slice->min_bot_y; + if (angle1 ? slice->edge1_top : slice->edge2_top) + slice->min_bot_y = arc->height; + else + slice->min_bot_y = 0; + } + else if (slice->edge1_top == slice->edge2_top) + { + if (angle2 < angle1) + { + slice->flip_top = slice->edge1_top; + slice->flip_bot = !slice->edge1_top; + } + else if (slice->edge1_top) + { + slice->min_top_y = 1; + slice->min_bot_y = arc->height; + } + else + { + slice->min_bot_y = 0; + slice->min_top_y = arc->height; + } + } + miGetPieEdge(arc, angle1, &slice->edge1, + slice->edge1_top, !slice->edge1_top); + miGetPieEdge(arc, angle2, &slice->edge2, + slice->edge2_top, slice->edge2_top); + } + else + { + double w2, h2, x1, y1, x2, y2, dx, dy, scale; + int signdx, signdy, y, k; + gboolean isInt1 = TRUE, isInt2 = TRUE; + + w2 = (double)arc->width / 2.0; + h2 = (double)arc->height / 2.0; + if ((angle1 == 0) || (angle1 == HALFCIRCLE)) + { + x1 = angle1 ? -w2 : w2; + y1 = 0.0; + } + else if ((angle1 == QUADRANT) || (angle1 == QUADRANT3)) + { + x1 = 0.0; + y1 = (angle1 == QUADRANT) ? h2 : -h2; + } + else + { + isInt1 = FALSE; + x1 = Dcos(angle1) * w2; + y1 = Dsin(angle1) * h2; + } + if ((angle2 == 0) || (angle2 == HALFCIRCLE)) + { + x2 = angle2 ? -w2 : w2; + y2 = 0.0; + } + else if ((angle2 == QUADRANT) || (angle2 == QUADRANT3)) + { + x2 = 0.0; + y2 = (angle2 == QUADRANT) ? h2 : -h2; + } + else + { + isInt2 = FALSE; + x2 = Dcos(angle2) * w2; + y2 = Dsin(angle2) * h2; + } + dx = x2 - x1; + dy = y2 - y1; + if (arc->height & 1) + { + y1 -= 0.5; + y2 -= 0.5; + } + if (arc->width & 1) + { + x1 += 0.5; + x2 += 0.5; + } + if (dy < 0.0) + { + dy = -dy; + signdy = -1; + } + else + signdy = 1; + if (dx < 0.0) + { + dx = -dx; + signdx = -1; + } + else + signdx = 1; + if (isInt1 && isInt2) + { + slice->edge1.dx = dx * 2; + slice->edge1.dy = dy * 2; + } + else + { + scale = (dx > dy) ? dx : dy; + slice->edge1.dx = floor((dx * 32768) / scale + .5); + slice->edge1.dy = floor((dy * 32768) / scale + .5); + } + if (!slice->edge1.dy) + { + if (signdx < 0) + { + y = floor(y1 + 1.0); + if (y >= 0) + { + slice->min_top_y = y; + slice->min_bot_y = arc->height; + } + else + { + slice->max_bot_y = -y - (arc->height & 1); + } + } + else + { + y = floor(y1); + if (y >= 0) + slice->max_top_y = y; + else + { + slice->min_top_y = arc->height; + slice->min_bot_y = -y - (arc->height & 1); + } + } + slice->edge1_top = TRUE; + slice->edge1.x = 65536; + slice->edge1.stepx = 0; + slice->edge1.e = 0; + slice->edge1.dx = -1; + slice->edge2 = slice->edge1; + slice->edge2_top = FALSE; + } + else if (!slice->edge1.dx) + { + if (signdy < 0) + x1 -= 1.0; + slice->edge1.x = ceil(x1); + slice->edge1_top = signdy < 0; + slice->edge1.x += arc->x + (arc->width >> 1); + slice->edge1.stepx = 0; + slice->edge1.e = 0; + slice->edge1.dx = -1; + slice->edge2_top = !slice->edge1_top; + slice->edge2 = slice->edge1; + } + else + { + if (signdx < 0) + slice->edge1.dx = -slice->edge1.dx; + if (signdy < 0) + slice->edge1.dx = -slice->edge1.dx; + k = ceil(((x1 + x2) * slice->edge1.dy - (y1 + y2) * slice->edge1.dx) / 2.0); + slice->edge2.dx = slice->edge1.dx; + slice->edge2.dy = slice->edge1.dy; + slice->edge1_top = signdy < 0; + slice->edge2_top = !slice->edge1_top; + miGetArcEdge(arc, &slice->edge1, k, + slice->edge1_top, !slice->edge1_top); + miGetArcEdge(arc, &slice->edge2, k, + slice->edge2_top, slice->edge2_top); + } + } +} + +#define ADDSPANS() \ + pts->x = xorg - x; \ + pts->y = yorg - y; \ + pts->width = slw; \ + pts->height = 1; \ + pts++; \ + if (miFillArcLower(slw)) \ + { \ + pts->x = xorg - x; \ + pts->y = yorg + y + dy; \ + pts->width = slw; \ + pts->height = 1; \ + pts++; \ + } + +static void +miFillEllipseI(pDraw, pGC, arc) + GdkDrawable* pDraw; + GdkGC* pGC; + miArc *arc; +{ + register int x, y, e; + int yk, xk, ym, xm, dx, dy, xorg, yorg; + int slw; + miFillArcRec info; + GdkRectangle* points; + register GdkRectangle* pts; + + points = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * arc->height); + if (!points) + return; + miFillArcSetup(arc, &info); + MIFILLARCSETUP(); + pts = points; + while (y > 0) + { + MIFILLARCSTEP(slw); + ADDSPANS(); + } + gdk_fb_fill_spans(pDraw, pGC, points, pts - points); + + DEALLOCATE_LOCAL(points); +} + +static void +miFillEllipseD(pDraw, pGC, arc) + GdkDrawable* pDraw; + GdkGC* pGC; + miArc *arc; +{ + register int x, y; + int xorg, yorg, dx, dy, slw; + double e, yk, xk, ym, xm; + miFillArcDRec info; + GdkRectangle* points; + register GdkRectangle* pts; + + points = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * arc->height); + if (!points) + return; + miFillArcDSetup(arc, &info); + MIFILLARCSETUP(); + pts = points; + while (y > 0) + { + MIFILLARCSTEP(slw); + ADDSPANS(); + } + gdk_fb_fill_spans(pDraw, pGC, points, pts - points); + DEALLOCATE_LOCAL(points); +} + +#define ADDSPAN(l,r) \ + if (r >= l) \ + { \ + pts->x = l; \ + pts->y = ya; \ + pts->width = r - l + 1; \ + pts->height = 1; \ + pts++; \ + } + +#define ADDSLICESPANS(flip) \ + if (!flip) \ + { \ + ADDSPAN(xl, xr); \ + } \ + else \ + { \ + xc = xorg - x; \ + ADDSPAN(xc, xr); \ + xc += slw - 1; \ + ADDSPAN(xl, xc); \ + } + +static void +miFillArcSliceI(pDraw, pGC, arc) + GdkDrawable* pDraw; + GdkGC* pGC; + miArc *arc; +{ + int yk, xk, ym, xm, dx, dy, xorg, yorg, slw; + register int x, y, e; + miFillArcRec info; + miArcSliceRec slice; + int ya, xl, xr, xc; + GdkRectangle* points; + register GdkRectangle* pts; + + miFillArcSetup(arc, &info); + miFillArcSliceSetup(arc, &slice, pGC); + MIFILLARCSETUP(); + slw = arc->height; + if (slice.flip_top || slice.flip_bot) + slw += (arc->height >> 1) + 1; + points = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * slw); + if (!points) + return; + pts = points; + while (y > 0) + { + MIFILLARCSTEP(slw); + MIARCSLICESTEP(slice.edge1); + MIARCSLICESTEP(slice.edge2); + if (miFillSliceUpper(slice)) + { + ya = yorg - y; + MIARCSLICEUPPER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_top); + } + if (miFillSliceLower(slice)) + { + ya = yorg + y + dy; + MIARCSLICELOWER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_bot); + } + } + + gdk_fb_fill_spans(pDraw, pGC, points, pts - points); + DEALLOCATE_LOCAL(points); +} + +static void +miFillArcSliceD(pDraw, pGC, arc) + GdkDrawable* pDraw; + GdkGC* pGC; + miArc *arc; +{ + register int x, y; + int dx, dy, xorg, yorg, slw; + double e, yk, xk, ym, xm; + miFillArcDRec info; + miArcSliceRec slice; + int ya, xl, xr, xc; + GdkRectangle* points; + register GdkRectangle* pts; + + miFillArcDSetup(arc, &info); + miFillArcSliceSetup(arc, &slice, pGC); + MIFILLARCSETUP(); + slw = arc->height; + if (slice.flip_top || slice.flip_bot) + slw += (arc->height >> 1) + 1; + points = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * slw); + if (!points) + return; + pts = points; + while (y > 0) + { + MIFILLARCSTEP(slw); + MIARCSLICESTEP(slice.edge1); + MIARCSLICESTEP(slice.edge2); + if (miFillSliceUpper(slice)) + { + ya = yorg - y; + MIARCSLICEUPPER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_top); + } + if (miFillSliceLower(slice)) + { + ya = yorg + y + dy; + MIARCSLICELOWER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_bot); + } + } + gdk_fb_fill_spans(pDraw, pGC, points, pts - points); + + DEALLOCATE_LOCAL(points); +} + +/* MIPOLYFILLARC -- The public entry for the PolyFillArc request. + * Since we don't have to worry about overlapping segments, we can just + * fill each arc as it comes. + */ +void +miPolyFillArc(pDraw, pGC, narcs, parcs) + GdkDrawable* pDraw; + GdkGC* pGC; + int narcs; + miArc *parcs; +{ + register int i; + register miArc *arc; + + for(i = narcs, arc = parcs; --i >= 0; arc++) + { + if (miFillArcEmpty(arc)) + continue;; + if ((arc->angle2 >= FULLCIRCLE) || (arc->angle2 <= -FULLCIRCLE)) + { + if (miCanFillArc(arc)) + miFillEllipseI(pDraw, pGC, arc); + else + miFillEllipseD(pDraw, pGC, arc); + } + else + { + if (miCanFillArc(arc)) + miFillArcSliceI(pDraw, pGC, arc); + else + miFillArcSliceD(pDraw, pGC, arc); + } + } +} diff --git a/gdk/linux-fb/mifillarc.h b/gdk/linux-fb/mifillarc.h new file mode 100644 index 0000000000..99a75d6423 --- /dev/null +++ b/gdk/linux-fb/mifillarc.h @@ -0,0 +1,224 @@ +/* $XFree86: xc/programs/Xserver/mi/mifillarc.h,v 3.3 1998/10/04 09:39:27 dawes Exp $ */ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +********************************************************/ + +/* $TOG: mifillarc.h /main/11 1998/02/09 14:46:57 kaleb $ */ + +#ifndef MIFILLARC_H +#define MIFILLARC_H 1 + +#define FULLCIRCLE (360 * 64) + +typedef struct _miFillArc { + int xorg, yorg; + int y; + int dx, dy; + int e; + int ym, yk, xm, xk; +} miFillArcRec; + +/* could use 64-bit integers */ +typedef struct _miFillArcD { + int xorg, yorg; + int y; + int dx, dy; + double e; + double ym, yk, xm, xk; +} miFillArcDRec; + +#define miFillArcEmpty(arc) (!(arc)->angle2 || \ + !(arc)->width || !(arc)->height || \ + (((arc)->width == 1) && ((arc)->height & 1))) + +#define miCanFillArc(arc) (((arc)->width == (arc)->height) || \ + (((arc)->width <= 800) && ((arc)->height <= 800))) + +#define MIFILLARCSETUP() \ + x = 0; \ + y = info.y; \ + e = info.e; \ + xk = info.xk; \ + xm = info.xm; \ + yk = info.yk; \ + ym = info.ym; \ + dx = info.dx; \ + dy = info.dy; \ + xorg = info.xorg; \ + yorg = info.yorg + +#define MIFILLARCSTEP(slw) \ + e += yk; \ + while (e >= 0) \ + { \ + x++; \ + xk -= xm; \ + e += xk; \ + } \ + y--; \ + yk -= ym; \ + slw = (x << 1) + dx; \ + if ((e == xk) && (slw > 1)) \ + slw-- + +#define MIFILLCIRCSTEP(slw) MIFILLARCSTEP(slw) +#define MIFILLELLSTEP(slw) MIFILLARCSTEP(slw) + +#define miFillArcLower(slw) (((y + dy) != 0) && ((slw > 1) || (e != xk))) + +typedef struct _miSliceEdge { + int x; + int stepx; + int deltax; + int e; + int dy; + int dx; +} miSliceEdgeRec, *miSliceEdgePtr; + +typedef struct _miArcSlice { + miSliceEdgeRec edge1, edge2; + int min_top_y, max_top_y; + int min_bot_y, max_bot_y; + gboolean edge1_top, edge2_top; + gboolean flip_top, flip_bot; +} miArcSliceRec; + +#define MIARCSLICESTEP(edge) \ + edge.x -= edge.stepx; \ + edge.e -= edge.dx; \ + if (edge.e <= 0) \ + { \ + edge.x -= edge.deltax; \ + edge.e += edge.dy; \ + } + +#define miFillSliceUpper(slice) \ + ((y >= slice.min_top_y) && (y <= slice.max_top_y)) + +#define miFillSliceLower(slice) \ + ((y >= slice.min_bot_y) && (y <= slice.max_bot_y)) + +#define MIARCSLICEUPPER(xl,xr,slice,slw) \ + xl = xorg - x; \ + xr = xl + slw - 1; \ + if (slice.edge1_top && (slice.edge1.x < xr)) \ + xr = slice.edge1.x; \ + if (slice.edge2_top && (slice.edge2.x > xl)) \ + xl = slice.edge2.x; + +#define MIARCSLICELOWER(xl,xr,slice,slw) \ + xl = xorg - x; \ + xr = xl + slw - 1; \ + if (!slice.edge1_top && (slice.edge1.x > xl)) \ + xl = slice.edge1.x; \ + if (!slice.edge2_top && (slice.edge2.x < xr)) \ + xr = slice.edge2.x; + +#define MIWIDEARCSETUP(x,y,dy,slw,e,xk,xm,yk,ym) \ + x = 0; \ + y = slw >> 1; \ + yk = y << 3; \ + xm = 8; \ + ym = 8; \ + if (dy) \ + { \ + xk = 0; \ + if (slw & 1) \ + e = -1; \ + else \ + e = -(y << 2) - 2; \ + } \ + else \ + { \ + y++; \ + yk += 4; \ + xk = -4; \ + if (slw & 1) \ + e = -(y << 2) - 3; \ + else \ + e = - (y << 3); \ + } + +#define MIFILLINARCSTEP(slw) \ + ine += inyk; \ + while (ine >= 0) \ + { \ + inx++; \ + inxk -= inxm; \ + ine += inxk; \ + } \ + iny--; \ + inyk -= inym; \ + slw = (inx << 1) + dx; \ + if ((ine == inxk) && (slw > 1)) \ + slw-- + +#define miFillInArcLower(slw) (((iny + dy) != 0) && \ + ((slw > 1) || (ine != inxk))) + +extern int miFreeArcCache( +#if NeedFunctionPrototypes + gpointer /*data*/, + guint /*id*/ +#endif +); + +extern struct finalSpan *realAllocSpan( +#if NeedFunctionPrototypes + void +#endif +); + +extern void miFillArcSetup( +#if NeedFunctionPrototypes + miArc * /*arc*/, + miFillArcRec * /*info*/ +#endif +); + +extern void miFillArcDSetup( +#if NeedFunctionPrototypes + miArc * /*arc*/, + miFillArcDRec * /*info*/ +#endif +); + +extern void miEllipseAngleToSlope( +#if NeedFunctionPrototypes + int /*angle*/, + int /*width*/, + int /*height*/, + int * /*dxp*/, + int * /*dyp*/, + double * /*d_dxp*/, + double * /*d_dyp*/ +#endif +); + +extern void miFillArcSliceSetup( +#if NeedFunctionPrototypes + miArc * /*arc*/, + miArcSliceRec * /*slice*/, + GdkGC* /*pGC*/ +#endif +); + +#endif diff --git a/gdk/linux-fb/mifpoly.h b/gdk/linux-fb/mifpoly.h new file mode 100644 index 0000000000..fd56f57ea1 --- /dev/null +++ b/gdk/linux-fb/mifpoly.h @@ -0,0 +1,112 @@ +/* $TOG: mifpoly.h /main/10 1998/02/09 14:47:09 kaleb $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#ifndef MIFPOLY_H +#define MIFPOLY_H 1 + +#define EPSILON 0.000001 +#define ISEQUAL(a,b) (fabs((a) - (b)) <= EPSILON) +#define UNEQUAL(a,b) (fabs((a) - (b)) > EPSILON) +#define WITHINHALF(a, b) (((a) - (b) > 0.0) ? (a) - (b) < 0.5 : \ + (b) - (a) <= 0.5) +#define ROUNDTOINT(x) ((int) (((x) > 0.0) ? ((x) + 0.5) : ((x) - 0.5))) +#define ISZERO(x) (fabs((x)) <= EPSILON) +#define PTISEQUAL(a,b) (ISEQUAL(a.x,b.x) && ISEQUAL(a.y,b.y)) +#define PTUNEQUAL(a,b) (UNEQUAL(a.x,b.x) || UNEQUAL(a.y,b.y)) +#define PtEqual(a, b) (((a).x == (b).x) && ((a).y == (b).y)) + +#define NotEnd 0 +#define FirstEnd 1 +#define SecondEnd 2 + +#define SQSECANT 108.856472512142 /* 1/sin^2(11/2) - for 11o miter cutoff */ +#define D2SECANT 5.21671526231167 /* 1/2*sin(11/2) - max extension per width */ + +#ifndef ICIEL +#ifdef NOINLINEICEIL +#define ICEIL(x) ((int)ceil(x)) +#else +#ifdef __GNUC__ +#define ICEIL ICIEL +static __inline int ICEIL(x) + double x; +{ + int _cTmp = x; + return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp+1; +} +#else +#define ICEIL(x) ((((x) == (_cTmp = (x))) || ((x) < 0.0)) ? _cTmp : _cTmp+1) +#define ICEILTEMPDECL static int _cTmp; +#endif +#endif +#endif + +/* Point with sub-pixel positioning. In this case we use doubles, but + * see mifpolycon.c for other suggestions + */ +typedef struct _SppPoint { + double x, y; +} SppPointRec, *SppPointPtr; + +typedef struct _SppArc { + double x, y, width, height; + double angle1, angle2; +} SppArcRec, *SppArcPtr; + +/* mifpolycon.c */ + +extern void miFillSppPoly( +#if NeedFunctionPrototypes + GdkDrawable* /*dst*/, + GdkGC* /*pgc*/, + int /*count*/, + SppPointPtr /*ptsIn*/, + int /*xTrans*/, + int /*yTrans*/, + double /*xFtrans*/, + double /*yFtrans*/ +#endif +); + +#endif /* MIFPOLY_H */ diff --git a/gdk/linux-fb/mifpolycon.c b/gdk/linux-fb/mifpolycon.c new file mode 100644 index 0000000000..341512f367 --- /dev/null +++ b/gdk/linux-fb/mifpolycon.c @@ -0,0 +1,261 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: mifpolycon.c /main/13 1998/02/09 14:47:05 kaleb $ */ +#include +#include "mi.h" +#include "mifpoly.h" + +static int GetFPolyYBounds(); + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +/* + * Written by Todd Newman; April. 1987. + * + * Fill a convex polygon. If the given polygon + * is not convex, then the result is undefined. + * The algorithm is to order the edges from smallest + * y to largest by partitioning the array into a left + * edge list and a right edge list. The algorithm used + * to traverse each edge is digital differencing analyzer + * line algorithm with y as the major axis. There's some funny linear + * interpolation involved because of the subpixel postioning. + */ +void +miFillSppPoly(GdkDrawable *dst, GdkGC *pgc, int count, SppPointPtr ptsIn, int xTrans, int yTrans, double xFtrans, double yFtrans) +#if 0 + GdkDrawable* dst; + GdkGC* pgc; + int count; /* number of points */ + SppPointPtr ptsIn; /* the points */ + int xTrans, yTrans; /* Translate each point by this */ + double xFtrans, yFtrans; /* translate before conversion + by this amount. This provides + a mechanism to match rounding + errors with any shape that must + meet the polygon exactly. + */ +#endif +{ + double xl, xr, /* x vals of left and right edges */ + ml, /* left edge slope */ + mr, /* right edge slope */ + dy, /* delta y */ + i; /* loop counter */ + int y, /* current scanline */ + j, + imin, /* index of vertex with smallest y */ + ymin, /* y-extents of polygon */ + ymax, + *Marked; /* set if this vertex has been used */ + register int left, right, /* indices to first endpoints */ + nextleft, + nextright; /* indices to second endpoints */ + GdkRectangle* ptsOut, + *FirstPoint; /* output buffer */ + + imin = GetFPolyYBounds(ptsIn, count, yFtrans, &ymin, &ymax); + + y = ymax - ymin + 1; + if ((count < 3) || (y <= 0)) + return; + ptsOut = FirstPoint = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * y); + Marked = (int *) ALLOCATE_LOCAL(sizeof(int) * count); + + if(!ptsOut || !Marked) + { + if (Marked) DEALLOCATE_LOCAL(Marked); + if (ptsOut) DEALLOCATE_LOCAL(ptsOut); + return; + } + + for(j = 0; j < count; j++) + Marked[j] = 0; + nextleft = nextright = imin; + Marked[imin] = -1; + y = ICEIL(ptsIn[nextleft].y + yFtrans); + + /* + * loop through all edges of the polygon + */ + do + { + /* add a left edge if we need to */ + if ((y > (ptsIn[nextleft].y + yFtrans) || + ISEQUAL(y, ptsIn[nextleft].y + yFtrans)) && + Marked[nextleft] != 1) + { + Marked[nextleft]++; + left = nextleft++; + + /* find the next edge, considering the end conditions */ + if (nextleft >= count) + nextleft = 0; + + /* now compute the starting point and slope */ + dy = ptsIn[nextleft].y - ptsIn[left].y; + if (dy != 0.0) + { + ml = (ptsIn[nextleft].x - ptsIn[left].x) / dy; + dy = y - (ptsIn[left].y + yFtrans); + xl = (ptsIn[left].x + xFtrans) + ml * MAX(dy, 0); + } + } + + /* add a right edge if we need to */ + if ((y > ptsIn[nextright].y + yFtrans) || + (ISEQUAL(y, ptsIn[nextright].y + yFtrans) + && Marked[nextright] != 1)) + { + Marked[nextright]++; + right = nextright--; + + /* find the next edge, considering the end conditions */ + if (nextright < 0) + nextright = count - 1; + + /* now compute the starting point and slope */ + dy = ptsIn[nextright].y - ptsIn[right].y; + if (dy != 0.0) + { + mr = (ptsIn[nextright].x - ptsIn[right].x) / dy; + dy = y - (ptsIn[right].y + yFtrans); + xr = (ptsIn[right].x + xFtrans) + mr * MAX(dy, 0); + } + } + + + /* + * generate scans to fill while we still have + * a right edge as well as a left edge. + */ + i = (MIN(ptsIn[nextleft].y, ptsIn[nextright].y) + yFtrans) - y; + + if (i < EPSILON) + { + if(Marked[nextleft] && Marked[nextright]) + { + /* Arrgh, we're trapped! (no more points) + * Out, we've got to get out of here before this decadence saps + * our will completely! */ + break; + } + continue; + } + else + { + j = (int) i; + if(!j) + j++; + } + while (j > 0) + { + int cxl, cxr; + + ptsOut->y = (y) + yTrans; + + cxl = ICEIL(xl); + cxr = ICEIL(xr); + ptsOut->height = 1; + /* reverse the edges if necessary */ + if (xl < xr) + { + ptsOut->width = cxr - cxl; + (ptsOut++)->x = cxl + xTrans; + } + else + { + ptsOut->width = cxl - cxr; + (ptsOut++)->x = cxr + xTrans; + } + y++; + + /* increment down the edges */ + xl += ml; + xr += mr; + j--; + } + } while (y <= ymax); + + /* Finally, fill the spans we've collected */ + gdk_fb_fill_spans(dst, pgc, FirstPoint, ptsOut-FirstPoint); + DEALLOCATE_LOCAL(Marked); + DEALLOCATE_LOCAL(FirstPoint); +} + + +/* Find the index of the point with the smallest y.also return the + * smallest and largest y */ +static +int +GetFPolyYBounds(pts, n, yFtrans, by, ty) + register SppPointPtr pts; + int n; + double yFtrans; + int *by, *ty; +{ + register SppPointPtr ptMin; + double ymin, ymax; + SppPointPtr ptsStart = pts; + + ptMin = pts; + ymin = ymax = (pts++)->y; + + while (--n > 0) { + if (pts->y < ymin) + { + ptMin = pts; + ymin = pts->y; + } + if(pts->y > ymax) + ymax = pts->y; + + pts++; + } + + *by = ICEIL(ymin + yFtrans); + *ty = ICEIL(ymax + yFtrans - 1); + return(ptMin-ptsStart); +} diff --git a/gdk/linux-fb/miline.h b/gdk/linux-fb/miline.h new file mode 100644 index 0000000000..a4ccf2c325 --- /dev/null +++ b/gdk/linux-fb/miline.h @@ -0,0 +1,177 @@ +/* $TOG: miline.h /main/7 1998/02/09 14:47:30 kaleb $ */ + +/* + +Copyright 1994, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/programs/Xserver/mi/miline.h,v 1.4 1999/10/13 22:33:11 dawes Exp $ */ + +#ifndef MILINE_H + +/* + * Public definitions used for configuring basic pixelization aspects + * of the sample implementation line-drawing routines provided in + * {mfb,mi,cfb*} at run-time. + */ + +#define XDECREASING 4 +#define YDECREASING 2 +#define YMAJOR 1 + +#define OCTANT1 (1 << (YDECREASING)) +#define OCTANT2 (1 << (YDECREASING|YMAJOR)) +#define OCTANT3 (1 << (XDECREASING|YDECREASING|YMAJOR)) +#define OCTANT4 (1 << (XDECREASING|YDECREASING)) +#define OCTANT5 (1 << (XDECREASING)) +#define OCTANT6 (1 << (XDECREASING|YMAJOR)) +#define OCTANT7 (1 << (YMAJOR)) +#define OCTANT8 (1 << (0)) + +#define XMAJOROCTANTS (OCTANT1 | OCTANT4 | OCTANT5 | OCTANT8) + +#define DEFAULTZEROLINEBIAS (OCTANT2 | OCTANT3 | OCTANT4 | OCTANT5) + +/* + * Devices can configure the rendering of routines in mi, mfb, and cfb* + * by specifying a thin line bias to be applied to a particular screen + * using the following function. The bias parameter is an OR'ing of + * the appropriate OCTANT constants defined above to indicate which + * octants to bias a line to prefer an axial step when the Bresenham + * error term is exactly zero. The octants are mapped as follows: + * + * \ | / + * \ 3 | 2 / + * \ | / + * 4 \ | / 1 + * \|/ + * ----------- + * /|\ + * 5 / | \ 8 + * / | \ + * / 6 | 7 \ + * / | \ + * + * For more information, see "Ambiguities in Incremental Line Rastering," + * Jack E. Bresenham, IEEE CG&A, May 1987. + */ + +#if 0 +extern void miSetZeroLineBias( +#if NeedFunctionPrototypes + ScreenPtr /* pScreen */, + unsigned int /* bias */ +#endif +); +#endif + +/* + * Private definitions needed for drawing thin (zero width) lines + * Used by the mi, mfb, and all cfb* components. + */ + +#define X_AXIS 0 +#define Y_AXIS 1 + +#define OUT_LEFT 0x08 +#define OUT_RIGHT 0x04 +#define OUT_ABOVE 0x02 +#define OUT_BELOW 0x01 + +#define OUTCODES(_result, _x, _y, _pbox) \ + if ( (_x) < (_pbox)->x1) (_result) |= OUT_LEFT; \ + else if ( (_x) >= (_pbox)->x2) (_result) |= OUT_RIGHT; \ + if ( (_y) < (_pbox)->y1) (_result) |= OUT_ABOVE; \ + else if ( (_y) >= (_pbox)->y2) (_result) |= OUT_BELOW; + +#define MIOUTCODES(outcode, x, y, xmin, ymin, xmax, ymax) \ +{\ + if (x < xmin) outcode |= OUT_LEFT;\ + if (x > xmax) outcode |= OUT_RIGHT;\ + if (y < ymin) outcode |= OUT_ABOVE;\ + if (y > ymax) outcode |= OUT_BELOW;\ +} + +#define SWAPINT(i, j) \ +{ register int _t = i; i = j; j = _t; } + +#define SWAPPT(i, j) \ +{ GdkPoint _t; _t = i; i = j; j = _t; } + +#define SWAPINT_PAIR(x1, y1, x2, y2)\ +{ int t = x1; x1 = x2; x2 = t;\ + t = y1; y1 = y2; y2 = t;\ +} + +#if 0 +#define miGetZeroLineBias(_pScreen) \ + ((miZeroLineScreenIndex < 0) ? \ + 0 : ((_pScreen)->devPrivates[miZeroLineScreenIndex].uval)) +#endif +#define miGetZeroLineBias() DEFAULTZEROLINEBIAS + +#define CalcLineDeltas(_x1,_y1,_x2,_y2,_adx,_ady,_sx,_sy,_SX,_SY,_octant) \ + (_octant) = 0; \ + (_sx) = (_SX); \ + if (((_adx) = (_x2) - (_x1)) < 0) { \ + (_adx) = -(_adx); \ + (_sx = -(_sx)); \ + (_octant) |= XDECREASING; \ + } \ + (_sy) = (_SY); \ + if (((_ady) = (_y2) - (_y1)) < 0) { \ + (_ady) = -(_ady); \ + (_sy = -(_sy)); \ + (_octant) |= YDECREASING; \ + } + +#define SetYMajorOctant(_octant) ((_octant) |= YMAJOR) + +#define FIXUP_ERROR(_e, _octant, _bias) \ + (_e) -= (((_bias) >> (_octant)) & 1) + +#define IsXMajorOctant(_octant) (!((_octant) & YMAJOR)) +#define IsYMajorOctant(_octant) ((_octant) & YMAJOR) +#define IsXDecreasingOctant(_octant) ((_octant) & XDECREASING) +#define IsYDecreasingOctant(_octant) ((_octant) & YDECREASING) + +extern int miZeroLineScreenIndex; + +extern int miZeroClipLine( +#if NeedFunctionPrototypes + int /*xmin*/, + int /*ymin*/, + int /*xmax*/, + int /*ymax*/, + int * /*new_x1*/, + int * /*new_y1*/, + int * /*new_x2*/, + int * /*new_y2*/, + unsigned int /*adx*/, + unsigned int /*ady*/, + int * /*pt1_clipped*/, + int * /*pt2_clipped*/, + int /*octant*/, + unsigned int /*bias*/, + int /*oc1*/, + int /*oc2*/ +#endif +); + +#endif /* MILINE_H */ diff --git a/gdk/linux-fb/mipoly.c b/gdk/linux-fb/mipoly.c new file mode 100644 index 0000000000..de1d740a20 --- /dev/null +++ b/gdk/linux-fb/mipoly.c @@ -0,0 +1,77 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: mipoly.c /main/5 1998/02/09 14:48:16 kaleb $ */ +/* + * mipoly.c + * + * Written by Brian Kelleher; June 1986 + * + * Draw polygons. This routine translates the point by the + * origin if pGC->miTranslate is non-zero, and calls + * to the appropriate routine to actually scan convert the + * polygon. + */ +#include "mi.h" + +extern gboolean miFillGeneralPoly( +#if NeedFunctionPrototypes + GdkDrawable* /*dst*/, + GdkGC* /*pgc*/, + int /*count*/, + GdkPoint* /*ptsIn*/ +#endif +); + +void +miFillPolygon(dst, pgc, shape, mode, count, pPts) + GdkDrawable* dst; + register GdkGC* pgc; + int shape, mode; + register int count; + GdkPoint* pPts; +{ + if (count == 0) + return; + + miFillGeneralPoly(dst, pgc, count, pPts); +} diff --git a/gdk/linux-fb/mipoly.h b/gdk/linux-fb/mipoly.h new file mode 100644 index 0000000000..d8dfe19664 --- /dev/null +++ b/gdk/linux-fb/mipoly.h @@ -0,0 +1,230 @@ +/* $TOG: mipoly.h /main/6 1998/02/09 14:48:20 kaleb $ */ +/* + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +#ifndef MIPOLY_H +#define MIPOLY_H + +#include "miscanfill.h" + +/* + * fill.h + * + * Created by Brian Kelleher; Oct 1985 + * + * Include file for filled polygon routines. + * + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counter-clockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* + * for the winding number rule + */ +#define CLOCKWISE 1 +#define COUNTERCLOCKWISE -1 + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + +/* mipolyutil.c */ + +extern gboolean miInsertEdgeInET( +#if NeedFunctionPrototypes + EdgeTable * /*ET*/, + EdgeTableEntry * /*ETE*/, + int /*scanline*/, + ScanLineListBlock ** /*SLLBlock*/, + int * /*iSLLBlock*/ +#endif +); + +extern gboolean miCreateETandAET( +#if NeedFunctionPrototypes + int /*count*/, + GdkPoint* /*pts*/, + EdgeTable * /*ET*/, + EdgeTableEntry * /*AET*/, + EdgeTableEntry * /*pETEs*/, + ScanLineListBlock * /*pSLLBlock*/ +#endif +); + +extern void miloadAET( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/, + EdgeTableEntry * /*ETEs*/ +#endif +); + +extern void micomputeWAET( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/ +#endif +); + +extern int miInsertionSort( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/ +#endif +); + +extern void miFreeStorage( +#if NeedFunctionPrototypes + ScanLineListBlock * /*pSLLBlock*/ +#endif +); + +#endif diff --git a/gdk/linux-fb/mipolygen.c b/gdk/linux-fb/mipolygen.c new file mode 100644 index 0000000000..5068536c71 --- /dev/null +++ b/gdk/linux-fb/mipolygen.c @@ -0,0 +1,214 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: mipolygen.c /main/9 1998/02/09 14:47:51 kaleb $ */ +#include "mi.h" +#include "mipoly.h" + +/* + * + * Written by Brian Kelleher; Oct. 1985 + * + * Routine to fill a polygon. Two fill rules are + * supported: frWINDING and frEVENODD. + * + * See fillpoly.h for a complete description of the algorithm. + */ + +gboolean +miFillGeneralPoly(dst, pgc, count, ptsIn) + GdkDrawable* dst; + GdkGC* pgc; + int count; /* number of points */ + GdkPoint* ptsIn; /* the points */ +{ + register EdgeTableEntry *pAET; /* the Active Edge Table */ + register int y; /* the current scanline */ + register int nPts = 0; /* number of pts in buffer */ + register EdgeTableEntry *pWETE; /* Winding Edge Table */ + register ScanLineList *pSLL; /* Current ScanLineList */ + register GdkRectangle* ptsOut; /* ptr to output buffers */ + GdkRectangle FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */ + EdgeTableEntry *pPrevAET; /* previous AET entry */ + EdgeTable ET; /* Edge Table header node */ + EdgeTableEntry AET; /* Active ET header node */ + EdgeTableEntry *pETEs; /* Edge Table Entries buff */ + ScanLineListBlock SLLBlock; /* header for ScanLineList */ + int fixWAET = 0; + + if (count < 3) + return(TRUE); + + if(!(pETEs = (EdgeTableEntry *) + ALLOCATE_LOCAL(sizeof(EdgeTableEntry) * count))) + return(FALSE); + ptsOut = FirstPoint; + if (!miCreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock)) + { + DEALLOCATE_LOCAL(pETEs); + return(FALSE); + } + pSLL = ET.scanlines.next; + + if (0 /* pgc->fillRule == EvenOddRule */) + { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) + { + ptsOut->x = pAET->bres.minor; + ptsOut->width = pAET->next->bres.minor - pAET->bres.minor; + ptsOut->height = 1; + ptsOut++->y = y; + nPts++; + + /* + * send out the buffer when its full + */ + if (nPts == NUMPTSTOBUFFER) + { + gdk_fb_fill_spans(dst, pgc, FirstPoint, nPts); + ptsOut = FirstPoint; + nPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + EVALUATEEDGEEVENODD(pAET, pPrevAET, y); + } + miInsertionSort(&AET); + } + } + else /* default to WindingNumber */ + { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + micomputeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) + { + /* + * if the next edge in the active edge table is + * also the next edge in the winding active edge + * table. + */ + if (pWETE == pAET) + { + ptsOut->x = pAET->bres.minor; + ptsOut->width = pAET->nextWETE->bres.minor - pAET->bres.minor; + ptsOut->height = 1; + ptsOut++->y = y; + nPts++; + + /* + * send out the buffer + */ + if (nPts == NUMPTSTOBUFFER) + { + gdk_fb_fill_spans(dst, pgc, FirstPoint, nPts); + ptsOut = FirstPoint; + nPts = 0; + } + + pWETE = pWETE->nextWETE; + while (pWETE != pAET) + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + } + + /* + * reevaluate the Winding active edge table if we + * just had to resort it or if we just exited an edge. + */ + if (miInsertionSort(&AET) || fixWAET) + { + micomputeWAET(&AET); + fixWAET = 0; + } + } + } + + /* + * Get any spans that we missed by buffering + */ + if(nPts > 0) + gdk_fb_fill_spans(dst, pgc, FirstPoint, nPts); + DEALLOCATE_LOCAL(pETEs); + miFreeStorage(SLLBlock.next); + return(TRUE); +} diff --git a/gdk/linux-fb/mipolyutil.c b/gdk/linux-fb/mipolyutil.c new file mode 100644 index 0000000000..3cf3f9f9e8 --- /dev/null +++ b/gdk/linux-fb/mipolyutil.c @@ -0,0 +1,392 @@ +/* $XFree86: xc/programs/Xserver/mi/mipolyutil.c,v 1.7 1998/10/04 09:39:31 dawes Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: mipolyutil.c /main/6 1998/02/09 14:48:12 kaleb $ */ +#include +#include "mi.h" +#include "miscanfill.h" +#include "mipoly.h" + +/* + * fillUtils.c + * + * Written by Brian Kelleher; Oct. 1985 + * + * This module contains all of the utility functions + * needed to scan convert a polygon. + * + */ + +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +gboolean +miInsertEdgeInET(ET, ETE, scanline, SLLBlock, iSLLBlock) + EdgeTable *ET; + EdgeTableEntry *ETE; + int scanline; + ScanLineListBlock **SLLBlock; + int *iSLLBlock; +{ + register EdgeTableEntry *start, *prev; + register ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)g_malloc(sizeof(ScanLineListBlock)); + if (!tmpSLLBlock) + return FALSE; + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = (ScanLineListBlock *)NULL; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = (EdgeTableEntry *)NULL; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = (EdgeTableEntry *)NULL; + start = pSLL->edgelist; + while (start && (start->bres.minor < ETE->bres.minor)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; + return TRUE; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +gboolean +miCreateETandAET(count, pts, ET, AET, pETEs, pSLLBlock) + register int count; + register GdkPoint* pts; + EdgeTable *ET; + EdgeTableEntry *AET; + register EdgeTableEntry *pETEs; + ScanLineListBlock *pSLLBlock; +{ + register GdkPoint* top, *bottom; + register GdkPoint* PrevPt, *CurrPt; + int iSLLBlock = 0; + + int dy; + + if (count < 2) return TRUE; + + /* + * initialize the Active Edge Table + */ + AET->next = (EdgeTableEntry *)NULL; + AET->back = (EdgeTableEntry *)NULL; + AET->nextWETE = (EdgeTableEntry *)NULL; + AET->bres.minor = INT_MIN; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = (ScanLineList *)NULL; + ET->ymax = INT_MIN; + ET->ymin = INT_MAX; + pSLLBlock->next = (ScanLineListBlock *)NULL; + + PrevPt = &pts[count-1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); + + if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock)) + { + miFreeStorage(pSLLBlock->next); + return FALSE; + } + + ET->ymax = MAX(ET->ymax, PrevPt->y); + ET->ymin = MIN(ET->ymin, PrevPt->y); + pETEs++; + } + + PrevPt = CurrPt; + } + return TRUE; +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +void +miloadAET(AET, ETEs) + register EdgeTableEntry *AET, *ETEs; +{ + register EdgeTableEntry *pPrevAET; + register EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor < ETEs->bres.minor)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +void +micomputeWAET(AET) + register EdgeTableEntry *AET; +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = (EdgeTableEntry *)NULL; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + ( inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = (EdgeTableEntry *)NULL; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +int +miInsertionSort(AET) + register EdgeTableEntry *AET; +{ + register EdgeTableEntry *pETEchase; + register EdgeTableEntry *pETEinsert; + register EdgeTableEntry *pETEchaseBackTMP; + register int changed = 0; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor > AET->bres.minor) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return(changed); +} + +/* + * Clean up our act. + */ +void +miFreeStorage(pSLLBlock) + register ScanLineListBlock *pSLLBlock; +{ + register ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + g_free(pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} diff --git a/gdk/linux-fb/miscanfill.h b/gdk/linux-fb/miscanfill.h new file mode 100644 index 0000000000..c9e1f50276 --- /dev/null +++ b/gdk/linux-fb/miscanfill.h @@ -0,0 +1,139 @@ +/* $TOG: miscanfill.h /main/6 1998/02/09 14:48:35 kaleb $ */ +/* + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +#ifndef SCANFILLINCLUDED +#define SCANFILLINCLUDED +/* + * scanfill.h + * + * Written by Brian Kelleher; Jan 1985 + * + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2) + + +#endif diff --git a/gdk/linux-fb/mispans.c b/gdk/linux-fb/mispans.c new file mode 100644 index 0000000000..a8638dd858 --- /dev/null +++ b/gdk/linux-fb/mispans.c @@ -0,0 +1,507 @@ +/* $XFree86: xc/programs/Xserver/mi/mispans.c,v 3.1 1998/10/04 09:39:33 dawes Exp $ */ +/*********************************************************** + +Copyright 1989, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $TOG: mispans.c /main/7 1998/02/09 14:48:44 kaleb $ */ + +#include "mi.h" +#include "mispans.h" +#include /* for memmove */ + +/* + +These routines maintain lists of Spans, in order to implement the +``touch-each-pixel-once'' rules of wide lines and arcs. + +Written by Joel McCormack, Summer 1989. + +*/ + + +void miInitSpanGroup(spanGroup) + SpanGroup *spanGroup; +{ + spanGroup->size = 0; + spanGroup->count = 0; + spanGroup->group = NULL; + spanGroup->ymin = SHRT_MAX; + spanGroup->ymax = SHRT_MIN; +} /* InitSpanGroup */ + +#define YMIN(spans) (spans->points[0].y) +#define YMAX(spans) (spans->points[spans->count-1].y) + +void miSubtractSpans (spanGroup, sub) + SpanGroup *spanGroup; + Spans *sub; +{ + int i, subCount, spansCount; + int ymin, ymax, xmin, xmax; + Spans *spans; + GdkRectangle* subPt, *spansPt; + int extra; + + ymin = YMIN(sub); + ymax = YMAX(sub); + spans = spanGroup->group; + for (i = spanGroup->count; i; i--, spans++) { + if (YMIN(spans) <= ymax && ymin <= YMAX(spans)) { + subCount = sub->count; + subPt = sub->points; + spansCount = spans->count; + spansPt = spans->points; + extra = 0; + for (;;) + { + while (spansCount && spansPt->y < subPt->y) + { + spansPt++; spansCount--; + } + if (!spansCount) + break; + while (subCount && subPt->y < spansPt->y) + { + subPt++; subCount--; + } + if (!subCount) + break; + if (subPt->y == spansPt->y) + { + xmin = subPt->x; + xmax = xmin + subPt->width; + if (xmin >= (spansPt->x + spansPt->width) || spansPt->x >= xmax) + { + ; + } + else if (xmin <= spansPt->x) + { + if (xmax >= (spansPt->x + spansPt->width)) + { + g_memmove (spansPt, spansPt + 1, sizeof *spansPt * (spansCount - 1)); + spansPt--; + spans->count--; + extra++; + } + else + { + spansPt->width -= xmax - spansPt->x; + spansPt->x = xmax; + } + } + else + { + if (xmax >= (spansPt->x + spansPt->width)) + { + spansPt->width = xmin - spansPt->x; + } + else + { + if (!extra) { + GdkRectangle* newPt; + +#define EXTRA 8 + newPt = (GdkRectangle*) g_realloc (spans->points, (spans->count + EXTRA) * sizeof (GdkRectangle)); + if (!newPt) + break; + spansPt = newPt + (spansPt - spans->points); + spans->points = newPt; + extra = EXTRA; + } + g_memmove (spansPt + 1, spansPt, sizeof *spansPt * (spansCount)); + spans->count++; + extra--; + spansPt->width = xmin - spansPt->x; + spansPt->height = 1; + spansPt++; + spansPt->width -= xmax - spansPt->x; + spansPt->height = 1; + spansPt->x = xmax; + } + } + } + spansPt++; spansCount--; + } + } + } +} + +void miAppendSpans(spanGroup, otherGroup, spans) + SpanGroup *spanGroup; + SpanGroup *otherGroup; + Spans *spans; +{ + register int ymin, ymax; + register int spansCount; + + spansCount = spans->count; + if (spansCount > 0) { + if (spanGroup->size == spanGroup->count) { + spanGroup->size = (spanGroup->size + 8) * 2; + spanGroup->group = (Spans *) + g_realloc(spanGroup->group, sizeof(Spans) * spanGroup->size); + } + + spanGroup->group[spanGroup->count] = *spans; + (spanGroup->count)++; + ymin = spans->points[0].y; + if (ymin < spanGroup->ymin) spanGroup->ymin = ymin; + ymax = spans->points[spansCount - 1].y; + if (ymax > spanGroup->ymax) spanGroup->ymax = ymax; + if (otherGroup && + otherGroup->ymin < ymax && + ymin < otherGroup->ymax) + { + miSubtractSpans (otherGroup, spans); + } + } + else + { + g_free (spans->points); + } +} /* AppendSpans */ + +void miFreeSpanGroup(spanGroup) + SpanGroup *spanGroup; +{ + if (spanGroup->group != NULL) g_free(spanGroup->group); +} + +static void QuickSortSpansX(points, numSpans) + register GdkRectangle points[]; + register int numSpans; +{ + register int x; + register int i, j, m; + register GdkRectangle* r; + +/* Always called with numSpans > 1 */ +/* Sorts only by x, as all y should be the same */ + +#define ExchangeSpans(a, b) \ +{ \ + GdkRectangle tpt; \ + \ + tpt = points[a]; points[a] = points[b]; points[b] = tpt; \ +} + + do { + if (numSpans < 9) { + /* Do insertion sort */ + register int xprev; + + xprev = points[0].x; + i = 1; + do { /* while i != numSpans */ + x = points[i].x; + if (xprev > x) { + /* points[i] is out of order. Move into proper location. */ + GdkRectangle tpt; + int k; + + for (j = 0; x >= points[j].x; j++) {} + tpt = points[i]; + for (k = i; k != j; k--) { + points[k] = points[k-1]; + } + points[j] = tpt; + x = points[i].x; + } /* if out of order */ + xprev = x; + i++; + } while (i != numSpans); + return; + } + + /* Choose partition element, stick in location 0 */ + m = numSpans / 2; + if (points[m].x > points[0].x) ExchangeSpans(m, 0); + if (points[m].x > points[numSpans-1].x) ExchangeSpans(m, numSpans-1); + if (points[m].x > points[0].x) ExchangeSpans(m, 0); + x = points[0].x; + + /* Partition array */ + i = 0; + j = numSpans; + do { + r = &(points[i]); + do { + r++; + i++; + } while (i != numSpans && r->x < x); + r = &(points[j]); + do { + r--; + j--; + } while (x < r->x); + if (i < j) ExchangeSpans(i, j); + } while (i < j); + + /* Move partition element back to middle */ + ExchangeSpans(0, j); + + /* Recurse */ + if (numSpans-j-1 > 1) + QuickSortSpansX(&points[j+1], numSpans-j-1); + numSpans = j; + } while (numSpans > 1); +} /* QuickSortSpans */ + + +static int UniquifySpansX(spans, newPoints, newWidths) + Spans *spans; + register GdkRectangle *newPoints; +{ + register int newx1, newx2, oldpt, i, y; + GdkRectangle *oldPoints, *startNewPoints = newPoints; + +/* Always called with numSpans > 1 */ +/* Uniquify the spans, and stash them into newPoints and newWidths. Return the + number of unique spans. */ + + + oldPoints = spans->points; + + y = oldPoints->y; + newx1 = oldPoints->x; + newx2 = newx1 + oldPoints->width; + + for (i = spans->count-1; i != 0; i--) { + oldPoints++; + oldpt = oldPoints->x; + if (oldpt > newx2) { + /* Write current span, start a new one */ + newPoints->x = newx1; + newPoints->y = y; + newPoints->width = newx2 - newx1; + newPoints->height = 1; + newPoints++; + newx1 = oldpt; + newx2 = oldpt + oldPoints->width; + } else { + /* extend current span, if old extends beyond new */ + oldpt = oldpt + oldPoints->width; + if (oldpt > newx2) newx2 = oldpt; + } + } /* for */ + + /* Write final span */ + newPoints->x = newx1; + newPoints->width = newx2 - newx1; + newPoints->height = 1; + newPoints->y = y; + + return (newPoints - startNewPoints) + 1; +} /* UniquifySpansX */ + +void +miDisposeSpanGroup (spanGroup) + SpanGroup *spanGroup; +{ + int i; + Spans *spans; + + for (i = 0; i < spanGroup->count; i++) + { + spans = spanGroup->group + i; + g_free (spans->points); + } +} + +void miFillUniqueSpanGroup(pDraw, pGC, spanGroup) + GdkDrawable* pDraw; + GdkGC* pGC; + SpanGroup *spanGroup; +{ + register int i; + register Spans *spans; + register Spans *yspans; + register int *ysizes; + register int ymin, ylength; + + /* Outgoing spans for one big call to FillSpans */ + register GdkRectangle* points; + register int count; + + if (spanGroup->count == 0) return; + + if (spanGroup->count == 1) { + /* Already should be sorted, unique */ + spans = spanGroup->group; + gdk_fb_fill_spans(pDraw, pGC, spans->points, spans->count); + g_free(spans->points); + } + else + { + /* Yuck. Gross. Radix sort into y buckets, then sort x and uniquify */ + /* This seems to be the fastest thing to do. I've tried sorting on + both x and y at the same time rather than creating into all those + y buckets, but it was somewhat slower. */ + + ymin = spanGroup->ymin; + ylength = spanGroup->ymax - ymin + 1; + + /* Allocate Spans for y buckets */ + yspans = (Spans *) g_malloc(ylength * sizeof(Spans)); + ysizes = (int *) g_malloc(ylength * sizeof (int)); + + if (!yspans || !ysizes) + { + if (yspans) + g_free (yspans); + if (ysizes) + g_free (ysizes); + miDisposeSpanGroup (spanGroup); + return; + } + + for (i = 0; i != ylength; i++) { + ysizes[i] = 0; + yspans[i].count = 0; + yspans[i].points = NULL; + } + + /* Go through every single span and put it into the correct bucket */ + count = 0; + for (i = 0, spans = spanGroup->group; + i != spanGroup->count; + i++, spans++) { + int index; + int j; + + for (j = 0, points = spans->points; + j != spans->count; + j++, points++) { + index = points->y - ymin; + if (index >= 0 && index < ylength) { + Spans *newspans = &(yspans[index]); + if (newspans->count == ysizes[index]) { + GdkRectangle* newpoints; + ysizes[index] = (ysizes[index] + 8) * 2; + newpoints = (GdkRectangle*) g_realloc( + newspans->points, + ysizes[index] * sizeof(GdkRectangle)); + if (!newpoints) + { + int i; + + for (i = 0; i < ylength; i++) + { + g_free (yspans[i].points); + } + g_free (yspans); + g_free (ysizes); + miDisposeSpanGroup (spanGroup); + return; + } + newspans->points = newpoints; + } + newspans->points[newspans->count] = *points; + (newspans->count)++; + } /* if y value of span in range */ + } /* for j through spans */ + count += spans->count; + g_free(spans->points); + spans->points = NULL; + } /* for i thorough Spans */ + + /* Now sort by x and uniquify each bucket into the final array */ + points = (GdkRectangle*) g_malloc(count * sizeof(GdkRectangle)); + if (!points) + { + int i; + + for (i = 0; i < ylength; i++) + { + g_free (yspans[i].points); + } + g_free (yspans); + g_free (ysizes); + if (points) + g_free (points); + return; + } + count = 0; + for (i = 0; i != ylength; i++) { + int ycount = yspans[i].count; + if (ycount > 0) { + if (ycount > 1) { + QuickSortSpansX(yspans[i].points, ycount); + count += UniquifySpansX + (&(yspans[i]), &(points[count])); + } else { + points[count] = yspans[i].points[0]; + count++; + } + g_free(yspans[i].points); + } + } + + gdk_fb_fill_spans(pDraw, pGC, points, count); + g_free(points); + g_free(yspans); + g_free(ysizes); /* use (DE)ALLOCATE_LOCAL for these? */ + } + + spanGroup->count = 0; + spanGroup->ymin = SHRT_MAX; + spanGroup->ymax = SHRT_MIN; +} + + +void miFillSpanGroup(pDraw, pGC, spanGroup) + GdkDrawable* pDraw; + GdkGC* pGC; + SpanGroup *spanGroup; +{ + register int i; + register Spans *spans; + + for (i = 0, spans = spanGroup->group; i != spanGroup->count; i++, spans++) { + gdk_fb_fill_spans(pDraw, pGC, spans->points, spans->count); + g_free(spans->points); + } + + spanGroup->count = 0; + spanGroup->ymin = SHRT_MAX; + spanGroup->ymax = SHRT_MIN; +} /* FillSpanGroup */ diff --git a/gdk/linux-fb/mispans.h b/gdk/linux-fb/mispans.h new file mode 100644 index 0000000000..4724e203ed --- /dev/null +++ b/gdk/linux-fb/mispans.h @@ -0,0 +1,127 @@ +/*********************************************************** + +Copyright 1989, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $TOG: mispans.h /main/5 1998/02/09 14:48:48 kaleb $ */ + +typedef struct { + int count; /* number of spans */ + GdkRectangle* points; /* pointer to list of start points */ +} Spans; + +typedef struct { + int size; /* Total number of *Spans allocated */ + int count; /* Number of *Spans actually in group */ + Spans *group; /* List of Spans */ + int ymin, ymax; /* Min, max y values encountered */ +} SpanGroup; + +/* Initialize SpanGroup. MUST BE DONE before use. */ +extern void miInitSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +/* Add a Spans to a SpanGroup. The spans MUST BE in y-sorted order */ +extern void miAppendSpans( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/, + SpanGroup * /*otherGroup*/, + Spans * /*spans*/ +#endif +); + +/* Paint a span group, possibly with some overlap */ +extern void miFillSpanGroup( +#if NeedFunctionPrototypes + GdkDrawable* /*pDraw*/, + GdkGC* /*pGC*/, + SpanGroup * /*spanGroup*/ +#endif +); + +/* Paint a span group, insuring that each pixel is painted at most once */ +extern void miFillUniqueSpanGroup( +#if NeedFunctionPrototypes + GdkDrawable* /*pDraw*/, + GdkGC* /*pGC*/, + SpanGroup * /*spanGroup*/ +#endif +); + +/* Free up data in a span group. MUST BE DONE or you'll suffer memory leaks */ +extern void miFreeSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +extern void miSubtractSpans( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/, + Spans * /*sub*/ +#endif +); + +extern void miDisposeSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +extern int miClipSpans( +#if NeedFunctionPrototypes + GdkRegion* /*prgnDst*/, + GdkPoint* /*ppt*/, + int * /*pwidth*/, + int /*nspans*/, + GdkPoint* /*pptNew*/, + int * /*pwidthNew*/, + int /*fSorted*/ +#endif +); + +/* Rops which must use span groups */ +#define miSpansCarefulRop(rop) (((rop) & 0xc) == 0x8 || ((rop) & 0x3) == 0x2) +#define miSpansEasyRop(rop) (!miSpansCarefulRop(rop)) + diff --git a/gdk/linux-fb/mistruct.h b/gdk/linux-fb/mistruct.h new file mode 100644 index 0000000000..ff5d738219 --- /dev/null +++ b/gdk/linux-fb/mistruct.h @@ -0,0 +1,58 @@ +/* $TOG: mistruct.h /main/4 1998/02/09 14:49:07 kaleb $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +#ifndef MISTRUCT_H +#define MISTRUCT_H + +#include "mitypes.h" + +/* information about dashes */ +typedef struct _miDash { + GdkPoint pt; + int e1, e2; /* keep these, so we don't have to do it again */ + int e; /* bresenham error term for this point on line */ + int which; + int newLine;/* 0 if part of same original line as previous dash */ +} miDashRec; + +#endif /* MISTRUCT_H */ diff --git a/gdk/linux-fb/mitypes.h b/gdk/linux-fb/mitypes.h new file mode 100644 index 0000000000..997ec8aa07 --- /dev/null +++ b/gdk/linux-fb/mitypes.h @@ -0,0 +1,486 @@ +#ifndef MITYPES_H +#define MITYPES_H + +#include + +#define ALLOCATE_LOCAL(size) alloca((int)(size)) +#define DEALLOCATE_LOCAL(ptr) /* as nothing */ + +#include +#include +#include +#include +#include +#include + +typedef struct _miDash *miDashPtr; + +#define CT_NONE 0 +#define CT_PIXMAP 1 +#define CT_REGION 2 +#define CT_UNSORTED 6 +#define CT_YSORTED 10 +#define CT_YXSORTED 14 +#define CT_YXBANDED 18 + +struct _GdkGCFuncs { + void (* ValidateGC)( + GdkGC* /*pGC*/, + unsigned long /*stateChanges*/, + GdkDrawable* /*pDrawable*/ + ); + + void (* ChangeGC)( + GdkGC* /*pGC*/, + unsigned long /*mask*/ + ); + + void (* CopyGC)( + GdkGC* /*pGCSrc*/, + unsigned long /*mask*/, + GdkGC* /*pGCDst*/ + ); + + void (* DestroyGC)( + GdkGC* /*pGC*/ + ); + + void (* ChangeClip)( + GdkGC* /*pGC*/, + int /*type*/, + gpointer /*pvalue*/, + int /*nrects*/ + ); + void (* DestroyClip)( + GdkGC* /*pGC*/ + ); + + void (* CopyClip)( + GdkGC* /*pgcDst*/, + GdkGC* /*pgcSrc*/ + ); + +}; + +typedef union { + guint32 val; + gpointer ptr; +} ChangeGCVal, *ChangeGCValPtr; + +#define PixmapBytePad(w, d) (w) +#define BitmapBytePad(w) (w) + +#if 0 +typedef struct _PaddingInfo { + int padRoundUp; /* pixels per pad unit - 1 */ + int padPixelsLog2; /* log 2 (pixels per pad unit) */ + int padBytesLog2; /* log 2 (bytes per pad unit) */ + int notPower2; /* bitsPerPixel not a power of 2 */ + int bytesPerPixel; /* only set when notPower2 is TRUE */ +} PaddingInfo; +extern PaddingInfo PixmapWidthPaddingInfo[]; + +#define PixmapWidthInPadUnits(w, d) \ + (PixmapWidthPaddingInfo[d].notPower2 ? \ + (((int)(w) * PixmapWidthPaddingInfo[d].bytesPerPixel + \ + PixmapWidthPaddingInfo[d].bytesPerPixel) >> \ + PixmapWidthPaddingInfo[d].padBytesLog2) : \ + ((int)((w) + PixmapWidthPaddingInfo[d].padRoundUp) >> \ + PixmapWidthPaddingInfo[d].padPixelsLog2)) + +#define PixmapBytePad(w, d) \ + (PixmapWidthInPadUnits(w, d) << PixmapWidthPaddingInfo[d].padBytesLog2) + +#define BitmapBytePad(w) \ + (((int)((w) + BITMAP_SCANLINE_PAD - 1) >> LOG2_BITMAP_PAD) << LOG2_BYTES_PER_SCANLINE_PAD) + +typedef struct _GdkDrawable GdkPixmap, *GdkPixmap*; +typedef struct _GdkDrawable GdkDrawable, *GdkDrawable*; +typedef struct _GdkGCOps GdkGCOps; +typedef struct _Region Region, *GdkRegion*; + +#define EVEN_DASH 0 +#define ODD_DASH ~0 + +typedef struct _GdkPoint { + gint16 x, y; +} GdkPoint, *GdkPoint*; + +typedef struct _GdkGC { + unsigned char depth; + unsigned char alu; + unsigned short line_width; + unsigned short dashOffset; + unsigned short numInDashList; + unsigned char *dash; + unsigned int lineStyle : 2; + unsigned int capStyle : 2; + unsigned int joinStyle : 2; + unsigned int fillStyle : 2; + unsigned int fillRule : 1; + unsigned int arcMode : 1; + unsigned int subWindowMode : 1; + unsigned int graphicsExposures : 1; + unsigned int clientClipType : 2; /* CT_ */ + unsigned int miTranslate:1; /* should mi things translate? */ + unsigned int tileIsPixel:1; /* tile is solid pixel */ + unsigned int fExpose:1; /* Call exposure handling */ + unsigned int freeCompClip:1; /* Free composite clip */ + unsigned int unused:14; /* see comment above */ + unsigned long planemask; + unsigned long fgPixel; + unsigned long bgPixel; + /* + * alas -- both tile and stipple must be here as they + * are independently specifiable + */ + /* PixUnion tile; + GdkPixmap* stipple;*/ + GdkPoint patOrg; /* origin for (tile, stipple) */ + struct _Font *font; + GdkPoint clipOrg; + GdkPoint lastWinOrg; /* position of window last validated */ + gpointer clientClip; + unsigned long stateChanges; /* masked with GC_ */ + unsigned long serialNumber; + /* GCFuncs *funcs; */ + GdkGCOps *ops; +} GdkGC, *GdkGC*; + +struct _GdkDrawable { + unsigned char type; /* DRAWABLE_ */ + unsigned char depth; + unsigned char bitsPerPixel; + short x; /* window: screen absolute, pixmap: 0 */ + short y; /* window: screen absolute, pixmap: 0 */ + unsigned short width; + unsigned short height; + unsigned long serialNumber; + + guchar *buffer; + int rowStride; +}; + +typedef struct _GdkSegment { + gint16 x1, y1, x2, y2; +} GdkSegment; + +typedef struct _GdkRectangle { + gint16 x, y; + guint16 width, height; +} GdkRectangle, *GdkRectangle*; + +typedef struct _Box { + short x1, y1, x2, y2; +} BoxRec, *BoxPtr; + +/* + * graphics operations invoked through a GC + */ + +/* graphics functions, as in GC.alu */ + +#define GDK_CLEAR 0x0 /* 0 */ +#define GDK_AND 0x1 /* src AND dst */ +#define GDK_AND_REVERSE 0x2 /* src AND NOT dst */ +#define GDK_COPY 0x3 /* src */ +#define GDK_AND_INVERT 0x4 /* NOT src AND dst */ +#define GDK_NOOP 0x5 /* dst */ +#define GDK_XOR 0x6 /* src XOR dst */ +#define GDK_OR 0x7 /* src OR dst */ +#define GDK_NOR 0x8 /* NOT src AND NOT dst */ +#define GDK_EQUIV 0x9 /* NOT src XOR dst */ +#define GDK_INVERT 0xa /* NOT dst */ +#define GDK_OR_REVERSE 0xb /* src OR NOT dst */ +#define GDK_COPY_INVERT 0xc /* NOT src */ +#define GDK_OR_INVERT 0xd /* NOT src OR dst */ +#define GDK_NAND 0xe /* NOT src OR NOT dst */ +#define GDK_SET 0xf /* 1 */ + +/* CoordinateMode for drawing routines */ + +#define CoordModeOrigin 0 /* relative to the origin */ +#define CoordModePrevious 1 /* relative to previous point */ + +/* Polygon shapes */ + +#define Complex 0 /* paths may intersect */ +#define Nonconvex 1 /* no paths intersect, but not convex */ +#define Convex 2 /* wholly convex */ + +/* LineStyle */ + +#define GDK_LINE_SOLID 0 +#define GDK_LINE_ON_OFF_DASH 1 +#define GDK_LINE_DOUBLE_DASH 2 + +/* capStyle */ + +#define GDK_CAP_NOT_LAST 0 +#define GDK_CAP_BUTT 1 +#define GDK_CAP_ROUND 2 +#define GDK_CAP_PROJECTING 3 + +/* joinStyle */ + +#define GDK_JOIN_MITER 0 +#define GDK_JOIN_ROUND 1 +#define GDK_JOIN_BEVEL 2 + +/* Arc modes for PolyFillArc */ + +#define ArcChord 0 /* join endpoints of arc */ +#define ArcPieSlice 1 /* join endpoints to center of arc */ + +/* fillRule */ + +#define EvenOddRule 0 +#define WindingRule 1 + +/* fillStyle */ + +#define GDK_SOLID 0 +#define GDK_TILED 1 +#define GDK_STIPPLED 2 +#define GDK_OPAQUE_STIPPLED 3 + +typedef enum { Linear8Bit, TwoD8Bit, Linear16Bit, TwoD16Bit } FontEncoding; + +#define MININT G_MININT +#define MAXINT G_MAXINT +#define MINSHORT G_MINSHORT +#define MAXSHORT G_MAXSHORT + +/* GC components: masks used in CreateGC, CopyGC, ChangeGC, OR'ed into + GC.stateChanges */ + +#define GDK_GC_FUNCTION (1L<<0) +#define GCPlaneMask (1L<<1) +#define GDK_GC_FOREGROUND (1L<<2) +#define GDK_GC_BACKGROUND (1L<<3) +#define GDK_GC_LINE_WIDTH (1L<<4) +#define GCLineStyle (1L<<5) +#define GDK_GC_CAP_STYLE (1L<<6) +#define GDK_GC_JOIN_STYLE (1L<<7) +#define GDK_GC_FILL_STYLE (1L<<8) +#define GCFillRule (1L<<9) +#define GCTile (1L<<10) +#define GDK_GC_STIPPLE (1L<<11) +#define GDK_GC_TS_X_ORIGIN (1L<<12) +#define GDK_GC_TS_Y_ORIGIN (1L<<13) +#define GCFont (1L<<14) +#define GCSubwindowMode (1L<<15) +#define GCGraphicsExposures (1L<<16) +#define GCClipXOrigin (1L<<17) +#define GCClipYOrigin (1L<<18) +#define GCClipMask (1L<<19) +#define GCDashOffset (1L<<20) +#define GCDashList (1L<<21) +#define GCArcMode (1L<<22) + +/* types for Drawable */ + +#define GDK_WINDOW_CHILD 0 +#define GDK_DRAWABLE_PIXMAP 1 +#define GDK_WINDOW_FOREIGN 2 +#define GDK_WINDOW_TEMP 3 + +#endif + +typedef GdkSegment BoxRec, *BoxPtr; + +typedef struct _miArc { + gint16 x, y; + guint16 width, height; + gint16 angle1, angle2; +} miArc; + +struct _GdkGCOps { + void (* FillSpans)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*nInit*/, + GdkPoint* /*pptInit*/, + int * /*pwidthInit*/, + int /*fSorted*/ +); + + void (* SetSpans)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + char * /*psrc*/, + GdkPoint* /*ppt*/, + int * /*pwidth*/, + int /*nspans*/, + int /*fSorted*/ +); + + void (* PutImage)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*depth*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*leftPad*/, + int /*format*/, + char * /*pBits*/ +); + + GdkRegion* (* CopyArea)( + GdkDrawable* /*pSrc*/, + GdkDrawable* /*pDst*/, + GdkGC* /*pGC*/, + int /*srcx*/, + int /*srcy*/, + int /*w*/, + int /*h*/, + int /*dstx*/, + int /*dsty*/ +); + + GdkRegion* (* CopyPlane)( + GdkDrawable* /*pSrcDrawable*/, + GdkDrawable* /*pDstDrawable*/, + GdkGC* /*pGC*/, + int /*srcx*/, + int /*srcy*/, + int /*width*/, + int /*height*/, + int /*dstx*/, + int /*dsty*/, + unsigned long /*bitPlane*/ +); + void (* PolyPoint)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*mode*/, + int /*npt*/, + GdkPoint* /*pptInit*/ +); + + void (* Polylines)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*mode*/, + int /*npt*/, + GdkPoint* /*pptInit*/ +); + + void (* PolySegment)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*nseg*/, + GdkSegment * /*pSegs*/ +); + + void (* PolyRectangle)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*nrects*/, + GdkRectangle * /*pRects*/ +); + + void (* PolyArc)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*narcs*/, + miArc * /*parcs*/ +); + + void (* FillPolygon)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*shape*/, + int /*mode*/, + int /*count*/, + GdkPoint* /*pPts*/ +); + + void (* PolyFillRect)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*nrectFill*/, + GdkRectangle * /*prectInit*/ +); + + void (* PolyFillArc)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*narcs*/, + miArc * /*parcs*/ +); + +#if 0 + int (* PolyText8)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/ +); + + int (* PolyText16)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + unsigned short * /*chars*/ +); + + void (* ImageText8)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/ +); + + void (* ImageText16)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + unsigned short * /*chars*/ +); + + void (* ImageGlyphBlt)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + unsigned int /*nglyph*/, + CharInfoPtr * /*ppci*/, + pointer /*pglyphBase*/ +); + + void (* PolyGlyphBlt)( + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + int /*x*/, + int /*y*/, + unsigned int /*nglyph*/, + CharInfoPtr * /*ppci*/, + pointer /*pglyphBase*/ +); +#endif + + void (* PushPixels)( + GdkGC* /*pGC*/, + GdkPixmap* /*pBitMap*/, + GdkDrawable* /*pDst*/, + int /*w*/, + int /*h*/, + int /*x*/, + int /*y*/ +); +}; + +#define SCRRIGHT(x, n) ((x)>>(n)) + +#endif /* MITYPES_H */ diff --git a/gdk/linux-fb/miwideline.c b/gdk/linux-fb/miwideline.c new file mode 100644 index 0000000000..d1e46b9468 --- /dev/null +++ b/gdk/linux-fb/miwideline.c @@ -0,0 +1,2108 @@ +/* $TOG: miwideline.c /main/60 1998/03/07 17:40:23 kaleb $ */ +/* + +Copyright 1988, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ +/* $XFree86: xc/programs/Xserver/mi/miwideline.c,v 1.7 1999/10/13 22:33:13 dawes Exp $ */ + +/* Author: Keith Packard, MIT X Consortium */ + +/* + * Mostly integer wideline code. Uses a technique similar to + * bresenham zero-width lines, except walks an X edge + */ + +#include +#ifdef _XOPEN_SOURCE +#include +#else +#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */ +#include +#undef _XOPEN_SOURCE +#endif + +#include "mi.h" +#include "miwideline.h" + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +static void miLineArc(); + +/* + * spans-based polygon filler + */ + +void +miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, overall_height, + left, right, left_count, right_count) + GdkDrawable* pDrawable; + GdkGC* pGC; + GdkColor *pixel; + SpanDataPtr spanData; + int y; /* start y coordinate */ + int overall_height; /* height of entire segment */ + PolyEdgePtr left, right; + int left_count, right_count; +{ + register int left_x = 0, left_e = 0; + int left_stepx = 0; + int left_signdx = 0; + int left_dy = 0, left_dx = 0; + + register int right_x = 0, right_e = 0; + int right_stepx = 0; + int right_signdx = 0; + int right_dy = 0, right_dx = 0; + + int height = 0; + int left_height = 0, right_height = 0; + + register GdkRectangle* ppt; + GdkRectangle* pptInit; + GdkColor oldPixel; + int xorg; + Spans spanRec; + + left_height = 0; + right_height = 0; + + if (!spanData) + { + pptInit = (GdkRectangle*) ALLOCATE_LOCAL (overall_height * sizeof(*ppt)); + if (!pptInit) + return; + ppt = pptInit; + oldPixel = GDK_GC_FBDATA(pGC)->values.foreground; + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, pixel); + } + } + else + { + spanRec.points = (GdkRectangle*) g_malloc (overall_height * sizeof (*ppt)); + if (!spanRec.points) + return; + ppt = spanRec.points; + } + + xorg = 0; + while ((left_count || left_height) && + (right_count || right_height)) + { + MIPOLYRELOADLEFT + MIPOLYRELOADRIGHT + + height = left_height; + if (height > right_height) + height = right_height; + + left_height -= height; + right_height -= height; + + while (--height >= 0) + { + if (right_x >= left_x) + { + ppt->y = y; + ppt->x = left_x + xorg; + ppt->width = right_x - left_x + 1; + ppt->height = 1; + ppt++; + } + y++; + + MIPOLYSTEPLEFT + + MIPOLYSTEPRIGHT + } + } + if (!spanData) + { + gdk_fb_fill_spans(pDrawable, pGC, pptInit, ppt - pptInit); + DEALLOCATE_LOCAL (pptInit); + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, &oldPixel); + } + } + else + { + spanRec.count = ppt - spanRec.points; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +static void +miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, x, y, w, h) + GdkDrawable* pDrawable; + GdkGC* pGC; + GdkColor* pixel; + SpanDataPtr spanData; + int x, y, w, h; +{ + register GdkRectangle* ppt; + GdkColor oldPixel; + Spans spanRec; + GdkRectangle rect; + + if (!spanData) + { + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + oldPixel = GDK_GC_FBDATA(pGC)->values.foreground; + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, pixel); + } + gdk_fb_fill_spans(pDrawable, pGC, &rect, 1); + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, &oldPixel); + } + } + else + { + spanRec.points = (GdkRectangle*) g_malloc (h * sizeof (*ppt)); + if (!spanRec.points) + return; + ppt = spanRec.points; + + while (h--) + { + ppt->x = x; + ppt->y = y; + ppt->width = w; + ppt->height = 1; + ppt++; + y++; + } + spanRec.count = ppt - spanRec.points; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +/* static */ int +miPolyBuildEdge (x0, y0, k, dx, dy, xi, yi, left, edge) + double x0, y0; + double k; /* x0 * dy - y0 * dx */ + register int dx, dy; + int xi, yi; + int left; + register PolyEdgePtr edge; +{ + int x, y, e; + int xady; + + if (dy < 0) + { + dy = -dy; + dx = -dx; + k = -k; + } + +#ifdef NOTDEF + { + double realk, kerror; + realk = x0 * dy - y0 * dx; + kerror = Fabs (realk - k); + if (kerror > .1) + printf ("realk: %g k: %g\n", realk, k); + } +#endif + y = ICEIL (y0); + xady = ICEIL (k) + y * dx; + + if (xady <= 0) + x = - (-xady / dy) - 1; + else + x = (xady - 1) / dy; + + e = xady - x * dy; + + if (dx >= 0) + { + edge->signdx = 1; + edge->stepx = dx / dy; + edge->dx = dx % dy; + } + else + { + edge->signdx = -1; + edge->stepx = - (-dx / dy); + edge->dx = -dx % dy; + e = dy - e + 1; + } + edge->dy = dy; + edge->x = x + left + xi; + edge->e = e - dy; /* bias to compare against 0 instead of dy */ + return y + yi; +} + +#define StepAround(v, incr, max) (((v) + (incr) < 0) ? (max - 1) : ((v) + (incr) == max) ? 0 : ((v) + (incr))) + +/* static */ int +miPolyBuildPoly (vertices, slopes, count, xi, yi, left, right, pnleft, pnright, h) + register PolyVertexPtr vertices; + register PolySlopePtr slopes; + int count; + int xi, yi; + PolyEdgePtr left, right; + int *pnleft, *pnright; + int *h; +{ + int top, bottom; + double miny, maxy; + register int i; + int j; + int clockwise; + int slopeoff; + register int s; + register int nright, nleft; + int y, lasty = 0, bottomy, topy = 0; + + /* find the top of the polygon */ + maxy = miny = vertices[0].y; + bottom = top = 0; + for (i = 1; i < count; i++) + { + if (vertices[i].y < miny) + { + top = i; + miny = vertices[i].y; + } + if (vertices[i].y >= maxy) + { + bottom = i; + maxy = vertices[i].y; + } + } + clockwise = 1; + slopeoff = 0; + + i = top; + j = StepAround (top, -1, count); + + if (slopes[j].dy * slopes[i].dx > slopes[i].dy * slopes[j].dx) + { + clockwise = -1; + slopeoff = -1; + } + + bottomy = ICEIL (maxy) + yi; + + nright = 0; + + s = StepAround (top, slopeoff, count); + i = top; + while (i != bottom) + { + if (slopes[s].dy != 0) + { + y = miPolyBuildEdge (vertices[i].x, vertices[i].y, + slopes[s].k, + slopes[s].dx, slopes[s].dy, + xi, yi, 0, + &right[nright]); + if (nright != 0) + right[nright-1].height = y - lasty; + else + topy = y; + nright++; + lasty = y; + } + + i = StepAround (i, clockwise, count); + s = StepAround (s, clockwise, count); + } + if (nright != 0) + right[nright-1].height = bottomy - lasty; + + if (slopeoff == 0) + slopeoff = -1; + else + slopeoff = 0; + + nleft = 0; + s = StepAround (top, slopeoff, count); + i = top; + while (i != bottom) + { + if (slopes[s].dy != 0) + { + y = miPolyBuildEdge (vertices[i].x, vertices[i].y, + slopes[s].k, + slopes[s].dx, slopes[s].dy, xi, yi, 1, + &left[nleft]); + + if (nleft != 0) + left[nleft-1].height = y - lasty; + nleft++; + lasty = y; + } + i = StepAround (i, -clockwise, count); + s = StepAround (s, -clockwise, count); + } + if (nleft != 0) + left[nleft-1].height = bottomy - lasty; + *pnleft = nleft; + *pnright = nright; + *h = bottomy - topy; + return topy; +} + +static void +miLineOnePoint (pDrawable, pGC, pixel, spanData, x, y) + GdkDrawable* pDrawable; + GdkGC* pGC; + GdkColor* pixel; + SpanDataPtr spanData; + int x, y; +{ + GdkColor oldPixel; + GdkRectangle rect; + + MILINESETPIXEL (pDrawable, pGC, pixel, oldPixel); + rect.width = 1; + rect.height = 1; + + gdk_fb_fill_spans(pDrawable, pGC, &rect, 1); + MILINERESETPIXEL (pDrawable, pGC, pixel, oldPixel); +} + +static void +miLineJoin (pDrawable, pGC, pixel, spanData, pLeft, pRight) + GdkDrawable* pDrawable; + GdkGC* pGC; + GdkColor *pixel; + SpanDataPtr spanData; + register LineFacePtr pLeft, pRight; +{ + double mx, my; + double denom = 0.0; + PolyVertexRec vertices[4]; + PolySlopeRec slopes[4]; + int edgecount; + PolyEdgeRec left[4], right[4]; + int nleft, nright; + int y, height; + int swapslopes; + int joinStyle = GDK_GC_FBDATA(pGC)->values.join_style; + int lw = GDK_GC_FBDATA(pGC)->values.line_width; + + if (lw == 1 && !spanData) { + /* Lines going in the same direction have no join */ + if (pLeft->dx >= 0 == pRight->dx <= 0) + return; + if (joinStyle != GDK_JOIN_ROUND) { + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + if (denom == 0) + return; /* no join to draw */ + } + if (joinStyle != GDK_JOIN_MITER) { + miLineOnePoint (pDrawable, pGC, pixel, spanData, pLeft->x, pLeft->y); + return; + } + } else { + if (joinStyle == GDK_JOIN_ROUND) + { + miLineArc(pDrawable, pGC, pixel, spanData, + pLeft, pRight, + (double)0.0, (double)0.0, TRUE); + return; + } + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + if (denom == 0.0) + return; /* no join to draw */ + } + + swapslopes = 0; + if (denom > 0) + { + pLeft->xa = -pLeft->xa; + pLeft->ya = -pLeft->ya; + pLeft->dx = -pLeft->dx; + pLeft->dy = -pLeft->dy; + } + else + { + swapslopes = 1; + pRight->xa = -pRight->xa; + pRight->ya = -pRight->ya; + pRight->dx = -pRight->dx; + pRight->dy = -pRight->dy; + } + + vertices[0].x = pRight->xa; + vertices[0].y = pRight->ya; + slopes[0].dx = -pRight->dy; + slopes[0].dy = pRight->dx; + slopes[0].k = 0; + + vertices[1].x = 0; + vertices[1].y = 0; + slopes[1].dx = pLeft->dy; + slopes[1].dy = -pLeft->dx; + slopes[1].k = 0; + + vertices[2].x = pLeft->xa; + vertices[2].y = pLeft->ya; + + if (joinStyle == GDK_JOIN_MITER) + { + my = (pLeft->dy * (pRight->xa * pRight->dy - pRight->ya * pRight->dx) - + pRight->dy * (pLeft->xa * pLeft->dy - pLeft->ya * pLeft->dx )) / + denom; + if (pLeft->dy != 0) + { + mx = pLeft->xa + (my - pLeft->ya) * + (double) pLeft->dx / (double) pLeft->dy; + } + else + { + mx = pRight->xa + (my - pRight->ya) * + (double) pRight->dx / (double) pRight->dy; + } + /* check miter limit */ + if ((mx * mx + my * my) * 4 > SQSECANT * lw * lw) + joinStyle = GDK_JOIN_BEVEL; + } + + if (joinStyle == GDK_JOIN_MITER) + { + slopes[2].dx = pLeft->dx; + slopes[2].dy = pLeft->dy; + slopes[2].k = pLeft->k; + if (swapslopes) + { + slopes[2].dx = -slopes[2].dx; + slopes[2].dy = -slopes[2].dy; + slopes[2].k = -slopes[2].k; + } + vertices[3].x = mx; + vertices[3].y = my; + slopes[3].dx = pRight->dx; + slopes[3].dy = pRight->dy; + slopes[3].k = pRight->k; + if (swapslopes) + { + slopes[3].dx = -slopes[3].dx; + slopes[3].dy = -slopes[3].dy; + slopes[3].k = -slopes[3].k; + } + edgecount = 4; + } + else + { + double scale, dx, dy, adx, ady; + + adx = dx = pRight->xa - pLeft->xa; + ady = dy = pRight->ya - pLeft->ya; + if (adx < 0) + adx = -adx; + if (ady < 0) + ady = -ady; + scale = ady; + if (adx > ady) + scale = adx; + slopes[2].dx = (dx * 65536) / scale; + slopes[2].dy = (dy * 65536) / scale; + slopes[2].k = ((pLeft->xa + pRight->xa) * slopes[2].dy - + (pLeft->ya + pRight->ya) * slopes[2].dx) / 2.0; + edgecount = 3; + } + + y = miPolyBuildPoly (vertices, slopes, edgecount, pLeft->x, pLeft->y, + left, right, &nleft, &nright, &height); + miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, height, left, right, nleft, nright); +} + +static int +miLineArcI (pDraw, pGC, xorg, yorg, points, widths) + GdkDrawable* pDraw; + GdkGC* pGC; + int xorg, yorg; + GdkRectangle* points; +{ + register GdkRectangle* tpts, *bpts; + register int x, y, e, ex, slw; + + tpts = points; + slw = GDK_GC_FBDATA(pGC)->values.line_width; + if (slw == 1) + { + tpts->x = xorg; + tpts->y = yorg; + tpts->width = 1; + tpts->height = 1; + return 1; + } + bpts = tpts + slw; + y = (slw >> 1) + 1; + if (slw & 1) + e = - ((y << 2) + 3); + else + e = - (y << 3); + ex = -4; + x = 0; + while (y) + { + e += (y << 3) - 4; + while (e >= 0) + { + x++; + e += (ex = -((x << 3) + 4)); + } + y--; + slw = (x << 1) + 1; + if ((e == ex) && (slw > 1)) + slw--; + tpts->x = xorg - x; + tpts->y = yorg - y; + tpts->width = slw; + tpts->height = 1; + tpts++; + if ((y != 0) && ((slw > 1) || (e != ex))) + { + bpts--; + bpts->x = xorg - x; + bpts->y = yorg + y; + bpts->height = 1; + bpts->width = slw; + } + } + return (GDK_GC_FBDATA(pGC)->values.line_width); +} + +#define CLIPSTEPEDGE(edgey,edge,edgeleft) \ + if (ybase == edgey) \ + { \ + if (edgeleft) \ + { \ + if (edge->x > xcl) \ + xcl = edge->x; \ + } \ + else \ + { \ + if (edge->x < xcr) \ + xcr = edge->x; \ + } \ + edgey++; \ + edge->x += edge->stepx; \ + edge->e += edge->dx; \ + if (edge->e > 0) \ + { \ + edge->x += edge->signdx; \ + edge->e -= edge->dy; \ + } \ + } + +static int +miLineArcD (pDraw, pGC, xorg, yorg, points, widths, + edge1, edgey1, edgeleft1, edge2, edgey2, edgeleft2) + GdkDrawable* pDraw; + GdkGC* pGC; + double xorg, yorg; + GdkRectangle* points; + PolyEdgePtr edge1, edge2; + int edgey1, edgey2; + gboolean edgeleft1, edgeleft2; +{ + register GdkRectangle* pts; + double radius, x0, y0, el, er, yk, xlk, xrk, k; + int xbase, ybase, y, boty, xl, xr, xcl, xcr; + int ymin, ymax; + gboolean edge1IsMin, edge2IsMin; + int ymin1, ymin2; + + pts = points; + xbase = floor(xorg); + x0 = xorg - xbase; + ybase = ICEIL (yorg); + y0 = yorg - ybase; + xlk = x0 + x0 + 1.0; + xrk = x0 + x0 - 1.0; + yk = y0 + y0 - 1.0; + radius = ((double)GDK_GC_FBDATA(pGC)->values.line_width) / 2.0; + y = floor(radius - y0 + 1.0); + ybase -= y; + ymin = ybase; + ymax = 65536; + edge1IsMin = FALSE; + ymin1 = edgey1; + if (edge1->dy >= 0) + { + if (!edge1->dy) + { + if (edgeleft1) + edge1IsMin = TRUE; + else + ymax = edgey1; + edgey1 = 65536; + } + else + { + if ((edge1->signdx < 0) == edgeleft1) + edge1IsMin = TRUE; + } + } + edge2IsMin = FALSE; + ymin2 = edgey2; + if (edge2->dy >= 0) + { + if (!edge2->dy) + { + if (edgeleft2) + edge2IsMin = TRUE; + else + ymax = edgey2; + edgey2 = 65536; + } + else + { + if ((edge2->signdx < 0) == edgeleft2) + edge2IsMin = TRUE; + } + } + if (edge1IsMin) + { + ymin = ymin1; + if (edge2IsMin && ymin1 > ymin2) + ymin = ymin2; + } else if (edge2IsMin) + ymin = ymin2; + el = radius * radius - ((y + y0) * (y + y0)) - (x0 * x0); + er = el + xrk; + xl = 1; + xr = 0; + if (x0 < 0.5) + { + xl = 0; + el -= xlk; + } + boty = (y0 < -0.5) ? 1 : 0; + if (ybase + y - boty > ymax) + boty = ymax - ybase - y; + while (y > boty) + { + k = (y << 1) + yk; + er += k; + while (er > 0.0) + { + xr++; + er += xrk - (xr << 1); + } + el += k; + while (el >= 0.0) + { + xl--; + el += (xl << 1) - xlk; + } + y--; + ybase++; + if (ybase < ymin) + continue; + xcl = xl + xbase; + xcr = xr + xbase; + CLIPSTEPEDGE(edgey1, edge1, edgeleft1); + CLIPSTEPEDGE(edgey2, edge2, edgeleft2); + if (xcr >= xcl) + { + pts->x = xcl; + pts->y = ybase; + pts->width = xcr - xcl + 1; + pts->height = 1; + pts++; + } + } + er = xrk - (xr << 1) - er; + el = (xl << 1) - xlk - el; + boty = floor(-y0 - radius + 1.0); + if (ybase + y - boty > ymax) + boty = ymax - ybase - y; + while (y > boty) + { + k = (y << 1) + yk; + er -= k; + while ((er >= 0.0) && (xr >= 0)) + { + xr--; + er += xrk - (xr << 1); + } + el -= k; + while ((el > 0.0) && (xl <= 0)) + { + xl++; + el += (xl << 1) - xlk; + } + y--; + ybase++; + if (ybase < ymin) + continue; + xcl = xl + xbase; + xcr = xr + xbase; + CLIPSTEPEDGE(edgey1, edge1, edgeleft1); + CLIPSTEPEDGE(edgey2, edge2, edgeleft2); + if (xcr >= xcl) + { + pts->x = xcl; + pts->y = ybase; + pts->width = xcr - xcl + 1; + pts->height = 1; + pts++; + } + } + return (pts - points); +} + +int +miRoundJoinFace (face, edge, leftEdge) + register LineFacePtr face; + register PolyEdgePtr edge; + gboolean *leftEdge; +{ + int y; + int dx, dy; + double xa, ya; + gboolean left; + + dx = -face->dy; + dy = face->dx; + xa = face->xa; + ya = face->ya; + left = 1; + if (ya > 0) + { + ya = 0.0; + xa = 0.0; + } + if (dy < 0 || (dy == 0 && dx > 0)) + { + dx = -dx; + dy = -dy; + left = !left; + } + if (dx == 0 && dy == 0) + dy = 1; + if (dy == 0) + { + y = ICEIL (face->ya) + face->y; + edge->x = -32767; + edge->stepx = 0; + edge->signdx = 0; + edge->e = -1; + edge->dy = 0; + edge->dx = 0; + edge->height = 0; + } + else + { + y = miPolyBuildEdge (xa, ya, 0.0, dx, dy, face->x, face->y, !left, edge); + edge->height = 32767; + } + *leftEdge = !left; + return y; +} + +void +miRoundJoinClip (pLeft, pRight, edge1, edge2, y1, y2, left1, left2) + register LineFacePtr pLeft, pRight; + PolyEdgePtr edge1, edge2; + int *y1, *y2; + gboolean *left1, *left2; +{ + double denom; + + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + + if (denom >= 0) + { + pLeft->xa = -pLeft->xa; + pLeft->ya = -pLeft->ya; + } + else + { + pRight->xa = -pRight->xa; + pRight->ya = -pRight->ya; + } + *y1 = miRoundJoinFace (pLeft, edge1, left1); + *y2 = miRoundJoinFace (pRight, edge2, left2); +} + +int +miRoundCapClip (face, isInt, edge, leftEdge) + register LineFacePtr face; + gboolean isInt; + register PolyEdgePtr edge; + gboolean *leftEdge; +{ + int y; + register int dx, dy; + double xa, ya, k; + gboolean left; + + dx = -face->dy; + dy = face->dx; + xa = face->xa; + ya = face->ya; + k = 0.0; + if (!isInt) + k = face->k; + left = 1; + if (dy < 0 || (dy == 0 && dx > 0)) + { + dx = -dx; + dy = -dy; + xa = -xa; + ya = -ya; + left = !left; + } + if (dx == 0 && dy == 0) + dy = 1; + if (dy == 0) + { + y = ICEIL (face->ya) + face->y; + edge->x = -32767; + edge->stepx = 0; + edge->signdx = 0; + edge->e = -1; + edge->dy = 0; + edge->dx = 0; + edge->height = 0; + } + else + { + y = miPolyBuildEdge (xa, ya, k, dx, dy, face->x, face->y, !left, edge); + edge->height = 32767; + } + *leftEdge = !left; + return y; +} + +static void +miLineArc (pDraw, pGC, pixel, spanData, leftFace, rightFace, xorg, yorg, isInt) + GdkDrawable* pDraw; + register GdkGC* pGC; + GdkColor* pixel; + SpanDataPtr spanData; + register LineFacePtr leftFace, rightFace; + double xorg, yorg; + gboolean isInt; +{ + GdkRectangle* points; + int xorgi = 0, yorgi = 0; + GdkColor oldPixel; + Spans spanRec; + int n; + PolyEdgeRec edge1, edge2; + int edgey1, edgey2; + gboolean edgeleft1, edgeleft2; + + if (isInt) + { + xorgi = leftFace ? leftFace->x : rightFace->x; + yorgi = leftFace ? leftFace->y : rightFace->y; + } + edgey1 = 65536; + edgey2 = 65536; + edge1.x = 0; /* not used, keep memory checkers happy */ + edge1.dy = -1; + edge2.x = 0; /* not used, keep memory checkers happy */ + edge2.dy = -1; + edgeleft1 = FALSE; + edgeleft2 = FALSE; + if (((GDK_GC_FBDATA(pGC)->values.line_style != GDK_LINE_SOLID || GDK_GC_FBDATA(pGC)->values.line_width > 2) && + (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND && GDK_GC_FBDATA(pGC)->values.join_style != GDK_JOIN_ROUND)) || + (GDK_GC_FBDATA(pGC)->values.join_style == GDK_JOIN_ROUND && GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_BUTT)) + { + if (isInt) + { + xorg = (double) xorgi; + yorg = (double) yorgi; + } + if (leftFace && rightFace) + { + miRoundJoinClip (leftFace, rightFace, &edge1, &edge2, + &edgey1, &edgey2, &edgeleft1, &edgeleft2); + } + else if (leftFace) + { + edgey1 = miRoundCapClip (leftFace, isInt, &edge1, &edgeleft1); + } + else if (rightFace) + { + edgey2 = miRoundCapClip (rightFace, isInt, &edge2, &edgeleft2); + } + isInt = FALSE; + } + if (!spanData) + { + points = (GdkRectangle*)ALLOCATE_LOCAL(sizeof(GdkRectangle) * GDK_GC_FBDATA(pGC)->values.line_width); + if (!points) + return; + oldPixel = GDK_GC_FBDATA(pGC)->values.foreground; + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, pixel); + } + } + else + { + points = (GdkRectangle*) g_malloc (GDK_GC_FBDATA(pGC)->values.line_width * sizeof (GdkRectangle)); + if (!points) + return; + spanRec.points = points; + } + if (isInt) + n = miLineArcI(pDraw, pGC, xorgi, yorgi, points); + else + n = miLineArcD(pDraw, pGC, xorg, yorg, points, + &edge1, edgey1, edgeleft1, + &edge2, edgey2, edgeleft2); + + if (!spanData) + { + gdk_fb_fill_spans(pDraw, pGC, points, n); + DEALLOCATE_LOCAL(points); + if (pixel->pixel != oldPixel.pixel) + { + gdk_gc_set_foreground(pGC, &oldPixel); + } + } + else + { + spanRec.count = n; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +void +miLineProjectingCap (pDrawable, pGC, pixel, spanData, face, isLeft, xorg, yorg, isInt) + GdkDrawable* pDrawable; + register GdkGC* pGC; + GdkColor *pixel; + SpanDataPtr spanData; + register LineFacePtr face; + gboolean isLeft; + double xorg, yorg; + gboolean isInt; +{ + int xorgi = 0, yorgi = 0; + int lw; + PolyEdgeRec lefts[2], rights[2]; + int lefty, righty, topy, bottomy; + PolyEdgePtr left, right; + PolyEdgePtr top, bottom; + double xa,ya; + double k; + double xap, yap; + int dx, dy; + double projectXoff, projectYoff; + double maxy; + int finaly; + + if (isInt) + { + xorgi = face->x; + yorgi = face->y; + } + lw = GDK_GC_FBDATA(pGC)->values.line_width; + dx = face->dx; + dy = face->dy; + k = face->k; + if (dy == 0) + { + lefts[0].height = lw; + lefts[0].x = xorgi; + if (isLeft) + lefts[0].x -= (lw >> 1); + lefts[0].stepx = 0; + lefts[0].signdx = 1; + lefts[0].e = -lw; + lefts[0].dx = 0; + lefts[0].dy = lw; + rights[0].height = lw; + rights[0].x = xorgi; + if (!isLeft) + rights[0].x += ((lw + 1) >> 1); + rights[0].stepx = 0; + rights[0].signdx = 1; + rights[0].e = -lw; + rights[0].dx = 0; + rights[0].dy = lw; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, yorgi - (lw >> 1), lw, + lefts, rights, 1, 1); + } + else if (dx == 0) + { + topy = yorgi; + bottomy = yorgi + dy; + if (isLeft) + topy -= (lw >> 1); + else + bottomy += (lw >> 1); + lefts[0].height = bottomy - topy; + lefts[0].x = xorgi - (lw >> 1); + lefts[0].stepx = 0; + lefts[0].signdx = 1; + lefts[0].e = -dy; + lefts[0].dx = dx; + lefts[0].dy = dy; + + rights[0].height = bottomy - topy; + rights[0].x = lefts[0].x + (lw-1); + rights[0].stepx = 0; + rights[0].signdx = 1; + rights[0].e = -dy; + rights[0].dx = dx; + rights[0].dy = dy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, bottomy - topy, lefts, rights, 1, 1); + } + else + { + xa = face->xa; + ya = face->ya; + projectXoff = -ya; + projectYoff = xa; + if (dx < 0) + { + right = &rights[1]; + left = &lefts[0]; + top = &rights[0]; + bottom = &lefts[1]; + } + else + { + right = &rights[0]; + left = &lefts[1]; + top = &lefts[0]; + bottom = &rights[1]; + } + if (isLeft) + { + righty = miPolyBuildEdge (xa, ya, + k, dx, dy, xorgi, yorgi, 0, right); + + xa = -xa; + ya = -ya; + k = -k; + lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, xorgi, yorgi, 1, left); + if (dx > 0) + { + ya = -ya; + xa = -xa; + } + xap = xa - projectXoff; + yap = ya - projectYoff; + topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, xorgi, yorgi, dx > 0, top); + bottomy = miPolyBuildEdge (xa, ya, + 0.0, -dy, dx, xorgi, yorgi, dx < 0, bottom); + maxy = -ya; + } + else + { + righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, xorgi, yorgi, 0, right); + + xa = -xa; + ya = -ya; + k = -k; + lefty = miPolyBuildEdge (xa, ya, + k, dx, dy, xorgi, yorgi, 1, left); + if (dx > 0) + { + ya = -ya; + xa = -xa; + } + xap = xa - projectXoff; + yap = ya - projectYoff; + topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, xorgi, dx > 0, top); + bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, xorgi, xorgi, dx < 0, bottom); + maxy = -ya + projectYoff; + } + finaly = ICEIL(maxy) + yorgi; + if (dx < 0) + { + left->height = bottomy - lefty; + right->height = finaly - righty; + top->height = righty - topy; + } + else + { + right->height = bottomy - righty; + left->height = finaly - lefty; + top->height = lefty - topy; + } + bottom->height = finaly - bottomy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, + bottom->height + bottomy - topy, lefts, rights, 2, 2); + } +} + +static void +miWideSegment (GdkDrawable *pDrawable, GdkGC *pGC, GdkColor *pixel, SpanDataPtr spanData, + int x1, int y1, int x2, int y2, gboolean projectLeft, gboolean projectRight, LineFacePtr leftFace, LineFacePtr rightFace) +{ + double l, L, r; + double xa, ya; + double projectXoff = 0.0, projectYoff = 0.0; + double k; + double maxy; + int x, y; + int dx, dy; + int finaly; + PolyEdgePtr left, right; + PolyEdgePtr top, bottom; + int lefty, righty, topy, bottomy; + int signdx; + PolyEdgeRec lefts[2], rights[2]; + LineFacePtr tface; + int lw = GDK_GC_FBDATA(pGC)->values.line_width; + + g_assert(leftFace); + /* draw top-to-bottom always */ + if (y2 < y1 || (y2 == y1 && x2 < x1)) + { + x = x1; + x1 = x2; + x2 = x; + + y = y1; + y1 = y2; + y2 = y; + + x = projectLeft; + projectLeft = projectRight; + projectRight = x; + + tface = leftFace; + leftFace = rightFace; + rightFace = tface; + } + + dy = y2 - y1; + signdx = 1; + dx = x2 - x1; + if (dx < 0) + signdx = -1; + + g_assert(leftFace); + leftFace->x = x1; + leftFace->y = y1; + leftFace->dx = dx; + leftFace->dy = dy; + + rightFace->x = x2; + rightFace->y = y2; + rightFace->dx = -dx; + rightFace->dy = -dy; + + if (dy == 0) + { + rightFace->xa = 0; + rightFace->ya = (double) lw / 2.0; + rightFace->k = -(double) (lw * dx) / 2.0; + leftFace->xa = 0; + leftFace->ya = -rightFace->ya; + leftFace->k = rightFace->k; + x = x1; + if (projectLeft) + x -= (lw >> 1); + y = y1 - (lw >> 1); + dx = x2 - x; + if (projectRight) + dx += ((lw + 1) >> 1); + dy = lw; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x, y, dx, dy); + } + else if (dx == 0) + { + leftFace->xa = (double) lw / 2.0; + leftFace->ya = 0; + leftFace->k = (double) (lw * dy) / 2.0; + rightFace->xa = -leftFace->xa; + rightFace->ya = 0; + rightFace->k = leftFace->k; + y = y1; + if (projectLeft) + y -= lw >> 1; + x = x1 - (lw >> 1); + dy = y2 - y; + if (projectRight) + dy += ((lw + 1) >> 1); + dx = lw; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x, y, dx, dy); + } + else + { + l = ((double) lw) / 2.0; + L = hypot ((double) dx, (double) dy); + + if (dx < 0) + { + right = &rights[1]; + left = &lefts[0]; + top = &rights[0]; + bottom = &lefts[1]; + } + else + { + right = &rights[0]; + left = &lefts[1]; + top = &lefts[0]; + bottom = &rights[1]; + } + r = l / L; + + /* coord of upper bound at integral y */ + ya = -r * dx; + xa = r * dy; + + if (projectLeft | projectRight) + { + projectXoff = -ya; + projectYoff = xa; + } + + /* xa * dy - ya * dx */ + k = l * L; + + leftFace->xa = xa; + leftFace->ya = ya; + leftFace->k = k; + rightFace->xa = -xa; + rightFace->ya = -ya; + rightFace->k = k; + + if (projectLeft) + righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, x1, y1, 0, right); + else + righty = miPolyBuildEdge (xa, ya, + k, dx, dy, x1, y1, 0, right); + + /* coord of lower bound at integral y */ + ya = -ya; + xa = -xa; + + /* xa * dy - ya * dx */ + k = - k; + + if (projectLeft) + lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, x1, y1, 1, left); + else + lefty = miPolyBuildEdge (xa, ya, + k, dx, dy, x1, y1, 1, left); + + /* coord of top face at integral y */ + + if (signdx > 0) + { + ya = -ya; + xa = -xa; + } + + if (projectLeft) + { + double xap = xa - projectXoff; + double yap = ya - projectYoff; + topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, x1, y1, dx > 0, top); + } + else + topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x1, y1, dx > 0, top); + + /* coord of bottom face at integral y */ + + if (projectRight) + { + double xap = xa + projectXoff; + double yap = ya + projectYoff; + bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, x2, y2, dx < 0, bottom); + maxy = -ya + projectYoff; + } + else + { + bottomy = miPolyBuildEdge (xa, ya, + 0.0, -dy, dx, x2, y2, dx < 0, bottom); + maxy = -ya; + } + + finaly = ICEIL (maxy) + y2; + + if (dx < 0) + { + left->height = bottomy - lefty; + right->height = finaly - righty; + top->height = righty - topy; + } + else + { + right->height = bottomy - righty; + left->height = finaly - lefty; + top->height = lefty - topy; + } + bottom->height = finaly - bottomy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, + bottom->height + bottomy - topy, lefts, rights, 2, 2); + } +} + +SpanDataPtr +miSetupSpanData (pGC, spanData, npt) + register GdkGC* pGC; + SpanDataPtr spanData; + int npt; +{ + if ((npt < 3 && GDK_GC_FBDATA(pGC)->values.cap_style != GDK_CAP_ROUND) || miSpansEasyRop(GDK_GC_FBDATA(pGC)->alu)) + return (SpanDataPtr) NULL; + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) + miInitSpanGroup (&spanData->bgGroup); + miInitSpanGroup (&spanData->fgGroup); + return spanData; +} + +void +miCleanupSpanData (pDrawable, pGC, spanData) + GdkDrawable* pDrawable; + GdkGC* pGC; + SpanDataPtr spanData; +{ + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) + { + GdkColor oldPixel, pixel; + + pixel = GDK_GC_FBDATA(pGC)->values.background; + oldPixel = GDK_GC_FBDATA(pGC)->values.foreground; + if (pixel.pixel != oldPixel.pixel) + gdk_gc_set_foreground(pGC, &pixel); + miFillUniqueSpanGroup (pDrawable, pGC, &spanData->bgGroup); + miFreeSpanGroup (&spanData->bgGroup); + if (pixel.pixel != oldPixel.pixel) + gdk_gc_set_foreground(pGC, &oldPixel); + } + miFillUniqueSpanGroup (pDrawable, pGC, &spanData->fgGroup); + miFreeSpanGroup (&spanData->fgGroup); +} + +void +miWideLine (pDrawable, pGC, mode, npt, pPts) + GdkDrawable* pDrawable; + register GdkGC* pGC; + int mode; + register int npt; + register GdkPoint* pPts; +{ + int x1, y1, x2, y2; + SpanDataRec spanDataRec; + SpanDataPtr spanData; + GdkColor pixel; + gboolean projectLeft, projectRight; + LineFaceRec leftFace, rightFace, prevRightFace; + LineFaceRec firstFace; + register int first; + gboolean somethingDrawn = FALSE; + gboolean selfJoin; + + spanData = miSetupSpanData (pGC, &spanDataRec, npt); + pixel = GDK_GC_FBDATA(pGC)->values.foreground; + x2 = pPts->x; + y2 = pPts->y; + first = TRUE; + selfJoin = FALSE; + if (npt > 1) + { + if (0 /* mode == CoordModePrevious*/) + { + int nptTmp; + GdkPoint* pPtsTmp; + + x1 = x2; + y1 = y2; + nptTmp = npt; + pPtsTmp = pPts + 1; + while (--nptTmp) + { + x1 += pPtsTmp->x; + y1 += pPtsTmp->y; + ++pPtsTmp; + } + if (x2 == x1 && y2 == y1) + selfJoin = TRUE; + } + else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y) + { + selfJoin = TRUE; + } + } + projectLeft = GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING && !selfJoin; + projectRight = FALSE; + while (--npt) + { + x1 = x2; + y1 = y2; + ++pPts; + x2 = pPts->x; + y2 = pPts->y; + if (0 /* mode == CoordModePrevious */) + { + x2 += x1; + y2 += y1; + } + if (x1 != x2 || y1 != y2) + { + somethingDrawn = TRUE; + if (npt == 1 && GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING && !selfJoin) + projectRight = TRUE; + miWideSegment (pDrawable, pGC, &pixel, spanData, x1, y1, x2, y2, + projectLeft, projectRight, &leftFace, &rightFace); + if (first) + { + if (selfJoin) + firstFace = leftFace; + else if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + { + if (GDK_GC_FBDATA(pGC)->values.line_width == 1 && !spanData) + miLineOnePoint (pDrawable, pGC, pixel, spanData, x1, y1); + else + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, + TRUE); + } + } + else + { + miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace, + &prevRightFace); + } + prevRightFace = rightFace; + first = FALSE; + projectLeft = FALSE; + } + if (npt == 1 && somethingDrawn) + { + if (selfJoin) + miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace, + &rightFace); + else if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + { + if (GDK_GC_FBDATA(pGC)->values.line_width == 1 && !spanData) + miLineOnePoint (pDrawable, pGC, pixel, spanData, x2, y2); + else + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, + TRUE); + } + } + } + /* handle crock where all points are coincedent */ + if (!somethingDrawn) + { + projectLeft = GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING; + miWideSegment (pDrawable, pGC, &pixel, spanData, + x2, y2, x2, y2, projectLeft, projectLeft, + &leftFace, &rightFace); + if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + { + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, + TRUE); + rightFace.dx = -1; /* sleezy hack to make it work */ + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, + TRUE); + } + } + if (spanData) + miCleanupSpanData (pDrawable, pGC, spanData); +} + +#define V_TOP 0 +#define V_RIGHT 1 +#define V_BOTTOM 2 +#define V_LEFT 3 + +static void +miWideDashSegment (pDrawable, pGC, spanData, pDashOffset, pDashIndex, + x1, y1, x2, y2, projectLeft, projectRight, leftFace, rightFace) + GdkDrawable* pDrawable; + register GdkGC* pGC; + int *pDashOffset, *pDashIndex; + SpanDataPtr spanData; + int x1, y1, x2, y2; + gboolean projectLeft, projectRight; + LineFacePtr leftFace, rightFace; +{ + int dashIndex, dashRemain; + unsigned char *pDash; + double L, l; + double k; + PolyVertexRec vertices[4]; + PolyVertexRec saveRight, saveBottom; + PolySlopeRec slopes[4]; + PolyEdgeRec left[2], right[2]; + LineFaceRec lcapFace, rcapFace; + int nleft, nright; + int h; + int y; + int dy, dx; + GdkColor pixel; + double LRemain; + double r; + double rdx, rdy; + double dashDx, dashDy; + double saveK = 0.0; + gboolean first = TRUE; + double lcenterx, lcentery, rcenterx = 0.0, rcentery = 0.0; + GdkColor fgPixel, bgPixel; + + dx = x2 - x1; + dy = y2 - y1; + dashIndex = *pDashIndex; + pDash = GDK_GC_FBDATA(pGC)->dash_list; + dashRemain = pDash[dashIndex] - *pDashOffset; + fgPixel = GDK_GC_FBDATA(pGC)->values.foreground; + bgPixel = GDK_GC_FBDATA(pGC)->values.background; + if (GDK_GC_FBDATA(pGC)->values.fill == GDK_OPAQUE_STIPPLED || + GDK_GC_FBDATA(pGC)->values.fill == GDK_TILED) + { + bgPixel = fgPixel; + } + + l = ((double) GDK_GC_FBDATA(pGC)->values.line_width) / 2.0; + if (dx == 0) + { + L = dy; + rdx = 0; + rdy = l; + if (dy < 0) + { + L = -dy; + rdy = -l; + } + } + else if (dy == 0) + { + L = dx; + rdx = l; + rdy = 0; + if (dx < 0) + { + L = -dx; + rdx = -l; + } + } + else + { + L = hypot ((double) dx, (double) dy); + r = l / L; + + rdx = r * dx; + rdy = r * dy; + } + k = l * L; + LRemain = L; + /* All position comments are relative to a line with dx and dy > 0, + * but the code does not depend on this */ + /* top */ + slopes[V_TOP].dx = dx; + slopes[V_TOP].dy = dy; + slopes[V_TOP].k = k; + /* right */ + slopes[V_RIGHT].dx = -dy; + slopes[V_RIGHT].dy = dx; + slopes[V_RIGHT].k = 0; + /* bottom */ + slopes[V_BOTTOM].dx = -dx; + slopes[V_BOTTOM].dy = -dy; + slopes[V_BOTTOM].k = k; + /* left */ + slopes[V_LEFT].dx = dy; + slopes[V_LEFT].dy = -dx; + slopes[V_LEFT].k = 0; + + /* preload the start coordinates */ + vertices[V_RIGHT].x = vertices[V_TOP].x = rdy; + vertices[V_RIGHT].y = vertices[V_TOP].y = -rdx; + + vertices[V_BOTTOM].x = vertices[V_LEFT].x = -rdy; + vertices[V_BOTTOM].y = vertices[V_LEFT].y = rdx; + + if (projectLeft) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + + slopes[V_LEFT].k = rdx * dx + rdy * dy; + } + + lcenterx = x1; + lcentery = y1; + + if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + { + lcapFace.dx = dx; + lcapFace.dy = dy; + lcapFace.x = x1; + lcapFace.y = y1; + + rcapFace.dx = -dx; + rcapFace.dy = -dy; + rcapFace.x = x1; + rcapFace.y = y1; + } + while (LRemain > dashRemain) + { + dashDx = (dashRemain * dx) / L; + dashDy = (dashRemain * dy) / L; + + rcenterx = lcenterx + dashDx; + rcentery = lcentery + dashDy; + + vertices[V_RIGHT].x += dashDx; + vertices[V_RIGHT].y += dashDy; + + vertices[V_BOTTOM].x += dashDx; + vertices[V_BOTTOM].y += dashDy; + + slopes[V_RIGHT].k = vertices[V_RIGHT].x * dx + vertices[V_RIGHT].y * dy; + + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || !(dashIndex & 1)) + { + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_ON_OFF_DASH && + GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING) + { + saveRight = vertices[V_RIGHT]; + saveBottom = vertices[V_BOTTOM]; + saveK = slopes[V_RIGHT].k; + + if (!first) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + + slopes[V_LEFT].k = vertices[V_LEFT].x * + slopes[V_LEFT].dy - + vertices[V_LEFT].y * + slopes[V_LEFT].dx; + } + + vertices[V_RIGHT].x += rdx; + vertices[V_RIGHT].y += rdy; + + vertices[V_BOTTOM].x += rdx; + vertices[V_BOTTOM].y += rdy; + + slopes[V_RIGHT].k = vertices[V_RIGHT].x * + slopes[V_RIGHT].dy - + vertices[V_RIGHT].y * + slopes[V_RIGHT].dx; + } + y = miPolyBuildPoly (vertices, slopes, 4, x1, y1, + left, right, &nleft, &nright, &h); + pixel = (dashIndex & 1) ? bgPixel : fgPixel; + miFillPolyHelper (pDrawable, pGC, &pixel, spanData, y, h, left, right, nleft, nright); + + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_ON_OFF_DASH) + { + switch (GDK_GC_FBDATA(pGC)->values.cap_style) + { + case GDK_CAP_PROJECTING: + vertices[V_BOTTOM] = saveBottom; + vertices[V_RIGHT] = saveRight; + slopes[V_RIGHT].k = saveK; + break; + case GDK_CAP_ROUND: + if (!first) + { + if (dx < 0) + { + lcapFace.xa = -vertices[V_LEFT].x; + lcapFace.ya = -vertices[V_LEFT].y; + lcapFace.k = slopes[V_LEFT].k; + } + else + { + lcapFace.xa = vertices[V_TOP].x; + lcapFace.ya = vertices[V_TOP].y; + lcapFace.k = -slopes[V_LEFT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + &lcapFace, (LineFacePtr) NULL, + lcenterx, lcentery, FALSE); + } + if (dx < 0) + { + rcapFace.xa = vertices[V_BOTTOM].x; + rcapFace.ya = vertices[V_BOTTOM].y; + rcapFace.k = slopes[V_RIGHT].k; + } + else + { + rcapFace.xa = -vertices[V_RIGHT].x; + rcapFace.ya = -vertices[V_RIGHT].y; + rcapFace.k = -slopes[V_RIGHT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rcapFace, + rcenterx, rcentery, FALSE); + break; + default: + break; + } + } + } + LRemain -= dashRemain; + ++dashIndex; + if (dashIndex == GDK_GC_FBDATA(pGC)->dash_list_len) + dashIndex = 0; + dashRemain = pDash[dashIndex]; + + lcenterx = rcenterx; + lcentery = rcentery; + + vertices[V_TOP] = vertices[V_RIGHT]; + vertices[V_LEFT] = vertices[V_BOTTOM]; + slopes[V_LEFT].k = -slopes[V_RIGHT].k; + first = FALSE; + } + + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || !(dashIndex & 1)) + { + vertices[V_TOP].x -= dx; + vertices[V_TOP].y -= dy; + + vertices[V_LEFT].x -= dx; + vertices[V_LEFT].y -= dy; + + vertices[V_RIGHT].x = rdy; + vertices[V_RIGHT].y = -rdx; + + vertices[V_BOTTOM].x = -rdy; + vertices[V_BOTTOM].y = rdx; + + + if (projectRight) + { + vertices[V_RIGHT].x += rdx; + vertices[V_RIGHT].y += rdy; + + vertices[V_BOTTOM].x += rdx; + vertices[V_BOTTOM].y += rdy; + slopes[V_RIGHT].k = vertices[V_RIGHT].x * + slopes[V_RIGHT].dy - + vertices[V_RIGHT].y * + slopes[V_RIGHT].dx; + } + else + slopes[V_RIGHT].k = 0; + + if (!first && GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_ON_OFF_DASH && + GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + slopes[V_LEFT].k = vertices[V_LEFT].x * + slopes[V_LEFT].dy - + vertices[V_LEFT].y * + slopes[V_LEFT].dx; + } + else + slopes[V_LEFT].k += dx * dx + dy * dy; + + + y = miPolyBuildPoly (vertices, slopes, 4, x2, y2, + left, right, &nleft, &nright, &h); + + pixel = (dashIndex & 1) ? GDK_GC_FBDATA(pGC)->values.background : GDK_GC_FBDATA(pGC)->values.foreground; + miFillPolyHelper (pDrawable, pGC, &pixel, spanData, y, h, left, right, nleft, nright); + if (!first && GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_ON_OFF_DASH && + GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + { + lcapFace.x = x2; + lcapFace.y = y2; + if (dx < 0) + { + lcapFace.xa = -vertices[V_LEFT].x; + lcapFace.ya = -vertices[V_LEFT].y; + lcapFace.k = slopes[V_LEFT].k; + } + else + { + lcapFace.xa = vertices[V_TOP].x; + lcapFace.ya = vertices[V_TOP].y; + lcapFace.k = -slopes[V_LEFT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + &lcapFace, (LineFacePtr) NULL, + rcenterx, rcentery, FALSE); + } + } + dashRemain = ((double) dashRemain) - LRemain; + if (dashRemain == 0) + { + dashIndex++; + if (dashIndex == GDK_GC_FBDATA(pGC)->dash_list_len) + dashIndex = 0; + dashRemain = pDash[dashIndex]; + } + + leftFace->x = x1; + leftFace->y = y1; + leftFace->dx = dx; + leftFace->dy = dy; + leftFace->xa = rdy; + leftFace->ya = -rdx; + leftFace->k = k; + + rightFace->x = x2; + rightFace->y = y2; + rightFace->dx = -dx; + rightFace->dy = -dy; + rightFace->xa = -rdy; + rightFace->ya = rdx; + rightFace->k = k; + + *pDashIndex = dashIndex; + *pDashOffset = pDash[dashIndex] - dashRemain; +} + +void +miWideDash (pDrawable, pGC, mode, npt, pPts) + GdkDrawable* pDrawable; + register GdkGC* pGC; + int mode; + register int npt; + register GdkPoint* pPts; +{ + int x1, y1, x2, y2; + GdkColor pixel; + gboolean projectLeft, projectRight; + LineFaceRec leftFace, rightFace, prevRightFace; + LineFaceRec firstFace; + int first; + int dashIndex, dashOffset; + register int prevDashIndex; + SpanDataRec spanDataRec; + SpanDataPtr spanData; + gboolean somethingDrawn = FALSE; + gboolean selfJoin; + gboolean endIsFg = FALSE, startIsFg = FALSE; + gboolean firstIsFg = FALSE, prevIsFg = FALSE; + +#ifndef XFree86Server + /* XXX backward compatibility */ + if (GDK_GC_FBDATA(pGC)->values.line_width == 0) + { + miZeroDashLine (pDrawable, pGC, mode, npt, pPts); + return; + } +#endif + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH && + (GDK_GC_FBDATA(pGC)->values.fill == GDK_OPAQUE_STIPPLED || GDK_GC_FBDATA(pGC)->values.fill == GDK_TILED)) + { + miWideLine (pDrawable, pGC, mode, npt, pPts); + return; + } + if (npt == 0) + return; + spanData = miSetupSpanData (pGC, &spanDataRec, npt); + x2 = pPts->x; + y2 = pPts->y; + first = TRUE; + selfJoin = FALSE; + if (0 /* mode == CoordModePrevious */) + { + int nptTmp; + GdkPoint* pPtsTmp; + + x1 = x2; + y1 = y2; + nptTmp = npt; + pPtsTmp = pPts + 1; + while (--nptTmp) + { + x1 += pPtsTmp->x; + y1 += pPtsTmp->y; + ++pPtsTmp; + } + if (x2 == x1 && y2 == y1) + selfJoin = TRUE; + } + else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y) + { + selfJoin = TRUE; + } + projectLeft = GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING && !selfJoin; + projectRight = FALSE; + dashIndex = 0; + dashOffset = 0; + miStepDash (GDK_GC_FBDATA(pGC)->dash_offset, &dashIndex, + GDK_GC_FBDATA(pGC)->dash_list, (int)GDK_GC_FBDATA(pGC)->dash_list_len, &dashOffset); + while (--npt) + { + x1 = x2; + y1 = y2; + ++pPts; + x2 = pPts->x; + y2 = pPts->y; + if (0 /* mode == CoordModePrevious */) + { + x2 += x1; + y2 += y1; + } + if (x1 != x2 || y1 != y2) + { + somethingDrawn = TRUE; + if (npt == 1 && GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING && + (!selfJoin || !firstIsFg)) + projectRight = TRUE; + prevDashIndex = dashIndex; + miWideDashSegment (pDrawable, pGC, spanData, &dashOffset, &dashIndex, + x1, y1, x2, y2, + projectLeft, projectRight, &leftFace, &rightFace); + startIsFg = !(prevDashIndex & 1); + endIsFg = (dashIndex & 1) ^ (dashOffset != 0); + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || startIsFg) + { + pixel = startIsFg ? GDK_GC_FBDATA(pGC)->values.foreground : GDK_GC_FBDATA(pGC)->values.background; + if (first || (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_ON_OFF_DASH && !prevIsFg)) + { + if (first && selfJoin) + { + firstFace = leftFace; + firstIsFg = startIsFg; + } + else if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, TRUE); + } + else + { + miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace, + &prevRightFace); + } + } + prevRightFace = rightFace; + prevIsFg = endIsFg; + first = FALSE; + projectLeft = FALSE; + } + if (npt == 1 && somethingDrawn) + { + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || endIsFg) + { + pixel = endIsFg ? GDK_GC_FBDATA(pGC)->values.foreground : GDK_GC_FBDATA(pGC)->values.background; + if (selfJoin && (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || firstIsFg)) + { + miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace, + &rightFace); + } + else + { + if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, TRUE); + } + } + else + { + /* glue a cap to the start of the line if + * we're OnOffDash and ended on odd dash + */ + if (selfJoin && firstIsFg) + { + pixel = GDK_GC_FBDATA(pGC)->values.foreground; + if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_PROJECTING) + miLineProjectingCap (pDrawable, pGC, &pixel, spanData, + &firstFace, TRUE, + (double)0.0, (double)0.0, TRUE); + else if (GDK_GC_FBDATA(pGC)->values.cap_style == GDK_CAP_ROUND) + miLineArc (pDrawable, pGC, &pixel, spanData, + &firstFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, TRUE); + } + } + } + } + /* handle crock where all points are coincident */ + if (!somethingDrawn && (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH || !(dashIndex & 1))) + { + /* not the same as endIsFg computation above */ + pixel = (dashIndex & 1) ? GDK_GC_FBDATA(pGC)->values.background : GDK_GC_FBDATA(pGC)->values.foreground; + switch (GDK_GC_FBDATA(pGC)->values.cap_style) { + case GDK_CAP_ROUND: + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, (LineFacePtr) NULL, + (double)x2, (double)y2, + FALSE); + break; + case GDK_CAP_PROJECTING: + x1 = GDK_GC_FBDATA(pGC)->values.line_width; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x2 - (x1 >> 1), y2 - (x1 >> 1), x1, x1); + break; + default: + break; + } + } + if (spanData) + miCleanupSpanData (pDrawable, pGC, spanData); +} + +/* these are stubs to allow old ddx miValidateGCs to work without change */ + +void +miMiter() +{ +} + +void +miNotMiter() +{ +} diff --git a/gdk/linux-fb/miwideline.h b/gdk/linux-fb/miwideline.h new file mode 100644 index 0000000000..a39b6c13bf --- /dev/null +++ b/gdk/linux-fb/miwideline.h @@ -0,0 +1,254 @@ +/* $TOG: miwideline.h /main/12 1998/02/09 14:49:26 kaleb $ */ +/* + +Copyright 1988, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ +/* $XFree86: xc/programs/Xserver/mi/miwideline.h,v 1.7 1998/10/04 09:39:35 dawes Exp $ */ + +/* Author: Keith Packard, MIT X Consortium */ + +#ifndef MI_WIDELINE_H +#define MI_WIDELINE_H 1 + +#include "mispans.h" + +/* + * interface data to span-merging polygon filler + */ + +typedef struct _SpanData { + SpanGroup fgGroup, bgGroup; +} SpanDataRec, *SpanDataPtr; + +#define AppendSpanGroup(pGC, pixel, spanPtr, spanData) { \ + SpanGroup *group, *othergroup = NULL; \ + if (pixel->pixel == GDK_GC_FBDATA(pGC)->values.foreground.pixel) \ + { \ + group = &spanData->fgGroup; \ + if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) \ + othergroup = &spanData->bgGroup; \ + } \ + else \ + { \ + group = &spanData->bgGroup; \ + othergroup = &spanData->fgGroup; \ + } \ + miAppendSpans (group, othergroup, spanPtr); \ +} + +/* + * Polygon edge description for integer wide-line routines + */ + +typedef struct _PolyEdge { + int height; /* number of scanlines to process */ + int x; /* starting x coordinate */ + int stepx; /* fixed integral dx */ + int signdx; /* variable dx sign */ + int e; /* initial error term */ + int dy; + int dx; +} PolyEdgeRec, *PolyEdgePtr; + +#define SQSECANT 108.856472512142 /* 1/sin^2(11/2) - miter limit constant */ + +/* + * types for general polygon routines + */ + +typedef struct _PolyVertex { + double x, y; +} PolyVertexRec, *PolyVertexPtr; + +typedef struct _PolySlope { + int dx, dy; + double k; /* x0 * dy - y0 * dx */ +} PolySlopeRec, *PolySlopePtr; + +/* + * Line face description for caps/joins + */ + +typedef struct _LineFace { + double xa, ya; + int dx, dy; + int x, y; + double k; +} LineFaceRec, *LineFacePtr; + +/* + * macros for polygon fillers + */ + +#define MIPOLYRELOADLEFT if (!left_height && left_count) { \ + left_height = left->height; \ + left_x = left->x; \ + left_stepx = left->stepx; \ + left_signdx = left->signdx; \ + left_e = left->e; \ + left_dy = left->dy; \ + left_dx = left->dx; \ + --left_count; \ + ++left; \ + } + +#define MIPOLYRELOADRIGHT if (!right_height && right_count) { \ + right_height = right->height; \ + right_x = right->x; \ + right_stepx = right->stepx; \ + right_signdx = right->signdx; \ + right_e = right->e; \ + right_dy = right->dy; \ + right_dx = right->dx; \ + --right_count; \ + ++right; \ + } + +#define MIPOLYSTEPLEFT left_x += left_stepx; \ + left_e += left_dx; \ + if (left_e > 0) \ + { \ + left_x += left_signdx; \ + left_e -= left_dy; \ + } + +#define MIPOLYSTEPRIGHT right_x += right_stepx; \ + right_e += right_dx; \ + if (right_e > 0) \ + { \ + right_x += right_signdx; \ + right_e -= right_dy; \ + } + +#define MILINESETPIXEL(pDrawable, pGC, pixel, oldPixel) { \ + oldPixel = GDK_GC_FBDATA(pGC)->values.foreground; \ + if (pixel->pixel != oldPixel.pixel) { \ + gdk_gc_set_foreground(pGC, pixel); \ + } \ +} +#define MILINERESETPIXEL(pDrawable, pGC, pixel, oldPixel) { \ + if (pixel->pixel != oldPixel.pixel) { \ + gdk_gc_set_foreground(pGC, &oldPixel); \ + } \ +} + +#ifndef ICEIL +#ifdef NOINLINEICEIL +#define ICEIL(x) ((int)ceil(x)) +#else +#ifdef __GNUC__ +#define ICEIL ICIEL +static __inline int ICEIL(x) + double x; +{ + int _cTmp = x; + return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp+1; +} +#else +#define ICEIL(x) ((((x) == (_cTmp = (x))) || ((x) < 0.0)) ? _cTmp : _cTmp+1) +#define ICEILTEMPDECL static int _cTmp; +#endif +#endif +#endif + +extern void miFillPolyHelper( +#if NeedFunctionPrototypes + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + GdkColor * /*pixel*/, + SpanDataPtr /*spanData*/, + int /*y*/, + int /*overall_height*/, + PolyEdgePtr /*left*/, + PolyEdgePtr /*right*/, + int /*left_count*/, + int /*right_count*/ +#endif +); +extern int miRoundJoinFace( +#if NeedFunctionPrototypes + LineFacePtr /*face*/, + PolyEdgePtr /*edge*/, + gboolean * /*leftEdge*/ +#endif +); + +extern void miRoundJoinClip( +#if NeedFunctionPrototypes + LineFacePtr /*pLeft*/, + LineFacePtr /*pRight*/, + PolyEdgePtr /*edge1*/, + PolyEdgePtr /*edge2*/, + int * /*y1*/, + int * /*y2*/, + gboolean * /*left1*/, + gboolean * /*left2*/ +#endif +); + +extern int miRoundCapClip( +#if NeedFunctionPrototypes + LineFacePtr /*face*/, + gboolean /*isInt*/, + PolyEdgePtr /*edge*/, + gboolean * /*leftEdge*/ +#endif +); + +extern void miLineProjectingCap( +#if NeedFunctionPrototypes + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + GdkColor * /*pixel*/, + SpanDataPtr /*spanData*/, + LineFacePtr /*face*/, + gboolean /*isLeft*/, + double /*xorg*/, + double /*yorg*/, + gboolean /*isInt*/ +#endif +); + +extern SpanDataPtr miSetupSpanData( +#if NeedFunctionPrototypes + GdkGC* /*pGC*/, + SpanDataPtr /*spanData*/, + int /*npt*/ +#endif +); + +extern void miCleanupSpanData( +#if NeedFunctionPrototypes + GdkDrawable* /*pDrawable*/, + GdkGC* /*pGC*/, + SpanDataPtr /*spanData*/ +#endif +); + +extern int miPolyBuildEdge(double x0, double y0, double k, int dx, int dy, + int xi, int yi, int left, PolyEdgePtr edge); +extern int miPolyBuildPoly(PolyVertexPtr vertices, PolySlopePtr slopes, + int count, int xi, int yi, PolyEdgePtr left, + PolyEdgePtr right, int *pnleft, int *pnright, + int *h); + +#endif diff --git a/gdk/linux-fb/mizerclip.c b/gdk/linux-fb/mizerclip.c new file mode 100644 index 0000000000..145ccd9551 --- /dev/null +++ b/gdk/linux-fb/mizerclip.c @@ -0,0 +1,622 @@ +/* $XFree86: xc/programs/Xserver/mi/mizerclip.c,v 1.1 1999/10/13 22:33:13 dawes Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#include "mi.h" +#include "miline.h" + +/* + +The bresenham error equation used in the mi/mfb/cfb line routines is: + + e = error + dx = difference in raw X coordinates + dy = difference in raw Y coordinates + M = # of steps in X direction + N = # of steps in Y direction + B = 0 to prefer diagonal steps in a given octant, + 1 to prefer axial steps in a given octant + + For X major lines: + e = 2Mdy - 2Ndx - dx - B + -2dx <= e < 0 + + For Y major lines: + e = 2Ndx - 2Mdy - dy - B + -2dy <= e < 0 + +At the start of the line, we have taken 0 X steps and 0 Y steps, +so M = 0 and N = 0: + + X major e = 2Mdy - 2Ndx - dx - B + = -dx - B + + Y major e = 2Ndx - 2Mdy - dy - B + = -dy - B + +At the end of the line, we have taken dx X steps and dy Y steps, +so M = dx and N = dy: + + X major e = 2Mdy - 2Ndx - dx - B + = 2dxdy - 2dydx - dx - B + = -dx - B + Y major e = 2Ndx - 2Mdy - dy - B + = 2dydx - 2dxdy - dy - B + = -dy - B + +Thus, the error term is the same at the start and end of the line. + +Let us consider clipping an X coordinate. There are 4 cases which +represent the two independent cases of clipping the start vs. the +end of the line and an X major vs. a Y major line. In any of these +cases, we know the number of X steps (M) and we wish to find the +number of Y steps (N). Thus, we will solve our error term equation. +If we are clipping the start of the line, we will find the smallest +N that satisfies our error term inequality. If we are clipping the +end of the line, we will find the largest number of Y steps that +satisfies the inequality. In that case, since we are representing +the Y steps as (dy - N), we will actually want to solve for the +smallest N in that equation. + +Case 1: X major, starting X coordinate moved by M steps + + -2dx <= 2Mdy - 2Ndx - dx - B < 0 + 2Ndx <= 2Mdy - dx - B + 2dx 2Ndx > 2Mdy - dx - B + 2Ndx <= 2Mdy + dx - B N > (2Mdy - dx - B) / 2dx + N <= (2Mdy + dx - B) / 2dx + +Since we are trying to find the smallest N that satisfies these +equations, we should use the > inequality to find the smallest: + + N = floor((2Mdy - dx - B) / 2dx) + 1 + = floor((2Mdy - dx - B + 2dx) / 2dx) + = floor((2Mdy + dx - B) / 2dx) + +Case 1b: X major, ending X coordinate moved to M steps + +Same derivations as Case 1, but we want the largest N that satisfies +the equations, so we use the <= inequality: + + N = floor((2Mdy + dx - B) / 2dx) + +Case 2: X major, ending X coordinate moved by M steps + + -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0 + -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0 + -2dx <= 2Ndx - 2Mdy - dx - B < 0 + 2Ndx >= 2Mdy + dx + B - 2dx 2Ndx < 2Mdy + dx + B + 2Ndx >= 2Mdy - dx + B N < (2Mdy + dx + B) / 2dx + N >= (2Mdy - dx + B) / 2dx + +Since we are trying to find the highest number of Y steps that +satisfies these equations, we need to find the smallest N, so +we should use the >= inequality to find the smallest: + + N = ceiling((2Mdy - dx + B) / 2dx) + = floor((2Mdy - dx + B + 2dx - 1) / 2dx) + = floor((2Mdy + dx + B - 1) / 2dx) + +Case 2b: X major, starting X coordinate moved to M steps from end + +Same derivations as Case 2, but we want the smallest number of Y +steps, so we want the highest N, so we use the < inequality: + + N = ceiling((2Mdy + dx + B) / 2dx) - 1 + = floor((2Mdy + dx + B + 2dx - 1) / 2dx) - 1 + = floor((2Mdy + dx + B + 2dx - 1 - 2dx) / 2dx) + = floor((2Mdy + dx + B - 1) / 2dx) + +Case 3: Y major, starting X coordinate moved by M steps + + -2dy <= 2Ndx - 2Mdy - dy - B < 0 + 2Ndx >= 2Mdy + dy + B - 2dy 2Ndx < 2Mdy + dy + B + 2Ndx >= 2Mdy - dy + B N < (2Mdy + dy + B) / 2dx + N >= (2Mdy - dy + B) / 2dx + +Since we are trying to find the smallest N that satisfies these +equations, we should use the >= inequality to find the smallest: + + N = ceiling((2Mdy - dy + B) / 2dx) + = floor((2Mdy - dy + B + 2dx - 1) / 2dx) + = floor((2Mdy - dy + B - 1) / 2dx) + 1 + +Case 3b: Y major, ending X coordinate moved to M steps + +Same derivations as Case 3, but we want the largest N that satisfies +the equations, so we use the < inequality: + + N = ceiling((2Mdy + dy + B) / 2dx) - 1 + = floor((2Mdy + dy + B + 2dx - 1) / 2dx) - 1 + = floor((2Mdy + dy + B + 2dx - 1 - 2dx) / 2dx) + = floor((2Mdy + dy + B - 1) / 2dx) + +Case 4: Y major, ending X coordinate moved by M steps + + -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0 + -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0 + -2dy <= 2Mdy - 2Ndx - dy - B < 0 + 2Ndx <= 2Mdy - dy - B + 2dy 2Ndx > 2Mdy - dy - B + 2Ndx <= 2Mdy + dy - B N > (2Mdy - dy - B) / 2dx + N <= (2Mdy + dy - B) / 2dx + +Since we are trying to find the highest number of Y steps that +satisfies these equations, we need to find the smallest N, so +we should use the > inequality to find the smallest: + + N = floor((2Mdy - dy - B) / 2dx) + 1 + +Case 4b: Y major, starting X coordinate moved to M steps from end + +Same analysis as Case 4, but we want the smallest number of Y steps +which means the largest N, so we use the <= inequality: + + N = floor((2Mdy + dy - B) / 2dx) + +Now let's try the Y coordinates, we have the same 4 cases. + +Case 5: X major, starting Y coordinate moved by N steps + + -2dx <= 2Mdy - 2Ndx - dx - B < 0 + 2Mdy >= 2Ndx + dx + B - 2dx 2Mdy < 2Ndx + dx + B + 2Mdy >= 2Ndx - dx + B M < (2Ndx + dx + B) / 2dy + M >= (2Ndx - dx + B) / 2dy + +Since we are trying to find the smallest M, we use the >= inequality: + + M = ceiling((2Ndx - dx + B) / 2dy) + = floor((2Ndx - dx + B + 2dy - 1) / 2dy) + = floor((2Ndx - dx + B - 1) / 2dy) + 1 + +Case 5b: X major, ending Y coordinate moved to N steps + +Same derivations as Case 5, but we want the largest M that satisfies +the equations, so we use the < inequality: + + M = ceiling((2Ndx + dx + B) / 2dy) - 1 + = floor((2Ndx + dx + B + 2dy - 1) / 2dy) - 1 + = floor((2Ndx + dx + B + 2dy - 1 - 2dy) / 2dy) + = floor((2Ndx + dx + B - 1) / 2dy) + +Case 6: X major, ending Y coordinate moved by N steps + + -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0 + -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0 + -2dx <= 2Ndx - 2Mdy - dx - B < 0 + 2Mdy <= 2Ndx - dx - B + 2dx 2Mdy > 2Ndx - dx - B + 2Mdy <= 2Ndx + dx - B M > (2Ndx - dx - B) / 2dy + M <= (2Ndx + dx - B) / 2dy + +Largest # of X steps means smallest M, so use the > inequality: + + M = floor((2Ndx - dx - B) / 2dy) + 1 + +Case 6b: X major, starting Y coordinate moved to N steps from end + +Same derivations as Case 6, but we want the smallest # of X steps +which means the largest M, so use the <= inequality: + + M = floor((2Ndx + dx - B) / 2dy) + +Case 7: Y major, starting Y coordinate moved by N steps + + -2dy <= 2Ndx - 2Mdy - dy - B < 0 + 2Mdy <= 2Ndx - dy - B + 2dy 2Mdy > 2Ndx - dy - B + 2Mdy <= 2Ndx + dy - B M > (2Ndx - dy - B) / 2dy + M <= (2Ndx + dy - B) / 2dy + +To find the smallest M, use the > inequality: + + M = floor((2Ndx - dy - B) / 2dy) + 1 + = floor((2Ndx - dy - B + 2dy) / 2dy) + = floor((2Ndx + dy - B) / 2dy) + +Case 7b: Y major, ending Y coordinate moved to N steps + +Same derivations as Case 7, but we want the largest M that satisfies +the equations, so use the <= inequality: + + M = floor((2Ndx + dy - B) / 2dy) + +Case 8: Y major, ending Y coordinate moved by N steps + + -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0 + -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0 + -2dy <= 2Mdy - 2Ndx - dy - B < 0 + 2Mdy >= 2Ndx + dy + B - 2dy 2Mdy < 2Ndx + dy + B + 2Mdy >= 2Ndx - dy + B M < (2Ndx + dy + B) / 2dy + M >= (2Ndx - dy + B) / 2dy + +To find the highest X steps, find the smallest M, use the >= inequality: + + M = ceiling((2Ndx - dy + B) / 2dy) + = floor((2Ndx - dy + B + 2dy - 1) / 2dy) + = floor((2Ndx + dy + B - 1) / 2dy) + +Case 8b: Y major, starting Y coordinate moved to N steps from the end + +Same derivations as Case 8, but we want to find the smallest # of X +steps which means the largest M, so we use the < inequality: + + M = ceiling((2Ndx + dy + B) / 2dy) - 1 + = floor((2Ndx + dy + B + 2dy - 1) / 2dy) - 1 + = floor((2Ndx + dy + B + 2dy - 1 - 2dy) / 2dy) + = floor((2Ndx + dy + B - 1) / 2dy) + +So, our equations are: + + 1: X major move x1 to x1+M floor((2Mdy + dx - B) / 2dx) + 1b: X major move x2 to x1+M floor((2Mdy + dx - B) / 2dx) + 2: X major move x2 to x2-M floor((2Mdy + dx + B - 1) / 2dx) + 2b: X major move x1 to x2-M floor((2Mdy + dx + B - 1) / 2dx) + + 3: Y major move x1 to x1+M floor((2Mdy - dy + B - 1) / 2dx) + 1 + 3b: Y major move x2 to x1+M floor((2Mdy + dy + B - 1) / 2dx) + 4: Y major move x2 to x2-M floor((2Mdy - dy - B) / 2dx) + 1 + 4b: Y major move x1 to x2-M floor((2Mdy + dy - B) / 2dx) + + 5: X major move y1 to y1+N floor((2Ndx - dx + B - 1) / 2dy) + 1 + 5b: X major move y2 to y1+N floor((2Ndx + dx + B - 1) / 2dy) + 6: X major move y2 to y2-N floor((2Ndx - dx - B) / 2dy) + 1 + 6b: X major move y1 to y2-N floor((2Ndx + dx - B) / 2dy) + + 7: Y major move y1 to y1+N floor((2Ndx + dy - B) / 2dy) + 7b: Y major move y2 to y1+N floor((2Ndx + dy - B) / 2dy) + 8: Y major move y2 to y2-N floor((2Ndx + dy + B - 1) / 2dy) + 8b: Y major move y1 to y2-N floor((2Ndx + dy + B - 1) / 2dy) + +We have the following constraints on all of the above terms: + + 0 < M,N <= 2^15 2^15 can be imposed by miZeroClipLine + 0 <= dx/dy <= 2^16 - 1 + 0 <= B <= 1 + +The floor in all of the above equations can be accomplished with a +simple C divide operation provided that both numerator and denominator +are positive. + +Since dx,dy >= 0 and since moving an X coordinate implies that dx != 0 +and moving a Y coordinate implies dy != 0, we know that the denominators +are all > 0. + +For all lines, (-B) and (B-1) are both either 0 or -1, depending on the +bias. Thus, we have to show that the 2MNdxy +/- dxy terms are all >= 1 +or > 0 to prove that the numerators are positive (or zero). + +For X Major lines we know that dx > 0 and since 2Mdy is >= 0 due to the +constraints, the first four equations all have numerators >= 0. + +For the second four equations, M > 0, so 2Mdy >= 2dy so (2Mdy - dy) >= dy +So (2Mdy - dy) > 0, since they are Y major lines. Also, (2Mdy + dy) >= 3dy +or (2Mdy + dy) > 0. So all of their numerators are >= 0. + +For the third set of four equations, N > 0, so 2Ndx >= 2dx so (2Ndx - dx) +>= dx > 0. Similarly (2Ndx + dx) >= 3dx > 0. So all numerators >= 0. + +For the fourth set of equations, dy > 0 and 2Ndx >= 0, so all numerators +are > 0. + +To consider overflow, consider the case of 2 * M,N * dx,dy + dx,dy. This +is bounded <= 2 * 2^15 * (2^16 - 1) + (2^16 - 1) + <= 2^16 * (2^16 - 1) + (2^16 - 1) + <= 2^32 - 2^16 + 2^16 - 1 + <= 2^32 - 1 +Since the (-B) and (B-1) terms are all 0 or -1, the maximum value of +the numerator is therefore (2^32 - 1), which does not overflow an unsigned +32 bit variable. + +*/ + +/* Bit codes for the terms of the 16 clipping equations defined below. */ + +#define T_2NDX (1 << 0) +#define T_2MDY (0) /* implicit term */ +#define T_DXNOTY (1 << 1) +#define T_DYNOTX (0) /* implicit term */ +#define T_SUBDXORY (1 << 2) +#define T_ADDDX (T_DXNOTY) /* composite term */ +#define T_SUBDX (T_DXNOTY | T_SUBDXORY) /* composite term */ +#define T_ADDDY (T_DYNOTX) /* composite term */ +#define T_SUBDY (T_DYNOTX | T_SUBDXORY) /* composite term */ +#define T_BIASSUBONE (1 << 3) +#define T_SUBBIAS (0) /* implicit term */ +#define T_DIV2DX (1 << 4) +#define T_DIV2DY (0) /* implicit term */ +#define T_ADDONE (1 << 5) + +/* Bit masks defining the 16 equations used in miZeroClipLine. */ + +#define EQN1 (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX) +#define EQN1B (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX) +#define EQN2 (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX) +#define EQN2B (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX) + +#define EQN3 (T_2MDY | T_SUBDY | T_BIASSUBONE | T_DIV2DX | T_ADDONE) +#define EQN3B (T_2MDY | T_ADDDY | T_BIASSUBONE | T_DIV2DX) +#define EQN4 (T_2MDY | T_SUBDY | T_SUBBIAS | T_DIV2DX | T_ADDONE) +#define EQN4B (T_2MDY | T_ADDDY | T_SUBBIAS | T_DIV2DX) + +#define EQN5 (T_2NDX | T_SUBDX | T_BIASSUBONE | T_DIV2DY | T_ADDONE) +#define EQN5B (T_2NDX | T_ADDDX | T_BIASSUBONE | T_DIV2DY) +#define EQN6 (T_2NDX | T_SUBDX | T_SUBBIAS | T_DIV2DY | T_ADDONE) +#define EQN6B (T_2NDX | T_ADDDX | T_SUBBIAS | T_DIV2DY) + +#define EQN7 (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY) +#define EQN7B (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY) +#define EQN8 (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY) +#define EQN8B (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY) + +/* miZeroClipLine + * + * returns: 1 for partially clipped line + * -1 for completely clipped line + * + */ +int +miZeroClipLine(xmin, ymin, xmax, ymax, + new_x1, new_y1, new_x2, new_y2, + adx, ady, + pt1_clipped, pt2_clipped, octant, bias, oc1, oc2) + int xmin, ymin, xmax, ymax; + int *new_x1, *new_y1, *new_x2, *new_y2; + int *pt1_clipped, *pt2_clipped; + unsigned int adx, ady; + int octant; + unsigned int bias; + int oc1, oc2; +{ + int swapped = 0; + int clipDone = 0; + guint32 utmp; + int clip1, clip2; + int x1, y1, x2, y2; + int x1_orig, y1_orig, x2_orig, y2_orig; + int xmajor; + int negslope, anchorval; + unsigned int eqn; + + x1 = x1_orig = *new_x1; + y1 = y1_orig = *new_y1; + x2 = x2_orig = *new_x2; + y2 = y2_orig = *new_y2; + + clip1 = 0; + clip2 = 0; + + xmajor = IsXMajorOctant(octant); + bias = ((bias >> octant) & 1); + + while (1) + { + if ((oc1 & oc2) != 0) /* trivial reject */ + { + clipDone = -1; + clip1 = oc1; + clip2 = oc2; + break; + } + else if ((oc1 | oc2) == 0) /* trivial accept */ + { + clipDone = 1; + if (swapped) + { + SWAPINT_PAIR(x1, y1, x2, y2); + SWAPINT(clip1, clip2); + } + break; + } + else /* have to clip */ + { + /* only clip one point at a time */ + if (oc1 == 0) + { + SWAPINT_PAIR(x1, y1, x2, y2); + SWAPINT_PAIR(x1_orig, y1_orig, x2_orig, y2_orig); + SWAPINT(oc1, oc2); + SWAPINT(clip1, clip2); + swapped = !swapped; + } + + clip1 |= oc1; + if (oc1 & OUT_LEFT) + { + negslope = IsYDecreasingOctant(octant); + utmp = xmin - x1_orig; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN2 : EQN1; + else + eqn = (swapped) ? EQN4 : EQN3; + anchorval = y1_orig; + } + else /* clip based on far endpt */ + { + utmp = x2_orig - xmin; + if (xmajor) + eqn = (swapped) ? EQN1B : EQN2B; + else + eqn = (swapped) ? EQN3B : EQN4B; + anchorval = y2_orig; + negslope = !negslope; + } + x1 = xmin; + } + else if (oc1 & OUT_ABOVE) + { + negslope = IsXDecreasingOctant(octant); + utmp = ymin - y1_orig; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN6 : EQN5; + else + eqn = (swapped) ? EQN8 : EQN7; + anchorval = x1_orig; + } + else /* clip based on far endpt */ + { + utmp = y2_orig - ymin; + if (xmajor) + eqn = (swapped) ? EQN5B : EQN6B; + else + eqn = (swapped) ? EQN7B : EQN8B; + anchorval = x2_orig; + negslope = !negslope; + } + y1 = ymin; + } + else if (oc1 & OUT_RIGHT) + { + negslope = IsYDecreasingOctant(octant); + utmp = x1_orig - xmax; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN2 : EQN1; + else + eqn = (swapped) ? EQN4 : EQN3; + anchorval = y1_orig; + } + else /* clip based on far endpt */ + { + /* + * Technically since the equations can handle + * utmp == 32768, this overflow code isn't + * needed since X11 protocol can't generate + * a line which goes more than 32768 pixels + * to the right of a clip rectangle. + */ + utmp = xmax - x2_orig; + if (xmajor) + eqn = (swapped) ? EQN1B : EQN2B; + else + eqn = (swapped) ? EQN3B : EQN4B; + anchorval = y2_orig; + negslope = !negslope; + } + x1 = xmax; + } + else if (oc1 & OUT_BELOW) + { + negslope = IsXDecreasingOctant(octant); + utmp = y1_orig - ymax; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN6 : EQN5; + else + eqn = (swapped) ? EQN8 : EQN7; + anchorval = x1_orig; + } + else /* clip based on far endpt */ + { + /* + * Technically since the equations can handle + * utmp == 32768, this overflow code isn't + * needed since X11 protocol can't generate + * a line which goes more than 32768 pixels + * below the bottom of a clip rectangle. + */ + utmp = ymax - y2_orig; + if (xmajor) + eqn = (swapped) ? EQN5B : EQN6B; + else + eqn = (swapped) ? EQN7B : EQN8B; + anchorval = x2_orig; + negslope = !negslope; + } + y1 = ymax; + } + + if (swapped) + negslope = !negslope; + + utmp <<= 1; /* utmp = 2N or 2M */ + if (eqn & T_2NDX) + utmp = (utmp * adx); + else /* (eqn & T_2MDY) */ + utmp = (utmp * ady); + if (eqn & T_DXNOTY) + if (eqn & T_SUBDXORY) + utmp -= adx; + else + utmp += adx; + else /* (eqn & T_DYNOTX) */ + if (eqn & T_SUBDXORY) + utmp -= ady; + else + utmp += ady; + if (eqn & T_BIASSUBONE) + utmp += bias - 1; + else /* (eqn & T_SUBBIAS) */ + utmp -= bias; + if (eqn & T_DIV2DX) + utmp /= (adx << 1); + else /* (eqn & T_DIV2DY) */ + utmp /= (ady << 1); + if (eqn & T_ADDONE) + utmp++; + + if (negslope) + utmp = -utmp; + + if (eqn & T_2NDX) /* We are calculating X steps */ + x1 = anchorval + utmp; + else /* else, Y steps */ + y1 = anchorval + utmp; + + oc1 = 0; + MIOUTCODES(oc1, x1, y1, xmin, ymin, xmax, ymax); + } + } + + *new_x1 = x1; + *new_y1 = y1; + *new_x2 = x2; + *new_y2 = y2; + + *pt1_clipped = clip1; + *pt2_clipped = clip2; + + return clipDone; +} diff --git a/gdk/linux-fb/mizerline.c b/gdk/linux-fb/mizerline.c new file mode 100644 index 0000000000..4ea91f357c --- /dev/null +++ b/gdk/linux-fb/mizerline.c @@ -0,0 +1,332 @@ +/* $XFree86: xc/programs/Xserver/mi/mizerline.c,v 3.4 1999/10/14 04:43:16 dawes Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $TOG: mizerline.c /main/18 1998/02/09 14:49:45 kaleb $ */ + +#include "mi.h" +#include "miline.h" + +/* Draw lineSolid, fillStyle-independent zero width lines. + * + * Must keep X and Y coordinates in "ints" at least until after they're + * translated and clipped to accomodate CoordModePrevious lines with very + * large coordinates. + * + * Draws the same pixels regardless of sign(dx) or sign(dy). + * + * Ken Whaley + * + */ + +/* largest positive value that can fit into a component of a point. + * Assumes that the point structure is {type x, y;} where type is + * a signed type. + */ +#define MAX_COORDINATE ((1 << (((sizeof(GdkPoint) >> 1) << 3) - 1)) - 1) + +#define MI_OUTPUT_POINT(xx, yy)\ +{\ + if ( !new_span && yy == current_y)\ + {\ + if (xx < spans->x)\ + spans->x = xx;\ + spans->width++; \ + }\ + else\ + {\ + ++Nspans;\ + ++spans;\ + spans->x = xx;\ + spans->y = yy;\ + spans->width = 1; \ + spans->height = 1; \ + current_y = yy;\ + new_span = FALSE;\ + }\ +} + +void +miZeroLine(pDraw, pGC, mode, npt, pptInit) + GdkDrawable* pDraw; + GdkGC* pGC; + int mode; /* Origin or Previous */ + int npt; /* number of points */ + GdkPoint* pptInit; +{ + int Nspans, current_y; + GdkPoint* ppt; + GdkRectangle* pspanInit, *spans; + int list_len; + int xleft, ytop, xright, ybottom; + int new_x1, new_y1, new_x2, new_y2; + int x, y, x1, y1, x2, y2, xstart, ystart; + int oc1, oc2; + int result; + int pt1_clipped, pt2_clipped = 0; + gboolean new_span; + int signdx, signdy; + int clipdx, clipdy; + int width, height; + int adx, ady; + int octant; + unsigned int bias = miGetZeroLineBias(); + int e, e1, e2, e3; /* Bresenham error terms */ + int length; /* length of lines == # of pixels on major axis */ + + xleft = 0; + ytop = 0; + xright = GDK_DRAWABLE_P(pDraw)->width - 1; + ybottom = GDK_DRAWABLE_P(pDraw)->height - 1; + + /* it doesn't matter whether we're in drawable or screen coordinates, + * FillSpans simply cannot take starting coordinates outside of the + * range of a GdkPoint component. + */ + + /* since we're clipping to the drawable's boundaries & coordinate + * space boundaries, we're guaranteed that the larger of width/height + * is the longest span we'll need to output + */ + width = xright - xleft + 1; + height = ybottom - ytop + 1; + list_len = (height >= width) ? height : width; + pspanInit = (GdkRectangle*)ALLOCATE_LOCAL(list_len * sizeof(GdkRectangle)); + if (!pspanInit) + return; + + Nspans = 0; + new_span = TRUE; + spans = pspanInit - 1; + ppt = pptInit; + + xstart = ppt->x; + ystart = ppt->y; + + /* x2, y2, oc2 copied to x1, y1, oc1 at top of loop to simplify + * iteration logic + */ + x2 = xstart; + y2 = ystart; + oc2 = 0; + MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); + + while (--npt > 0) + { + if (Nspans > 0) + gdk_fb_fill_spans(pDraw, pGC, pspanInit, Nspans); + Nspans = 0; + new_span = TRUE; + spans = pspanInit - 1; + + x1 = x2; + y1 = y2; + oc1 = oc2; + ++ppt; + + x2 = ppt->x; + y2 = ppt->y; + + oc2 = 0; + MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); + + CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant); + + if (adx > ady) + { + e1 = ady << 1; + e2 = e1 - (adx << 1); + e = e1 - adx; + length = adx; /* don't draw endpoint in main loop */ + + FIXUP_ERROR(e, octant, bias); + + new_x1 = x1; + new_y1 = y1; + new_x2 = x2; + new_y2 = y2; + pt1_clipped = 0; + pt2_clipped = 0; + + if ((oc1 | oc2) != 0) + { + result = miZeroClipLine(xleft, ytop, xright, ybottom, + &new_x1, &new_y1, &new_x2, &new_y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2); + if (result == -1) + continue; + + length = abs(new_x2 - new_x1); + + /* if we've clipped the endpoint, always draw the full length + * of the segment, because then the capstyle doesn't matter + */ + if (pt2_clipped) + length++; + + if (pt1_clipped) + { + /* must calculate new error terms */ + clipdx = abs(new_x1 - x1); + clipdy = abs(new_y1 - y1); + e += (clipdy * e2) + ((clipdx - clipdy) * e1); + } + } + + /* draw the segment */ + + x = new_x1; + y = new_y1; + + e3 = e2 - e1; + e = e - e1; + + while (length--) + { + MI_OUTPUT_POINT(x, y); + e += e1; + if (e >= 0) + { + y += signdy; + e += e3; + } + x += signdx; + } + } + else /* Y major line */ + { + e1 = adx << 1; + e2 = e1 - (ady << 1); + e = e1 - ady; + length = ady; /* don't draw endpoint in main loop */ + + SetYMajorOctant(octant); + FIXUP_ERROR(e, octant, bias); + + new_x1 = x1; + new_y1 = y1; + new_x2 = x2; + new_y2 = y2; + pt1_clipped = 0; + pt2_clipped = 0; + + if ((oc1 | oc2) != 0) + { + result = miZeroClipLine(xleft, ytop, xright, ybottom, + &new_x1, &new_y1, &new_x2, &new_y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2); + if (result == -1) + continue; + + length = abs(new_y2 - new_y1); + + /* if we've clipped the endpoint, always draw the full length + * of the segment, because then the capstyle doesn't matter + */ + if (pt2_clipped) + length++; + + if (pt1_clipped) + { + /* must calculate new error terms */ + clipdx = abs(new_x1 - x1); + clipdy = abs(new_y1 - y1); + e += (clipdx * e2) + ((clipdy - clipdx) * e1); + } + } + + /* draw the segment */ + + x = new_x1; + y = new_y1; + + e3 = e2 - e1; + e = e - e1; + + while (length--) + { + MI_OUTPUT_POINT(x, y); + e += e1; + if (e >= 0) + { + x += signdx; + e += e3; + } + y += signdy; + } + } + } + + /* only do the capnotlast check on the last segment + * and only if the endpoint wasn't clipped. And then, if the last + * point is the same as the first point, do not draw it, unless the + * line is degenerate + */ + if ( (! pt2_clipped) && (GDK_GC_FBDATA(pGC)->values.cap_style != GDK_CAP_NOT_LAST) && + (((xstart != x2) || (ystart != y2)) || (ppt == pptInit + 1))) + { + MI_OUTPUT_POINT(x, y); + } + + if (Nspans > 0) + gdk_fb_fill_spans(pDraw, pGC, pspanInit, Nspans); + + DEALLOCATE_LOCAL(pspanInit); +} + +void +miZeroDashLine(dst, pgc, mode, nptInit, pptInit) +GdkDrawable* dst; +GdkGC* pgc; +int mode; +int nptInit; /* number of points in polyline */ +GdkPoint *pptInit; /* points in the polyline */ +{ + /* XXX kludge until real zero-width dash code is written */ + GDK_GC_FBDATA(pgc)->values.line_width = 1; + miWideDash (dst, pgc, mode, nptInit, pptInit); + GDK_GC_FBDATA(pgc)->values.line_width = 0; +}