/* $Header: llx.c,v 4.11 88/11/24 18:07:18 samarcq Exp $ */ #include #include #include #include #include extern char *malloc(); XAssocTable *XCreateAssocTable(); #define NULL 0 /* Les plans sur lesquels on travaille */ #define PLANES 1 /* Macro pour mettre un int dans la CVAL d'un symbole Lisp */ #define SETINTCVAL(symb,i) *symb = (i) & 0xffff #define LLLOCATOR(x,y) ((((x) & 0xffff) << 16) | ((y) & 0xffff)) /* les curseurs e'dite's par bitmap */ #define lelisp_width 16 #define lelisp_height 16 #define lelisp_x_hot 1 #define lelisp_y_hot 1 static short lelisp_bits[] = { 0x0000, 0x0002, 0x0006, 0x000e, 0x001e, 0x003e, 0x007e, 0x00fe, 0x001e, 0x0036, 0x0032, 0x0060, 0x0060, 0x00c0, 0x00c0, 0x0000}; #define lelisp_mask_width 16 #define lelisp_mask_height 16 #define lelisp_mask_x_hot 1 #define lelisp_mask_y_hot 1 static short lelisp_mask_bits[] = { 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x01ff, 0x007f, 0x007f, 0x00f3, 0x00f0, 0x01e0, 0x01e0, 0x01c0}; #define nocursor_width 16 #define nocursor_height 16 #define nocursor_x_hot 1 #define nocursor_y_hot 1 static short nocursor_bits[] = { 0x0000, 0x0002, 0x0006, 0x000e, 0x001e, 0x003e, 0x007e, 0x00fe, 0x001e, 0x0036, 0x0032, 0x0060, 0x0060, 0x00c0, 0x00c0, 0x0000}; #define nocursor_mask_width 16 #define nocursor_mask_height 16 #define nocursor_mask_x_hot 1 #define nocursor_mask_y_hot 1 static short nocursor_mask_bits[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* Les environnements graphiques */ struct grenv { FontInfo *font; Pattern linestyle; int mode; Pixmap pattern; int clipx; int clipy; int clipw; int cliph; int topwindowp; }; #define FONTINFO(n) (grenvs[n]->font) #define FONT(n) (grenvs[n]->font->id) #define MODE(n) (grenvs[n]->mode) #define LINE_STYLE(n) (grenvs[n]->linestyle) #define PATTERN(n) (grenvs[n]->pattern) #define CLIPX(n) (grenvs[n]->clipx) #define CLIPY(n) (grenvs[n]->clipy) #define CLIPW(n) (grenvs[n]->clipw) #define CLIPH(n) (grenvs[n]->cliph) #define TOPWINDOWP(n) (grenvs[n]->topwindowp) #define BORDERWIDTH 2 FontInfo *normalfont, *attributefont; Cursor cursor, nocursor; #define MAX_WINDOW 5000 /* nombre maximal de fene^tres */ int nwindow = 0; /* nombre de fene^tres construites */ int event_mode; /* le mode de la souris */ Display *display; /* l'e'cran ouvert */ Window windows[MAX_WINDOW]; /* le vecteur des Fene^tres */ Window clips[MAX_WINDOW]; /* les fene^tres transparentes pour le clip */ struct grenv *grenvs[MAX_WINDOW]; /* graphenvs */ Pixmap patterns[5]; Pattern line_style[4] = {0, DashedLine, DottedLine, XMakePattern(0xf900, 10, 1)}; XAssocTable *assoctable; Window keyboardgrabber = 0; Window downgrabber = 0; short down_dx, down_dy; int reversevideo; int ForePixel, BackPixel; Pixmap ForePixmap, BackPixmap; /* Les vertex pour les POLYxxx et le FILLAREA */ #define MAXVERTEX 1024 Vertex vertex[MAXVERTEX]; int * lispnil; int * lispxerrorsymbol; int * lisperrorsymbol; FontInfo * load_font (s) char *s; { return XOpenFont(s); } FontInfo * default_font1 () { return normalfont; } FontInfo * default_font2 () { return attributefont; } struct grenv *ge_default (topp) int topp; { struct grenv *ge; ge = (struct grenv *) malloc(sizeof(struct grenv)); ge->font = default_font1(); ge->linestyle = 0; ge->mode = GXcopy; ge->pattern = ForePixmap; ge->clipx = 0; ge->clipy = 0; ge->clipw = 0; ge->cliph = 0; ge->topwindowp = topp; return(ge); } /*ARGSUSED*/ llxError (dpy, rep) Display *dpy; XErrorEvent *rep; { pusharg(LLT_T, lispxerrorsymbol); pusharg(LLT_STRING, XErrDescrip(rep->error_code)); pusharg(LLT_T, (int *) XLookUpAssoc(assoctable, rep->window)); lispcall(LLT_T, 3, lisperrorsymbol); return(0); } ll_io () { printf("IT event\r\n"); /* appeler ensuite ll_mouse() dans llmachine.s */ } /* Initialisations generales. */ Display *bitprologue(xmax, ymax, planes, lenil, llxerror, llerror) int *xmax, *ymax, *lenil, *llxerror, *llerror, *planes; { char *def; display = XOpenDisplay (NULL); if (display == NULL) { perror ("bitprologue: can't open display"); return (0); } XSetDisplay (display); XErrorHandler(llxError); *xmax = (DisplayWidth () - 1) & 0xffff; *ymax = (DisplayHeight () - 1) & 0xffff; *planes = DisplayPlanes () & 0xffff; lispnil = lenil; lispxerrorsymbol = llxerror; lisperrorsymbol = llerror; /* reversevideo:on blanc sur fond noir. (reversevideo true) reversevideo:off noir sur fond blanc. (reversevideo false) default off, reversevideo false. */ def = XGetDefault("lelisp", "ReverseVideo"); if (!def) def = XGetDefault("xterm", "ReverseVideo"); if (!def) def = "off"; reversevideo = strcmp(def, "off"); /* vrai si def <> "off" */ ForePixel = reversevideo ? WhitePixel : BlackPixel ; BackPixel = reversevideo ? BlackPixel : WhitePixel ; ForePixmap = reversevideo ? WhitePixmap : BlackPixmap ; BackPixmap = reversevideo ? BlackPixmap : WhitePixmap ; def = XGetDefault("lelisp", "font"); if (!def) def = "vtsingle"; normalfont = load_font(def); def = XGetDefault("lelisp", "attributefont"); if (!def) def = "vtbold"; attributefont = load_font(def); cursor = XCreateCursor(lelisp_width, lelisp_height, lelisp_bits, lelisp_mask_bits, lelisp_x_hot, lelisp_y_hot, ForePixel, BackPixel, GXcopy); nocursor = XCreateCursor(nocursor_width, nocursor_height, nocursor_bits, nocursor_mask_bits, nocursor_x_hot, nocursor_y_hot, ForePixel, BackPixel, GXcopy); init_pattern(); assoctable = XCreateAssocTable(64); set_event_mode(0); return (display); } void bitepilogue () { XDestroyAssocTable(assoctable); XFreePixmap(patterns[4]); XFreePixmap(patterns[3]); XFreePixmap(patterns[2]); XFreePixmap(patterns[1]); XFreePixmap(patterns[0]); XFreeCursor(nocursor); XFreeCursor(cursor); XCloseFont(attributefont); XCloseFont(normalfont); XFlush(); XCloseDisplay(display); } /* Cre'ation d'une fene^tre */ int create_window(lisp, left, top, width, height, ti, hi, vi) int * lisp; int left, top, width, height; char *ti; { Window win, cli; struct grenv *ge; if (nwindow == MAX_WINDOW) return(-1); /* plus de fene^tres! */ /* X ne veut pas de fene^tres de taille 0 */ if (width == 0) width = 1; if (height == 0) height = 1; /* allocation du GRAPHENV */ ge = ge_default(1); /* cre'ation et allocation du FRAME */ win = XCreateWindow(RootWindow, left-BORDERWIDTH, top-BORDERWIDTH, width, height, BORDERWIDTH, hi ? ForePixmap : patterns[2], BackPixmap); XTileAbsolute(win); XStoreName(win, ti); cli = XCreateTransparency(win, 0, 0, width, height); XClipDrawThrough(cli); XDefineCursor(win, cursor); /* choix de l'input */ XSelectInput (win, 0); /* ne pas avoir le premier redisplay */ XMapWindow (cli); if (vi) XMapWindow (win); XSelectInput (win, 0x7fbd); /* mise a jour des vecteurs */ windows[nwindow] = win; clips[nwindow] = cli; grenvs[nwindow] = ge; XMakeAssoc(assoctable, win, lisp); /* initialisation du ge */ set_cur_mode (nwindow, 3); set_clip(nwindow, 0, 0, width, height); return (nwindow++); } create_subwindow(lisp, father, left, top, width, height, vi) int * lisp; int father, left, top, width, height, vi;{ Window win, cli; struct grenv *ge; if (nwindow == MAX_WINDOW) return(-1); /* plus de fene^tres! */ if (width == 0) width = 1; if (height == 0) height = 1; /* allocation du GRAPHENV */ ge = ge_default(0); /* cre'ation et alloc= nwindow)) return (-1); if (keyboardgrabber = windows[n]) keyboardgrabber = 0; if (downgrabber = windows[n]) downgrabber = 0; XDeleteAssoc(assoctable, windows[n]); XDestroyWindow (windows[n]); free (grenvs[n]); if (n != nwindow - 1) while (++n < nwindow) { windows[n - 1] = windows[n]; grenvs[n - 1] = grenvs[n]; clips[n - 1] = clips[n]; } nwindow--; return (0); } pop_window (n) int n; { XRaiseWindow (windows[n]); } /*ARGSUSED*/ move_behind_window (n1, n2) int n1, n2; { XLowerWindow (windows[n1]); } int * find_window (x, y, lispnil) int * lispnil; int x, y;{ int lx, ly, loc = LLLOCATOR(x, y); int *lispwin = NULL, *prevlispwin = NULL; Window win = RootWindow; do { prevlispwin = lispwin; XInterpretLocator (win, &lx, &ly, &win, loc); lispwin = (int *) XLookUpAssoc(assoctable, win); } while (lispwin); return prevlispwin ? prevlispwin : lispnil; } map_window (win, x, y, lx, ly) int win, x, y; int *lx, *ly; { Window pwin; int px, py; XInterpretLocator (windows[win], &px, &py, &pwin, LLLOCATOR(x, y)); *lx = px & 0xffff; *ly = py & 0xffff; } grab_keyboard (win, flag) int win, flag;{ if (flag) keyboardgrabber = windows[win]; else keyboardgrabber = 0; } /* grabbe uniquement le clavier, a` corriger */ grab_mouse (win, flag) int win, flag; { if (flag){ keyboardgrabber = windows[win]; downgrabber = windows[win]; } else { keyboardgrabber = 0; downgrabber = 0; } } draw_cn (win, x, y, cn) char cn; { char cnstring[2]; cnstring[0] = cn; XTextPad (clips[win], x-CLIPX(win), y-CLIPY(win), cnstring, 1, FONT (win), 0, 0, ForePixel, BackPixel, MODE (win), PLANES); } draw_substr (win, x, y, s, start, length) char *s; { XTextPad (clips[win], x-CLIPX(win), y-CLIPY(win), s + start, length, FONT (win), 0, 0, ForePixel, BackPixel, MODE (win), PLANES); } fast_draw_substr (win, x, y, s, start, length) struct LL_STRING *s; short win, x, y, start, length; { char *cs; cs = (&((s->ll_strobj)->ll_strfil))+start; XTextPad (clips[win], x-CLIPX(win), y-CLIPY(win), cs, length, FONT (win), 0, 0, ForePixel, BackPixel, MODE (win), PLANES); } width_substr (s, start, length, win) char *s; { int plength; char c = s[start + length]; s[start + length] = '\0'; plength = (win >= 0) ? XStringWidth (s+start, FONTINFO (win), 0, 0) : XStringWidth (s+start, default_font1(), 0, 0); s[start + length] = c; return (plength); } /*ARGSUSED*/ height_cn (cn, win) char cn; { return (win >= 0) ? FONTINFO (win) -> height : default_font1() -> height; } xinc_substr (s, start, length, win) char *s; { return (width_substr (s, start, length, win)); } tycleol(win, x, y) { x -= CLIPX(win); y -= CLIPY(win); XPixSet(clips[win], x, y, 2000, FONTINFO(win)->height, BackPixel); } draw_cursor (win, x, y, st) { x -= CLIPX(win); y -= CLIPY(win); XLine (clips[win], x, y, x, y+(FONTINFO(win)->height)-1, 1, 1, st ? ForePixel : BackPixel, GXcopy, PLANES); } clear_ge (win) { XClear (clips[win]); } /* Les environnements graphiques */ current_font (win, font) FontInfo * font; { FONTINFO (win) = font; } /* la souris */ #define MAXEVENT 256 XEvent events[MAXEVENT]; int ievent = 0, mevent = 0; fillevent(event) XEvent *event; { int i, pending; if (ievent == mevent) { pending = XPending(); if (pending > MAXEVENT) pending = MAXEVENT; if (pending == 0) pending = 1; for (i = 0; i < pending; i++) XNextEvent(&events[i]); ievent = 0; mevent = pending; } *event = events[ievent]; ievent++; } struct llevent { int code; int * window; int detail; int gx; int gy; int x; int y; int w; int h; }; i_peek_mouse (event) struct llevent *event; { XEvent rep; XPeekEvent (&rep); parse_event (event, &rep, 0); } i_read_mouse (event) struct llevent *event; { XEvent rep; XNextEvent(&rep); if (((rep.type) == EnterWindow) || ((rep.type) == LeaveWindow)) if (XPending()) XNextEvent(&rep); parse_event (event, &rep, 1); } flush_event () { XSync (1); } read_mouse (event) struct llevent *event; { Window subw; short state; int x, y; XQueryMouseButtons(RootWindow, &x, &y, &subw, &state); event->gx = x & 0xffff; event->gy = y & 0xffff; event->detail = (state & 0x0700) ? (state & 0xffff) : 0; } set_event_mode (mode) int mode; { if(mode & 0x2) XCompressEvents (); else XExpandEvents(); event_mode = mode; } int * eventp (lispnil) int * lispnil; { return XPending() ? (int *) 1 : lispnil; } #define ASCII 256 #define MOVE 257 #define DRAG 258 #define DOWN 259 #define UP 260 #define MODIFY 264 #define KILL 265 #define REPAINT 266 #define RELEASE 267 #define ENTERWINDOW 268 #define LEAVEWINDOW 269 #define UNMAPWINDOW 270 #define FOCUSCHANGE 271 #define CODEBIDON 272 parse_event (event, rep, readp) struct llevent *event; XEvent * rep; int readp; { /* readp est vrai si on est dans un READ-EVENT, faux dans un PEEK-EVENT */ int nbytes; char *bytes; int detail; /* L'e've'nement appartient a trois classes: - clavier - souris - fene^tre */ switch (rep -> type) { case MouseMoved: case ButtonPressed: case ButtonReleased: parse_mouse_event(event, rep, readp); break; case KeyPressed: case KeyReleased: parse_keyboard_event(event, rep, readp); break; default: parse_window_event(event, rep, readp); break; } } #define bit(i,mask) ((i & mask)? 1 : 0) parse_mouse_event(event, rep, readp) struct llevent *event; XEvent * rep; int readp; { int detail; /* recherche de la fene^tre: downgrabber ou window de l'evt */ event->window = (int *)XLookUpAssoc(assoctable, downgrabber ? downgrabber : rep->window); /* calcul du code et du detail */ detail = ((XMouseMovedEvent *) rep) -> detail; switch(rep -> type) { case MouseMoved: event -> detail = bit(detail, MiddleMask) + 2*bit(detail, RightMask); event -> code = detail & (LeftMask | MiddleMask | RightMask) ? DRAG : MOVE; break; case ButtonPressed: event->code = DOWN; if (readp) downgrabber = rep->window; event->detail = 2-(detail & 0xff); down_dx = (((XMouseMovedEvent *) rep) -> location >> 16) - ((XMouseMovedEvent *) rep) -> x; down_dy = (((XMouseMovedEvent *) rep) -> location & 0xffff) - ((XMouseMovedEvent *) rep) -> y; break; case ButtonReleased: event->code = UP; if (readp) downgrabber = 0; event->detail = 2-(detail & 0xff); break; } event->detail += 3*bit(detail, ShiftMask) + 6*bit(detail, ControlMask) + 9*bit(detail, MetaMask); set_x_y_gx_gy(event, rep); } parse_keyboard_event(event, rep, readp) struct llevent *event; XEvent * rep; int readp; { int nbytes; char *bytes; /* recherche de la fene^tre: keyboardgrabber ou window de l'evt */ event->window = (int *)XLookUpAssoc(assoctable, keyboardgrabber ? keyboardgrabber : rep->window); /* recheche du code et des caracteres */ switch (rep -> type) { case KeyPressed: bytes = XLookupMapping (rep, &nbytes); if (nbytes == 1){ event->detail = *bytes; event->code = ASCII; }else{ event->code = CODEBIDON; event->detail = '\33'; } break; case KeyReleased: event->code = RELEASE; break; } } parse_window_event(event, rep, readp) struct llevent *event; XEvent * rep; int readp; { /* recherche de la fene^tre: window de l'evt */ event->window = (int *)XLookUpAssoc(assoctable, rep -> window); switch (rep -> type) { case EnterWindow: event->code = ENTERWINDOW; break; case LeaveWindow: event->code = LEAVEWINDOW; break; case ExposeWindow: /* d'abord un resize */ event->code = MODIFY; event->x = (int) lispnil; event->y = (int) lispnil; event->w = ((XExposeEvent *) rep) -> width; event->h = ((XExposeEvent *) rep) -> height; /* qui sera suivi d'un repaint */ if (readp) { rep -> type = ExposeRegion; XPutBackEvent(rep); } break; case ExposeRegion: event->code = REPAINT; event->x = ((XExposeEvent *) rep) -> x; event->y = ((XExposeEvent *) rep) -> y; event->w = ((XExposeEvent *) rep) -> width; event->h = ((XExposeEvent *) rep) -> height; break; case UnmapWindow: event->code = UNMAPWINDOW; break; case FocusChange: event->code = FOCUSCHANGE; if (event->detail == LeaveWindow) event->window = lispnil; break; default: event->code = CODEBIDON; } } find_window_number (window) Window window; { int i; for (i = 0; (i gx = ((XMouseMovedEvent *) rep) -> location >> 16; event -> gy = ((XMouseMovedEvent *) rep) -> location & 0xffff; event -> x = event -> gx - down_dx; event -> y = event -> gy - down_dy; event->x &= 0xffff; event->y &= 0xffff; } /* le graphique */ int vecmode[16]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; int vecmodei[16]={15, 7, 11, 3, 13, 5, 9, 1, 14, 6, 10, 2, 12, 4, 8, 0}; set_cur_mode (win, n) { MODE (win) = reversevideo ? vecmode[n] : vecmodei[n]; } set_clip (win, x, y, w, h) { XConfigureWindow (clips[win], x, y, w, h); XLowerWindow(clips[win]); CLIPX(win) = x; CLIPY(win) = y; CLIPW(win) = w; CLIPH(win) = h; } draw_polymarker (win, n, vx, vy) int vx[], vy[]; { int next = 0; while (n) { vx = &vx[next]; vy = &vy[next]; next = fill_point (win, n, vx, vy); vertex_draw(win, vertex, 2*next); n -= next; } } fill_point (win, n, vx, vy) int vx[], vy[];{ int i, j, fill; fill = n*2 < MAXVERTEX ? n : MAXVERTEX/2; for(i = 0; i < fill; i+=1){ j = i+i; vertex[j].x = vx[i]-CLIPX(win); vertex[j].y = vy[i]-CLIPY(win); vertex[j].flags = VertexDontDraw; vertex[j+1].x = vx[i]-CLIPX(win); vertex[j+1].y = vy[i]-CLIPY(win); vertex[j+1].flags = 0; } return fill; } draw_point (win, x, y) int win, x, y; { draw_line (win, x, y, x+1, y+1); } draw_line (win, x0, y0, x1, y1) { Vertex line[2]; line[0].x = x0-CLIPX(win); line[0].y = y0-CLIPY(win); line[0].flags = 0; line[1].x = x1-CLIPX(win); line[1].y = y1-CLIPY(win); line[1].flags = 0; vertex_draw(win, line, 2); } draw_rectangle (win, x, y, w, h) { Vertex rect[5]; x -= CLIPX(win); y -= CLIPY(win); rect[0].x = x; rect[0].y = y; rect[0].flags = 0; rect[1].x = x; rect[1].y = y+h; rect[1].flags = 0; rect[2].x = x+w; rect[2].y = y+h; rect[2].flags = 0; rect[3].x = x+w; rect[3].y = y; rect[3].flags = 0; rect[4].x = x; rect[4].y = y; rect[4].flags = 0; vertex_draw(win, rect, 5); } draw_polyline (win, n, vx, vy) int vx[], vy[]; { int next = 0; while (n) { vx = &vx[next]; vy = &vy[next]; next = fill_vertex (win, n, vx, vy, 0); vertex_draw(win, vertex, next); n -= next; } } vertex_draw (win, vertex, n) Vertex *vertex; { if (LINE_STYLE (win)){ XDrawDashed (clips[win], vertex, n, 1, 1, ForePixel, LINE_STYLE(win), MODE(win), PLANES); } else XDraw (clips[win], vertex, n, 1, 1, ForePixel, MODE (win), PLANES); } set_line_style (win, n) { LINE_STYLE(win) = line_style[n]; } Pixmap make_pattern (l1, l2) int l1, l2; { Bitmap b; Pixmap p; short vec[16]; int i; for (i=0; i < 16; i+=2) { vec[i]=l1; vec[i+1]=l2; } b = XStoreBitmap(16, 16, vec); p = XMakePixmap(b, WhitePixel, BlackPixel); XFreeBitmap(b); return(p); } init_pattern () { patterns[reversevideo ? 0 : 1] = make_pattern (0, 0); patterns[reversevideo ? 1 : 0] = make_pattern(0xffff, 0xffff); patterns[2] = reversevideo ? make_pattern(0xaaaa, 0x5555) : make_pattern(0x5555, 0xaaaa); patterns[reversevideo ? 3 : 4] = make_pattern(0x8888, 0x2222); patterns[reversevideo ? 4 : 3] = make_pattern(0x7777, 0xdddd); } set_cur_pattern(win, n) int win, n; { PATTERN(win)=patterns[n]; } fill_area (win, n, vx, vy) int vx[], vy[]; { int i; for(i = 0; i < n; i++){ vertex[i].x = vx[i]-CLIPX(win); vertex[i].y = vy[i]-CLIPY(win); vertex[i].flags = 0; } vertex[i].x = vx[0]-CLIPX(win); vertex[i].y = vy[0]-CLIPY(win); vertex[i].flags = 0; vertex[0].flags = VertexStartClosed | VertexDontDraw; vertex[i].flags = VertexEndClosed; #ifdef sun no_cursor (win); #endif sun XDrawTiled (clips[win], vertex, i+1, PATTERN(win), MODE(win), PLANES); #ifdef sun std_cursor (win); #endif sun } no_cursor (win) { XDefineCursor (clips[win], nocursor); } std_cursor (win) { XDefineCursor (clips[win], cursor); } fill_rectangle (win, x, y, w, h) { XTileFill(clips[win], x-CLIPX(win), y-CLIPY(win), w, h, PATTERN(win), 0, MODE(win), PLANES); } fill_vertex (win, n, vx, vy, flags) int vx[], vy[];{ int i, fill; fill = n < MAXVERTEX ? n : MAXVERTEX; for(i = 0; i < fill; i++){ vertex[i].x = vx[i]-CLIPX(win); vertex[i].y = vy[i]-CLIPY(win); vertex[i].flags = flags; } return fill; } draw_ellipse(win, x, y, rx, ry) { fil_ver_ell (win, x, y, rx, ry); vertex_draw (win, vertex, 5); } fill_ellipse(win, x, y, rx, ry) { fil_ver_ell (win, x, y, rx, ry); XDrawTiled(clips[win], vertex, 5, PATTERN(win), MODE(win), PLANES); } fil_ver_ell (win, x, y, rx, ry) { x -= CLIPX(win); y -= CLIPY(win); vertex[0].x = x+rx; vertex[1].x = x; vertex[2].x = x-rx; vertex[3].x = x; vertex[4].x = x+rx; vertex[0].y = y; vertex[1].y = y-ry; vertex[2].y = y; vertex[3].y = y+ry; vertex[4].y = y; vertex[0].flags = VertexCurved | VertexStartClosed; vertex[1].flags = VertexCurved; vertex[2].flags = VertexCurved; vertex[3].flags = VertexCurved; vertex[4].flags = VertexCurved | VertexEndClosed; } draw_vertex(win, n, vx, vy, vf) int vx[], vy[], vf[];{ int i; for(i=0; i