]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement dots and dashes on Android
authorPo Lu <luangruo@yahoo.com>
Sun, 28 Apr 2024 08:58:58 +0000 (16:58 +0800)
committerEshel Yaron <me@eshelyaron.com>
Mon, 29 Apr 2024 15:05:42 +0000 (17:05 +0200)
* java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine)
(measureLine, polyDashPattern): New function.
(perform): Delegate to polyDashPattern if the line style is not
LineSolid.  Also simplify now that anti-aliasing need no longer
be taken into account.

* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Mention
omission in commentary.

* java/org/gnu/emacs/EmacsGC.java (EmacsGC): Disable
anti-aliasing in default paint object.
<line_style, line>: New fields.
(markDirty): Apply stroke width.

* src/android.c (android_init_emacs_gc_class): Initialize new
fields.
(android_create_gc, android_free_gc, android_change_gc)
(android_set_dashes, android_get_gc_values):

* src/androidgui.h (enum android_line_style)
(enum android_gc_value_mask, struct android_gc): Introduce line
style, width, dash offset and dash GC attributes.

* src/androidterm.c (android_draw_dash, android_fill_underline)
(android_draw_glyph_string): Port from X.

* src/xterm.c (x_draw_dash): Delete redundant code.

(cherry picked from commit e658a6938e3b7a8a7c0be8b74fbd885787c26df6)

java/org/gnu/emacs/EmacsDrawLine.java
java/org/gnu/emacs/EmacsDrawRectangle.java
java/org/gnu/emacs/EmacsGC.java
src/android.c
src/androidgui.h
src/androidterm.c
src/xterm.c

index 61b7d54d63cc1687a0139c95917d866e9cfe5665..a49fe96c26ef0e01372daf8e7445b8e5eb202b1a 100644 (file)
@@ -25,6 +25,97 @@ import android.graphics.Rect;
 
 public final class EmacsDrawLine
 {
+  /* Return the normalized slope and magnitude of a line whose extrema
+     are DX and DY removed, on the X and Y axes respectively, from its
+     origin point.  */
+
+  private static float[]
+  measureLine (float dx, float dy)
+  {
+    float hypot;
+
+    if (dx == 0f && dy == 0f)
+      return new float[] { 0f, 0f, 0f, };
+
+    if (dx == 0f)
+      return new float[] { 0f, dy > 0f ? 1f : -1f, Math.abs (dy), };
+    else if (dy == 0f)
+      return new float[] { dx > 0f ? 1f : -1f, 0f, Math.abs (dx), };
+    else
+      {
+       hypot = (float) Math.hypot (dx, dy);
+       return new float[] { dx / hypot, dy / hypot, hypot, };
+      }
+  }
+
+  private static void
+  polyDashPattern (EmacsGC gc, Canvas canvas, Paint paint, float x0,
+                  float y0, float x1, float y1)
+  {
+    int patternTotal, i, offset;
+    float dx, dy, mag, dash_mag, rem, lx1, ly1;
+    float[] measured;
+    boolean which;
+
+    /* Compute the total length of this pattern.  */
+    patternTotal = 0;
+    for (i = 0; i < gc.dashes.length; ++i)
+      patternTotal += gc.dashes[i];
+    if ((gc.dashes.length & 1) != 0)
+      patternTotal += patternTotal;
+
+    /* Subtract as much of the offset as does not contribute to the
+       phase at the first pixel of the line.  */
+    offset = gc.dash_offset % patternTotal;
+
+    /* Set I to the first dash that ought to be drawn and WHICH to its
+       phase.  */
+    i = 0;
+    which = true;
+    while (offset >= gc.dashes[i])
+      {
+       offset -= gc.dashes[i++];
+       if (i >= gc.dashes.length)
+         i = 0;
+       which = !which;
+      }
+
+    /* Compute the length of the first visible segment.  */
+    dash_mag = gc.dashes[i] - offset;
+
+    /* Compute the slope of the line.  */
+    dx = x1 - x0;
+    dy = y1 - y0;
+    measured = measureLine (dx, dy);
+    dx = measured[0];
+    dy = measured[1];
+    rem = mag = measured[2];
+    lx1 = x0;
+    ly1 = y0;
+
+    while (rem > 0f)
+      {
+       dash_mag = Math.min (dash_mag, rem);
+       rem -= dash_mag;
+
+       /* End of this segment.  */
+       x1 = (mag - rem) * dx + x0;
+       y1 = (mag - rem) * dy + y0;
+
+       if (which)
+         canvas.drawLine (lx1, ly1, x1, y1, paint);
+       which = !which;
+
+       /* Start of the next segment.  */
+       lx1 = x1;
+       ly1 = y1;
+       i++;
+       if (i >= gc.dashes.length)
+         i = 0;
+       dash_mag = gc.dashes[i];
+      }
+  }
+
   public static void
   perform (EmacsDrawable drawable, EmacsGC gc,
           int x, int y, int x2, int y2)
@@ -52,22 +143,20 @@ public final class EmacsDrawLine
     if (canvas == null)
       return;
 
-    paint.setStyle (Paint.Style.FILL);
-
     /* Since drawLine has PostScript style behavior, adjust the
        coordinates appropriately.
 
-       The left most pixel of a straight line is always partially
-       filled.  Patch it in manually.  */
+       The leftmost pixel of a straight line is always partially filled.
+       Patch it in manually.  */
 
     if (gc.clip_mask == null)
       {
-       canvas.drawLine ((float) x + 0.5f, (float) y + 0.5f,
-                        (float) x2 + 0.5f, (float) y2 + 0.5f,
-                        paint);
-
-       if (x2 > x)
-         canvas.drawRect (new Rect (x, y, x + 1, y + 1), paint);
+       if (gc.line_style != EmacsGC.GC_LINE_ON_OFF_DASH)
+         canvas.drawLine ((float) x, (float) y, (float) x2, (float) y2,
+                          paint);
+       else
+         polyDashPattern (gc, canvas, paint, (float) x, (float) y,
+                          (float) x2, (float) y2);
       }
 
     /* DrawLine with clip mask not implemented; it is not used by
index a8f68c6530a1e5467a1b60946aade0127e351a61..e40a7c1606855a3ce24c83deef8bbd4453ee4da0 100644 (file)
@@ -52,6 +52,9 @@ public final class EmacsDrawRectangle
     paint = gc.gcPaint;
     paint.setStyle (Paint.Style.STROKE);
 
+    /* This graphics request, in contrast to X, does not presently
+       respect the GC's line style.  */
+
     if (gc.clip_mask == null)
       /* Use canvas.drawRect with a RectF.  That seems to reliably
         get PostScript behavior.  */
index b2474c5bd76d8e71799b136d272750ea8ef297a1..ec2b9c9e475e3a18eff3761ccfaffdb5fa8af988 100644 (file)
@@ -46,12 +46,17 @@ public final class EmacsGC extends EmacsHandleObject
   public static final int GC_FILL_SOLID                        = 0;
   public static final int GC_FILL_OPAQUE_STIPPLED      = 1;
 
+  public static final int GC_LINE_SOLID                        = 0;
+  public static final int GC_LINE_ON_OFF_DASH          = 1;
+
   public static final Xfermode xorAlu, srcInAlu;
 
   public int function, fill_style;
   public int foreground, background;
   public int clip_x_origin, clip_y_origin;
   public int ts_origin_x, ts_origin_y;
+  public int line_style, line_width;
+  public int dashes[], dash_offset;
   public Rect clip_rects[], real_clip_rects[];
   public EmacsPixmap clip_mask, stipple;
   public Paint gcPaint;
@@ -89,6 +94,10 @@ public final class EmacsGC extends EmacsHandleObject
     foreground = 0;
     background = 0xffffff;
     gcPaint = new Paint ();
+
+    /* Android S and above enable anti-aliasing unless explicitly told
+       otherwise.  */
+    gcPaint.setAntiAlias (false);
   }
 
   /* Mark this GC as dirty.  Apply parameters to the paint and
@@ -119,7 +128,8 @@ public final class EmacsGC extends EmacsHandleObject
        clipRectID = ++clip_serial;
       }
 
-    gcPaint.setStrokeWidth (1f);
+    /* A line_width of 0 is equivalent to that of 1.  */
+    gcPaint.setStrokeWidth (line_width < 1 ? 1 : line_width);
     gcPaint.setColor (foreground | 0xff000000);
     gcPaint.setXfermode (function == GC_XOR
                         ? xorAlu : srcInAlu);
index 00a77fc398d8f7136c5420f85ce8a92cecc29e66..2777add50592a95bb16081ac86c005f9cb83dccb 100644 (file)
@@ -177,7 +177,9 @@ static jfieldID emacs_gc_function, emacs_gc_clip_rects;
 static jfieldID emacs_gc_clip_x_origin, emacs_gc_clip_y_origin;
 static jfieldID emacs_gc_stipple, emacs_gc_clip_mask;
 static jfieldID emacs_gc_fill_style, emacs_gc_ts_origin_x;
-static jfieldID emacs_gc_ts_origin_y;
+static jfieldID emacs_gc_ts_origin_y, emacs_gc_line_style;
+static jfieldID emacs_gc_line_width, emacs_gc_dash_offset;
+static jfieldID emacs_gc_dashes;
 
 /* The constructor and one function.  */
 static jmethodID emacs_gc_constructor, emacs_gc_mark_dirty;
@@ -3254,6 +3256,22 @@ android_init_emacs_gc_class (void)
     = (*android_java_env)->GetFieldID (android_java_env,
                                       emacs_gc_class,
                                       "ts_origin_y", "I");
+  emacs_gc_line_style
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "line_style", "I");
+  emacs_gc_line_width
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "line_width", "I");
+  emacs_gc_dash_offset
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "dash_offset", "I");
+  emacs_gc_dashes
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "dashes", "[I");
 }
 
 struct android_gc *
@@ -3285,6 +3303,11 @@ android_create_gc (enum android_gc_value_mask mask,
   gc->stipple = ANDROID_NONE;
   gc->ts_x_origin = 0;
   gc->ts_y_origin = 0;
+  gc->line_style = ANDROID_LINE_SOLID;
+  gc->line_width = 0;
+  gc->dash_offset = 0;
+  gc->dashes = NULL;
+  gc->n_segments = 0;
 
   if (!gc->gcontext)
     {
@@ -3323,6 +3346,7 @@ android_free_gc (struct android_gc *gc)
 {
   android_destroy_handle (gc->gcontext);
 
+  xfree (gc->dashes);
   xfree (gc->clip_rects);
   xfree (gc);
 }
@@ -3332,7 +3356,7 @@ android_change_gc (struct android_gc *gc,
                   enum android_gc_value_mask mask,
                   struct android_gc_values *values)
 {
-  jobject what, gcontext;
+  jobject what, gcontext, array;
   jboolean clip_changed;
 
   clip_changed = false;
@@ -3448,6 +3472,59 @@ android_change_gc (struct android_gc *gc,
       gc->ts_y_origin = values->ts_y_origin;
     }
 
+  if (mask & ANDROID_GC_LINE_STYLE)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_line_style,
+                                       values->line_style);
+      gc->line_style = values->line_style;
+    }
+
+  if (mask & ANDROID_GC_LINE_WIDTH)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_line_width,
+                                       values->line_width);
+      gc->line_width = values->line_width;
+    }
+
+  if (mask & ANDROID_GC_DASH_OFFSET)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_dash_offset,
+                                       values->dash_offset);
+      gc->dash_offset = values->dash_offset;
+    }
+
+  if (mask & ANDROID_GC_DASH_LIST)
+    {
+      /* Compare the new dash pattern with the old.  */
+      if (gc->dashes && gc->n_segments == 1
+         && gc->dashes[0] == values->dash)
+       /* If they be identical, nothing needs to change.  */
+       mask &= ~ANDROID_GC_DASH_LIST;
+      else
+       {
+         if (gc->n_segments != 1)
+           gc->dashes = xrealloc (gc->dashes, sizeof *gc->dashes);
+         gc->n_segments = 1;
+         gc->dashes[0] = values->dash;
+         array = (*android_java_env)->NewIntArray (android_java_env, 1);
+         android_exception_check ();
+         (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                                 array, 0, 1,
+                                                 (jint *) &values->dash);
+         (*android_java_env)->SetObjectField (android_java_env,
+                                              gcontext,
+                                              emacs_gc_dashes,
+                                              array);
+         ANDROID_DELETE_LOCAL_REF (array);
+       }
+    }
+
   if (mask)
     {
       (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
@@ -3539,6 +3616,75 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
          n_clip_rects * sizeof *gc->clip_rects);
 }
 
+void
+android_set_dashes (struct android_gc *gc, int dash_offset,
+                   int *dash_list, int n)
+{
+  int i;
+  jobject array, gcontext;
+
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  if (n == gc->n_segments
+      && (!gc->dashes || !memcmp (gc->dashes, dash_list,
+                                 sizeof *dash_list * n)))
+    /* No change in the dash list.  */
+    goto set_offset;
+
+  if (!n)
+    {
+      /* Reset the dash list to its initial empty state.  */
+      xfree (gc->dashes);
+      gc->dashes = NULL;
+      array = NULL;
+    }
+  else
+    {
+      /* If the size of the array has not changed, it can be reused.  */
+
+      if (n != gc->n_segments)
+       {
+         gc->dashes = xrealloc (gc->dashes, sizeof *gc->dashes * n);
+         array = (*android_java_env)->NewIntArray (android_java_env, n);
+         android_exception_check ();
+       }
+      else
+       array = (*android_java_env)->GetObjectField (android_java_env,
+                                                    gcontext,
+                                                    emacs_gc_dashes);
+
+      /* Copy the list of segments into both arrays.  */
+      for (i = 0; i < n; ++i)
+       gc->dashes[i] = dash_list[i];
+      verify (sizeof (int) == sizeof (jint));
+      (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                             array, 0, n,
+                                             (jint *) dash_list);
+    }
+
+  /* Replace the dash array in the GContext object if required.  */
+  if (n != gc->n_segments)
+    {
+      (*android_java_env)->SetObjectField (android_java_env,
+                                          gcontext,
+                                          emacs_gc_dashes,
+                                          array);
+      ANDROID_DELETE_LOCAL_REF (array);
+    }
+
+  gc->n_segments = n;
+
+ set_offset:
+  /* And the offset.  */
+  if (dash_offset != gc->dash_offset)
+    (*android_java_env)->SetIntField (android_java_env,
+                                     gcontext,
+                                     emacs_gc_dash_offset,
+                                     dash_offset);
+  gc->dash_offset = dash_offset;
+}
+
 void
 android_reparent_window (android_window w, android_window parent_handle,
                         int x, int y)
@@ -3690,7 +3836,8 @@ android_get_gc_values (struct android_gc *gc,
     values->ts_y_origin = gc->ts_y_origin;
 
   /* Fields involving handles are not used by Emacs, and thus not
-     implemented */
+     implemented.  In addition, the size of GCClipMask and GCDashList is
+     not static, precluding their retrieval.  */
 }
 
 void
index f941c7cc57794f08553f653b45cd8dda6ffa9cb0..5e4f6ec39894b23898139dad9279d93cbbc6d4b2 100644 (file)
@@ -71,6 +71,10 @@ enum android_gc_value_mask
     ANDROID_GC_FILL_STYLE        = (1 << 7),
     ANDROID_GC_TILE_STIP_X_ORIGIN = (1 << 8),
     ANDROID_GC_TILE_STIP_Y_ORIGIN = (1 << 9),
+    ANDROID_GC_LINE_STYLE        = (1 << 10),
+    ANDROID_GC_LINE_WIDTH        = (1 << 11),
+    ANDROID_GC_DASH_LIST         = (1 << 12),
+    ANDROID_GC_DASH_OFFSET       = (1 << 13),
   };
 
 enum android_fill_style
@@ -79,6 +83,12 @@ enum android_fill_style
     ANDROID_FILL_OPAQUE_STIPPLED = 1,
   };
 
+enum android_line_style
+  {
+    ANDROID_LINE_SOLID          = 0,
+    ANDROID_LINE_ON_OFF_DASH    = 1,
+  };
+
 enum android_window_value_mask
   {
     ANDROID_CW_BACK_PIXEL       = (1 << 1),
@@ -114,6 +124,18 @@ struct android_gc_values
 
   /* The tile-stipple X and Y origins.  */
   int ts_x_origin, ts_y_origin;
+
+  /* The line style.  */
+  enum android_line_style line_style;
+
+  /* The line width.  */
+  int line_width;
+
+  /* Offset in pixels into the dash pattern specified below.  */
+  int dash_offset;
+
+  /* One integer providing both segments of a even-odd dash pattern.  */
+  int dash;
 };
 
 /* X-like graphics context structure.  This is implemented in
@@ -152,6 +174,18 @@ struct android_gc
 
   /* The tile-stipple X and Y origins.  */
   int ts_x_origin, ts_y_origin;
+
+  /* The line style.  */
+  enum android_line_style line_style;
+
+  /* The line width.  */
+  int line_width;
+
+  /* Offset in pixels into the dash pattern specified below.  */
+  int dash_offset;
+
+  /* The segments of an even/odd dash pattern.  */
+  int *dashes, n_segments;
 };
 
 enum android_swap_action
@@ -675,6 +709,7 @@ extern void android_set_clip_rectangles (struct android_gc *,
                                         int, int,
                                         struct android_rectangle *,
                                         int);
+extern void android_set_dashes (struct android_gc *, int, int *, int);
 extern void android_change_gc (struct android_gc *,
                               enum android_gc_value_mask,
                               struct android_gc_values *);
index 25b7fa97ebcfd1bab157eda7bf6559a5b2d23371..4549941ee2eb890cba5b7d72f32005b598a6ff9c 100644 (file)
@@ -4031,6 +4031,80 @@ android_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
   s->char2b = NULL;
 }
 
+/* Draw a dashed underline of thickness THICKNESS and width WIDTH onto F
+   at a vertical offset of OFFSET from the position of the glyph string
+   S, with each segment SEGMENT pixels in length.  */
+
+static void
+android_draw_dash (struct frame *f, struct glyph_string *s, int width,
+                  int segment, int offset, int thickness)
+{
+  struct android_gc *gc;
+  struct android_gc_values gcv;
+  int y_center;
+
+  /* Configure the GC, the dash pattern and a suitable offset.  */
+  gc = s->gc;
+
+  gcv.line_style = ANDROID_LINE_ON_OFF_DASH;
+  gcv.line_width = thickness;
+  android_change_gc (s->gc, (ANDROID_GC_LINE_STYLE
+                            | ANDROID_GC_LINE_WIDTH), &gcv);
+  android_set_dashes (s->gc, s->x, &segment, 1);
+
+  /* Offset the origin of the line by half the line width. */
+  y_center = s->ybase + offset + thickness / 2;
+  android_draw_line (FRAME_ANDROID_WINDOW (f), gc,
+                    s->x, y_center, s->x + width, y_center);
+
+  /* Restore the initial line style.  */
+  gcv.line_style = ANDROID_LINE_SOLID;
+  gcv.line_width = 1;
+  android_change_gc (s->gc, (ANDROID_GC_LINE_STYLE
+                            | ANDROID_GC_LINE_WIDTH), &gcv);
+}
+
+/* Draw an underline of STYLE onto F at an offset of POSITION from the
+   baseline of the glyph string S, DECORATION_WIDTH in length, and
+   THICKNESS in height.  */
+
+static void
+android_fill_underline (struct frame *f, struct glyph_string *s,
+                       enum face_underline_type style, int position,
+                       int decoration_width, int thickness)
+{
+  int segment;
+
+  segment = thickness * 3;
+
+  switch (style)
+    {
+      /* FACE_UNDERLINE_DOUBLE_LINE is treated identically to SINGLE, as
+        the second line will be filled by another invocation of this
+        function.  */
+    case FACE_UNDERLINE_SINGLE:
+    case FACE_UNDERLINE_DOUBLE_LINE:
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             s->gc, s->x, s->ybase + position,
+                             decoration_width, thickness);
+      break;
+
+    case FACE_UNDERLINE_DOTS:
+      segment = thickness;
+      FALLTHROUGH;
+
+    case FACE_UNDERLINE_DASHES:
+      android_draw_dash (f, s, decoration_width, segment, position,
+                        thickness);
+      break;
+
+    case FACE_NO_UNDERLINE:
+    case FACE_UNDERLINE_WAVE:
+    default:
+      emacs_abort ();
+    }
+}
+
 static void
 android_draw_glyph_string (struct glyph_string *s)
 {
@@ -4167,16 +4241,13 @@ android_draw_glyph_string (struct glyph_string *s)
                   android_set_foreground (s->gc, xgcv.foreground);
                 }
             }
-          else if (s->face->underline == FACE_UNDERLINE_SINGLE
-                  || s->face->underline == FACE_UNDERLINE_DOUBLE_LINE)
+          else if (s->face->underline >= FACE_UNDERLINE_SINGLE)
             {
               unsigned long thickness, position;
-              int y;
 
               if (s->prev
-                 && ((s->prev->face->underline == FACE_UNDERLINE_SINGLE)
-                     || (s->prev->face->underline
-                         == FACE_UNDERLINE_DOUBLE_LINE))
+                 && (s->prev->face->underline != FACE_UNDERLINE_WAVE
+                     && s->prev->face->underline >= FACE_UNDERLINE_SINGLE)
                  && (s->prev->face->underline_at_descent_line_p
                      == s->face->underline_at_descent_line_p)
                  && (s->prev->face->underline_pixels_above_descent_line
@@ -4257,18 +4328,16 @@ android_draw_glyph_string (struct glyph_string *s)
              {
                struct android_gc_values xgcv;
 
-               y = s->ybase + position;
-               if (s->face->underline_defaulted_p)
-                 android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
-                                         s->x, y, decoration_width, thickness);
-               else
+               if (!s->face->underline_defaulted_p)
                  {
                    android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
                    android_set_foreground (s->gc, s->face->underline_color);
-                   android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
-                                           s->x, y, decoration_width, thickness);
                  }
 
+               android_fill_underline (s->f, s, s->face->underline,
+                                       position, decoration_width,
+                                       thickness);
+
                /* Place a second underline above the first if this was
                   requested in the face specification.  */
 
@@ -4276,9 +4345,8 @@ android_draw_glyph_string (struct glyph_string *s)
                  {
                    /* Compute the position of the second underline.  */
                    position = position - thickness - 1;
-                   y        = s->ybase + position;
-                   android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
-                                           s->gc, s->x, y, decoration_width,
+                   android_fill_underline (s->f, s, s->face->underline,
+                                           position, decoration_width,
                                            thickness);
                  }
 
index 505a3d9360a992d063b609edc191bb098e91e6b2..93d347a77efe6498c4688485ae14901b00a4b394 100644 (file)
@@ -10851,7 +10851,6 @@ x_draw_dash (struct frame *f, struct glyph_string *s, int width,
   gc = s->gc;
   display = FRAME_X_DISPLAY (f);
 
-  XGetGCValues (display, s->gc, GCLineStyle, &gcv);
   gcv.line_style = LineOnOffDash;
   gcv.line_width = thickness;
   XChangeGC (display, s->gc, GCLineStyle | GCLineWidth, &gcv);